/** * Compiles foo[y][x][] = {expr} (multiple offset) * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement */ protected function _assignArrayIndexMultiple($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $codePrinter = $compilationContext->codePrinter; $offsetExprs = array(); foreach ($statement['index-expr'] as $indexExpr) { $expression = new Expression($indexExpr); $expression->setReadOnly(true); $exprIndex = $expression->compile($compilationContext); switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'string': case 'variable': break; default: throw new CompilerException("Index: " . $exprIndex->getType() . " cannot be used as array index in assignment without cast", $indexExpr); } $offsetExprs[] = $exprIndex; } $compilationContext->headersManager->add('kernel/array'); /** * Create a temporal zval (if needed) */ $symbolVariable = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); $targetVariable = $compilationContext->symbolTable->getVariableForWrite($variable, $compilationContext, $statement); $offsetExprs[] = 'a'; $compilationContext->backend->assignArrayMulti($targetVariable, $symbolVariable, $offsetExprs, $compilationContext); }
/** * @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 $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException * @throws Exception */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Missing left part of the expression", $expression); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); switch ($left->getType()) { case 'bool': case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '~(' . $left->getCode() . ')', $expression); case 'variable': $variable = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression['left']); switch ($variable->getType()) { case 'bool': case 'int': case 'uint': case 'long': return new CompiledExpression('int', '~' . $variable->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('int', '~zephir_get_intval(' . $variable->getName() . ')', $expression); default: throw new CompilerException("Unknown type: " . $variable->getType(), $expression); } break; default: throw new CompilerException("Unknown type: " . $left->getType(), $expression); } }
/** * Compiles x::y[a][b] = {expr} (multiple offset assignment) * * @param string $variable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ protected function _assignStaticPropertyArrayMultipleIndex($classEntry, $property, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Create a temporal zval (if needed) */ $variableExpr = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); /** * Only string/variable/int */ $offsetExprs = array(); foreach ($statement['index-expr'] as $indexExpr) { $indexExpression = new Expression($indexExpr); $resolvedIndex = $indexExpression->compile($compilationContext); switch ($resolvedIndex->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Expression: " . $resolvedIndex->getType() . " cannot be used as index without cast", $statement['index-expr']); } $offsetExprs[] = $resolvedIndex; } $compilationContext->backend->assignStaticPropertyArrayMulti($classEntry, $variableExpr, $property, $offsetExprs, $compilationContext); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } }
/** * Executes the operator * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { if (!isset($expression['parameters'])) { throw new CompilerException("Invalid 'parameters' for new-type", $expression); } switch ($expression['internal-type']) { case 'array': $compilationContext->headersManager->add('kernel/array'); $functionName = 'create_array'; break; case 'string': $compilationContext->headersManager->add('kernel/string'); $functionName = 'create_string'; break; default: throw new CompilerException("Cannot build instance of type", $expression); } $builder = new FunctionCallBuilder($functionName, $expression['parameters'], 1, $expression['file'], $expression['line'], $expression['char']); /** * Implicit type coercing */ $castBuilder = new CastOperatorBuilder($expression['internal-type'], $builder); $expression = new Expression($castBuilder->get()); $expression->setReadOnly($this->_readOnly); return $expression->compile($compilationContext); }
/** * Compile expression * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException * @throws Exception */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new \Exception("Missing left part of the expression"); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); switch ($left->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression($left->getType(), '+' . $left->getCode(), $expression); case 'variable': $variable = $compilationContext->symbolTable->getVariable($left->getCode()); switch ($variable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression($variable->getType(), '+' . $variable->getName(), $expression); case 'variable': return new CompiledExpression('variable', $variable->getName(), $expression); default: throw new CompilerException("Cannot operate plus with variable of '" . $left->getType() . "' type"); } break; default: throw new CompilerException("Cannot operate plus with '" . $left->getType() . "' type"); } }
/** * @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); }
/** * Transforms calls to method "toHex" to sprintf('%X') call * * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @return bool|mixed|\Zephir\CompiledExpression */ public function toHex($caller, CompilationContext $compilationContext, Call $call, array $expression) { $exprBuilder = BuilderFactory::getInstance(); $functionCall = $exprBuilder->statements()->functionCall('sprintf', array($exprBuilder->literal(Types::STRING, '%X'), $caller))->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); }
/** * Resolves the access to a property in an object * * @param array $expression * @param CompilationContext $compilationContext * @return \CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; $propertyAccess = $expression; $expr = new Expression($propertyAccess['left']); $exprVariable = $expr->compile($compilationContext); switch ($exprVariable->getType()) { case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression); switch ($variableVariable->getType()) { case 'variable': break; default: throw new CompilerException("Variable type: " . $variableVariable->getType() . " cannot be used as object", $propertyAccess['left']); } break; default: throw new CompilerException("Cannot use expression: " . $exprVariable->getType() . " as an object", $propertyAccess['left']); } switch ($propertyAccess['right']['type']) { case 'variable': $propertyVariable = $compilationContext->symbolTable->getVariableForRead($propertyAccess['right']['value'], $compilationContext, $expression); break; case 'string': $propertyVariable = $compilationContext->symbolTable->getTempVariableForWrite('string', $compilationContext); $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $propertyVariable->getName(), 'operator' => 'assign', 'expr' => $expression['right'])))); $statement->compile($compilationContext); break; default: throw new CompilerException("Variable type: " . $propertyAccess['right']['type'] . " cannot be used as object", $propertyAccess['left']); } /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } /** * Variable that receives a property value must be polymorphic */ if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign property value", $expression); } /** * At this point, we don't know the exact dynamic type fetched from the property */ $symbolVariable->setDynamicTypes('undefined'); $compilationContext->headersManager->add('kernel/object'); $codePrinter->output('zephir_read_property_zval(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $propertyVariable->getName() . ', PH_NOISY_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Resolves the access to a property in an object * * @param array $expression * @param CompilationContext $compilationContext * @return \CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; $propertyAccess = $expression; $expr = new Expression($propertyAccess['left']); $exprVariable = $expr->compile($compilationContext); switch ($exprVariable->getType()) { case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression); switch ($variableVariable->getType()) { case 'variable': break; default: throw new CompilerException("Variable type: " . $variableVariable->getType() . " cannot be used as object", $propertyAccess['left']); } break; default: throw new CompilerException("Cannot use expression: " . $exprVariable->getType() . " as an object", $propertyAccess['left']); } switch ($propertyAccess['right']['type']) { case 'variable': $propertyVariable = $compilationContext->symbolTable->getVariableForRead($propertyAccess['right']['value'], $compilationContext, $expression); break; case 'string': $propertyVariable = null; break; default: throw new CompilerException("Variable type: " . $propertyAccess['right']['type'] . " cannot be used as object", $propertyAccess['left']); } /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } /** * Variable that receives a property value must be polymorphic */ if ($symbolVariable && !$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign property value", $expression); } /** * At this point, we don't know the exact dynamic type fetched from the property */ $symbolVariable->setDynamicTypes('undefined'); $compilationContext->headersManager->add('kernel/object'); $property = $propertyVariable ? $propertyVariable : Utils::addSlashes($expression['right']['value']); $compilationContext->backend->fetchProperty($symbolVariable, $variableVariable, $property, false, $compilationContext, false); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/exception'); $codePrinter = $compilationContext->codePrinter; $statement = $this->_statement; $expr = $statement['expr']; /** * This optimizes throw new Exception("hello") */ if (!$compilationContext->insideTryCatch) { if (isset($expr['class']) && isset($expr['parameters']) && count($expr['parameters']) == 1 && $expr['parameters'][0]['parameter']['type'] == 'string') { $className = Utils::getFullName($expr['class'], $compilationContext->classDefinition->getNamespace(), $compilationContext->aliasManager); if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); $message = $expr['parameters'][0]['parameter']['value']; $class = $classDefinition->getClassEntry(); $this->throwStringException($codePrinter, $class, $message, $statement['expr']); return; } else { if ($compilationContext->compiler->isBundledClass($className)) { $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true); if ($classEntry) { $message = $expr['parameters'][0]['parameter']['value']; $this->throwStringException($codePrinter, $classEntry, $message, $statement['expr']); return; } } } } else { if (in_array($expr['type'], array('string', 'char', 'int', 'double'))) { $class = $compilationContext->classDefinition->getClassEntryByClassName('Exception', $compilationContext); $this->throwStringException($codePrinter, $class, $expr['value'], $expr); return; } } } $throwExpr = new Expression($expr); $resolvedExpr = $throwExpr->compile($compilationContext); if (!in_array($resolvedExpr->getType(), array('variable', 'string'))) { throw new CompilerException("Expression '" . $resolvedExpr->getType() . '" cannot be used as exception', $expr); } $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $expr); if (!in_array($variableVariable->getType(), array('variable', 'string'))) { throw new CompilerException("Variable '" . $variableVariable->getType() . "' cannot be used as exception", $expr); } $variableCode = $compilationContext->backend->getVariableCode($variableVariable); $codePrinter->output('zephir_throw_exception_debug(' . $variableCode . ', "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ' TSRMLS_CC);'); if (!$compilationContext->insideTryCatch) { $codePrinter->output('ZEPHIR_MM_RESTORE();'); $codePrinter->output('return;'); } else { $codePrinter->output('goto try_end_' . $compilationContext->currentTryCatch . ';'); $codePrinter->outputBlankLine(); } if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } }
/** * @param $expression * @param CompilationContext $compilationContext * @return bool|CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'typeof' expression", $expression['left']); } $builder = new FunctionCallBuilder('gettype', array(array('parameter' => $expression['left']))); $expression = new Expression($builder->get()); return $expression->compile($compilationContext); }
/** * @param $expression * @param CompilationContext $compilationContext * @return bool|CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'typeof' expression", $expression['left']); } $functionCall = BuilderFactory::getInstance()->statements()->functionCall('gettype', array($expression['left'])); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/exception'); $codePrinter = $compilationContext->codePrinter; $statement = $this->_statement; $expr = $statement['expr']; /** * This optimizes throw new Exception("hello") */ if (!$compilationContext->insideTryCatch) { if (isset($expr['class'])) { if (isset($expr['parameters']) && count($expr['parameters']) == 1) { if ($expr['parameters'][0]['parameter']['type'] == 'string') { $className = Utils::getFullName($expr['class'], $compilationContext->classDefinition->getNamespace(), $compilationContext->aliasManager); if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); $message = $expr['parameters'][0]['parameter']['value']; $codePrinter->output('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(' . $classDefinition->getClassEntry() . ', "' . Utils::addSlashes($message, true, Types::STRING) . '", "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ');'); $codePrinter->output('return;'); return; } else { if ($compilationContext->compiler->isInternalClass($className)) { $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, true); if ($classEntry) { $message = $expr['parameters'][0]['parameter']['value']; $codePrinter->output('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(' . $classEntry . ', "' . Utils::addSlashes($message, true, Types::STRING) . '", "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ');'); $codePrinter->output('return;'); return; } } } } } } } $throwExpr = new Expression($expr); $resolvedExpr = $throwExpr->compile($compilationContext); if ($resolvedExpr->getType() != 'variable') { throw new CompilerException("Expression '" . $resolvedExpr->getType() . '" cannot be used as exception', $expr); } $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $expr); if ($variableVariable->getType() != 'variable') { throw new CompilerException("Variable '" . $variableVariable->getType() . "' cannot be used as exception", $expr); } $codePrinter->output('zephir_throw_exception_debug(' . $variableVariable->getName() . ', "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ' TSRMLS_CC);'); if (!$compilationContext->insideTryCatch) { $codePrinter->output('ZEPHIR_MM_RESTORE();'); $codePrinter->output('return;'); } else { $codePrinter->output('goto try_end_' . $compilationContext->insideTryCatch . ';'); $codePrinter->outputBlankLine(); } if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } }
/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'irange' expression", $expression['left']); } if (!isset($expression['right'])) { throw new CompilerException("Invalid 'right' operand for 'irange' expression", $expression['right']); } $builder = new FunctionCallBuilder('range', array(array('parameter' => $expression['left']), array('parameter' => $expression['right']))); /** * Implicit type coercing */ $castBuilder = new CastOperatorBuilder('array', $builder); $expression = new Expression($castBuilder->get()); $expression->setReadOnly($this->_readOnly); return $expression->compile($compilationContext); }
/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'irange' expression", $expression['left']); } if (!isset($expression['right'])) { throw new CompilerException("Invalid 'right' operand for 'irange' expression", $expression['right']); } $exprBuilder = Expression\Builder\BuilderFactory::getInstance(); /** * Implicit type coercing */ $castBuilder = $exprBuilder->operators()->cast(Types::ARRAY_, $exprBuilder->statements()->functionCall('range', array($expression['left'], $expression['right']))); $expression = new Expression($castBuilder->build()); $expression->setReadOnly($this->_readOnly); return $expression->compile($compilationContext); }
/** * Intercepts calls to built-in methods * * @param string $methodName * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @throws CompilerException * @return bool|\Zephir\CompiledExpression */ public function invokeMethod($methodName, $caller, CompilationContext $compilationContext, Call $call, array $expression) { if (method_exists($this, $methodName)) { return $this->{$methodName}($caller, $compilationContext, $call, $expression); } if (isset($this->methodMap[$methodName])) { if (isset($expression['parameters'])) { $parameters = $expression['parameters']; array_unshift($parameters, array('parameter' => $caller)); } else { $parameters = array(array('parameter' => $caller)); } $builder = new FunctionCallBuilder($this->methodMap[$methodName], $parameters, FunctionCall::CALL_NORMAL, $expression['file'], $expression['line'], $expression['char']); $expression = new Expression($builder->get()); return $expression->compile($compilationContext); } throw new CompilerException(sprintf('Method "%s" is not a built-in method of type "%s"', $methodName, $this->getTypeName()), $expression); }
/** * * @param array $expression * @param \CompilationContext $compilationContext * @return \CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/operators'); if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'empty' expression", $expression['left']); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly(true); $left = $leftExpr->compile($compilationContext); if ($left->getType() != 'variable') { throw new CompilerException("'empty' operand only can be a variable", $expression['left']); } $variableLeft = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression['left']); if ($variableLeft->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in 'empty' operators", $expression['left']); } return new CompiledExpression('bool', 'ZEPHIR_IS_EMPTY(' . $variableLeft->getName() . ')', $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/array'); $expression = $this->_statement['expr']; $flags = 'PH_SEPARATE'; if ($expression['type'] == 'list') { $expression = $expression['left']; } switch ($expression['type']) { case 'array-access': $expr = new Expression($expression['left']); $expr->setReadOnly(true); $exprVar = $expr->compile($compilationContext); $variable = $compilationContext->symbolTable->getVariableForWrite($exprVar->getCode(), $compilationContext, $this->_statement); $expr = new Expression($expression['right']); $expr->setReadOnly(true); $exprIndex = $expr->compile($compilationContext); break; case 'property-access': $expr = new Expression($expression['left']); $expr->setReadOnly(true); $exprVar = $expr->compile($compilationContext); $variable = $compilationContext->symbolTable->getVariableForWrite($exprVar->getCode(), $compilationContext, $this->_statement); $variableCode = $compilationContext->backend->getVariableCode($variable); $compilationContext->headersManager->add('kernel/object'); $compilationContext->codePrinter->output('zephir_unset_property(' . $variableCode . ', "' . $expression['right']['value'] . '" TSRMLS_CC);'); return true; case 'property-dynamic-access': //@todo fix it //@todo fix it default: throw new CompilerException('Cannot use expression type: ' . $expression['type'] . ' in "unset"', $expression); } if (!in_array($variable->getType(), array('variable', 'array'))) { throw new CompilerException('Cannot use variable type: ' . $variable->gettype() . ' in "unset"', $expression['left']); } if ($variable->hasDifferentDynamicType(array('undefined', 'array', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non array/object in unset operator', 'non-valid-unset', $expression['left']); } $compilationContext->backend->arrayUnset($variable, $exprIndex, $flags, $compilationContext); }
/** * Intercepts calls to built-in methods * * @param string $methodName * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @throws CompilerException * @return bool|\Zephir\CompiledExpression */ public function invokeMethod($methodName, $caller, CompilationContext $compilationContext, Call $call, array $expression) { /** * Checks first whether the method exist in the array type definition */ if (method_exists($this, $methodName)) { return $this->{$methodName}($caller, $compilationContext, $call, $expression); } /** * Check the method map */ if (isset($this->methodMap[$methodName])) { $paramNumber = $this->getNumberParam($methodName); if ($paramNumber == 0) { if (isset($expression['parameters'])) { $parameters = $expression['parameters']; array_unshift($parameters, array('parameter' => $caller)); } else { $parameters = array(array('parameter' => $caller)); } } else { if (isset($expression['parameters'])) { $parameters = array(); foreach ($expression['parameters'] as $number => $parameter) { if ($number == $paramNumber) { $parameters[] = null; } $parameters[] = $parameter; } $parameters[$paramNumber] = array('parameter' => $caller); } else { $parameters = array(array('parameter' => $caller)); } } $builder = new FunctionCallBuilder($this->methodMap[$methodName], $parameters, FunctionCall::CALL_NORMAL, $expression['file'], $expression['line'], $expression['char']); $expression = new Expression($builder->get()); return $expression->compile($compilationContext); } throw new CompilerException(sprintf('Method "%s" is not a built-in method of type "%s"', $methodName, $this->getTypeName()), $expression); }
public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'unlikely' expression", $expression['left']); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly(true); $left = $leftExpr->compile($compilationContext); if ($left->getType() == 'bool') { return new CompiledExpression('bool', 'unlikely(' . $left->getCode() . ')', $expression); } if ($left->getType() == 'variable') { $variable = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression['left']); switch ($variable->getType()) { case 'bool': return new CompiledExpression('bool', 'unlikely(' . $variable->getName() . ')', $expression); default: return new CompiledExpression('bool', 'unlikely(zephir_is_true(' . $variable->getName() . '))', $expression); } } throw new CompilerException("Cannot use expression type: '" . $left->getType() . "' in 'unlikely' operator", $expression['left']); }
/** * Compile expression * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException * @throws Exception */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new \Exception("Missing left part of the expression"); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); switch ($left->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression($left->getType(), '-' . $left->getCode(), $expression); case 'variable': $variable = $compilationContext->symbolTable->getVariable($left->getCode()); switch ($variable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression($variable->getType(), '-' . $variable->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->codePrinter->output('zephir_negate(' . $compilationContext->backend->getVariableCode($variable) . ' TSRMLS_CC);'); return new CompiledExpression('variable', $variable->getName(), $expression); default: throw new CompilerException("Cannot operate minus with variable of '" . $left->getType() . "' type"); } break; default: throw new CompilerException("Cannot operate minus with '" . $left->getType() . "' type"); } }
/** * @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; }
/** * 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(); }
/** * Compiles a reference to a value * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " to store a reference", $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); switch ($left->getType()) { case 'variable': case 'string': case 'object': case 'array': case 'callable': break; default: throw new CompilerException("Cannot obtain a reference from type: " . $left->getType(), $expression); } $leftVariable = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression); switch ($leftVariable->getType()) { case 'variable': case 'string': case 'object': case 'array': case 'callable': break; default: throw new CompilerException("Cannot obtain reference from variable type: " . $leftVariable->getType(), $expression); } $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable->increaseVariantIfNull(); $compilationContext->codePrinter->output('ZEPHIR_MAKE_REFERENCE(' . $symbolVariable->getName() . ', ' . $leftVariable->getName() . ');'); return new CompiledExpression('reference', $symbolVariable->getRealName(), $expression); }
/** * Compiles a type cast operation * * @param $expression * @param CompilationContext $compilationContext * @return bool|CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { $expr = new Expression($expression['right']); $resolved = $expr->compile($compilationContext); switch ($expression['left']) { case 'int': switch ($resolved->getType()) { case 'null': return new CompiledExpression('int', 0, $expression); case 'int': return new CompiledExpression('int', $resolved->getCode(), $expression); case 'double': return new CompiledExpression('int', '(int) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('int', $resolved->getBooleanCode(), $expression); case 'string': $compilationContext->headersManager->add('kernel/operators'); /** * zephir_get_intval_ex use zval variable * before use with it we create a new variable and assign value of literal * * @todo Optimize by creating native function for string without zval using */ $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('string', $compilationContext); $let = new LetVariable(); $original = $resolved->getOriginal(); $original['operator'] = 'assign'; $let->assign($symbolVariable->getName(), $symbolVariable, $resolved, new ReadDetector(), $compilationContext, $original); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval_ex(' . $symbol . ')', $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval(' . $symbol . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('int', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('int', '(int) (' . $symbolVariable->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '(int) (' . $symbolVariable->getName() . ')', $expression); case 'array': case 'variable': case 'string': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'long': switch ($resolved->getType()) { case 'int': return new CompiledExpression('long', $resolved->getCode(), $expression); case 'double': return new CompiledExpression('long', '(long) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('long', $resolved->getBooleanCode(), $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); return new CompiledExpression('long', 'zephir_get_intval(' . $symbolVariable->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('long', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('long', '(long) (' . $symbolVariable->getName() . ')', $expression); case 'variable': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('long', 'zephir_get_intval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'double': switch ($resolved->getType()) { case 'null': return new CompiledExpression('double', 0, $expression); case 'bool': return new CompiledExpression('double', $resolved->getBooleanCode(), $expression); case 'double': return new CompiledExpression('double', $resolved->getCode(), $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('double', 'zephir_get_doubleval(' . $symbol . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('double', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('double', '(double) (' . $symbolVariable->getName() . ')', $expression); case 'bool': return new CompiledExpression('double', '(double) (' . $symbolVariable->getName() . ')', $expression); case 'array': case 'variable': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('double', 'zephir_get_doubleval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'bool': switch ($resolved->getType()) { case 'int': return new CompiledExpression('bool', '(zend_bool) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('bool', $resolved->getCode(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } switch ($symbolVariable->getType()) { case 'variable': return new CompiledExpression('bool', 'zephir_get_boolval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'char': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('char', $compilationContext); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $variableCode = $compilationContext->backend->getVariableCode($symbolVariable); $compilationContext->codePrinter->output($tempVariable->getName() . ' = (char) zephir_get_intval(' . $variableCode . ');'); return new CompiledExpression('variable', $tempVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'string': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('string', $compilationContext); $symbolVariable->setMustInitNull(true); $symbolVariable->setIsInitialized(true, $compilationContext); $symbolVariable->increaseUses(); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); $resolvedVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext); $resolvedCode = $compilationContext->backend->getVariableCode($resolvedVariable); $compilationContext->codePrinter->output('zephir_get_strval(' . $symbol . ', ' . $resolvedCode . ');'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'array': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('array', $compilationContext, $expression); $symbolVariable->setMustInitNull(true); $symbolVariable->setIsInitialized(true, $compilationContext, $expression); $symbolVariable->increaseUses(); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); $resolvedVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext); $resolvedCode = $compilationContext->backend->getVariableCode($resolvedVariable); $compilationContext->codePrinter->output('zephir_get_arrval(' . $symbol . ', ' . $resolvedCode . ');'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'object': switch ($resolved->getType()) { case 'int': case 'double': case 'bool': case 'null': case 'string': case 'array': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); /** * zephir_convert_to_object use zval variable * before use with it we create a new variable and assign value of literal */ $let = new LetVariable(); $original = $resolved->getOriginal(); $original['operator'] = 'assign'; $let->assign($symbolVariable->getName(), $symbolVariable, $resolved, new ReadDetector(), $compilationContext, $original); $compilationContext->codePrinter->output('zephir_convert_to_object(' . $symbol . ');'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } $compilationContext->codePrinter->output('zephir_convert_to_object(' . $symbol . ');'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } }
/** * Compiles x::y[a][b][] = {expr} (multiple offset assignment) * * @param string $variable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ protected function _assignStaticPropertyArrayMultipleIndex($classEntry, $property, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Create a temporal zval (if needed) */ $variableExpr = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); /** * Only string/variable/int */ $offsetExprs = array(); foreach ($statement['index-expr'] as $indexExpr) { $indexExpression = new Expression($indexExpr); $resolvedIndex = $indexExpression->compile($compilationContext); switch ($resolvedIndex->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Expression: " . $resolvedIndex->getType() . " cannot be used as index without cast", $statement['index-expr']); } $offsetExprs[] = $resolvedIndex; } $keys = ''; $numberParams = 1; $offsetItems = array(); foreach ($offsetExprs as $offsetExpr) { switch ($offsetExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $keys .= 'l'; $offsetItems[] = $offsetExpr->getCode(); $numberParams++; break; case 'string': $keys .= 's'; $offsetItems[] = 'SL("' . $offsetExpr->getCode() . '")'; $numberParams += 2; break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($offsetExpr->getCode(), $compilationContext, $statement); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $keys .= 'l'; $offsetItems[] = $variableIndex->getName(); $numberParams++; break; case 'string': case 'variable': $keys .= 'z'; $offsetItems[] = $variableIndex->getName(); $numberParams++; break; default: throw new CompilerException("Variable: " . $variableIndex->getType() . " cannot be used as array index", $statement); } break; default: throw new CompilerException("Value: " . $offsetExpr->getType() . " cannot be used as array index", $statement); } } $codePrinter->output('zephir_update_static_property_array_multi_ce(' . $classEntry . ', SL("' . $property . '"), &' . $variableExpr->getName() . ' TSRMLS_CC, SL("' . $keys . 'a"), ' . $numberParams . ', ' . join(', ', $offsetItems) . ');'); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } }
/** * 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); }
/** * Optimizes expressions * * @param $exprRaw * @param CompilationContext $compilationContext * @return bool|string * @throws CompilerException */ public function optimize($exprRaw, CompilationContext $compilationContext) { $conditions = $this->optimizeNot($exprRaw, $compilationContext); if ($conditions !== false) { return $conditions; } /** * Discard first level parentheses */ if ($exprRaw['type'] == 'list') { $expr = new Expression($exprRaw['left']); } else { $expr = new Expression($exprRaw); } $expr->setReadOnly(true); $expr->setEvalMode(true); $compiledExpression = $expr->compile($compilationContext); /** * Possible corrupted expression? */ if (!is_object($compiledExpression)) { throw new CompilerException('Corrupted expression: ' . $exprRaw['type'], $exprRaw); } /** * Generate the condition according to the value returned by the evaluated expression */ switch ($compiledExpression->getType()) { case 'null': $this->_unreachable = true; return '0'; case 'int': case 'uint': case 'long': case 'ulong': case 'double': $code = $compiledExpression->getCode(); if (is_numeric($code)) { if ($code == '1') { $this->_unreachableElse = true; } else { $this->_unreachable = true; } } return $code; case 'char': case 'uchar': return $compiledExpression->getCode(); case 'bool': $code = $compiledExpression->getBooleanCode(); if ($code == '1') { $this->_unreachableElse = true; } else { if ($code == '0') { $this->_unreachable = true; } } return $code; case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($compiledExpression->getCode(), $compilationContext, $exprRaw); $possibleValue = $variableRight->getPossibleValue(); if (is_object($possibleValue)) { $possibleValueBranch = $variableRight->getPossibleValueBranch(); if ($possibleValueBranch instanceof Branch) { /** * Check if the possible value was assigned in the root branch */ if ($possibleValueBranch->getType() == Branch::TYPE_ROOT) { if ($possibleValue instanceof LiteralCompiledExpression) { switch ($possibleValue->getType()) { case 'null': $this->_unreachable = true; break; case 'bool': if ($possibleValue->getBooleanCode() == '0') { $this->_unreachable = true; } else { $this->_unreachableElse = true; } break; case 'int': if (!intval($possibleValue->getCode())) { $this->_unreachable = true; } else { $this->_unreachableElse = true; } break; case 'double': if (!floatval($possibleValue->getCode())) { $this->_unreachable = true; } else { $this->_unreachableElse = true; } break; default: //echo $possibleValue->getType(); } } } } } $this->_usedVariables[] = $variableRight->getName(); /** * Evaluate the variable */ switch ($variableRight->getType()) { case 'int': case 'uint': case 'char': case 'uchar': case 'long': case 'ulong': return $variableRight->getName(); case 'string': return $variableRight->getName() . ' && Z_STRLEN_P(' . $variableRight->getName() . ')'; case 'bool': return $variableRight->getName(); case 'double': return $variableRight->getName(); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return 'zephir_is_true(&' . $variableRight->getName() . ')'; } else { return 'zephir_is_true(' . $variableRight->getName() . ')'; } break; default: throw new CompilerException("Variable can't be evaluated " . $variableRight->getType(), $exprRaw); } break; default: throw new CompilerException("Expression " . $compiledExpression->getType() . " can't be evaluated", $exprRaw); } }