/** * Creates a closure * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws \Zephir\CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { $classDefinition = new ClassDefinition($compilationContext->config->get('namespace'), self::$id . '__closure'); $classDefinition->setIsFinal(true); $compilerFile = new CompilerFileAnonymous($classDefinition, $compilationContext->config, $compilationContext->logger); $compilationContext->compiler->addClassDefinition($compilerFile, $classDefinition); /** * Builds parameters using the only parameter available */ $parameters = new ClassMethodParameters(array(array('type' => 'parameter', 'name' => $expression['left']['value'], 'const' => 0, 'data-type' => 'variable', 'mandatory' => 0, 'reference' => 0, 'file' => $expression['left']['file'], 'line' => $expression['left']['line'], 'char' => $expression['left']['char']))); $exprBuilder = BuilderFactory::getInstance(); $statementBlockBuilder = $exprBuilder->statements()->block(array($exprBuilder->statements()->returnX($exprBuilder->raw($expression['right'])))); $block = $statementBlockBuilder->build(); $classMethod = new ClassMethod($classDefinition, array('public', 'final'), '__invoke', $parameters, new StatementsBlock($block), null, null, $expression); $classDefinition->addMethod($classMethod, $block); $compilationContext->headersManager->add('kernel/object'); if ($this->expecting) { if ($this->expectingVariable) { $symbolVariable = $this->expectingVariable; } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $expression); } $symbolVariable->initVariant($compilationContext); $compilationContext->backend->createClosure($symbolVariable, $classDefinition, $compilationContext); self::$id++; return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * Transforms calls to method "toHex" to sprintf('%X') call * * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @return bool|mixed|\Zephir\CompiledExpression */ public function toHex($caller, CompilationContext $compilationContext, Call $call, array $expression) { $exprBuilder = BuilderFactory::getInstance(); $functionCall = $exprBuilder->statements()->functionCall('sprintf', array($exprBuilder->literal(Types::STRING, '%X'), $caller))->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); }
/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $codePrinter = $compilationContext->codePrinter; $compilationContext->insideTryCatch++; $currentTryCatch = ++$compilationContext->currentTryCatch; $codePrinter->outputBlankLine(); $codePrinter->output('/* try_start_' . $currentTryCatch . ': */'); $codePrinter->outputBlankLine(); if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } $codePrinter->outputBlankLine(); $codePrinter->output('try_end_' . $currentTryCatch . ':'); /** * If 'try' is the latest statement add a 'dummy' statement to avoid compilation errors */ $codePrinter->outputBlankLine(); $compilationContext->insideTryCatch--; if (isset($this->_statement['catches'])) { /** * Check if there was an exception */ $codePrinter->output('if (EG(exception)) {'); $codePrinter->increaseLevel(); $exprBuilder = BuilderFactory::getInstance(); foreach ($this->_statement['catches'] as $catch) { if (isset($catch['variable'])) { $variable = $compilationContext->symbolTable->getVariableForWrite($catch['variable']['value'], $compilationContext, $catch['variable']); if ($variable->getType() != 'variable') { throw new CompilerException('Only dynamic variables can be used to catch exceptions', $catch['exception']); } } else { $variable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext, $compilationContext); } $compilationContext->backend->copyOnWrite($variable, 'EG(exception)', $compilationContext); /** * @TODO, use a builder here */ $variable->setIsInitialized(true, $compilationContext, $catch); $variable->setMustInitNull(true); /** * Check if any of the classes in the catch block match the thrown exception */ foreach ($catch['classes'] as $class) { $ifCheck = $exprBuilder->statements()->ifX()->setCondition($exprBuilder->operators()->binary(BinaryOperator::OPERATOR_INSTANCEOF, $exprBuilder->variable($variable->getName()), $exprBuilder->variable($class['value'])))->setStatements($exprBuilder->statements()->block(array_merge(array($exprBuilder->statements()->rawC('zend_clear_exception(TSRMLS_C);')), isset($catch['statements']) ? $catch['statements'] : array()))); $ifStatement = new IfStatement($ifCheck->build()); $ifStatement->compile($compilationContext); } if ($variable->isTemporal()) { $variable->setIdle(true); } } $codePrinter->decreaseLevel(); $codePrinter->output('}'); } else { $codePrinter->output('zend_clear_exception(TSRMLS_C);'); } }
/** * @param $expression * @param CompilationContext $compilationContext * @return bool|CompiledExpression * @throws CompilerException */ public function compile($expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'typeof' expression", $expression['left']); } $functionCall = BuilderFactory::getInstance()->statements()->functionCall('gettype', array($expression['left'])); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); }
/** * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression * @throws CompilerException */ public function compile(array $expression, CompilationContext $compilationContext) { if (!isset($expression['left'])) { throw new CompilerException("Invalid 'left' operand for 'irange' expression", $expression['left']); } if (!isset($expression['right'])) { throw new CompilerException("Invalid 'right' operand for 'irange' expression", $expression['right']); } $exprBuilder = Expression\Builder\BuilderFactory::getInstance(); /** * Implicit type coercing */ $castBuilder = $exprBuilder->operators()->cast(Types::ARRAY_, $exprBuilder->statements()->functionCall('range', array($expression['left'], $expression['right']))); $expression = new Expression($castBuilder->build()); $expression->setReadOnly($this->_readOnly); return $expression->compile($compilationContext); }
/** * Intercepts calls to built-in methods * * @param string $methodName * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @throws CompilerException * @return bool|\Zephir\CompiledExpression */ public function invokeMethod($methodName, $caller, CompilationContext $compilationContext, Call $call, array $expression) { /** * Checks first whether the method exist in the array type definition */ if (method_exists($this, $methodName)) { return $this->{$methodName}($caller, $compilationContext, $call, $expression); } /** * Check the method map */ if (isset($this->methodMap[$methodName])) { $paramNumber = $this->getNumberParam($methodName); if ($paramNumber == 0) { if (isset($expression['parameters'])) { $parameters = $expression['parameters']; array_unshift($parameters, array('parameter' => $caller)); } else { $parameters = array(array('parameter' => $caller)); } } else { if (isset($expression['parameters'])) { $parameters = array(); foreach ($expression['parameters'] as $number => $parameter) { if ($number == $paramNumber) { $parameters[] = null; } $parameters[] = $parameter; } $parameters[$paramNumber] = array('parameter' => $caller); } else { $parameters = array(array('parameter' => $caller)); } } $functionCall = BuilderFactory::getInstance()->statements()->functionCall($this->methodMap[$methodName], $parameters)->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); } throw new CompilerException(sprintf('Method "%s" is not a built-in method of type "%s"', $methodName, $this->getTypeName()), $expression); }
/** * @param CompilationContext $compilationContext * @throws CompilerException */ public function compile(CompilationContext $compilationContext) { $statement = $this->_statement; if (!isset($statement['data-type'])) { throw new CompilerException("Data type is required", $this->_statement); } $typeInference = $compilationContext->typeInference; $symbolTable = $compilationContext->symbolTable; foreach ($statement['variables'] as $variable) { $varName = $variable['variable']; if ($symbolTable->hasVariableInBranch($varName, $compilationContext->branchManager->getCurrentBranch())) { throw new CompilerException("Variable '" . $varName . "' is already defined", $variable); } $currentType = $statement['data-type']; /** * Replace original data type by the pre-processed infered type */ if ($typeInference) { if ($currentType == 'variable') { $type = $typeInference->getInferedType($varName); if (is_string($type)) { $currentType = $type; } } } switch ($currentType) { case 'variable': case 'array-access': case 'property-access': case 'static-property-access': case 'fcall': case 'mcall': case 'scall': $currentType = 'variable'; break; } /** * Variables are added to the symbol table */ if (isset($variable['expr'])) { $symbolVariable = $symbolTable->addVariable($currentType, $varName, $compilationContext, $variable['expr']); } else { $symbolVariable = $symbolTable->addVariable($currentType, $varName, $compilationContext); } $varName = $symbolVariable->getName(); /** * Set the node where the variable is declared */ $symbolVariable->setOriginal($variable); $symbolVariable->setIsInitialized(true, $compilationContext); //$symbolVariable->increaseMutates(); if ($currentType == 'variable') { $symbolVariable->setMustInitNull(true); $symbolVariable->setLocalOnly(false); } //$symbolVariable->increaseVariantIfNull(); if (isset($variable['expr'])) { $builder = BuilderFactory::getInstance(); $letBuilder = $builder->statements()->let(array($builder->operators()->assignVariable($varName, $builder->raw($variable['expr'])))); $letStatement = new LetStatement($letBuilder->build()); $letStatement->compile($compilationContext); } else { $symbolVariable->enableDefaultAutoInitValue(); } } }
/** * @return $this|ExpressionLetStatement */ protected function getLetStatement() { $exprBuilder = BuilderFactory::getInstance(); if ($this->isStatic()) { $className = '\\' . $this->classDefinition->getCompleteName(); $expr = $exprBuilder->raw($this->original['default']); return $exprBuilder->statements()->let(array($exprBuilder->operators()->assignStaticProperty($className, $this->name, $expr)->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char']))); } $lsb = $exprBuilder->statements()->let(array($exprBuilder->operators()->assignProperty('this', $this->name, $exprBuilder->raw($this->original['default']))->setFile($this->original['default']['file'])->setLine($this->original['default']['line'])->setChar($this->original['default']['char']))); return $exprBuilder->statements()->ifX()->setCondition($exprBuilder->operators()->binary(BinaryOperator::OPERATOR_EQUALS, $exprBuilder->operators()->binary(BinaryOperator::OPERATOR_ACCESS_PROPERTY, $exprBuilder->variable('this'), $exprBuilder->literal(Types::STRING, $this->name)), $exprBuilder->literal(Types::NULL_)))->setStatements($exprBuilder->statements()->block(array($lsb))); }
/** * Transforms calls to method "join" to function calls to "join" * * @param object $caller * @param CompilationContext $compilationContext * @param Call $call * @param array $expression * @return bool|\Zephir\CompiledExpression */ public function join($caller, CompilationContext $compilationContext, Call $call, array $expression) { $functionCall = BuilderFactory::getInstance()->statements()->functionCall('join', $expression['parameters'])->addArgument($caller)->setFile($expression['file'])->setLine($expression['line'])->setChar($expression['char']); $expression = new Expression($functionCall->build()); return $expression->compile($compilationContext); }
/** * Compiles a for statement that use a 'range' as expression * * @param array $exprRaw * @param \CompilationContext $compilationContext * @return boolean */ public function compileRange($exprRaw, CompilationContext $compilationContext) { if (!count($exprRaw['parameters'])) { return false; } if (count($exprRaw['parameters']) > 3) { return false; } $functionCall = new FunctionCall(); $parameters = $functionCall->getResolvedParamsAsExpr($exprRaw['parameters'], $compilationContext, $exprRaw); if (count($parameters) != 2 && count($parameters) != 3) { throw new CompilerException("Wrong number of parameters", $this->_statement['expr']); } if ($parameters[0]->getType() != 'variable') { if (!$parameters[0]->isIntCompatibleType()) { return false; } } if ($parameters[1]->getType() != 'variable') { if (!$parameters[1]->isIntCompatibleType()) { return false; } } $codePrinter = $compilationContext->codePrinter; $exprBuilder = BuilderFactory::getInstance(); /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { /** * This variable is used to check if the loop is in its first iteration */ $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('long', $compilationContext, $this->_statement); $keyVariable->increaseUses(); } /** * This variable is used to check if the loop is in its first iteration */ $flagVariable = $compilationContext->symbolTable->getTempVariableForWrite('bool', $compilationContext, $this->_statement); if ($parameters[0]->getType() != 'variable') { $tempVariable = $compilationContext->symbolTable->addTemp($parameters[0]->getType(), $compilationContext); } else { $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[0]->getCode(), $compilationContext, $this->_statement['expr']); $tempVariable = $compilationContext->symbolTable->addTemp($rangeVariable->getType(), $compilationContext); } /** * Create a copy of the current value in the end of the range to avoid modify the range * inside the cycle */ if ($parameters[1]->getType() != 'variable') { $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($parameters[1]->getType(), $compilationContext); } else { $rangeVariable = $compilationContext->symbolTable->getVariableForRead($parameters[1]->getCode(), $compilationContext, $this->_statement['expr']); $upperBoundVariable = $compilationContext->symbolTable->getTempVariable($rangeVariable->getType(), $compilationContext); } /** * Create an implicit 'let' operation to set the current value in the upper bound of the range */ $builderLet = $exprBuilder->statements()->let(); $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($upperBoundVariable->getName(), $exprBuilder->literal($parameters[1]->getType(), $parameters[1]->getCode())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))); $statement = new LetStatement($builderLet->build()); $statement->compile($compilationContext); if ($this->_statement['reverse']) { /** * Create an implicit 'let' operation for the initialize expression */ $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($tempVariable->getName(), $exprBuilder->variable($upperBoundVariable->getName())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))); $statement = new LetStatement($builderLet->build()); } else { /** * Create an implicit 'let' operation for the initialize expression */ $builderLet->setAssignments(array($exprBuilder->operators()->assignVariable($tempVariable->getName(), $exprBuilder->literal($parameters[0]->getType(), $parameters[0]->getCode())->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))->setFile($this->_statement['file'])->setLine($this->_statement['line'])->setChar($this->_statement['char']))); $statement = new LetStatement($builderLet->build()); } $statement->compile($compilationContext); /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { $codePrinter->output($keyVariable->getName() . ' = 0;'); } $codePrinter->output($flagVariable->getName() . ' = 0;'); if ($this->_statement['reverse']) { $conditionExpr = array('type' => 'greater-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => $parameters[0]->getType(), 'value' => $parameters[0]->getCode())); } else { $conditionExpr = array('type' => 'less-equal', 'left' => array('type' => 'variable', 'value' => $tempVariable->getName()), 'right' => array('type' => 'variable', 'value' => $upperBoundVariable->getName())); } $expr = new EvalExpression(); $condition = $expr->optimize($conditionExpr, $compilationContext); $codePrinter->output('if (' . $condition . ') {'); $codePrinter->increaseLevel(); /** * Inside a cycle */ $compilationContext->insideCycle++; $codePrinter->output('while (1) {'); $codePrinter->increaseLevel(); $codePrinter->output('if (' . $flagVariable->getName() . ') {'); $codePrinter->increaseLevel(); if (isset($this->_statement['key'])) { $codePrinter->output($keyVariable->getName() . '++;'); } if ($this->_statement['reverse']) { if (!isset($parameters[2])) { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'decr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } else { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'sub-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } } else { if (!isset($parameters[2])) { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'incr', 'variable' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } else { $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'add-assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $parameters[2]->getType(), 'value' => $parameters[2]->getCode(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); } } $statement->compile($compilationContext); /** * Multi-line conditions would need to be regenerated here */ $condition = $expr->optimize($conditionExpr, $compilationContext); $codePrinter->output('if (!(' . $condition . ')) {'); $codePrinter->output("\t" . "break;"); $codePrinter->output('}'); $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->output("\t" . $flagVariable->getName() . ' = 1;'); $codePrinter->output('}'); /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { /** * Check for anonymous variables */ if ($this->_statement['key'] != '_') { $keyVariableName = $this->_statement['key']; } else { $keyVariableName = $keyVariable->getName(); } /** * Create an implicit 'let' operation, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $keyVariableName, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $keyVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); $statement->compile($compilationContext); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { /** * Check for anonymous variables */ if ($this->_statement['value'] != '_') { $valueVariable = $this->_statement['value']; } else { $valueVariable = $tempVariable->getName(); } /** * Create an implicit 'let' operation, @TODO use a builder */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'variable' => $valueVariable, 'operator' => 'assign', 'expr' => array('type' => 'variable', 'value' => $tempVariable->getName(), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char']), 'file' => $this->_statement['file'], 'line' => $this->_statement['line'], 'char' => $this->_statement['char'])))); $statement->compile($compilationContext); } $codePrinter->decreaseLevel(); /** * 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); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); $codePrinter->decreaseLevel(); $codePrinter->output('}'); }