Arkitektur
Phasis er en trævandrer med fire trin og en opportunistisk bytecode-VM. Datavejen er:
JS text
│
▼
┌──────────┐
│ Lexer │ src/Lexer/
└────┬─────┘
▼ tokens
┌──────────┐
│ Parser │ src/Parser/
└────┬─────┘
▼ AST nodes
┌──────────────┐
│ Interpreter │ src/Runtime/
│ + Bytecode │ src/Bytecode/
│ VM │
└────┬─────────┘
▼ JsValue
PHP host
Hvert trin er en klasse af typen PHP med en lille grænseflade, som det næste trin bruger. Der er intet linktrin — parsing producerer et AST, som fortolkeren gennemløber direkte.
Lexer
src/Lexer/Lexer.php konverterer kildetekst til en strøm af forekomster af Token. Hvert token er en skrivebeskyttet struct: type (TokenType-enum, ~80 varianter), værdi, linje, kolonne.
Lexeren håndterer:
- Tal-litteraler — decimal, hex (
0x), oktal (0o), binær (0b), eksponentialform og numeriske separatorer. - Strenglitteraler — enkelt- og dobbeltanførselstegn, alle escape-sekvenser samt skabelonlitteraler med indlejrede udtryk.
- Regulære udtrykslitteraler — fuld
/pattern/flags-tokenisering med kendskab til, hvornår et/skal fortolkes som regex eller division. - Identifikatorer, nøgleord, tegnsætning.
- Kommentarer (linje- og blokkommentarer), bevaret som trivia.
- Unicode-kilde — UTF-8-bytes fortolket som den samme UTF-16-kilde som JS.
Outputtet er en generator, der udleverer tokens efter behov. Parseren bruger dem med ét tokens lookahead.
Parser
src/Parser/Parser.php er en håndskrevet Pratt-parser. Udtryksprioriteten findes i src/Parser/Precedence.php. Sætninger håndteres med deres egen rekursive nedstigning.
Den producerer uforanderlige AST-noder fra src/Ast/. Hver node indeholder sin kildeplacering til stack traces og fejlrapportering.
Bemærkelsesværdige former:
- Udtryk:
Literal,Identifier,BinaryExpression,UnaryExpression,AssignmentExpression,CallExpression,MemberExpression,ArrowFunction,ObjectExpression,ArrayExpression,ConditionalExpression,TemplateLiteral,SpreadElement,NewExpression,ThisExpression,SequenceExpression,TaggedTemplate,ClassExpression,YieldExpression,AwaitExpression. - Sætninger:
BlockStatement,IfStatement,ForStatement,ForInStatement,ForOfStatement,WhileStatement,DoWhileStatement,SwitchStatement,ReturnStatement,ThrowStatement,TryStatement,BreakStatement,ContinueStatement,ExpressionStatement. - Deklarationer:
VariableDeclaration,FunctionDeclaration,ClassDeclaration,ImportDeclaration. - Mønstre (destructuring):
ArrayPattern,ObjectPattern,RestElement,AssignmentPattern.
Automatisk indsættelse af semikolon implementeres i parseren (ikke lexeren) i henhold til specifikationen.
Fortolker
src/Runtime/Interpreter.php gennemløber AST'et. Den besøger hver node og returnerer en JsValue (eller en Completion for sætninger, der ændrer kontrolflowet).
Vigtige støtteklasser:
Environment(src/Runtime/Environment.php) — leksikal scope-kæde. Hvert funktionskald opretter et nyt miljø, der peger på sin leksikale forælder.varhoistes til funktionsscopet;let/consthar blokscope med en TDZ.CallStack(src/Runtime/CallStack.php) — eksplicit stack med dybdegrænse. Funktions- og metodekald skubber frames på stacken; undtagelser ruller dem tilbage.Completion(src/Runtime/Completion.php) — diskrimineret union afnormal,return,throw,break,continuesamt en valgfri værdi og et label.Reference(src/Runtime/Reference.php) — specifikationens Reference-type til evaluering af venstre side i tildelinger ogdelete.
Værdier
JavaScript-værdier er PHP-objekter under src/Value/. Hver implementerer grænsefladen JsValue og har sine egne typekonverteringsmetoder (toNumber(), toString(), toBoolean()).
| Klasse | JS type |
|---|---|
JsUndefined |
undefined (singleton) |
JsNull |
null (singleton) |
JsBoolean |
true / false |
JsNumber |
IEEE 754 double; bevarer NaN, ±Infinity, ±0 |
JsString |
UTF-16-strengsemantik |
JsObject |
basisobjekt med egenskabskort og prototypekæde |
JsArray |
udvider JsObject med længdesporing |
JsFunction |
closure med indfanget Environment |
JsSymbol |
Symbol-primitiv |
JsBigInt |
vilkårlig præcision via bcmath |
JsProxy |
alle 13 traps, kan tilbagekaldes |
JsPromise |
synkron executor + microtask-kø |
JsGenerator |
dobbelt tilstand: framesnapshot (bytecodekompilerede bodies; se Bytecode VM § Generators med framesnapshot) med PHP Fiber-fallback til yield*, try-with-yield og async generators |
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry |
samlinger |
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray |
binære data |
Talhåndtering er den største historiske kilde til fejl: NaN !== NaN skal være sand, -0 === 0 skal være sand, og kanttilfælde for ToNumber kræver præcision på bitniveau. Klassen Spec/TypeConversion implementerer specifikationsalgoritmerne ordret og er udtømmende testet.
Indbygget bibliotek
src/BuiltIn/ leverer alle standardbiblioteksobjekter: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console samt Web Platform-/Fetch-pakkerne (URL, Headers, Request, Response, Blob, FormData, fulde Streams, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest og event loop (setTimeout, setInterval, queueMicrotask, AsyncContext).
Hver indbygget funktion er en statisk install()-metode på en klasse. Engine kalder dem i afhængighedsrækkefølge under oprettelsen. Hver enkelt definerer sin prototypekæde, installerer metoder som JsFunction-instanser pakket omkring PHP-closures og eksponerer konstruktøren som en global.
Bro mellem PHP↔JS
Værdikonvertering på tværs af grænsen foregår to steder:
Phasis\Interop\PhpToJs::convert()— PHP-værdi tilJsValue, brugt afsetGlobal(),call()-argumenter og returværdier fra værtsfunktioner. En kaldbar PHP bliver til enJsFunction, hvis body kalder den.PhpToJs::toPhp()—JsValuetil PHP for argumenterne til en værtsfunktion.Engine::toPhp()(privat) —JsValuetil PHP for resultaterne afeval()ogcall().
Der findes ingen objektbro: PHP-objekter, der ikke kan kaldes, konverteres til undefined (issue #18). Klassen JsToPhp i src/Bytecode/ er ikke relateret til interoperabilitet; det er transpilereren, der beskrives nedenfor. Konverteringsreglerne er dokumenteret under Værdikonvertering.
Fejlhåndtering
En ikke-fanget JS throw bliver til en PHP Phasis\Exceptions\JsThrowable; fejl, som motoren selv frembringer, vises som Phasis\Exceptions\TypeError, RangeError eller ReferenceError, alle underklasser af Phasis\Exceptions\RuntimeError. Inde i motoren fanger en JS try / catch JsThrowable, enhver RuntimeError og Phasis\Exceptions\SyntaxError. Andre PHP-undtagelser, herunder dem, der udløses af værtsfunktioner, konverteres ikke og går direkte videre til PHP-kalderen (issue #20).
Der findes to grænser: grænsen for kaldestakken (CallStack::maxDepth, 4,096 frames samt en vagt på 8,192 frames for transpilerede closures) og maxLoopIterations, der indstilles via Engine::setLimit(), og som kun håndhæves af træfortolkeren (issue #19). Begge udløser Phasis\Exceptions\InternalError. Der er ingen grænser for strenglængde, outputstørrelse eller køretid.
Bytecode-VM
Varme funktioner sænkes på to måder. JsToPhp (i src/Bytecode/) udsender en PHP-closure for bodies med mange tal eller lokale variabler, som PHPs tracing-JIT kan kompilere til native kode; pipelinen sænker læsning af new Ctor(args), obj.method(args) og this.member, hvilket er nok til, at det typiske mønster med en klassemetode i en løkke helt omgår VM'en. Alt andet kompileres til flad bytecode, som VM::execute bruger, med en brugerdefineret kaldestak, der holder alle JS-kald inden for én enkelt PHP-frame og en inline-cache pr. PC til læsning af prototypemetoder. Enkle generator-bodies kompileres også til bytecode, hvor Op::YIELD indfanger et framesnapshot på heap'en i stedet for at passere gennem PHP Fiber.
Alle former, som compileren ikke sænker (e.g. with, try med en catch med ny binding, yield*, async generators), falder tilbage til træfortolkeren. Se Bytecode VM for hele mekanismen.