Arsitektur
Phasis adalah tree walker empat tahap dengan VM bytecode oportunistis. Alur datanya:
JS text
│
▼
┌──────────┐
│ Lexer │ src/Lexer/
└────┬─────┘
▼ tokens
┌──────────┐
│ Parser │ src/Parser/
└────┬─────┘
▼ AST nodes
┌──────────────┐
│ Interpreter │ src/Runtime/
│ + Bytecode │ src/Bytecode/
│ VM │
└────┬─────────┘
▼ JsValue
PHP host
Setiap tahap adalah kelas PHP dengan antarmuka kecil yang digunakan tahap berikutnya. Tidak ada tahap penautan — parsing menghasilkan AST yang langsung ditelusuri interpreter.
Lexer
src/Lexer/Lexer.php mengubah teks sumber menjadi aliran instans Token. Setiap token adalah struct readonly: tipe (enum TokenType, sekitar 80 kasus), nilai, baris, kolom.
Lexer menangani:
- Literal angka — desimal, heksadesimal (
0x), oktal (0o), biner (0b), eksponensial, pemisah numerik. - Literal string — tanda kutip tunggal dan ganda, semua urutan escape, literal template dengan ekspresi bersarang.
- Literal ekspresi reguler — tokenisasi
/pattern/flagspenuh dengan mengetahui kapan/harus dibaca sebagai regex atau pembagian. - Identifier, kata kunci, tanda baca.
- Komentar (baris dan blok), dipertahankan sebagai trivia.
- Sumber Unicode — byte UTF-8 ditafsirkan sebagai sumber UTF-16 JS.
Output-nya berupa generator yang menghasilkan token secara malas. Parser mengonsumsinya dengan lookahead satu token.
Parser
src/Parser/Parser.php adalah parser Pratt yang ditulis manual. Prioritas ekspresi berada di src/Parser/Precedence.php. Pernyataan menggunakan recursive descent tersendiri.
Parser menghasilkan node AST immutable dari src/Ast/. Setiap node menyimpan lokasi sumbernya untuk stack trace dan pelaporan error.
Bentuk penting:
- Ekspresi:
Literal,Identifier,BinaryExpression,UnaryExpression,AssignmentExpression,CallExpression,MemberExpression,ArrowFunction,ObjectExpression,ArrayExpression,ConditionalExpression,TemplateLiteral,SpreadElement,NewExpression,ThisExpression,SequenceExpression,TaggedTemplate,ClassExpression,YieldExpression,AwaitExpression. - Pernyataan:
BlockStatement,IfStatement,ForStatement,ForInStatement,ForOfStatement,WhileStatement,DoWhileStatement,SwitchStatement,ReturnStatement,ThrowStatement,TryStatement,BreakStatement,ContinueStatement,ExpressionStatement. - Deklarasi:
VariableDeclaration,FunctionDeclaration,ClassDeclaration,ImportDeclaration. - Pola (destructuring):
ArrayPattern,ObjectPattern,RestElement,AssignmentPattern.
Penyisipan titik koma otomatis diterapkan di parser (bukan lexer) sesuai spesifikasi.
Interpreter
src/Runtime/Interpreter.php adalah penelusur AST. Ia mengunjungi setiap node dan mengembalikan JsValue (atau Completion untuk pernyataan yang mengubah alur kontrol).
Kelas pendukung utama:
Environment(src/Runtime/Environment.php) — rantai cakupan leksikal. Setiap pemanggilan fungsi membuat lingkungan baru yang menunjuk ke induk leksikalnya.vardiangkat ke cakupan fungsi;let/constmemiliki cakupan blok dengan TDZ.CallStack(src/Runtime/CallStack.php) — stack eksplisit dengan batas kedalaman. Pemanggilan fungsi dan metode menambahkan frame; exception mengurainya.Completion(src/Runtime/Completion.php) — union bertag darinormal,return,throw,break,continuebeserta nilai dan label opsional.Reference(src/Runtime/Reference.php) — tipe Reference spesifikasi untuk evaluasi sisi kiri dalam assignment dandelete.
Nilai
Nilai JavaScript adalah objek PHP di bawah src/Value/. Masing-masing menerapkan antarmuka JsValue dan memiliki metode konversi tipenya sendiri (toNumber(), toString(), toBoolean()).
| Kelas | Tipe JS |
|---|---|
JsUndefined |
undefined (singleton) |
JsNull |
null (singleton) |
JsBoolean |
true / false |
JsNumber |
IEEE 754 double; mempertahankan NaN, ±Infinity, ±0 |
JsString |
semantik string UTF-16 |
JsObject |
objek dasar dengan peta properti dan rantai prototipe |
JsArray |
memperluas JsObject dengan pelacakan panjang |
JsFunction |
closure dengan Environment yang tertangkap |
JsSymbol |
primitif Symbol |
JsBigInt |
presisi arbitrer melalui bcmath |
JsProxy |
semua 13 trap, dapat dicabut |
JsPromise |
executor sinkron + antrean microtask |
JsGenerator |
dua mode: frame-snapshot (badan terkompilasi bytecode; lihat VM Bytecode § Generator frame-snapshot) dengan fallback Fiber PHP untuk yield*, try-with-yield, dan generator asinkron |
JsMap, JsSet, JsWeakMap, JsWeakSet, JsWeakRef, JsFinalizationRegistry |
koleksi |
JsArrayBuffer, JsSharedArrayBuffer, JsDataView, JsTypedArray |
data biner |
Penanganan angka adalah sumber terbesar bug historis: NaN !== NaN harus bernilai true, -0 === 0 harus bernilai true, dan kasus khusus ToNumber memerlukan ketelitian hingga tingkat bit. Kelas Spec/TypeConversion menerapkan algoritme spesifikasi secara verbatim dan telah diuji secara menyeluruh.
Pustaka bawaan
src/BuiltIn/ menyediakan semua objek pustaka standar: Array, String, Object, Math, JSON, Date, RegExp, Map, Set, Promise, Proxy, Reflect, Symbol, BigInt, TypedArray, Temporal, Intl, console, serta paket Web Platform / Fetch (URL, Headers, Request, Response, Blob, FormData, Streams lengkap, EventTarget, AbortController/AbortSignal, DOMException, structuredClone, performance, TextEncoder/TextDecoder, atob/btoa), crypto + SubtleCrypto, WebSocket, XMLHttpRequest, dan event loop (setTimeout, setInterval, queueMicrotask, AsyncContext).
Setiap bawaan adalah metode statis install() pada sebuah kelas. Engine memanggil semuanya sesuai urutan dependensi saat konstruksi. Masing-masing mendefinisikan rantai prototipe, memasang metode sebagai instans JsFunction yang dibungkus dalam closure PHP, dan mengekspos konstruktor sebagai global.
Jembatan PHP↔JS
Konversi nilai lintas batas berlangsung di dua tempat:
Phasis\Interop\PhpToJs::convert()— nilai PHP menjadiJsValue, digunakan untuk argumensetGlobal(),call(), dan nilai kembalian fungsi host. Callable PHP menjadiJsFunctionyang isi fungsinya memanggil callable tersebut.PhpToJs::toPhp()—JsValuemenjadi PHP untuk argumen fungsi host.Engine::toPhp()(privat) —JsValuemenjadi PHP untuk hasileval()dancall().
Tidak ada jembatan objek: objek PHP yang bukan callable dikonversi menjadi undefined (masalah #18). Kelas JsToPhp di src/Bytecode/ tidak terkait dengan interoperabilitas; kelas itu adalah transpiler yang dijelaskan di bawah. Aturan konversi didokumentasikan di Konversi nilai.
Penanganan error
JS throw yang tidak tertangkap menjadi PHP Phasis\Exceptions\JsThrowable; error yang dimunculkan engine sendiri muncul sebagai Phasis\Exceptions\TypeError, RangeError, atau ReferenceError, yang semuanya merupakan subclass dari Phasis\Exceptions\RuntimeError. Di dalam engine, JS try / catch menangkap JsThrowable, setiap RuntimeError, dan Phasis\Exceptions\SyntaxError. Exception PHP lainnya, termasuk yang dilempar oleh fungsi host, tidak dikonversi dan langsung diteruskan ke pemanggil PHP (masalah #20).
Ada dua batas: batas maksimum call stack (CallStack::maxDepth, 4,096 frame, ditambah penjaga 8,192 frame untuk closure yang ditranspilasi) dan maxLoopIterations, yang ditetapkan melalui Engine::setLimit() dan hanya diberlakukan oleh tree-walker (masalah #19). Keduanya memunculkan Phasis\Exceptions\InternalError. Tidak ada batas panjang string, ukuran output, atau waktu eksekusi.
VM Bytecode
Fungsi yang sering dipanggil diturunkan dengan dua cara. JsToPhp (di src/Bytecode/) menghasilkan closure PHP untuk isi fungsi yang berat pada numerik / variabel lokal dan dapat dikompilasi menjadi native oleh tracing JIT milik PHP; pipeline ini menurunkan pembacaan new Ctor(args), obj.method(args), dan this.member, sehingga pola kanonis metode kelas di dalam loop dapat sepenuhnya melewati VM. Sisanya dikompilasi menjadi bytecode datar yang dijalankan oleh VM::execute, dengan call stack khusus yang menjaga setiap pemanggilan JS di dalam satu frame PHP serta cache inline per-PC untuk pembacaan metode prototipe. Isi generator sederhana juga dikompilasi menjadi bytecode, dengan Op::YIELD yang menangkap snapshot frame di heap alih-alih melintasi Fiber PHP.
Bentuk apa pun yang tidak diturunkan oleh kompiler (e.g. with, try dengan catch yang mengikat ulang, yield*, generator asinkron) kembali menggunakan tree-walker. Lihat VM Bytecode untuk mekanisme selengkapnya.