/** * 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->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 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[] = 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); } }
/** * Compiles x->y[a][b][] = {expr} (multiple offset assignment) * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ protected function _assignPropertyArrayMultipleIndex($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Create a temporal zval (if needed) */ $variableExpr = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); if (count($statement['index-expr']) > 16) { throw new CompilerException("Too many array indexes", $statement); } /** * Only string/variable/int */ $offsetExprs = array(); foreach ($statement['index-expr'] as $indexExpr) { $indexExpression = new Expression($indexExpr); $resolvedIndex = $indexExpression->compile($compilationContext); switch ($resolvedIndex->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Expression: " . $resolvedIndex->getType() . " cannot be used as index without cast", $statement); } $offsetExprs[] = $resolvedIndex; } /** * Check if the property 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); } } } } $offsetExprs[] = 'a'; $compilationContext->backend->assignPropertyArrayMulti($symbolVariable, $variableExpr, $property, $offsetExprs, $compilationContext); }
/** * Compiles x->y[z] = {expr} (single offset assignment) * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ protected function _assignPropertyArraySingleIndex($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Only string/variable/int */ $indexExpression = new Expression($statement['index-expr'][0]); $resolvedIndex = $indexExpression->compile($compilationContext); switch ($resolvedIndex->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Expression: " . $resolvedIndex->getType() . " cannot be used as index without cast", $statement); } if ($resolvedIndex->getType() == 'variable') { $indexVariable = $compilationContext->symbolTable->getVariableForRead($resolvedIndex->getCode(), $compilationContext, $statement); switch ($indexVariable->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Variable: " . $indexVariable->getType() . " cannot be used as index without cast", $statement); } if ($indexVariable->getType() == 'variable') { if ($indexVariable->hasDifferentDynamicType(array('undefined', 'int', 'string'))) { $compilationContext->logger->warning('Possible attempt to use non string/long dynamic variable as array index', 'invalid-array-offset', $statement); } } } switch ($resolvedIndex->getType()) { case 'int': case 'uint': case 'long': $indexVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_LONG(' . $indexVariable->getName() . ', ' . $resolvedIndex->getCode() . ');'); break; case 'string': $indexVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_STRING(' . $indexVariable->getName() . ', "' . $resolvedIndex->getCode() . '", 1);'); break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($resolvedIndex->getCode(), $compilationContext, $statement['index-expr']); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': $indexVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $statement); $codePrinter->output('ZVAL_LONG(' . $indexVariable->getName() . ', ' . $variableIndex->getName() . ');'); break; } break; } /** * 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 ($indexVariable->getType()) { case 'variable': case 'string': switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ZEPHIR_GLOBAL(global_null) TSRMLS_CC);'); break; case 'bool': $booleanCode = $resolvedExpr->getBooleanCode(); if ($booleanCode == '1') { $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ZEPHIR_GLOBAL(global_true) TSRMLS_CC);'); } elseif ($booleanCode == '0') { $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ZEPHIR_GLOBAL(global_false) TSRMLS_CC);'); } else { $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_BOOL(' . $tempVariable->getName() . ', ' . $booleanCode . ');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); } break; case 'int': case 'long': case 'uint': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'char': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', \'' . $resolvedExpr->getCode() . '\');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'double': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_DOUBLE(' . $tempVariable->getName() . ', ' . $resolvedExpr->getCode() . ');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'string': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_STRING(' . $tempVariable->getName() . ', "' . $resolvedExpr->getCode() . '", 1);'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'array': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['index-expr']); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $variableExpr->getName() . ' TSRMLS_CC);'); break; case 'variable': $variableExpr = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['index-expr']); switch ($variableExpr->getType()) { case 'bool': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_BOOL(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'int': case 'long': $tempVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); $codePrinter->output('ZVAL_LONG(' . $tempVariable->getName() . ', ' . $variableExpr->getName() . ');'); $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $tempVariable->getName() . ' TSRMLS_CC);'); break; case 'variable': case 'string': case 'array': $codePrinter->output('zephir_update_property_array(' . $symbolVariable->getName() . ', SL("' . $property . '"), ' . $indexVariable->getName() . ', ' . $variableExpr->getName() . ' TSRMLS_CC);'); if ($variableExpr->isTemporal()) { $variableExpr->setIdle(true); } break; default: throw new CompilerException("Cannot update variable type: " . $variableExpr->getType(), $statement); } break; default: throw new CompilerException("Variable index: " . $indexVariable->getType() . " cannot be updated into array property", $statement); } break; default: throw new CompilerException("Index: " . $resolvedIndex->getType() . " cannot be updated into array property", $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; }
/** * Compiles x->y[a][b][] = {expr} (multiple offset assignment) * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement * @throws CompilerException */ protected function _assignPropertyArrayMultipleIndex($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, array $statement) { $codePrinter = $compilationContext->codePrinter; $property = $statement['property']; $compilationContext->headersManager->add('kernel/object'); /** * Create a temporal zval (if needed) */ $variableExpr = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); if (count($statement['index-expr']) > 16) { throw new CompilerException("Too many array indexes", $statement); } /** * Only string/variable/int */ $offsetExprs = array(); foreach ($statement['index-expr'] as $indexExpr) { $indexExpression = new Expression($indexExpr); $resolvedIndex = $indexExpression->compile($compilationContext); switch ($resolvedIndex->getType()) { case 'string': case 'int': case 'uint': case 'ulong': case 'long': case 'variable': break; default: throw new CompilerException("Expression: " . $resolvedIndex->getType() . " cannot be used as index without cast", $statement); } $offsetExprs[] = $resolvedIndex; } /** * Check if the property 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); } } } } $keys = ''; $numberParams = 0; $offsetItems = array(); foreach ($offsetExprs as $offsetExpr) { switch ($offsetExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $keys .= 'l'; $offsetItems[] = $offsetExpr->getCode(); $numberParams++; break; case 'string': $keys .= 's'; $offsetItems[] = 'SL("' . $offsetExpr->getCode() . '")'; $numberParams += 2; break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($offsetExpr->getCode(), $compilationContext, $statement); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $keys .= 'l'; $offsetItems[] = $variableIndex->getName(); $numberParams++; break; case 'string': case 'variable': $keys .= 'z'; $offsetItems[] = $variableIndex->getName(); $numberParams++; break; default: throw new CompilerException("Variable: " . $variableIndex->getType() . " cannot be used as array index", $statement); } break; default: throw new CompilerException("Value: " . $offsetExpr->getType() . " cannot be used as array index", $statement); } } $codePrinter->output('zephir_update_property_array_multi(' . $symbolVariable->getName() . ', SL("' . $property . '"), &' . $variableExpr->getName() . ' TSRMLS_CC, SL("' . $keys . 'a"), ' . $numberParams . ', ' . join(', ', $offsetItems) . ');'); }