Arkitektur
Phasis är en fyrastegs trädvandring med en opportunistisk bytekod-VM. Dataflödet är:
JS text
│
▼
┌──────────┐
│ Lexer │ src/Lexer/
└────┬─────┘
▼ tokens
┌──────────┐
│ Parser │ src/Parser/
└────┬─────┘
▼ AST nodes
┌──────────────┐
│ Interpreter │ src/Runtime/
│ + Bytecode │ src/Bytecode/
│ VM │
└────┬─────────┘
▼ JsValue
PHP host
Varje steg är en klass i PHP med ett litet gränssnitt som nästa steg använder. Det finns inget länkningssteg – parsningen skapar en AST som interpreteraren går igenom direkt.
Lexer
src/Lexer/Lexer.php omvandlar källtext till en ström av Token-instanser. Varje token är en skrivskyddad struct: typ (TokenType-enum, ~80 fall), värde, rad, kolumn.
Lexern hanterar:
- Numeriska literaler – decimala, hexadecimala (
0x), oktala (0o), binära (0b), exponentiella samt tal med sifferavgränsare. - Strängliteraler – enkla och dubbla citattecken, alla escape-sekvenser och malliteraler med nästlade uttryck.
- Reguljära uttryck som literaler – fullständig tokenisering av
/pattern/flagsmed hänsyn till om en/ska tolkas som regex eller division. - Identifierare, nyckelord, interpunktion.
- Kommentarer (rad- och blockkommentarer), bevarade som trivia.
- Källkod i Unicode – UTF-8-byte tolkade som samma JS-källa.
Utdata är en generator som ger token efter behov. Parsern förbrukar dem med en tokens framförhållning.
Parsern
src/Parser/Parser.php är en handskriven Pratt-parser. Prioriteten för uttryck anges i src/Parser/Precedence.php. Satser använder egen rekursiv nedstigning.
Den skapar oföränderliga AST-noder från src/Ast/. Varje nod innehåller sin källposition för stackspårningar och felrapportering.
Noterbara strukturer:
- Uttryck:
Literal,Identifier,BinaryExpression,UnaryExpression,AssignmentExpression,CallExpression,MemberExpression,ArrowFunction,ObjectExpression,ArrayExpression,ConditionalExpression,TemplateLiteral,SpreadElement,NewExpression,ThisExpression,SequenceExpression,TaggedTemplate,ClassExpression,YieldExpression,AwaitExpression. - Sats:
BlockStatement,IfStatement,ForStatement,ForInStatement,ForOfStatement,WhileStatement,DoWhileStatement,SwitchStatement,ReturnStatement,ThrowStatement,TryStatement,BreakStatement,ContinueStatement,ExpressionStatement. - Deklaration:
VariableDeclaration,FunctionDeclaration,ClassDeclaration,ImportDeclaration. - Mönster (destrukturering):
ArrayPattern,ObjectPattern,RestElement,AssignmentPattern.
Automatisk infogning av semikolon implementeras i parsern (inte lexern) enligt specifikationen.
Interpreterare
src/Runtime/Interpreter.php är AST-vandraren. Den besöker varje nod och returnerar ett JsValue (eller ett Completion för satser som ändrar kontrollflödet).
Viktiga stödklasser:
Environment(src/Runtime/Environment.php) – lexikal scope-kedja. Varje funktionsanrop skapar en ny miljö som pekar på sin lexikala förälder.varhissas upp till funktionsscopet;let/constär blockscope-begränsade med en TDZ.CallStack(src/Runtime/CallStack.php) – explicit stack med djupgräns. Funktions- och metodanrop lägger till stackramar; undantag avvecklar dem.Completion(src/Runtime/Completion.php) – diskriminerad union avnormal,return,throw,break,continuesamt ett valfritt värde och en etikett.Reference(src/Runtime/Reference.php) – specifikationens Reference-typ för utvärdering av vänsterled vid tilldelningar ochdelete.
Värden
JavaScript-värden är PHP-objekt under src/Value/. Varje värde implementerar gränssnittet JsValue och har egna typkonverteringsmetoder (toNumber(), toString(), toBoolean()).
| Klass | Typen JS |
|---|---|
JsUndefined |
undefined (singleton) |
JsNull |
null (singleton) |
JsBoolean |
true / false |
JsNumber |
IEEE 754-double; bevarar NaN, ±Infinity, ±0 |
JsString |
UTF-16-strängsemantik |
JsObject |
basobjekt med egenskapskarta och prototypkedja |
JsArray |
utökar JsObject med längdspårning |
JsFunction |
closure med infångad Environment |
JsSymbol |
Symbol-primitiv |
JsBigInt |
godtycklig precision via bcmath |
JsProxy |
alla 13 fällor, kan återkallas |
JsPromise |
synkron exekverare + mikrouppgiftskö |
JsGenerator |
två lägen: ramögonblicksbild (bytekodkompilerade kroppar; se Bytekod-VM § Generatorer med ramögonblicksbild) med PHP Fiber-reservlösning för yield*, try-with-yield och asynkrona generatorer |
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry |
samlingar |
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray |
binärdata |
Talhantering är den enskilt största källan till historiska buggar: NaN !== NaN måste vara sant, -0 === 0 måste vara sant, och gränsfall för ToNumber kräver exakt hantering på bitnivå. Klassen Spec/TypeConversion implementerar specifikationens algoritmer ordagrant och är uttömmande testad.
Inbyggt bibliotek
src/BuiltIn/ tillhandahåller alla standardbiblioteksobjekt: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console, plus webbplattformens och Fetch-paketen (URL, Headers, Request, Response, Blob, FormData, fullständiga Streams, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest och händelseslingan (setTimeout, setInterval, queueMicrotask, AsyncContext).
Varje inbyggd funktion är en statisk metod install() i en klass. Engine anropar dem i beroendeordning vid konstruktionen. Varje funktion definierar sin prototypkedja, installerar metoder som JsFunction-instanser omslutna runt PHP-closures och exponerar konstruktorn som global.
Brygga mellan PHP↔JS
Värdekonvertering över gränsen sker på två ställen:
Phasis\Interop\PhpToJs::convert()– värde från PHP tillJsValue, används försetGlobal(),call()-argument och returvärden från värdfunktioner. Ett anropbart PHP blir ettJsFunctionvars kropp anropar det.PhpToJs::toPhp()– frånJsValuetill PHP för argumenten till en värdfunktion.Engine::toPhp()(privat) – frånJsValuetill PHP för resultaten fråneval()ochcall().
Det finns ingen objektbrygga: objekt från PHP som inte är anropbara konverteras till undefined (problem nr 18). Klassen JsToPhp i src/Bytecode/ har inget med interoperabilitet att göra; det är transpilern som beskrivs nedan. Konverteringsreglerna finns i Värdekonvertering.
Felhantering
Ett ouppfångat JS throw blir ett PHP Phasis\Exceptions\JsThrowable; fel som motorn själv ger upphov till visas som Phasis\Exceptions\TypeError, RangeError eller ReferenceError, som alla är underklasser till Phasis\Exceptions\RuntimeError. Inne i motorn fångar en JS try / catch JsThrowable, alla RuntimeError och Phasis\Exceptions\SyntaxError. Andra undantag från PHP, inklusive sådana som kastas av värdfunktioner, konverteras inte utan skickas direkt vidare till anroparen PHP (problem nr 20).
Det finns två gränser: taket för anropsstacken (CallStack::maxDepth, 4,096 ramar, plus en skyddsgräns på 8,192 ramar för transpilerade closures) och maxLoopIterations, som ställs in via Engine::setLimit() och bara upprätthålls av trädvandraren (problem nr 19). Båda kastar Phasis\Exceptions\InternalError. Det finns inga gränser för stränglängd, utdatas storlek eller exekveringstid.
Bytekod-VM
Hetta funktioner sänks på två sätt. JsToPhp (i src/Bytecode/) genererar en PHP-closure för kroppar med mycket numerik eller många lokala variabler, som PHPs spårnings-JIT kan kompilera till maskinkod; pipelinen sänker new Ctor(args), obj.method(args) och this.member-läsningar, vilket räcker för att det typiska mönstret med en klassmetod i en loop ska kringgå VM:n helt. Allt annat kompileras till platt bytekod som VM::execute kör, med en anpassad anropsstack som håller varje JS-anrop inom en enda PHP-ram och en cache per PC för läsningar av prototypmetoder. Enkla generatorkroppar kompileras också till bytekod, där Op::YIELD fångar en ögonblicksbild av en ram på heapen i stället för att gå via PHP Fiber.
Former som kompilatorn inte sänker (e.g. with, try med en catch som binder om, yield*, asynkrona generatorer) faller tillbaka till trädvandraren. Se Bytekod-VM för hela mekanismen.