/** * 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); }
/** * @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); }
/** * 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"); } }
/** * 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 $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); } }
/** * @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 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); }
/** * @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 */ 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); }
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 array $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compileTypeHint($expression, CompilationContext $compilationContext) { $expr = new Expression($expression['right']); $expr->setReadOnly(true); $resolved = $expr->compile($compilationContext); if ($resolved->getType() != 'variable') { throw new CompilerException("Type-Hints only can be applied to dynamic variables", $expression); } $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("Type-Hints only can be applied to dynamic variables", $expression); } $symbolVariable->setDynamicTypes('object'); $symbolVariable->setClassTypes($compilationContext->getFullName($expression['left']['value'])); return $resolved; }
/** * 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); }
/** * 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); } }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; /** * @TODO implement optimizers here */ if ($exprRaw['type'] == 'fcall') { if ($exprRaw['name'] == 'range') { $status = $this->compileRange($exprRaw, $compilationContext); if ($status !== false) { return; } } if ($exprRaw['name'] == 'iterator') { $status = $this->compileIterator($exprRaw, $compilationContext); if ($status !== false) { return; } } } $expr = new Expression($exprRaw); $expr->setReadOnly(true); $expression = $expr->compile($compilationContext); /** * Check for traversing a constant string */ if ($expression->getType() == 'string') { $this->compileStringTraverse($expression, $compilationContext, null); return; } if ($expression->getType() != 'variable' && $expression->getType() != 'array') { throw new CompilerException("Unknown type: " . $expression->getType(), $exprRaw); } $exprVariable = $compilationContext->symbolTable->getVariableForRead($expression->getCode(), $compilationContext, $this->_statement['expr']); switch ($exprVariable->getType()) { case 'variable': case 'array': $this->compileHashTraverse($expression, $compilationContext, $exprVariable); break; case 'string': $this->compileStringTraverse($expression, $compilationContext, $exprVariable); break; default: throw new CompilerException("Cannot traverse value type: " . $exprVariable->getType(), $exprRaw); } }
/** * @param array $expression * @param CompilationContext $compilationContext */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Missing left part of the expression", $expression); } if (!isset($expression['right'])) { throw new CompilerException("Missing right part of the expression", $expression); } /** * Check for constant folding optimizations */ switch ($this->_operator) { case '&': case '|': case '^': case '<<': case '>>': $optimized = $this->optimizeConstantFolding($expression, $compilationContext); if (is_object($optimized)) { return $optimized; } break; } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly(true); $left = $leftExpr->compile($compilationContext); $rightExpr = new Expression($expression['right']); $rightExpr->setReadOnly(true); $right = $rightExpr->compile($compilationContext); switch ($left->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'double': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' (int) (' . $right->getCode() . '))', $expression); case 'bool': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' zephir_get_numberval(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' zephir_get_numberval(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate 'int' with '" . $right->getType() . "'", $expression); } break; case 'bool': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression('int', '(' . $left->getBooleanCode() . ' ' . $this->_bitOperator . '((' . $right->getCode() . ') ? 1 : 0))', $expression); case 'bool': return new CompiledExpression('int', '(' . $left->getBooleanCode() . ' ' . $this->_bitOperator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($expression['right']['value'], $compilationContext, $expression); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '((int) (' . $left->getBooleanCode() . ') ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '((int) (' . $left->getBooleanCode() . ') ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '((int) (' . $left->getBooleanCode() . ') ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '((int) (' . $left->getBooleanCode() . ') ' . $this->_operator . ' zephir_get_numberval(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('int', '((int) (' . $left->getBooleanCode() . ') ' . $this->_operator . ' zephir_get_numberval(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot operate ('bool') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate 'bool' with '" . $right->getType() . "'", $expression); } break; case 'double': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'double': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' (int) (' . $right->getCode() . '))', $expression); case 'bool': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($expression['right']['value'], $compilationContext, $expression); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' zephir_get_numberval(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' zephir_get_numberval(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot operate variable('double') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate 'double' with '" . $right->getType() . "'", $expression); } break; case 'string': switch ($right->getType()) { default: throw new CompilerException("Operation is not supported between strings", $expression); } break; case 'variable': $variableLeft = $compilationContext->symbolTable->getVariableForRead($left->resolve(null, $compilationContext), $compilationContext, $expression); switch ($variableLeft->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': case 'char': case 'uchar': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' (int) (zephir_get_numberval(&' . $variableRight->getName() . ')))', $expression); } else { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' (int) (zephir_get_numberval(' . $variableRight->getName() . ')))', $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with '" . $right->getType() . "'", $expression); } break; case 'bool': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'bool': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_bitOperator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'bool': return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_bitOperator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_get_numberval(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_get_numberval(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with '" . $right->getType() . "'", $expression); } break; case 'double': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'double': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_operator . ' (int) (' . $right->getCode() . '))', $expression); case 'bool': return new CompiledExpression('int', '((int) (' . $left->getCode() . ') ' . $this->_bitOperator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($expression['right']['value'], $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('int', '((int) (' . $variableLeft->getName() . ') ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('int', '((int) (' . $variableLeft->getName() . ') ' . $this->_operator . ' (int) (' . $variableRight->getName() . '))', $expression); case 'bool': return new CompiledExpression('int', '((int) (' . $variableLeft->getName() . ') ' . $this->_bitOperator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '((int) (' . $variableLeft->getName() . ') ' . $this->_operator . ' (int) (zephir_get_numberval(&' . $variableRight->getName() . ')))', $expression); } else { return new CompiledExpression('int', '((int) (' . $variableLeft->getName() . ') ' . $this->_operator . ' (int) (zephir_get_numberval(' . $variableRight->getName() . ')))', $expression); } break; default: throw new CompilerException("Cannot operate variable('double') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate variable('int') with '" . $right->getType() . "'", $expression); } break; case 'string': throw new CompilerException("Cannot operate string variables'", $expression); case 'variable': switch ($right->getType()) { /* a + 1 */ case 'int': case 'uint': case 'long': case 'ulong': case 'double': $compilationContext->headersManager->add('kernel/operators'); $op = $this->_operator; if ($variableLeft->isLocalOnly()) { $op1 = '&' . $variableLeft->getName(); } else { $op1 = $variableLeft->getName(); } $op2 = $right->getCode(); if ($right->getType() == 'double') { return new CompiledExpression('int', '((int) (zephir_get_numberval(' . $op1 . ')) ' . $op . ' (int) (' . $op2 . '))', $expression); } else { return new CompiledExpression('int', '((int) (zephir_get_numberval(' . $op1 . ')) ' . $op . ' ' . $op2 . ')', $expression); } break; /* a(var) + a(x) */ /* a(var) + a(x) */ case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->resolve(null, $compilationContext), $compilationContext, $expression); switch ($variableRight->getType()) { /* a(var) + a(int) */ case 'int': case 'uint': case 'long': case 'ulong': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { return new CompiledExpression('int', '((int) (zephir_get_numberval(&' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); } else { return new CompiledExpression('int', '((int) (zephir_get_numberval(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); } break; /* a(var) + a(bool) */ /* a(var) + a(bool) */ case 'bool': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { return new CompiledExpression('int', '((int) (zephir_get_numberval(&' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); } else { return new CompiledExpression('int', '((int) (zephir_get_numberval(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); } break; /* a(var) + a(var) */ /* a(var) + a(var) */ case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { $op1 = '&' . $variableLeft->getName(); } else { $op1 = $variableLeft->getName(); } if ($variableRight->isLocalOnly()) { $op2 = '&' . $variableRight->getName(); } else { $op2 = $variableRight->getName(); } $expected = $this->getExpected($compilationContext, $expression); if ($expected->isLocalOnly()) { $compilationContext->codePrinter->output($this->_zvalOperator . '(&' . $expected->getName() . ', ' . $op1 . ', ' . $op2 . ' TSRMLS_CC);'); } else { $compilationContext->codePrinter->output($this->_zvalOperator . '(' . $expected->getName() . ', ' . $op1 . ', ' . $op2 . ' TSRMLS_CC);'); } if ($variableLeft->isTemporal()) { $variableLeft->setIdle(true); } if ($variableRight->isTemporal()) { $variableRight->setIdle(true); } return new CompiledExpression('variable', $expected->getName(), $expression); default: throw new CompilerException("Cannot operate 'variable' with variable ('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot operate 'variable' with '" . $right->getType() . "'", $expression); } break; default: throw new CompilerException("Unknown '" . $variableLeft->getType() . "'", $expression); } break; default: throw new CompilerException("Unsupported type: " . $left->getType(), $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); $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); $compilationContext->headersManager->add('kernel/object'); if ($exprVar->getCode() == 'this') { $compilationContext->codePrinter->output('zephir_unset_property(this_ptr, "' . $expression['right']['value'] . '" TSRMLS_CC);'); } else { $compilationContext->codePrinter->output('zephir_unset_property(' . $exprVar->getCode() . ', "' . $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); } $variable = $compilationContext->symbolTable->getVariableForWrite($exprVar->getCode(), $compilationContext, $this->_statement); 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']); } switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $compilationContext->codePrinter->output('zephir_array_unset_long(&' . $variable->getName() . ', ' . $exprIndex->getCode() . ', ' . $flags . ');'); break; case 'string': $compilationContext->codePrinter->output('zephir_array_unset_string(&' . $variable->getName() . ', SS("' . $exprIndex->getCode() . '"), ' . $flags . ');'); break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $exprIndex->getOriginal()); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $compilationContext->codePrinter->output('zephir_array_unset_long(&' . $variable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ');'); break; case 'string': case 'variable': $compilationContext->headersManager->add('kernel/array'); $compilationContext->codePrinter->output('zephir_array_unset(&' . $variable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ');'); break; default: throw new CompilerException("Variable type: " . $variableIndex->getType() . " cannot be used as array index without cast", $expression['right']); } break; default: throw new CompilerException("Cannot use expression: " . $exprIndex->getType() . " as array index without cast", $expression['right']); } }
/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/array'); $variable = $compilationContext->symbolTable->getVariableForWrite($expression['left']['value'], $compilationContext, $expression['left']); if ($variable->getType() != 'variable') { throw new CompilerException('Cannot use variable type: ' . $variable->gettype() . ' in "fetch" operator', $expression); } /** * return_value must not be observed */ if ($variable->getName() != 'return_value') { /* * @todo use a read detector here */ $readOnly = false; $line = max($compilationContext->symbolTable->getLastCallLine(), $compilationContext->symbolTable->getLastUnsetLine()); if ($line === false || $line > 0 && $line < $expression['line']) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($variable->getName()); if ($numberMutations == 1) { if ($variable->getNumberMutations() == 1) { $variable->setIsInitialized(true, $compilationContext, $expression); $variable->setMemoryTracked(false); $variable->setDynamicTypes('undefined'); $readOnly = true; } } } if (!$readOnly || $expression['right']['type'] != 'array-access') { $variable->setIsInitialized(true, $compilationContext, $expression); $variable->observeVariant($compilationContext); $variable->setDynamicTypes('undefined'); $variable->setPossibleValue(new CompiledExpression('undefined', '', $expression), $compilationContext); } } else { $variable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } if ($readOnly) { $flags = '1'; } else { $flags = '0'; } switch ($expression['right']['type']) { case 'array-access': $exprVariable = new Expression($expression['right']['left']); $exprVariable->setReadOnly(true); $exprVariable->setNoisy(false); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as array", $expression['right']['left']); } $evalVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression['right']['left']); if ($evalVariable->getType() != 'variable' && $evalVariable->getType() != 'array') { throw new CompilerException("Variable type: " . $variable->getType() . " cannot be used as array", $expression['right']['left']); } if ($evalVariable->getType() == 'variable') { if ($evalVariable->hasDifferentDynamicType(array('undefined', 'array', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non array in fetch operator', 'non-valid-fetch', $expression['right']); } } $expr = new Expression($expression['right']['right']); $expr->setReadOnly(true); $expr->setNoisy(false); $resolvedExpr = $expr->compile($compilationContext); return $compilationContext->backend->arrayIssetFetch($variable, $evalVariable, $resolvedExpr, $flags, $expression, $compilationContext); break; case 'property-access': $exprVariable = new Expression($expression['right']['left']); $exprVariable->setReadOnly(true); $exprVariable->setNoisy(false); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as object", $expression['right']['left']); } $evalVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression['right']['left']); if ($evalVariable->getType() != 'variable') { throw new CompilerException("Variable type: " . $variable->getType() . " cannot be used as object", $expression['right']['left']); } if ($evalVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non object in fetch operator', 'non-valid-fetch', $expression['right']); } $property = $expression['right']['right']['value']; $compilationContext->headersManager->add('kernel/object'); $symbol = $compilationContext->backend->getVariableCodePointer($variable); $evalSymbol = $compilationContext->backend->getVariableCode($evalVariable); return new CompiledExpression('bool', 'zephir_fetch_property(' . $symbol . ', ' . $evalSymbol . ', SL("' . $property . '"), PH_SILENT_CC)', $expression); case 'property-dynamic-access': $exprVariable = new Expression($expression['right']['left']); $exprVariable->setReadOnly(true); $exprVariable->setNoisy(false); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as object", $expression['right']['left']); } $evalVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression['right']['left']); if ($evalVariable->getType() != 'variable') { throw new CompilerException("Variable type: " . $evalVariable->getType() . " cannot be used as object", $expression['right']['left']); } if ($evalVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non object in fetch operator', 'non-valid-fetch', $expression['right']); } $exprVariableProperty = new Expression($expression['right']['right']); $exprVariableProperty->setReadOnly(true); $exprCompiledVariableProperty = $exprVariableProperty->compile($compilationContext); if ($exprCompiledVariableProperty->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariableProperty->getType() . " cannot be used in property-dynamic-access", $expression['right']['right']); } $evalVariableProperty = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariableProperty->getCode(), $compilationContext, $expression['right']['right']); if ($evalVariableProperty->getType() != 'variable' && $evalVariableProperty->getType() != 'string') { throw new CompilerException("Variable type: " . $evalVariableProperty->getType() . " cannot be used in property-dynamic-access", $expression['right']['right']); } $compilationContext->headersManager->add('kernel/object'); $symbol = $compilationContext->backend->getVariableCodePointer($variable); $evalSymbol = $compilationContext->backend->getVariableCode($evalVariable); $evalPropertySymbol = $compilationContext->backend->getVariableCode($evalVariableProperty); return new CompiledExpression('bool', 'zephir_fetch_property_zval(' . $symbol . ', ' . $evalSymbol . ', ' . $evalPropertySymbol . ', PH_SILENT_CC)', $expression); default: throw new CompilerException('Cannot use this expression for "fetch" operators: ' . $expression['right']['type'], $expression); } }
/** * Compiles an 'isset' operator * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { if ($expression['left']['type'] == 'list') { $left = $expression['left']['left']; } else { $left = $expression['left']; } switch ($left['type']) { case 'array-access': $compilationContext->headersManager->add('kernel/array'); $exprVariable = new Expression($left['left']); $exprVariable->setReadOnly(true); $exprVariable->setNoisy(false); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable' && $exprCompiledVariable->getType() != 'array') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as array", $left['left']); } $variable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $left['left']); switch ($variable->getType()) { case 'array': case 'variable': break; default: throw new CompilerException("Variable type: " . $variable->getType() . " cannot be used as array", $left['left']); break; } if ($variable->getType() == 'variable') { if ($variable->hasDifferentDynamicType(array('undefined', 'array', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non array in isset operator', 'non-valid-isset', $expression); } } $expr = new Expression($left['right']); $expr->setReadOnly(true); $expr->setNoisy(false); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': case 'long': case 'string': return $compilationContext->backend->arrayIsset($variable, $resolvedExpr, $left['right'], $compilationContext); case 'variable': $indexVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $left['right']); return $compilationContext->backend->arrayIsset($variable, $indexVariable, $left['right'], $compilationContext); break; default: throw new CompilerException('[' . $left['right']['type'] . ']', $expression); } break; case 'property-access': case 'property-dynamic-access': $compilationContext->headersManager->add('kernel/object'); $exprVariable = new Expression($left['left']); $exprVariable->setReadOnly(true); $exprCompiledVariable = $exprVariable->compile($compilationContext); if ($exprCompiledVariable->getType() != 'variable') { throw new CompilerException("Expression type: " . $exprCompiledVariable->getType() . " cannot be used as object", $left['left']); } $variable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $left['left']); if ($variable->getType() != 'variable') { throw new CompilerException("Variable type: " . $variable->getType() . " cannot be used as object", $left['left']); } if ($variable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non object in isset operator', 'non-valid-isset', $expression); } $variableCode = $compilationContext->backend->getVariableCode($variable); if ($left['type'] == 'property-access') { return $compilationContext->backend->propertyIsset($variable, $left['right']['value'], $compilationContext); } $expr = new Expression($left['right']); $expr->setReadOnly(true); $expr->setNoisy(false); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'variable': $indexVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $left['right']); switch ($indexVariable->getType()) { case 'variable': case 'string': $indexVariableCode = $compilationContext->backend->getVariableCode($indexVariable); return new CompiledExpression('bool', 'zephir_isset_property_zval(' . $variableCode . ', ' . $indexVariableCode . ' TSRMLS_CC)', $left['right']); default: throw new CompilerException('[' . $indexVariable->getType() . ']', $expression); } break; default: throw new CompilerException('[' . $expression['left']['right']['type'] . ']', $expression); } break; case 'property-string-access': return new CompiledExpression('bool', '(0 == 0)', $left); case 'static-property-access': return new CompiledExpression('bool', '(0 == 0)', $left); default: throw new CompilerException('This expression is not valid for "isset" operator', $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']); $expr->setReadOnly(true); $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']); } $property = $propertyAccess['right']['value']; $propertyDefinition = null; $classDefinition = null; $currentClassDefinition = $compilationContext->classDefinition; /** * If the property is accessed on 'this', we check if the method does exist */ if ($variableVariable->getRealName() == 'this') { $classDefinition = $currentClassDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $expression); } $propertyDefinition = $classDefinition->getProperty($property); } else { /** * If we know the class related to a variable we could check if the property * is defined on that class */ if ($variableVariable->hasAnyDynamicType('object')) { $classType = current($variableVariable->getClassTypes()); $compiler = $compilationContext->compiler; if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); if (!$classDefinition) { throw new CompilerException("Cannot locate class definition for class: " . $classType, $expression); } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classType . "' does not have a property called: '" . $property . "'", $expression); } $propertyDefinition = $classDefinition->getProperty($property); } } } /** * Having a proper propertyDefinition we can check if the property is readable * according to its modifiers */ if ($propertyDefinition) { if ($propertyDefinition->isStatic()) { throw new CompilerException("Attempt to access static property '" . $property . "' as non static", $expression); } if (!$propertyDefinition->isPublic()) { /** * Protected variables only can be read in the class context * where they were declared */ if ($classDefinition == $currentClassDefinition) { if ($propertyDefinition->isPrivate()) { $declarationDefinition = $propertyDefinition->getClassDefinition(); if ($declarationDefinition != $currentClassDefinition) { throw new CompilerException("Attempt to access private property '" . $property . "' outside of its declared class context: '" . $declarationDefinition->getCompleteName() . "'", $expression); } } } else { if ($propertyDefinition->isProtected()) { } else { if ($propertyDefinition->isPrivate()) { $declarationDefinition = $propertyDefinition->getClassDefinition(); if ($declarationDefinition != $currentClassDefinition) { throw new CompilerException("Attempt to access private property '" . $property . "' outside of its declared class context: '" . $declarationDefinition->getCompleteName() . "'", $expression); } } } } } } /** * Resolves the symbol that expects the value */ $readOnly = false; if ($classDefinition == $currentClassDefinition && $this->_readOnly) { if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ if ($symbolVariable->getName() != 'return_value') { $line = $compilationContext->symbolTable->getLastCallLine(); if ($line === false || $line > 0 && $line < $expression['line']) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName()); if ($numberMutations == 1) { if ($symbolVariable->getNumberMutations() == $numberMutations) { $symbolVariable->setMemoryTracked(false); $readOnly = true; } } } } /** * Variable is not read only or it wasn't promoted */ if (!$readOnly) { if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); $this->_readOnly = false; } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } } $this->_readOnly = false; } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } } else { if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ if ($symbolVariable->getName() != 'return_value') { $line = $compilationContext->symbolTable->getLastCallLine(); if ($line === false || $line > 0 && $line < $expression['line']) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName()); if ($numberMutations == 1) { if ($symbolVariable->getNumberMutations() == $numberMutations) { $symbolVariable->setMemoryTracked(false); $readOnly = true; } } } } /** * Variable is not read only or it wasn't promoted */ if (!$readOnly) { if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); $this->_readOnly = false; } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } } 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->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'); if ($classDefinition == $currentClassDefinition) { if ($this->_readOnly || $readOnly) { $codePrinter->output($symbolVariable->getName() . ' = zephir_fetch_nproperty_this(' . $variableVariable->getName() . ', SL("' . $property . '"), PH_NOISY_CC);'); } else { $codePrinter->output('zephir_read_property_this(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', SL("' . $property . '"), PH_NOISY_CC);'); } } else { $codePrinter->output('zephir_read_property(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', SL("' . $property . '"), PH_NOISY_CC);'); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Compiles a method call * * @param Expression $expr * @param CompilationContext $compilationContext */ public function compile(Expression $expr, CompilationContext $compilationContext) { $expression = $expr->getExpression(); $exprVariable = new Expression($expression['variable']); $exprVariable->setReadOnly(true); $exprCompiledVariable = $exprVariable->compile($compilationContext); $builtInType = false; switch ($exprCompiledVariable->getType()) { case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprCompiledVariable->getCode(), $compilationContext, $expression); switch ($variableVariable->getType()) { case 'variable': $caller = $variableVariable; break; default: /* Check if there is a built-in type optimizer available */ $builtInTypeClass = 'Zephir\\Types\\' . ucfirst($variableVariable->getType()) . 'Type'; if (class_exists($builtInTypeClass)) { /** * @var $builtInType \Zephir\Types\AbstractType */ $builtInType = new $builtInTypeClass(); $caller = $exprCompiledVariable; } else { throw new CompilerException("Methods cannot be called on variable type: " . $variableVariable->getType(), $expression); } } break; default: /* Check if there is a built-in type optimizer available */ $builtInTypeClass = 'Zephir\\Types\\' . ucfirst($exprCompiledVariable->getType()) . 'Type'; if (class_exists($builtInTypeClass)) { $builtInType = new $builtInTypeClass(); $caller = $exprCompiledVariable; } else { throw new CompilerException("Cannot use expression: " . $exprCompiledVariable->getType() . " as method caller", $expression['variable']); } } $codePrinter = $compilationContext->codePrinter; $type = $expression['call-type']; /** * In normal method calls and dynamic string method calls we just use the name given by the user */ if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) { $methodName = strtolower($expression['name']); } else { $variableMethod = $compilationContext->symbolTable->getVariableForRead($expression['name'], $compilationContext, $expression); if (is_object($builtInType)) { throw new CompilerException("Dynamic method invocation for type: " . $variableMethod->getType() . " is not supported", $expression); } if ($variableMethod->isNotVariableAndString()) { throw new CompilerException("Cannot use variable type: " . $variableMethod->getType() . " as dynamic method name", $expression); } } $symbolVariable = null; /** * Create temporary variable if needed */ $mustInit = false; $isExpecting = $expr->isExpectingReturn(); if ($isExpecting) { $symbolVariable = $expr->getExpectingVariable(); if (is_object($symbolVariable)) { $readDetector = new ReadDetector($expression); if ($caller == $symbolVariable || $readDetector->detect($symbolVariable->getName(), $expression)) { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression); } else { $mustInit = true; } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserveOrNullify('variable', $compilationContext, $expression); } } /** * Method calls only return zvals so we need to validate the target variable is also a zval */ if (!$builtInType) { if ($isExpecting) { if (!$symbolVariable->isVariable()) { throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression); } /** * At this point, we don't know the exact dynamic type returned by the method call */ $symbolVariable->setDynamicTypes('undefined'); } } else { return $builtInType->invokeMethod($methodName, $caller, $compilationContext, $this, $expression); } $check = true; if (isset($expression['check'])) { $check = $expression['check']; } /** * Try to check if the method exist in the callee, only when method call is self::CALL_NORMAL */ if ($type == self::CALL_NORMAL) { if ($variableVariable->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasMethod($methodName)) { if ($check) { $found = false; $interfaces = $classDefinition->isAbstract() ? $classDefinition->getImplementedInterfaces() : null; if (is_array($interfaces)) { $compiler = $compilationContext->compiler; foreach ($interfaces as $interface) { $classInterfaceDefinition = $compiler->getClassDefinition($interface); if ($classInterfaceDefinition->hasMethod($methodName)) { $found = true; $classMethod = $classInterfaceDefinition->getMethod($methodName); break; } } } if (!$found) { $possibleMethod = $classDefinition->getPossibleMethodName($expression['name']); if ($possibleMethod && $expression['name'] != $possibleMethod) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not implement method: '" . $expression['name'] . "'. Did you mean '" . $possibleMethod . "'?", $expression); } else { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not implement method: '" . $expression['name'] . "'", $expression); } } } } else { if ($check) { $classMethod = $classDefinition->getMethod($methodName); } } if ($check) { /** * Private methods must be called in their declaration scope */ if ($classMethod->isPrivate()) { if ($classMethod->getClassDefinition() != $classDefinition) { throw new CompilerException("Cannot call private method '" . $expression['name'] . "' out of its scope", $expression); } } /** * Try to produce an exception if method is called with a wrong number of parameters */ if (isset($expression['parameters'])) { $callNumberParameters = count($expression['parameters']); } else { $callNumberParameters = 0; } $expectedNumberParameters = $classMethod->getNumberOfRequiredParameters(); if (!$expectedNumberParameters && $callNumberParameters > 0) { $numberParameters = $classMethod->getNumberOfParameters(); if ($callNumberParameters > $numberParameters) { throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression); } } if ($callNumberParameters < $expectedNumberParameters) { throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression); } $method = $classMethod; } } else { /** * Variables whose dynamic type is 'object' can be used * to determine method existence in compile time */ if ($check && $variableVariable->hasAnyDynamicType('object')) { $classTypes = $variableVariable->getClassTypes(); if (count($classTypes)) { $numberImplemented = 0; $compiler = $compilationContext->compiler; foreach ($classTypes as $classType) { if ($compiler->isClass($classType) || $compiler->isInterface($classType) || $compiler->isBundledClass($classType) || $compiler->isBundledInterface($classType)) { if ($compiler->isClass($classType) || $compiler->isInterface($classType)) { $classDefinition = $compiler->getClassDefinition($classType); } else { $classDefinition = $compiler->getInternalClassDefinition($classType); } if (!$classDefinition) { throw new CompilerException("Cannot locate class definition for class " . $classType, $expression); } if (!$classDefinition->hasMethod($methodName)) { if (!$classDefinition->isInterface()) { if (count($classTypes) == 1) { throw new CompilerException("Class '" . $classType . "' does not implement method: '" . $expression['name'] . "'", $expression); } } continue; } $method = $classDefinition->getMethod($methodName); /** * Private methods must be called in their declaration scope */ if ($method->isPrivate()) { if ($method->getClassDefinition() != $classDefinition) { throw new CompilerException("Cannot call private method '" . $expression['name'] . "' out of its scope", $expression); } } /** * Check visibility for protected methods */ if ($method->isProtected() && $method->getClassDefinition() != $classDefinition && $method->getClassDefinition() != $classDefinition->getExtendsClass()) { throw new CompilerException("Cannot call protected method '" . $expression['name'] . "' out of its scope", $expression); } /** * Try to produce an exception if a method is called with a wrong number of parameters * We only check extension parameters if methods are extension methods * Internal methods may have invalid Reflection information */ if ($method instanceof ClassMethod && !$method->isBundled()) { if (isset($expression['parameters'])) { $callNumberParameters = count($expression['parameters']); } else { $callNumberParameters = 0; } $classMethod = $classDefinition->getMethod($methodName); $expectedNumberParameters = $classMethod->getNumberOfRequiredParameters(); if (!$expectedNumberParameters && $callNumberParameters > 0) { $numberParameters = $classMethod->getNumberOfParameters(); if ($callNumberParameters > $numberParameters) { $className = $classDefinition->getCompleteName(); throw new CompilerException("Method '" . $className . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression); } } if ($callNumberParameters < $expectedNumberParameters) { throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' called with a wrong number of parameters, the method has: " . $expectedNumberParameters . ", passed: " . $callNumberParameters, $expression); } } /** * The method is checked in the first class that implements the method * We could probably have collisions here */ $numberImplemented++; break; } else { $numberImplemented++; $compilationContext->logger->warning("Class \"" . $classType . "\" does not exist at compile time", "nonexistent-class", $expression); } } if ($numberImplemented == 0) { if (!$classDefinition->isInterface()) { if (count($classTypes) > 1) { throw new CompilerException("None of classes: '" . join(' or ', $classTypes) . "' implement method: '" . $expression['name'] . "'", $expression); } else { throw new CompilerException("Class '" . $classTypes[0] . "' does not implement method: '" . $expression['name'] . "'", $expression); } } else { // @TODO, raise an exception here? } } } } } } if (isset($method)) { $this->_reflection = $method; } /** * Transfer the return type-hint to the returned variable */ if ($isExpecting) { if (isset($method)) { if ($method instanceof ClassMethod) { if ($method->isVoid()) { throw new CompilerException("Method '" . $classDefinition->getCompleteName() . "::" . $expression['name'] . "' is marked as 'void' and it does not return anything", $expression); } $returnClassTypes = $method->getReturnClassTypes(); if ($returnClassTypes !== null) { $symbolVariable->setDynamicTypes('object'); foreach ($returnClassTypes as &$returnClassType) { $returnClassType = $compilationContext->getFullName($returnClassType); } $symbolVariable->setClassTypes($returnClassTypes); } $returnTypes = $method->getReturnTypes(); if ($returnTypes !== null) { foreach ($returnTypes as $dataType => $returnType) { $symbolVariable->setDynamicTypes($dataType); } } } } } /** * Some parameters in internal methods receive parameters as references */ if (isset($expression['parameters'])) { $references = array(); if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) { if (isset($method)) { if ($method instanceof \ReflectionMethod) { $position = 0; foreach ($method->getParameters() as $parameter) { if ($parameter->isPassedByReference()) { $references[$position] = true; } $position++; } } } } } /** * Include fcall header */ $compilationContext->headersManager->add('kernel/fcall'); /** * Call methods must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); /** * Mark references */ if (isset($expression['parameters'])) { $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression, isset($method) ? $method : null); if (count($references)) { foreach ($params as $position => $param) { if (isset($references[$position])) { $compilationContext->codePrinter->output('Z_SET_ISREF_P(' . $param . ');'); } } } // We check here if a correct parameter type is passed to the called method if ($type == self::CALL_NORMAL) { if (isset($method) && $method instanceof ClassMethod && isset($expression['parameters'])) { $resolvedTypes = $this->getResolvedTypes(); $resolvedDynamicTypes = $this->getResolvedDynamicTypes(); //$typeInference = $method->getStaticTypeInferencePass(); foreach ($method->getParameters() as $n => $parameter) { if (isset($parameter['data-type'])) { if (!isset($resolvedTypes[$n])) { continue; } /** * If the passed parameter is different to the expected type we show a warning */ if ($resolvedTypes[$n] != $parameter['data-type']) { switch ($resolvedTypes[$n]) { case 'bool': case 'boolean': switch ($parameter['data-type']) { /* compatible types */ case 'bool': case 'boolean': case 'variable': break; default: $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression); break; } break; case 'array': switch ($parameter['data-type']) { /* compatible types */ case 'array': case 'variable': break; case 'callable': /** * Array can be a callable type, example: [$this, "method"] * * @todo we need to check this array if can... */ break; default: $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression); break; } break; case 'callable': switch ($parameter['data-type']) { /* compatible types */ case 'callable': case 'variable': break; default: $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression); break; } break; case 'string': switch ($parameter['data-type']) { /* compatible types */ case 'string': case 'variable': break; default: $compilationContext->logger->warning("Passing possible incorrect type for parameter: " . $classDefinition->getCompleteName() . '::' . $method->getName() . '(' . $parameter['name'] . '), passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter['data-type'], "possible-wrong-parameter", $expression); break; } break; /** * Passing polymorphic variables to static typed parameters * could lead to potential unexpected type coercions */ /** * Passing polymorphic variables to static typed parameters * could lead to potential unexpected type coercions */ case 'variable': if ($resolvedDynamicTypes[$n] != $parameter['data-type']) { if ($resolvedDynamicTypes[$n] == 'undefined') { $compilationContext->logger->warning("Passing possible incorrect type to parameter: " . $classDefinition->getCompleteName() . '::' . $parameter[$n]['name'] . ', passing: ' . $resolvedDynamicTypes[$n] . ', ' . "expecting: " . $parameter[$n]['data-type'], "possible-wrong-parameter-undefined", $expression); } //echo '1: ', $resolvedTypes[$n], ' ', $resolvedDynamicTypes[$n], ' ', $parameter[0]['data-type'], ' ', PHP_EOL; } break; } } } } } } } else { $params = array(); } // Add the last call status to the current symbol table $this->addCallStatusFlag($compilationContext); // Initialize non-temporary variables if ($mustInit) { $symbolVariable->setMustInitNull(true); $symbolVariable->trackVariant($compilationContext); } // Generate the code according to the call type if ($type == self::CALL_NORMAL || $type == self::CALL_DYNAMIC_STRING) { $realMethod = $this->getRealCalledMethod($compilationContext, $variableVariable, $methodName); $isInternal = false; if (is_object($realMethod[1])) { $isInternal = $realMethod[1]->isInternal(); if ($isInternal && $realMethod[0] > 1) { throw new CompilerException("Cannot resolve method: '" . $expression['name'] . "' in polymorphic variable", $expression); } } if (!$isInternal) { // Check if the method call can have an inline cache $methodCache = $compilationContext->cacheManager->getMethodCache(); $cachePointer = $methodCache->get($compilationContext, $methodName, $variableVariable); if (!count($params)) { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD(' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');'); } else { $codePrinter->output('ZEPHIR_CALL_METHOD(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_METHOD(NULL, ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD(' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');'); } else { $codePrinter->output('ZEPHIR_CALL_METHOD(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_METHOD(NULL, ' . $variableVariable->getName() . ', "' . $methodName . '", ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } } else { if (!count($params)) { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_INTERNAL_METHOD_P0(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');'); } else { $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_P0(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_NORETURN_P0(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_INTERNAL_METHOD_P' . count($params) . '(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');'); } else { $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_P' . count($params) . '(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_INTERNAL_METHOD_NORETURN_P' . count($params) . '(' . $variableVariable->getName() . ', ' . $method->getInternalName() . ', ' . join(', ', $params) . ');'); } } } } else { if ($type == self::CALL_DYNAMIC) { switch ($variableMethod->getType()) { case 'string': case 'variable': break; default: throw new Exception('Cannot use variable type: ' . $variableMethod->getType() . ' as method caller'); } $cachePointer = 'NULL, 0'; if (!count($params)) { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD_ZVAL(' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ');'); } else { $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(NULL, ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_METHOD_ZVAL(' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $params) . ');'); } else { $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_METHOD_ZVAL(NULL, ' . $variableVariable->getName() . ', ' . $variableMethod->getName() . ', ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } } } // Temporary variables must be copied if they have more than one reference foreach ($this->getMustCheckForCopyVariables() as $checkVariable) { $codePrinter->output('zephir_check_temp_parameter(' . $checkVariable . ');'); } // We can mark temporary variables generated as idle foreach ($this->getTemporalVariables() as $tempVariable) { $tempVariable->setIdle(true); } // Release parameters marked as references if (isset($expression['parameters'])) { if (count($references)) { foreach ($params as $position => $param) { if (isset($references[$position])) { $compilationContext->codePrinter->output('Z_UNSET_ISREF_P(' . $param . ');'); } } } } $this->addCallStatusOrJump($compilationContext); if ($isExpecting) { return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } return new CompiledExpression('null', null, $expression); }
/** * @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();'); }
/** * Resolves paramameters * * @param array $parameters * @param CompilationContext $compilationContext * @param array $expression * @param boolean $readOnly * @return array|null|CompiledExpression[] */ public function getResolvedParamsAsExpr($parameters, CompilationContext $compilationContext, $expression, $readOnly = false) { if (!$this->_resolvedParams) { $hasParametersByName = false; foreach ($parameters as $parameter) { if (isset($parameter['name'])) { $hasParametersByName = true; break; } } /** * All parameters must be passed by name */ if ($hasParametersByName) { foreach ($parameters as $parameter) { if (!isset($parameter['name'])) { throw new CompilerException('All parameters must use named', $parameter); } } } if ($hasParametersByName) { if ($this->_reflection) { $positionalParameters = array(); foreach ($this->_reflection->getParameters() as $position => $reflectionParameter) { if (is_object($reflectionParameter)) { $positionalParameters[$reflectionParameter->getName()] = $position; } else { $positionalParameters[$reflectionParameter['name']] = $position; } } $orderedParameters = array(); foreach ($parameters as $parameter) { if (isset($positionalParameters[$parameter['name']])) { $orderedParameters[$positionalParameters[$parameter['name']]] = $parameter; } else { throw new CompilerException('Named parameter "' . $parameter['name'] . '" is not a valid parameter name, available: ' . join(', ', array_keys($positionalParameters)), $parameter['parameter']); } } $parameters_count = count($parameters); for ($i = 0; $i < $parameters_count; $i++) { if (!isset($orderedParameters[$i])) { $orderedParameters[$i] = array('parameter' => array('type' => 'null')); } } $parameters = $orderedParameters; } } $params = array(); foreach ($parameters as $parameter) { if (is_array($parameter['parameter'])) { $paramExpr = new Expression($parameter['parameter']); switch ($parameter['parameter']['type']) { case 'property-access': case 'array-access': case 'static-property-access': $paramExpr->setReadOnly(true); break; default: $paramExpr->setReadOnly($readOnly); break; } $params[] = $paramExpr->compile($compilationContext); continue; } if ($parameter['parameter'] instanceof CompiledExpression) { $params[] = $parameter['parameter']; continue; } throw new CompilerException("Invalid expression ", $expression); } $this->_resolvedParams = $params; } return $this->_resolvedParams; }
/** * Compile the expression * * @param array $expression * @param CompilationContext $compilationContext */ public function compile($expression, CompilationContext $compilationContext) { $conditions = $this->optimizeTypeOf($expression, $compilationContext); if ($conditions !== false) { return $conditions; } if (!isset($expression['left'])) { throw new CompilerException("Missing left part of the expression", $expression); } if (!isset($expression['right'])) { throw new CompilerException("Missing right part of the expression", $expression); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly(true); $left = $leftExpr->compile($compilationContext); $rightExpr = new Expression($expression['right']); $rightExpr->setReadOnly(true); $right = $rightExpr->compile($compilationContext); switch ($left->getType()) { case 'null': switch ($right->getType()) { case 'null': return new CompiledExpression('bool', '(0 ' . $this->_operator . ' 0)', $expression); case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('bool', '(0 ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'char': case 'uchar': return new CompiledExpression('bool', '(\'\\0\' ' . $this->_operator . ' \'' . $right->getCode() . '\')', $expression); case 'double': return new CompiledExpression('bool', '(0 ' . $this->_operator . ' (int) ' . $right->getCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', '0 ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $condition = $compilationContext->backend->getTypeofCondition($variableRight, $this->_operator, 'null', $compilationContext); return new CompiledExpression('bool', $condition, $expression); default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Unknown type: " . $right->getType(), $expression); } break; case 'int': case 'uint': case 'long': case 'double': case 'ulong': case 'char': case 'uchar': switch ($right->getType()) { case 'null': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator, $expression); case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'char': case 'uchar': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' \'' . $right->getCode() . '\'', $expression); case 'double': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' (int) ' . $right->getCode(), $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $variableCode = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $this->_zvalLongNegOperator . '(' . $variableCode . ', ' . $left->getCode() . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Cannot compare " . $left->getType() . " with " . $right->getType(), $expression); } break; case 'bool': switch ($right->getType()) { case 'null': return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' 0', $expression); case 'int': case 'uint': case 'long': case 'ulong': return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'char': case 'uchar': return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' \'' . $right->getCode() . '\'', $expression); case 'double': return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' (int) ' . $right->getCode(), $expression); case 'bool': return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode(), $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $boolOperator = $left->getBooleanCode() == '1' ? $this->_zvalBoolTrueOperator : $this->_zvalBoolFalseOperator; $variableRight = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $boolOperator . '(' . $variableRight . ')', $expression); default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Cannot compare " . $left->getType() . " with " . $right->getType(), $expression); } break; case 'string': $variableLeft = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $variableLeftCode = $compilationContext->backend->getVariableCode($variableLeft); $compilationContext->backend->assignString($variableLeft, $left->getCode(), $compilationContext, true, false); switch ($right->getType()) { case 'string': case 'null': $rightStr = $right->getType() == 'null' ? '' : $right->getCode(); $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalStringOperator . '(' . $variableLeftCode . ', "' . $rightStr . '")', $expression['left']); break; case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'string': case 'variable': $compilationContext->headersManager->add('kernel/operators'); $variableRight = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $this->_zvalOperator . '(' . $variableLeftCode . ', ' . $variableRight . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Unknown type: " . $right->getType(), $expression['left']); } break; case 'variable': $variable = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression['left']); $variableCode = $compilationContext->backend->getVariableCode($variable); switch ($variable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'char': case 'uchar': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' \'' . $right->getCode() . '\'', $expression); case 'bool': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode(), $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': case 'double': return new CompiledExpression('bool', $variable->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $variableRightCode = $compilationContext->backend->getVariableCode($variableRight); $variableCode = $compilationContext->backend->getVariableCode($variable); return new CompiledExpression('bool', $this->_zvalLongNegOperator . '(' . $variableRightCode . ', ' . $variableCode . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Cannot compare variable: " . $variable->getType() . " with: " . $right->getType(), $expression); } break; case 'double': switch ($right->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'bool': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode(), $expression); case 'char': case 'uchar': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' \'' . $right->getCode() . '\'', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': return new CompiledExpression('bool', $variable->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $variableRightCode = $compilationContext->backend->getVariableCode($variableRight); $variableCode = $compilationContext->backend->getVariableCode($variable); return new CompiledExpression('bool', $this->_zvalDoubleNegOperator . '(' . $variableRightCode . ', ' . $variableCode . ')', $expression); default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Cannot compare variable: " . $variable->getType() . " with: " . $right->getType(), $expression); } break; case 'bool': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression['left']); case 'bool': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode(), $expression['left']); case 'null': return new CompiledExpression('bool', $left->getCode() . ' ' . $this->_operator . ' 0', $expression['left']); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'bool': case 'double': return new CompiledExpression('bool', $variable->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $boolOperator = $left->getBooleanCode() == '1' ? $this->_zvalBoolTrueOperator : $this->_zvalBoolFalseOperator; $variableRightCode = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $boolOperator . '(' . $variableRightCode . ')', $expression); default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Cannot compare variable: " . $variable->getType() . " with: " . $right->getType(), $expression); } break; case 'array': switch ($right->getType()) { case 'null': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalStringOperator . '(' . $variableCode . ', "")', $expression['left']); break; case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'string': case 'variable': case 'array': $compilationContext->headersManager->add('kernel/operators'); $variableRight = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $this->_zvalOperator . '(' . $variableCode . ', ' . $variableRight . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Unknown type: " . $right->getType(), $expression['left']); } break; case 'string': switch ($right->getType()) { case 'null': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalStringOperator . '(' . $variableCode . ', "")', $expression['left']); break; case 'string': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalStringOperator . '(' . $variableCode . ', "' . $right->getCode() . '")', $expression['left']); break; case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'string': case 'variable': $compilationContext->headersManager->add('kernel/operators'); $variableRight = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $this->_zvalOperator . '(' . $variableCode . ', ' . $variableRight . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Unknown type: " . $right->getType(), $expression['left']); } break; case 'variable': switch ($right->getType()) { case 'null': $compilationContext->headersManager->add('kernel/operators'); $condition = $compilationContext->backend->getTypeofCondition($variable, $this->_operator, 'null', $compilationContext); return new CompiledExpression('bool', $condition, $expression['left']); break; case 'int': case 'uint': case 'long': case 'ulong': case 'double': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalLongOperator . '(' . $variableCode . ', ' . $right->getCode() . ')', $expression['left']); break; case 'char': case 'uchar': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalLongOperator . '(' . $variableCode . ', \'' . $right->getCode() . '\')', $expression['left']); break; case 'bool': $compilationContext->headersManager->add('kernel/operators'); $zvalBoolOperator = $right->getCode() == 'true' ? $this->_zvalBoolTrueOperator : $this->_zvalBoolFalseOperator; return new CompiledExpression('bool', $zvalBoolOperator . '(' . $variableCode . ')', $expression['left']); break; case 'string': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalStringOperator . '(' . $variableCode . ', "' . $right->getCode() . '")', $expression['left']); break; case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['left']); switch ($variableRight->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalLongOperator . '(' . $variableCode . ', ' . $variableRight->getName() . ')', $expression); break; case 'double': $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', $this->_zvalDoubleOperator . '(' . $variableCode . ', ' . $variableRight->getName() . ')', $expression); break; case 'string': case 'variable': case 'array': $compilationContext->headersManager->add('kernel/operators'); $variableRight = $compilationContext->backend->getVariableCode($variableRight); return new CompiledExpression('bool', $this->_zvalOperator . '(' . $variableCode . ', ' . $variableRight . ')', $expression); break; default: throw new CompilerException("Unknown type: " . $variableRight->getType(), $expression['right']); } break; default: throw new CompilerException("Unknown type: " . $right->getType(), $expression['left']); } break; default: throw new CompilerException("Unknown type: " . $variable->getType(), $expression); } break; default: throw new CompilerException("Unknown type: " . $left->getType(), $expression); } }
public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Missing left part of the expression", $expression); } if (!isset($expression['right'])) { throw new CompilerException("Missing right part of the expression", $expression); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); $rightExpr = new Expression($expression['right']); $rightExpr->setReadOnly($this->_readOnly); $right = $rightExpr->compile($compilationContext); switch ($left->getType()) { case 'int': switch ($right->getType()) { case 'int': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'double': return new CompiledExpression('double', '((double) ' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'bool': return new CompiledExpression('int', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare 'int' with '" . $right->getType() . "'", $expression); } break; case 'bool': switch ($right->getType()) { case 'int': case 'double': return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_bitOperator . ' ((' . $right->getCode() . ') ? 1 : 0))', $expression); case 'bool': return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_bitOperator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': if ($variableRight->isLocalOnly()) { $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('bool', '(' . $left->getBooleanCode() . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot add variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare 'bool' with '" . $right->getType() . "'", $expression); } break; case 'double': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'double': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $left->getCode() . ' ' . $this->_operator . ' ' . $right->getBooleanCode() . ')', $expression); default: throw new CompilerException("Cannot compare 'double' with '" . $right->getType() . "'", $expression); } break; case 'string': switch ($right->getType()) { default: throw new CompilerException("Operation is not supported between strings", $expression); } break; case 'variable': $variableLeft = $compilationContext->symbolTable->getVariableForRead($left->resolve(null, $compilationContext), $compilationContext, $expression); switch ($variableLeft->getType()) { case 'int': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'double': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('int', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with '" . $right->getType() . "'", $expression); } break; case 'bool': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $right->getCode() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_bitOperator . ' ' . $right->getBooleanCode() . ')', $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName() . ')', $expression); case 'bool': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_bitOperator . ' ' . $variableRight->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . '))', $expression); } else { return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . '))', $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with '" . $right->getType() . "'", $expression); } break; case 'double': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'double': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'bool': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_bitOperator . '' . $right->getBooleanCode(), $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' (double) ' . $variableRight->getName(), $expression); case 'double': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'bool': return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_bitOperator . '' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . ')', $expression); } else { return new CompiledExpression('bool', $variableLeft->getName() . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . ')', $expression); } break; default: throw new CompilerException("Cannot compare variable('double') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with '" . $right->getType() . "'", $expression); } break; case 'string': switch ($right->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'double': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $right->getCode(), $expression); case 'bool': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_bitOperator . '' . $right->getBooleanCode(), $expression); case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); switch ($variableRight->getType()) { case 'int': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'double': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_operator . ' ' . $variableRight->getName(), $expression); case 'string': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_operator . ' (' . $variableRight->getName() . ' && Z_STRLEN_P(' . $variableRight->getName() . '))', $expression); case 'bool': return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . $this->_bitOperator . ' ' . $variableRight->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableRight->isLocalOnly()) { return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . ' ' . $this->_operator . ' zephir_is_true(&' . $variableRight->getName() . ')', $expression); } else { return new CompiledExpression('bool', '(' . $variableLeft->getName() . ' && Z_STRLEN_P(' . $variableLeft->getName() . ')) ' . ' ' . $this->_operator . ' zephir_is_true(' . $variableRight->getName() . ')', $expression); } break; default: throw new CompilerException("Cannot compare variable('double') with variable('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare variable('int') with '" . $right->getType() . "'", $expression); } break; case 'variable': switch ($right->getType()) { /* a && 1 */ case 'int': case 'double': $compilationContext->headersManager->add('kernel/operators'); $op = $this->_operator; if ($variableLeft->isLocalOnly()) { $op1 = '&' . $variableLeft->getName(); } else { $op1 = $variableLeft->getName(); } $op2 = $right->getCode(); $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', 'zephir_is_true(' . $op1 . ') ' . $op . ' ' . $op2, $expression); /* a && 1 */ /* a && 1 */ case 'bool': $compilationContext->headersManager->add('kernel/operators'); $op = $this->_operator; if ($variableLeft->isLocalOnly()) { $op1 = '&' . $variableLeft->getName(); } else { $op1 = $variableLeft->getName(); } $op2 = $right->getCode(); $compilationContext->headersManager->add('kernel/operators'); return new CompiledExpression('bool', 'zephir_is_true(' . $op1 . ') ' . $op . ' ' . $op2, $expression); /* a(var) && a(x) */ /* a(var) && a(x) */ case 'variable': $variableRight = $compilationContext->symbolTable->getVariableForRead($right->resolve(null, $compilationContext), $compilationContext, $expression); switch ($variableRight->getType()) { /* a(var) && a(int) */ case 'int': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { return new CompiledExpression('bool', 'zephir_is_true(&' . $variableLeft->getName() . ') ' . $this->_operator . ' ' . $variableRight->getName(), $expression); } else { return new CompiledExpression('bool', 'zephir_is_true(' . $variableLeft->getName() . ') ' . $this->_operator . ' ' . $variableRight->getName(), $expression); } break; /* a(var) && a(bool) */ /* a(var) && a(bool) */ case 'bool': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { return new CompiledExpression('bool', 'zephir_is_true(&' . $variableLeft->getName() . ') ' . $this->_operator . ' ' . $variableRight->getName(), $expression); } else { return new CompiledExpression('bool', 'zephir_is_true(' . $variableLeft->getName() . ') ' . $this->_operator . ' ' . $variableRight->getName(), $expression); } break; /* a(var) && a(var) */ /* a(var) && a(var) */ case 'variable': $compilationContext->headersManager->add('kernel/operators'); if ($variableLeft->isLocalOnly()) { $op1 = '&' . $variableLeft->getName(); } else { $op1 = $variableLeft->getName(); } if ($variableRight->isLocalOnly()) { $op2 = '&' . $variableRight->getName(); } else { $op2 = $variableRight->getName(); } $expected = $this->getExpected($compilationContext, $expression); if ($expected->isLocalOnly()) { $compilationContext->codePrinter->output('add_function(&' . $expected->getName() . ', ' . $op1 . ', ' . $op2 . ' TSRMLS_CC);'); } else { $compilationContext->codePrinter->output('add_function(' . $expected->getName() . ', ' . $op1 . ', ' . $op2 . ' TSRMLS_CC);'); } return new CompiledExpression('variable', $expected->getName(), $expression); default: throw new CompilerException("Cannot compare 'variable' with variable ('" . $variableRight->getType() . "')", $expression); } break; default: throw new CompilerException("Cannot compare 'variable' with '" . $right->getType() . "'", $expression); } break; default: throw new CompilerException("Unknown '" . $variableLeft->getType() . "'", $expression); } break; default: throw new CompilerException("Unsupported type: " . $left->getType(), $expression); } }
public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new \Exception("Missing left part of the expression"); } if (!isset($expression['right'])) { throw new \Exception("Missing right part of the expression"); } $leftExpr = new Expression($expression['left']); $leftExpr->setReadOnly($this->_readOnly); $left = $leftExpr->compile($compilationContext); /** * This variable is used to check if the compound and expression is evaluated as true or false */ $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext); switch ($left->getType()) { case 'int': case 'bool': case 'char': case 'double': case 'uint': case 'uchar': $assignExprLeft = array('type' => $left->getType(), 'value' => $left->getCode()); break; case 'variable': $assignExprLeft = array('type' => 'variable', 'value' => $left->getCode()); break; case 'null': $assignExprLeft = array('type' => 'null', 'value' => null); break; } if (!isset($assignExprLeft)) { throw new CompilerException($left->getType(), $expression['left']); } /** * Create an implicit 'let' operation to update the evaluated left operator */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $flagVariable->getName(), 'operator' => 'assign', 'expr' => $assignExprLeft, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char'])))); $statement->compile($compilationContext); $compilationContext->codePrinter->output('if (' . $flagVariable->getName() . ') {'); $compilationContext->codePrinter->increaseLevel(); $rightExpr = new Expression($expression['right']); $rightExpr->setReadOnly($this->_readOnly); $right = $rightExpr->compile($compilationContext); switch ($right->getType()) { case 'int': case 'bool': case 'char': case 'double': case 'uint': case 'uchar': $assignExprRight = array('type' => $right->getType(), 'value' => $right->getCode()); break; case 'variable': $assignExprRight = array('type' => 'variable', 'value' => $right->getCode()); break; case 'null': $assignExprRight = array('type' => 'null', 'value' => null); break; } if (!isset($assignExprRight)) { throw new CompilerException($right->getType(), $expression['right']); } /** * Create an implicit 'let' operation to update the evaluated right operator */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $flagVariable->getName(), 'operator' => 'assign', 'expr' => $assignExprRight, 'file' => $expression['right']['file'], 'line' => $expression['right']['line'], 'char' => $expression['right']['char'])))); $statement->compile($compilationContext); $compilationContext->codePrinter->decreaseLevel(); $compilationContext->codePrinter->output('}'); return new CompiledExpression('bool', $flagVariable->getName(), $expression); }
/** * Performs concat compilation * * @param Expression $expression * @param CompilationContext $compilationContext */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Missing left part of the expression", $expression); } if (!isset($expression['right'])) { throw new CompilerException("Missing right part of the expression", $expression); } $compilationContext->headersManager->add('kernel/concat'); /** * Try to optimize the concatenation */ $optimized = $this->_getOptimizedConcat($expression, $compilationContext, $isFullString); if (is_array($optimized)) { if (!$isFullString) { $expected = $this->getExpectedComplexLiteral($compilationContext, $expression); } else { $expected = $this->getExpectedComplexLiteral($compilationContext, $expression, 'string'); } $expected->setDynamicTypes('string'); $compilationContext->codePrinter->output('ZEPHIR_CONCAT_' . strtoupper($optimized[0]) . '(' . $expected->getName() . ', ' . $optimized[1] . ');'); return new CompiledExpression('variable', $expected->getName(), $expression); } /** * If the expression cannot be optimized, fall back to the standard compilation */ $leftExpr = new Expression($expression['left']); switch ($expression['left']['type']) { case 'array-access': case 'property-access': $leftExpr->setReadOnly(true); break; default: $leftExpr->setReadOnly($this->_readOnly); break; } $left = $leftExpr->compile($compilationContext); if ($left->getType() == 'variable') { $variableLeft = $compilationContext->symbolTable->getVariableForRead($left->getCode(), $compilationContext, $expression['right']); } $rightExpr = new Expression($expression['right']); switch ($expression['left']['type']) { case 'array-access': case 'property-access': $rightExpr->setReadOnly(true); break; default: $rightExpr->setReadOnly($this->_readOnly); break; } $right = $rightExpr->compile($compilationContext); if ($right->getType() == 'variable') { $variableLeft = $compilationContext->symbolTable->getVariableForRead($right->getCode(), $compilationContext, $expression['right']); } $expected = $this->getExpectedComplexLiteral($compilationContext, $expression); if ($left->getType() == 'string' && $right->getType() == 'variable') { $compilationContext->codePrinter->output('ZEPHIR_CONCAT_SV(' . $expected->getName() . ', "' . $left->getCode() . '", ' . $right->getCode() . ');'); } if ($left->getType() == 'variable' && $right->getType() == 'string') { $compilationContext->codePrinter->output('ZEPHIR_CONCAT_VS(' . $expected->getName() . ', ' . $left->getCode() . ', "' . $right->getCode() . '");'); } if ($left->getType() == 'variable' && $right->getType() == 'variable') { $compilationContext->codePrinter->output('zephir_concat_function(' . $expected->getName() . ', ' . $left->getCode() . ', ' . $right->getCode() . ' TSRMLS_CC);'); } $expected->setDynamicTypes('string'); return new CompiledExpression('variable', $expected->getName(), $expression); }
/** * Compiles foo[x] = {expr} * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { /** * Resolve the left part of the expression */ $expr = new Expression($expression['left']); $expr->setReadOnly(true); $exprVariable = $expr->compile($compilationContext); /** * Only dynamic variables can be used as arrays */ switch ($exprVariable->getType()) { case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression); switch ($variableVariable->getType()) { case 'variable': case 'array': case 'string': break; default: throw new CompilerException("Variable type: " . $variableVariable->getType() . " cannot be used as array", $expression['left']); } break; default: throw new CompilerException("Cannot use expression: " . $exprVariable->getType() . " as an array", $expression['left']); } /** * Resolve the dimension according to variable's type */ switch ($variableVariable->getType()) { case 'variable': return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext); case 'array': return $this->_accessDimensionArray($expression, $variableVariable, $compilationContext); case 'string': return $this->_accessStringOffset($expression, $variableVariable, $compilationContext); } }
/** * 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); $keys = ''; $numberParams = 0; $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_array_update_multi(&' . $variable . ', &' . $symbolVariable->getName() . ' TSRMLS_CC, SL("' . $keys . '"), ' . $numberParams . ', ' . join(', ', $offsetItems) . ');'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } }