/** * Assigns a zval value to a static low-level type * * @todo rewrite this to build ifs and throw from builders * * @param array $parameter * @param CompilationContext $compilationContext * @return string * @throws CompilerException */ public function checkStrictType(array $parameter, CompilationContext $compilationContext) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $compilationContext->headersManager->add('kernel/exception'); $codePrinter = new CodePrinter(); $codePrinter->increaseLevel(); $oldCodePrinter = $compilationContext->codePrinter; $compilationContext->codePrinter = $codePrinter; $compilationContext->backend->checkStrictType($dataType, $parameter, $compilationContext); $compilationContext->codePrinter = $oldCodePrinter; return $codePrinter->getOutput(); }
public function initializeVariableDefaults($variables, CompilationContext $compilationContext) { $codePrinter = new CodePrinter(); $codePrinter->increaseLevel(); $oldCodePrinter = $compilationContext->codePrinter; $compilationContext->codePrinter = $codePrinter; /* Initialize default values in dynamic variables */ foreach ($variables as $variable) { /** * Initialize 'dynamic' variables with default values */ if ($variable->getType() == 'variable') { if ($variable->getNumberUses() > 0) { if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); $compilationContext->backend->initVar($variable, $compilationContext); switch ($defaultValue['type']) { case 'int': case 'uint': case 'long': $compilationContext->backend->assignLong($variable, $defaultValue['value'], $compilationContext); break; case 'bool': $compilationContext->backend->assignBool($variable, $defaultValue['value'], $compilationContext); break; case 'char': case 'uchar': if (strlen($defaultValue['value']) > 2) { if (strlen($defaultValue['value']) > 10) { throw new CompilerException("Invalid char literal: '" . substr($defaultValue['value'], 0, 10) . "...'", $defaultValue); } else { throw new CompilerException("Invalid char literal: '" . $defaultValue['value'] . "'", $defaultValue); } } $compilationContext->backend->assignLong($variable, '\'' . $defaultValue['value'] . '\'', $compilationContext); break; case 'null': $compilationContext->backend->assignNull($variable, $compilationContext); break; case 'double': $compilationContext->backend->assignDouble($variable, $defaultValue['value'], $compilationContext); break; case 'string': $compilationContext->backend->assignString($variable, Utils::addSlashes($defaultValue['value'], true), $compilationContext); break; case 'array': case 'empty-array': $compilationContext->backend->initArray($variable, $compilationContext, null); break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } continue; } /** * Initialize 'string' variables with default values */ if ($variable->getType() == 'string') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); $compilationContext->backend->initVar($variable, $compilationContext); switch ($defaultValue['type']) { case 'string': $compilationContext->backend->assignString($variable, Utils::addSlashes($defaultValue['value'], true), $compilationContext); break; case 'null': $compilationContext->backend->assignString($variable, null, $compilationContext); break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } continue; } /** * Initialize 'array' variables with default values */ if ($variable->getType() == 'array') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); $compilationContext->backend->initVar($variable, $compilationContext); switch ($defaultValue['type']) { case 'null': $compilationContext->backend->assignNull($variable, $compilationContext); break; case 'array': case 'empty-array': $compilationContext->backend->initArray($variable, $compilationContext, null); break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } } $compilationContext->codePrinter = $oldCodePrinter; return $codePrinter->getOutput(); }
/** * Compiles the method * * @param CompilationContext $compilationContext * @return null * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { /** * Set the method currently being compiled */ $compilationContext->currentMethod = $this; /** * Initialize the method warm-up to null */ $compilationContext->methodWarmUp = null; /** * Assign pre-made compilation passses */ $localContext = $this->localContext; $typeInference = $this->typeInference; $callGathererPass = $this->callGathererPass; /** * Every method has its own symbol table */ $symbolTable = new SymbolTable($compilationContext); if ($localContext) { $symbolTable->setLocalContext($localContext); } /** * Parameters has an additional extra mutation */ $parameters = $this->parameters; if ($localContext) { if (is_object($parameters)) { foreach ($parameters->getParameters() as $parameter) { $localContext->increaseMutations($parameter['name']); } } } /** * Initialization of parameters happens in a fictitious external branch */ $branch = new Branch(); $branch->setType(Branch::TYPE_EXTERNAL); /** * BranchManager helps to create graphs of conditional/loop/root/jump branches */ $branchManager = new BranchManager(); $branchManager->addBranch($branch); /** * Cache Manager manages function calls, method calls and class entries caches */ $cacheManager = new CacheManager(); $cacheManager->setGatherer($callGathererPass); $compilationContext->branchManager = $branchManager; $compilationContext->cacheManager = $cacheManager; $compilationContext->typeInference = $typeInference; $compilationContext->symbolTable = $symbolTable; $oldCodePrinter = $compilationContext->codePrinter; /** * Change the code printer to a single method instance */ $codePrinter = new CodePrinter(); $compilationContext->codePrinter = $codePrinter; /** * Set an empty function cache */ $compilationContext->functionCache = null; /** * Reset try/catch and loop counter */ $compilationContext->insideCycle = 0; $compilationContext->insideTryCatch = 0; $compilationContext->currentTryCatch = 0; if (is_object($parameters)) { /** * Round 1. Create variables in parameters in the symbol table */ $classCastChecks = array(); foreach ($parameters->getParameters() as $parameter) { /** * Change dynamic variables to low level types */ if ($typeInference) { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $type = $typeInference->getInferedType($parameter['name']); if (is_string($type)) { /* promote polymorphic parameters to low level types */ } } } else { $type = $typeInference->getInferedType($parameter['name']); if (is_string($type)) { /* promote polymorphic parameters to low level types */ } } } $symbolParam = null; if (isset($parameter['data-type'])) { switch ($parameter['data-type']) { case 'object': case 'callable': case 'resource': case 'variable': $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext); break; default: $symbol = $symbolTable->addVariable($parameter['data-type'], $parameter['name'], $compilationContext); $symbolParam = $symbolTable->addVariable('variable', $parameter['name'] . '_param', $compilationContext); if ($parameter['data-type'] == 'string' || $parameter['data-type'] == 'array') { $symbol->setMustInitNull(true); } break; } } else { $symbol = $symbolTable->addVariable('variable', $parameter['name'], $compilationContext); } /** * Some parameters can be read-only */ if (isset($parameter['const']) && $parameter['const']) { $symbol->setReadOnly(true); if (is_object($symbolParam)) { $symbolParam->setReadOnly(true); } } if (is_object($symbolParam)) { /** * Parameters are marked as 'external' */ $symbolParam->setIsExternal(true); /** * Assuming they're initialized */ $symbolParam->setIsInitialized(true, $compilationContext, $parameter); /** * Initialize auxiliar parameter zvals to null */ $symbolParam->setMustInitNull(true); /** * Increase uses */ $symbolParam->increaseUses(); } else { if (isset($parameter['default'])) { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $symbol->setMustInitNull(true); } } else { $symbol->setMustInitNull(true); } } } /** * Original node where the variable was declared */ $symbol->setOriginal($parameter); /** * Parameters are marked as 'external' */ $symbol->setIsExternal(true); /** * Assuming they're initialized */ $symbol->setIsInitialized(true, $compilationContext, $parameter); /** * Variables with class/type must be objects across the execution */ if (isset($parameter['cast'])) { $symbol->setDynamicTypes('object'); $symbol->setClassTypes($compilationContext->getFullName($parameter['cast']['value'])); $classCastChecks[] = array($symbol, $parameter); } else { if (isset($parameter['data-type'])) { if ($parameter['data-type'] == 'variable') { $symbol->setDynamicTypes('undefined'); } } else { $symbol->setDynamicTypes('undefined'); } } } } /** * Initialize the properties within create_object, handler code */ if ($this->getName() == 'zephir_init_properties') { $codePrinter->increaseLevel(); $codePrinter->output('{'); $codePrinter->increaseLevel(); $codePrinter->output('zval *this_ptr = NULL;'); $codePrinter->output('ZEPHIR_CREATE_OBJECT(this_ptr, class_type);'); $codePrinter->decreaseLevel(); } /** * Compile the block of statements if any */ if (is_object($this->statements)) { if ($this->hasModifier('static')) { $compilationContext->staticContext = true; } else { $compilationContext->staticContext = false; } /** * Compile the statements block as a 'root' branch */ $this->statements->compile($compilationContext, false, Branch::TYPE_ROOT); } /** * Initialize default values in dynamic variables */ $initVarCode = ""; foreach ($symbolTable->getVariables() as $variable) { /** * Initialize 'dynamic' variables with default values */ if ($variable->getType() == 'variable') { if ($variable->getNumberUses() > 0) { if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'int': case 'uint': case 'long': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL; break; case 'char': case 'uchar': if (strlen($defaultValue['value']) > 2) { if (strlen($defaultValue['value']) > 10) { throw new CompilerException("Invalid char literal: '" . substr($defaultValue['value'], 0, 10) . "...'", $defaultValue); } else { throw new CompilerException("Invalid char literal: '" . $defaultValue['value'] . "'", $defaultValue); } } $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_LONG(' . $variable->getName() . ', \'' . $defaultValue['value'] . '\');' . PHP_EOL; break; case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL; break; case 'double': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_DOUBLE(' . $variable->getName() . ', ' . $defaultValue['value'] . ');' . PHP_EOL; break; case 'string': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL; break; case 'array': case 'empty-array': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } continue; } /** * Initialize 'string' variables with default values */ if ($variable->getType() == 'string') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'string': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_STRING(' . $variable->getName() . ', "' . Utils::addSlashes($defaultValue['value'], true) . '", 1);' . PHP_EOL; break; case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_EMPTY_STRING(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } continue; } /** * Initialize 'array' variables with default values */ if ($variable->getType() == 'array') { if ($variable->getNumberUses() > 0) { $defaultValue = $variable->getDefaultInitValue(); if (is_array($defaultValue)) { $symbolTable->mustGrownStack(true); switch ($defaultValue['type']) { case 'null': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'ZVAL_NULL(' . $variable->getName() . ');' . PHP_EOL; break; case 'array': case 'empty-array': $initVarCode .= "\t" . 'ZEPHIR_INIT_VAR(' . $variable->getName() . ');' . PHP_EOL; $initVarCode .= "\t" . 'array_init(' . $variable->getName() . ');' . PHP_EOL; break; default: throw new CompilerException('Invalid default type: ' . $defaultValue['type'] . ' for data type: ' . $variable->getType(), $variable->getOriginal()); } } } } } /** * Fetch parameters from vm-top */ $initCode = ""; $code = ""; if (is_object($parameters)) { /** * Round 2. Fetch the parameters in the method */ $params = array(); $requiredParams = array(); $optionalParams = array(); $numberRequiredParams = 0; $numberOptionalParams = 0; foreach ($parameters->getParameters() as $parameter) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } switch ($dataType) { case 'object': case 'callable': case 'resource': case 'variable': if (!$this->isInternal()) { $params[] = '&' . $parameter['name']; } else { $params[] = $parameter['name']; } break; default: if (!$this->isInternal()) { $params[] = '&' . $parameter['name'] . '_param'; } else { $params[] = $parameter['name'] . '_param'; } break; } if (isset($parameter['default'])) { $optionalParams[] = $parameter; $numberOptionalParams++; } else { $requiredParams[] = $parameter; $numberRequiredParams++; } } /** * Pass the write detector to the method statement block to check if the parameter * variable is modified so as do the proper separation */ $parametersToSeparate = array(); if (is_object($this->statements)) { /** * If local context is not available */ if (!$localContext) { $writeDetector = new WriteDetector(); } foreach ($parameters->getParameters() as $parameter) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } switch ($dataType) { case 'variable': case 'string': case 'array': case 'resource': case 'object': case 'callable': $name = $parameter['name']; if (!$localContext) { if ($writeDetector->detect($name, $this->statements->getStatements())) { $parametersToSeparate[$name] = true; } } else { if ($localContext->getNumberOfMutations($name) > 1) { $parametersToSeparate[$name] = true; } } break; } } } /** * Initialize required parameters */ foreach ($requiredParams as $parameter) { if (isset($parameter['mandatory'])) { $mandatory = $parameter['mandatory']; } else { $mandatory = 0; } if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } if ($dataType != 'variable') { /** * Assign value from zval to low level type */ if ($mandatory) { $initCode .= $this->checkStrictType($parameter, $compilationContext); } else { $initCode .= $this->assignZvalValue($parameter, $compilationContext); } } switch ($dataType) { case 'variable': case 'resource': case 'object': case 'callable': if (isset($parametersToSeparate[$parameter['name']])) { $symbolTable->mustGrownStack(true); $initCode .= "\t" . "ZEPHIR_SEPARATE_PARAM(" . $parameter['name'] . ");" . PHP_EOL; } break; } } /** * Initialize optional parameters */ foreach ($optionalParams as $parameter) { if (isset($parameter['mandatory'])) { $mandatory = $parameter['mandatory']; } else { $mandatory = 0; } if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } switch ($dataType) { case 'object': case 'callable': case 'resource': case 'variable': $name = $parameter['name']; break; default: $name = $parameter['name'] . '_param'; break; } /** * Assign the default value according to the variable's type */ $initCode .= "\t" . 'if (!' . $name . ') {' . PHP_EOL; $initCode .= $this->assignDefaultValue($parameter, $compilationContext); if (isset($parametersToSeparate[$name]) || $dataType != 'variable') { $initCode .= "\t" . '} else {' . PHP_EOL; if (isset($parametersToSeparate[$name])) { $initCode .= "\t\t" . "ZEPHIR_SEPARATE_PARAM(" . $name . ");" . PHP_EOL; } else { if ($mandatory) { $initCode .= $this->checkStrictType($parameter, $compilationContext, $mandatory); } else { $initCode .= "\t" . $this->assignZvalValue($parameter, $compilationContext); } } } $initCode .= "\t" . '}' . PHP_EOL; } /** * Fetch the parameters to zval pointers */ $codePrinter->preOutputBlankLine(); if (!$this->isInternal()) { $compilationContext->headersManager->add('kernel/memory'); if ($symbolTable->getMustGrownStack()) { $code .= "\t" . 'zephir_fetch_params(1, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL; } else { $code .= "\t" . 'zephir_fetch_params(0, ' . $numberRequiredParams . ', ' . $numberOptionalParams . ', ' . join(', ', $params) . ');' . PHP_EOL; } } else { foreach ($params as $param) { $code .= "\t" . $param . ' = ' . $param . '_ext;' . PHP_EOL; } } $code .= PHP_EOL; } $code .= $initCode . $initVarCode; $codePrinter->preOutput($code); /** * Fetch used superglobals */ foreach ($symbolTable->getVariables() as $name => $variable) { if ($symbolTable->isSuperGlobal($name)) { $codePrinter->preOutput("\t" . 'zephir_get_global(&' . $name . ', SS("' . $name . '") TSRMLS_CC);'); } } /** * Grow the stack if needed */ if ($symbolTable->getMustGrownStack()) { $compilationContext->headersManager->add('kernel/memory'); $codePrinter->preOutput("\t" . 'ZEPHIR_MM_GROW();'); } /** * Check if there are unused variables */ $usedVariables = array(); $completeName = $compilationContext->classDefinition->getCompleteName(); foreach ($symbolTable->getVariables() as $variable) { if ($variable->getNumberUses() <= 0) { if ($variable->isExternal() == false) { $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal()); continue; } $compilationContext->logger->warning('Variable "' . $variable->getName() . '" declared but not used in ' . $completeName . '::' . $this->getName(), "unused-variable-external", $variable->getOriginal()); } if ($variable->getName() != 'this_ptr' && $variable->getName() != 'return_value' && $variable->getName() != 'return_value_ptr') { $type = $variable->getType(); if (!isset($usedVariables[$type])) { $usedVariables[$type] = array(); } $usedVariables[$type][] = $variable; } } /** * Check if there are assigned but not used variables * Warn whenever a variable is unused aside from its declaration. */ foreach ($symbolTable->getVariables() as $variable) { if ($variable->isExternal() == true || $variable->isTemporal()) { continue; } if ($variable->getName() == 'this_ptr' || $variable->getName() == 'return_value' || $variable->getName() == 'return_value_ptr' || $variable->getName() == 'ZEPHIR_LAST_CALL_STATUS') { continue; } if (!$variable->isUsed()) { $node = $variable->getLastUsedNode(); if (is_array($node)) { $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $node); } else { $compilationContext->logger->warning('Variable "' . $variable->getName() . '" assigned but not used in ' . $completeName . '::' . $this->getName(), "unused-variable", $variable->getOriginal()); } } } if (count($usedVariables)) { $codePrinter->preOutputBlankLine(); } /** * Generate the variable definition for variables used */ foreach ($usedVariables as $type => $variables) { $pointer = null; switch ($type) { case 'int': $code = 'int '; break; case 'uint': $code = 'unsigned int '; break; case 'char': $code = 'char '; break; case 'uchar': $code = 'unsigned char '; break; case 'long': $code = 'long '; break; case 'ulong': $code = 'unsigned long '; break; case 'bool': $code = 'zend_bool '; break; case 'double': $code = 'double '; break; case 'string': case 'variable': case 'array': case 'null': $pointer = '*'; $code = 'zval '; break; case 'HashTable': $pointer = '*'; $code = 'HashTable '; break; case 'HashPosition': $code = 'HashPosition '; break; case 'zend_class_entry': $pointer = '*'; $code = 'zend_class_entry '; break; case 'zend_function': $pointer = '*'; $code = 'zend_function '; break; case 'zend_object_iterator': $pointer = '*'; $code = 'zend_object_iterator '; break; case 'zend_property_info': $pointer = '*'; $code = 'zend_property_info '; break; case 'zephir_fcall_cache_entry': $pointer = '*'; $code = 'zephir_fcall_cache_entry '; break; case 'static_zephir_fcall_cache_entry': $pointer = '*'; $code = 'zephir_nts_static zephir_fcall_cache_entry '; break; case 'static_zend_class_entry': $pointer = '*'; $code = 'zephir_nts_static zend_class_entry '; break; case 'zephir_ce_guard': $code = 'zephir_nts_static zend_bool '; break; default: throw new CompilerException("Unsupported type in declare: " . $type); } $groupVariables = array(); $defaultValues = array(); /** * @var $variables Variable[] */ foreach ($variables as $variable) { $isComplex = $type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object'; if ($isComplex && $variable->mustInitNull()) { if ($variable->isLocalOnly()) { $groupVariables[] = $variable->getName() . ' = zval_used_for_init'; } else { if ($variable->isDoublePointer()) { $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $variable->getName() . ' = NULL'; } } continue; } if ($variable->isLocalOnly()) { $groupVariables[] = $variable->getName(); continue; } if ($variable->isDoublePointer()) { if ($variable->mustInitNull()) { $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $pointer . $variable->getName(); } continue; } $defaultValue = $variable->getDefaultInitValue(); if ($defaultValue !== null) { switch ($type) { case 'variable': case 'string': case 'array': case 'resource': case 'callable': case 'object': $groupVariables[] = $pointer . $variable->getName(); break; case 'char': if (strlen($defaultValue) > 4) { if (strlen($defaultValue) > 10) { throw new CompilerException("Invalid char literal: '" . substr($defaultValue, 0, 10) . "...'", $variable->getOriginal()); } else { throw new CompilerException("Invalid char literal: '" . $defaultValue . "'", $variable->getOriginal()); } } /* no break */ /* no break */ default: $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue; break; } continue; } if ($variable->mustInitNull() && $pointer) { $groupVariables[] = $pointer . $variable->getName() . ' = NULL'; continue; } $groupVariables[] = $pointer . $variable->getName(); } $codePrinter->preOutput("\t" . $code . join(', ', $groupVariables) . ';'); } /** * Finalize the method compilation */ if (is_object($this->statements)) { /** * If the last statement is not a 'return' or 'throw' we need to * restore the memory stack if needed */ $lastType = $this->statements->getLastStatementType(); if ($lastType != 'return' && $lastType != 'throw' && !$this->hasChildReturnStatementType($this->statements->getLastStatement())) { if ($symbolTable->getMustGrownStack()) { $compilationContext->headersManager->add('kernel/memory'); $codePrinter->output("\t" . 'ZEPHIR_MM_RESTORE();'); } /** * If a method has return-type hints we need to ensure the last statement is a 'return' statement */ if ($this->hasReturnTypes()) { throw new CompilerException('Reached end of the method without returning a valid type specified in the return-type hints', $this->expression['return-type']); } } } if ($this->getName() == 'zephir_init_properties') { $codePrinter->increaseLevel(); $codePrinter->output('return Z_OBJVAL_P(this_ptr);'); $codePrinter->decreaseLevel(); $codePrinter->output('}'); $codePrinter->decreaseLevel(); } /** * Remove macros that grow/restore the memory frame stack if it wasn't used */ $code = $this->removeMemoryStackReferences($symbolTable, $codePrinter->getOutput()); /** * Restore the compilation context */ $oldCodePrinter->output($code); $compilationContext->codePrinter = $oldCodePrinter; $compilationContext->branchManager = null; $compilationContext->cacheManager = null; $compilationContext->typeInference = null; $codePrinter->clear(); return null; }
public function genFcallCode() { $codePrinter = new CodePrinter(); $codePrinter->output('#ifndef ZEPHIR_KERNEL_FCALL_INTERNAL_H'); $codePrinter->output('#define ZEPHIR_KERNEL_FCALL_INTERNAL_H'); $codePrinter->increaseLevel(); ksort($this->requiredMacros); foreach ($this->requiredMacros as $name => $info) { list($scope, $mode, $paramCount) = $info; $paramsStr = ''; $retParam = ''; $retValueUsed = '0'; $params = array(); $zvals = array(); $initStatements = array(); $postStatements = array(); for ($i = 0; $i < $paramCount; ++$i) { $params[] = 'p' . $i; } if ($paramCount) { $paramsStr = ', ' . implode(', ', $params); } if ($mode == 'CALL_INTERNAL_METHOD_P') { $retValueUsed = '1'; $retParam = 'return_value_ptr'; $initStatements[] = 'ZEPHIR_INIT_NVAR(*(return_value_ptr)); \\'; } $objParam = $scope ? 'scope_ce, ' : 'object, '; $macroName = $name . '(' . ($retParam ? $retParam . ', ' : '') . $objParam . 'method' . $paramsStr . ')'; $codePrinter->output('#define ' . $macroName . ' \\'); if (!$retParam) { $retParam = 'return_value'; } $codePrinter->increaseLevel(); $codePrinter->output('do { \\'); $codePrinter->increaseLevel(); if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') { $codePrinter->output('zval *rv = NULL; \\'); $codePrinter->output('zval **rvp = &rv; \\'); $codePrinter->output('ALLOC_INIT_ZVAL(rv); \\'); $retParam = 'rvp'; } $codePrinter->output('ZEPHIR_BACKUP_SCOPE() \\'); $codePrinter->output('ZEPHIR_BACKUP_THIS_PTR() \\'); if (!$scope) { $codePrinter->output('ZEPHIR_SET_THIS(object); \\'); $codePrinter->output('ZEPHIR_SET_SCOPE((Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL), (Z_TYPE_P(object) == IS_OBJECT ? Z_OBJCE_P(object) : NULL)); \\'); } else { $codePrinter->output('ZEPHIR_SET_THIS(NULL); \\'); $codePrinter->output('ZEPHIR_SET_SCOPE(scope_ce, scope_ce); \\'); } /* Create new zval's for parameters */ for ($i = 0; $i < $paramCount; ++$i) { //$zv = '_' . $params[$i]; //$zvals[] = $zv; //$initStatements[] = 'ALLOC_ZVAL(' . $zv . '); \\'; //$initStatements[] = 'INIT_PZVAL_COPY(' . $zv . ', ' . $params[$i] . '); \\'; //$postStatements[] = 'zval_ptr_dtor(&' . $zv . '); \\'; $zv = $params[$i]; $zvals[] = $zv; $initStatements[] = 'Z_ADDREF_P(' . $zv . '); \\'; $postStatements[] = 'Z_DELREF_P(' . $zv . '); \\'; } if ($i) { //$codePrinter->output('zval *' . implode(', *', $zvals) . '; \\'); } foreach ($initStatements as $statement) { $codePrinter->output($statement); } $zvalStr = $i ? ', ' . implode(', ', $zvals) : ''; $retExpr = ''; if ($retParam) { if ($retParam == 'return_value') { $retExpr = ', return_value, return_value_ptr'; } else { $retExpr = ', *' . $retParam . ', ' . $retParam; } } $codePrinter->output('method(0' . $retExpr . ', ' . ($scope ? 'NULL, ' : $objParam) . $retValueUsed . $zvalStr . ' TSRMLS_CC); \\'); if ($mode == 'CALL_INTERNAL_METHOD_NORETURN_P') { $postStatements[] = 'zval_ptr_dtor(rvp); \\'; } foreach ($postStatements as $statement) { $codePrinter->output($statement); } $codePrinter->output('ZEPHIR_LAST_CALL_STATUS = EG(exception) ? FAILURE : SUCCESS; \\'); $codePrinter->output('ZEPHIR_RESTORE_THIS_PTR(); \\'); $codePrinter->output('ZEPHIR_RESTORE_SCOPE(); \\'); $codePrinter->decreaseLevel(); $codePrinter->output('} while (0)'); $codePrinter->decreaseLevel(); $codePrinter->output(''); } $codePrinter->decreaseLevel(); $codePrinter->output("#endif"); Utils::checkAndWriteIfNeeded($codePrinter->getOutput(), 'ext/kernel/fcall_internal.h'); }