Skip to content

Commit

Permalink
Callstack collection and dump & positions for invokable
Browse files Browse the repository at this point in the history
  • Loading branch information
tuqqu committed Jun 19, 2024
1 parent 56763b3 commit 65542c3
Show file tree
Hide file tree
Showing 35 changed files with 361 additions and 137 deletions.
10 changes: 6 additions & 4 deletions .php-cs-fixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
];

config->setRules(rules);
config->setFinder(PhpCsFixer\Finder::create()
->in(__DIR__ . '/bin')
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests')
config->setRiskyAllowed(true);
config->setFinder(
PhpCsFixer\Finder::create()
->in(__DIR__ . '/bin')
->in(__DIR__ . '/src')
->in(__DIR__ . '/tests'),
);

return config;
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ php main.php

## WIP

This is a toy project, currently work-in-progress.
This is a toy project, not intended for production use.

To see what is already implemented, refer to [tests](tests/Functional/files/).

Expand All @@ -61,3 +61,7 @@ make test
```

run `make help` for more commands.

## Differences from the Go compiler

- No support for real goroutines, go statements run sequentially
29 changes: 22 additions & 7 deletions bin/go-php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
declare(strict_types=1);

use GoPhp\EnvVarSet;
use GoPhp\ErrorHandler\OutputToStream;
use GoPhp\ErrorHandler\StreamWriter;
use GoPhp\ExitCode;
use GoPhp\Interpreter;
use GoPhp\Stream\ResourceOutputStream;

use const GoPhp\VERSION;

use function GoPhp\Debug\dump_call_stack;

const AUTOLOAD_PATHS = [
__DIR__ . '/../vendor/autoload.php',
__DIR__ . '/../../../autoload.php',
Expand Down Expand Up @@ -55,6 +58,7 @@ function print_help(): void
-e, --eval Evaluate given code
--goroot Set GOROOT environment variable
--gopath Set GOPATH environment variable
--dump-stack-trace Dump stack trace on error
HELP, VERSION));
}
Expand All @@ -71,6 +75,7 @@ enum Flag: string
case Eval = 'eval';
case Goroot = 'goroot';
case Gopath = 'gopath';
case DumpStackTrace = 'dump-stack-trace';

private const array FLAGS_WITH_VALUE = [
self::Goroot,
Expand All @@ -81,6 +86,7 @@ enum Flag: string
self::Help,
self::Version,
self::Eval,
self::DumpStackTrace,
];

/**
Expand Down Expand Up @@ -139,6 +145,7 @@ function main(array $argv): never
$gopath = EnvVarSet::DEFAULT_GOPATH;
$eval = false;
$script = null;
$dumpTrace = false;

foreach ($argv as $arg) {
if ($eval) {
Expand Down Expand Up @@ -166,6 +173,9 @@ function main(array $argv): never
case Flag::Eval:
$eval = true;
break;
case Flag::DumpStackTrace:
$dumpTrace = true;
break;
default:
$file = $arg;
break 2;
Expand All @@ -177,25 +187,30 @@ function main(array $argv): never
exit(0);
}

$src = $script !== null
? $script
: file_get_contents($file);

$stderr = new ResourceOutputStream(STDERR);
$errorHandler = new OutputToStream($stderr);
$errorHandler = new StreamWriter($stderr);

$runtime = Interpreter::create(
source: $src,
source: $script !== null ? $script : null,
errorHandler: $errorHandler,
envVars: new EnvVarSet(
goroot: $goroot,
gopath: $gopath,
),
filename: $file,
toplevel: $script !== null,
debug: $dumpTrace,
);

$result = $runtime->run();

if ($dumpTrace && $result->exitCode === ExitCode::Failure) {
$callStack = $result->debugger?->getCallStack();

fwrite(STDERR, "\n");
dump_call_stack($callStack);
}

exit($result->exitCode->value);
}

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"tuqqu/go-parser": "^0.5.1"
},
"require-dev": {
"symfony/var-dumper": "^6",
"symfony/var-dumper": "^7",
"friendsofphp/php-cs-fixer": "^3.48",
"phpunit/phpunit": "^10.5",
"vimeo/psalm": "^5.20"
Expand All @@ -29,6 +29,7 @@
"files": [
"src/asserts.php",
"src/utils.php",
"src/Debug/utils.php",
"src/GoValue/utils.php"
]
},
Expand Down
2 changes: 1 addition & 1 deletion examples/helloworld/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
$result = $interp->run();

print "Output:\n$stdout\n";
print "Exit code: $result->exitCode->value\n";
print "Exit code: {$result->exitCode->value}\n";
2 changes: 1 addition & 1 deletion examples/sorting/main.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
$result = $interp->run();

print "Output:\n$stdout\n";
print "Exit code: $result->exitCode->value\n";
print "Exit code: {$result->exitCode->value}\n";
6 changes: 3 additions & 3 deletions src/Builtin/BuiltinFunc/Recover.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ public function __construct(
public function __invoke(Argv $argv): InterfaceValue
{
assert_argc($this, $argv, 0);
$lastPanic = $this->panicPointer->pointsTo();

if ($this->panicPointer->panic !== null) {
$lastPanic = $this->panicPointer->panic;
$this->panicPointer->panic = null;
if ($lastPanic !== null) {
$this->panicPointer->clear();

return new InterfaceValue($lastPanic->panicValue);
}
Expand Down
44 changes: 0 additions & 44 deletions src/CallStackCollectorDebugger.php

This file was deleted.

49 changes: 49 additions & 0 deletions src/Debug/CallStack.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?php

declare(strict_types=1);

namespace GoPhp\Debug;

use function array_pop;
use function array_reverse;
use function array_shift;
use function count;

final class CallStack
{
/**
* @var list<CallTrace>
*/
private array $callTraces = [];

public function __construct(
private readonly int $limit,
) {}

public function add(CallTrace $callTrace): void
{
if (count($this->callTraces) > $this->limit) {
array_shift($this->callTraces);
}

$this->callTraces[] = $callTrace;
}

public function pop(): void
{
array_pop($this->callTraces);
}

/**
* @return list<CallTrace>
*/
public function getCallTraces(): array
{
return array_reverse($this->callTraces);
}

public function count(): int
{
return count($this->callTraces);
}
}
43 changes: 43 additions & 0 deletions src/Debug/CallStackCollectorDebugger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<?php

declare(strict_types=1);

namespace GoPhp\Debug;

use GoPhp\Error\InternalError;
use GoPhp\GoValue\AddressableValue;
use GoPhp\GoValue\BuiltinFuncValue;
use GoPhp\InvokableCall;

final class CallStackCollectorDebugger implements Debugger
{
public const int DEFAULT_STACK_TRACE_DEPTH = 128;

private readonly CallStack $callStack;

public function __construct(int $maxTraceDepth = self::DEFAULT_STACK_TRACE_DEPTH)
{
$this->callStack = new CallStack($maxTraceDepth);
}

public function addStackTrace(InvokableCall $call): void
{
$name = match (true) {
$call->func instanceof AddressableValue => $call->func->getQualifiedName(),
$call->func instanceof BuiltinFuncValue => $call->func->getName(),
default => throw new InternalError(sprintf('unknown call stack value %s', $call->func::class)),
};

$this->callStack->add(new CallTrace($name, $call->getPosition()));
}

public function releaseLastStackTrace(): void
{
$this->callStack->pop();
}

public function getCallStack(): CallStack
{
return $this->callStack;
}
}
15 changes: 15 additions & 0 deletions src/Debug/CallTrace.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

namespace GoPhp\Debug;

use GoParser\Lexer\Position;

final class CallTrace
{
public function __construct(
public readonly string $name,
public readonly ?Position $position,
) {}
}
16 changes: 16 additions & 0 deletions src/Debug/Debugger.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace GoPhp\Debug;

use GoPhp\InvokableCall;

interface Debugger
{
public function addStackTrace(InvokableCall $call): void;

public function releaseLastStackTrace(): void;

public function getCallStack(): CallStack;
}
44 changes: 44 additions & 0 deletions src/Debug/utils.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace GoPhp\Debug;

use GoParser\Lexer\Position;

use function realpath;
use function sprintf;

use const STDERR;

/**
* Dumps the call stack to STDERR.
*/
function dump_call_stack(CallStack $callStack): void
{
$positionDumper = fn(?Position $position): string => $position === null
? 'unknown'
: sprintf(
"%s:%d:%d",
$position->filename === null
? ''
: realpath($position->filename),
$position->line,
$position->offset,
);

$callTraces = $callStack->getCallTraces();
$length = $callStack->count();

for ($i = 0; $i < $length; $i++) {
if ($i === 0) {
continue;
}

fwrite(STDERR, sprintf(
"%s(...)\n\t%s\n",
$callTraces[$i]->name,
$positionDumper($callTraces[$i - 1]->position),
));
}
}
Loading

0 comments on commit 65542c3

Please sign in to comment.