/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $codePrinter = $compilationContext->codePrinter; $codePrinter->output('do {'); /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Compile statements in the 'while' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } $compilationContext->codePrinter->increaseLevel(); $expr = new EvalExpression(); $condition = $expr->optimize($exprRaw, $compilationContext); $compilationContext->codePrinter->decreaseLevel(); /** * Restore the cycle counter */ $compilationContext->insideCycle--; /** * Compound conditions can be evaluated in a single line of the C-code */ $codePrinter->output('} while (' . $condition . ');'); }
/** * @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 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(); 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); } $codePrinter->output('ZEPHIR_CPY_WRT(' . $variable->getName() . ', EG(exception));'); /** * @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 = new IfStatementBuilder(new BinaryOperatorBuilder('instanceof', new VariableBuilder($variable->getName()), new VariableBuilder($class['value'])), new StatementsBlockBuilder(array_merge(array(array('type' => 'cblock', 'value' => 'zend_clear_exception(TSRMLS_C);')), isset($catch['statements']) ? $catch['statements'] : array()), true)); $ifStatement = new IfStatement($ifCheck->get()); $ifStatement->compile($compilationContext); } if ($variable->isTemporal()) { $variable->setIdle(true); } } $codePrinter->decreaseLevel(); $codePrinter->output('}'); } else { $codePrinter->output('zend_clear_exception(TSRMLS_C);'); } }
/** * 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('}'); }
/** * Compiles traversing of hash values * - Evaluated expression must be a zval * - Every key must be a zval * - Every value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, $compilationContext, $exprVariable) { $codePrinter = $compilationContext->codePrinter; /** * Initialize 'key' variable */ if (isset($this->_statement['key'])) { $keyVariable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['key'], $compilationContext, $this->_statement['expr']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { $variable = $compilationContext->symbolTable->getVariableForWrite($this->_statement['value'], $compilationContext, $this->_statement['expr']); if ($variable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['value'] . " type: " . $variable->getType() . " as value in hash traversal", $this->_statement['expr']); } $variable->setMustInitNull(true); $variable->setIsInitialized(true, $compilationContext, $this->_statement); $variable->setDynamicTypes('undefined'); } /** * Variables are initialized in a different way inside cycle */ $compilationContext->insideCycle++; /** * Create a hash table and hash pointer temporary variables */ $arrayPointer = $compilationContext->symbolTable->addTemp('HashPosition', $compilationContext); $arrayHash = $compilationContext->symbolTable->addTemp('HashTable', $compilationContext); /** * Create a temporary zval to fetch the items from the hash */ $tempVariable = $compilationContext->symbolTable->addTemp('variable', $compilationContext); $tempVariable->setIsDoublePointer(true); $compilationContext->headersManager->add('kernel/hash'); /** * We have to check if hashes are modified within the for's block */ $duplicateHash = '0'; if (isset($this->_statement['statements'])) { $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } } $codePrinter->output('zephir_is_iterable(' . $expression->getCode() . ', &' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ', ' . $duplicateHash . ', ' . $this->_statement['reverse'] . ', "' . Compiler::getShortUserPath($this->_statement['file']) . '", ' . $this->_statement['line'] . ');'); $codePrinter->output('for ('); $codePrinter->output(' ; zephir_hash_get_current_data_ex(' . $arrayHash->getName() . ', (void**) &' . $tempVariable->getName() . ', &' . $arrayPointer->getName() . ') == SUCCESS'); if ($this->_statement['reverse']) { $codePrinter->output(' ; zephir_hash_move_backwards_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } else { $codePrinter->output(' ; zephir_hash_move_forward_ex(' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ')'); } $codePrinter->output(') {'); if (isset($this->_statement['key'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HMKEY(' . $this->_statement['key'] . ', ' . $arrayHash->getName() . ', ' . $arrayPointer->getName() . ');'); } if (isset($this->_statement['value'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HVALUE(' . $this->_statement['value'] . ', ' . $tempVariable->getName() . ');'); } /** * Compile statements in the 'for' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->compile($compilationContext); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); }
/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $codePrinter = $compilationContext->codePrinter; $compilationContext->insideSwitch++; $exprEval = new Expression($exprRaw); $exprEval->setReadOnly(true); $resolvedExpr = $exprEval->compile($compilationContext); if (isset($this->_statement['clauses'])) { $evalExpr = new EvalExpression(); $codePrinter->output('do {'); $compilationContext->codePrinter->increaseLevel(); if ($resolvedExpr->getType() != 'variable') { /** * Create a temporary variable */ $tempVariable = $compilationContext->symbolTable->getTempVariable($resolvedExpr->getType(), $compilationContext); /** * Simulate an assignment to the temporary variable */ $statement = new LetStatement(array('type' => 'let', 'assignments' => array(array('assign-type' => 'variable', 'operator' => 'assign', 'variable' => $tempVariable->getName(), 'expr' => array('type' => $resolvedExpr->getType(), 'value' => $resolvedExpr->getCode(), 'file' => $exprRaw['file'], 'line' => $exprRaw['line'], 'char' => $exprRaw['char']), 'file' => $exprRaw['file'], 'line' => $exprRaw['line'], 'char' => $exprRaw['char'])))); $statement->compile($compilationContext); } else { $tempVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExpr->getCode(), $compilationContext, $exprRaw); } $clauses = $this->normalizeClauses($this->_statement['clauses']); $tempLeft = array('type' => 'variable', 'value' => $tempVariable->getRealName()); /** * In the first round we group case clauses that have block statements * with the ones that does not have one */ $blocks = array(); $exprStack = array(); $defaultBlock = null; foreach ($clauses as $clause) { if ($clause['type'] == 'case') { $expr = array('type' => 'equals', 'left' => $tempLeft, 'right' => $clause['expr']); if (!isset($clause['statements'])) { $exprStack[] = $expr; } else { $exprStack[] = $expr; $blocks[] = array('expr' => $exprStack, 'block' => $clause['statements']); $exprStack = array(); } } else { if ($clause['type'] == 'default') { $defaultBlock = $clause['statements']; } } } /** * In the second round we generate the conditions with their blocks * grouping 'cases' without a statement block using an 'or' */ foreach ($blocks as $block) { $expressions = $block['expr']; if (count($expressions) == 1) { $condition = $evalExpr->optimize($expressions[0], $compilationContext); $codePrinter->output('if (' . $condition . ') {'); } else { $orConditions = array(); foreach ($expressions as $expression) { $orConditions[] = $evalExpr->optimize($expression, $compilationContext); } $codePrinter->output('if (' . join(' || ', $orConditions) . ') {'); } if (isset($block['block'])) { $st = new StatementsBlock($block['block']); $branch = $st->compile($compilationContext, false, Branch::TYPE_SWITCH); $branch->setRelatedStatement($this); } $codePrinter->output('}'); } $compilationContext->codePrinter->decreaseLevel(); /** * The default block is resolved at the end of the 'switch' */ if ($defaultBlock) { $st = new StatementsBlock($defaultBlock); $st->compile($compilationContext); } $compilationContext->insideSwitch--; $codePrinter->output('} while(0);'); $codePrinter->outputBlankLine(); } }
/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $expr = new EvalExpression(); $condition = $expr->optimize($exprRaw, $compilationContext); /** * This pass tries to move dynamic variable initialization out of the if/else branch */ if (isset($this->_statement['statements']) && (isset($this->_statement['else_statements']) || isset($this->_statement['elseif_statements']))) { $readDetector = new ReadDetector(); $skipVariantInit = new SkipVariantInit(); $skipVariantInit->setVariablesToSkip(0, $expr->getUsedVariables()); $skipVariantInit->pass(0, new StatementsBlock($this->_statement['statements'])); $lastBranchId = 0; if (isset($this->_statement['else_statements'])) { ++$lastBranchId; $skipVariantInit->setVariablesToSkip($lastBranchId, $expr->getUsedVariables()); $skipVariantInit->pass($lastBranchId, new StatementsBlock($this->_statement['else_statements'])); } if (isset($this->_statement['elseif_statements'])) { foreach ($this->_statement['elseif_statements'] as $key => $statement) { $this->_statement['elseif_statements'][$key]['condition'] = $expr->optimize($statement['expr'], $compilationContext); $lastBranchId++; $skipVariantInit->setVariablesToSkip($lastBranchId, $expr->getUsedVariables()); if (!isset($statement['statements'])) { continue; } $skipVariantInit->pass($lastBranchId, new StatementsBlock($statement)); } } $symbolTable = $compilationContext->symbolTable; foreach ($skipVariantInit->getVariables() as $variable) { if ($symbolTable->hasVariable($variable)) { $symbolVariable = $symbolTable->getVariable($variable); if ($symbolVariable->getType() == 'variable') { if (!$readDetector->detect($variable, $exprRaw)) { $symbolVariable->initVariant($compilationContext); $symbolVariable->skipInitVariant(2); } } } } } $compilationContext->codePrinter->output('if (' . $condition . ') {'); $this->_evalExpression = $expr; /** * Try to mark latest temporary variable used as idle */ $evalVariable = $expr->getEvalVariable(); if (is_object($evalVariable)) { if ($evalVariable->isTemporal()) { $evalVariable->setIdle(true); } } /** * Compile statements in the 'if' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $branch = $st->compile($compilationContext, $expr->isUnreachable(), Branch::TYPE_CONDITIONAL_TRUE); $branch->setRelatedStatement($this); } /** * Compile statements in the 'elseif' block */ if (isset($this->_statement['elseif_statements'])) { foreach ($this->_statement['elseif_statements'] as $key => $statement) { if (!isset($statement['statements'])) { continue; } $st = new StatementsBlock($statement['statements']); $compilationContext->codePrinter->output('} else if (' . $statement['condition'] . ') {'); $branch = $st->compile($compilationContext, $expr->isUnreachable(), Branch::TYPE_CONDITIONAL_TRUE); $branch->setRelatedStatement($this); } } /** * Compile statements in the 'else' block */ if (isset($this->_statement['else_statements'])) { $compilationContext->codePrinter->output('} else {'); $st = new StatementsBlock($this->_statement['else_statements']); $branch = $st->compile($compilationContext, $expr->isUnreachableElse(), Branch::TYPE_CONDITIONAL_FALSE); $branch->setRelatedStatement($this); } $compilationContext->codePrinter->output('}'); }