Arhitektura
Phasis je drevesni sprehajalnik s štirimi stopnjami in oportunističnim VM za bajtno kodo. Pot podatkov je:
JS text
│
▼
┌──────────┐
│ Lexer │ src/Lexer/
└────┬─────┘
▼ tokens
┌──────────┐
│ Parser │ src/Parser/
└────┬─────┘
▼ AST nodes
┌──────────────┐
│ Interpreter │ src/Runtime/
│ + Bytecode │ src/Bytecode/
│ VM │
└────┬─────────┘
▼ JsValue
PHP host
Vsaka stopnja je razred PHP z majhnim vmesnikom, ki ga uporablja naslednja stopnja. Koraka povezovanja ni — razčlenjevanje ustvari AST, po katerem interpreter hodi neposredno.
Leksikalni analizator
src/Lexer/Lexer.php pretvori izvorno besedilo v tok primerkov Token. Vsak žeton je nespremenljiva struktura: tip (naštevanje TokenType, približno 80 primerov), vrednost, vrstica, stolpec.
Leksikalni analizator obravnava:
- Številske literale — decimalne, šestnajstiške (
0x), osmiške (0o), dvojiške (0b), eksponentne in s številsko ločnico. - Nizovne literale — enojne in dvojne narekovaje, vsa ubežna zaporedja, predloge z ugnezdenimi izrazi.
- Literale regularnih izrazov — popolno tokenizacijo
/pattern/flagsz upoštevanjem, ali naj bo/regularni izraz ali deljenje. - Identifikatorje, ključne besede, ločila.
- Komentarje (vrstične in blokovne), ohranjene kot nepomembno besedilo.
- Izvorno kodo Unicode — bajti UTF-8, interpretirani kot izvorna koda JS v UTF-16.
Izhod je generator, ki žetone ustvarja sproti. Razčlenjevalnik jih porablja z vpogledom za en žeton naprej.
Razčlenjevalnik
src/Parser/Parser.php je ročno napisan razčlenjevalnik Pratt. Prednost operatorjev v izrazih je določena v src/Parser/Precedence.php. Stavki uporabljajo lastno rekurzivno spuščanje.
Ustvari nespremenljiva vozlišča AST iz src/Ast/. Vsako vozlišče vsebuje lokacijo v izvorni kodi za sledenje skladu in poročanje o napakah.
Značilne oblike:
- Izraz:
Literal,Identifier,BinaryExpression,UnaryExpression,AssignmentExpression,CallExpression,MemberExpression,ArrowFunction,ObjectExpression,ArrayExpression,ConditionalExpression,TemplateLiteral,SpreadElement,NewExpression,ThisExpression,SequenceExpression,TaggedTemplate,ClassExpression,YieldExpression,AwaitExpression. - Stavek:
BlockStatement,IfStatement,ForStatement,ForInStatement,ForOfStatement,WhileStatement,DoWhileStatement,SwitchStatement,ReturnStatement,ThrowStatement,TryStatement,BreakStatement,ContinueStatement,ExpressionStatement. - Deklaracija:
VariableDeclaration,FunctionDeclaration,ClassDeclaration,ImportDeclaration. - Vzorec (destrukturiranje):
ArrayPattern,ObjectPattern,RestElement,AssignmentPattern.
Samodejno vstavljanje podpičij je v skladu s specifikacijo implementirano v razčlenjevalniku (ne v leksikalnem analizatorju).
Interpreter
src/Runtime/Interpreter.php je sprehajalnik AST. Obišče vsako vozlišče in vrne JsValue (ali Completion za stavke, ki spremenijo potek izvajanja).
Ključni podporni razredi:
Environment(src/Runtime/Environment.php) — veriga leksikalnih obsegov. Vsak klic funkcije ustvari novo okolje, povezano z nadrejenim leksikalnim obsegom.varse dvigne na obseg funkcije;let/conststa omejena na blok in imata TDZ.CallStack(src/Runtime/CallStack.php) — eksplicitni sklad z omejitvijo globine. Klici funkcij in metod dodajo okvirje; izjeme jih odvijejo.Completion(src/Runtime/Completion.php) — označena unijanormal,return,throw,break,continuez neobvezno vrednostjo in oznako.Reference(src/Runtime/Reference.php) — referenčni tip iz specifikacije za vrednotenje leve strani pri prirejanjih indelete.
Vrednosti
Vrednosti JavaScript so objekti PHP pod src/Value/. Vsak implementira vmesnik JsValue in ima lastne metode za pretvorbo tipov (toNumber(), toString(), toBoolean()).
| Razred | Tip JS |
|---|---|
JsUndefined |
undefined (singleton) |
JsNull |
null (singleton) |
JsBoolean |
true / false |
JsNumber |
dvojna natančnost IEEE 754; ohrani NaN, ±Infinity, ±0 |
JsString |
semantika nizov UTF-16 |
JsObject |
osnovni objekt z zemljevidom lastnosti in verigo prototipov |
JsArray |
razširja JsObject s sledenjem dolžini |
JsFunction |
zaprtje z zajetim Environment |
JsSymbol |
primitivni Symbol |
JsBigInt |
poljubna natančnost prek bcmath |
JsProxy |
vseh 13 pasti, preklicljiv |
JsPromise |
sinhroni izvajalnik + čakalna vrsta mikronalog |
JsGenerator |
dvojni način: posnetek okvira (telesa, prevedena v bajtno kodo; glejte VM za bajtno kodo § Generatorji s posnetkom okvira) s povratkom na PHP Fiber za yield*, try-with-yield in asinhrone generatorje |
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry |
zbirke |
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray |
binarni podatki |
Obravnava števil je največji zgodovinski vir napak: NaN !== NaN mora biti resničen, -0 === 0 mora biti resničen, robni primeri ToNumber pa zahtevajo natančno obravnavo na ravni bitov. Razred Spec/TypeConversion dobesedno implementira algoritme iz specifikacije in je temeljito preizkušen.
Vgrajena knjižnica
src/BuiltIn/ zagotavlja vse objekte standardne knjižnice: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console ter pakete Web Platform / Fetch (URL, Headers, Request, Response, Blob, FormData, polne tokove Streams, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest in zanko dogodkov (setTimeout, setInterval, queueMicrotask, AsyncContext).
Vsaka vgrajena komponenta je statična metoda install() razreda. Engine jih med ustvarjanjem pokliče v vrstnem redu odvisnosti. Vsaka določi svojo verigo prototipov, namesti metode kot primerke JsFunction, ovite okoli zaprtij PHP, in konstruktor razkrije kot globalno vrednost.
Most PHP↔JS
Pretvorba vrednosti čez mejo poteka na dveh mestih:
Phasis\Interop\PhpToJs::convert()— vrednost PHP vJsValue; uporablja se za argumentesetGlobal()incall()ter povratne vrednosti gostiteljskih funkcij. Klicna vrednost PHP postaneJsFunction, katere telo jo pokliče.PhpToJs::toPhp()—JsValuev PHP za argumente gostiteljske funkcije.Engine::toPhp()(zasebno) —JsValuev PHP za rezultateeval()incall().
Mostu za objekte ni: objekti PHP, ki niso klicljivi, se pretvorijo v undefined (težava št. 18). Razred JsToPhp v src/Bytecode/ ni povezan z interoperabilnostjo; gre za transpiler, opisan spodaj. Pravila pretvorbe so dokumentirana v Pretvorba vrednosti.
Obravnava napak
Neujeta napaka JS throw postane PHP Phasis\Exceptions\JsThrowable; napake, ki jih sproži sam pogon, se prikažejo kot Phasis\Exceptions\TypeError, RangeError ali ReferenceError, ki so vsi podrazredi Phasis\Exceptions\RuntimeError. Znotraj pogona JS try / catch ujame JsThrowable, vse RuntimeError in Phasis\Exceptions\SyntaxError. Druge izjeme PHP, tudi tiste, ki jih sprožijo gostiteljske funkcije, se ne pretvorijo in se neposredno prenesejo klicatelju PHP (težava št. 20).
Obstajata dve omejitvi: zgornja meja klicnega sklada (CallStack::maxDepth, 4,096 okvirjev in dodatna varovalka za 8,192 okvir pri transpajliranih zaprtjih) ter maxLoopIterations, nastavljena prek Engine::setLimit(), ki jo uveljavlja le drevesni sprehajalnik (težava št. 19). Obe sprožita Phasis\Exceptions\InternalError. Omejitev dolžine nizov, velikosti izhoda ali časa izvajanja ni.
VM za bajtno kodo
Funkcije, ki se pogosto izvajajo, se pretvorijo na dva načina. JsToPhp (v src/Bytecode/) ustvari zaprtje PHP za telesa z veliko številskimi operacijami ali lokalnimi spremenljivkami, ki jih sledilni JIT v PHP lahko prevede v izvorno kodo; cevovod pretvori branje new Ctor(args), obj.method(args) in this.member, kar zadošča, da značilni vzorec metode razreda v zanki v celoti obide VM. Vse drugo se prevede v plosko bajtno kodo, ki jo porablja VM::execute; uporablja lasten klicni sklad, ki vsak klic JS ohrani znotraj enega samega okvira PHP, ter predpomnilnik za posamezni PC pri branju metod prototipov. Tudi preprosta telesa generatorjev se prevedejo v bajtno kodo; Op::YIELD zajame posnetek okvira na kopici namesto prehoda čez PHP Fiber.
Vse oblike, ki jih prevajalnik ne pretvori (e.g. with, try s ponovno vezanim catch, yield*, asinhroni generatorji), se vrnejo k drevesnemu sprehajalniku. Celoten mehanizem je opisan v razdelku VM za bajtno kodo.