/** * @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 */ 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); }
public function forStatement(Variable $exprVariable, $keyVariable, $variable, $duplicateKey, $duplicateHash, $statement, $statementBlock, CompilationContext $compilationContext) { /** * 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); $codePrinter = $compilationContext->codePrinter; $codePrinter->output('zephir_is_iterable(' . $this->getVariableCode($exprVariable) . ', ' . $duplicateHash . ', "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $macro = null; $reverse = $statement['reverse'] ? 'REVERSE_' : ''; if (isset($keyVariable)) { $arrayNumKey = $compilationContext->symbolTable->addTemp('zend_ulong', $compilationContext); $arrayStrKey = $compilationContext->symbolTable->addTemp('zend_string', $compilationContext); } if (isset($keyVariable) && isset($variable)) { $macro = 'ZEND_HASH_' . $reverse . 'FOREACH_KEY_VAL'; $codePrinter->output($macro . '(Z_ARRVAL_P(' . $this->getVariableCode($exprVariable) . '), ' . $arrayNumKey->getName() . ', ' . $arrayStrKey->getName() . ', ' . $tempVariable->getName() . ')'); } else { if (isset($keyVariable)) { $macro = 'ZEND_HASH_' . $reverse . 'FOREACH_KEY'; $codePrinter->output($macro . '(Z_ARRVAL_P(' . $this->getVariableCode($exprVariable) . '), ' . $arrayNumKey->getName() . ', ' . $arrayStrKey->getName() . ')'); } else { $macro = 'ZEND_HASH_' . $reverse . 'FOREACH_VAL'; $codePrinter->output($macro . '(Z_ARRVAL_P(' . $this->getVariableCode($exprVariable) . '), ' . $tempVariable->getName() . ')'); } } $codePrinter->output('{'); if (isset($keyVariable)) { $codePrinter->increaseLevel(); if ($duplicateKey) { $compilationContext->symbolTable->mustGrownStack(true); $keyVariable->initVariant($compilationContext); } $codePrinter->output('if (' . $arrayStrKey->getName() . ' != NULL) { '); $codePrinter->increaseLevel(); if ($duplicateKey) { $codePrinter->output('ZVAL_STR_COPY(' . $this->getVariableCode($keyVariable) . ', ' . $arrayStrKey->getName() . ');'); } else { $codePrinter->output('ZVAL_STR(' . $this->getVariableCode($keyVariable) . ', ' . $arrayStrKey->getName() . ');'); } $codePrinter->decreaseLevel(); $codePrinter->output('} else {'); $codePrinter->increaseLevel(); $codePrinter->output('ZVAL_LONG(' . $this->getVariableCode($keyVariable) . ', ' . $arrayNumKey->getName() . ');'); $codePrinter->decreaseLevel(); $codePrinter->output('}'); $codePrinter->decreaseLevel(); } if (isset($variable)) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->increaseLevel(); $variable->initVariant($compilationContext); $codePrinter->output('ZVAL_COPY(' . $this->getVariableCode($variable) . ', ' . $this->getVariableCode($tempVariable) . ');'); $codePrinter->decreaseLevel(); } /** * Compile statements in the 'for' block */ if (isset($statement['statements'])) { $statementBlock->isLoop(true); if (isset($statement['key'])) { $statementBlock->getMutateGatherer()->increaseMutations($statement['key']); } $statementBlock->getMutateGatherer()->increaseMutations($statement['value']); $statementBlock->compile($compilationContext); } $codePrinter->output('} ZEND_HASH_FOREACH_END();'); /* Since we do not observe, still do cleanup */ if (isset($variable)) { $variable->initVariant($compilationContext); } if (isset($keyVariable)) { $keyVariable->initVariant($compilationContext); } }
/** * Compiles an array initialization * * @param array $expression * @param CompilationContext $compilationContext * @return CompiledExpression */ public function compile($expression, CompilationContext $compilationContext) { /** * Resolves the symbol that expects the value */ if ($this->_expecting) { if ($this->_expectingVariable) { $symbolVariable = $this->_expectingVariable; $symbolVariable->initVariant($compilationContext); if ($symbolVariable->getType() != 'variable' && $symbolVariable->getType() != 'array') { throw new CompilerException("Cannot use variable type: " . $symbolVariable->getType() . " as an array", $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('array', $compilationContext, $expression); } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForWrite('array', $compilationContext, $expression); } /*+ * Mark the variable as an array */ $symbolVariable->setDynamicTypes('array'); $codePrinter = $compilationContext->codePrinter; /** * This calculates a prime number bigger than the current array size to possibly * reduce hash collisions when adding new members to the array */ $arrayLength = intval(count($expression['left']) * 1.25); if (!function_exists('gmp_nextprime')) { $codePrinter->output('array_init_size(' . $symbolVariable->getName() . ', ' . ($arrayLength + 1) . ');'); } else { $codePrinter->output('array_init_size(' . $symbolVariable->getName() . ', ' . gmp_strval(gmp_nextprime($arrayLength)) . ');'); } foreach ($expression['left'] as $item) { if (isset($item['key'])) { $key = null; $exprKey = new Expression($item['key']); $resolvedExprKey = $exprKey->compile($compilationContext); switch ($resolvedExprKey->getType()) { case 'string': $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('add_assoc_long_ex(' . $symbolVariable->getName() . ', SS("' . $resolvedExprKey->getCode() . '"), ' . $resolvedExpr->getCode() . ');'); break; case 'double': $codePrinter->output('add_assoc_double_ex(' . $symbolVariable->getName() . ', SS("' . $resolvedExprKey->getCode() . '"), ' . $resolvedExpr->getCode() . ');'); break; case 'bool': $compilationContext->headersManager->add('kernel/array'); if ($resolvedExpr->getCode() == 'true') { $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', SL("' . $resolvedExprKey->getCode() . '"), &ZEPHIR_GLOBAL(global_true), PH_COPY | PH_SEPARATE);'); } else { $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', SL("' . $resolvedExprKey->getCode() . '"), &ZEPHIR_GLOBAL(global_false), PH_COPY | PH_SEPARATE);'); } break; case 'string': $codePrinter->output('add_assoc_stringl_ex(' . $symbolVariable->getName() . ', SS("' . $resolvedExprKey->getCode() . '"), SL("' . $resolvedExpr->getCode() . '"), 1);'); break; case 'null': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', SL("' . $resolvedExprKey->getCode() . '"), &ZEPHIR_GLOBAL(global_null), PH_COPY | PH_SEPARATE);'); break; case 'array': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', SL("' . $resolvedExprKey->getCode() . '"), &' . $valueVariable->getName() . ', PH_COPY | PH_SEPARATE);'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; case 'variable': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', SL("' . $resolvedExprKey->getCode() . '"), &' . $valueVariable->getName() . ', PH_COPY | PH_SEPARATE);'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; default: throw new CompilerException("Invalid value type: " . $resolvedExpr->getType(), $item['value']); } break; case 'int': case 'uint': case 'long': case 'ulong': $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('add_index_long(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getCode() . ');'); break; case 'bool': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('add_index_bool(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getBooleanCode() . ');'); if ($resolvedExpr->getCode() == 'true') { $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &ZEPHIR_GLOBAL(global_true), PH_COPY, "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ');'); } else { $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &ZEPHIR_GLOBAL(global_false), PH_COPY, "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ');'); } break; case 'double': $codePrinter->output('add_index_double(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getCode() . ');'); break; case 'null': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &ZEPHIR_GLOBAL(global_null), PH_COPY, "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ');'); break; case 'string': $codePrinter->output('add_index_stringl(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', SL("' . $resolvedExpr->getCode() . '"), 1);'); break; case 'array': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &' . $valueVariable->getName() . ', PH_COPY, "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ');'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; case 'variable': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &' . $valueVariable->getName() . ', PH_COPY, "' . Compiler::getShortUserPath($expression['file']) . '", ' . $expression['line'] . ');'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; default: throw new CompilerException("Invalid value type: " . $item['value']['type'], $item['value']); } break; case 'variable': $variableVariable = $compilationContext->symbolTable->getVariableForRead($resolvedExprKey->getCode(), $compilationContext, $item['key']); switch ($variableVariable->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('add_index_double(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('add_index_bool(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getBooleanCode() . ');'); break; case 'double': $codePrinter->output('add_index_double(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', ' . $resolvedExpr->getCode() . ');'); break; case 'null': $codePrinter->output('add_index_null(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ');'); break; case 'string': $codePrinter->output('add_index_stringl(' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', SL("' . $resolvedExpr->getCode() . '"), 1);'); break; case 'variable': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_long(&' . $symbolVariable->getName() . ', ' . $resolvedExprKey->getCode() . ', &' . $valueVariable->getName() . ', PH_COPY, "' . Compiler::getShortUserPath($item['file']) . '", ' . $item['line'] . ');'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; default: throw new CompilerException("Invalid value type: " . $item['value']['type'], $item['value']); } break; case 'string': $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('add_assoc_long_ex(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . '), ' . $resolvedExpr->getCode() . ');'); break; case 'double': $codePrinter->output('add_assoc_double_ex(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . '), ' . $resolvedExpr->getCode() . ');'); break; case 'bool': $codePrinter->output('add_assoc_bool_ex(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . '), ' . $resolvedExpr->getBooleanCode() . ');'); break; case 'string': $codePrinter->output('add_assoc_stringl_ex(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . ') + 1, SL("' . $resolvedExpr->getCode() . '"), 1);'); break; case 'null': $codePrinter->output('add_assoc_null_ex(' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . ') + 1);'); break; case 'variable': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_string(&' . $symbolVariable->getName() . ', Z_STRVAL_P(' . $resolvedExprKey->getCode() . '), Z_STRLEN_P(' . $item['key']['value'] . '), &' . $valueVariable->getName() . ', PH_COPY);'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; default: throw new CompilerException("Invalid value type: " . $resolvedExpr->getType(), $item['value']); } break; case 'variable': $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); switch ($resolvedExpr->getType()) { case 'null': case 'bool': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_zval(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', &' . $valueVariable->getName() . ', PH_COPY);'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; case 'variable': $compilationContext->headersManager->add('kernel/array'); $valueVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $codePrinter->output('zephir_array_update_zval(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', &' . $valueVariable->getName() . ', PH_COPY);'); if ($valueVariable->isTemporal()) { $valueVariable->setIdle(true); } break; default: throw new CompilerException("Invalid value type: " . $item['value']['type'], $item['value']); } break; default: throw new CompilerException("Cannot use variable type: " . $variableVariable->getType() . " as array index", $item['key']); } break; default: throw new CompilerException("Invalid key type: " . $resolvedExprKey->getType(), $item['key']); } } else { $expr = new Expression($item['value']); $resolvedExpr = $expr->compile($compilationContext); $itemVariable = $this->getArrayValue($resolvedExpr, $compilationContext); $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fast_append(' . $symbolVariable->getName() . ', ' . $itemVariable->getName() . ');'); if ($itemVariable->isTemporal()) { $itemVariable->setIdle(true); } } } return new CompiledExpression('array', $symbolVariable->getRealName(), $expression); }
public function forStatement(Variable $exprVariable, $keyVariable, $variable, $duplicateKey, $duplicateHash, $statement, $statementBlock, CompilationContext $compilationContext) { /** * 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); $codePrinter = $compilationContext->codePrinter; $reverse = $statement['reverse'] ? 1 : 0; $codePrinter->output('zephir_is_iterable(' . $this->getVariableCode($exprVariable) . ', &' . $arrayHash->getName() . ', &' . $arrayPointer->getName() . ', ' . $duplicateHash . ', ' . $reverse . ', "' . Compiler::getShortUserPath($statement['file']) . '", ' . $statement['line'] . ');'); $codePrinter->output('for ('); $codePrinter->output(' ; zephir_hash_get_current_data_ex(' . $arrayHash->getName() . ', (void**) &' . $tempVariable->getName() . ', &' . $arrayPointer->getName() . ') == SUCCESS'); if ($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($keyVariable)) { 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($variable)) { $compilationContext->symbolTable->mustGrownStack(true); $codePrinter->output("\t" . 'ZEPHIR_GET_HVALUE(' . $variable->getName() . ', ' . $tempVariable->getName() . ');'); } /** * Compile statements in the 'for' block */ if (isset($statement['statements'])) { $statementBlock->isLoop(true); if (isset($statement['key'])) { $statementBlock->getMutateGatherer()->increaseMutations($statement['key']); } $statementBlock->getMutateGatherer()->increaseMutations($statement['value']); $statementBlock->compile($compilationContext); } $codePrinter->output('}'); /* Make sure to free the duplicated hash table */ if ($duplicateHash) { $codePrinter->output('zend_hash_destroy(' . $arrayHash->getName() . ');'); $codePrinter->output('FREE_HASHTABLE(' . $arrayHash->getName() . ');'); } }
/** * @param array $expression * @param Variable $variableVariable * @param CompilationContext $compilationContext */ protected function _accessDimensionArray($expression, Variable $variableVariable, CompilationContext $compilationContext) { $arrayAccess = $expression; if ($variableVariable->getType() == 'variable') { if ($variableVariable->hasAnyDynamicType('unknown')) { throw new CompilerException("Cannot use non-initialized variable as an array", $arrayAccess['left']); } /** * Trying to use a non-object dynamic variable as object */ if ($variableVariable->hasDifferentDynamicType(array('undefined', 'array', 'null'))) { $compilationContext->logger->warning('Possible attempt to access array-index on a non-array dynamic variable', 'non-array-access', $arrayAccess['left']); } } $codePrinter = $compilationContext->codePrinter; /** * Resolves the symbol that expects the value */ $readOnly = false; $symbolVariable = $this->_expectingVariable; if ($this->_readOnly) { if ($this->_expecting && $this->_expectingVariable) { /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ if ($symbolVariable->getName() != 'return_value') { $line = $compilationContext->symbolTable->getLastCallLine(); if ($line === false || $line > 0 && $line < $expression['line']) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName()); if ($numberMutations == 1) { if ($symbolVariable->getNumberMutations() == $numberMutations) { $symbolVariable->setMemoryTracked(false); $readOnly = true; } } } } /** * Variable is not read-only or it wasn't promoted */ if (!$readOnly) { if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); $this->_readOnly = false; } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression); } } } else { $symbolVariable = $compilationContext->symbolTable->getTempNonTrackedUninitializedVariable('variable', $compilationContext, $expression); } } else { if ($this->_expecting && $this->_expectingVariable) { /** * If a variable is assigned once in the method, we try to promote it * to a read only variable */ if ($symbolVariable->getName() != 'return_value') { $line = $compilationContext->symbolTable->getLastCallLine(); if ($line === false || $line > 0 && $line < $expression['line']) { $numberMutations = $compilationContext->symbolTable->getExpectedMutations($symbolVariable->getName()); if ($numberMutations == 1) { if ($symbolVariable->getNumberMutations() == $numberMutations) { $symbolVariable->setMemoryTracked(false); $readOnly = true; } } } } /** * Variable is not read-only or it wasn't promoted */ if (!$readOnly) { if ($symbolVariable->getName() != 'return_value') { $symbolVariable->observeVariant($compilationContext); $this->_readOnly = false; } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } } else { $symbolVariable = $compilationContext->symbolTable->getTempVariableForObserve('variable', $compilationContext, $expression); } } /** * Variable that receives property accesses must be polimorphic */ if ($symbolVariable->getType() != 'variable') { throw new CompilerException("Cannot use variable: " . $symbolVariable->getType() . " to assign array index", $expression); } /** * At this point, we don't know the type fetched from the index */ $symbolVariable->setDynamicTypes('undefined'); if ($this->_readOnly || $readOnly) { if ($this->_noisy) { $flags = 'PH_NOISY | PH_READONLY'; } else { $flags = 'PH_READONLY'; } } else { if ($this->_noisy) { $flags = 'PH_NOISY'; } else { $flags = '0'; } } /** * Right part of expression is the index */ $expr = new Expression($arrayAccess['right']); $exprIndex = $expr->compile($compilationContext); switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_long(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $exprIndex->getCode() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'string': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_string(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', SL("' . $exprIndex->getCode() . '"), ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $expression); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': $compilationContext->headersManager->add('kernel/array'); $codePrinter->output('zephir_array_fetch_long(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); break; case 'string': case 'variable': $compilationContext->headersManager->add('kernel/array'); if ($variableIndex->isLocalOnly()) { $codePrinter->output('zephir_array_fetch(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', &' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); } else { $codePrinter->output('zephir_array_fetch(&' . $symbolVariable->getName() . ', ' . $variableVariable->getName() . ', ' . $variableIndex->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($arrayAccess['file']) . '", ' . $arrayAccess['line'] . ' TSRMLS_CC);'); } break; default: throw new CompilerException("Variable type: " . $variableIndex->getType() . " cannot be used as array index without cast", $arrayAccess['right']); } break; default: throw new CompilerException("Cannot use expression: " . $exprIndex->getType() . " as array index without cast", $arrayAccess['right']); } return new CompiledExpression('variable', $symbolVariable->getRealName(), $expression); }
/** * 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('}'); }
/** * Compiles foo[y] = {expr} (one offset) * * @param string $variable * @param ZephirVariable $symbolVariable * @param CompiledExpression $resolvedExpr * @param CompilationContext $compilationContext * @param array $statement */ protected function _assignArrayIndexSingle($variable, ZephirVariable $symbolVariable, CompiledExpression $resolvedExpr, CompilationContext $compilationContext, $statement) { $expression = new Expression($statement['index-expr'][0]); $exprIndex = $expression->compile($compilationContext); switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': case 'string': case 'variable': break; default: throw new CompilerException("Index: " . $exprIndex->getType() . " cannot be used as array offset in assignment without cast", $statement['index-expr'][0]); } $codePrinter = $compilationContext->codePrinter; /** * Create a temporal zval (if needed) */ $symbolVariable = $this->_getResolvedArrayItem($resolvedExpr, $compilationContext); $flags = 'PH_COPY | PH_SEPARATE'; $isGlobalVariable = $compilationContext->symbolTable->isSuperGlobal($variable); $compilationContext->headersManager->add('kernel/array'); if ($isGlobalVariable) { $variableTempSeparated = $compilationContext->symbolTable->getTempLocalVariableForWrite('int', $compilationContext); $codePrinter->output($variableTempSeparated->getName() . ' = zephir_maybe_separate_zval(&' . $variable . ');'); } switch ($exprIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('zephir_array_update_long(&' . $variable . ', ' . $exprIndex->getCode() . ', &' . $symbolVariable->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($statement['index-expr'][0]['file']) . '", ' . $statement['index-expr'][0]['line'] . ');'); break; case 'string': $codePrinter->output('zephir_array_update_string(&' . $variable . ', SL("' . $exprIndex->getCode() . '"), &' . $symbolVariable->getName() . ', ' . $flags . ');'); break; case 'variable': $variableIndex = $compilationContext->symbolTable->getVariableForRead($exprIndex->getCode(), $compilationContext, $statement); $variableIndexName = ($variableIndex->isLocalOnly() ? '&' : '') . $variableIndex->getName(); switch ($variableIndex->getType()) { case 'int': case 'uint': case 'long': case 'ulong': $codePrinter->output('zephir_array_update_long(&' . $variable . ', ' . $variableIndexName . ', &' . $symbolVariable->getName() . ', ' . $flags . ', "' . Compiler::getShortUserPath($statement['index-expr'][0]['file']) . '", ' . $statement['index-expr'][0]['line'] . ');'); break; case 'string': $codePrinter->output('zephir_array_update_zval(&' . $variable . ', ' . $variableIndexName . ', &' . $symbolVariable->getName() . ', ' . $flags . ');'); break; case 'variable': $codePrinter->output('zephir_array_update_zval(&' . $variable . ', ' . $variableIndexName . ', &' . $symbolVariable->getName() . ', ' . $flags . ');'); break; default: throw new CompilerException("Variable: " . $variableIndex->getType() . " cannot be used as array index", $statement); } break; default: throw new CompilerException("Value: " . $exprIndex->getType() . " cannot be used as array index", $statement); } if ($isGlobalVariable) { $codePrinter->output('if (' . $variableTempSeparated->getName() . ') {'); $codePrinter->output("\t" . 'ZEND_SET_SYMBOL(&EG(symbol_table), "' . $variable . '", ' . $variable . ');'); $codePrinter->output('}'); } }
/** * 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); } }
/** * 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;'); }