/** * @param CompilationContext $compilationContext */ public function compile(CompilationContext $compilationContext) { $exprRaw = $this->_statement['expr']; $codePrinter = $compilationContext->codePrinter; /** * Compound conditions can be evaluated in a single line of the C-code */ $codePrinter->output('while (1) {'); $codePrinter->increaseLevel(); /** * Variables are initialized in a different way inside loops */ $compilationContext->insideCycle++; $expr = new EvalExpression(); $condition = $expr->optimize($exprRaw, $compilationContext); $this->_evalExpression = $expr; $codePrinter->output('if (!(' . $condition . ')) {'); $codePrinter->output("\t" . 'break;'); $codePrinter->output('}'); $codePrinter->decreaseLevel(); /** * Compile statements in the 'while' block */ if (isset($this->_statement['statements'])) { $st = new StatementsBlock($this->_statement['statements']); $st->isLoop(true); $st->compile($compilationContext); } /** * Restore the cycle counter */ $compilationContext->insideCycle--; $codePrinter->output('}'); }
/** * 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 * - A key must be a zval * - A value must be a zval * * @param array $expression * @param CompilationContext $compilationContext * @param Variable $exprVariable */ public function compileHashTraverse($expression, CompilationContext $compilationContext, Variable $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']); if ($keyVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $this->_statement['key'] . " type: " . $keyVariable->getType() . " as key in hash traversal", $this->_statement['expr']); } } else { $keyVariable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $keyVariable->setMustInitNull(true); $keyVariable->setIsInitialized(true, $compilationContext, $this->_statement); $keyVariable->setDynamicTypes('undefined'); } /** * Initialize 'value' variable */ if (isset($this->_statement['value'])) { if ($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']); } } else { $variable = $compilationContext->symbolTable->getTempVariableForWrite('variable', $compilationContext); } $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'); $duplicateHash = '0'; $duplicateKey = true; /** * We have to check if hashes are modified within the for's block */ if (isset($this->_statement['statements'])) { /** * Create the statements block here to obtain the last use line */ $st = new StatementsBlock($this->_statement['statements']); $detector = new ForValueUseDetector(); if ($detector->detect($exprVariable->getName(), $this->_statement['statements'])) { $duplicateHash = '1'; } /** * Detect if the key is modified or passed to an external scope */ if (isset($this->_statement['key'])) { if (!$keyVariable->isTemporal()) { $detector->setDetectionFlags(ForValueUseDetector::DETECT_ALL); if ($detector->detect($keyVariable->getName(), $this->_statement['statements'])) { $loopContext = $compilationContext->currentMethod->getLocalContextPass(); //echo $st->getLastLine(); //echo $loopContext->getLastVariableUseLine($keyVariable->getName()); $duplicateKey = true; } } } } $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'])) { if ($duplicateKey) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HMKEY(' . $keyVariable->getName() . ', ' . $arrayHash->getName() . ', ' . $arrayPointer->getName() . ');'); } else { $codePrinter->output("\t" . 'ZEPHIR_GET_HKEY(' . $keyVariable->getName() . ', ' . $arrayHash->getName() . ', ' . $arrayPointer->getName() . ');'); } } if (isset($this->_statement['value'])) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HVALUE(' . $variable->getName() . ', ' . $tempVariable->getName() . ');'); } /** * Compile statements in the 'for' block */ if (isset($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('}'); }