/** * @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']) && isset($expr['parameters']) && count($expr['parameters']) == 1 && $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']; $class = $classDefinition->getClassEntry(); $this->throwStringException($codePrinter, $class, $message, $statement['expr']); return; } else { if ($compilationContext->compiler->isBundledClass($className)) { $classEntry = $compilationContext->classDefinition->getClassEntryByClassName($className, $compilationContext, true); if ($classEntry) { $message = $expr['parameters'][0]['parameter']['value']; $this->throwStringException($codePrinter, $classEntry, $message, $statement['expr']); return; } } } } else { if (in_array($expr['type'], array('string', 'char', 'int', 'double'))) { $class = $compilationContext->classDefinition->getClassEntryByClassName('Exception', $compilationContext); $this->throwStringException($codePrinter, $class, $expr['value'], $expr); return; } } } $throwExpr = new Expression($expr); $resolvedExpr = $throwExpr->compile($compilationContext); if (!in_array($resolvedExpr->getType(), array('variable', 'string'))) { throw new CompilerException("Expression '" . $resolvedExpr->getType() . '" cannot be used as exception', $expr); } $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $expr); if (!in_array($variableVariable->getType(), array('variable', 'string'))) { throw new CompilerException("Variable '" . $variableVariable->getType() . "' cannot be used as exception", $expr); } $variableCode = $compilationContext->backend->getVariableCode($variableVariable); $codePrinter->output('zephir_throw_exception_debug(' . $variableCode . ', "' . 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->currentTryCatch . ';'); $codePrinter->outputBlankLine(); } if ($variableVariable->isTemporal()) { $variableVariable->setIdle(true); } }
/** * @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); } }
/** * Transform class/interface name to FQN format * * @param string $name * @return string */ protected function getFullName($name) { return Utils::getFullName($name, $this->_namespace, $this->_aliasManager); }
/** * @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); }