/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $expr = new Expression($expression['left']); $expr->setReadOnly(true); $expr->setExpectReturn(true); $exprPath = $expr->compile($compilationContext); if ($exprPath->getType() == 'variable') { $exprVariable = $compilationContext->symbolTable->getVariableForRead($exprPath->getCode(), $compilationContext, $expression); if ($exprVariable->getType() == 'variable') { if ($exprVariable->hasDifferentDynamicType(array('undefined', 'string'))) { $compilationContext->logger->warning('Possible attempt to use invalid type as path in "require" operator', 'non-valid-require', $expression); } } } $symbolVariable = false; if ($this->isExpecting()) { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression); } $compilationContext->headersManager->add('kernel/memory'); $compilationContext->headersManager->add('kernel/require'); $codePrinter = $compilationContext->codePrinter; if ($symbolVariable) { $codePrinter->output('ZEPHIR_OBSERVE_OR_NULLIFY_PPZV(&' . $symbolVariable->getName() . ');'); $codePrinter->output('if (zephir_require_zval_ret(&' . $symbolVariable->getName() . ', ' . $exprPath->getCode() . ' TSRMLS_CC) == FAILURE) {'); } else { $codePrinter->output('if (zephir_require_zval(' . $exprPath->getCode() . ' TSRMLS_CC) == FAILURE) {'); } $codePrinter->output("\t" . 'RETURN_MM_NULL();'); $codePrinter->output('}'); if ($symbolVariable) { return new CompiledExpression('variable', $symbolVariable->getName(), $expression); } return new CompiledExpression('null', null, $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $expression = array('type' => 'require', 'left' => $this->_statement['expr'], 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']); $expr = new Expression($expression); $expr->setExpectReturn(false, null); $expr->compile($compilationContext); }
/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/object'); $exprVariable = new Expression($expression['left']); $exprVariable->setReadOnly(true); $exprVariable->setExpectReturn(true); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as array", $expression); } $clonedVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression); if ($clonedVariable->getType() != 'variable') { throw new CompilerException("Variable type: " . $exprVariable->getType() . " cannot be cloned"); } if ($clonedVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non array in "clone" operator', 'non-valid-clone', $expression); } $symbolVariable = $this->getExpected($compilationContext, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("Objects can only be cloned into dynamic variables", $expression); } $symbolVariable->setDynamicTypes('object'); $symbolVariable->setIsInitialized(true, $compilationContext, $expression); /* Inherit the dynamic type data from the cloned object */ $symbolVariable->setDynamicTypes($clonedVariable->getDynamicTypes()); $symbolVariable->setClassTypes($clonedVariable->getClassTypes()); $compilationContext->codePrinter->output('if (zephir_clone(' . $symbolVariable->getName() . ', ' . $clonedVariable->getName() . ' TSRMLS_CC) == FAILURE) {'); $compilationContext->codePrinter->output("\t" . 'RETURN_MM();'); $compilationContext->codePrinter->output('}'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); }
/** * @param CompilationContext $compilationContext * @param boolean $unreachable * @param int $branchType * @return Branch */ public function compile(CompilationContext $compilationContext, $unreachable = false, $branchType = Branch::TYPE_UNKNOWN) { $compilationContext->codePrinter->increaseLevel(); $compilationContext->currentBranch++; /** * Create a new branch */ $currentBranch = new Branch(); $currentBranch->setType($branchType); $currentBranch->setUnreachable($unreachable); /** * Activate branch in the branch manager */ $compilationContext->branchManager->addBranch($currentBranch); $this->_unreachable = $unreachable; $statements = $this->_statements; foreach ($statements as $statement) { /** * Generate GDB hints */ if ($this->_debug) { if (isset($statement['file'])) { if ($statement['type'] != 'declare' && $statement['type'] != 'comment') { $compilationContext->codePrinter->outputNoIndent('#line ' . $statement['line'] . ' "' . $statement['file'] . '"'); } } } /** * Show warnings if code is generated when the 'unreachable state' is 'on' */ if ($this->_unreachable === true) { switch ($statement['type']) { case 'echo': $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expressions'][0]); break; case 'let': $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['assignments'][0]); break; case 'fetch': case 'fcall': case 'mcall': case 'scall': case 'if': case 'while': case 'do-while': case 'switch': case 'for': case 'return': case 'c-block': if (isset($statement['expr'])) { $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement['expr']); } else { $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement); } break; default: $compilationContext->logger->warning('Unreachable code', "unreachable-code", $statement); } } switch ($statement['type']) { case 'let': $letStatement = new LetStatement($statement); $letStatement->compile($compilationContext); break; case 'echo': $echoStatement = new EchoStatement($statement); $echoStatement->compile($compilationContext); break; case 'declare': $declareStatement = new DeclareStatement($statement); $declareStatement->compile($compilationContext); break; case 'if': $ifStatement = new IfStatement($statement); $ifStatement->compile($compilationContext); break; case 'while': $whileStatement = new WhileStatement($statement); $whileStatement->compile($compilationContext); break; case 'do-while': $doWhileStatement = new DoWhileStatement($statement); $doWhileStatement->compile($compilationContext); break; case 'switch': $switchStatement = new SwitchStatement($statement); $switchStatement->compile($compilationContext); break; case 'for': $forStatement = new ForStatement($statement); $forStatement->compile($compilationContext); break; case 'return': $returnStatement = new ReturnStatement($statement); $returnStatement->compile($compilationContext); $this->_unreachable = true; break; case 'require': $requireStatement = new RequireStatement($statement); $requireStatement->compile($compilationContext); break; case 'loop': $loopStatement = new LoopStatement($statement); $loopStatement->compile($compilationContext); break; case 'break': $breakStatement = new BreakStatement($statement); $breakStatement->compile($compilationContext); $this->_unreachable = true; break; case 'continue': $continueStatement = new ContinueStatement($statement); $continueStatement->compile($compilationContext); $this->_unreachable = true; break; case 'unset': $unsetStatement = new UnsetStatement($statement); $unsetStatement->compile($compilationContext); break; case 'throw': $throwStatement = new ThrowStatement($statement); $throwStatement->compile($compilationContext); $this->_unreachable = true; break; case 'try-catch': $throwStatement = new TryCatchStatement($statement); $throwStatement->compile($compilationContext); $this->_unreachable = false; break; case 'fetch': $expr = new Expression($statement['expr']); $expr->setExpectReturn(false); $compiledExpression = $expr->compile($compilationContext); $compilationContext->codePrinter->output($compiledExpression->getCode() . ';'); break; case 'mcall': $methodCall = new MethodCall(); $expr = new Expression($statement['expr']); $expr->setExpectReturn(false); $methodCall->compile($expr, $compilationContext); break; case 'fcall': $functionCall = new FunctionCall(); $expr = new Expression($statement['expr']); $expr->setExpectReturn(false); $compiledExpression = $functionCall->compile($expr, $compilationContext); switch ($compiledExpression->getType()) { case 'int': case 'double': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': case 'bool': $compilationContext->codePrinter->output($compiledExpression->getCode() . ';'); break; } break; case 'scall': $methodCall = new StaticCall(); $expr = new Expression($statement['expr']); $expr->setExpectReturn(false); $methodCall->compile($expr, $compilationContext); break; case 'cblock': $compilationContext->codePrinter->output($statement['value']); break; case 'empty': break; default: throw new Exception('Unsupported statement: ' . $statement['type']); } if ($statement['type'] != 'comment') { $this->_lastStatement = $statement; } } /** * Traverses temporal variables created in a specific branch * marking them as idle */ $compilationContext->symbolTable->markTemporalVariablesIdle($compilationContext); $compilationContext->branchManager->removeBranch($currentBranch); $compilationContext->currentBranch--; $compilationContext->codePrinter->decreaseLevel(); return $currentBranch; }
/** * Resolves an expression * * @param CompilationContext $compilationContext * @return bool|CompiledExpression|mixed * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $expression = $this->_expression; $type = $expression['type']; $compilableExpression = null; switch ($type) { case 'null': return new LiteralCompiledExpression('null', null, $expression); case 'int': case 'integer': return new LiteralCompiledExpression('int', $expression['value'], $expression); case 'long': case 'double': case 'bool': return new LiteralCompiledExpression($type, $expression['value'], $expression); case 'string': if (!$this->_stringOperation) { if (ctype_digit($expression['value'])) { return new CompiledExpression('int', $expression['value'], $expression); } } return new LiteralCompiledExpression('string', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'istring': return new LiteralCompiledExpression('istring', str_replace(PHP_EOL, '\\n', $expression['value']), $expression); case 'char': if (!strlen($expression['value'])) { throw new CompilerException("Invalid empty char literal", $expression); } if (strlen($expression['value']) > 2) { if (strlen($expression['value']) > 10) { throw new CompilerException("Invalid char literal: '" . substr($expression['value'], 0, 10) . "...'", $expression); } else { throw new CompilerException("Invalid char literal: '" . $expression['value'] . "'", $expression); } } return new LiteralCompiledExpression('char', $expression['value'], $expression); case 'variable': $var = $compilationContext->symbolTable->getVariable($expression['value']); if ($var) { if ($var->getRealName() == 'this') { $var = 'this'; } else { $var = $var->getName(); } } else { $var = $expression['value']; } return new CompiledExpression('variable', $var, $expression); case 'constant': $compilableExpression = new Constants(); break; case 'empty-array': return $this->emptyArray($expression, $compilationContext); case 'array-access': $compilableExpression = new NativeArrayAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'property-access': $compilableExpression = new PropertyAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'property-string-access': case 'property-dynamic-access': $compilableExpression = new PropertyDynamicAccess(); $compilableExpression->setNoisy($this->isNoisy()); break; case 'static-constant-access': $compilableExpression = new StaticConstantAccess(); break; case 'static-property-access': $compilableExpression = new StaticPropertyAccess(); break; case 'fcall': $functionCall = new FunctionCall(); return $functionCall->compile($this, $compilationContext); case 'mcall': $methodCall = new MethodCall(); return $methodCall->compile($this, $compilationContext); case 'scall': $staticCall = new StaticCall(); return $staticCall->compile($this, $compilationContext); case 'isset': $compilableExpression = new IssetOperator(); break; case 'fetch': $compilableExpression = new FetchOperator(); break; case 'empty': $compilableExpression = new EmptyOperator(); break; case 'array': $compilableExpression = new NativeArray(); break; case 'new': $compilableExpression = new NewInstanceOperator(); break; case 'new-type': $compilableExpression = new NewInstanceTypeOperator(); break; case 'not': $compilableExpression = new NotOperator(); break; case 'bitwise_not': $compilableExpression = new BitwiseNotOperator(); break; case 'equals': $compilableExpression = new EqualsOperator(); break; case 'not-equals': $compilableExpression = new NotEqualsOperator(); break; case 'identical': $compilableExpression = new IdenticalOperator(); break; case 'not-identical': $compilableExpression = new NotIdenticalOperator(); break; case 'greater': $compilableExpression = new GreaterOperator(); break; case 'less': $compilableExpression = new LessOperator(); break; case 'less-equal': $compilableExpression = new LessEqualOperator(); break; case 'greater-equal': $compilableExpression = new GreaterEqualOperator(); break; case 'add': $compilableExpression = new AddOperator(); break; case 'minus': $compilableExpression = new MinusOperator(); break; case 'sub': $compilableExpression = new SubOperator(); break; case 'mul': $compilableExpression = new MulOperator(); break; case 'div': $compilableExpression = new DivOperator(); break; case 'mod': $compilableExpression = new ModOperator(); break; case 'and': $compilableExpression = new AndOperator(); break; case 'or': $compilableExpression = new OrOperator(); break; case 'bitwise_and': $compilableExpression = new BitwiseAndOperator(); break; case 'bitwise_or': $compilableExpression = new BitwiseOrOperator(); break; case 'bitwise_xor': $compilableExpression = new BitwiseXorOperator(); break; case 'bitwise_shiftleft': $compilableExpression = new ShiftLeftOperator(); break; case 'bitwise_shiftright': $compilableExpression = new ShiftRightOperator(); break; case 'concat': $expr = new ConcatOperator(); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'irange': $compilableExpression = new RangeInclusiveOperator(); break; case 'erange': $compilableExpression = new RangeExclusiveOperator(); break; case 'list': if ($expression['left']['type'] == 'list') { $compilationContext->logger->warning("Unnecessary extra parentheses", "extra-parentheses", $expression); } $numberPrints = $compilationContext->codePrinter->getNumberPrints(); $expr = new Expression($expression['left']); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); $resolved = $expr->compile($compilationContext); if ($compilationContext->codePrinter->getNumberPrints() - $numberPrints <= 1) { if (strpos($resolved->getCode(), ' ') !== false) { return new CompiledExpression($resolved->getType(), '(' . $resolved->getCode() . ')', $expression); } } return $resolved; case 'cast': $compilableExpression = new CastOperator(); break; case 'type-hint': return $this->compileTypeHint($expression, $compilationContext); case 'instanceof': $compilableExpression = new InstanceOfOperator(); break; case 'clone': $compilableExpression = new CloneOperator(); break; case 'ternary': $compilableExpression = new TernaryOperator(); break; case 'short-ternary': $expr = new ShortTernaryOperator(); $expr->setReadOnly($this->isReadOnly()); $expr->setExpectReturn($this->_expecting, $this->_expectingVariable); return $expr->compile($expression, $compilationContext); case 'likely': if (!$this->_evalMode) { throw new CompilerException("'likely' operator can only be used in evaluation expressions", $expression); } $expr = new LikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'unlikely': if (!$this->_evalMode) { throw new CompilerException("'unlikely' operator can only be used in evaluation expressions", $expression); } $expr = new UnlikelyOperator(); $expr->setReadOnly($this->isReadOnly()); return $expr->compile($expression, $compilationContext); case 'typeof': $compilableExpression = new TypeOfOperator(); break; case 'require': $compilableExpression = new RequireOperator(); break; case 'closure': $compilableExpression = new Closure(); break; case 'closure-arrow': $compilableExpression = new ClosureArrow(); break; case 'reference': $compilableExpression = new Reference(); break; default: throw new CompilerException("Unknown expression: " . $type, $expression); } if (!$compilableExpression) { throw new CompilerException("Unknown expression passed as compilableExpression", $expression); } $compilableExpression->setReadOnly($this->isReadOnly()); $compilableExpression->setExpectReturn($this->_expecting, $this->_expectingVariable); return $compilableExpression->compile($expression, $compilationContext); }
/** * Assigns a default value * * @param array $parameter * @param CompilationContext $compilationContext * @return string * @throws CompilerException */ public function assignDefaultValue(array $parameter, CompilationContext $compilationContext) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } /** * Class-Hinted parameters only can be null? */ if (isset($parameter['cast'])) { if ($parameter['default']['type'] != 'null') { throw new CompilerException('Class-Hinted parameters only can have "null" as default parameter', $parameter); } } $oldCodePrinter = $compilationContext->codePrinter; $codePrinter = new CodePrinter(); $codePrinter->increaseLevel(); $codePrinter->increaseLevel(); $compilationContext->codePrinter = $codePrinter; $paramVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext); /** * @todo Refactoring this place, move to one - static-constant-access */ switch ($dataType) { case 'int': case 'uint': case 'long': case 'ulong': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'int') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(int)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'int': case 'uint': case 'long': $codePrinter->output($parameter['name'] . ' = ' . $parameter['default']['value'] . ';'); break; case 'double': $codePrinter->output($parameter['name'] . ' = (int) ' . $parameter['default']['value'] . ';'); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(int)", $parameter); } break; case 'double': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'double') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(double)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'int': case 'uint': case 'long': $codePrinter->output($parameter['name'] . ' = (double) ' . $parameter['default']['value'] . ';'); break; case 'double': $codePrinter->output($parameter['name'] . ' = ' . $parameter['default']['value'] . ';'); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(double)", $parameter); } break; case 'bool': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'bool') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(bool)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'bool': if ($parameter['default']['value'] == 'true') { $codePrinter->output($parameter['name'] . ' = 1;'); } else { $codePrinter->output($parameter['name'] . ' = 0;'); } break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(bool)", $parameter); } break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'string') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(string)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, null, $compilationContext); break; case 'string': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, Utils::addSlashes($parameter['default']['value'], true), $compilationContext); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(string)", $parameter); } break; case 'array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'null': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->initArray($paramVariable, $compilationContext, null); break; case 'empty-array': case 'array': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->initArray($paramVariable, $compilationContext, null); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(array)", $parameter); } break; case 'variable': $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'int': case 'uint': case 'long': case 'ulong': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignLong($symbolVariable, $parameter['default']['value'], $compilationContext); break; case 'double': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignDouble($symbolVariable, $parameter['default']['value'], $compilationContext); break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, Utils::addSlashes($parameter['default']['value'], true), $compilationContext); break; case 'bool': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { if ($parameter['default']['value'] == 'true') { $compilationContext->backend->assignZval($paramVariable, $compilationContext->backend->resolveValue('true', $compilationContext), $compilationContext); } else { $compilationContext->backend->assignZval($paramVariable, $compilationContext->backend->resolveValue('false', $compilationContext), $compilationContext); } } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); if ($parameter['default']['value'] == 'true') { $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('true', $compilationContext), $compilationContext); } else { $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('false', $compilationContext), $compilationContext); } } break; case 'null': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { $compilationContext->backend->assignZval($symbolVariable, $compilationContext->backend->resolveValue('null', $compilationContext), $compilationContext); } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('null', $compilationContext), $compilationContext); } break; case 'empty-array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->initArray($symbolVariable, $compilationContext, null); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(variable)", $parameter); } break; default: throw new CompilerException("Default parameter type: " . $dataType, $parameter); } $compilationContext->codePrinter = $oldCodePrinter; return $codePrinter->getOutput(); }
/** * Creates a new instance * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; /** * Resolves the symbol that expects the value */ $this->_literalOnly = false; $symbolVariable = $this->getExpectedNonLiteral($compilationContext, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("Objects can only be instantiated into dynamic variables", $expression); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot use non-heap variable to store new instance", $expression); } if ($symbolVariable->getName() != 'return_value') { if ($symbolVariable->hasDifferentDynamicType(array('unknown', 'undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non-object in "new" operator', 'non-valid-new', $expression); } } /** * Mark variables as dynamic objects */ $symbolVariable->setDynamicTypes('object'); $dynamic = false; if ($expression['class'] == 'self' || $expression['class'] == 'static') { $className = $compilationContext->classDefinition->getCompleteName(); } else { $className = $expression['class']; $dynamic = $expression['dynamic']; if (!$dynamic) { $className = $compilationContext->getFullName($expression['class']); } } if (!$className) { throw new CompilerException("A class name is required to instantiate the object", $expression); } /** * stdclass doesn't have constructors */ $lowerClassName = strtolower($className); $isStdClass = $lowerClassName === 'stdclass' || $lowerClassName === '\\stdclass'; if ($isStdClass) { if (isset($expression['parameters']) && count($expression['parameters']) > 0) { throw new CompilerException("stdclass does not receive parameters in its constructor", $expression); } $compilationContext->backend->initObject($symbolVariable, null, $compilationContext); $symbolVariable->setClassTypes('stdclass'); } else { $classDefinition = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); } /** * Classes inside the same extension */ if ($classDefinition) { $compilationContext->backend->initObject($symbolVariable, $classDefinition->getClassEntry($compilationContext), $compilationContext); $symbolVariable->setClassTypes($className); $symbolVariable->setAssociatedClass($classDefinition); } else { /** * Classes outside the extension */ if ($dynamic) { $classNameVariable = $compilationContext->symbolTable->getVariableForRead($className, $compilationContext, $expression); if ($classNameVariable->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in new operator. " . $classNameVariable->getName(), $expression); } /** * Use a safe string version of the variable to avoid segfaults */ $compilationContext->headersManager->add('kernel/object'); $safeSymbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext, $expression); $safeSymbolVariable->setMustInitNull(true); $safeSymbolVariable->setIsInitialized(true, $compilationContext, $expression); $safeSymbolVariable->increaseUses(); $compilationContext->codePrinter->output('zephir_fetch_safe_class(' . $safeSymbolVariable->getName() . ', ' . $classNameVariable->getName() . ');'); $safeSymbol = $compilationContext->backend->getVariableCode($safeSymbolVariable); $classNameToFetch = 'Z_STRVAL_P(' . $safeSymbol . '), Z_STRLEN_P(' . $safeSymbol . ')'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, true, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { if (!class_exists($className, false)) { $compilationContext->logger->warning('Class "' . $className . '" does not exist at compile time', "nonexistent-class", $expression); $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $reflectionClass = new \ReflectionClass($className); if ($reflectionClass->isInterface()) { throw new CompilerException('Interfaces cannot be instantiated', $expression); } else { if (method_exists($reflectionClass, 'isTrait')) { if ($reflectionClass->isTrait()) { throw new CompilerException('Traits cannot be instantiated', $expression); } } } $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true); if (!$classEntry) { $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $symbolVariable->setAssociatedClass($reflectionClass); } } $symbolVariable->setClassTypes($className); } $compilationContext->backend->initObject($symbolVariable, $classEntry, $compilationContext); } } /** * Mark variable initialized */ $symbolVariable->setIsInitialized(true, $compilationContext, $expression); /** * Don't check the constructor for stdclass instances */ if ($isStdClass) { return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } /** * Call the constructor * For classes in the same extension we check if the class does implement a constructor * For external classes we always assume the class does implement a constructor */ $callConstructor = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); if ($classDefinition->getType() != 'class') { throw new CompilerException("Only classes can be instantiated", $expression); } $callConstructor = $classDefinition->hasMethod("__construct"); } else { if ($compilationContext->compiler->isBundledClass($className)) { $classDefinition = $compilationContext->compiler->getInternalClassDefinition($className); $callConstructor = $classDefinition->hasMethod("__construct"); } } /* @TODO use the MethodBuilder here */ if (isset($expression['parameters'])) { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'parameters' => $expression['parameters'], 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } else { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } /** * If we are certain that there is a constructor we call it, otherwise we checked it at runtime. */ if ($callConstructor) { $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); } else { $compilationContext->headersManager->add('kernel/fcall'); /* @todo, generate the code using builders */ $compilationContext->backend->checkConstructor($symbolVariable, $compilationContext); $codePrinter->increaseLevel(); $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Assigns a default value * * @param array $parameter * @param CompilationContext $compilationContext * @return string * @throws CompilerException */ public function assignDefaultValue(array $parameter, CompilationContext $compilationContext) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } /** * Class-Hinted parameters only can be null? */ if (isset($parameter['cast'])) { if ($parameter['default']['type'] != 'null') { throw new CompilerException('Class-Hinted parameters only can have "null" as default parameter', $parameter); } } $code = ''; /** * @todo Refactoring this place, move to one - static-constant-access */ switch ($dataType) { case 'int': case 'uint': case 'long': case 'ulong': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'int') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(int)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $code .= "\t\t" . $parameter['name'] . ' = 0;' . PHP_EOL; break; case 'int': case 'uint': case 'long': $code .= "\t\t" . $parameter['name'] . ' = ' . $parameter['default']['value'] . ';' . PHP_EOL; break; case 'double': $code .= "\t\t" . $parameter['name'] . ' = (int) ' . $parameter['default']['value'] . ';' . PHP_EOL; break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(int)", $parameter); } break; case 'double': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'double') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(double)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $code .= "\t\t" . $parameter['name'] . ' = 0;' . PHP_EOL; break; case 'int': case 'uint': case 'long': $code .= "\t\t" . $parameter['name'] . ' = (double) ' . $parameter['default']['value'] . ';' . PHP_EOL; break; case 'double': $code .= "\t\t" . $parameter['name'] . ' = ' . $parameter['default']['value'] . ';' . PHP_EOL; break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(double)", $parameter); } break; case 'bool': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'bool') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(bool)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $code .= "\t\t" . $parameter['name'] . ' = 0;' . PHP_EOL; break; case 'bool': if ($parameter['default']['value'] == 'true') { $code .= "\t\t" . $parameter['name'] . ' = 1;' . PHP_EOL; } else { $code .= "\t\t" . $parameter['name'] . ' = 0;' . PHP_EOL; } break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(bool)", $parameter); } break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'string') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(string)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'ZVAL_EMPTY_STRING(' . $parameter['name'] . ');' . PHP_EOL; break; case 'string': $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'ZVAL_STRING(' . $parameter['name'] . ', "' . Utils::addSlashes($parameter['default']['value'], true) . '", 1);' . PHP_EOL; break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(string)", $parameter); } break; case 'array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'null': $code .= "\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t" . 'array_init(' . $parameter['name'] . ');' . PHP_EOL; break; case 'empty-array': case 'array': $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'array_init(' . $parameter['name'] . ');' . PHP_EOL; break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(array)", $parameter); } break; case 'variable': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); return $this->assignDefaultValue($parameter, $compilationContext); break; case 'int': case 'uint': case 'long': case 'ulong': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'ZVAL_LONG(' . $parameter['name'] . ', ' . $parameter['default']['value'] . ');' . PHP_EOL; break; case 'double': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'ZVAL_DOUBLE(' . $parameter['name'] . ', ' . $parameter['default']['value'] . ');' . PHP_EOL; break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'ZVAL_STRING(' . $parameter['name'] . ', "' . Utils::addSlashes($parameter['default']['value'], true) . '", 1);' . PHP_EOL; break; case 'bool': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { if ($parameter['default']['value'] == 'true') { $code .= "\t\t" . $parameter['name'] . ' = ZEPHIR_GLOBAL(global_true);' . PHP_EOL; } else { $code .= "\t\t" . $parameter['name'] . ' = ZEPHIR_GLOBAL(global_false);' . PHP_EOL; } } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); if ($parameter['default']['value'] == 'true') { $code .= "\t\t" . 'ZEPHIR_CPY_WRT(' . $parameter['name'] . ', ZEPHIR_GLOBAL(global_true));' . PHP_EOL; } else { $code .= "\t\t" . 'ZEPHIR_CPY_WRT(' . $parameter['name'] . ', ZEPHIR_GLOBAL(global_false));' . PHP_EOL; } } break; case 'null': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { $code .= "\t\t" . $parameter['name'] . ' = ZEPHIR_GLOBAL(global_null);' . PHP_EOL; } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $code .= "\t\t" . 'ZEPHIR_CPY_WRT(' . $parameter['name'] . ', ZEPHIR_GLOBAL(global_null));' . PHP_EOL; } break; case 'empty-array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $code .= "\t\t" . 'ZEPHIR_INIT_VAR(' . $parameter['name'] . ');' . PHP_EOL; $code .= "\t\t" . 'array_init(' . $parameter['name'] . ');' . PHP_EOL; break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(variable)", $parameter); } break; default: throw new CompilerException("Default parameter type: " . $dataType, $parameter); } return $code; }
/** * Compiles ClassName::foo = {expr} * * @param $className * @param $property * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * * @throws CompilerException * @internal param string $variable */ public function assignStatic($className, $property, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $compiler = $compilationContext->compiler; if (!in_array($className, array('self', 'static', 'parent'))) { $className = $compilationContext->getFullName($className); if ($compiler->isClass($className)) { $classDefinition = $compiler->getClassDefinition($className); } else { if ($compiler->isBundledClass($className)) { $classDefinition = $compiler->getInternalClassDefinition($className); } else { throw new CompilerException("Cannot locate class '" . $className . "'", $statement); } } } else { if (in_array($className, array('self', 'static'))) { $classDefinition = $compilationContext->classDefinition; } else { if ($className == 'parent') { $classDefinition = $compilationContext->classDefinition; $extendsClass = $classDefinition->getExtendsClass(); if (!$extendsClass) { throw new CompilerException('Cannot assign static property "' . $property . '" on parent because class ' . $classDefinition->getCompleteName() . ' does not extend any class', $statement); } else { $classDefinition = $classDefinition->getExtendsClassDefinition(); } } } } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } /** @var $propertyDefinition ClassProperty */ $propertyDefinition = $classDefinition->getProperty($property); if (!$propertyDefinition->isStatic()) { throw new CompilerException("Cannot access non-static property '" . $classDefinition->getCompleteName() . '::' . $property . "'", $statement); } if ($propertyDefinition->isPrivate()) { if ($classDefinition != $compilationContext->classDefinition) { throw new CompilerException("Cannot access private static property '" . $classDefinition->getCompleteName() . '::' . $property . "' out of its declaring context", $statement); } } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); $classEntry = $classDefinition->getClassEntry($compilationContext); switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->updateStaticProperty($classEntry, $property, 'null', $compilationContext); break; case 'int': case 'uint': case 'long': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, '\'' . $resolvedExpr->getCode() . '\'', $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': switch ($statement['operator']) { case 'assign': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $tempVariable->initVariant($compilationContext); if ($resolvedExpr->getCode()) { $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext); } else { $codePrinter->output('ZVAL_EMPTY_STRING(' . $tempVariable->getName() . ');'); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } $codePrinter->output('zephir_update_static_property_ce(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'bool': if ($resolvedExpr->getBooleanCode() == '1') { $compilationContext->backend->updateStaticProperty($classEntry, $property, 'true', $compilationContext); } else { if ($resolvedExpr->getBooleanCode() == '0') { $compilationContext->backend->updateStaticProperty($classEntry, $property, 'false', $compilationContext); } else { $codePrinter->output('if (' . $resolvedExpr->getBooleanCode() . ') {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateStaticProperty($classEntry, $property, 'true', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateStaticProperty($classEntry, $property, 'false', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); } } break; case 'empty-array': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->initArray($tempVariable, $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'array': $compilationContext->backend->updateStaticProperty($classEntry, $property, $resolvedExpr, $compilationContext); break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); if ($compilationContext->insideCycle) { $propertyCache = $compilationContext->symbolTable->getTempVariableForWrite('zend_property_info', $compilationContext); $propertyCache->setMustInitNull(true); $propertyCache->setReusable(false); $codePrinter->output('zephir_update_static_property_ce_cache(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ', &' . $propertyCache->getName() . ' TSRMLS_CC);'); } else { $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext); if ($compilationContext->insideCycle) { $propertyCache = $compilationContext->symbolTable->getTempVariableForWrite('zend_property_info', $compilationContext); $propertyCache->setMustInitNull(true); $propertyCache->setReusable(false); $codePrinter->output('zephir_update_static_property_ce_cache(' . $classEntry . ', SL("' . $property . '"), &' . $tempVariable->getName() . ', &' . $propertyCache->getName() . ' TSRMLS_CC);'); } else { $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); } if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignBool($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateStaticProperty($classEntry, $property, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': switch ($statement['operator']) { case 'concat-assign': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $expression = new Expression(array('type' => 'static-property-access', 'left' => array('value' => $statement['variable']), 'right' => array('value' => $statement['property']))); $expression->setExpectReturn(true, $tempVariable); $expression->compile($compilationContext); $variableVariableCode = $compilationContext->backend->getVariableCode($variableVariable); $tempVariableCode = $compilationContext->backend->getVariableCode($tempVariable); $compilationContext->codePrinter->output('zephir_concat_function(' . $variableVariableCode . ', ' . $tempVariableCode . ', ' . $variableVariableCode . ');'); //continue //continue case 'assign': $compilationContext->backend->updateStaticProperty($classEntry, $property, $variableVariable, $compilationContext); if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'variable': case 'array': $compilationContext->backend->updateStaticProperty($classEntry, $property, $variableVariable, $compilationContext); if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $statement = $this->_statement; $codePrinter = $compilationContext->codePrinter; if (isset($statement['expr'])) { $currentMethod = $compilationContext->currentMethod; if ($currentMethod->isConstructor()) { throw new CompilerException("Constructors cannot return values", $statement['expr']); } if ($currentMethod->isVoid()) { throw new CompilerException("Method is marked as 'void' and it must not return any value", $statement['expr']); } /** * Use return member for properties on this */ if ($statement['expr']['type'] == 'property-access') { if ($statement['expr']['left']['type'] == 'variable') { if ($statement['expr']['left']['value'] == 'this') { if ($statement['expr']['right']['type'] == 'variable') { /** * If the property is accessed on 'this', we check if the property does exist */ $property = $statement['expr']['right']['value']; $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement['expr']['right']); } $compilationContext->headersManager->add('kernel/object'); $codePrinter->output('RETURN_MM_MEMBER(this_ptr, "' . $property . '");'); return; } } } } /** * Fetches return_value and tries to return the value directly there */ $variable = $compilationContext->symbolTable->getVariable('return_value'); $expr = new Expression($statement['expr']); $expr->setExpectReturn(true, $variable); $expr->setReadOnly(true); $resolvedExpr = $expr->compile($compilationContext); /** * Here we check if the variable returns a compatible type according to its type hints */ if ($currentMethod->hasReturnTypes()) { switch ($resolvedExpr->getType()) { case 'null': if (!$currentMethod->areReturnTypesNullCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'int': case 'uint': case 'long': case 'char': case 'uchar': if (!$currentMethod->areReturnTypesIntCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'bool': if (!$currentMethod->areReturnTypesBoolCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'double': if (!$currentMethod->areReturnTypesDoubleCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'string': if (!$currentMethod->areReturnTypesStringCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'variable': $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['expr']); switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': if (!$currentMethod->areReturnTypesIntCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'double': if (!$currentMethod->areReturnTypesDoubleCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'string': if (!$currentMethod->areReturnTypesStringCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'bool': if (!$currentMethod->areReturnTypesBoolCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'variable': break; } break; } } switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('RETURN_MM_NULL();'); break; case 'int': case 'uint': case 'long': case 'char': case 'uchar': $codePrinter->output('RETURN_MM_LONG(' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('RETURN_MM_BOOL(' . $resolvedExpr->getBooleanCode() . ');'); break; case 'double': $codePrinter->output('RETURN_MM_DOUBLE(' . $resolvedExpr->getCode() . ');'); break; case 'string': case 'istring': $codePrinter->output('RETURN_MM_STRING("' . Utils::addSlashes($resolvedExpr->getCode()) . '", 1);'); break; case 'array': if ($resolvedExpr->getCode() != 'return_value') { $codePrinter->output('RETURN_CTOR(' . $resolvedExpr->getCode() . ');'); } else { $codePrinter->output('RETURN_MM();'); } break; case 'variable': if (!isset($symbolVariable)) { $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['expr']); } switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': $codePrinter->output('RETURN_MM_LONG(' . $symbolVariable->getName() . ');'); break; case 'double': $codePrinter->output('RETURN_MM_DOUBLE(' . $symbolVariable->getName() . ');'); break; case 'string': case 'array': $codePrinter->output('RETURN_CTOR(' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('RETURN_MM_BOOL(' . $symbolVariable->getName() . ');'); break; case 'variable': if ($symbolVariable->getName() == 'this_ptr') { $codePrinter->output('RETURN_THIS();'); } else { if ($symbolVariable->getName() != 'return_value') { if (!$symbolVariable->isExternal()) { if ($symbolVariable->isLocalOnly()) { $codePrinter->output('RETURN_LCTOR(' . $symbolVariable->getName() . ');'); } else { if (!$symbolVariable->isMemoryTracked()) { $codePrinter->output('RETURN_CTOR(' . $symbolVariable->getName() . ');'); } else { $codePrinter->output('RETURN_CCTOR(' . $symbolVariable->getName() . ');'); } } } else { $codePrinter->output('RETVAL_ZVAL(' . $symbolVariable->getName() . ', 1, 0);'); $codePrinter->output('RETURN_MM();'); } } else { $codePrinter->output('RETURN_MM();'); } } if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Cannot return variable '" . $symbolVariable->getType() . "'", $statement['expr']); } break; default: throw new CompilerException("Cannot return '" . $resolvedExpr->getType() . "'", $statement['expr']); } return; } /** * Return without an expression */ $codePrinter->output('RETURN_MM_NULL();'); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $readDetector = new ReadDetector(); $statement = $this->_statement; foreach ($statement['assignments'] as $assignment) { $variable = $assignment['variable']; /** * Get the symbol from the symbol table if necessary */ switch ($assignment['assign-type']) { case 'static-property': case 'static-property-append': case 'static-property-array-index': case 'static-property-array-index-append': case 'dynamic-variable-string': $symbolVariable = null; break; case 'array-index': case 'variable-append': case 'object-property': case 'array-index-append': case 'string-dynamic-object-property': case 'variable-dynamic-object-property': $symbolVariable = $compilationContext->symbolTable->getVariableForUpdate($variable, $compilationContext, $assignment); break; default: $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($variable, $compilationContext, $assignment); break; } /** * Incr/Decr assignments don't require an expression */ if (isset($assignment['expr'])) { $expr = new Expression($assignment['expr']); switch ($assignment['assign-type']) { case 'variable': if (!$readDetector->detect($variable, $assignment['expr'])) { if (isset($assignment['operator'])) { if ($assignment['operator'] == 'assign') { $expr->setExpectReturn(true, $symbolVariable); } } else { $expr->setExpectReturn(true, $symbolVariable); } } else { if (isset($assignment['operator'])) { if ($assignment['operator'] == 'assign') { $expr->setExpectReturn(true); } } else { $expr->setExpectReturn(true); } } break; } switch ($assignment['expr']['type']) { case 'property-access': case 'array-access': case 'type-hint': $expr->setReadOnly(true); break; } $resolvedExpr = $expr->compile($compilationContext); /** * Bad implemented operators could return values different than objects */ if (!is_object($resolvedExpr)) { throw new CompilerException("Resolved expression is not valid", $assignment['expr']); } } /** * There are four types of assignments */ switch ($assignment['assign-type']) { case 'variable': $let = new LetVariable(); $let->assign($variable, $symbolVariable, $resolvedExpr, $readDetector, $compilationContext, $assignment); break; case 'variable-append': $let = new LetVariableAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property': $let = new LetObjectProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'variable-dynamic-object-property': $let = new LetObjectDynamicProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'string-dynamic-object-property': $let = new LetObjectDynamicStringProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'static-property': $let = new LetStaticProperty(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-append': $let = new LetStaticPropertyAppend(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-array-index': $let = new LetStaticPropertyArrayIndex(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-array-index-append': $let = new LetStaticPropertyArrayIndexAppend(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'array-index': $let = new LetArrayIndex(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'array-index-append': $let = new LetArrayIndexAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-append': $let = new LetObjectPropertyAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-array-index': $let = new LetObjectPropertyArrayIndex(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-array-index-append': $let = new LetObjectPropertyArrayIndexAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'incr': $let = new LetIncr(); $let->assign($variable, $symbolVariable, $compilationContext, $assignment); break; case 'decr': $let = new LetDecr(); $let->assign($variable, $symbolVariable, $compilationContext, $assignment); break; case 'object-property-incr': $let = new LetObjectPropertyIncr(); $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment); break; case 'object-property-decr': $let = new LetObjectPropertyDecr(); $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment); break; case 'dynamic-variable': $let = new LetExportSymbol(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'dynamic-variable-string': $let = new LetExportSymbolString(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; default: throw new CompilerException("Unknown assignment: " . $assignment['assign-type'], $assignment); } } }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $statement = $this->_statement; if (!isset($statement['data-type'])) { throw new CompilerException("Data type is required", $this->_statement); } $typeInference = $compilationContext->typeInference; $symbolTable = $compilationContext->symbolTable; foreach ($statement['variables'] as $variable) { if ($symbolTable->hasVariable($variable['variable'])) { throw new CompilerException("Variable '" . $variable['variable'] . "' is already defined", $variable); } $currentType = $statement['data-type']; /** * Replace original data type by the pre-processed infered type */ if ($typeInference) { if ($currentType == 'variable') { $type = $typeInference->getInferedType($variable['variable']); if (is_string($type)) { $currentType = $type; } } } /** * Variables are added to the symbol table */ if (isset($variable['expr'])) { $symbolVariable = $symbolTable->addVariable($currentType, $variable['variable'], $compilationContext, $variable['expr']); } else { $symbolVariable = $symbolTable->addVariable($currentType, $variable['variable'], $compilationContext); } /** * Set the node where the variable is declared */ $symbolVariable->setOriginal($variable); if (isset($variable['expr']['type'])) { $defaultType = $variable['expr']['type']; } else { $defaultType = null; } if (isset($variable['expr']['value'])) { $defaultValue = $variable['expr']['value']; } else { $defaultValue = null; } /** * Variables with a default value are initialized by default */ if ($defaultValue !== null || $defaultType !== null) { if ($currentType == 'variable' || $currentType == 'string' || $currentType == 'array') { $symbolVariable->increaseVariantIfNull(); } switch ($currentType) { case 'int': case 'uint': case 'ulong': case 'long': switch ($defaultType) { case 'int': case 'uint': case 'ulong': break; case 'null': $defaultValue = 0; break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'double': switch ($defaultType) { case 'int': case 'uint': case 'long': case 'double': break; case 'null': $defaultValue = 0; break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'bool': switch ($defaultType) { case 'bool': if ($variable['expr']['value'] == 'true') { $defaultValue = 1; } else { $defaultValue = 0; } break; case 'null': $defaultValue = 0; break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'char': case 'uchar': switch ($defaultType) { case 'char': case 'uchar': $defaultValue = '\'' . $defaultValue . '\''; break; case 'int': break; case 'null': $defaultValue = 0; break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'string': $defaultValue = $variable['expr']; switch ($defaultType) { case 'string': case 'null': break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'array': $defaultValue = $variable['expr']; switch ($defaultType) { case 'null': case 'array': case 'empty-array': break; default: self::invalidDefaultTypeException($variable['expr']['type'], $statement['data-type'], $variable); } break; case 'variable': $defaultValue = $variable['expr']; switch ($defaultType) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': $symbolVariable->setDynamicTypes('long'); break; case 'double': $symbolVariable->setDynamicTypes('double'); break; case 'string': case 'ulong': $symbolVariable->setDynamicTypes('string'); break; case 'array': $expression = new Expression($variable['expr']); $expression->setExpectReturn(true, $symbolVariable); $expression->compile($compilationContext); // no break // no break case 'array': case 'empty-array': $symbolVariable->setDynamicTypes('array'); break; case 'null': $symbolVariable->setDynamicTypes('null'); $symbolVariable->setMustInitNull(true); $symbolVariable->setLocalOnly(false); break; default: self::invalidDefaultTypeException($defaultType, $statement['data-type'], $variable); } break; default: throw new CompilerException('Invalid variable type: ' . $currentType, $variable); } $symbolVariable->setDefaultInitValue($defaultValue); $symbolVariable->setIsInitialized(true, $compilationContext); $symbolVariable->increaseMutates(); $symbolVariable->setPossibleValue(new LiteralCompiledExpression($defaultType, $defaultValue, $variable['expr']), $compilationContext); } } }