Documentation
¶
Overview ¶
Package bytecode defines the moejs instruction set and the compiled-function template shared between the compiler and the engine's interpreter.
See doc.go for the ISA reference and opcodes.go for the encoding. The types in this file are the data model: nothing in a Function is mutated after compilation, so one *Function is shared by every realm (and every goroutine) that instantiates the same module. Per-realm state (inline caches, materialized constants) lives in engine side tables keyed by *Function.
Package bytecode: instruction set reference.
Machine model ¶
moejs runs a register machine. A function executes in a window of NumRegs registers R[0..NumRegs-1] carved out of one contiguous per-realm value stack. Arguments arrive in R[0..argc-1]; missing parameters are undefined; when HasRest is set the surplus arguments are collected into an array in R[NumParams] before any other instruction runs, and when HasArguments is set an unmapped arguments object holding every argument is created in the next register (R[NumParams], or R[NumParams+1] after a rest array). `this` is held in the frame, not in a register. Registers above the parameters hold locals (un-captured let/const/var/function bindings) and expression temporaries; the compiler allocates them stack-like so that a call block (callee, this, args) is always the highest live group of registers.
Captured bindings never live in registers: they live in closure environments (Env). env^0 is the innermost environment; env^d is its d-th parent. A function whose CaptureLayout has n > 0 slots creates its own Env of n slots at entry (with the parameter registers listed in the layout copied in); otherwise it runs directly in the environment captured by its closure. Block scopes with captured bindings push and pop nested Envs.
Every instruction is one 32-bit word: op:8 | A:8 | B:8 | C:8, or op:8 | A:8 | Bx:16 (unsigned) / sBx:16 (signed). Ops marked +X below are followed by one ExtraArg word (a raw uint32); GetEnvChkW has two. Jump offsets are relative to the pc of the following word. Constants K[i] are Function.Consts; F[i] are Function.Children.
Exceptions unwind through Function.Handlers without panic/recover: the interpreter takes the first row whose [Start, End) covers the faulting pc, pops closure environments down to StackDepth, stores the thrown value as described by Kind, and resumes at Handler. Rows without a match propagate the error to the caller's frame. Interrupts (Realm.Interrupt) are checked at function entry and at every backward Jmp and are not catchable.
Opcodes ¶
Loads and moves
LoadConst A Bx R[A] = K[Bx] (number, string or bigint constant) LoadInt A sBx R[A] = sBx (small integer) LoadUndef A R[A] = undefined LoadNull A R[A] = null LoadTrue A R[A] = true LoadFalse A R[A] = false LoadHole A R[A] = hole (the TDZ marker; never observable) LoadThis A R[A] = this (undefined at module top level; lexical in arrows) LoadCallee A R[A] = the function object being executed (named function expressions) Move A B R[A] = R[B] UndefRange A B R[A .. A+B-1] = undefined (var bindings at entry)
Closure environments
GetEnv A B C R[A] = env^B[C] GetEnvChk A B C +X R[A] = env^B[C]; ReferenceError "K[X] is not defined" if hole SetEnv A B C env^B[C] = R[A] GetEnvW A B +X R[A] = env^B[X] (slot >= 256) GetEnvChkW A B +X +X R[A] = env^B[X1] with TDZ check; X2 = name constant SetEnvW A B +X env^B[X] = R[A] PushEnv Bx env = new Env(env, Bx slots initialized to undefined) PopEnv env = env.parent CopyEnv env = copy of env with the same parent (per-iteration let bindings) CheckTDZ A +X ReferenceError "Cannot access 'K[X]' before initialization" if R[A] is hole
Globals (X = name constant:16 | inline cache:16)
GetGlobal A +X R[A] = globalThis[name]; ReferenceError if absent GetGlobalOrUndef A +X R[A] = globalThis[name] or undefined (typeof operand) SetGlobal A +X globalThis[name] = R[A]; ReferenceError if absent (strict)
Properties
GetProp A B +X R[A] = R[B].name (X = name:16 | ic:16; TypeError on null/undefined base) GetLen A B +X R[A] = R[B].length (array and string fast paths; X = ic in the high half) SetProp A B +X R[A].name = R[B] (strict PutValue) GetElem A B C R[A] = R[B][R[C]] (dense array and string index fast paths) SetElem A B C R[A][R[B]] = R[C] DelProp A B +X R[A] = delete R[B].name (strict: TypeError when not configurable) DelElem A B C R[A] = delete R[B][R[C]] In A B C R[A] = R[B] in R[C] InstanceOf A B C R[A] = R[B] instanceof R[C]
Literals
NewObject A Bx R[A] = {} (ordinary object, Bx = property count hint)
NewArray A Bx R[A] = [] (Bx = capacity hint)
DefineField A B +X CreateDataProperty(R[A], K[name], R[B]) (X = name:16 | ic:16; the IC caches the shape transition)
DefineElem A B C CreateDataProperty(R[A], ToPropertyKey(R[B]), R[C])
DefineMethod A B C DefineElem for an object-literal method with a computed key: SetFunctionName(R[C], key) first
DefineAccessor A B C +X DefinePropertyOrThrow(R[A], ToPropertyKey(R[B]), {[[Get]] or [[Set]]: R[C], [[Enumerable]], [[Configurable]]: true}),
merging with an existing accessor half; X = AccessorSetter | AccessorEnumerable | AccessorNameKey
(AccessorNameKey: SetFunctionName(R[C], key, "get"/"set") first, for computed keys)
SetProto A B if R[B] is an object or null, R[A].[[Prototype]] = R[B] (the __proto__: v literal form)
CopyDataProps A B copy own enumerable properties of R[B] into R[A] (object spread)
CopyDataPropsEx A B C same, skipping the keys listed in array R[C] (object rest pattern)
ArrayPush A B append R[B] to array R[A]
ArrayHole A append a hole to array R[A] (length += 1)
AppendSpread A B append every value produced by iterating R[B] to array R[A]
Closure A Bx R[A] = new function object over F[Bx] closing over env (and this for arrows)
NewRegExp A Bx R[A] = new RegExp(K[Bx].pattern, K[Bx].flags)
GetTemplate A +X R[A] = the template object of K[X lo] (GetTemplateObject: frozen cooked strings with a frozen raw array), created on the site's first evaluation in the realm and cached in IC slot X hi
Arithmetic (number fast paths; BigInt operands take the slow path)
Add A B C R[A] = R[B] + R[C] (numbers, string concatenation, ToPrimitive) Sub A B C R[A] = R[B] - R[C] Mul A B C R[A] = R[B] * R[C] Div A B C R[A] = R[B] / R[C] Mod A B C R[A] = R[B] % R[C] Exp A B C R[A] = R[B] ** R[C] AddImm A B C R[A] = R[B] + int8(C) (Add semantics: strings concatenate) SubImm A B C R[A] = R[B] - int8(C) Inc A B R[A] = ToNumeric(R[B]) + 1 (++; never concatenates) Dec A B R[A] = ToNumeric(R[B]) - 1 (--) Neg A B R[A] = -R[B] Plus A B R[A] = +R[B] (ToNumber) Not A B R[A] = !ToBoolean(R[B]) BitNot A B R[A] = ~R[B] ToNumeric A B R[A] = ToNumeric(R[B]) (postfix update result) RequireObjectCoercible A throw TypeError when R[A] is null or undefined (empty or rest-only destructuring pattern) ToStr A B R[A] = ToString(R[B]) (template substitutions) Concat A B C R[A] = R[B] + R[C], both already strings (rope above the threshold) Typeof A B R[A] = typeof R[B] TypeofIs A B C R[A] = typeof R[B] === TypeNames[C] BitAnd A B C R[A] = R[B] & R[C] BitOr A B C R[A] = R[B] | R[C] BitXor A B C R[A] = R[B] ^ R[C] Shl A B C R[A] = R[B] << R[C] Shr A B C R[A] = R[B] >> R[C] UShr A B C R[A] = R[B] >>> R[C]
Comparison
Eq A B C R[A] = R[B] == R[C] Ne A B C R[A] = R[B] != R[C] StrictEq A B C R[A] = R[B] === R[C] StrictNe A B C R[A] = R[B] !== R[C] Lt A B C R[A] = R[B] < R[C] Le A B C R[A] = R[B] <= R[C] Gt A B C R[A] = R[B] > R[C] Ge A B C R[A] = R[B] >= R[C]
Control flow (offsets relative to the next pc)
Jmp sBx pc += sBx; a negative offset is a loop back-edge and checks the interrupt flag JmpT A sBx if ToBoolean(R[A]) pc += sBx JmpF A sBx if !ToBoolean(R[A]) pc += sBx JmpNullish A sBx if R[A] is null or undefined pc += sBx JmpNotNullish A sBx if R[A] is neither null nor undefined pc += sBx JmpNotUndef A sBx if R[A] is not undefined pc += sBx
Calls
Call A B R[A] = R[A](this = R[A+1], args R[A+2 .. A+1+B]); TypeError if not callable
CallSpread A R[A] = R[A](this = R[A+1], ...array R[A+2])
New A B R[A] = new R[A](R[A+2 .. A+1+B]); TypeError if not a constructor
NewSpread A R[A] = new R[A](...array R[A+2])
Ret A return R[A]
RetUndef return undefined
Throw A throw R[A]
ThrowConstAssign +X throw TypeError("Assignment to constant variable.") (K[X] names the binding)
Iteration (for-of, spread and array destructuring share GetIterator and IteratorStepValue; R[A], R[A+1] hold the Iterator Record: the iterator and its next method, or, while the protocol is unobservable, the array or string itself and the next index; R[A] is undefined once done or closed)
IterInit A B R[A], R[A+1] = GetIterator(R[B]); TypeError if not iterable IterNext A sBx R[A+2] = next value; when exhausted pc += sBx IterValue A B R[B] = next value or undefined when exhausted (destructuring) IterRest A B R[B] = array of the remaining values (rest element) IterClose A sBx IteratorClose(R[A], normal completion) unless done; pc += sBx IterThrow A B IteratorClose(R[A], throw completion) unless done; throw R[B] ForInInit A B R[A] = enumerable string keys of R[B] (prototype chain, deduplicated), R[A+1] = 0, R[A+2] = the object ForInNext A sBx R[A+3] = next key still present as a property; when exhausted pc += sBx
Classes, super, new.target and private names (off the interpreter's jump table: they only run while classes are defined and in class code)
CtorEntry A R[A] = new.target; TypeError "Class constructor X cannot be invoked without 'new'" in a [[Call]] (first op of every class constructor)
LoadNewTarget A R[A] = new.target, undefined in a [[Call]] (first op of a function with NewTarget set)
LoadHome A R[A] = [[HomeObject]] of the running function
SetHome A B [[HomeObject]] of function R[A] = R[B]
CreateClass A B C finish class constructor R[A] (the Closure of a class-constructor template):
R[A+1] = the new prototype object, R[A].prototype = R[A+1] (read-only),
R[A+1].constructor = R[A] (non-enumerable), [[HomeObject]] of R[A] = R[A+1];
C&1: R[B] is the heritage (null, or a constructor whose prototype is an object or null);
C&2: first name R[A] after property key R[A+1] (an anonymous class field value)
DefineClassMethod A B C R[A][R[B]] := R[C] non-enumerable, [[HomeObject]] of R[C] = R[A];
an unnamed function is named after the key
ToPropertyKey A B R[A] = ToPropertyKey(R[B]) (computed field keys)
GetProtoOf A B R[A] = R[B].[[GetPrototypeOf]]() or null: the super base of home object R[B],
or the super constructor of active function R[B]
GetSuper A B C R[A] = super property R[C] of base R[B] read with receiver R[B+1];
TypeError if the base is null (A may equal B)
SetSuper A B C set super property R[B] of base R[A] to R[C] with receiver R[A+1]; TypeError if refused
SuperCall A B R[A] = Construct(R[A], args R[A+2 .. A+1+B], new.target R[A+1]); TypeError if not a constructor
SuperCallSpread A same with the argument array R[A+2] (AppendSpread-built, or the default constructor's rest array)
CheckSuper A ReferenceError "Super constructor may only be called once" unless R[A] is hole
DerivedResult A B R[A] = the value a derived constructor returns for return value R[A] and raw `this` R[B]:
an object as is; undefined gives `this` (ReferenceError if still hole); otherwise TypeError
NewPrivateName A Bx R[A] = a new private name described by K[Bx] ("#x", or the class name for a brand)
SetPrivateMethod A B C +X make R[B] the method, getter or setter of private name R[A], checked against
the brand R[C] (or, with PrivateStatic, the class constructor R[C]); X = Private* flags
GetPrivate A B C R[A] = R[B].#R[C]; TypeError when R[B] lacks it
SetPrivate A B C R[A].#R[B] = R[C]; TypeError when R[A] lacks it or it is a method
DefPrivate A B C add private field #R[B] = R[C] to object R[A]; TypeError if already present
AddBrand A B add class brand R[B] to object R[A]; TypeError if already present
InPrivate A B C R[A] = #R[B] in R[C]; TypeError if R[C] is not an object
ThrowError A Bx throw a new TypeError (A=ThrowTypeError) or ReferenceError (A=ThrowReferenceError) with message K[Bx]
Index ¶
- Constants
- Variables
- func DecodeA(w uint32) uint8
- func DecodeB(w uint32) uint8
- func DecodeBx(w uint32) uint16
- func DecodeC(w uint32) uint8
- func DecodeSBx(w uint32) int16
- func Disassemble(fn *Function) string
- func EncodeABC(op Op, a, b, c uint8) uint32
- func EncodeABx(op Op, a uint8, bx uint16) uint32
- func EncodeAsBx(op Op, a uint8, sbx int16) uint32
- func ExtraArg(lo, hi uint16) uint32
- func ExtraHi(x uint32) uint16
- func ExtraLo(x uint32) uint16
- func TypeNameIndex(name string) (uint8, bool)
- type CaptureLayout
- type Const
- type ConstKind
- type Format
- type Function
- type GlobalNames
- type Handler
- type HandlerKind
- type Kind
- type LineEntry
- type Op
- type SourceInfo
Constants ¶
const ( TypeUndefined uint8 = iota TypeObject TypeBoolean TypeNumber TypeString TypeSymbol TypeBigInt TypeFunction )
TypeName indices used by TypeofIs (operand C).
const ( AccessorSetter uint32 = 1 << iota // define the setter half (otherwise the getter) AccessorEnumerable // the property is enumerable AccessorNameKey // name the function "get <key>"/"set <key>" at run time (computed keys) )
DefineAccessor flags (its ExtraArg word). Object literals set AccessorEnumerable; class accessors do not.
const ( PrivateGetter uint32 = 1 << iota // the function is the getter of an accessor PrivateSetter // the function is the setter of an accessor PrivateStatic // checked against the class constructor instead of a brand )
SetPrivateMethod flags (its ExtraArg word).
const ( ThrowTypeError uint8 = iota ThrowReferenceError )
ThrowError kinds (operand A).
const ( MaxRegister = 255 // registers are 8-bit operands MaxBx = 0xFFFF // unsigned 16-bit operand MaxSBx = 0x7FFF MinSBx = -0x8000 )
Instruction word layout constants.
const NoRegister uint16 = 0xFFFF
NoRegister marks a CaptureLayout slot that is not initialized from a parameter register.
const OpCount = int(opCount)
OpCount is the number of defined opcodes.
const UndefinedCooked = "\xff"
UndefinedCooked is the Const.Cooked entry of a tagged template chunk with an invalid escape, whose cooked value is undefined. The byte 0xFF never occurs in WTF-8.
Variables ¶
var DescribeCallee func(f *Function, pc uint32) string
DescribeCallee, when non-nil, returns the source form of the callee of the Call, CallSpread, New or NewSpread instruction at pc of f ("obj.foo", "f(...)"), or "" when it cannot tell. Package compiler installs it; the engine calls it only to word the TypeError of a call or construction of a value that is not callable, so no call site carries any extra data.
var TypeNames = [typeNameCount]string{"undefined", "object", "boolean", "number", "string", "symbol", "bigint", "function"}
TypeNames spells the TypeofIs operand.
Functions ¶
func Disassemble ¶
Disassemble renders fn (and, indented, its children) in a stable text form for tests and debugging.
func EncodeAsBx ¶
EncodeAsBx packs an AsBx instruction.
func TypeNameIndex ¶
TypeNameIndex returns the TypeofIs operand for a typeof result string.
Types ¶
type CaptureLayout ¶
type CaptureLayout []uint16
CaptureLayout describes the function's own closure environment: its length is the number of Env slots created at function entry (zero means the function creates no Env and runs in its closure's environment), and entry i is the parameter register whose value is boxed into slot i at entry, or NoRegister for slots initialized to undefined.
type Const ¶
type Const struct {
Kind ConstKind
Num float64 // ConstNumber
Str string // ConstString (WTF-8), ConstBigInt (decimal or 0x hex), ConstRegExp (pattern)
IsASCII bool // ConstString: every byte of Str is < 0x80
Units []uint16 // ConstString: UTF-16 code units when !IsASCII (nil otherwise)
Key bool // ConstString: used as a property name (the engine interns it)
Flags string // ConstRegExp flags
Cooked []string // ConstTemplate cooked strings (WTF-8, or UndefinedCooked)
Raw []string // ConstTemplate raw strings (UTF-8)
Index int // ConstFunction: index into Function.Children
}
Const is a constant-pool entry. Only the fields relevant to Kind are set.
type ConstKind ¶
type ConstKind uint8
ConstKind tags a constant-pool entry.
const ( // ConstNumber is a float64 literal (Num). ConstNumber ConstKind = iota // ConstString is a string literal (Str, IsASCII, Units). ConstString // ConstBigInt is a BigInt literal stored as text (Str): decimal, or hex // after "0x" (see syntax.BigIntLit). ConstBigInt // ConstRegExp is a regular-expression literal (Str = pattern, Flags). ConstRegExp // ConstTemplate is a tagged template's strings (Cooked, Raw); untagged // templates are lowered to Concat. ConstTemplate // ConstFunction is an index into Function.Children (Index). Reserved; // Closure indexes Children directly. ConstFunction )
type Function ¶
type Function struct {
Name string // "" for anonymous functions
Length int // formal parameter count exposed as .length
Strict bool // always true (strict mode only)
Kind Kind
NumRegs uint16 // register window size
NumParams uint16 // declared parameters (excluding rest)
HasRest bool // the rest array is collected into R[NumParams] at entry
// HasArguments makes the entry create an unmapped arguments object in
// the register after the parameters (and the rest array).
HasArguments bool
// NewTarget is set when the function reads new.target (LoadNewTarget):
// [[Construct]] then passes it through the realm. Class constructors
// always receive it.
NewTarget bool
// Generator marks a generator function (of kind KindMethod: not
// constructible); its body starts with GenStart.
Generator bool
// Async marks an async function, async generator function or module
// with top-level await (see compiler/async.go).
Async bool
// ICCount is the number of inline-cache slots this function needs. Their
// indices are assigned per realm on the function's first call there
// (engine.Realm.AllocIC); the template is never mutated.
ICCount uint32
Code []uint32
Consts []Const
Children []*Function
Handlers []Handler
CaptureLayout CaptureLayout
LineTable []LineEntry
Source *SourceInfo
// Exports maps export names to module Env slots (KindModule only). The
// module Env is the function's own Env, so every exported binding is a
// CaptureLayout slot and GetBindingValue is one slice index.
Exports map[string]int
// Globals lists the names a script declares at top level (KindScript
// only; nil when it declares none) for the checks of
// GlobalDeclarationInstantiation before the body runs.
Globals *GlobalNames
}
Function is an immutable compiled-function template. A single *Function is shared by every realm that instantiates the same module, so nothing in it may be mutated after compilation; per-realm state (inline caches) lives in engine.Realm side tables keyed by *Function.
func (*Function) Position ¶
Position returns the source line and column of the instruction at pc, or (0, 0) when the function has no line table.
func (*Function) SourceText ¶
SourceText returns the function's source slice for Function.prototype.toString, or "" when unknown.
type GlobalNames ¶
type GlobalNames struct {
Lexical []string // let, const and class names
Function []string // function declaration names
Var []string // var names that are not also function names
}
GlobalNames are the top-level declarations of a script, each list in source order.
type Handler ¶
type Handler struct {
Start uint32
End uint32
Handler uint32
StackDepth uint16
Kind HandlerKind
Reg uint16 // register receiving the exception (see HandlerKind)
}
Handler is one exception-table row: [Start, End) is the protected pc range, Handler is the pc to resume at and StackDepth the closure-environment depth (number of PushEnv levels relative to function entry) to unwind to. Rows are ordered innermost first; the interpreter takes the first row whose range covers the faulting pc.
type HandlerKind ¶
type HandlerKind uint8
HandlerKind distinguishes catch and finally rows of the handler table.
const ( // HandlerCatch receives the thrown value in R[Reg]. HandlerCatch HandlerKind = iota // HandlerFinally receives the completion kind (1 = throw) in R[Reg] and // the thrown value in R[Reg+1]. HandlerFinally )
type Kind ¶
type Kind uint8
Kind classifies a compiled function template.
const ( // KindNormal is an ordinary function declaration or expression. KindNormal Kind = iota // KindArrow is an arrow function (lexical this, not constructible). KindArrow // KindMethod is a concise object-literal method (not constructible). KindMethod // KindModule is the module top-level body. KindModule // KindScript is a strict-mode script top-level body (RunString). KindScript // KindClassCtor is a base class constructor: [[Construct]] creates // `this` from new.target and [[Call]] throws (CtorEntry). The class // kinds come last so IsClassCtor is one comparison. KindClassCtor // KindDerivedCtor is the constructor of a class with an extends clause: // `this` stays uninitialized until super() returns. KindDerivedCtor )
func (Kind) IsClassCtor ¶
IsClassCtor reports whether k is a class constructor kind.
type Op ¶
type Op uint8
Op is an opcode. Opcodes are dense so that the interpreter's switch compiles to a jump table. See doc.go for the semantics of every op.
const ( // --- loads and moves --- LoadConst Op = iota // ABx R[A] = K[Bx] LoadInt // AsBx R[A] = sBx LoadUndef // A R[A] = undefined LoadNull // A R[A] = null LoadTrue // A R[A] = true LoadFalse // A R[A] = false LoadHole // A R[A] = hole (TDZ marker) LoadThis // A R[A] = this LoadCallee // A R[A] = the running function object Move // AB R[A] = R[B] UndefRange // AB R[A .. A+B-1] = undefined // --- closure environments --- GetEnv // ABC R[A] = env^B[C] GetEnvChk // ABC+X R[A] = env^B[C]; ReferenceError if hole; X = name const SetEnv // ABC env^B[C] = R[A] GetEnvW // AB+X R[A] = env^B[X] GetEnvChkW // AB+X+X R[A] = env^B[X1]; ReferenceError if hole; X2 = name const SetEnvW // AB+X env^B[X] = R[A] PushEnv // ABx env = new Env(parent env, Bx slots) PopEnv // - env = env.parent CopyEnv // - env = shallow copy of env (per-iteration bindings) CheckTDZ // A+X ReferenceError "Cannot access 'K[X]' before initialization" if R[A] is hole // --- globals --- GetGlobal // A+X R[A] = global[name]; ReferenceError if absent; X = name:16 | ic:16 GetGlobalOrUndef // A+X R[A] = global[name] or undefined (typeof) SetGlobal // A+X global[name] = R[A]; ReferenceError if absent // --- properties --- GetProp // AB+X R[A] = R[B].name; X = name:16 | ic:16 GetLen // AB+X R[A] = R[B].length; X = ic:16 SetProp // AB+X R[A].name = R[B]; X = name:16 | ic:16 GetElem // ABC R[A] = R[B][R[C]] SetElem // ABC R[A][R[B]] = R[C] DelProp // AB+X R[A] = delete R[B].name; X = name const DelElem // ABC R[A] = delete R[B][R[C]] In // ABC R[A] = R[B] in R[C] InstanceOf // ABC R[A] = R[B] instanceof R[C] // --- literals --- NewObject // ABx R[A] = {} with room for Bx properties NewArray // ABx R[A] = [] with capacity Bx DefineField // AB+X R[A].name := R[B] (CreateDataProperty); X = name const DefineElem // ABC R[A][R[B]] := R[C] (CreateDataProperty) DefineMethod // ABC R[A][R[B]] := R[C], naming the function R[C] after the key (computed-key method) DefineAccessor // ABC+X define R[C] as the getter or setter of R[A][R[B]]; X = Accessor* flags SetProto // AB R[A].[[Prototype]] = R[B] when object or null (__proto__: v) CopyDataProps // AB copy own enumerable properties of R[B] into R[A] CopyDataPropsEx // ABC same, excluding the keys listed in array R[C] ArrayPush // AB append R[B] to array R[A] ArrayHole // A append a hole to array R[A] AppendSpread // AB append every element of iterable R[B] to array R[A] Closure // ABx R[A] = new function object from Children[Bx] NewRegExp // ABx R[A] = new RegExp(K[Bx].pattern, K[Bx].flags) GetTemplate // A+X R[A] = template object of K[X] (GetTemplateObject); X = const:16 | ic:16 // --- arithmetic --- Add // ABC R[A] = R[B] + R[C] Sub // ABC Mul // ABC Div // ABC Mod // ABC Exp // ABC R[A] = R[B] ** R[C] AddImm // ABC R[A] = R[B] + int8(C) SubImm // ABC R[A] = R[B] - int8(C) Inc // AB R[A] = ToNumeric(R[B]) + 1 Dec // AB R[A] = ToNumeric(R[B]) - 1 Neg // AB R[A] = -R[B] Plus // AB R[A] = +R[B] (ToNumber) Not // AB R[A] = !R[B] BitNot // AB R[A] = ~R[B] ToNumeric // AB R[A] = ToNumeric(R[B]) // RequireObjectCoercible throws TypeError when R[A] is null or undefined // (destructuring through a pattern with no properties to read). RequireObjectCoercible // A ToStr // AB R[A] = ToString(R[B]) Concat // ABC R[A] = R[B] concatenated with R[C] (both strings) Typeof // AB R[A] = typeof R[B] TypeofIs // ABC R[A] = typeof R[B] === TypeName(C) BitAnd // ABC BitOr // ABC BitXor // ABC Shl // ABC Shr // ABC signed >> UShr // ABC >>> // --- comparison --- Eq // ABC R[A] = R[B] == R[C] Ne // ABC StrictEq // ABC StrictNe // ABC Lt // ABC Le // ABC Gt // ABC Ge // ABC // --- control flow --- Jmp // sBx pc += sBx (negative offsets are back-edges: interrupt check) JmpT // AsBx if ToBoolean(R[A]) pc += sBx JmpF // AsBx if !ToBoolean(R[A]) pc += sBx JmpNullish // AsBx if R[A] is null or undefined JmpNotNullish // AsBx JmpNotUndef // AsBx // --- calls --- Call // AB R[A] = R[A].call(this = R[A+1], R[A+2 .. A+1+B]) CallSpread // A R[A] = R[A].call(this = R[A+1], ...R[A+2]) New // AB R[A] = new R[A](R[A+2 .. A+1+B]) NewSpread // A R[A] = new R[A](...R[A+2]) Ret // A return R[A] RetUndef // - return undefined Throw // A throw R[A] ThrowConstAssign // +X throw TypeError("Assignment to constant variable."); X = name const // --- iteration --- IterInit // AB R[A], R[A+1] = iterator state over R[B] IterNext // AsBx R[A+2] = next value of iterator R[A]; when done pc += sBx IterValue // AB R[B] = next value of iterator R[A], or undefined when done IterRest // AB R[B] = array of the remaining values of iterator R[A] IterClose // AsBx IteratorClose(R[A], normal); pc += sBx IterThrow // AB IteratorClose(R[A], throw R[B]); throw R[B] ForInInit // AB R[A] = key list of R[B], R[A+1] = 0, R[A+2] = ToObject(R[B]) (or undefined) ForInNext // AsBx R[A+3] = next key still present on R[A+2]; when done pc += sBx // --- classes (dispatched off the interpreter's jump table) --- CtorEntry // A R[A] = new.target; TypeError when a class constructor is called LoadNewTarget // A R[A] = new.target, or undefined in a [[Call]] LoadHome // A R[A] = [[HomeObject]] of the running function SetHome // AB [[HomeObject]] of function R[A] = R[B] CreateClass // ABC make R[A] a class constructor, R[A+1] = its prototype; C&1: R[B] is the heritage, C&2: named after key R[A+1] DefineClassMethod // ABC R[A][R[B]] := R[C] non-enumerable, [[HomeObject]] R[A] ToPropertyKey // AB R[A] = ToPropertyKey(R[B]) GetProtoOf // AB R[A] = R[B].[[GetPrototypeOf]]() (super base, super constructor) GetSuper // ABC R[A] = R[B][R[C]] with receiver R[B+1] SetSuper // ABC R[A][R[B]] = R[C] with receiver R[A+1] SuperCall // AB R[A] = Construct(R[A], R[A+2 .. A+1+B], new.target R[A+1]) SuperCallSpread // A R[A] = Construct(R[A], ...R[A+2], new.target R[A+1]) CheckSuper // A ReferenceError unless R[A] (the raw `this` binding) is hole DerivedResult // AB R[A] = result of a derived constructor returning R[A] with `this` R[B] NewPrivateName // ABx R[A] = new private name described by K[Bx] SetPrivateMethod // ABC+X private name R[A] names method R[B] owned by brand/class R[C]; X = Private* flags GetPrivate // ABC R[A] = R[B].#R[C] SetPrivate // ABC R[A].#R[B] = R[C] DefPrivate // ABC add private field #R[B] = R[C] to R[A] AddBrand // AB add class brand R[B] to R[A] InPrivate // ABC R[A] = #R[B] in R[C] ThrowError // ABx throw a new error of kind A (Throw* kinds) with message K[Bx] // --- generators (dispatched off the jump table after the class ops) --- GenFunc // A make the fresh closure R[A] a generator function GenStart // A R[A] = new generator with `this` R[A]; suspends it, and the call returns it Yield // ABC generator R[B] suspends yielding R[A] (C=1: R[A] is a result object, yielded as is); // resumed with the sent value in R[A] and the mode in R[A+1]: undefined next, true throw, false return GenIter // AB R[A] = GetIterator(R[B]) (never a fast mode), R[A+1] = its next method Delegate // AB one yield* step on the record R[B..B+1] with the value R[A] and mode R[A+1] (as Yield): // --- async functions and iteration (dispatched after the generator ops) --- AsyncFunc // A make the fresh closure R[A] an async function or async generator function AsyncStart // A R[A] = the state of an async function call with `this` R[A]; the call returns its promise AsyncGenStart // A R[A] = new async generator with `this` R[A]; suspends it, and the call returns it Await // AB R[B] (the async state) awaits R[A]; resumed with R[A] = the result, R[A+1] = undefined (fulfilled) or true (rejected) AsyncReturn // AB resolve the promise of async function R[B] with R[A]; R[A] = the promise AsyncThrow // AB reject the promise of async function R[B] with R[A]; R[A] = the promise AsyncYield // AB async generator R[B] settles its head request with {value: R[A], done: false}; resumed as Yield AsyncIterInit // AB R[A] = GetIterator(R[B], async), R[A+1] = its next method AsyncIterNext // A R[A+2] = Call(R[A+1], R[A]) AsyncIterResult // AsBx TypeError unless R[A+2] is an object; when done pc += sBx, else R[A+2] = its value AsyncIterClose // AsBx R[A] = undefined; pc += sBx if the iterator has no return method, else R[A+2] = Call(return, iterator) AsyncIterClosed // A TypeError unless the awaited return result R[A+2] is an object AsyncDelegate // ABC one async yield* step on the record R[B..B+2] with value R[A] and mode R[A+1] (C=0 call, C=1 result) )
Instruction formats. Every instruction is one 32-bit word:
ABC : op:8 | A:8 | B:8 | C:8 ABx : op:8 | A:8 | Bx:16 (Bx unsigned) AsBx: op:8 | A:8 | sBx:16 (sBx signed, jump offset relative to the next pc)
Ops marked "+X" in doc.go are followed by one ExtraArg word (a raw uint32) whose layout is given per op; a few use two.
func (Op) ExtraWords ¶
ExtraWords returns how many ExtraArg words follow an instruction of op.
type SourceInfo ¶
type SourceInfo struct {
Name string // file name used in stack traces
Src string // full module/script source text
Start int // byte offset of the function's first character
End int // byte offset one past the function's last character
}
SourceInfo locates a function template inside its source file. Function.prototype.toString slices Src[Start:End].