/** * @param array $expression * @param Call $call * @param CompilationContext $context * @return bool|CompiledExpression|mixed * @throws CompilerException */ public function optimize(array $expression, Call $call, CompilationContext $context) { if (!isset($expression['parameters'])) { return false; } if (count($expression['parameters']) != 2) { return false; } /** * Process the expected symbol to be returned */ $call->processExpectedReturn($context); $symbolVariable = $call->getSymbolVariable(); if ($symbolVariable->isNotVariableAndString()) { throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression); } if ($expression['parameters'][0]['parameter']['type'] == 'string') { $str = Utils::addSlashes($expression['parameters'][0]['parameter']['value']); unset($expression['parameters'][0]); } if ($call->mustInitSymbolVariable()) { $symbolVariable->initVariant($context); } $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression); $context->headersManager->add('kernel/string'); $symbolVariable->setDynamicTypes('string'); if (isset($str)) { $context->codePrinter->output('zephir_fast_join_str(' . $symbolVariable->getName() . ', SL("' . $str . '"), ' . $resolvedParams[0] . ' TSRMLS_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } $context->codePrinter->output('zephir_fast_join(' . $symbolVariable->getName() . ', ' . $resolvedParams[0] . ', ' . $resolvedParams[1] . ' TSRMLS_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $compilationContext->headersManager->add('kernel/exception'); $codePrinter = $compilationContext->codePrinter; $statement = $this->_statement; $expr = $statement['expr']; /** * This optimizes throw new Exception("hello") */ if (!$compilationContext->insideTryCatch) { if (isset($expr['class'])) { if (isset($expr['parameters']) && count($expr['parameters']) == 1) { if ($expr['parameters'][0]['parameter']['type'] == 'string') { $className = Utils::getFullName($expr['class'], $compilationContext->classDefinition->getNamespace(), $compilationContext->aliasManager); if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); $message = $expr['parameters'][0]['parameter']['value']; $codePrinter->output('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(' . $classDefinition->getClassEntry() . ', "' . Utils::addSlashes($message, true, Types::STRING) . '", "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ');'); $codePrinter->output('return;'); return; } else { if ($compilationContext->compiler->isInternalClass($className)) { $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, true); if ($classEntry) { $message = $expr['parameters'][0]['parameter']['value']; $codePrinter->output('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(' . $classEntry . ', "' . Utils::addSlashes($message, true, Types::STRING) . '", "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ');'); $codePrinter->output('return;'); return; } } } } } } } $throwExpr = new Expression($expr); $resolvedExpr = $throwExpr->compile($compilationContext); if ($resolvedExpr->getType() != 'variable') { throw new CompilerException("Expression '" . $resolvedExpr->getType() . '" cannot be used as exception', $expr); } $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $expr); if ($variableVariable->getType() != 'variable') { throw new CompilerException("Variable '" . $variableVariable->getType() . "' cannot be used as exception", $expr); } $codePrinter->output('zephir_throw_exception_debug(' . $variableVariable->getName() . ', "' . Compiler::getShortUserPath($statement['expr']['file']) . '", ' . $statement['expr']['line'] . ' TSRMLS_CC);'); if (!$compilationContext->insideTryCatch) { $codePrinter->output('ZEPHIR_MM_RESTORE();'); $codePrinter->output('return;'); } else { $codePrinter->output('goto try_end_' . $compilationContext->insideTryCatch . ';'); $codePrinter->outputBlankLine(); } if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } }
/** * @param array $expression * @param Call $call * @param CompilationContext $context * @return bool|CompiledExpression|mixed * @throws CompilerException */ public function optimize(array $expression, Call $call, CompilationContext $context) { if (!isset($expression['parameters'])) { return false; } if (count($expression['parameters']) < 2) { throw new CompilerException("'explode' require two parameter"); } /** * Process the expected symbol to be returned */ $call->processExpectedReturn($context); $symbolVariable = $call->getSymbolVariable(true, $context); if ($symbolVariable->isNotVariableAndString()) { throw new CompilerException("Returned values by functions can only be assigned to variant variables", $expression); } /** * Process limit */ $limit = 'LONG_MAX'; $limitOffset = 2; if (count($expression['parameters']) == 3 && $expression['parameters'][2]['parameter']['type'] == 'int') { $limit = $expression['parameters'][2]['parameter']['value'] . ' '; unset($expression['parameters'][2]); } if ($expression['parameters'][0]['parameter']['type'] == 'string') { $str = Utils::addSlashes($expression['parameters'][0]['parameter']['value']); unset($expression['parameters'][0]); if (count($expression['parameters']) == 2) { $limitOffset = 1; } } $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression); if (isset($resolvedParams[$limitOffset])) { $context->headersManager->add('kernel/operators'); $limit = 'zephir_get_intval(' . $resolvedParams[$limitOffset] . ') '; } $context->headersManager->add('kernel/string'); $symbolVariable->setDynamicTypes('array'); if ($call->mustInitSymbolVariable()) { $symbolVariable->initVariant($context); } $symbol = $context->backend->getVariableCode($symbolVariable); if (isset($str)) { $context->codePrinter->output('zephir_fast_explode_str(' . $symbol . ', SL("' . $str . '"), ' . $resolvedParams[0] . ', ' . $limit . ' TSRMLS_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } $context->codePrinter->output('zephir_fast_explode(' . $symbol . ', ' . $resolvedParams[0] . ', ' . $resolvedParams[1] . ', ' . $limit . ' TSRMLS_CC);'); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * @param array $expression * @param Call $call * @param CompilationContext $context * @return bool|CompiledExpression|mixed */ public function optimize(array $expression, Call $call, CompilationContext $context) { if (!isset($expression['parameters'])) { return false; } if (count($expression['parameters']) != 2) { return false; } if ($expression['parameters'][1]['parameter']['type'] == 'string') { $str = Utils::addSlashes($expression['parameters'][1]['parameter']['value']); unset($expression['parameters'][1]); } $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression); $context->headersManager->add('kernel/string'); if (isset($str)) { return new CompiledExpression('bool', 'zephir_memnstr_str(' . $resolvedParams[0] . ', SL("' . $str . '"), "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ')', $expression); } return new CompiledExpression('bool', 'zephir_memnstr(' . $resolvedParams[0] . ', ' . $resolvedParams[1] . ', "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ')', $expression); }
/** * @param array $expression * @param Call $call * @param CompilationContext $context * @return bool|CompiledExpression|mixed */ public function optimize(array $expression, Call $call, CompilationContext $context) { if (!isset($expression['parameters'])) { return false; } if (count($expression['parameters']) != 2) { return false; } if ($expression['parameters'][1]['parameter']['type'] == 'string') { $str = Utils::addSlashes($expression['parameters'][1]['parameter']['value']); unset($expression['parameters'][1]); } $context->headersManager->add('kernel/object'); $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression); if (isset($str)) { return new CompiledExpression('bool', '(zephir_method_exists_ex(' . $resolvedParams[0] . ', SS("' . strtolower($str) . '") TSRMLS_CC) == SUCCESS)', $expression); } return new CompiledExpression('bool', '(zephir_method_exists(' . $resolvedParams[0] . ', ' . $resolvedParams[1] . ' TSRMLS_CC) == SUCCESS)', $expression); }
/** * @param array $expression * @param Call $call * @param CompilationContext $context * @return bool|CompiledExpression|mixed */ public function optimize(array $expression, Call $call, CompilationContext $context) { if (!isset($expression['parameters'])) { return false; } if (count($expression['parameters']) != 1) { return false; } if ($expression['parameters'][0]['parameter']['type'] == 'string') { $str = Utils::addSlashes($expression['parameters'][0]['parameter']['value']); unset($expression['parameters'][0]); } $context->headersManager->add('kernel/object'); $resolvedParams = $call->getReadOnlyResolvedParams($expression['parameters'], $context, $expression); if (isset($str)) { /* TODO: Solve this macro stuff better, move to backend */ $macro = $context->backend->isZE3() ? 'SL' : 'SS'; return new CompiledExpression('bool', '(zephir_function_exists_ex(' . $macro . '("' . strtolower($str) . '") TSRMLS_CC) == SUCCESS)', $expression); } return new CompiledExpression('bool', '(zephir_function_exists(' . $resolvedParams[0] . ' TSRMLS_CC) == SUCCESS)', $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $statement = $this->_statement; $codePrinter = $compilationContext->codePrinter; if (isset($statement['expr'])) { $currentMethod = $compilationContext->currentMethod; if ($currentMethod->isConstructor()) { throw new CompilerException("Constructors cannot return values", $statement['expr']); } if ($currentMethod->isVoid()) { throw new CompilerException("Method is marked as 'void' and it must not return any value", $statement['expr']); } /** * Use return member for properties on this */ if ($statement['expr']['type'] == 'property-access') { if ($statement['expr']['left']['type'] == 'variable') { if ($statement['expr']['left']['value'] == 'this') { if ($statement['expr']['right']['type'] == 'variable') { /** * If the property is accessed on 'this', we check if the property does exist */ $property = $statement['expr']['right']['value']; $classDefinition = $compilationContext->classDefinition; if (!$classDefinition->hasProperty($property)) { throw new CompilerException("Class '" . $classDefinition->getCompleteName() . "' does not have a property called: '" . $property . "'", $statement['expr']['right']); } $compilationContext->headersManager->add('kernel/object'); $codePrinter->output('RETURN_MM_MEMBER(this_ptr, "' . $property . '");'); return; } } } } /** * Fetches return_value and tries to return the value directly there */ $variable = $compilationContext->symbolTable->getVariable('return_value'); $expr = new Expression($statement['expr']); $expr->setExpectReturn(true, $variable); $expr->setReadOnly(true); $resolvedExpr = $expr->compile($compilationContext); /** * Here we check if the variable returns a compatible type according to its type hints */ if ($currentMethod->hasReturnTypes()) { switch ($resolvedExpr->getType()) { case 'null': if (!$currentMethod->areReturnTypesNullCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'int': case 'uint': case 'long': case 'char': case 'uchar': if (!$currentMethod->areReturnTypesIntCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'bool': if (!$currentMethod->areReturnTypesBoolCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'double': if (!$currentMethod->areReturnTypesDoubleCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'string': if (!$currentMethod->areReturnTypesStringCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'variable': $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['expr']); switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': if (!$currentMethod->areReturnTypesIntCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'double': if (!$currentMethod->areReturnTypesDoubleCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'string': if (!$currentMethod->areReturnTypesStringCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'bool': if (!$currentMethod->areReturnTypesBoolCompatible()) { throw new CompilerException("Returning type: " . $resolvedExpr->getType() . " but this type is not compatible with return-type hints declared in the method", $statement['expr']); } break; case 'variable': break; } break; } } switch ($resolvedExpr->getType()) { case 'null': $codePrinter->output('RETURN_MM_NULL();'); break; case 'int': case 'uint': case 'long': case 'char': case 'uchar': $codePrinter->output('RETURN_MM_LONG(' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('RETURN_MM_BOOL(' . $resolvedExpr->getBooleanCode() . ');'); break; case 'double': $codePrinter->output('RETURN_MM_DOUBLE(' . $resolvedExpr->getCode() . ');'); break; case 'string': case 'istring': $codePrinter->output('RETURN_MM_STRING("' . Utils::addSlashes($resolvedExpr->getCode()) . '", 1);'); break; case 'array': if ($resolvedExpr->getCode() != 'return_value') { $codePrinter->output('RETURN_CTOR(' . $resolvedExpr->getCode() . ');'); } else { $codePrinter->output('RETURN_MM();'); } break; case 'variable': if (!isset($symbolVariable)) { $symbolVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $statement['expr']); } switch ($symbolVariable->getType()) { case 'int': case 'uint': case 'long': case 'char': case 'uchar': $codePrinter->output('RETURN_MM_LONG(' . $symbolVariable->getName() . ');'); break; case 'double': $codePrinter->output('RETURN_MM_DOUBLE(' . $symbolVariable->getName() . ');'); break; case 'string': case 'array': $codePrinter->output('RETURN_CTOR(' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('RETURN_MM_BOOL(' . $symbolVariable->getName() . ');'); break; case 'variable': if ($symbolVariable->getName() == 'this_ptr') { $codePrinter->output('RETURN_THIS();'); } else { if ($symbolVariable->getName() != 'return_value') { if (!$symbolVariable->isExternal()) { if ($symbolVariable->isLocalOnly()) { $codePrinter->output('RETURN_LCTOR(' . $symbolVariable->getName() . ');'); } else { if (!$symbolVariable->isMemoryTracked()) { $codePrinter->output('RETURN_CTOR(' . $symbolVariable->getName() . ');'); } else { $codePrinter->output('RETURN_CCTOR(' . $symbolVariable->getName() . ');'); } } } else { $codePrinter->output('RETVAL_ZVAL(' . $symbolVariable->getName() . ', 1, 0);'); $codePrinter->output('RETURN_MM();'); } } else { $codePrinter->output('RETURN_MM();'); } } if ($symbolVariable->isTemporal()) { $symbolVariable->setIdle(true); } break; default: throw new CompilerException("Cannot return variable '" . $symbolVariable->getType() . "'", $statement['expr']); } break; default: throw new CompilerException("Cannot return '" . $resolvedExpr->getType() . "'", $statement['expr']); } return; } /** * Return without an expression */ $codePrinter->output('RETURN_MM_NULL();'); }
/** * Resolve parameters using zvals in the stack and without allocating memory for constants * * @param array $parameters * @param CompilationContext $compilationContext * @param array $expression * @return array */ public function getReadOnlyResolvedParams($parameters, CompilationContext $compilationContext, array $expression) { $codePrinter = $compilationContext->codePrinter; $exprParams = $this->getResolvedParamsAsExpr($parameters, $compilationContext, $expression, true); $params = array(); $types = array(); $dynamicTypes = array(); foreach ($exprParams as $compiledExpression) { $expression = $compiledExpression->getOriginal(); switch ($compiledExpression->getType()) { case 'null': $params[] = $compilationContext->backend->resolveValue('null', $compilationContext); $types[] = 'null'; $dynamicTypes[] = 'null'; break; case 'int': case 'uint': case 'long': $parameterVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($parameterVariable, $compiledExpression->getCode(), $compilationContext); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'char': case 'uchar': $parameterVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $compilationContext->backend->assignLong($parameterVariable, '\'' . $compiledExpression->getCode() . '\'', $compilationContext); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'double': $parameterVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(&' . $parameterVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'bool': if ($compiledExpression->getCode() == 'true') { $params[] = $compilationContext->backend->resolveValue('true', $compilationContext); } else { if ($compiledExpression->getCode() == 'false') { $params[] = $compilationContext->backend->resolveValue('false', $compilationContext); } else { throw new Exception('?'); } } $types[] = 'bool'; $dynamicTypes[] = 'bool'; break; case 'ulong': case 'string': case 'istring': if ($compilationContext->backend->getName() == 'ZendEngine2') { $parameterVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); } else { $parameterVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } $compilationContext->backend->assignString($parameterVariable, Utils::addSlashes($compiledExpression->getCode()), $compilationContext, true, false); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'array': $parameterVariable = $compilationContext->symbolTable->getVariableForRead($compiledExpression->getCode(), $compilationContext, $expression); $params[] = $compilationContext->backend->getVariableCode($parameterVariable); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'variable': $parameterVariable = $compilationContext->symbolTable->getVariableForRead($compiledExpression->getCode(), $compilationContext, $expression); switch ($parameterVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $parameterTempVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterTempVariable->getType(); $dynamicTypes[] = $parameterTempVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'char': case 'uchar': $parameterTempVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_LONG(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterTempVariable->getType(); $dynamicTypes[] = $parameterTempVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'double': $parameterTempVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $codePrinter->output('ZVAL_DOUBLE(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterTempVariable->getType(); $dynamicTypes[] = $parameterTempVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'bool': $parameterTempVariable = $compilationContext->backend->getScalarTempVariable('variable', $compilationContext, true); $compilationContext->backend->assignBool($parameterTempVariable, '(' . $parameterVariable->getName() . ' ? 1 : 0)', $compilationContext); $params[] = $compilationContext->backend->getVariableCode($parameterTempVariable); $dynamicTypes[] = $parameterTempVariable->getType(); $types[] = $parameterTempVariable->getType(); break; case 'string': case 'variable': case 'array': $params[] = $compilationContext->backend->getVariableCode($parameterVariable); $dynamicTypes[] = $parameterVariable->getType(); $types[] = $parameterVariable->getType(); break; default: throw new CompilerException("Cannot use variable type: " . $parameterVariable->getType() . " as parameter", $expression); } break; default: throw new CompilerException("Cannot use value type: " . $compiledExpression->getType() . " as parameter", $expression); } } $this->_resolvedTypes = $types; $this->_resolvedDynamicTypes = $dynamicTypes; return $params; }
public function generateFunctionInformation() { $headerPrinter = new CodePrinter(); $entryPrinter = new CodePrinter(); /** * Create argument info */ foreach ($this->functionDefinitions as $func) { $funcName = $func->getInternalName(); $argInfoName = 'arginfo_' . strtolower($funcName); $headerPrinter->output('PHP_FUNCTION(' . $funcName . ');'); $parameters = $func->getParameters(); if (count($parameters)) { $headerPrinter->output('ZEND_BEGIN_ARG_INFO_EX(' . $argInfoName . ', 0, 0, ' . $func->getNumberOfRequiredParameters() . ')'); foreach ($parameters->getParameters() as $parameter) { switch ($parameter['data-type']) { case 'array': $headerPrinter->output("\t" . 'ZEND_ARG_ARRAY_INFO(0, ' . $parameter['name'] . ', ' . (isset($parameter['default']) ? 1 : 0) . ')'); break; case 'variable': if (isset($parameter['cast'])) { switch ($parameter['cast']['type']) { case 'variable': $value = $parameter['cast']['value']; $headerPrinter->output("\t" . 'ZEND_ARG_OBJ_INFO(0, ' . $parameter['name'] . ', ' . Utils::escapeClassName($compilationContext->getFullName($value)) . ', ' . (isset($parameter['default']) ? 1 : 0) . ')'); break; default: throw new Exception('Unexpected exception'); } } else { $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); } break; default: $headerPrinter->output("\t" . 'ZEND_ARG_INFO(0, ' . $parameter['name'] . ')'); break; } } $headerPrinter->output('ZEND_END_ARG_INFO()'); $headerPrinter->outputBlankLine(); } /** Generate FE's */ $paramData = count($parameters) ? $argInfoName : 'NULL'; if ($func->isGlobal()) { $entryPrinter->output('ZEND_NAMED_FE(' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')'); } else { $entryPrinter->output('ZEND_NS_NAMED_FE("' . str_replace('\\', '\\\\', $func->getNamespace()) . '", ' . $func->getName() . ', ZEND_FN(' . $funcName . '), ' . $paramData . ')'); } } $entryPrinter->output('ZEND_FE_END'); return array($headerPrinter->getOutput(), $entryPrinter->getOutput()); }
/** * Tries to find specific an specialized optimizer for function calls * * @param string $funcName * @param array $expression * @param Call $call * @param CompilationContext $compilationContext */ protected function optimize($funcName, array $expression, Call $call, CompilationContext $compilationContext) { $optimizer = false; /** * Check if the optimizer is already cached */ if (!isset(self::$_optimizers[$funcName])) { $camelizeFunctionName = Utils::camelize($funcName); /** * Check every optimizer directory for an optimizer */ foreach (self::$_optimizerDirectories as $directory) { $path = $directory . DIRECTORY_SEPARATOR . $camelizeFunctionName . 'Optimizer.php'; if (file_exists($path)) { require_once $path; $className = 'Zephir\\Optimizers\\FunctionCall\\' . $camelizeFunctionName . 'Optimizer'; if (!class_exists($className, false)) { throw new \Exception('Class ' . $className . ' cannot be loaded'); } $optimizer = new $className(); if (!$optimizer instanceof OptimizerAbstract) { throw new \Exception('Class ' . $className . ' must be instance of OptimizerAbstract'); } break; } } self::$_optimizers[$funcName] = $optimizer; } else { $optimizer = self::$_optimizers[$funcName]; } if ($optimizer) { return $optimizer->optimize($expression, $call, $compilationContext); } return false; }
/** * Assigns a default value * * @param array $parameter * @param CompilationContext $compilationContext * @return string * @throws CompilerException */ public function assignDefaultValue(array $parameter, CompilationContext $compilationContext) { if (isset($parameter['data-type'])) { $dataType = $parameter['data-type']; } else { $dataType = 'variable'; } /** * Class-Hinted parameters only can be null? */ if (isset($parameter['cast'])) { if ($parameter['default']['type'] != 'null') { throw new CompilerException('Class-Hinted parameters only can have "null" as default parameter', $parameter); } } $oldCodePrinter = $compilationContext->codePrinter; $codePrinter = new CodePrinter(); $codePrinter->increaseLevel(); $codePrinter->increaseLevel(); $compilationContext->codePrinter = $codePrinter; $paramVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext); /** * @todo Refactoring this place, move to one - static-constant-access */ switch ($dataType) { case 'int': case 'uint': case 'long': case 'ulong': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'int') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(int)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'int': case 'uint': case 'long': $codePrinter->output($parameter['name'] . ' = ' . $parameter['default']['value'] . ';'); break; case 'double': $codePrinter->output($parameter['name'] . ' = (int) ' . $parameter['default']['value'] . ';'); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(int)", $parameter); } break; case 'double': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'double') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(double)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'int': case 'uint': case 'long': $codePrinter->output($parameter['name'] . ' = (double) ' . $parameter['default']['value'] . ';'); break; case 'double': $codePrinter->output($parameter['name'] . ' = ' . $parameter['default']['value'] . ';'); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(double)", $parameter); } break; case 'bool': switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'bool') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(bool)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $codePrinter->output($parameter['name'] . ' = 0;'); break; case 'bool': if ($parameter['default']['value'] == 'true') { $codePrinter->output($parameter['name'] . ' = 1;'); } else { $codePrinter->output($parameter['name'] . ' = 0;'); } break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(bool)", $parameter); } break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); if ($compiledExpression->getType() != 'string') { throw new CompilerException("Default parameter value type: " . $compiledExpression->getType() . " cannot be assigned to variable(string)", $parameter); } $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'null': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, null, $compilationContext); break; case 'string': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, Utils::addSlashes($parameter['default']['value'], true), $compilationContext); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(string)", $parameter); } break; case 'array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); switch ($parameter['default']['type']) { case 'null': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->initArray($paramVariable, $compilationContext, null); break; case 'empty-array': case 'array': $compilationContext->backend->initVar($paramVariable, $compilationContext); $compilationContext->backend->initArray($paramVariable, $compilationContext, null); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(array)", $parameter); } break; case 'variable': $symbolVariable = $compilationContext->symbolTable->getVariableForWrite($parameter['name'], $compilationContext, $parameter['default']); switch ($parameter['default']['type']) { case 'static-constant-access': /** * Now I can write code for easy use on Expression because code in this method don't write with codePrinter ;( * @todo Rewrite all to codePrinter */ $expression = new Expression($parameter['default']); $expression->setExpectReturn(true, $symbolVariable); $compiledExpression = $expression->compile($compilationContext); $parameter['default']['type'] = $compiledExpression->getType(); $parameter['default']['value'] = $compiledExpression->getCode(); $compilationContext->codePrinter = $oldCodePrinter; return $this->assignDefaultValue($parameter, $compilationContext); break; case 'int': case 'uint': case 'long': case 'ulong': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignLong($symbolVariable, $parameter['default']['value'], $compilationContext); break; case 'double': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignDouble($symbolVariable, $parameter['default']['value'], $compilationContext); break; case 'string': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->assignString($paramVariable, Utils::addSlashes($parameter['default']['value'], true), $compilationContext); break; case 'bool': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { if ($parameter['default']['value'] == 'true') { $compilationContext->backend->assignZval($paramVariable, $compilationContext->backend->resolveValue('true', $compilationContext), $compilationContext); } else { $compilationContext->backend->assignZval($paramVariable, $compilationContext->backend->resolveValue('false', $compilationContext), $compilationContext); } } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); if ($parameter['default']['value'] == 'true') { $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('true', $compilationContext), $compilationContext); } else { $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('false', $compilationContext), $compilationContext); } } break; case 'null': $expectedMutations = $compilationContext->symbolTable->getExpectedMutations($parameter['name']); if ($expectedMutations < 2) { $compilationContext->backend->assignZval($symbolVariable, $compilationContext->backend->resolveValue('null', $compilationContext), $compilationContext); } else { $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->copyOnWrite($paramVariable, $compilationContext->backend->resolveValue('null', $compilationContext), $compilationContext); } break; case 'empty-array': $compilationContext->symbolTable->mustGrownStack(true); $compilationContext->headersManager->add('kernel/memory'); $compilationContext->backend->initVar($symbolVariable, $compilationContext); $compilationContext->backend->initArray($symbolVariable, $compilationContext, null); break; default: throw new CompilerException("Default parameter value type: " . $parameter['default']['type'] . " cannot be assigned to variable(variable)", $parameter); } break; default: throw new CompilerException("Default parameter type: " . $dataType, $parameter); } $compilationContext->codePrinter = $oldCodePrinter; return $codePrinter->getOutput(); }
/** * Throws an exception escaping the data * * @param CodePrinter $printer * @param string $class * @param string $message * @param array $expression */ private function throwStringException(CodePrinter $printer, $class, $message, $expression) { $message = Utils::addSlashes($message); $path = Compiler::getShortUserPath($expression['file']); $printer->output(sprintf('ZEPHIR_THROW_EXCEPTION_DEBUG_STR(%s, "%s", "%s", %s);', $class, $message, $path, $expression['line'])); $printer->output('return;'); }
/** * Resolve parameters using zvals in the stack and without allocating memory for constants * * @param array $parameters * @param CompilationContext $compilationContext * @param array $expression * @return array */ public function getReadOnlyResolvedParams($parameters, CompilationContext $compilationContext, array $expression) { $codePrinter = $compilationContext->codePrinter; $exprParams = $this->getResolvedParamsAsExpr($parameters, $compilationContext, $expression, true); $params = array(); $types = array(); $dynamicTypes = array(); foreach ($exprParams as $compiledExpression) { $expression = $compiledExpression->getOriginal(); switch ($compiledExpression->getType()) { case 'null': $params[] = 'ZEPHIR_GLOBAL(global_null)'; $types[] = 'null'; $dynamicTypes[] = 'null'; break; case 'int': case 'uint': case 'long': $parameterVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_LONG(&' . $parameterVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'char': case 'uchar': $parameterVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_LONG(&' . $parameterVariable->getName() . ', \'' . $compiledExpression->getCode() . '\');'); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'double': $parameterVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_DOUBLE(&' . $parameterVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'bool': if ($compiledExpression->getCode() == 'true') { $params[] = 'ZEPHIR_GLOBAL(global_true)'; } else { if ($compiledExpression->getCode() == 'false') { $params[] = 'ZEPHIR_GLOBAL(global_false)'; } else { throw new Exception('?'); } } $types[] = 'bool'; $dynamicTypes[] = 'bool'; break; case 'ulong': case 'string': case 'istring': $parameterVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_STRING(&' . $parameterVariable->getName() . ', "' . Utils::addSlashes($compiledExpression->getCode()) . '", 0);'); $this->_temporalVariables[] = $parameterVariable; $params[] = '&' . $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'array': $parameterVariable = $compilationContext->symbolTable->getVariableForRead($compiledExpression->getCode(), $compilationContext, $expression); $params[] = $parameterVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); break; case 'variable': $parameterVariable = $compilationContext->symbolTable->getVariableForRead($compiledExpression->getCode(), $compilationContext, $expression); switch ($parameterVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $parameterTempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_LONG(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'char': case 'uchar': $parameterTempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_LONG(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'double': $parameterTempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $expression); $codePrinter->output('ZVAL_DOUBLE(&' . $parameterTempVariable->getName() . ', ' . $compiledExpression->getCode() . ');'); $params[] = '&' . $parameterTempVariable->getName(); $types[] = $parameterVariable->getType(); $dynamicTypes[] = $parameterVariable->getType(); $this->_temporalVariables[] = $parameterTempVariable; break; case 'bool': $params[] = '(' . $parameterVariable->getName() . ' ? ZEPHIR_GLOBAL(global_true) : ZEPHIR_GLOBAL(global_false))'; $dynamicTypes[] = $parameterVariable->getType(); $types[] = $parameterVariable->getType(); break; case 'string': case 'variable': case 'array': if ($parameterVariable->isLocalOnly()) { $params[] = '&' . $parameterVariable->getName(); } else { $params[] = $parameterVariable->getName(); } $dynamicTypes[] = $parameterVariable->getType(); $types[] = $parameterVariable->getType(); break; default: throw new CompilerException("Cannot use variable type: " . $parameterVariable->getType() . " as parameter", $expression); } break; default: throw new CompilerException("Cannot use value type: " . $compiledExpression->getType() . " as parameter", $expression); } } $this->_resolvedTypes = $types; $this->_resolvedDynamicTypes = $dynamicTypes; return $params; }
/** * Resolves the access to a property in an object * * @param array $expression * @param CompilationContext $compilationContext * @return \CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; $propertyAccess = $expression; $expr = new Expression($propertyAccess['left']); $exprVariable = $expr->compile($compilationContext); switch ($exprVariable->getType()) { case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($exprVariable->getCode(), $compilationContext, $expression); switch ($variableVariable->getType()) { case 'variable': break; default: throw new CompilerException("Variable type: " . $variableVariable->getType() . " cannot be used as object", $propertyAccess['left']); } break; default: throw new CompilerException("Cannot use expression: " . $exprVariable->getType() . " as an object", $propertyAccess['left']); } switch ($propertyAccess['right']['type']) { case 'variable': $propertyVariable = $compilationContext->symbolTable->getVariableForRead($propertyAccess['right']['value'], $compilationContext, $expression); break; case 'string': $propertyVariable = null; break; default: throw new CompilerException("Variable type: " . $propertyAccess['right']['type'] . " cannot be used as object", $propertyAccess['left']); } /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } /** * Variable that receives a property value must be polymorphic */ if ($symbolVariable && !$symbolVariable->isVariable()) { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign property value", $expression); } /** * At this point, we don't know the exact dynamic type fetched from the property */ $symbolVariable->setDynamicTypes('undefined'); $compilationContext->headersManager->add('kernel/object'); $property = $propertyVariable ? $propertyVariable : Utils::addSlashes($expression['right']['value']); $compilationContext->backend->fetchProperty($symbolVariable, $variableVariable, $property, false, $compilationContext, false); return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { foreach ($this->_statement['expressions'] as $echoExpr) { $expr = new Expression($echoExpr); $expr->setReadOnly(true); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': $compilationContext->codePrinter->output('php_printf("%d", ' . $resolvedExpr->getCode() . ');'); break; case 'bool': $compilationContext->codePrinter->output('php_printf("%s", ' . $resolvedExpr->getBooleanCode() . ' ? "1": "");'); break; case 'double': $compilationContext->codePrinter->output('php_printf("%f", ' . $resolvedExpr->getCode() . ');'); break; case 'char': case 'uchar': $compilationContext->codePrinter->output('php_printf("%c", \'' . $resolvedExpr->getCode() . '\');'); break; case 'long': $compilationContext->codePrinter->output('php_printf("%ld", ' . $resolvedExpr->getCode() . ');'); break; case 'string': $compilationContext->codePrinter->output('php_printf("' . Utils::addSlashes($resolvedExpr->getCode()) . '");'); break; case 'null': break; case 'variable': $variable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $echoExpr); switch ($variable->getType()) { case 'int': $compilationContext->codePrinter->output('php_printf("%d", ' . $variable->getName() . ');'); break; case 'long': $compilationContext->codePrinter->output('php_printf("%ld", ' . $variable->getName() . ');'); break; case 'double': $compilationContext->codePrinter->output('php_printf("%f", ' . $variable->getName() . ');'); break; case 'uchar': case 'char': $compilationContext->codePrinter->output('php_printf("%c", ' . $variable->getName() . ');'); break; case 'bool': $compilationContext->codePrinter->output('php_printf("%s", ' . $variable->getName() . ' ? "1": "");'); break; case 'string': case 'variable': $compilationContext->codePrinter->output('zend_print_zval(' . $variable->getName() . ', 0);'); break; case 'null': break; default: throw new CompilerException("Unknown type: " . $variable->getType(), $echoExpr); } break; default: throw new CompilerException("Unknown type: " . $resolvedExpr->getType(), $echoExpr); } } }
/** * Generates the concatenation code * * @return array */ public function genConcatCode() { $code = ' #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <php.h> #include "php_ext.h" #include <ext/standard/php_string.h> #include "ext.h" #include "kernel/main.h" #include "kernel/memory.h" #include "kernel/concat.h"' . PHP_EOL . PHP_EOL; $pcodeh = ' #ifndef ZEPHIR_KERNEL_CONCAT_H #define ZEPHIR_KERNEL_CONCAT_H #include <php.h> #include <Zend/zend.h> #include "kernel/main.h" '; $codeh = ''; $macros = array(); ksort($this->concatKeys, SORT_STRING); foreach ($this->concatKeys as $key => $one) { $len = strlen($key); $params = array(); $zvalCopy = array(); $useCopy = array(); $avars = array(); $zvars = array(); $svars = array(); $lengths = array(); $sparams = array(); $lparams = array(); for ($i = 0; $i < $len; $i++) { $n = $i + 1; $t = substr($key, $i, 1); $sparams[] = 'op' . $n; if ($t == 's') { $params[] = 'const char *op' . $n . ', zend_uint op' . $n . '_len'; $lparams[] = 'op' . $n . ', sizeof(op' . $n . ')-1'; $lengths[] = 'op' . $n . '_len'; $svars[] = $n; $avars[$n] = 's'; } else { $params[] = 'zval *op' . $n; $lparams[] = 'op' . $n; $zvalCopy[] = 'op' . $n . '_copy'; $useCopy[] = 'use_copy' . $n . ' = 0'; $lengths[] = 'Z_STRLEN_P(op' . $n . ')'; $zvars[] = $n; $avars[$n] = 'v'; } } $macros[] = '#define ZEPHIR_CONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(result, ' . join(', ', $lparams) . ', 0);'; $macros[] = '#define ZEPHIR_SCONCAT_' . strtoupper($key) . '(result, ' . join(', ', $sparams) . ') \\' . PHP_EOL . "\t" . ' zephir_concat_' . $key . '(result, ' . join(', ', $lparams) . ', 1);'; $macros[] = ''; $proto = 'void zephir_concat_' . $key . '(zval *result, ' . join(', ', $params) . ', int self_var)'; $proto = 'void zephir_concat_' . $key . '(zval *result, ' . join(', ', $params) . ', int self_var)'; $codeh .= '' . $proto . ';' . PHP_EOL; $code .= $proto . '{' . PHP_EOL . PHP_EOL; if (count($zvalCopy)) { $code .= "\t" . 'zval result_copy, ' . join(', ', $zvalCopy) . ';' . PHP_EOL; $code .= "\t" . 'int use_copy = 0, ' . join(', ', $useCopy) . ';' . PHP_EOL; } else { $code .= "\t" . 'zval result_copy;' . PHP_EOL; $code .= "\t" . 'int use_copy = 0;' . PHP_EOL; } $code .= "\t" . 'uint offset = 0, length;' . PHP_EOL . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (Z_TYPE_P(op' . $zvar . ') != IS_STRING) {' . PHP_EOL; $code .= "\t" . ' use_copy' . $zvar . ' = zend_make_printable_zval(op' . $zvar . ', &op' . $zvar . '_copy);' . PHP_EOL; $code .= "\t" . ' if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' op' . $zvar . ' = &op' . $zvar . '_copy;' . PHP_EOL; $code .= "\t" . ' }' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'length = ' . join(' + ', $lengths) . ';' . PHP_EOL; $code .= "\t" . 'if (self_var) {' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t\t" . 'if (Z_TYPE_P(result) != IS_STRING) {' . PHP_EOL; $code .= "\t\t\t" . 'use_copy = zend_make_printable_zval(result, &result_copy);' . PHP_EOL; $code .= "\t\t\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t\t\t\t" . 'ZEPHIR_CPY_WRT_CTOR(result, (&result_copy));' . PHP_EOL; $code .= "\t\t\t" . '}' . PHP_EOL; $code .= "\t\t" . '}' . PHP_EOL . PHP_EOL; $code .= "\t\t" . 'offset = Z_STRLEN_P(result);' . PHP_EOL; $code .= "\t\t" . 'length += offset;' . PHP_EOL; $code .= "\t\t" . 'Z_STR_P(result) = zend_string_realloc(Z_STR_P(result), length, 0);' . PHP_EOL; $code .= '' . PHP_EOL; $code .= "\t" . '} else {' . PHP_EOL; $code .= "\t\t" . 'ZVAL_STR(result, zend_string_alloc(length, 0));' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $position = ''; foreach ($avars as $n => $type) { if ($type == 's') { $code .= "\t" . 'memcpy(Z_STRVAL_P(result) + offset' . $position . ', op' . $n . ', op' . $n . '_len);' . PHP_EOL; $position .= ' + op' . $n . '_len'; } else { $code .= "\t" . 'memcpy(Z_STRVAL_P(result) + offset' . $position . ', Z_STRVAL_P(op' . $n . '), Z_STRLEN_P(op' . $n . '));' . PHP_EOL; $position .= ' + Z_STRLEN_P(op' . $n . ')'; } } $code .= "\t" . 'Z_STRVAL_P(result)[length] = 0;' . PHP_EOL; $code .= "\t" . 'zend_string_forget_hash_val(Z_STR_P(result));' . PHP_EOL; foreach ($zvars as $zvar) { $code .= "\t" . 'if (use_copy' . $zvar . ') {' . PHP_EOL; $code .= "\t" . ' zval_dtor(op' . $zvar . ');' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; } $code .= "\t" . 'if (use_copy) {' . PHP_EOL; $code .= "\t" . ' zval_dtor(&result_copy);' . PHP_EOL; $code .= "\t" . '}' . PHP_EOL . PHP_EOL; $code .= "}" . PHP_EOL . PHP_EOL; } $code .= <<<EOF void zephir_concat_function(zval *result, zval *op1, zval *op2) /* {{{ */ { zval tmp; /* Force separate zval, if op2=result will be reallocated */ if (unlikely(result == op2)) { ZVAL_COPY_VALUE(&tmp, op2); op2 = &tmp; } concat_function(result, op1, op2 TSRMLS_CC); } EOF; $codeh .= "void zephir_concat_function(zval *result, zval *op1, zval *op2);\n#endif /* ZEPHIR_KERNEL_CONCAT_H */\n"; Utils::checkAndWriteIfNeeded($pcodeh . join(PHP_EOL, $macros) . PHP_EOL . PHP_EOL . $codeh, 'ext/kernel/concat.h'); Utils::checkAndWriteIfNeeded($code, 'ext/kernel/concat.c'); }
/** * 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); } }
/** * Compiles the method * * @param CompilationContext $compilationContext * @return null * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { /** * Set the method currently being compiled */ $compilationContext->currentMethod = $this; if (is_object($this->_statements)) { /** * This pass checks for zval variables than can be potentially * used without allocating memory and track it * these variables are stored in the stack */ if ($compilationContext->config->get('local-context-pass', 'optimizations')) { $localContext = new LocalContextPass(); $localContext->pass($this->_statements); } else { $localContext = null; } /** * This pass tries to infer types for dynamic variables * replacing them by low level variables */ if ($compilationContext->config->get('static-type-inference', 'optimizations')) { $typeInference = new StaticTypeInference(); $typeInference->pass($this->_statements); if ($compilationContext->config->get('static-type-inference-second-pass', 'optimizations')) { $typeInference->reduce(); $typeInference->pass($this->_statements); } } else { $typeInference = null; } /** * This pass counts how many times a specific */ if ($compilationContext->config->get('call-gatherer-pass', 'optimizations')) { $callGathererPass = new CallGathererPass($compilationContext); $callGathererPass->pass($this->_statements); } else { $callGathererPass = null; } } else { $localContext = null; $typeInference = null; $callGathererPass = null; } /** * 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 both function and method call 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; 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'); } } } $compilationContext->codePrinter->increaseLevel(); /** * Checks that a class-hinted variable meets its declaration */ foreach ($classCastChecks as $classCastCheck) { foreach ($classCastCheck[0]->getClassTypes() as $className) { /** * If the parameter is nullable check it must pass the 'instanceof' validation */ if (!isset($classCastCheck[1]['default'])) { $evalExpr = new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className))); } else { $evalExpr = new BinaryOperatorBuilder('and', new BinaryOperatorBuilder('not-equals', new TypeOfOperatorBuilder(new VariableBuilder($classCastCheck[0]->getName())), new LiteralBuilder("string", "null")), new UnaryOperatorBuilder('not', new BinaryOperatorBuilder('instanceof', new VariableBuilder($classCastCheck[0]->getName()), new VariableBuilder('\\' . $className)))); } $ifCheck = new IfStatementBuilder($evalExpr, new StatementsBlockBuilder(array(new ThrowStatementBuilder(new NewInstanceOperatorBuilder('\\InvalidArgumentException', array(new ParameterBuilder(new LiteralBuilder("string", "Parameter '" . $classCastCheck[0]->getName() . "' must be an instance of '" . Utils::escapeClassName($className) . "'")))))))); $ifStatement = new IfStatement($ifCheck->get()); $ifStatement->compile($compilationContext); } } $compilationContext->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': case 'char': case 'uchar': $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': $params[] = '&' . $parameter['name']; break; default: $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 'string': case 'array': 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(); $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; } $code .= PHP_EOL; } $code .= $initCode . $initVarCode; $codePrinter->preOutput($code); /** * 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; } } 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; default: throw new CompilerException("Unsupported type in declare: " . $type); } $groupVariables = array(); $defaultValues = array(); /** * @var $variables Variable[] */ foreach ($variables as $variable) { if (($type == 'variable' || $type == 'string' || $type == 'array' || $type == 'resource' || $type == 'callable' || $type == 'object') && $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'; } } } else { if ($variable->isLocalOnly()) { $groupVariables[] = $variable->getName(); } else { if ($variable->isDoublePointer()) { if ($variable->mustInitNull()) { $groupVariables[] = $pointer . $pointer . $variable->getName() . ' = NULL'; } else { $groupVariables[] = $pointer . $pointer . $variable->getName(); } } else { $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; default: $groupVariables[] = $pointer . $variable->getName() . ' = ' . $defaultValue; break; } } else { if ($variable->mustInitNull() && $pointer) { $groupVariables[] = $pointer . $variable->getName() . ' = NULL'; } else { $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']); } } } /** * Remove macros that restore the memory 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; }
/** * Resolves a PHP constant value into C-code * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws \Zephir\CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $isPhpConstant = false; $isZephirConstant = false; $constantName = $expression['value']; $mergedConstants = array_merge($this->envConstans, $this->magickConstants, $this->resources); if (!defined($expression['value']) && !in_array($constantName, $mergedConstants)) { if (!$compilationContext->compiler->isConstant($constantName)) { $compilationContext->logger->warning("Constant '" . $constantName . "' does not exist at compile time", 'nonexistent-constant', $expression); } else { $isZephirConstant = true; } } else { if (strpos($constantName, 'VERSION') !== false) { $isPhpConstant = false; } else { $isPhpConstant = true; } } if ($isZephirConstant && !in_array($constantName, $this->resources)) { $constant = $compilationContext->compiler->getConstant($constantName); return new LiteralCompiledExpression($constant[0], $constant[1], $expression); } if ($isPhpConstant && !in_array($constantName, $mergedConstants)) { $constantName = constant($constantName); $type = strtolower(gettype($constantName)); switch ($type) { case 'integer': return new LiteralCompiledExpression('int', $constantName, $expression); case 'double': return new LiteralCompiledExpression('double', $constantName, $expression); case 'string': return new LiteralCompiledExpression('string', Utils::addSlashes($constantName, true, Types::STRING), $expression); case 'object': throw new CompilerException('?'); default: return new LiteralCompiledExpression($type, $constantName, $expression); } } if (in_array($constantName, $this->magickConstants)) { switch ($constantName) { case '__CLASS__': return new CompiledExpression('string', $compilationContext->classDefinition->getCompleteName(), $expression); //no break //no break case '__NAMESPACE__': return new CompiledExpression('string', $compilationContext->classDefinition->getNamespace(), $expression); //no break //no break case '__METHOD__': return new CompiledExpression('string', $compilationContext->classDefinition->getName() . ':' . $compilationContext->currentMethod->getName(), $expression); //no break //no break case '__FUNCTION__': return new CompiledExpression('string', $compilationContext->currentMethod->getName(), $expression); //no break } $compilationContext->logger->warning("Magic constant '" . $constantName . "' is not supported", 'not-supported-magic-constant', $expression); return new LiteralCompiledExpression('null', null, $expression); } if ($this->_expecting && $this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; $symbolVariable->setLocalOnly(false); $symbolVariable->setMustInitNull(true); $symbolVariable->initVariant($compilationContext); } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } if (!$symbolVariable->isVariable()) { throw new CompilerException('Cannot use variable: ' . $symbolVariable->getType() . ' to assign property value', $expression); } $compilationContext->codePrinter->output('ZEPHIR_GET_CONSTANT(' . $symbolVariable->getName() . ', "' . $expression['value'] . '");'); return new CompiledExpression('variable', $symbolVariable->getName(), $expression); }
/** * Creates a new instance * * @param $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; /** * Resolves the symbol that expects the value */ $this->_literalOnly = false; $symbolVariable = $this->getExpectedNonLiteral($compilationContext, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("Objects can only be instantiated into dynamic variables", $expression); } if ($symbolVariable->isLocalOnly()) { throw new CompilerException("Cannot use non-heap variable to store new instance", $expression); } if ($symbolVariable->getName() != 'return_value') { if ($symbolVariable->hasDifferentDynamicType(array('unknown', 'undefined', 'object', 'null'))) { $compilationContext->logger->warning('Possible attempt to use non-object in "new" operator', 'non-valid-new', $expression); } } /** * Mark variables as dynamic objects */ $symbolVariable->setDynamicTypes('object'); $dynamic = false; if ($expression['class'] == 'self' || $expression['class'] == 'static') { $className = $compilationContext->classDefinition->getCompleteName(); } else { $className = $expression['class']; $dynamic = $expression['dynamic']; if (!$dynamic) { $className = $compilationContext->getFullName($expression['class']); } } if (!$className) { throw new CompilerException("A class name is required to instantiate the object", $expression); } /** * stdclass doesn't have constructors */ $lowerClassName = strtolower($className); $isStdClass = $lowerClassName === 'stdclass' || $lowerClassName === '\\stdclass'; if ($isStdClass) { if (isset($expression['parameters']) && count($expression['parameters']) > 0) { throw new CompilerException("stdclass does not receive parameters in its constructor", $expression); } $compilationContext->backend->initObject($symbolVariable, null, $compilationContext); $symbolVariable->setClassTypes('stdclass'); } else { $classDefinition = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); } /** * Classes inside the same extension */ if ($classDefinition) { $compilationContext->backend->initObject($symbolVariable, $classDefinition->getClassEntry($compilationContext), $compilationContext); $symbolVariable->setClassTypes($className); $symbolVariable->setAssociatedClass($classDefinition); } else { /** * Classes outside the extension */ if ($dynamic) { $classNameVariable = $compilationContext->symbolTable->getVariableForRead($className, $compilationContext, $expression); if ($classNameVariable->isNotVariableAndString()) { throw new CompilerException("Only dynamic/string variables can be used in new operator. " . $classNameVariable->getName(), $expression); } /** * Use a safe string version of the variable to avoid segfaults */ $compilationContext->headersManager->add('kernel/object'); $safeSymbolVariable = $compilationContext->symbolTable->getTempVariable('variable', $compilationContext, $expression); $safeSymbolVariable->setMustInitNull(true); $safeSymbolVariable->setIsInitialized(true, $compilationContext, $expression); $safeSymbolVariable->increaseUses(); $compilationContext->codePrinter->output('zephir_fetch_safe_class(' . $safeSymbolVariable->getName() . ', ' . $classNameVariable->getName() . ');'); $safeSymbol = $compilationContext->backend->getVariableCode($safeSymbolVariable); $classNameToFetch = 'Z_STRVAL_P(' . $safeSymbol . '), Z_STRLEN_P(' . $safeSymbol . ')'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, true, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { if (!class_exists($className, false)) { $compilationContext->logger->warning('Class "' . $className . '" does not exist at compile time', "nonexistent-class", $expression); $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $reflectionClass = new \ReflectionClass($className); if ($reflectionClass->isInterface()) { throw new CompilerException('Interfaces cannot be instantiated', $expression); } else { if (method_exists($reflectionClass, 'isTrait')) { if ($reflectionClass->isTrait()) { throw new CompilerException('Traits cannot be instantiated', $expression); } } } $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true); if (!$classEntry) { $classNameToFetch = 'SL("' . Utils::escapeClassName($className) . '")'; $zendClassEntry = $compilationContext->cacheManager->getClassEntryCache()->get($classNameToFetch, false, $compilationContext); $classEntry = $zendClassEntry->getName(); } else { $symbolVariable->setAssociatedClass($reflectionClass); } } $symbolVariable->setClassTypes($className); } $compilationContext->backend->initObject($symbolVariable, $classEntry, $compilationContext); } } /** * Mark variable initialized */ $symbolVariable->setIsInitialized(true, $compilationContext, $expression); /** * Don't check the constructor for stdclass instances */ if ($isStdClass) { return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); } /** * Call the constructor * For classes in the same extension we check if the class does implement a constructor * For external classes we always assume the class does implement a constructor */ $callConstructor = false; if ($compilationContext->compiler->isClass($className)) { $classDefinition = $compilationContext->compiler->getClassDefinition($className); if ($classDefinition->getType() != 'class') { throw new CompilerException("Only classes can be instantiated", $expression); } $callConstructor = $classDefinition->hasMethod("__construct"); } else { if ($compilationContext->compiler->isBundledClass($className)) { $classDefinition = $compilationContext->compiler->getInternalClassDefinition($className); $callConstructor = $classDefinition->hasMethod("__construct"); } } /* @TODO use the MethodBuilder here */ if (isset($expression['parameters'])) { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'parameters' => $expression['parameters'], 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } else { $callExpr = new Expression(array('variable' => array('type' => 'variable', 'value' => $symbolVariable->getRealName()), 'name' => '__construct', 'call-type' => MethodCall::CALL_NORMAL, 'file' => $expression['file'], 'line' => $expression['line'], 'char' => $expression['char'], 'check' => $callConstructor)); } /** * If we are certain that there is a constructor we call it, otherwise we checked it at runtime. */ if ($callConstructor) { $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); } else { $compilationContext->headersManager->add('kernel/fcall'); /* @todo, generate the code using builders */ $compilationContext->backend->checkConstructor($symbolVariable, $compilationContext); $codePrinter->increaseLevel(); $methodCall = new MethodCall(); $callExpr->setExpectReturn(false); $methodCall->compile($callExpr, $compilationContext); $codePrinter->decreaseLevel(); $codePrinter->output('}'); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Convert Class/Interface name to C ClassEntry * * @param string $className * @param CompilationContext $compilationContext * @param boolean $check * @return string * @throws CompilerException */ public function getClassEntryByClassName($className, CompilationContext $compilationContext, $check = true) { switch (strtolower($className)) { /** * Zend classes */ case 'exception': $classEntry = 'zend_exception_get_default(TSRMLS_C)'; break; /** * Zend interfaces (Zend/zend_interfaces.h) */ /** * Zend interfaces (Zend/zend_interfaces.h) */ case 'iterator': $classEntry = 'zend_ce_iterator'; break; case 'arrayaccess': $classEntry = 'zend_ce_arrayaccess'; break; case 'serializable': $classEntry = 'zend_ce_serializable'; break; case 'iteratoraggregate': $classEntry = 'zend_ce_aggregate'; break; /** * SPL Exceptions */ /** * SPL Exceptions */ case 'logicexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_LogicException'; break; case 'badfunctioncallexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_BadFunctionCallException'; break; case 'badmethodcallexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_BadMethodCallException'; break; case 'domainexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_DomainException'; break; case 'invalidargumentexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_InvalidArgumentException'; break; case 'lengthexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_LengthException'; break; case 'outofrangeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OutOfRangeException'; break; case 'runtimeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_RuntimeException'; break; case 'outofboundsexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OutOfBoundsException'; break; case 'overflowexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_OverflowException'; break; case 'rangeexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_RangeException'; break; case 'underflowexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_UnderflowException'; break; case 'unexpectedvalueexception': $compilationContext->headersManager->add('ext/spl/spl_exceptions'); $classEntry = 'spl_ce_UnexpectedValueException'; break; /** * SPL Iterators Interfaces (spl/spl_iterators.h) */ /** * SPL Iterators Interfaces (spl/spl_iterators.h) */ case 'recursiveiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveIterator'; break; case 'recursiveiteratoriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveIteratorIterator'; break; case 'recursivetreeiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveTreeIterator'; break; case 'filteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_FilterIterator'; break; case 'recursivefilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveFilterIterator'; break; case 'parentiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_ParentIterator'; break; case 'seekableiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_SeekableIterator'; break; case 'limititerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_LimitIterator'; break; case 'cachingiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_CachingIterator'; break; case 'recursivecachingiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveCachingIterator'; break; case 'outeriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_OuterIterator'; break; case 'iteratoriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_IteratorIterator'; break; case 'norewinditerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_NoRewindIterator'; break; case 'infiniteiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_InfiniteIterator'; break; case 'emptyiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_EmptyIterator'; break; case 'appenditerator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_AppendIterator'; break; case 'regexiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RegexIterator'; break; case 'recursiveregexiterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveRegexIterator'; break; case 'directoryiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_DirectoryIterator'; break; case 'filesystemiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_FilesystemIterator'; break; case 'recursivedirectoryiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_RecursiveDirectoryIterator'; break; case 'globiterator': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_GlobIterator'; break; case 'splfileobject': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplFileObject'; break; case 'spltempfileobject': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplTempFileObject'; break; case 'countable': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_Countable'; break; case 'callbackfilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_CallbackFilterIterator'; break; case 'recursivecallbackfilteriterator': $compilationContext->headersManager->add('ext/spl/spl_iterators'); $classEntry = 'spl_ce_RecursiveCallbackFilterIterator'; break; case 'arrayobject': $compilationContext->headersManager->add('ext/spl/spl_array'); $classEntry = 'spl_ce_ArrayObject'; break; case 'splfixedarray': $compilationContext->headersManager->add('ext/spl/spl_fixedarray'); $classEntry = 'spl_ce_SplFixedArray'; break; case 'splpriorityqueue': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplPriorityQueue'; break; case 'splfileinfo': $compilationContext->headersManager->add('ext/spl/spl_directory'); $classEntry = 'spl_ce_SplFileInfo'; break; case 'splheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplHeap'; break; case 'splminheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplMinHeap'; break; case 'splmaxheap': $compilationContext->headersManager->add('ext/spl/spl_heap'); $classEntry = 'spl_ce_SplMaxHeap'; break; case 'splstack': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplStack'; break; case 'splqueue': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplQueue'; break; case 'spldoublylinkedlist': $compilationContext->headersManager->add('ext/spl/spl_dllist'); $classEntry = 'spl_ce_SplDoublyLinkedList'; break; case 'stdclass': $classEntry = 'zend_standard_class_def'; break; case 'closure': $compilationContext->headersManager->add('Zend/zend_closures'); $classEntry = 'zend_ce_closure'; break; case 'pdo': $compilationContext->headersManager->add('ext/pdo/php_pdo_driver'); $classEntry = 'php_pdo_get_dbh_ce()'; break; case 'pdostatement': $compilationContext->headersManager->add('kernel/main'); $classEntry = $compilationContext->backend->fetchClassEntry("pdostatement"); break; case 'pdoexception': $compilationContext->headersManager->add('ext/pdo/php_pdo_driver'); $classEntry = 'php_pdo_get_exception()'; break; case 'datetime': $compilationContext->headersManager->add('ext/date/php_date'); $classEntry = 'php_date_get_date_ce()'; break; case 'datetimezone': $compilationContext->headersManager->add('ext/date/php_date'); $classEntry = 'php_date_get_timezone_ce()'; break; // Reflection /*case 'reflector': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflector_ptr'; break; case 'reflectionexception': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_exception_ptr'; break; case 'reflection': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_ptr'; break; case 'reflectionfunctionabstract': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_abstract_ptr'; break; case 'reflectionfunction': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_ptr'; break; case 'reflectionparameter': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_parameter_ptr'; break; case 'reflectionclass': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_class_ptr'; break; case 'reflectionobject': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_object_ptr'; break; case 'reflectionmethod': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_method_ptr'; break; case 'reflectionproperty': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_property_ptr'; break; case 'reflectionextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_extension_ptr'; break; case 'reflectionzendextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_zend_extension_ptr'; break;*/ // Reflection /*case 'reflector': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflector_ptr'; break; case 'reflectionexception': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_exception_ptr'; break; case 'reflection': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_ptr'; break; case 'reflectionfunctionabstract': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_abstract_ptr'; break; case 'reflectionfunction': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_function_ptr'; break; case 'reflectionparameter': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_parameter_ptr'; break; case 'reflectionclass': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_class_ptr'; break; case 'reflectionobject': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_object_ptr'; break; case 'reflectionmethod': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_method_ptr'; break; case 'reflectionproperty': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_property_ptr'; break; case 'reflectionextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_extension_ptr'; break; case 'reflectionzendextension': $compilationContext->headersManager->add('ext/reflection/php_reflection'); $classEntry = 'reflection_zend_extension_ptr'; break;*/ default: if (!$check) { throw new CompilerException('Unknown class entry for "' . $className . '"'); } else { $classEntry = $compilationContext->backend->fetchClassEntry(Utils::escapeClassName(strtolower($className))); } } return $classEntry; }
/** * @param $expression * @param CompilationContext $context * @return CompiledExpression * @throws CompilerException * @throws \Zephir\Exception */ public function compile($expression, CompilationContext $context) { $left = new Expression($expression['left']); $resolved = $left->compile($context); if ($resolved->getType() != 'variable') { throw new CompilerException("InstanceOf requires a 'dynamic variable' in the left operand", $expression); } $symbolVariable = $context->symbolTable->getVariableForRead($resolved->getCode(), $context, $expression); if (!$symbolVariable->isVariable()) { throw new CompilerException("InstanceOf requires a 'dynamic variable' in the left operand", $expression); } $right = new Expression($expression['right']); $resolved = $right->compile($context); $resolvedVariable = $resolved->getCode(); switch ($resolved->getType()) { case 'string': $className = Utils::getFullName($resolvedVariable, $context->classDefinition->getNamespace(), $context->aliasManager); if ($context->compiler->isClass($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if (!class_exists($className, false)) { $code = 'SL("' . $resolvedVariable . '")'; } else { $classEntry = $context->classDefinition->getClassEntryByClassName($className, $context, true); if (!$classEntry) { $code = 'SL("' . $resolvedVariable . '")'; } } } break; default: switch ($resolved->getType()) { case 'variable': if ($resolvedVariable == 'this') { /** * @todo It's an optimization variant, but maybe we need to get entry in runtime? */ $classEntry = $context->classDefinition->getClassEntry($context); } elseif (!$context->symbolTable->hasVariable($resolvedVariable)) { $className = $context->getFullName($resolvedVariable); if ($className == 'Traversable') { $symbol = $context->backend->getVariableCode($symbolVariable); return new CompiledExpression('bool', 'zephir_zval_is_traversable(' . $symbol . ' TSRMLS_CC)', $expression); } if ($context->compiler->isClass($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if ($context->compiler->isInterface($className)) { $classDefinition = $context->compiler->getClassDefinition($className); $classEntry = $classDefinition->getClassEntry($context); } else { if (!class_exists($className, false)) { $code = 'SL("' . trim(Utils::escapeClassName($className), "\\") . '")'; } else { $classEntry = $context->classDefinition->getClassEntryByClassName($className, $context, true); if (!$classEntry) { $code = 'SL("' . trim(Utils::escapeClassName($className), "\\") . '")'; } } } } } else { $code = 'Z_STRVAL_P(' . $resolvedVariable . '), Z_STRLEN_P(' . $resolvedVariable . ')'; } break; case 'property-access': case 'array-access': $context->headersManager->add('kernel/operators'); $tempVariable = $context->symbolTable->getTempVariableForWrite('string', $context); $tempVariable->setMustInitNull(true); $tempVariableName = $tempVariable->getName(); $context->codePrinter->output('zephir_get_strval(' . $tempVariableName . ', ' . $resolvedVariable . ');'); $code = 'Z_STRVAL_P(' . $tempVariableName . '), Z_STRLEN_P(' . $tempVariableName . ')'; break; default: throw new CompilerException("InstanceOf requires a 'variable' or a 'string' in the right operand", $expression); } } /* @TODO, Possible optimization is use zephir_is_instance when the const class name is an internal class or interface */ $context->headersManager->add('kernel/object'); $symbol = $context->backend->getVariableCode($symbolVariable); if (isset($code)) { return new CompiledExpression('bool', 'zephir_is_instance_of(' . $symbol . ', ' . $code . ' TSRMLS_CC)', $expression); } return new CompiledExpression('bool', 'zephir_instance_of_ev(' . $symbol . ', ' . $classEntry . ' TSRMLS_CC)', $expression); }
public function declareConstant($type, $name, $value, CompilationContext $context) { $ce = $context->classDefinition->getClassEntry($context); $dType = null; switch ($type) { case 'bool': $value = $value == 'false' ? '0' : 1; break; case 'long': case 'int': $dType = 'long'; break; case 'double': break; case 'string': case 'char': if ($type == 'string' || $type == 'char') { $value = "\"" . Utils::addSlashes($value) . "\""; } $dType = 'string'; break; } if (!isset($dType)) { $dType = $type; } if ($dType == 'null') { $context->codePrinter->output('zephir_declare_class_constant_null(' . $ce . ', SL("' . $name . '"));'); } else { $context->codePrinter->output('zephir_declare_class_constant_' . $dType . '(' . $ce . ', SL("' . $name . '"), ' . $value . ');'); } }
/** * Produce the code to register a class constant * * @param CompilationContext $compilationContext * @throws CompilerException * @throws Exception */ public function compile(CompilationContext $compilationContext) { $this->processValue($compilationContext); switch ($this->value['type']) { case 'long': case 'int': $compilationContext->codePrinter->output("zend_declare_class_constant_long(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'double': $compilationContext->codePrinter->output("zend_declare_class_constant_double(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), " . $this->value['value'] . " TSRMLS_CC);"); break; case 'bool': if ($this->value['value'] == 'false') { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 0 TSRMLS_CC);"); } else { $compilationContext->codePrinter->output("zend_declare_class_constant_bool(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), 1 TSRMLS_CC);"); } break; case 'string': case 'char': $compilationContext->codePrinter->output("zend_declare_class_constant_string(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\"), \"" . Utils::addSlashes($this->value['value']) . "\" TSRMLS_CC);"); break; case 'null': $compilationContext->codePrinter->output("zend_declare_class_constant_null(" . $compilationContext->classDefinition->getClassEntry($compilationContext) . ", SL(\"" . $this->getName() . "\") TSRMLS_CC);"); break; default: throw new CompilerException('Type "' . $this->value['type'] . '" is not supported.'); } }
/** * 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; }
/** * @param array $expression * @param CompilationContext $compilationContext * @param boolean $isFullString */ private function _getOptimizedConcat($expression, CompilationContext $compilationContext, &$isFullString) { $originalExpr = $expression; $isFullString = true; $parts = array(); while ($expression && isset($expression['left'])) { $parts[] = $expression['right']; if ($expression['left']['type'] == 'concat') { $expression = $expression['left']; } else { $parts[] = $expression['left']; $expression = null; } } if ($expression) { $parts[] = $expression['right']; $parts[] = $expression['left']; } $key = ''; $concatParts = array(); $parts = array_reverse($parts); foreach ($parts as $part) { $expr = new Expression($part); $expr->setStringOperation(true); switch ($part['type']) { case 'array-access': case 'property-access': $expr->setReadOnly(true); break; default: $expr->setReadOnly($this->_readOnly); break; } $compiledExpr = $expr->compile($compilationContext); switch ($compiledExpr->getType()) { case 'variable': $variable = $compilationContext->symbolTable->getVariableForRead($compiledExpr->getCode(), $compilationContext, $originalExpr); switch ($variable->getType()) { case 'variable': $key .= 'v'; $concatParts[] = $variable->getName(); $isFullString = false; break; case 'string': $key .= 'v'; $concatParts[] = $variable->getName(); break; case 'int': case 'long': $key .= 'v'; $tempVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $originalExpr); $compilationContext->codePrinter->output('ZVAL_LONG(&' . $tempVariable->getName() . ', ' . $compiledExpr->getCode() . ');'); $concatParts[] = '&' . $tempVariable->getName(); break; default: throw new CompilerException("Variable type: " . $variable->getType() . " cannot be used in concat operation", $compiledExpr->getOriginal()); } break; case 'string': $key .= 's'; $concatParts[] = '"' . Utils::addSlashes($compiledExpr->getCode()) . '"'; break; default: throw new CompilerException("Variable type: " . $compiledExpr->getType() . " cannot be used in concat operation", $compiledExpr->getOriginal()); } } $compilationContext->stringsManager->addConcatKey($key); return array($key, join(', ', $concatParts)); }
/** * Compiles traversing of string values * - Evaluated expression must be a string * - Every key must be an integer or compatible * - Every value must be a char/integer or compatible * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileStringTraverse($expression, CompilationContext $compilationContext, $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']); switch ($keyVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': break; default: throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in string traversal", $this->_statement['expr']); } } else { $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('int', $compilationContext); $keyVariable->increaseUses(); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { if ($this->_statement['value'] != '_') { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); switch ($variable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'char': case 'uchar': break; default: throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in string traversal", $this->_statement['expr']); } } else { $variable = $compilationContext->symbolTable->getTempVariableForWrite('char', $compilationContext); $variable->increaseUses(); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); } $tempVariable = $compilationContext->symbolTable->addTemp('long', $compilationContext); /** * Create a temporary value to store the constant string */ if ($expression->getType() == 'string') { $constantVariable = $compilationContext->symbolTable->getTempLocalVariableForWrite('variable', $compilationContext, $this->_statement); $compilationContext->backend->assignString($constantVariable, Utils::addSlashes($expression->getCode()), $compilationContext, true, false); $stringVariable = $constantVariable; } else { $stringVariable = $exprVariable; } $stringVariableCode = $compilationContext->backend->getVariableCode($stringVariable); if ($this->_statement['reverse']) { $codePrinter->output('for (' . $tempVariable->getName() . ' = Z_STRLEN_P(' . $stringVariableCode . '); ' . $tempVariable->getName() . ' >= 0; ' . $tempVariable->getName() . '--) {'); } else { $codePrinter->output('for (' . $tempVariable->getName() . ' = 0; ' . $tempVariable->getName() . ' < Z_STRLEN_P(' . $stringVariableCode . '); ' . $tempVariable->getName() . '++) {'); } if (isset($this->_statement['key'])) { $codePrinter->output("\t" . $keyVariable->getName() . ' = ' . $tempVariable->getName() . '; '); } $compilationContext->headersManager->add('kernel/operators'); $codePrinter->output("\t" . $variable->getName() . ' = ZEPHIR_STRING_OFFSET(' . $stringVariableCode . ', ' . $tempVariable->getName() . ');'); /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Compile statements in the 'for' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->isLoop(true); if (isset($this->_statement['key'])) { $st->getMutateGatherer()->increaseMutations($this->_statement['key']); } $st->getMutateGatherer()->increaseMutations($this->_statement['value']); $st->compile($compilationContext); } $compilationContext->insideCycle--; $codePrinter->output('}'); }
/** * Declare class property with default value * * @param CompilationContext $compilationContext * @param $type * @param $value * @throws CompilerException */ protected function declareProperty(CompilationContext $compilationContext, $type, $value) { $codePrinter = $compilationContext->codePrinter; if (is_object($value)) { //fix this return; } switch ($type) { case 'long': case 'int': $codePrinter->output("zend_declare_property_long(" . $compilationContext->classDefinition->getClassEntry() . ", SL(\"" . $this->getName() . "\"), " . $value . ", " . $this->getVisibilityAccesor() . " TSRMLS_CC);"); break; case 'double': $codePrinter->output("zend_declare_property_double(" . $compilationContext->classDefinition->getClassEntry() . ", SL(\"" . $this->getName() . "\"), " . $value . ", " . $this->getVisibilityAccesor() . " TSRMLS_CC);"); break; case 'bool': $codePrinter->output("zend_declare_property_bool(" . $compilationContext->classDefinition->getClassEntry() . ", SL(\"" . $this->getName() . "\"), " . $this->getBooleanCode($value) . ", " . $this->getVisibilityAccesor() . " TSRMLS_CC);"); break; case Types::CHAR: case Types::STRING: $codePrinter->output("zend_declare_property_string(" . $compilationContext->classDefinition->getClassEntry() . ", SL(\"" . $this->getName() . "\"), \"" . Utils::addSlashes($value, true, $type) . "\", " . $this->getVisibilityAccesor() . " TSRMLS_CC);"); break; case 'array': case 'empty-array': case 'null': $codePrinter->output("zend_declare_property_null(" . $compilationContext->classDefinition->getClassEntry() . ", SL(\"" . $this->getName() . "\"), " . $this->getVisibilityAccesor() . " TSRMLS_CC);"); break; default: throw new CompilerException('Unknown default type: ' . $type, $this->original); } }
/** * Transform class/interface name to FQN format * * @param string $name * @return string */ protected function getFullName($name) { return Utils::getFullName($name, $this->_namespace, $this->_aliasManager); }
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'); }