/** * Compiles a type cast operation * * @param $expression * @param CompilationContext $compilationContext * @return bool|CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { $expr = new Expression($expression['right']); $resolved = $expr->compile($compilationContext); switch ($expression['left']) { case 'int': switch ($resolved->getType()) { case 'null': return new CompiledExpression('int', 0, $expression); case 'int': return new CompiledExpression('int', $resolved->getCode(), $expression); case 'double': return new CompiledExpression('int', '(int) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('int', $resolved->getBooleanCode(), $expression); case 'string': $compilationContext->headersManager->add('kernel/operators'); /** * zephir_get_intval_ex use zval variable * before use with it we create a new variable and assign value of literal * * @todo Optimize by creating native function for string without zval using */ $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('string', $compilationContext); $let = new LetVariable(); $original = $resolved->getOriginal(); $original['operator'] = 'assign'; $let->assign($symbolVariable->getName(), $symbolVariable, $resolved, new ReadDetector(), $compilationContext, $original); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval_ex(' . $symbol . ')', $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval(' . $symbol . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('int', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('int', '(int) (' . $symbolVariable->getName() . ')', $expression); case 'bool': return new CompiledExpression('int', '(int) (' . $symbolVariable->getName() . ')', $expression); case 'array': case 'variable': case 'string': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('int', 'zephir_get_intval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'long': switch ($resolved->getType()) { case 'int': return new CompiledExpression('long', $resolved->getCode(), $expression); case 'double': return new CompiledExpression('long', '(long) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('long', $resolved->getBooleanCode(), $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); return new CompiledExpression('long', 'zephir_get_intval(' . $symbolVariable->getName() . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('long', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('long', '(long) (' . $symbolVariable->getName() . ')', $expression); case 'variable': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('long', 'zephir_get_intval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'double': switch ($resolved->getType()) { case 'null': return new CompiledExpression('double', 0, $expression); case 'bool': return new CompiledExpression('double', $resolved->getBooleanCode(), $expression); case 'double': return new CompiledExpression('double', $resolved->getCode(), $expression); case 'array': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('double', 'zephir_get_doubleval(' . $symbol . ')', $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); switch ($symbolVariable->getType()) { case 'int': return new CompiledExpression('double', $symbolVariable->getName(), $expression); case 'double': return new CompiledExpression('double', '(double) (' . $symbolVariable->getName() . ')', $expression); case 'bool': return new CompiledExpression('double', '(double) (' . $symbolVariable->getName() . ')', $expression); case 'array': case 'variable': $symbol = $compilationContext->backend->getVariableCode($symbolVariable); return new CompiledExpression('double', 'zephir_get_doubleval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'bool': switch ($resolved->getType()) { case 'int': return new CompiledExpression('bool', '(zend_bool) ' . $resolved->getCode(), $expression); case 'bool': return new CompiledExpression('bool', $resolved->getCode(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } switch ($symbolVariable->getType()) { case 'variable': return new CompiledExpression('bool', 'zephir_get_boolval(' . $symbol . ')', $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . "(" . $symbolVariable->getType() . ") to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'char': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('char', $compilationContext); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $variableCode = $compilationContext->backend->getVariableCode($symbolVariable); $compilationContext->codePrinter->output($tempVariable->getName() . ' = (char) zephir_get_intval(' . $variableCode . ');'); return new CompiledExpression('variable', $tempVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'string': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('string', $compilationContext); $symbolVariable->setMustInitNull(true); $symbolVariable->setIsInitialized(true, $compilationContext); $symbolVariable->increaseUses(); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); $resolvedVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext); $resolvedCode = $compilationContext->backend->getVariableCode($resolvedVariable); $compilationContext->codePrinter->output('zephir_get_strval(' . $symbol . ', ' . $resolvedCode . ');'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'array': switch ($resolved->getType()) { case 'variable': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('array', $compilationContext, $expression); $symbolVariable->setMustInitNull(true); $symbolVariable->setIsInitialized(true, $compilationContext, $expression); $symbolVariable->increaseUses(); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); $resolvedVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext); $resolvedCode = $compilationContext->backend->getVariableCode($resolvedVariable); $compilationContext->codePrinter->output('zephir_get_arrval(' . $symbol . ', ' . $resolvedCode . ');'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; case 'object': switch ($resolved->getType()) { case 'int': case 'double': case 'bool': case 'null': case 'string': case 'array': $compilationContext->headersManager->add('kernel/operators'); $compilationContext->symbolTable->mustGrownStack(true); $symbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); /** * zephir_convert_to_object use zval variable * before use with it we create a new variable and assign value of literal */ $let = new LetVariable(); $original = $resolved->getOriginal(); $original['operator'] = 'assign'; $let->assign($symbolVariable->getName(), $symbolVariable, $resolved, new ReadDetector(), $compilationContext, $original); $compilationContext->codePrinter->output('zephir_convert_to_object(' . $symbol . ');'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); case 'variable': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolved->getCode(), $compilationContext, $expression); $symbol = $compilationContext->backend->getVariableCode($symbolVariable); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } $compilationContext->codePrinter->output('zephir_convert_to_object(' . $symbol . ');'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } break; default: throw new CompilerException("Cannot cast: " . $resolved->getType() . " to " . $expression['left'], $expression); } }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $readDetector = new ReadDetector(); $statement = $this->_statement; foreach ($statement['assignments'] as $assignment) { $variable = $assignment['variable']; /** * Get the symbol from the symbol table if necessary */ switch ($assignment['assign-type']) { case 'static-property': case 'static-property-append': case 'static-property-array-index': case 'static-property-array-index-append': case 'dynamic-variable-string': $symbolVariable = null; break; case 'array-index': case 'variable-append': case 'object-property': case 'array-index-append': case 'string-dynamic-object-property': case 'variable-dynamic-object-property': $symbolVariable = $compilationContext->symbolTable->getVariableForUpdate($variable, $compilationContext, $assignment); break; default: $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($variable, $compilationContext, $assignment); break; } /** * Incr/Decr assignments don't require an expression */ if (isset($assignment['expr'])) { $expr = new Expression($assignment['expr']); switch ($assignment['assign-type']) { case 'variable': if (!$readDetector->detect($variable, $assignment['expr'])) { if (isset($assignment['operator'])) { if ($assignment['operator'] == 'assign') { $expr->setExpectReturn(true, $symbolVariable); } } else { $expr->setExpectReturn(true, $symbolVariable); } } else { if (isset($assignment['operator'])) { if ($assignment['operator'] == 'assign') { $expr->setExpectReturn(true); } } else { $expr->setExpectReturn(true); } } break; } switch ($assignment['expr']['type']) { case 'property-access': case 'array-access': case 'type-hint': $expr->setReadOnly(true); break; } $resolvedExpr = $expr->compile($compilationContext); /** * Bad implemented operators could return values different than objects */ if (!is_object($resolvedExpr)) { throw new CompilerException("Resolved expression is not valid", $assignment['expr']); } } /** * There are four types of assignments */ switch ($assignment['assign-type']) { case 'variable': $let = new LetVariable(); $let->assign($variable, $symbolVariable, $resolvedExpr, $readDetector, $compilationContext, $assignment); break; case 'variable-append': $let = new LetVariableAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property': $let = new LetObjectProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'variable-dynamic-object-property': $let = new LetObjectDynamicProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'string-dynamic-object-property': $let = new LetObjectDynamicStringProperty(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'static-property': $let = new LetStaticProperty(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-append': $let = new LetStaticPropertyAppend(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-array-index': $let = new LetStaticPropertyArrayIndex(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'static-property-array-index-append': $let = new LetStaticPropertyArrayIndexAppend(); $let->assignStatic($variable, $assignment['property'], $resolvedExpr, $compilationContext, $assignment); break; case 'array-index': $let = new LetArrayIndex(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'array-index-append': $let = new LetArrayIndexAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-append': $let = new LetObjectPropertyAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-array-index': $let = new LetObjectPropertyArrayIndex(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'object-property-array-index-append': $let = new LetObjectPropertyArrayIndexAppend(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'incr': $let = new LetIncr(); $let->assign($variable, $symbolVariable, $compilationContext, $assignment); break; case 'decr': $let = new LetDecr(); $let->assign($variable, $symbolVariable, $compilationContext, $assignment); break; case 'object-property-incr': $let = new LetObjectPropertyIncr(); $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment); break; case 'object-property-decr': $let = new LetObjectPropertyDecr(); $let->assign($variable, $assignment['property'], $symbolVariable, $compilationContext, $assignment); break; case 'dynamic-variable': $let = new LetExportSymbol(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; case 'dynamic-variable-string': $let = new LetExportSymbolString(); $let->assign($variable, $symbolVariable, $resolvedExpr, $compilationContext, $assignment); break; default: throw new CompilerException("Unknown assignment: " . $assignment['assign-type'], $assignment); } } }