/** * Compiles {var} = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $codePrinter = $compilationContext->codePrinter; $codePrinter->output('if (zephir_set_symbol(' . $symbolVariable->getName() . ', ' . $resolvedExpr->getCode() . ' TSRMLS_CC) == FAILURE){'); $codePrinter->output(' return;'); $codePrinter->output('}'); }
/** * Examine internal class information and returns the method called * * @param CompilationContext $compilationContext * @param Variable $caller * @param string $methodName * @return array */ private function getRealCalledMethod(CompilationContext $compilationContext, Variable $caller, $methodName) { $compiler = $compilationContext->compiler; $numberPoly = 0; $method = null; if ($caller->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if ($classDefinition->hasMethod($methodName)) { $numberPoly++; $method = $classDefinition->getMethod($methodName); } } else { $classTypes = $caller->getClassTypes(); foreach ($classTypes as $classType) { if ($compiler->isClass($classType) || $compiler->isInterface($classType) || $compiler->isBundledClass($classType) || $compiler->isBundledInterface($classType)) { if ($compiler->isInterface($classType)) { continue; } if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); } else { $classDefinition = $compiler->getInternalClassDefinition($classType); } if (!$classDefinition) { continue; } if ($classDefinition->hasMethod($methodName) && !$classDefinition->isInterface()) { $numberPoly++; $method = $classDefinition->getMethod($methodName); } } } } return array($numberPoly, $method); }
/** * Compiles obj->x++ * * @param string $variable * @param string $property * @param ZephirVariable $symbolVariable * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, $property, ZephirVariable $symbolVariable, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } /** * Arrays must be stored in the HEAP */ if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } /** * Only dynamic variables can be used as arrays */ if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: '" . $symbolVariable->getType() . "' as array", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to increment non-object dynamic variable', 'non-object-update', $statement); } $compilationContext->headersManager->add('kernel/object'); $compilationContext->codePrinter->output('RETURN_ON_FAILURE(zephir_property_incr(' . $symbolVariable->getName() . ', SL("' . $property . '") TSRMLS_CC));'); }
/** * Compiles obj->x++ * * @param string $variable * @param string $property * @param ZephirVariable $symbolVariable * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, $property, ZephirVariable $symbolVariable, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } /** * Arrays must be stored in the HEAP */ if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } /** * Only dynamic variables can be used as arrays */ if (!$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable type: '" . $symbolVariable->getType() . "' as array", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to increment non-object dynamic variable', 'non-object-update', $statement); } /** * Check if the variable to update is defined */ if ($symbolVariable->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } $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 ($symbolVariable->hasAnyDynamicType('object')) { $classType = current($symbolVariable->getClassTypes()); $compiler = $compilationContext->compiler; if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); if (!$classDefinition) { throw new CompilerException("Cannot locate class definition for class: " . $classType, $statement); } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classType . "' does not have a property called: '" . $property . "'", $statement); } } } } $compilationContext->headersManager->add('kernel/object'); $compilationContext->codePrinter->output('RETURN_ON_FAILURE(zephir_property_incr(' . $symbolVariable->getName() . ', SL("' . $property . '") TSRMLS_CC));'); }
/** * Compiles {var} = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $codePrinter = $compilationContext->codePrinter; $variable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext, $statement); $variable->setMustInitNull(true); $letStatement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $variable->getName(), 'operator' => 'assign', 'expr' => array('type' => $resolvedExpr->getType(), 'value' => $resolvedExpr->getCode(), 'file' => $statement['file'], 'line' => $statement['line'], 'char' => $statement['char']), 'file' => $statement['file'], 'line' => $statement['line'], 'char' => $statement['char'])))); $letStatement->compile($compilationContext); $codePrinter->output('if (zephir_set_symbol(' . $symbolVariable->getName() . ', ' . $variable->getName() . ' TSRMLS_CC) == FAILURE){'); $codePrinter->output("\t" . 'return;'); $codePrinter->output('}'); }
/** * Compiles foo[y][] = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { /** * Arrays must be stored in the HEAP */ if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } /** * Only dynamic variables and arrays can be used as arrays */ if ($symbolVariable->isNotVariableAndArray()) { throw new CompilerException("Cannot use variable type: '" . $symbolVariable->getType() . "' as array", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'array', 'null'))) { $compilationContext->logger->warning('Possible attempt to update index on a non-array dynamic variable', 'non-array-update', $statement); } $this->_assignArrayIndexMultiple($variable, $symbolVariable, $resolvedExpr, $compilationContext, $statement); }
/** * Compiles x-- * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } $codePrinter = $compilationContext->codePrinter; switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': case 'char': case 'uchar': $codePrinter->output($variable . '--;'); break; case 'variable': /** * Variable is probably not initialized here */ if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Attempt to increment uninitialized variable", $statement); } /** * Decrement non-numeric variables could be expensive */ if (!$symbolVariable->hasAnyDynamicType(array('undefined', 'int', 'long', 'double', 'uint'))) { $compilationContext->logger->warning('Possible attempt to decrement non-numeric dynamic variable', 'non-valid-decrement', $statement); } $compilationContext->headersManager->add('kernel/operators'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('zephir_decrement(&' . $variable . ');'); } else { $symbolVariable->separate($compilationContext); $codePrinter->output('zephir_decrement(' . $variable . ');'); } break; default: throw new CompilerException("Cannot decrement variable: " . $symbolVariable->getType(), $statement); } }
/** * Compiles x++ * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompilationContext $compilationContext * @param array $statement * * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->isReadOnly()) { /** * @TODO implement increment of objects members */ throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } $codePrinter =& $compilationContext->codePrinter; switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': case 'char': case 'uchar': $codePrinter->output($variable . '++;'); break; case 'variable': /** * Update non-numeric dynamic variables could be expensive */ if (!$symbolVariable->hasAnyDynamicType(array('undefined', 'long', 'double'))) { $compilationContext->logger->warning('Possible attempt to increment non-numeric dynamic variable', 'non-valid-increment', $statement); } $compilationContext->headersManager->add('kernel/operators'); if (!$symbolVariable->isLocalOnly()) { $symbolVariable->separate($compilationContext); } $symbol = $compilationContext->backend->getVariableCode($symbolVariable); $codePrinter->output('zephir_increment(' . $symbol . ');'); break; default: throw new CompilerException("Cannot increment: " . $symbolVariable->getType(), $statement); } }
/** * Compiles x->y[] = foo * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Attempt to use variable type: " . $symbolVariable->getType() . " as object", $statement); } $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Check if the variable to update is defined */ if ($symbolVariable->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } $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 ($symbolVariable->hasAnyDynamicType('object')) { $classType = current($symbolVariable->getClassTypes()); $compiler = $compilationContext->compiler; if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); if (!$classDefinition) { throw new CompilerException("Cannot locate class definition for class: " . $classType, $statement); } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classType . "' does not have a property called: '" . $property . "'", $statement); } } } } switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); break; case 'bool': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), (' . $resolvedExpr->getBooleanCode() . ') ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', \'' . $resolvedExpr->getCode() . '\');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_STRING(' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", 1);'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'array': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $variableExpr->getName() . ' TSRMLS_CC);'); break; case 'variable': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableExpr->getType()) { case 'int': case 'long': case 'uint': case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_BOOL(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'variable': case 'string': case 'array': case 'resource': case 'object': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $variableExpr->getName() . ' TSRMLS_CC);'); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } break; default: throw new CompilerException("Variable: " . $variableExpr->getType() . " cannot be appended to array property", $statement); } break; default: throw new CompilerException("Expression: " . $resolvedExpr->getType() . " cannot be appended to array property", $statement); } }
public function ifVariableValueUndefined(Variable $var, CompilationContext $context, $onlyBody = false, $useCodePrinter = true) { $body = '!' . $var->getName(); $output = 'if (' . $body . ') {'; if ($useCodePrinter) { $context->codePrinter->output($output); } return $onlyBody ? $body : $output; }
/** * @param array $expression * @param Variable $variableVariable * @param CompilationContext $compilationContext */ protected function _accessDimensionArray($expression, Variable $variableVariable, CompilationContext $compilationContext) { $arrayAccess = $expression; if ($variableVariable->getType() == 'variable') { if ($variableVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an array", $arrayAccess['left']); } /** * Trying to use a non-object dynamic variable as object */ if ($variableVariable->hasDifferentDynamicType(array('undefined', 'array', 'null'))) { $compilationContext->logger->warning('Possible attempt to access array-index on a non-array dynamic variable', 'non-array-access', $arrayAccess['left']); } } $codePrinter = $compilationContext->codePrinter; /** * Resolves the symbol that expects the value */ $readOnly = false; $symbolVariable = $this->_expectingVariable; if ($this->_readOnly) { if ($this->_expecting && $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->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression); } } } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression); } } else { if ($this->_expecting && $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); } } /** * Variable that receives property accesses must be polimorphic */ if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign array index", $expression); } /** * At this point, we don't know the type fetched from the index */ $symbolVariable->setDynamicTypes('undefined'); if ($this->_readOnly || $readOnly) { if ($this->_noisy) { $flags = 'PH_NOISY | PH_READONLY'; } else { $flags = 'PH_READONLY'; } } else { if ($this->_noisy) { $flags = 'PH_NOISY'; } else { $flags = '0'; } } /** * Right part of expression is the index */ $expr = new Expression($arrayAccess['right']); $exprIndex = $expr->compile($compilationContext); switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_long(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $exprIndex->getCode() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'string': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_string(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', SL("' . $exprIndex->getCode() . '"), ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $expression); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_long(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'string': case 'variable': $compilationContext->headersManager->add('kernel/array'); if ($variableIndex->isLocalOnly()) { $codePrinter->output('zephir_array_fetch(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', &' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_array_fetch(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); } break; default: throw new CompilerException("Variable type: " . $variableIndex->getType() . " cannot be used as array index without cast", $arrayAccess['right']); } break; default: throw new CompilerException("Cannot use expression: " . $exprIndex->getType() . " as array index without cast", $arrayAccess['right']); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Compiles foo = {expr} * Changes the value of a mutable variable * * @param $variable * @param Variable $symbolVariable * @param CompiledExpression $resolvedExpr * @param ReadDetector $readDetector * @param CompilationContext $compilationContext * @param $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, ReadDetector $readDetector, CompilationContext $compilationContext, $statement) { if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } $codePrinter = $compilationContext->codePrinter; /** * Only initialize variables if it's direct assignment */ if ($statement['operator'] == 'assign') { $symbolVariable->setIsInitialized(true, $compilationContext, $statement); } else { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } } /** * Set the assigned value to the variable as a CompiledExpression * We could use this expression for further analysis */ $symbolVariable->setPossibleValue($resolvedExpr, $compilationContext); $type = $symbolVariable->getType(); switch ($type) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': switch ($resolvedExpr->getType()) { case 'null': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = 0;'); break; case 'add-assign': $codePrinter->output($variable . ' += 0;'); break; case 'sub-assign': $codePrinter->output($variable . ' -= 0;'); break; case 'mul-assign': $codePrinter->output($variable . ' *= 0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'int': case 'uint': case 'long': case 'ulong': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $resolvedExpr->getCode() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $resolvedExpr->getCode() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $resolvedExpr->getCode() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= ' . $resolvedExpr->getCode() . ';'); break; case 'div-assign': $codePrinter->output($variable . ' /= ' . $resolvedExpr->getCode() . ';'); break; case 'mod-assign': $codePrinter->output($variable . ' %= ' . $resolvedExpr->getCode() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = \'' . $resolvedExpr->getCode() . '\';'); break; case 'add-assign': $codePrinter->output($variable . ' += \'' . $resolvedExpr->getCode() . '\';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= \'' . $resolvedExpr->getCode() . '\';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= \'' . $resolvedExpr->getCode() . '\';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (long) (' . $resolvedExpr->getCode() . ');'); break; case 'add-assign': $codePrinter->output($variable . ' += (long) (' . $resolvedExpr->getCode() . ');'); break; case 'sub-assign': $codePrinter->output($variable . ' -= (long) (' . $resolvedExpr->getCode() . ');'); break; case 'mul-assign': $codePrinter->output($variable . ' *= (long) (' . $resolvedExpr->getCode() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'bool': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $resolvedExpr->getBooleanCode() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $resolvedExpr->getBooleanCode() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'variable': $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($itemVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'bool': case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $itemVariable->getName() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $itemVariable->getName() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= ' . $itemVariable->getName() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (long) ' . $itemVariable->getName() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += (long) ' . $itemVariable->getName() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= (long) ' . $itemVariable->getName() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= (long) ' . $itemVariable->getName() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; case 'variable': $compilationContext->headersManager->add('kernel/operators'); switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');'); break; case 'add-assign': $codePrinter->output($variable . ' += zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');'); break; case 'sub-assign': $codePrinter->output($variable . ' -= zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');'); break; case 'mul-assign': $codePrinter->output($variable . ' *= zephir_get_numberval(' . $resolvedExpr->resolve(null, $compilationContext) . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: int", $statement); } break; default: throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Value type '" . $resolvedExpr->getType() . "' cannot be assigned to variable: int", $statement); } break; case 'double': switch ($resolvedExpr->getType()) { case 'null': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = 0.0;'); break; case 'add-assign': $codePrinter->output($variable . ' += 0.0;'); break; case 'sub-assign': $codePrinter->output($variable . ' -= 0.0;'); break; case 'mul-assign': $codePrinter->output($variable . ' *= 0.0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'int': case 'uint': case 'long': case 'ulong': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (double) (' . $resolvedExpr->getCode() . ');'); break; case 'add-assign': $codePrinter->output($variable . ' += (double) (' . $resolvedExpr->getCode() . ');'); break; case 'sub-assign': $codePrinter->output($variable . ' -= (double) (' . $resolvedExpr->getCode() . ');'); break; case 'mul-assign': $codePrinter->output($variable . ' *= (double) (' . $resolvedExpr->getCode() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $resolvedExpr->getCode() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $resolvedExpr->getCode() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $resolvedExpr->getCode() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= ' . $resolvedExpr->getCode() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'bool': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $resolvedExpr->getBooleanCode() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $resolvedExpr->getBooleanCode() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= ' . $resolvedExpr->getBooleanCode() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'variable': $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($itemVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'bool': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (double) ' . $itemVariable->getName() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += (double) ' . $itemVariable->getName() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= (double) ' . $itemVariable->getName() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= (double) ' . $itemVariable->getName() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';'); break; case 'add-assign': $codePrinter->output($variable . ' += ' . $itemVariable->getName() . ';'); break; case 'sub-assign': $codePrinter->output($variable . ' -= ' . $itemVariable->getName() . ';'); break; case 'mul-assign': $codePrinter->output($variable . ' *= ' . $itemVariable->getName() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; case 'variable': $compilationContext->headersManager->add('kernel/operators'); switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = zephir_get_numberval(' . $itemVariable->getName() . ');'); break; case 'add-assign': $codePrinter->output($variable . ' += zephir_get_numberval(' . $itemVariable->getName() . ');'); break; case 'sub-assign': $codePrinter->output($variable . ' -= zephir_get_numberval(' . $itemVariable->getName() . ');'); break; case 'mul-assign': $codePrinter->output($variable . ' *= zephir_get_numberval(' . $itemVariable->getName() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: double", $statement); } break; default: throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } break; case 'array': switch ($resolvedExpr->getType()) { case 'variable': case 'array': switch ($statement['operator']) { case 'assign': if ($variable != $resolvedExpr->getCode()) { $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); /* Inherit the dynamic type data from the assigned value */ $symbolVariable->setDynamicTypes('array'); $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; default: throw new CompilerException("You cannot {$statement['operator']} {$resolvedExpr->getType()} for array type", $resolvedExpr->getOriginal()); } break; case 'string': switch ($resolvedExpr->getType()) { case 'null': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'string': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); if ($resolvedExpr->getCode()) { $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);'); } else { $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');'); } break; case 'concat-assign': $codePrinter->output('zephir_concat_self_str(&' . $variable . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); if ($resolvedExpr->getCode()) { $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);'); } else { $codePrinter->output('ZVAL_EMPTY_STRING(' . $variable . ');'); } break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self_str(&' . $variable . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'variable': $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($itemVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $compilationContext->headersManager->add('kernel/string'); $codePrinter->output('Z_STRLEN_P(' . $variable . ') = zephir_spprintf(&Z_STRVAL_P(' . $variable . '), 0, "%ld", ' . $itemVariable->getName() . ');'); $codePrinter->output('Z_TYPE_P(' . $variable . ') = IS_STRING;'); break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self_long(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $compilationContext->headersManager->add('kernel/string'); $codePrinter->output('Z_STRLEN_P(' . $variable . ') = zephir_spprintf(&Z_STRVAL_P(' . $variable . '), 0, "%c", ' . $itemVariable->getName() . ');'); $codePrinter->output('Z_TYPE_P(' . $variable . ') = IS_STRING;'); break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self_char(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'string': switch ($statement['operator']) { case 'assign': $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); if ($variable != $itemVariable->getName()) { $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');'); } break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: string", $statement); } break; case 'variable': switch ($statement['operator']) { case 'assign': $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_get_strval(' . $variable . ', ' . $itemVariable->getName() . ');'); break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } break; case 'bool': switch ($resolvedExpr->getType()) { case 'null': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = 0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: null", $statement); } break; case 'int': case 'uint': case 'long': case 'ulong': case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (' . $resolvedExpr->getCode() . ') ? 1 : 0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement); } break; case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (\'' . $resolvedExpr->getCode() . '\') ? 1 : 0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement); } break; case 'bool': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $resolvedExpr->getBooleanCode() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $statement); } break; case 'variable': $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($itemVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'double': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = (' . $itemVariable->getName() . ') ? 1 : 0;'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'bool': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = ' . $itemVariable->getName() . ';'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'variable': case 'string': case 'array': switch ($statement['operator']) { case 'assign': $codePrinter->output($variable . ' = zephir_is_true(' . $itemVariable->getName() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Cannot assign variable: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $statement); } break; case 'variable': switch ($resolvedExpr->getType()) { case 'null': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('null'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('ZVAL_NULL(&' . $variable . ');'); } else { $codePrinter->output('ZVAL_NULL(' . $variable . ');'); } break; } break; case 'int': case 'uint': case 'long': case 'ulong': if ($symbolVariable->isLocalOnly()) { $symbol = '&' . $variable; } else { $symbol = $variable; } switch ($statement['operator']) { case 'mul-assign': case 'sub-assign': case 'add-assign': switch ($statement['operator']) { case 'mul-assign': $functionName = 'ZEPHIR_MUL_ASSIGN'; break; case 'sub-assign': $functionName = 'ZEPHIR_SUB_ASSIGN'; break; case 'add-assign': $functionName = 'ZEPHIR_ADD_ASSIGN'; break; } $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output($functionName . '(' . $symbol . ', ' . $tempVariable->getName() . ');'); break; case 'assign': $symbolVariable->setDynamicTypes('long'); if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('int', $compilationContext); $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';'); $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $tempVariable->getName() . ');'); } else { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $resolvedExpr->getCode() . ');'); } break; case 'div-assign': $symbolVariable->setDynamicTypes('double'); if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext); $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';'); $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $tempVariable->getName() . ');'); } else { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $resolvedExpr->getCode() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'char': case 'uchar': if ($symbolVariable->isLocalOnly()) { $symbol = '&' . $variable; } else { $symbol = $variable; } switch ($statement['operator']) { case 'assign': $symbolVariable->setDynamicTypes('long'); if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('char', $compilationContext); $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';'); $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_LONG(' . $symbol . ', ' . $tempVariable->getName() . ');'); } else { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_LONG(' . $symbol . ', \'' . $resolvedExpr->getCode() . '\');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'double': if ($symbolVariable->isLocalOnly()) { $symbol = '&' . $variable; } else { $symbol = $variable; } switch ($statement['operator']) { case 'assign': $symbolVariable->setDynamicTypes('double'); if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext); $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getCode() . ';'); $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $tempVariable->getName() . ');'); } else { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $symbol . ', ' . $resolvedExpr->getCode() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'bool': if ($symbolVariable->isLocalOnly()) { $symbol = '&' . $variable; } else { $symbol = $variable; } switch ($statement['operator']) { case 'assign': $symbolVariable->setDynamicTypes('bool'); if ($resolvedExpr->getCode() == 'true') { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_BOOL(' . $symbol . ', 1);'); } else { if ($resolvedExpr->getCode() == 'false') { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_BOOL(' . $symbol . ', 0);'); } else { if ($readDetector->detect($variable, $resolvedExpr->getOriginal())) { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('double', $compilationContext); $codePrinter->output($tempVariable->getName() . ' = ' . $resolvedExpr->getBooleanCode() . ';'); $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_BOOL(' . $symbol . ', ' . $tempVariable->getName() . ');'); } else { $symbolVariable->initVariant($compilationContext); $codePrinter->output('ZVAL_BOOL(' . $symbol . ', ' . $resolvedExpr->getBooleanCode() . ');'); } } } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'string': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('string'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('ZVAL_STRING(&' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);'); } else { $codePrinter->output('ZVAL_STRING(' . $variable . ', "' . $resolvedExpr->getCode() . '", 1);'); } break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self_str(&' . $variable . ', SL("' . $resolvedExpr->getCode() . '") TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'array': switch ($statement['operator']) { case 'assign': if ($variable != $resolvedExpr->getCode()) { $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); /* Inherit the dynamic type data from the assigned value */ $symbolVariable->setDynamicTypes('array'); $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'variable': $itemVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $resolvedExpr->getOriginal()); switch ($itemVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('long'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $itemVariable->getName() . ');'); } else { $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $itemVariable->getName() . ');'); } break; case 'add-assign': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('long'); $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite($itemVariable->getType(), $compilationContext); if ($symbolVariable->isLocalOnly()) { $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(&' . $variable . ');'); $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $tempVariable->getName() . ' + ' . $itemVariable->getName() . ');'); } else { $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(' . $variable . ');'); $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $tempVariable->getName() . ' + ' . $itemVariable->getName() . ');'); } break; case 'sub-assign': $compilationContext->headersManager->add('kernel/operators'); $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('long'); $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite($itemVariable->getType(), $compilationContext); if ($symbolVariable->isLocalOnly()) { $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(&' . $variable . ');'); $codePrinter->output('ZVAL_LONG(&' . $variable . ', ' . $tempVariable->getName() . ' - ' . $itemVariable->getName() . ');'); } else { $codePrinter->output($tempVariable->getName() . ' = zephir_get_numberval(' . $variable . ');'); $codePrinter->output('ZVAL_LONG(' . $variable . ', ' . $tempVariable->getName() . ' - ' . $itemVariable->getName() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'double': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('double'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('ZVAL_DOUBLE(&' . $variable . ', ' . $itemVariable->getName() . ');'); } else { $codePrinter->output('ZVAL_DOUBLE(' . $variable . ', ' . $itemVariable->getName() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'bool': switch ($statement['operator']) { case 'assign': $symbolVariable->initVariant($compilationContext); $symbolVariable->setDynamicTypes('bool'); if ($symbolVariable->isLocalOnly()) { $codePrinter->output('ZVAL_BOOL(&' . $variable . ', ' . $itemVariable->getName() . ');'); } else { $codePrinter->output('ZVAL_BOOL(' . $variable . ', ' . $itemVariable->getName() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'array': switch ($statement['operator']) { case 'assign': if ($variable != $resolvedExpr->getCode()) { $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); /* Inherit the dynamic type data from the assigned value */ $symbolVariable->setDynamicTypes('array'); $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $resolvedExpr->getCode() . ');'); } break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; case 'variable': switch ($statement['operator']) { case 'assign': if ($itemVariable->getName() != $variable) { $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); /* Inherit the dynamic type data from the assigned value */ $symbolVariable->setDynamicTypes($itemVariable->getDynamicTypes()); $symbolVariable->setClassTypes($itemVariable->getClassTypes()); $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');'); if ($itemVariable->isTemporal()) { $itemVariable->setIdle(true); } } break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; case 'add-assign': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('ZEPHIR_ADD_ASSIGN(' . $variable . ', ' . $itemVariable->getName() . ');'); break; case 'sub-assign': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('ZEPHIR_SUB_ASSIGN(' . $variable . ', ' . $itemVariable->getName() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; case 'string': switch ($statement['operator']) { case 'assign': if ($itemVariable->getName() != $variable) { $symbolVariable->setMustInitNull(true); $compilationContext->symbolTable->mustGrownStack(true); /* Inherit the dynamic type data from the assigned value */ $symbolVariable->setDynamicTypes($itemVariable->getDynamicTypes()); $symbolVariable->setClassTypes($itemVariable->getClassTypes()); $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable . ', ' . $itemVariable->getName() . ');'); if ($itemVariable->isTemporal()) { $itemVariable->setIdle(true); } } break; case 'concat-assign': $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output('zephir_concat_self(&' . $variable . ', ' . $itemVariable->getName() . ' TSRMLS_CC);'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for variable type: " . $itemVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type: " . $itemVariable->getType(), $resolvedExpr->getOriginal()); } break; default: throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $resolvedExpr->getOriginal()); } break; default: throw new CompilerException("Unknown type: " . $type, $statement); } }
/** * Compiles foo[] = {expr} * * @param $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: '" . $symbolVariable->getType() . "' as array", $statement); } if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'array'))) { $compilationContext->logger->warning('Possible attempt to append elements on a non-array dynamic variable', 'non-array-append', $statement); } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/array'); $type = $symbolVariable->getType(); switch ($type) { case 'variable': switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('zephir_array_append(&' . $variable . ', ZEPHIR_GLOBAL(global_null), PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); break; case 'int': case 'uint': case 'long': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_LONG(' . $symbolVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'double': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_DOUBLE(' . $symbolVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'bool': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_BOOL(' . $symbolVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'ulong': case 'string': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_STRING(' . $symbolVariable->getName() . ', "' . Utils::addSlashes($resolvedExpr->getCode(), true) . '", 1);'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'array': $exprVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $exprVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); break; case 'variable': $exprVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($exprVariable->getType()) { case 'int': case 'uint': case 'long': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_LONG(' . $symbolVariable->getName() . ', ' . $exprVariable->getName() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'double': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_DOUBLE(' . $symbolVariable->getName() . ', ' . $exprVariable->getName() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'bool': $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_BOOL(' . $symbolVariable->getName() . ', ' . $exprVariable->getName() . ');'); $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $symbolVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $symbolVariable->setIdle(true); break; case 'variable': case 'string': case 'array': $codePrinter->output('zephir_array_append(&' . $variable . ', ' . $exprVariable->getName() . ', PH_SEPARATE, "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); break; default: throw new CompilerException("Unknown type " . $exprVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } break; default: throw new CompilerException("Unknown type", $statement); } }
/** * Return a variable in the symbol table, it will be used for a mutating operation * This method implies mutation of one of the members of the variable but no the variables it self * * @param string $name * @param CompilationContext $compilationContext * @param array $statement * @return Variable */ public function getVariableForUpdate($name, CompilationContext $compilationContext, array $statement = null) { /** * Create superglobals just in time */ if ($this->isSuperGlobal($name)) { if (!$this->hasVariable($name)) { /** * @TODO, injecting globals, initialize to null and check first? */ $superVar = new Variable('variable', $name, $compilationContext->currentBranch); $superVar->setIsInitialized(true, $compilationContext, $statement); $superVar->setDynamicTypes('array'); $superVar->increaseMutates(); $superVar->increaseUses(); $superVar->setIsExternal(true); $superVar->setUsed(true, $statement); $this->variables[$name] = $superVar; return $superVar; } } if (!$this->hasVariable($name)) { throw new CompilerException("Cannot mutate variable '" . $name . "' because it wasn't defined", $statement); } $variable = $this->getVariable($name); $variable->increaseUses(); $variable->increaseMutates(); /** * Saves the last place where the variable was mutated * We discard mutations inside loops because iterations could use the value * and Zephir only provides top-down compilation */ $variable->setUsed(true, $statement); return $variable; }
/** * Compiles foo[] = {expr} * * @param $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is local only", $statement); } /** * Only dynamic variables and arrays can be used as arrays */ if ($symbolVariable->isNotVariableAndArray()) { throw new CompilerException("Cannot use variable type: '" . $symbolVariable->getType() . "' as an array", $statement); } if ($symbolVariable->getType() == 'variable') { if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'array'))) { $compilationContext->logger->warning('Possible attempt to append elements on a non-array dynamic variable', 'non-array-append', $statement); } } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/array'); $type = $symbolVariable->getType(); switch ($type) { case 'array': case 'variable': switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->addArrayEntry($symbolVariable, null, 'null', $compilationContext, $statement); break; case 'int': case 'uint': case 'long': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignBool($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'ulong': case 'string': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'array': $exprVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $compilationContext->backend->addArrayEntry($symbolVariable, null, $exprVariable, $compilationContext, $statement); break; case 'variable': $exprVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($exprVariable->getType()) { case 'int': case 'uint': case 'long': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignLong($tempVariable, $exprVariable, $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignDouble($tempVariable, $exprVariable, $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $compilationContext->backend->assignBool($tempVariable, $exprVariable, $compilationContext); $compilationContext->backend->addArrayEntry($symbolVariable, null, $tempVariable, $compilationContext, $statement); $tempVariable->setIdle(true); break; case 'variable': case 'string': case 'array': $compilationContext->backend->addArrayEntry($symbolVariable, null, $exprVariable, $compilationContext, $statement); break; default: throw new CompilerException("Unknown type: " . $exprVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $statement); } break; default: throw new CompilerException("Unknown type: " . $type, $statement); } }
/** * Compiles foo->{"x"} = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException * @throws \Exception */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Variable type '" . $symbolVariable->getType() . "' cannot be used as object", $statement); } $propertyName = $statement['property']; if (!is_string($propertyName)) { throw new CompilerException("Expected string to update object property, " . gettype($propertyName) . " received", $statement); } if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate static property '" . $compilationContext->classDefinition->getCompleteName() . "::" . $propertyName . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " as an object", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to update property on non-object dynamic property', 'non-valid-objectupdate', $statement); } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'null', $compilationContext); break; case 'int': case 'long': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); break; case 'bool': if ($resolvedExpr->getBooleanCode() == '1') { $value = 'true'; } else { if ($resolvedExpr->getBooleanCode() == '0') { $value = 'false'; } else { throw new \Exception("?"); } } $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $value, $compilationContext); break; case 'empty-array': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->initArray($tempVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); break; case 'array': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $variableVariable, $compilationContext); break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignBool($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); break; case 'string': case 'variable': case 'array': $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $resolvedExpr, $compilationContext); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * Compiles traversing of hash values * - Evaluated expression must be a zval * - Every key must be a zval * - Every value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, $compilationContext, $exprVariable) { $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { $keyVariable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['key'], $compilationContext, $this->_statement['expr']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); if ($variable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in hash traversal", $this->_statement['expr']); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); $variable->setDynamicTypes('undefined'); } /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Create a hash table and hash pointer temporary variables */ $arrayPointer = $compilationContext->symbolTable->addTemp('HashPosition', $compilationContext); $arrayHash = $compilationContext->symbolTable->addTemp('HashTable', $compilationContext); /** * Create a temporary zval to fetch the items from the hash */ $tempVariable = $compilationContext->symbolTable->addTemp('variable', $compilationContext); $tempVariable->setIsDoublePointer(true); $compilationContext->headersManager->add('kernel/hash'); /** * We have to check if hashes are modified within the for's block */ $duplicateHash = '0'; if (isset($this->_statement['statements'])) { $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } } $codePrinter->output('zephir_is_iterable(' . $expression->getCode() . ', &' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ', ' . $duplicateHash . ', ' . $this->_statement['reverse'] . ', "' . Compiler::getShortUserPath($this->_statement['file']) . '", ' . $this->_statement['line'] . ');'); $codePrinter->output('for ('); $codePrinter->output(' ; zephir_hash_get_current_data_ex(' . $arrayHash->getName() . ', (void**) &' . $tempVariable->getName() . ', &' . $arrayPointer->getName() . ') == SUCCESS'); if ($this->_statement['reverse']) { $codePrinter->output(' ; zephir_hash_move_backwards_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } else { $codePrinter->output(' ; zephir_hash_move_forward_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } $codePrinter->output(') {'); if (isset($this->_statement['key'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HMKEY(' . $this->_statement['key'] . ', ' . $arrayHash->getName() . ', ' . $arrayPointer->getName() . ');'); } if (isset($this->_statement['value'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HVALUE(' . $this->_statement['value'] . ', ' . $tempVariable->getName() . ');'); } /** * Compile statements in the 'for' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); }
/** * Compiles x->y[z] = foo * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Attempt to use variable type: " . $symbolVariable->getType() . " as object", $statement); } /** * Update the property according to the number of array-offsets */ if (count($statement['index-expr']) == 1) { $this->_assignPropertyArraySingleIndex($variable, $symbolVariable, $resolvedExpr, $compilationContext, $statement); } else { $this->_assignPropertyArrayMultipleIndex($variable, $symbolVariable, $resolvedExpr, $compilationContext, $statement); } }
/** * Compiles foo->x = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Variable type '" . $symbolVariable->getType() . "' cannot be used as object", $statement); } $propertyName = $statement['property']; $className = $compilationContext->classDefinition->getCompleteName(); if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate static property '" . $className . "::" . $propertyName . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " as an object", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object'))) { $compilationContext->logger->warning('Possible attempt to update property on non-object dynamic property', 'non-valid-objectupdate', $statement); } /** * Try to check if property is implemented on related object */ if ($variable == 'this') { if (!$compilationContext->classDefinition->hasProperty($propertyName)) { throw new CompilerException("Property '" . $propertyName . "' is not defined on class '" . $className . "'", $statement); } } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'null', $compilationContext); break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'mul-assign': case 'sub-assign': case 'add-assign': switch ($statement['operator']) { case 'mul-assign': $functionName = 'ZEPHIR_MUL_ASSIGN'; break; case 'sub-assign': $functionName = 'ZEPHIR_SUB_ASSIGN'; break; case 'add-assign': $functionName = 'ZEPHIR_ADD_ASSIGN'; break; } $resolvedVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignLong($resolvedVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $codePrinter->output($functionName . '(' . $compilationContext->backend->getVariableCode($tempVariable) . ', ' . $compilationContext->backend->getVariableCode($resolvedVariable) . ')'); break; case 'assign': $tempVariable->initNonReferenced($compilationContext); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'assign': $tempVariable->initNonReferenced($compilationContext); $compilationContext->backend->assignLong($tempVariable, '\'' . $resolvedExpr->getBooleanCode() . '\'', $compilationContext); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'mul-assign': case 'sub-assign': case 'add-assign': switch ($statement['operator']) { case 'mul-assign': $functionName = 'ZEPHIR_MUL_ASSIGN'; break; case 'sub-assign': $functionName = 'ZEPHIR_SUB_ASSIGN'; break; case 'add-assign': $functionName = 'ZEPHIR_ADD_ASSIGN'; break; } $resolvedVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $compilationContext->backend->assignDouble($resolvedVariable, $resolvedExpr->getBooleanCode(), $compilationContext); $codePrinter->output($functionName . '(' . $compilationContext->backend->getVariableCode($tempVariable) . ', ' . $compilationContext->backend->getVariableCode($resolvedVariable) . ')'); break; case 'assign': $tempVariable->initNonReferenced($compilationContext); $compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getBooleanCode(), $compilationContext); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'string': if ($compilationContext->backend->getName() == 'ZendEngine2') { $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); } else { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, false); } switch ($statement['operator']) { case 'concat-assign': $codePrinter->output('zephir_concat_self_str(&' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);'); break; case 'assign': /* We only can use nonReferenced variables for not refcounted stuff in ZE3 */ if ($compilationContext->backend->getName() == 'ZendEngine2') { $tempVariable->initNonReferenced($compilationContext); } else { $tempVariable->initVariant($compilationContext); } $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext); break; } $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'array': $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $resolvedExpr, $compilationContext); break; case 'bool': $codePrinter->output('if (' . $resolvedExpr->getBooleanCode() . ') {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'true', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'false', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); break; /* unreachable code */ /* unreachable code */ case 'empty-array': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); $compilationContext->backend->initArray($tempVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $resolvedExpr->getCode(), $compilationContext); $tempVariable->setIdle(true); break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $variableVariable, $compilationContext); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $tempVariable, $compilationContext); $tempVariable->setIdle(true); break; case 'bool': $codePrinter->output('if (' . $variableVariable->getName() . ') {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'true', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $compilationContext->backend->updateProperty($symbolVariable, $propertyName, 'false', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); break; case 'array': case 'string': case 'variable': $compilationContext->backend->updateProperty($symbolVariable, $propertyName, $variableVariable, $compilationContext); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * Calls static methods on using a dynamic variable as class and a dynamic method * * @param array $expression * @param Variable $symbolVariable * @param boolean $mustInit * @param boolean $isExpecting * @param CompilationContext $compilationContext */ protected function callFromDynamicClassDynamicMethod(array $expression, $symbolVariable, $mustInit, $isExpecting, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; /** * Call static methods must grown the stack */ $compilationContext->symbolTable->mustGrownStack(true); if ($mustInit) { $symbolVariable->setMustInitNull(true); $symbolVariable->trackVariant($compilationContext); } $cachePointer = 'NULL'; if (isset($expression['parameters']) && count($expression['parameters'])) { $params = $this->getResolvedParams($expression['parameters'], $compilationContext, $expression); } else { $params = array(); } /** * Obtain the class entry from the variable */ $classNameVariable = $compilationContext->symbolTable->getVariableForRead($expression['class'], $compilationContext, $expression); if ($classNameVariable->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in dynamic classes", $expression); } /** * @todo Validate if the variable is really a string! */ $classEntryVariable = $compilationContext->symbolTable->addTemp('zend_class_entry', $compilationContext); $codePrinter->output($classEntryVariable->getName() . ' = zend_fetch_class(Z_STRVAL_P(' . $classNameVariable->getName() . '), Z_STRLEN_P(' . $classNameVariable->getName() . '), ZEND_FETCH_CLASS_AUTO TSRMLS_CC);'); $classEntry = $classEntryVariable->getName(); /** * Obtain the method name from the variable */ $methodNameVariable = $compilationContext->symbolTable->getVariableForRead($expression['name'], $compilationContext, $expression); if ($methodNameVariable->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in dynamic methods", $expression); } /** * @todo Validate if the variable is really a string! */ $methodName = 'Z_STRVAL_P(' . $methodNameVariable->getName() . ')'; if (!count($params)) { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_CE_STATIC(' . $classEntry . ', ' . $methodName . ', ' . $cachePointer . ');'); } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(&' . $symbolVariable->getName() . ', ' . $classEntry . ', ' . $methodName . ', ' . $cachePointer . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(NULL, ' . $classEntry . ', ' . $methodName . ', ' . $cachePointer . ');'); } } else { if ($isExpecting) { if ($symbolVariable->getName() == 'return_value') { $codePrinter->output('ZEPHIR_RETURN_CALL_CE_STATIC(' . $classEntry . ', ' . $methodName . ', ' . $cachePointer . ', ' . join(', ', $params) . ');'); } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(&' . $symbolVariable->getName() . ', ' . $classEntry . ', ' . $methodName . ', ' . $cachePointer . ', ' . join(', ', $params) . ');'); } } else { $codePrinter->output('ZEPHIR_CALL_CE_STATIC(NULL, ' . $classEntry . ', ' . $methodName . ', ' . $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 . ');'); } $this->addCallStatusOrJump($compilationContext); }
/** * Add the last-call-status flag to the current symbol table * * @param CompilationContext $compilationContext */ public function addCallStatusFlag(CompilationContext $compilationContext) { if (!$compilationContext->symbolTable->hasVariable('ZEPHIR_LAST_CALL_STATUS')) { $callStatus = new Variable('int', 'ZEPHIR_LAST_CALL_STATUS', $compilationContext->currentBranch); $callStatus->setIsInitialized(true, $compilationContext, array()); $callStatus->increaseUses(); $callStatus->setReadOnly(true); $compilationContext->symbolTable->addRawVariable($callStatus); } }
/** * Compiles x->y[] = foo * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Attempt to use variable type: " . $symbolVariable->getType() . " as object", $statement); } $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Check if the variable to update is defined */ if ($symbolVariable->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement); } $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 ($symbolVariable->hasAnyDynamicType('object')) { $classType = current($symbolVariable->getClassTypes()); $compiler = $compilationContext->compiler; if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); if (!$classDefinition) { throw new CompilerException("Cannot locate class definition for class: " . $classType, $statement); } if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classType . "' does not have a property called: '" . $property . "'", $statement); } } } } switch ($resolvedExpr->getType()) { case 'null': $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, 'null', $compilationContext); break; case 'bool': $codePrinter->output('if (' . $resolvedExpr->getBooleanCode() . ') {'); $codePrinter->increaseLevel(); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, 'true', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, 'false', $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, '\'' . $resolvedExpr->getCode() . '\'', $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignString($tempVariable, $resolvedExpr->getCode(), $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'array': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $variableExpr, $compilationContext); break; case 'variable': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableExpr->getType()) { case 'int': case 'long': case 'uint': case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($tempVariable, $variableExpr, $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignDouble($tempVariable, $variableExpr, $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $compilationContext->backend->assignBool($tempVariable, $variableExpr, $compilationContext); $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $tempVariable, $compilationContext); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'variable': case 'string': case 'array': case 'resource': case 'object': $compilationContext->backend->assignArrayProperty($symbolVariable, $property, null, $variableExpr, $compilationContext); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } break; default: throw new CompilerException("Variable: " . $variableExpr->getType() . " cannot be appended to array property", $statement); } break; default: throw new CompilerException("Expression: " . $resolvedExpr->getType() . " cannot be appended to array property", $statement); } }
/** * Compiles x->y[z][] = foo * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if (!$symbolVariable->isVariable()) { throw new CompilerException("Attempt to use variable type: " . $symbolVariable->getType() . " as object", $statement); } $this->_assignPropertyArrayMultipleIndex($variable, $symbolVariable, $resolvedExpr, $compilationContext, $statement); }
/** * Compiles traversing of hash values * * - Evaluated expression must be a zval * - A key must be a zval * - A value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, CompilationContext $compilationContext, Variable $exprVariable) { $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { if ($this->_statement['key'] != '_') { $keyVariable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['key'], $compilationContext, $this->_statement['expr']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } } else { $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { if ($this->_statement['value'] != '_') { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); if ($variable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in hash traversal", $this->_statement['expr']); } } else { $variable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); $variable->setDynamicTypes('undefined'); } /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; $compilationContext->headersManager->add('kernel/hash'); $duplicateHash = '0'; $duplicateKey = true; /** * We have to check if hashes are modified within the for's block */ if (isset($this->_statement['statements'])) { /** * Create the statements block here to obtain the last use line */ $st = new StatementsBlock($this->_statement['statements']); $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } /** * Detect if the key is modified or passed to an external scope */ if (isset($this->_statement['key'])) { if (!$keyVariable->isTemporal()) { $detector->setDetectionFlags(ForValueUseDetector::DETECT_ALL); if ($detector->detect($keyVariable->getName(), $this->_statement['statements'])) { $loopContext = $compilationContext->currentMethod->getLocalContextPass(); //echo $st->getLastLine(); //echo $loopContext->getLastVariableUseLine($keyVariable->getName()); $duplicateKey = true; } } } } $compilationContext->backend->forStatement($exprVariable, isset($this->_statement['key']) ? $keyVariable : null, isset($this->_statement['value']) ? $variable : null, $duplicateKey, $duplicateHash, $this->_statement, $st, $compilationContext); /** * Restore the cycle counter */ $compilationContext->insideCycle--; }
/** * Compiles foo->{x} = {expr} * * @param string $variable * @param Variable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Variable type '" . $symbolVariable->getType() . "' cannot be used as object", $statement); } $propertyName = $statement['property']; $propertyVariable = $compilationContext->symbolTable->getVariableForRead($propertyName, $compilationContext, $statement); if ($propertyVariable->isNotVariableAndString()) { throw new CompilerException("Cannot use variable type '" . $propertyVariable->getType() . "' to update object property", $statement); } if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate static property '" . $compilationContext->classDefinition->getCompleteName() . "::" . $propertyName . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " as an object", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to update property on non-object dynamic property', 'non-valid-objectupdate', $statement); } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); $propertyVariableName = $compilationContext->symbolTable->getVariable($propertyName); switch ($resolvedExpr->getType()) { case 'null': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); } break; case 'int': case 'long': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_STRING(' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", 1);'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } break; case 'bool': if ($variable == 'this') { if ($resolvedExpr->getBooleanCode() == '1') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_true) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } } else { if ($resolvedExpr->getBooleanCode() == '1') { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_true) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } } break; case 'empty-array': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('array_init(' . $tempVariable->getName() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $variableVariable->getName() . ');'); $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_BOOL(' . $tempVariable->getName() . ', ' . $variableVariable->getName() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $propertyVariableName->getName() . '), Z_STRLEN_P(' . $propertyVariableName->getName() . '), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } break; case 'string': case 'variable': $codePrinter->output('zephir_update_property_zval_zval(' . $symbolVariable->getName() . ', ' . $propertyVariable->getName() . ', ' . $resolvedExpr->getCode() . ' TSRMLS_CC);'); if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * Retrieves/Creates a function cache for a method call * * @param CompilationContext $compilationContext * @param ClassMethod $method * @param Variable $caller */ public function get(CompilationContext $compilationContext, $methodName, Variable $caller) { $compiler = $compilationContext->compiler; $numberPoly = 0; if ($caller->getRealName() == 'this') { $classDefinition = $compilationContext->classDefinition; if ($classDefinition->hasMethod($methodName)) { $numberPoly++; $method = $classDefinition->getMethod($methodName); } } else { $classTypes = $caller->getClassTypes(); foreach ($classTypes as $classType) { if ($compiler->isClass($classType) || $compiler->isInterface($classType) || $compiler->isBundledClass($classType) || $compiler->isBundledInterface($classType)) { if ($compiler->isInterface($classType)) { continue; } if ($compiler->isClass($classType)) { $classDefinition = $compiler->getClassDefinition($classType); } else { $classDefinition = $compiler->getInternalClassDefinition($classType); } if (!$classDefinition) { continue; } if ($classDefinition->hasMethod($methodName) && !$classDefinition->isInterface()) { $numberPoly++; $method = $classDefinition->getMethod($methodName); } } } } if (!$numberPoly) { // Try to generate a cache based on the fact the variable is not modified within the loop block if ($compilationContext->insideCycle && !$caller->isTemporal()) { if (count($compilationContext->cycleBlocks) && $caller->getType() == 'variable') { $currentBlock = $compilationContext->cycleBlocks[count($compilationContext->cycleBlocks) - 1]; if ($currentBlock->getMutateGatherer(true)->getNumberOfMutations($caller->getName()) == 0) { $functionCache = $compilationContext->symbolTable->getTempVariableForWrite('zephir_fcall_cache_entry', $compilationContext); $functionCache->setMustInitNull(true); $functionCache->setReusable(false); return '&' . $functionCache->getName() . ', 0'; } } } return 'NULL, 0'; } if (!$method instanceof \ReflectionMethod) { if ($method->getClassDefinition()->isExternal()) { return 'NULL, 0'; } $completeName = $method->getClassDefinition()->getCompleteName(); if (isset($this->cache[$completeName][$method->getName()])) { return $this->cache[$completeName][$method->getName()] . ', ' . SlotsCache::getExistingMethodSlot($method); } $gatherer = $this->gatherer; if (is_object($gatherer)) { $number = $gatherer->getNumberOfMethodCalls($method->getClassDefinition()->getCompleteName(), $method->getName()); } else { $number = 0; } $staticCacheable = !$method->getClassDefinition()->isInterface() && ($compilationContext->currentMethod == $method || $method->getClassDefinition()->isFinal() || $method->isFinal() || $method->isPrivate()); if ($number > 1 || $compilationContext->insideCycle) { $cacheable = true; } else { $cacheable = false; } } else { $staticCacheable = false; $cacheable = false; } // Recursive methods require warm-up if ($compilationContext->currentMethod == $method) { if (!$compilationContext->methodWarmUp) { $compilationContext->methodWarmUp = new MethodCallWarmUp(); } } $associatedClass = false; if ($caller->getName() != 'this_ptr') { $associatedClass = $caller->getAssociatedClass(); if ($this->isClassCacheable($associatedClass)) { $staticCacheable = true; } } if ($staticCacheable) { $cacheSlot = SlotsCache::getMethodSlot($method); } else { $cacheSlot = '0'; } if ($cacheable) { $functionCacheVar = $compilationContext->symbolTable->getTempVariableForWrite('zephir_fcall_cache_entry', $compilationContext); $functionCacheVar->setMustInitNull(true); $functionCacheVar->setReusable(false); $functionCache = '&' . $functionCacheVar->getName(); } else { $functionCache = 'NULL'; } if (!$method instanceof \ReflectionMethod) { $this->cache[$completeName][$method->getName()] = $functionCache; } return $functionCache . ', ' . $cacheSlot; }
public function ifVariableValueUndefined(Variable $var, CompilationContext $context, $useBody = false, $useCodePrinter = true) { if ($var->isDoublePointer()) { return parent::ifVariableValueUndefined($var, $context, $useBody, $useCodePrinter); } $body = 'Z_TYPE_P(' . $this->getVariableCode($var) . ') == IS_UNDEF'; $output = 'if (' . $body . ') {'; if ($useCodePrinter) { $context->codePrinter->output($output); } return $useBody ? $body : $output; }
/** * Compiles foo->x = {expr} * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Variable type '" . $symbolVariable->getType() . "' cannot be used as object", $statement); } $propertyName = $statement['property']; $className = $compilationContext->classDefinition->getCompleteName(); if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate static property '" . $className . "::" . $propertyName . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " as an object", $statement); } if ($symbolVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an object", $statement); } /** * Trying to use a non-object dynamic variable as object */ if ($symbolVariable->hasDifferentDynamicType(array('undefined', 'object'))) { $compilationContext->logger->warning('Possible attempt to update property on non-object dynamic property', 'non-valid-objectupdate', $statement); } /** * Try to check if property is implemented on related object */ if ($variable == 'this') { if (!$compilationContext->classDefinition->hasProperty($propertyName)) { throw new CompilerException("Property '" . $propertyName . "' is not defined on class '" . $className . "'", $statement); } } $codePrinter = $compilationContext->codePrinter; $compilationContext->headersManager->add('kernel/object'); switch ($resolvedExpr->getType()) { case 'null': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); } break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'mul-assign': case 'sub-assign': case 'add-assign': switch ($statement['operator']) { case 'mul-assign': $functionName = 'ZEPHIR_MUL_ASSIGN'; break; case 'sub-assign': $functionName = 'ZEPHIR_SUB_ASSIGN'; break; case 'add-assign': $functionName = 'ZEPHIR_ADD_ASSIGN'; break; } $resolvedVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $resolvedVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); $codePrinter->output($functionName . '(' . $tempVariable->getName() . ', ' . $resolvedVariable->getName() . ')'); break; case 'assign': $tempVariable->initNonReferenced($compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'assign': $tempVariable->initNonReferenced($compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', \'' . $resolvedExpr->getBooleanCode() . '\');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'mul-assign': case 'sub-assign': case 'add-assign': switch ($statement['operator']) { case 'mul-assign': $functionName = 'ZEPHIR_MUL_ASSIGN'; break; case 'sub-assign': $functionName = 'ZEPHIR_SUB_ASSIGN'; break; case 'add-assign': $functionName = 'ZEPHIR_ADD_ASSIGN'; break; } $resolvedVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $resolvedVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); $codePrinter->output($functionName . '(' . $tempVariable->getName() . ', ' . $resolvedVariable->getName() . ')'); break; case 'assign': $tempVariable->initNonReferenced($compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $resolvedExpr->getBooleanCode() . ');'); break; default: throw new CompilerException("Operator '" . $statement['operator'] . "' is not supported for object property: " . $tempVariable->getType(), $statement); } if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); switch ($statement['operator']) { case 'concat-assign': $codePrinter->output('zephir_concat_self_str(&' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", sizeof("' . $resolvedExpr->getCode() . '")-1 TSRMLS_CC);'); break; case 'assign': $tempVariable->initNonReferenced($compilationContext); $codePrinter->output('ZVAL_STRING(' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", 1);'); break; } if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'array': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $resolvedExpr->getCode() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $resolvedExpr->getCode() . ' TSRMLS_CC);'); } break; case 'bool': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), (' . $resolvedExpr->getBooleanCode() . ') ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), (' . $resolvedExpr->getBooleanCode() . ') ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } break; case 'empty-array': /* unreachable code */ $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext); $codePrinter->output('array_init(' . $tempVariable->getName() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $variableVariable->getName() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $variableVariable->getName() . ');'); if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); } $tempVariable->setIdle(true); break; case 'bool': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $variableVariable->getName() . ' ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $variableVariable->getName() . ' ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } break; case 'array': case 'string': case 'variable': if ($variable == 'this') { $codePrinter->output('zephir_update_property_this(this_ptr, SL("' . $propertyName . '"), ' . $variableVariable->getName() . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_update_property_zval(' . $symbolVariable->getName() . ', SL("' . $propertyName . '"), ' . $variableVariable->getName() . ' TSRMLS_CC);'); } if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Unknown type " . $variableVariable->getType(), $statement); } break; default: throw new CompilerException("Unknown type " . $resolvedExpr->getType(), $statement); } }
/** * Compiles foo = {expr} * Changes the value of a mutable variable * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param ReadDetector $readDetector * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, ReadDetector $readDetector, CompilationContext $compilationContext, array $statement) { if ($symbolVariable->isReadOnly()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is read only", $statement); } $codePrinter = $compilationContext->codePrinter; /** * Only initialize variables if it's direct assignment */ if ($statement['operator'] == 'assign') { $symbolVariable->setIsInitialized(true, $compilationContext, $statement); } else { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } } /** * Set the assigned value to the variable as a CompiledExpression * We could use this expression for further analysis */ $symbolVariable->setPossibleValue($resolvedExpr, $compilationContext); $type = $symbolVariable->getType(); switch ($type) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': $this->doNumericAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext); break; case 'double': $this->doDoubleAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext); break; case 'array': $this->doArrayAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext); break; case 'string': $this->doStringAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext); break; case 'bool': $this->doBoolAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext); break; case 'variable': $this->doVariableAssignment($codePrinter, $resolvedExpr, $symbolVariable, $variable, $statement, $compilationContext, $readDetector); break; default: throw new CompilerException("Unknown type: " . $type, $statement); } }
/** * Compiles x->y[] = foo * * @param string $variable * @param Variable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext, * @param array $statement */ public function assign($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { if (!$symbolVariable->isInitialized()) { throw new CompilerException("Cannot mutate variable '" . $variable . "' because it is not initialized", $statement); } if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Attempt to use variable type: " . $symbolVariable->getType() . " as object", $statement); } $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /* @todo check whether property really does exist */ switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); break; case 'bool': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), (' . $resolvedExpr->getBooleanCode() . ') ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', \'' . $resolvedExpr->getCode() . '\');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_STRING(' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", 1);'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'array': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $variableExpr->getName() . ' TSRMLS_CC);'); break; case 'variable': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement); switch ($variableExpr->getType()) { case 'int': case 'long': case 'uint': case 'char': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'bool': $tempVariable = $compilationContext->symbolTable->getTempNonTrackedVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_BOOL(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $tempVariable->getName() . ' TSRMLS_CC);'); if ($tempVariable->isTemporal()) { $tempVariable->setIdle(true); } break; case 'variable': case 'string': case 'array': case 'resource': case 'object': $codePrinter->output('zephir_update_property_array_append(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $variableExpr->getName() . ' TSRMLS_CC);'); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } break; default: throw new CompilerException("Variable: " . $variableExpr->getType() . " cannot be appended to array property", $statement); } break; default: throw new CompilerException("Expression: " . $resolvedExpr->getType() . " cannot be appended to array property", $statement); } }