protected function compileExpr(Op\Expr $op, $indent) { $phi = ''; foreach ($op->result->usages as $usage) { if ($usage instanceof Op\Phi) { $phi .= $indent . $this->getVarName($usage->result) . " = " . $this->getVarName($op->result) . ";\n"; } } $result = ''; switch ($op->getType()) { case 'Expr_ArrayDimFetch': $var = $this->getVarName($op->var); $dim = $this->getVarName($op->dim); $result = $this->getVarName($op->result); switch ($this->mapToCType($op->var->type)) { case 'zend_string*': assert($this->mapToCType($op->dim->type) === 'zend_long'); $safety = $indent . "if ({$dim} < 0 || {$dim} >= ZSTR_LEN({$var})) {\n"; $safety .= $indent . "\t{$result} = ZSTR_EMPTY_ALLOC();\n"; $safety .= $indent . "\tzend_error(E_NOTICE, \"Uninitialized string offset: %pd\", {$dim});\n"; $safety .= $indent . "} else if (CG(one_char_string)[(unsigned char) ZSTR_VAL({$var})[{$dim}]]) {\n"; $safety .= $indent . "\t{$result} = CG(one_char_string)[(unsigned char) ZSTR_VAL({$var})[{$dim}]];\n"; $safety .= $indent . "} else {\n"; $safety .= $indent . "\t{$result} = zend_string_init(ZSTR_VAL({$var}) + {$dim}, 1, 0);\n"; $safety .= $indent . "\tfree_{$result} = 1;\n"; $safety .= $indent . "}\n{$phi}"; return $safety; case 'HashTable*': $dimType = $this->mapToCType($op->dim->type); $resultType = $this->mapToCType($op->result->type); if ($dimType === 'zend_long') { $safety = $indent . "do {\n"; $safety .= $indent . "\tzval* tmp = zend_hash_index_find({$var}, {$dim});\n"; $safety .= $indent . "\tif (tmp == NULL) {\n"; if ($resultType === 'zval') { $safety .= $indent . "\t\tZVAL_NULL(&{$result});\n"; $safety .= $indent . "\t\tzend_error(E_NOTICE, \"Uninitialized offset: %pd\", {$dim});\n"; $safety .= $indent . "\t} else {\n"; $safety .= $indent . "\t\t{$result} = *tmp;\n"; $safety .= $indent . "\t}\n"; $safety .= $indent . "} while(0);\n"; return $safety; } else { $typeInfo = $this->getTypeInfo($resultType); $safety .= $indent . "\t\t" . $typeInfo['default']($result) . ";\n"; $safety .= $indent . "\t\tzend_error(E_NOTICE, \"Uninitialized offset: %pd\", {$dim});\n"; $safety .= $indent . "\t} else if (Z_TYPE_P(tmp) != {$typeInfo['ztype']}) {\n"; $safety .= $indent . "\t\tzend_throw_error(NULL, \"Offset is not an {$typeInfo['stringtype']}: %pd\", {$dim});\n"; $safety .= $indent . "\t} else {\n"; $safety .= $indent . "\t\t{$result} = {$typeInfo['ztypefetch']}(tmp);\n"; $safety .= $indent . "\t}\n"; $safety .= $indent . "} while(0); \n"; return $safety; } } default: throw new \LogicException("Unknown array dim fetch type {$op->var->type}"); } break; case 'Expr_Assign': $result = $this->getVarName($op->var) . ' = ' . $this->getVarName($op->expr); foreach ($op->var->usages as $usage) { if ($usage instanceof Op\Phi) { $phi .= $indent . $this->getVarName($usage->result) . " = " . $this->getVarName($op->var) . ";\n"; } } break; case 'Expr_BinaryOp_BitwiseAnd': $result = $this->getVarName($op->left) . " & " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_BitwiseOr': $result = $this->getVarName($op->left) . " | " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_BitwiseXor': $result = $this->getVarName($op->left) . " ^ " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Coalesce': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_Concat': $result = $this->getVarName($op->left) . " . " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Div': $result = $this->getVarName($op->left) . " / " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Equal': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_Greater': $result = $this->getVarName($op->left) . " > " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_GreaterOrEqual': $result = $this->getVarName($op->left) . " >= " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Identical': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_LogicalXor': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_Minus': $result = $this->getVarName($op->left) . " - " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Mod': $result = $this->getVarName($op->left) . " % " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Mul': $result = $this->getVarName($op->left) . " * " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_NotEqual': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_NotIdentical': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_Plus': $result = $this->getVarName($op->left) . " + " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Pow': throw new \LogicException("TODO"); break; case 'Expr_BinaryOp_ShiftLeft': $result = $this->getVarName($op->left) . " << " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_ShiftRight': $result = $this->getVarName($op->left) . " >> " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Smaller': $result = $this->getVarName($op->left) . " < " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_SmallerOrEqual': $result = $this->getVarName($op->left) . " <= " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Spaceship': throw new \LogicException("TODO"); break; case 'Expr_Print': $result = "1;\n" . $this->compilePrintStatement($op, $indent); break; default: throw new \RuntimeException("Unknown expression found: " . $op->getType()); } foreach ($op->result->usages as $usage) { if ($usage instanceof Op\Phi) { $phi .= $indent . $this->getVarName($usage->result) . " = " . $this->getVarName($op->result) . ";\n"; } } if (count($op->result->usages) === 0) { return $indent . $result . ";\n" . $phi; } return $indent . $this->getVarName($op->result) . " = " . $result . ";\n" . $phi; }
protected function compileExpr(Op\Expr $op, $indent) { $phi = ''; $result = ''; switch ($op->getType()) { case 'Expr_ArrayDimFetch': $result = $this->getVarName($op->var) . "[" . $this->getVarName($op->dim) . "]"; break; case 'Expr_Assign': $result = $this->getVarName($op->var) . ' = ' . $this->getVarName($op->expr); foreach ($op->var->usages as $usage) { if ($usage instanceof Op\Phi) { $phi .= $indent . $this->getVarName($usage->result) . " = " . $this->getVarName($op->var) . ";\n"; } } break; case 'Expr_BinaryOp_BitwiseAnd': $result = $this->getVarName($op->left) . " & " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_BitwiseOr': $result = $this->getVarName($op->left) . " | " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_BitwiseXor': $result = $this->getVarName($op->left) . " ^ " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Coalesce': $result = $this->getVarName($op->left) . " ?? " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Concat': $result = $this->getVarName($op->left) . " . " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Div': $result = $this->getVarName($op->left) . " / " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Equal': $result = $this->getVarName($op->left) . " == " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Greater': $result = $this->getVarName($op->left) . " > " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_GreaterOrEqual': $result = $this->getVarName($op->left) . " >= " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Identical': $result = $this->getVarName($op->left) . " === " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_LogicalXor': $result = $this->getVarName($op->left) . " XOR " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Minus': $result = $this->getVarName($op->left) . " - " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Mod': $result = $this->getVarName($op->left) . " % " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Mul': $result = $this->getVarName($op->left) . " * " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_NotEqual': $result = $this->getVarName($op->left) . " != " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_NotIdentical': $result = $this->getVarName($op->left) . " !== " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Plus': $result = $this->getVarName($op->left) . " + " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Pow': $result = $this->getVarName($op->left) . " ** " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_ShiftLeft': $result = $this->getVarName($op->left) . " << " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_ShiftRight': $result = $this->getVarName($op->left) . " >> " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Smaller': $result = $this->getVarName($op->left) . " < " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_SmallerOrEqual': $result = $this->getVarName($op->left) . " <= " . $this->getVarName($op->right); break; case 'Expr_BinaryOp_Spaceship': $result = $this->getVarName($op->left) . " <=> " . $this->getVarName($op->right); break; case 'Expr_Cast_Array': $result = "(array) " . $this->getVarName($op->expr); break; case 'Expr_Cast_Bool': $result = "(bool) " . $this->getVarName($op->expr); break; case 'Expr_Cast_Double': $result = "(float) " . $this->getVarName($op->expr); break; case 'Expr_Cast_Int': $result = "(int) " . $this->getVarName($op->expr); break; case 'Expr_Cast_Object': $result = "(object) " . $this->getVarName($op->expr); break; case 'Expr_Cast_String': $result = "(string) " . $this->getVarName($op->expr); break; case 'Expr_Cast_Unset': $result = "(unset) " . $this->getVarName($op->expr); break; default: throw new \RuntimeException("Unknown expression found: " . $op->getType()); } foreach ($op->result->usages as $usage) { if ($usage instanceof Op\Phi) { $phi .= $indent . $this->getVarName($usage->result) . " = " . $this->getVarName($op->result) . ";\n"; } } if (count($op->result->usages) === 0) { return $indent . $result . ";\n" . $phi; } return $indent . $this->getVarName($op->result) . " = " . $result . ";\n" . $phi; }