diff --git a/main.cc b/main.cc index b298acc0..2a9bc7da 100644 --- a/main.cc +++ b/main.cc @@ -37,6 +37,9 @@ int main(int cpp_argc, char **cpp_argv) { } zend_catch { rc = EG(exit_status); + if (EG(exception)) { + zend_exception_error(EG(exception), E_ERROR); + } } zend_end_try(); #if PPROF_ON diff --git a/src/Php/Translator.php b/src/Php/Translator.php index 59059c68..5cc05d3b 100644 --- a/src/Php/Translator.php +++ b/src/Php/Translator.php @@ -91,6 +91,11 @@ class Translator extends \PhpAot\Core\Translator 'pipe', ]; + private array $unsupportedFunctions = [ + 'compact', + 'extract' + ]; + private array $nativeFunctions = []; private array $internalFunctions = []; private int $optimizeLevel = 0; @@ -1315,28 +1320,33 @@ class Translator extends \PhpAot\Core\Translator private function parseFuncCall(mixed $expr): string { - $name = $this->parseIdentifier($expr->name); - if ($this->isNativeFunction($name)) { - return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')'; - } - if ($this->isInternalFunction($name)) { - $fn = 'php::' . $name; + if ($expr->name->getType() === self::EXPR_VARIABLE) { + $fn = $this->parseIdentifier($expr->name); + $name = ''; } else { - $fn = '"' . $name . '"'; - } - if ($name === 'strlen' or $name === 'sizeof' or $name === 'count') { - return 'php::len(' . $this->parseIdentifier($expr->args[0]->value) . ')'; - } - if (count($expr->args) == 1) { - switch ($name) { - case 'intval': - return $this->convertIntExpr($this->parseExpr($expr->args[0]->value)); - case 'floatval': - return $this->convertFloatExpr($this->parseExpr($expr->args[0]->value)); - case 'boolval': - return $this->convertBoolExpr($this->parseExpr($expr->args[0]->value)); - default: - break; + $name = $this->parseIdentifier($expr->name); + if ($this->isNativeFunction($name)) { + return self::PREFIX . $name . '(' . $this->parseCallArgs($expr->args, $name) . ')'; + } + if ($this->isInternalFunction($name)) { + $fn = 'php::' . $name; + } else { + $fn = '"' . $name . '"'; + } + if ($name === 'strlen' or $name === 'sizeof' or $name === 'count') { + return 'php::len(' . $this->parseIdentifier($expr->args[0]->value) . ')'; + } + if (count($expr->args) == 1) { + switch ($name) { + case 'intval': + return $this->convertIntExpr($this->parseExpr($expr->args[0]->value)); + case 'floatval': + return $this->convertFloatExpr($this->parseExpr($expr->args[0]->value)); + case 'boolval': + return $this->convertBoolExpr($this->parseExpr($expr->args[0]->value)); + default: + break; + } } } if (empty($expr->args)) { @@ -2254,15 +2264,39 @@ class Translator extends \PhpAot\Core\Translator } } + private function identifierToStr(Node $node, bool $require = true): string + { + $id = $this->parseIdentifier($node); + if ($node->getType() === self::EXPR_VARIABLE) { + if ($require) { + $this->requireVar($node, $id); + } + return $id; + } else { + return '"'. $id . '"'; + } + } + + private function requireVar($node, string $var): void + { + if (!$this->hasVar($var)) { + $this->fatalError($node, 'The variable `' . $var . '` is not defined'); + } + } + private function parseStaticCall(mixed $expr): string { - $class = $this->parseIdentifier($expr->class); - $method = $this->parseIdentifier($expr->name); - $fn = $class . '::' . $method; + if ($expr->class->getType() === self::EXPR_VARIABLE or $expr->name->getType() === self::EXPR_VARIABLE) { + $fn = 'php::concat({' . $this->identifierToStr($expr->class). ', "::", ' . $this->identifierToStr($expr->name) . '})'; + } else { + $class = $this->parseIdentifier($expr->class); + $method = $this->parseIdentifier($expr->name); + $fn = '"'. $class . '::' . $method . '"'; + } if (empty($expr->args)) { - return 'php::call("' . $fn . '")'; + return 'php::call(' . $fn . ')'; } else { - return 'php::call("' . $fn . '", {' . $this->parseCallArgs($expr->args) . '})'; + return 'php::call(' . $fn . ', {' . $this->parseCallArgs($expr->args) . '})'; } } @@ -2320,7 +2354,7 @@ class Translator extends \PhpAot\Core\Translator } $code = $this->getIndent() . $var . ' = ' . $exVar . ';' . PHP_EOL; - $code .= $this->getIndent() . 'if ('; + $code .= $this->getIndent() . 'if (' . $var . ' && '; foreach ($types as $type) { $code .= 'php::instanceOf(' . $var . ', "' . $this->parseIdentifier($type) . '")'; } diff --git a/tests/aot/static-call.phpt b/tests/aot/static-call.phpt new file mode 100644 index 00000000..329f59ae --- /dev/null +++ b/tests/aot/static-call.phpt @@ -0,0 +1,9 @@ +--TEST-- +static calls +--FILE-- + +--EXPECTF-- diff --git a/tests/zend/dynamic_call/dynamic_call_002.phpt b/tests/zend/dynamic_call/dynamic_call_002.phpt new file mode 100644 index 00000000..e19f4754 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_002.phpt @@ -0,0 +1,15 @@ +--TEST-- +Testing dynamic call with invalid value for method name +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: %s +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/tests/zend/dynamic_call/dynamic_call_003.phpt b/tests/zend/dynamic_call/dynamic_call_003.phpt new file mode 100644 index 00000000..cda01b80 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_003.phpt @@ -0,0 +1,16 @@ +--TEST-- +Testing dynamic call with invalid method name +--FILE-- + +--EXPECTF-- +Fatal error: Uncaught Error: %s +Stack trace: +#0 {main} + thrown in %s on line %d diff --git a/tests/zend/dynamic_call/dynamic_call_004.phpt b/tests/zend/dynamic_call/dynamic_call_004.phpt new file mode 100644 index 00000000..0cb3a2b5 --- /dev/null +++ b/tests/zend/dynamic_call/dynamic_call_004.phpt @@ -0,0 +1,17 @@ +--TEST-- +Testing dynamic call with undefined variables +--SKIPIF-- + +--FILE-- + +--EXPECTF-- +Warning: Undefined variable $a in %s on line %d + +Fatal error: Uncaught Error: Class name must be a valid object or a string in %s:%d +Stack trace: +#0 {main} + thrown in %s on line %d