Arhitektuur
Phasis on neljaastmeline puu läbiv käitur, millel on oportunistlik baitkoodiga virtuaalmasin. Andmete liikumine:
JS text
│
▼
┌──────────┐
│ Lexer │ src/Lexer/
└────┬─────┘
▼ tokens
┌──────────┐
│ Parser │ src/Parser/
└────┬─────┘
▼ AST nodes
┌──────────────┐
│ Interpreter │ src/Runtime/
│ + Bytecode │ src/Bytecode/
│ VM │
└────┬─────────┘
▼ JsValue
PHP host
Iga etapp on PHP klass väikese liidesepinnaga, mida kasutab järgmine etapp. Linkimisetappi pole – parsingu tulemus on AST, mida interpretaator läbib otse.
Leksikaalanalüsaator
src/Lexer/Lexer.php teisendab lähtekoodi tokenite vooguks, mis koosneb Token eksemplaridest. Iga token on kirjutuskaitstud struktuur: tüüp (TokenType enum, umbes 80 varianti), väärtus, rida ja veerg.
Leksikaalanalüsaator käsitleb järgmist:
- Arvuliteraalid – kümnend-, kuueteistkümnend- (
0x), kaheksand- (0o), kahend- (0b) ja astmikkirjapilt ning arvude eraldajad. - Stringiliteraalid – ühe- ja kahekordsed jutumärgid, kõik paojadad ning malliliteraalid pesastatud avaldistega.
- Regulaaravaldiste literaalid – täielik
/pattern/flagstokeniseerimine, arvestades, kas/tähistab regulaaravaldist või jagamist. - Tähised, võtmesõnad ja kirjavahemärgid.
- Kommentaarid (rea- ja plokikommentaarid), mis säilitatakse trivia osana.
- Unicode lähtekood – UTF-8 baite tõlgendatakse JS UTF-16 lähtekoodina.
Väljund on generaator, mis väljastab tokeneid laisalt. Parser tarbib neid ühe tokeni võrra ette vaadates.
Parser
src/Parser/Parser.php on käsitsi kirjutatud Pratti parser. Avaldiste prioriteedid on määratud src/Parser/Precedence.php-s. Lausetel on oma rekursiivne laskumine.
See loob src/Ast/ põhjal muutmatud AST-sõlmed. Iga sõlm sisaldab oma lähtekohta pinu jälgede ja vigadest teatamise jaoks.
Märkimisväärsed vormid:
- Avaldis:
Literal,Identifier,BinaryExpression,UnaryExpression,AssignmentExpression,CallExpression,MemberExpression,ArrowFunction,ObjectExpression,ArrayExpression,ConditionalExpression,TemplateLiteral,SpreadElement,NewExpression,ThisExpression,SequenceExpression,TaggedTemplate,ClassExpression,YieldExpression,AwaitExpression. - Lause:
BlockStatement,IfStatement,ForStatement,ForInStatement,ForOfStatement,WhileStatement,DoWhileStatement,SwitchStatement,ReturnStatement,ThrowStatement,TryStatement,BreakStatement,ContinueStatement,ExpressionStatement. - Deklaratsioon:
VariableDeclaration,FunctionDeclaration,ClassDeclaration,ImportDeclaration. - Muster (destruktureerimine):
ArrayPattern,ObjectPattern,RestElement,AssignmentPattern.
Automaatne semikooloni lisamine on spetsifikatsiooni järgi rakendatud parseris (mitte leksikaalanalüsaatoris).
Interpretaator
src/Runtime/Interpreter.php läbib AST-d. See külastab iga sõlme ja tagastab JsValue (või Completion juhtvoogu muutvate lausete puhul).
Peamised abiklassid:
Environment(src/Runtime/Environment.php) – leksikaalsete ulatuste ahel. Iga funktsioonikutse loob uue keskkonna, mis viitab oma leksikaalsele vanemale.vartõstetakse funktsiooni ulatusse;let/conston plokiulatuses ja TDZ-ga.CallStack(src/Runtime/CallStack.php) – selgesõnaline pinu koos sügavuspiiranguga. Funktsiooni- ja meetodikutsed lisavad pinuraame; erandid eemaldavad need.Completion(src/Runtime/Completion.php) – eristatud ühend tüübiganormal,return,throw,break,continuening valikulise väärtuse ja sildiga.Reference(src/Runtime/Reference.php) – spetsifikatsiooni Reference-tüüp omistamiste jadeletevasaku poole hindamiseks.
Väärtused
JavaScript väärtused on src/Value/ all olevad PHP objektid. Igaüks neist rakendab liidest JsValue ja sisaldab oma tüübiteisenduse meetodeid (toNumber(), toString(), toBoolean()).
| Klass | JS tüüp |
|---|---|
JsUndefined |
undefined (ainikeksemplar) |
JsNull |
null (ainikeksemplar) |
JsBoolean |
true / false |
JsNumber |
IEEE 754 topel täpsus; säilitab NaN-i, ±Infinity ja ±0 |
JsString |
UTF-16 stringi semantika |
JsObject |
põhiobjekt omaduste kaardi ja prototüübiahelaga |
JsArray |
laiendab JsObject-t, jälgides pikkust |
JsFunction |
sulund, milles on hõivatud Environment |
JsSymbol |
primitiivne Symbol-väärtus |
JsBigInt |
täppisarvutus bcmath abil |
JsProxy |
kõik 13 lõksu; tühistatav |
JsPromise |
sünkroonne täitur + mikroülesannete järjekord |
JsGenerator |
kaherežiimiline: kaadri hetktõmmis (baitkoodiks kompileeritud kehad; vt Baitkoodi VM § Kaadri hetktõmmise generaatorid) koos PHP Fiberi varulahendusega yield*, try-with-yieldi ja asünkroonsete generaatorite jaoks |
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry |
kogumid |
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray |
binaarandmed |
Arvude käsitlemine on ajalooliselt olnud suurim vigade allikas: NaN !== NaN peab kehtima, -0 === 0 peab kehtima ning ToNumberi erijuhtumid nõuavad täpsust bititasemel. Klass Spec/TypeConversion rakendab spetsifikatsiooni algoritme sõna-sõnalt ja seda on ammendavalt testitud.
Sisseehitatud teek
src/BuiltIn/ pakub kõiki standardteegi objekte: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console, lisaks Web Platformi / Fetchi paketid (URL, Headers, Request, Response, Blob, FormData, täielik Streams, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest ja sündmuste tsükli (setTimeout, setInterval, queueMicrotask, AsyncContext).
Iga sisseehitatud objekt on klassi staatiline install()-meetod. Engine kutsub need konstruktsiooni käigus sõltuvuste järjekorras välja. Igaüks määratleb oma prototüübiahela, paigaldab meetodid kui JsFunction eksemplarid, mis on ümbritsetud PHP sulunditega, ning avalikustab konstruktori globaalse objektina.
PHP↔JS sild
Väärtuste teisendamine üle piiri toimub kahes kohas:
Phasis\Interop\PhpToJs::convert()– PHP väärtusestJsValue-ks; seda kasutataksesetGlobal(),call()argumentide ja hostifunktsiooni tagastusväärtuste puhul. PHP-ga väljakutsutavast objektist saabJsFunction, mille sisu seda välja kutsub.PhpToJs::toPhp()–JsValue-st PHP-ks hostifunktsiooni argumentide jaoks.Engine::toPhp()(privaatne) – tulemuste jaoksJsValue-st PHP-ks:eval()jacall().
Objektisilda pole: mitte väljakutsutavad PHP objektid teisendatakse väärtuseks undefined (probleem #18). Klass JsToPhp failis src/Bytecode/ pole koostalitlusvõimega seotud; see on allpool kirjeldatud transpiiler. Teisendusreeglid on kirjas jaotises Väärtuste teisendamine.
Vigade käsitlemine
Püüdmata JS throw muutub objektiks PHP Phasis\Exceptions\JsThrowable; mootori enda tekitatud vead kuvatakse kujul Phasis\Exceptions\TypeError, RangeError või ReferenceError, mis kõik on klassi Phasis\Exceptions\RuntimeError alamklassid. Mootori sees püüab JS try / catch kinni JsThrowable, kõik RuntimeError ja Phasis\Exceptions\SyntaxError. Muid PHP erandeid, sealhulgas hostifunktsioonide tekitatuid, ei teisendata ning need levivad otse PHP kutsujani (probleem #20).
Piiranguid on kaks: pinu ülempiir (CallStack::maxDepth, 4,096 raami, lisaks 8,192 raami piirang transpiilitud sulunditele) ja maxLoopIterations, mille väärtus määratakse parameetriga Engine::setLimit() ja mille jõustamist kontrollib ainult puu läbiv käitur (probleem #19). Mõlemad tekitavad Phasis\Exceptions\InternalError. Stringi pikkusele, väljundi suurusele ega käitusajale piiranguid pole.
Baitkoodi VM
Sageli käivitatavad funktsioonid kompileeritakse kahel viisil. JsToPhp (asukohas src/Bytecode/) väljastab PHP sulundi arvudest ja lokaalm muutujatest sõltuvatele funktsioonikehadele, mille PHP jälgiv JIT-kompilaator saab masinkoodiks kompileerida; töötlusahel teisendab new Ctor(args), obj.method(args) ja this.member lugemised, mis võimaldab tüüpilisel klassimeetodi-tsüklis mustril virtuaalmasinast täielikult mööda minna. Kõik muu kompileeritakse lamendatud baitkoodiks, mida käitab VM::execute; sellel on kohandatud väljakutsete pinu, mis hoiab kõik JS väljakutsed ühes PHP raamis, ning prototüübimeetodite lugemiste jaoks on olemas PC-põhine vahemälu. Ka lihtsad generaatorikehad kompileeritakse baitkoodiks; Op::YIELD salvestab hunnikusse pinuraami hetktõmmise, selle asemel et läbida PHP Fiberit.
Iga kompilaatori poolt toetamata struktuur (e.g, with, try ümberseotud püüdmisplokiga, yield*, asünkroonsed generaatorid) suunatakse tagasi puu läbivale käiturile. Kogu mehhanismi kohta vt Baitkoodi VM.