diff options
| author | nsfisis <nsfisis@gmail.com> | 2024-07-11 02:57:23 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2024-07-11 02:57:23 +0900 |
| commit | 26f49b7e27076e689541b9e13a1b54f60a4ee5c2 (patch) | |
| tree | a3762813c34a384f21ddeed630ddf333a1cc1b05 /src/WebAssembly/Execution/Stack.php | |
| parent | 326273f20c2d7dfe3d866eb720d1bb914570e3a3 (diff) | |
| download | php-waddiwasi-26f49b7e27076e689541b9e13a1b54f60a4ee5c2.tar.gz php-waddiwasi-26f49b7e27076e689541b9e13a1b54f60a4ee5c2.tar.zst php-waddiwasi-26f49b7e27076e689541b9e13a1b54f60a4ee5c2.zip | |
feat: organize namespaces
Diffstat (limited to 'src/WebAssembly/Execution/Stack.php')
| -rw-r--r-- | src/WebAssembly/Execution/Stack.php | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/WebAssembly/Execution/Stack.php b/src/WebAssembly/Execution/Stack.php new file mode 100644 index 0000000..4181dcd --- /dev/null +++ b/src/WebAssembly/Execution/Stack.php @@ -0,0 +1,212 @@ +<?php + +declare(strict_types=1); + +namespace Nsfisis\Waddiwasi\WebAssembly\Execution; + +use Nsfisis\Waddiwasi\WebAssembly\Structure\Types\RefType; +use function assert; +use function count; +use function is_float; +use function is_int; +use function is_null; + +final class Stack +{ + /** + * @var list<Frame> + */ + private array $frames = []; + + private ?Frame $currentFrame = null; + + /** + * @var list<int|float|Ref|Frame|Label> + */ + private array $entries; + + public function __construct() + { + } + + public function pushFrame(Frame $frame): void + { + if ($this->getCallStackLimit() <= count($this->frames)) { + throw new StackOverflowException(); + } + $this->push($frame); + $this->frames[] = $frame; + $this->currentFrame = $frame; + } + + public function pushLabel(Label $label): void + { + $this->push($label); + } + + public function pushValue(int|float|Ref $val): void + { + $this->push($val); + } + + public function pushBool(bool $value): void + { + $this->pushValue((int)$value); + } + + public function pushRefNull(RefType $type): void + { + $this->pushValue(Ref::RefNull($type)); + } + + public function pushRefFunc(int $addr): void + { + $this->pushValue(Ref::RefFunc($addr)); + } + + public function pushRefExtern(int $addr): void + { + $this->pushValue(Ref::RefExtern($addr)); + } + + public function clear(): void + { + $this->frames = []; + $this->currentFrame = null; + $this->entries = []; + } + + public function popFrame(): Frame + { + $result = $this->pop(); + assert($result instanceof Frame); + array_pop($this->frames); + if (count($this->frames) === 0) { + $this->currentFrame = null; + } else { + $this->currentFrame = end($this->frames); + } + return $result; + } + + public function popValue(): int|float|Ref + { + $result = $this->pop(); + assert( + is_int($result) || is_float($result) || $result instanceof Ref, + 'Expected a value on the stack, but got ' . print_r($result, true), + ); + return $result; + } + + /** + * @return list<int|float|Ref> + */ + public function popNValues(int $n): array + { + $results = []; + for ($i = 0; $i < $n; $i++) { + $results[] = $this->popValue(); + } + return $results; + } + + public function popInt(): int + { + $v = $this->popValue(); + assert(is_int($v), "Expected an int on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + /** + * @return F32 + */ + public function popFloat(): float + { + $v = $this->popValue(); + assert(is_float($v), "Expected a float on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + public function popRef(): Ref + { + $v = $this->popValue(); + assert($v instanceof Ref, "Expected a Ref on top of the stack, but got " . self::getValueTypeName($v)); + return $v; + } + + /** + * @return list<int|float|Ref> + */ + public function popValuesToLabel(): array + { + $results = []; + while (!$this->isEmpty()) { + $top = $this->pop(); + if ($top instanceof Label) { + break; + } else { + assert(is_int($top) || is_float($top) || $top instanceof Ref); + $results[] = $top; + } + } + return $results; + } + + public function popEntriesToCurrentFrame(): void + { + while (!$this->isEmpty() && !$this->top() instanceof Frame) { + $this->pop(); + } + $this->popFrame(); + } + + public function top(): int|float|Ref|Frame|Label|null + { + $n = array_key_last($this->entries); + return $n === null ? null : $this->entries[$n]; + } + + public function count(): int + { + return count($this->entries); + } + + public function isEmpty(): bool + { + return $this->count() === 0; + } + + public function currentFrame(): Frame + { + assert($this->currentFrame !== null); + return $this->currentFrame; + } + + public function getCallStackLimit(): int + { + return 1024; + } + + private function push(int|float|Ref|Frame|Label $entry): void + { + $this->entries[] = $entry; + } + + private function pop(): int|float|Ref|Frame|Label|null + { + return array_pop($this->entries); + } + + private static function getValueTypeName(int|float|Ref|Frame|Label|null $value): string + { + return match (true) { + is_null($value) => 'null', + is_int($value) => 'int', + is_float($value) => 'float', + $value instanceof Ref => 'Ref', + $value instanceof Frame => 'Frame', + $value instanceof Label => 'Label', + }; + } +} |
