Source code

Revision control

Other Tools

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sw=2 et tw=0 ft=c:
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef vm_Opcodes_h
#define vm_Opcodes_h
#include "mozilla/Attributes.h"
#include <stddef.h>
// clang-format off
/*
* [SMDOC] Bytecode Definitions
*
* SpiderMonkey bytecode instructions.
*
* To use this header, define a macro of the form:
*
* #define MACRO(op, op_snake, token, length, nuses, ndefs, format) ...
*
* Then `FOR_EACH_OPCODE(MACRO)` invokes `MACRO` for every opcode.
*
* Field Description
* ----- -----------
* op UpperCamelCase form of opcode id
* op_snake snake_case form of opcode id
* token Pretty-printer string, or null if ugly
* length Number of bytes including any immediate operands
* nuses Number of stack slots consumed by bytecode, -1 if variadic
* ndefs Number of stack slots produced by bytecode
* format JOF_ flags describing instruction operand layout, etc.
*
* For more about `format`, see the comments on the `JOF_` constants defined in
* BytecodeUtil.h.
*
*
* [SMDOC] Bytecode Invariants
*
* Creating scripts that do not follow the rules can lead to undefined
* behavior. Bytecode has many consumers, not just the interpreter: JITs,
* analyses, the debugger. That's why the rules below apply even to code that
* can't be reached in ordinary execution (such as code after an infinite loop
* or inside an `if (false)` block).
*
* The `code()` of a script must be a packed (not aligned) sequence of valid
* instructions from start to end. Each instruction has a single byte opcode
* followed by a number of operand bytes based on the opcode.
*
* ## Jump instructions
*
* Operands named `offset`, `forwardOffset`, or `defaultOffset` are jump
* offsets, the distance in bytes from the start of the current instruction to
* the start of another instruction in the same script. Operands named
* `forwardOffset` or `defaultOffset` must be positive.
*
* Forward jumps must jump to a `JSOp::JumpTarget` instruction. Backward jumps,
* indicated by negative offsets, must jump to a `JSOp::LoopHead` instruction.
* Jump offsets can't be zero.
*
* Needless to say, scripts must not contain overlapping instruction sequences
*
* A script's `trynotes` and `scopeNotes` impose further constraints. Each try
* note and each scope note marks a region of the bytecode where some invariant
* holds, or some cleanup behavior is needed--that there's a for-in iterator in
* a particular stack slot, for instance, which must be closed on error. All
* paths into the span must establish that invariant. In practice, this means
* other code never jumps into the span: the only way in is to execute the
* bytecode instruction that sets up the invariant (in our example,
* `JSOp::Iter`).
*
* If a script's `trynotes` (see "Try Notes" in JSScript.h) contain a
* `JSTRY_CATCH` or `JSTRY_FINALLY` span, there must be a `JSOp::Try`
* instruction immediately before the span and a `JSOp::JumpTarget immediately
* after it. Instructions must not jump to this `JSOp::JumpTarget`. (The VM puts
* us there on exception.) Furthermore, the instruction sequence immediately
* following a `JSTRY_CATCH` span must read `JumpTarget; Exception` or, in
* non-function scripts, `JumpTarget; Undefined; SetRval; Exception`. (These
* instructions run with an exception pending; other instructions aren't
* designed to handle that.)
*
* Unreachable instructions are allowed, but they have to follow all the rules.
*
* Control must not reach the end of a script. (Currently, the last instruction
* is always JSOp::RetRval.)
*
* ## Other operands
*
* Operands named `nameIndex` or `atomIndex` (which appear on instructions that
* have `JOF_ATOM` in the `format` field) must be valid indexes into
* `script->atoms()`.
*
* Operands named `argc` (`JOF_ARGC`) are argument counts for call
* instructions. `argc` must be small enough that the instruction's nuses is <=
* the current stack depth (see "Stack depth" below).
*
* Operands named `argno` (`JOF_QARG`) refer to an argument of the current
* function. `argno` must be in the range `0..script->function()->nargs()`.
* Instructions with these operands must appear only in function scripts.
*
* Operands named `localno` (`JOF_LOCAL`) refer to a local variable stored in
* the stack frame. `localno` must be in the range `0..script->nfixed()`.
*
* Operands named `resumeIndex` (`JOF_RESUMEINDEX`) refer to a resume point in
* the current script. `resumeIndex` must be a valid index into
* `script->resumeOffsets()`.
*
* Operands named `hops` and `slot` (`JOF_ENVCOORD`) refer a slot in an
* `EnvironmentObject`. At run time, they must point to a fixed slot in an
* object on the current environment chain. See `EnvironmentCoordinates`.
*
* Operands with the following names must be valid indexes into
* `script->gcthings()`, and the pointer in the vector must point to the right
* type of thing:
*
* - `objectIndex` (`JOF_OBJECT`): `PlainObject*` or `ArrayObject*`
* - `baseobjIndex` (`JOF_OBJECT`): `PlainObject*`
* - `funcIndex` (`JOF_OBJECT`): `JSFunction*`
* - `regexpIndex` (`JOF_REGEXP`): `RegExpObject*`
* - `scopeIndex` (`JOF_SCOPE`): `Scope*`
* - `lexicalScopeIndex` (`JOF_SCOPE`): `LexicalScope*`
* - `withScopeIndex` (`JOF_SCOPE`): `WithScope*`
* - `bigIntIndex` (`JOF_BIGINT`): `BigInt*`
*
* Operands named `icIndex` (`JOF_ICINDEX`) must be exactly the number of
* preceding instructions in the script that have the JOF_IC flag.
* (Rationale: Each JOF_IC instruction has a unique entry in
* `script->jitScript()->icEntries()`. At run time, in the bytecode
* interpreter, we have to find that entry. We could store the IC index as an
* operand to each JOF_IC instruction, but it's more memory-efficient to use a
* counter and reset the counter to `icIndex` after each jump.)
*
* ## Stack depth
*
* Each instruction has a compile-time stack depth, the number of values on the
* interpreter stack just before executing the instruction. It isn't explicitly
* present in the bytecode itself, but (for reachable instructions, anyway)
* it's a function of the bytecode.
*
* - The first instruction has stack depth 0.
*
* - Each successor of an instruction X has a stack depth equal to
*
* X's stack depth - `js::StackUses(X)` + `js::StackDefs(X)`
*
* except for `JSOp::Case` (below).
*
* X's "successors" are: the next instruction in the script, if
* `js::FlowsIntoNext(op)` is true for X's opcode; one or more
* `JSOp::JumpTarget`s elsewhere, if X is a forward jump or
* `JSOp::TableSwitch`; and/or a `JSOp::LoopHead` if it's a backward jump.
*
* - `JSOp::Case` is a special case because its stack behavior is eccentric.
* The formula above is correct for the next instruction. The jump target
* has a stack depth that is 1 less.
*
* - See `JSOp::Gosub` for another special case.
*
* - The `JSOp::JumpTarget` instruction immediately following a `JSTRY_CATCH`
* or `JSTRY_FINALLY` span has the same stack depth as the `JSOp::Try`
* instruction that precedes the span.
*
* Every instruction covered by the `JSTRY_CATCH` or `JSTRY_FINALLY` span
* must have a stack depth >= that value, so that error recovery is
* guaranteed to find enough values on the stack to resume there.
*
* - `script->nslots() - script->nfixed()` must be >= the maximum stack
* depth of any instruction in `script`. (The stack frame must be big
* enough to run the code.)
*
* `BytecodeParser::parse()` computes stack depths for every reachable
* instruction in a script.
*
* ## Scopes and environments
*
* As with stack depth, each instruction has a static scope, which is a
* compile-time characterization of the eventual run-time environment chain
* when that instruction executes. Just as every instruction has a stack budget
* (nuses/ndefs), every instruction either pushes a scope, pops a scope, or
* neither. The same successor relation applies as above.
*
* Every scope used in a script is stored in the `JSScript::gcthings()` vector.
* They can be accessed using `getScope(index)` if you know what `index` to
* pass.
*
* The scope of every instruction (that's reachable via the successor relation)
* is given in two independent ways: by the bytecode itself and by the scope
* notes. The two sources must agree.
*
* ## Further rules
*
* All reachable instructions must be reachable without taking any backward
* edges.
*
* Instructions with the `JOF_CHECKSLOPPY` flag must not be used in strict mode
* code. `JOF_CHECKSTRICT` instructions must not be used in nonstrict code.
*
* Many instructions have their own additional rules. These are documented on
* the various opcodes below (look for the word "must").
*/
// clang-format on
// clang-format off
/*
* SpiderMonkey bytecode categorization (as used in generated documentation):
*
* [Index]
* [Constants]
* [Expressions]
* Unary operators
* Binary operators
* Conversions
* Other expressions
* [Objects]
* Creating objects
* Defining properties
* Accessing properties
* Super
* Enumeration
* Iteration
* SetPrototype
* Array literals
* RegExp literals
* [Functions]
* Creating functions
* Creating constructors
* Calls
* Generators and async functions
* [Control flow]
* Jump targets
* Jumps
* Return
* Exceptions
* [Variables and scopes]
* Initialization
* Looking up bindings
* Getting binding values
* Setting binding values
* Entering and leaving environments
* Creating and deleting bindings
* Function environment setup
* [Stack operations]
* [Other]
*/
// clang-format on
// clang-format off
#define FOR_EACH_OPCODE(MACRO) \
/*
* Push `undefined`.
*
* Category: Constants
* Operands:
* Stack: => undefined
*/ \
MACRO(Undefined, undefined, "", 1, 0, 1, JOF_BYTE) \
/*
* Push `null`.
*
* Category: Constants
* Operands:
* Stack: => null
*/ \
MACRO(Null, null, js_null_str, 1, 0, 1, JOF_BYTE) \
/*
* Push a boolean constant.
*
* Category: Constants
* Operands:
* Stack: => true/false
*/ \
MACRO(False, false_, js_false_str, 1, 0, 1, JOF_BYTE) \
MACRO(True, true_, js_true_str, 1, 0, 1, JOF_BYTE) \
/*
* Push the `int32_t` immediate operand as an `Int32Value`.
*
* `JSOp::Zero`, `JSOp::One`, `JSOp::Int8`, `JSOp::Uint16`, and `JSOp::Uint24`
* are all compact encodings for `JSOp::Int32`.
*
* Category: Constants
* Operands: int32_t val
* Stack: => val
*/ \
MACRO(Int32, int32, NULL, 5, 0, 1, JOF_INT32) \
/*
* Push the number `0`.
*
* Category: Constants
* Operands:
* Stack: => 0
*/ \
MACRO(Zero, zero, "0", 1, 0, 1, JOF_BYTE) \
/*
* Push the number `1`.
*
* Category: Constants
* Operands:
* Stack: => 1
*/ \
MACRO(One, one, "1", 1, 0, 1, JOF_BYTE) \
/*
* Push the `int8_t` immediate operand as an `Int32Value`.
*
* Category: Constants
* Operands: int8_t val
* Stack: => val
*/ \
MACRO(Int8, int8, NULL, 2, 0, 1, JOF_INT8) \
/*
* Push the `uint16_t` immediate operand as an `Int32Value`.
*
* Category: Constants
* Operands: uint16_t val
* Stack: => val
*/ \
MACRO(Uint16, uint16, NULL, 3, 0, 1, JOF_UINT16) \
/*
* Push the `uint24_t` immediate operand as an `Int32Value`.
*
* Category: Constants
* Operands: uint24_t val
* Stack: => val
*/ \
MACRO(Uint24, uint24, NULL, 4, 0, 1, JOF_UINT24) \
/*
* Push the 64-bit floating-point immediate operand as a `DoubleValue`.
*
* If the operand is a NaN, it must be the canonical NaN (see
* `JS::detail::CanonicalizeNaN`).
*
* Category: Constants
* Operands: double val
* Stack: => val
*/ \
MACRO(Double, double_, NULL, 9, 0, 1, JOF_DOUBLE) \
/*
* Push the BigInt constant `script->getBigInt(bigIntIndex)`.
*
* Category: Constants
* Operands: uint32_t bigIntIndex
* Stack: => bigint
*/ \
MACRO(BigInt, big_int, NULL, 5, 0, 1, JOF_BIGINT) \
/*
* Push the string constant `script->getAtom(atomIndex)`.
*
* Category: Constants
* Operands: uint32_t atomIndex
* Stack: => string
*/ \
MACRO(String, string, NULL, 5, 0, 1, JOF_ATOM) \
/*
* Push a well-known symbol.
*
* `symbol` must be in range for `JS::SymbolCode`.
*
* Category: Constants
* Operands: uint8_t symbol (the JS::SymbolCode of the symbol to use)
* Stack: => symbol
*/ \
MACRO(Symbol, symbol, NULL, 2, 0, 1, JOF_UINT8) \
/*
* Pop the top value on the stack, discard it, and push `undefined`.
*
* Implements: [The `void` operator][1], step 3.
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => undefined
*/ \
MACRO(Void, void_, NULL, 1, 1, 1, JOF_BYTE) \
/*
* [The `typeof` operator][1].
*
* Infallible. The result is always a string that depends on the [type][2]
* of `val`.
*
* `JSOp::Typeof` and `JSOp::TypeofExpr` are the same except
* that--amazingly--`JSOp::Typeof` affects the behavior of an immediately
* *preceding* `JSOp::GetName` or `JSOp::GetGName` instruction! This is how
* we implement [`typeof`][1] step 2, making `typeof nonExistingVariable`
* return `"undefined"` instead of throwing a ReferenceError.
*
* In a global scope:
*
* - `typeof x` compiles to `GetGName "x"; Typeof`.
* - `typeof (0, x)` compiles to `GetGName "x"; TypeofExpr`.
*
* Emitting the same bytecode for these two expressions would be a bug.
* Per spec, the latter throws a ReferenceError if `x` doesn't exist.
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => (typeof val)
*/ \
MACRO(Typeof, typeof_, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
MACRO(TypeofExpr, typeof_expr, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [The unary `+` operator][1].
*
* `+val` doesn't do any actual math. It just calls [ToNumber][2](val).
*
* The conversion can call `.toString()`/`.valueOf()` methods and can
* throw. The result on success is always a Number. (Per spec, unary `-`
* supports BigInts, but unary `+` does not.)
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => (+val)
*/ \
MACRO(Pos, pos, "+ ", 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [The unary `-` operator][1].
*
* Convert `val` to a numeric value, then push `-val`. The conversion can
* call `.toString()`/`.valueOf()` methods and can throw. The result on
* success is always numeric.
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => (-val)
*/ \
MACRO(Neg, neg, "- ", 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [The bitwise NOT operator][1] (`~`).
*
* `val` is converted to an integer, then bitwise negated. The conversion
* can call `.toString()`/`.valueOf()` methods and can throw. The result on
* success is always an Int32 or BigInt value.
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => (~val)
*/ \
MACRO(BitNot, bit_not, "~", 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [The logical NOT operator][1] (`!`).
*
* `val` is first converted with [ToBoolean][2], then logically
* negated. The result is always a boolean value. This does not call
* user-defined methods and can't throw.
*
*
* Category: Expressions
* Type: Unary operators
* Operands:
* Stack: val => (!val)
*/ \
MACRO(Not, not_, "!", 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [Binary bitwise operations][1] (`|`, `^`, `&`).
*
* The arguments are converted to integers first. The conversion can call
* `.toString()`/`.valueOf()` methods and can throw. The result on success
* is always an Int32 or BigInt Value.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(BitOr, bit_or, "|", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(BitXor, bit_xor, "^", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(BitAnd, bit_and, "&", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Loose equality operators (`==` and `!=`).
*
* Pop two values, compare them, and push the boolean result. The
* comparison may perform conversions that call `.toString()`/`.valueOf()`
* methods and can throw.
*
* Implements: [Abstract Equality Comparison][1].
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(Eq, eq, "==", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Ne, ne, "!=", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Strict equality operators (`===` and `!==`).
*
* Pop two values, check whether they're equal, and push the boolean
* result. This does not call user-defined methods and can't throw
* (except possibly due to OOM while flattening a string).
*
* Implements: [Strict Equality Comparison][1].
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(StrictEq, strict_eq, "===", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(StrictNe, strict_ne, "!==", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Relative operators (`<`, `>`, `<=`, `>=`).
*
* Pop two values, compare them, and push the boolean result. The
* comparison may perform conversions that call `.toString()`/`.valueOf()`
* methods and can throw.
*
* Implements: [Relational Operators: Evaluation][1].
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(Lt, lt, "<", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Gt, gt, ">", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Le, le, "<=", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Ge, ge, ">=", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [The `instanceof` operator][1].
*
* This throws a `TypeError` if `target` is not an object. It calls
* `target[Symbol.hasInstance](value)` if the method exists. On success,
* the result is always a boolean value.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: value, target => (value instanceof target)
*/ \
MACRO(Instanceof, instanceof, js_instanceof_str, 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [The `in` operator][1].
*
* Push `true` if `obj` has a property with the key `id`. Otherwise push `false`.
*
* This throws a `TypeError` if `obj` is not an object. This can fire
* proxy hooks and can throw. On success, the result is always a boolean
* value.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: id, obj => (id in obj)
*/ \
MACRO(In, in_, js_in_str, 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [Bitwise shift operators][1] (`<<`, `>>`, `>>>`).
*
* Pop two values, convert them to integers, perform a bitwise shift, and
* push the result.
*
* Conversion can call `.toString()`/`.valueOf()` methods and can throw.
* The result on success is always an Int32 or BigInt Value.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(Lsh, lsh, "<<", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Rsh, rsh, ">>", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Ursh, ursh, ">>>", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [The binary `+` operator][1].
*
* Pop two values, convert them to primitive values, add them, and push the
* result. If both values are numeric, add them; if either is a
* string, do string concatenation instead.
*
* The conversion can call `.toString()`/`.valueOf()` methods and can throw.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval + rval)
*/ \
MACRO(Add, add, "+", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [The binary `-` operator][1].
*
* Pop two values, convert them to numeric values, subtract the top value
* from the other one, and push the result.
*
* The conversion can call `.toString()`/`.valueOf()` methods and can
* throw. On success, the result is always numeric.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval - rval)
*/ \
MACRO(Sub, sub, "-", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Add or subtract 1.
*
* `val` must already be a numeric value, such as the result of
* `JSOp::ToNumeric`.
*
* Implements: [The `++` and `--` operators][1], step 3 of each algorithm.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: val => (val +/- 1)
*/ \
MACRO(Inc, inc, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
MACRO(Dec, dec, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* [The multiplicative operators][1] (`*`, `/`, `%`).
*
* Pop two values, convert them to numeric values, do math, and push the
* result.
*
* The conversion can call `.toString()`/`.valueOf()` methods and can
* throw. On success, the result is always numeric.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval OP rval)
*/ \
MACRO(Mul, mul, "*", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Div, div, "/", 1, 2, 1, JOF_BYTE|JOF_IC) \
MACRO(Mod, mod, "%", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* [The exponentiation operator][1] (`**`).
*
* Pop two values, convert them to numeric values, do exponentiation, and
* push the result. The top value is the exponent.
*
* The conversion can call `.toString()`/`.valueOf()` methods and can
* throw. This throws a RangeError if both values are BigInts and the
* exponent is negative.
*
*
* Category: Expressions
* Type: Binary operators
* Operands:
* Stack: lval, rval => (lval ** rval)
*/ \
MACRO(Pow, pow, "**", 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Convert a value to a property key.
*
* Implements: [ToPropertyKey][1], except that if the result would be the
* string representation of some integer in the range 0..2^31, we push the
* corresponding Int32 value instead. This is because the spec insists that
* array indices are strings, whereas for us they are integers.
*
* This is used for code like `++obj[index]`, which must do both a
* `JSOp::GetElem` and a `JSOp::SetElem` with the same property key. Both
* instructions would convert `index` to a property key for us, but the
* spec says to convert it only once.
*
* The conversion can call `.toString()`/`.valueOf()` methods and can
* throw.
*
*
* Category: Expressions
* Type: Conversions
* Operands:
* Stack: propertyNameValue => propertyKey
*/ \
MACRO(ToPropertyKey, to_property_key, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* Convert a value to a numeric value (a Number or BigInt).
*
* Implements: [ToNumeric][1](val).
*
* Note: This is used to implement [`++` and `--`][2]. Surprisingly, it's
* not possible to get the right behavior using `JSOp::Add` and `JSOp::Sub`
* alone. For one thing, `JSOp::Add` sometimes does string concatenation,
* while `++` always does numeric addition. More fundamentally, the result
* of evaluating `x--` is ToNumeric(old value of `x`), a value that the
* sequence `GetLocal "x"; One; Sub; SetLocal "x"` does not give us.
*
*
* Category: Expressions
* Type: Conversions
* Operands:
* Stack: val => ToNumeric(val)
*/ \
MACRO(ToNumeric, to_numeric, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* Convert a value to a string.
*
* Implements: [ToString][1](val).
*
* Note: This is used in code for template literals, like `${x}${y}`. Each
* substituted value must be converted using ToString. `JSOp::Add` by itself
* would do a slightly wrong kind of conversion (hint="number" rather than
* hint="string").
*
*
* Category: Expressions
* Type: Conversions
* Stack: val => ToString(val)
*/ \
MACRO(ToString, to_string, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Push the global `this` value. Not to be confused with the `globalThis`
* property on the global.
*
* This must be used only in scopes where `this` refers to the global
* `this`.
*
* Category: Expressions
* Type: Other expressions
* Operands:
* Stack: => this
*/ \
MACRO(GlobalThis, global_this, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Push the value of `new.target`.
*
* The result is a constructor or `undefined`.
*
* This must be used only in scripts where `new.target` is allowed:
* non-arrow function scripts and other scripts that have a non-arrow
* function script on the scope chain.
*
* Implements: [GetNewTarget][1].
*
*
* Category: Expressions
* Type: Other expressions
* Operands:
* Stack: => new.target
*/ \
MACRO(NewTarget, new_target, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Dynamic import of the module specified by the string value on the top of
* the stack.
*
* Implements: [Import Calls][1].
*
*
* Category: Expressions
* Type: Other expressions
* Operands:
* Stack: moduleId => promise
*/ \
MACRO(DynamicImport, dynamic_import, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Push the `import.meta` object.
*
* This must be used only in module code.
*
* Category: Expressions
* Type: Other expressions
* Operands:
* Stack: => import.meta
*/ \
MACRO(ImportMeta, import_meta, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Create and push a new object with no properties.
*
* Category: Objects
* Type: Creating objects
* Operands:
* Stack: => obj
*/ \
MACRO(NewInit, new_init, NULL, 1, 0, 1, JOF_BYTE|JOF_IC) \
/*
* Create and push a new object of a predetermined shape.
*
* The new object has the shape of the template object
* `script->getObject(baseobjIndex)`. Subsequent `InitProp` instructions
* must fill in all slots of the new object before it is used in any other
* way.
*
* For `JSOp::NewObject`, the new object has a group based on the allocation
* site (or a new group if the template's group is a singleton). For
* `JSOp::NewObjectWithGroup`, the new object has the same group as the
* template object.
*
* Category: Objects
* Type: Creating objects
* Operands: uint32_t baseobjIndex
* Stack: => obj
*/ \
MACRO(NewObject, new_object, NULL, 5, 0, 1, JOF_OBJECT|JOF_IC) \
MACRO(NewObjectWithGroup, new_object_with_group, NULL, 5, 0, 1, JOF_OBJECT|JOF_IC) \
/*
* Push a preconstructed object.
*
* Going one step further than `JSOp::NewObject`, this instruction doesn't
* just reuse the shape--it actually pushes the preconstructed object
* `script->getObject(objectIndex)` right onto the stack. The object must
* be a singleton `PlainObject` or `ArrayObject`.
*
* The spec requires that an *ObjectLiteral* or *ArrayLiteral* creates a
* new object every time it's evaluated, so this instruction must not be
* used anywhere it might be executed more than once.
*
* There's a shell-only option, `newGlobal({cloneSingletons: true})`, that
* makes this instruction do a deep copy of the object. A few tests use it.
*
* Category: Objects
* Type: Creating objects
* Operands: uint32_t objectIndex
* Stack: => obj
*/ \
MACRO(Object, object, NULL, 5, 0, 1, JOF_OBJECT) \
/*
* Create and push a new ordinary object with the provided [[Prototype]].
*
* This is used to create the `.prototype` object for derived classes.
*
* Category: Objects
* Type: Creating objects
* Operands:
* Stack: proto => obj
*/ \
MACRO(ObjWithProto, obj_with_proto, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Define a data property on an object.
*
* `obj` must be an object.
*
* Implements: [CreateDataPropertyOrThrow][1] as used in
* [PropertyDefinitionEvaluation][2] of regular and shorthand
* *PropertyDefinition*s.
*
*
* Category: Objects
* Type: Defining properties
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
MACRO(InitProp, init_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
/*
* Like `JSOp::InitProp`, but define a non-enumerable property.
*
* This is used to define class methods.
*
* Implements: [PropertyDefinitionEvaluation][1] for methods, steps 3 and
* 4, when *enumerable* is false.
*
*
* Category: Objects
* Type: Defining properties
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
MACRO(InitHiddenProp, init_hidden_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
/*
* Like `JSOp::InitProp`, but define a non-enumerable, non-writable,
* non-configurable property.
*
* This is used to define the `.prototype` property on classes.
*
* Implements: [MakeConstructor][1], step 8, when *writablePrototype* is
* false.
*
*
* Category: Objects
* Type: Defining properties
* Operands: uint32_t nameIndex
* Stack: obj, val => obj
*/ \
MACRO(InitLockedProp, init_locked_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT|JOF_IC) \
/*
* Define a data property on `obj` with property key `id` and value `val`.
*
* `obj` must be an object.
*
* Implements: [CreateDataPropertyOrThrow][1]. This instruction is used for
* object literals like `{0: val}` and `{[id]: val}`, and methods like
* `*[Symbol.iterator]() {}`.
*
* `JSOp::InitHiddenElem` is the same but defines a non-enumerable property,
* for class methods.
*
*
* Category: Objects
* Type: Defining properties
* Operands:
* Stack: obj, id, val => obj
*/ \
MACRO(InitElem, init_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
MACRO(InitHiddenElem, init_hidden_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
/*
* Define an accessor property on `obj` with the given `getter`.
* `nameIndex` gives the property name.
*
* `obj` must be an object and `getter` must be a function.
*
* `JSOp::InitHiddenPropGetter` is the same but defines a non-enumerable
* property, for getters in classes.
*
* Category: Objects
* Type: Defining properties
* Operands: uint32_t nameIndex
* Stack: obj, getter => obj
*/ \
MACRO(InitPropGetter, init_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
MACRO(InitHiddenPropGetter, init_hidden_prop_getter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
/*
* Define an accessor property on `obj` with property key `id` and the given `getter`.
*
* This is used to implement getters like `get [id]() {}` or `get 0() {}`.
*
* `obj` must be an object and `getter` must be a function.
*
* `JSOp::InitHiddenElemGetter` is the same but defines a non-enumerable
* property, for getters in classes.
*
* Category: Objects
* Type: Defining properties
* Operands:
* Stack: obj, id, getter => obj
*/ \
MACRO(InitElemGetter, init_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
MACRO(InitHiddenElemGetter, init_hidden_elem_getter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
/*
* Define an accessor property on `obj` with the given `setter`.
*
* This is used to implement ordinary setters like `set foo(v) {}`.
*
* `obj` must be an object and `setter` must be a function.
*
* `JSOp::InitHiddenPropSetter` is the same but defines a non-enumerable
* property, for setters in classes.
*
* Category: Objects
* Type: Defining properties
* Operands: uint32_t nameIndex
* Stack: obj, setter => obj
*/ \
MACRO(InitPropSetter, init_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
MACRO(InitHiddenPropSetter, init_hidden_prop_setter, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPINIT) \
/*
* Define an accesssor property on `obj` with property key `id` and the
* given `setter`.
*
* This is used to implement setters with computed property keys or numeric
* keys.
*
* `JSOp::InitHiddenElemSetter` is the same but defines a non-enumerable
* property, for setters in classes.
*
* Category: Objects
* Type: Defining properties
* Operands:
* Stack: obj, id, setter => obj
*/ \
MACRO(InitElemSetter, init_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
MACRO(InitHiddenElemSetter, init_hidden_elem_setter, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPINIT) \
/*
* Get the value of the property `obj.name`. This can call getters and
* proxy traps.
*
* `JSOp::CallProp` is exactly like `JSOp::GetProp` but hints to the VM that we're
* getting a method in order to call it.
*
* Implements: [GetV][1], [GetValue][2] step 5.
*
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj => obj[name]
*/ \
MACRO(GetProp, get_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_IC) \
MACRO(CallProp, call_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_IC) \
/*
* Get the value of the property `obj[key]`.
*
* `JSOp::CallElem` is exactly like `JSOp::GetElem` but hints to the VM that
* we're getting a method in order to call it.
*
* Implements: [GetV][1], [GetValue][2] step 5.
*
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: obj, key => obj[key]
*/ \
MACRO(GetElem, get_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_TYPESET|JOF_IC) \
MACRO(CallElem, call_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_TYPESET|JOF_IC) \
/*
* Push the value of `obj.length`.
*
* `nameIndex` must be the index of the atom `"length"`. This then behaves
* exactly like `JSOp::GetProp`.
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj => obj.length
*/ \
MACRO(Length, length, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_IC) \
/*
* Non-strict assignment to a property, `obj.name = val`.
*
* This throws a TypeError if `obj` is null or undefined. If it's a
* primitive value, the property is set on ToObject(`obj`), typically with
* no effect.
*
* Implements: [PutValue][1] step 6 for non-strict code.
*
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj, val => val
*/ \
MACRO(SetProp, set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC) \
/*
* Like `JSOp::SetProp`, but for strict mode code. Throw a TypeError if
* `obj[key]` exists but is non-writable, if it's an accessor property with
* no setter, or if `obj` is a primitive value.
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj, val => val
*/ \
MACRO(StrictSetProp, strict_set_prop, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC) \
/*
* Non-strict assignment to a property, `obj[key] = val`.
*
* Implements: [PutValue][1] step 6 for non-strict code.
*
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: obj, key, val => val
*/ \
MACRO(SetElem, set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY|JOF_IC) \
/*
* Like `JSOp::SetElem`, but for strict mode code. Throw a TypeError if
* `obj[key]` exists but is non-writable, if it's an accessor property with
* no setter, or if `obj` is a primitive value.
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: obj, key, val => val
*/ \
MACRO(StrictSetElem, strict_set_elem, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT|JOF_IC) \
/*
* Delete a property from `obj`. Push true on success, false if the
* property existed but could not be deleted. This implements `delete
* obj.name` in non-strict code.
*
* Throws if `obj` is null or undefined. Can call proxy traps.
*
* Implements: [`delete obj.propname`][1] step 5 in non-strict code.
*
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj => succeeded
*/ \
MACRO(DelProp, del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSLOPPY) \
/*
* Like `JSOp::DelProp`, but for strict mode code. Push `true` on success,
* else throw a TypeError.
*
* Category: Objects
* Type: Accessing properties
* Operands: uint32_t nameIndex
* Stack: obj => succeeded
*/ \
MACRO(StrictDelProp, strict_del_prop, NULL, 5, 1, 1, JOF_ATOM|JOF_PROP|JOF_CHECKSTRICT) \
/*
* Delete the property `obj[key]` and push `true` on success, `false`
* if the property existed but could not be deleted.
*
* This throws if `obj` is null or undefined. Can call proxy traps.
*
* Implements: [`delete obj[key]`][1] step 5 in non-strict code.
*
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: obj, key => succeeded
*/ \
MACRO(DelElem, del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSLOPPY) \
/*
* Like `JSOp::DelElem, but for strict mode code. Push `true` on success,
* else throw a TypeError.
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: obj, key => succeeded
*/ \
MACRO(StrictDelElem, strict_del_elem, NULL, 1, 2, 1, JOF_BYTE|JOF_ELEM|JOF_CHECKSTRICT) \
/*
* Push true if `obj` has an own property `id`.
*
* Note that `obj` is the top value, like `JSOp::In`.
*
* This opcode is not used for normal JS. Self-hosted code uses it by
* calling the intrinsic `hasOwn(id, obj)`. For example,
* `Object.prototype.hasOwnProperty` is implemented this way (see
* js/src/builtin/Object.js).
*
* Category: Objects
* Type: Accessing properties
* Operands:
* Stack: id, obj => (obj.hasOwnProperty(id))
*/ \
MACRO(HasOwn, has_own, NULL, 1, 2, 1, JOF_BYTE|JOF_IC) \
/*
* Push a bool representing the presence of private field id on obj.
* May throw, depending on the ThrowCondition.
*
* Two arguments:
* - throwCondition: One of the ThrowConditions defined in
* ThrowMsgKind.h. Determines why (or if) this op will throw.
* - msgKind: One of the ThrowMsgKinds defined in ThrowMsgKind.h, which
* maps to one of the messages in js.msg. Note: It's not possible to
* pass arguments to the message at the moment.
*
* Category: Control flow
* Category: Objects
* Type: Accessing properties
* Operands: ThrowCondition throwCondition, ThrowMsgKind msgKind
* Stack: obj, key => obj, key, (obj.hasOwnProperty(id))
*/ \
MACRO(CheckPrivateField, check_private_field, NULL, 3, 2, 3, JOF_TWO_UINT8|JOF_CHECKSTRICT|JOF_IC) \
/*
* Push the SuperBase of the method `callee`. The SuperBase is
* `callee.[[HomeObject]].[[GetPrototypeOf]]()`, the object where `super`
* property lookups should begin.
*
* `callee` must be a function that has a HomeObject that's an object,
* typically produced by `JSOp::Callee` or `JSOp::EnvCallee`.
*
* Implements: [GetSuperBase][1], except that instead of the environment,
* the argument supplies the callee.
*
*
* Category: Objects
* Type: Super
* Operands:
* Stack: callee => superBase
*/ \
MACRO(SuperBase, super_base, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Get the value of `receiver.name`, starting the property search at `obj`.
* In spec terms, `obj.[[Get]](name, receiver)`.
*
* Implements: [GetValue][1] for references created by [`super.name`][2].
* The `receiver` is `this` and `obj` is the SuperBase of the enclosing
* method.
*
*
* Category: Objects
* Type: Super
* Operands: uint32_t nameIndex
* Stack: receiver, obj => super.name
*/ \
MACRO(GetPropSuper, get_prop_super, NULL, 5, 2, 1, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_IC) \
/*
* Get the value of `receiver[key]`, starting the property search at `obj`.
* In spec terms, `obj.[[Get]](key, receiver)`.
*
* Implements: [GetValue][1] for references created by [`super[key]`][2]
* (where the `receiver` is `this` and `obj` is the SuperBase of the enclosing
* method); [`Reflect.get(obj, key, receiver)`][3].
*
*
* Category: Objects
* Type: Super
* Operands:
* Stack: receiver, key, obj => super[key]
*/ \
MACRO(GetElemSuper, get_elem_super, NULL, 1, 3, 1, JOF_BYTE|JOF_ELEM|JOF_TYPESET|JOF_IC) \
/*
* Assign `val` to `receiver.name`, starting the search for an existing
* property at `obj`. In spec terms, `obj.[[Set]](name, val, receiver)`.
*
* Implements: [PutValue][1] for references created by [`super.name`][2] in
* non-strict code. The `receiver` is `this` and `obj` is the SuperBase of
* the enclosing method.
*
*
* Category: Objects
* Type: Super
* Operands: uint32_t nameIndex
* Stack: receiver, obj, val => val
*/ \
MACRO(SetPropSuper, set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSLOPPY) \
/*
* Like `JSOp::SetPropSuper`, but for strict mode code.
*
* Category: Objects
* Type: Super
* Operands: uint32_t nameIndex
* Stack: receiver, obj, val => val
*/ \
MACRO(StrictSetPropSuper, strict_set_prop_super, NULL, 5, 3, 1, JOF_ATOM|JOF_PROP|JOF_PROPSET|JOF_CHECKSTRICT) \
/*
* Assign `val` to `receiver[key]`, strating the search for an existing
* property at `obj`. In spec terms, `obj.[[Set]](key, val, receiver)`.
*
* Implements: [PutValue][1] for references created by [`super[key]`][2] in
* non-strict code. The `receiver` is `this` and `obj` is the SuperBase of
* the enclosing method.
*
*
* Category: Objects
* Type: Super
* Operands:
* Stack: receiver, key, obj, val => val
*/ \
MACRO(SetElemSuper, set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSLOPPY) \
/*
* Like `JSOp::SetElemSuper`, but for strict mode code.
*
* Category: Objects
* Type: Super
* Operands:
* Stack: receiver, key, obj, val => val
*/ \
MACRO(StrictSetElemSuper, strict_set_elem_super, NULL, 1, 4, 1, JOF_BYTE|JOF_ELEM|JOF_PROPSET|JOF_CHECKSTRICT) \
/*
* Set up a for-in loop by pushing a `PropertyIteratorObject` over the
* enumerable properties of `val`.
*
* Implements: [ForIn/OfHeadEvaluation][1] step 6,
* [EnumerateObjectProperties][1]. (The spec refers to an "Iterator object"
* with a `next` method, but notes that it "is never directly accessible"
* to scripts. The object we use for this has no public methods.)
*
* If `val` is null or undefined, this pushes an empty iterator.
*
* The `iter` object pushed by this instruction must not be used or removed
* from the stack except by `JSOp::MoreIter` and `JSOp::EndIter`, or by error
* handling.
*
* The script's `JSScript::trynotes()` must mark the body of the `for-in`
* loop, i.e. exactly those instructions that begin executing with `iter`
* on the stack, starting with the next instruction (always
* `JSOp::LoopHead`). Code must not jump into or out of this region: control
* can enter only by executing `JSOp::Iter` and can exit only by executing a
* `JSOp::EndIter` or by exception unwinding. (A `JSOp::EndIter` is always
* emitted at the end of the loop, and extra copies are emitted on "exit
* slides", where a `break`, `continue`, or `return` statement exits the
* loop.)
*
* Typically a single try note entry marks the contiguous chunk of bytecode
* from the instruction after `JSOp::Iter` to `JSOp::EndIter` (inclusive);
* but if that range contains any instructions on exit slides, after a
* `JSOp::EndIter`, then those must be correctly noted as *outside* the
* loop.
*
*
* Category: Objects
* Type: Enumeration
* Operands:
* Stack: val => iter
*/ \
MACRO(Iter, iter, NULL, 1, 1, 1, JOF_BYTE|JOF_IC) \
/*
* Get the next property name for a for-in loop.
*
* `iter` must be a `PropertyIteratorObject` produced by `JSOp::Iter`. This
* pushes the property name for the next loop iteration, or
* `MagicValue(JS_NO_ITER_VALUE)` if there are no more enumerable
* properties to iterate over. The magic value must be used only by
* `JSOp::IsNoIter` and `JSOp::EndIter`.
*
* Category: Objects
* Type: Enumeration
* Operands:
* Stack: iter => iter, name
*/ \
MACRO(MoreIter, more_iter, NULL, 1, 1, 2, JOF_BYTE) \
/*
* Test whether the value on top of the stack is
* `MagicValue(JS_NO_ITER_VALUE)` and push the boolean result.
*
* Category: Objects
* Type: Enumeration
* Operands:
* Stack: val => val, done
*/ \
MACRO(IsNoIter, is_no_iter, NULL, 1, 1, 2, JOF_BYTE) \
/*
* No-op instruction to hint to IonBuilder that the value on top of the
* stack is the string key in a for-in loop.
*
* Category: Objects
* Type: Enumeration
* Operands:
* Stack: val => val
*/ \
MACRO(IterNext, iter_next, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Exit a for-in loop, closing the iterator.
*
* `iter` must be a `PropertyIteratorObject` pushed by `JSOp::Iter`.
*
* Category: Objects
* Type: Enumeration
* Operands:
* Stack: iter, iterval =>
*/ \
MACRO(EndIter, end_iter, NULL, 1, 2, 0, JOF_BYTE) \
/*
* Check that the top value on the stack is an object, and throw a
* TypeError if not. `kind` is used only to generate an appropriate error
* message.
*
* Implements: [GetIterator][1] step 5, [IteratorNext][2] step 3. Both
* operations call a JS method which scripts can define however they want,
* so they check afterwards that the method returned an object.
*
*
* Category: Objects
* Type: Iteration
* Operands: CheckIsObjectKind kind
* Stack: result => result
*/ \
MACRO(CheckIsObj, check_is_obj, NULL, 2, 1, 1, JOF_UINT8) \
/*
* Throw a TypeError if `val` is `null` or `undefined`.
*
* Implements: [RequireObjectCoercible][1]. But most instructions that
* require an object will perform this check for us, so of the dozens of
* calls to RequireObjectCoercible in the spec, we need this instruction
* only for [destructuring assignment][2] and [initialization][3].
*
*
* Category: Objects
* Type: Iteration
* Operands:
* Stack: val => val
*/ \
MACRO(CheckObjCoercible, check_obj_coercible, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Create and push an async iterator wrapping the sync iterator `iter`.
* `next` should be `iter`'s `.next` method.
*
* Implements: [CreateAsyncToSyncIterator][1]. The spec says this operation
* takes one argument, but that argument is a Record with two relevant
* fields, `[[Iterator]]` and `[[NextMethod]]`.
*
* Used for `for await` loops.
*
*
* Category: Objects
* Type: Iteration
* Operands:
* Stack: iter, next => asynciter
*/ \
MACRO(ToAsyncIter, to_async_iter, NULL, 1, 2, 1, JOF_BYTE) \
/*
* Set the prototype of `obj`.
*
* `obj` must be an object.
*
* Implements: [B.3.1 __proto__ Property Names in Object Initializers][1], step 7.a.
*
*
* Category: Objects
* Type: SetPrototype
* Operands:
* Stack: obj, protoVal => obj
*/ \
MACRO(MutateProto, mutate_proto, NULL, 1, 2, 1, JOF_BYTE) \
/*
* Create and push a new Array object with the given `length`,
* preallocating enough memory to hold that many elements.
*
* Category: Objects
* Type: Array literals
* Operands: uint32_t length
* Stack: => array
*/ \
MACRO(NewArray, new_array, NULL, 5, 0, 1, JOF_UINT32|JOF_IC) \
/*
* Initialize an array element `array[index]` with value `val`.
*
* `val` may be `MagicValue(JS_ELEMENTS_HOLE)`. If it is, this does nothing.
*
* This never calls setters or proxy traps.
*
* `array` must be an Array object created by `JSOp::NewArray` with length >
* `index`, and never used except by `JSOp::InitElemArray`.
*
* Implements: [ArrayAccumulation][1], the third algorithm, step 4, in the
* common case where *nextIndex* is known.
*
*
* Category: Objects
* Type: Array literals
* Operands: uint32_t index
* Stack: array, val => array
*/ \
MACRO(InitElemArray, init_elem_array, NULL, 5, 2, 1, JOF_UINT32|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
/*
* Initialize an array element `array[index++]` with value `val`.
*
* `val` may be `MagicValue(JS_ELEMENTS_HOLE)`. If it is, no element is
* defined, but the array length and the stack value `index` are still
* incremented.
*
* This never calls setters or proxy traps.
*
* `array` must be an Array object created by `JSOp::NewArray` and never used
* except by `JSOp::InitElemArray` and `JSOp::InitElemInc`.
*
* `index` must be an integer, `0 <= index <= INT32_MAX`. If `index` is
* `INT32_MAX`, this throws a RangeError.
*
* This instruction is used when an array literal contains a
* *SpreadElement*. In `[a, ...b, c]`, `InitElemArray 0` is used to put
* `a` into the array, but `InitElemInc` is used for the elements of `b`
* and for `c`.
*
* Implements: Several steps in [ArrayAccumulation][1] that call
* CreateDataProperty, set the array length, and/or increment *nextIndex*.
*
*
* Category: Objects
* Type: Array literals
* Operands:
* Stack: array, index, val => array, (index + 1)
*/ \
MACRO(InitElemInc, init_elem_inc, NULL, 1, 3, 2, JOF_BYTE|JOF_ELEM|JOF_PROPINIT|JOF_IC) \
/*
* Push `MagicValue(JS_ELEMENTS_HOLE)`, representing an *Elision* in an
* array literal (like the missing property 0 in the array `[, 1]`).
*
* This magic value must be used only by `JSOp::InitElemArray` or
* `JSOp::InitElemInc`.
*
* Category: Objects
* Type: Array literals
* Operands:
* Stack: => hole
*/ \
MACRO(Hole, hole, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Create and push a new array that shares the elements of a template
* object.
*
* `script->getObject(objectIndex)` must be a copy-on-write array whose
* elements are all primitive values.
*
* This is an optimization. This single instruction implements an entire
* array literal, saving run time, code, and memory compared to
* `JSOp::NewArray` and a series of `JSOp::InitElem` instructions.
*
* Category: Objects
* Type: Array literals
* Operands: uint32_t objectIndex
* Stack: => array
*/ \
MACRO(NewArrayCopyOnWrite, new_array_copy_on_write, NULL, 5, 0, 1, JOF_OBJECT) \
/*
* Clone and push a new RegExp object.
*
* Implements: [Evaluation for *RegularExpressionLiteral*][1].
*
*
* Category: Objects
* Type: RegExp literals
* Operands: uint32_t regexpIndex
* Stack: => regexp
*/ \
MACRO(RegExp, reg_exp, NULL, 5, 0, 1, JOF_REGEXP) \
/*
* Push a function object.
*
* This clones the function unless it's a singleton; see
* `CanReuseFunctionForClone`. The new function inherits the current
* environment chain.
*
* Used to create most JS functions. Notable exceptions are arrow functions
* and derived or default class constructors.
*
* The function indicated by `funcIndex` must be a non-arrow function.
*
* Implements: [InstantiateFunctionObject][1], [Evaluation for
* *FunctionExpression*][2], and so on.
*
*
* Category: Functions
* Type: Creating functions
* Operands: uint32_t funcIndex
* Stack: => fn
*/ \
MACRO(Lambda, lambda, NULL, 5, 0, 1, JOF_OBJECT) \
/*
* Push a new arrow function.
*
* `newTarget` matters only if the arrow function uses the expression
* `new.target`. It should be the current value of `new.target`, so that
* the arrow function inherits `new.target` from the enclosing scope. (If
* `new.target` is illegal here, the value doesn't matter; use `null`.)
*
* The function indicated by `funcIndex` must be an arrow function.
*
* Category: Functions
* Type: Creating functions
* Operands: uint32_t funcIndex
* Stack: newTarget => arrowFn
*/ \
MACRO(LambdaArrow, lambda_arrow, NULL, 5, 1, 1, JOF_OBJECT) \
/*
* Set the name of a function.
*
* `fun` must be a function object. `name` must be a string, Int32 value,
* or symbol (like the result of `JSOp::ToId`).
*
* Implements: [SetFunctionName][1], used e.g. to name methods with
* computed property names.
*
*
* Category: Functions
* Type: Creating functions
* Operands: FunctionPrefixKind prefixKind
* Stack: fun, name => fun
*/ \
MACRO(SetFunName, set_fun_name, NULL, 2, 2, 1, JOF_UINT8) \
/*
* Initialize the home object for functions with super bindings.
*
* `fun` must be a method, getter, or setter, so that it has a
* [[HomeObject]] slot. `homeObject` must be a plain object or (for static
* methods) a constructor.
*
* Category: Functions
* Type: Creating functions
* Operands:
* Stack: fun, homeObject => fun
*/ \
MACRO(InitHomeObject, init_home_object, NULL, 1, 2, 1, JOF_BYTE) \
/*
* Throw a TypeError if `baseClass` isn't either `null` or a constructor.
*
* Implements: [ClassDefinitionEvaluation][1] step 6.f.
*
*
* Category: Functions
* Type: Creating constructors
* Operands:
* Stack: baseClass => baseClass
*/ \
MACRO(CheckClassHeritage, check_class_heritage, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Like `JSOp::Lambda`, but using `proto` as the new function's
* `[[Prototype]]` (or `%FunctionPrototype%` if `proto` is `null`).
*
* `proto` must be either a constructor or `null`. We use
* `JSOp::CheckClassHeritage` to check.
*
* This is used to create the constructor for a derived class.
*
* Implements: [ClassDefinitionEvaluation][1] steps 6.e.ii, 6.g.iii, and
* 12 for derived classes.
*
*
* Category: Functions
* Type: Creating constructors
* Operands: uint32_t funcIndex
* Stack: proto => obj
*/ \
MACRO(FunWithProto, fun_with_proto, NULL, 5, 1, 1, JOF_OBJECT) \
/*
* Create and push a default constructor for a base class.
*
* A default constructor behaves like `constructor() {}`.
*
* Implements: [ClassDefinitionEvaluation for *ClassTail*][1], steps
* 10.b. and 12-17.
*
* The `sourceStart`/`sourceEnd` offsets are the start/end offsets of the
* class definition in the source buffer, used for `toString()`. They must
* be valid offsets into the source buffer, measured in code units, such
* that `scriptSource->substring(cx, start, end)` is valid.
*
*
* Category: Functions
* Type: Creating constructors
* Operands: uint32_t nameIndex, uint32_t sourceStart, uint32_t sourceEnd
* Stack: => constructor
*/ \
MACRO(ClassConstructor, class_constructor, NULL, 13, 0, 1, JOF_CLASS_CTOR) \
/*
* Create and push a default constructor for a derived class.
*
* A default derived-class constructor behaves like
* `constructor(...args) { super(...args); }`.
*
* Implements: [ClassDefinitionEvaluation for *ClassTail*][1], steps
* 10.a. and 12-17.
*
* `sourceStart` and `sourceEnd` follow the same rules as for
* `JSOp::ClassConstructor`.
*
*
* Category: Functions
* Type: Creating constructors
* Operands: uint32_t nameIndex, uint32_t sourceStart, uint32_t sourceEnd
* Stack: proto => constructor
*/ \
MACRO(DerivedConstructor, derived_constructor, NULL, 13, 1, 1, JOF_CLASS_CTOR) \
/*
* Pushes the current global's FunctionPrototype.
*
* `kind` must be in range for `JSProtoKey` (and must not be
* `JSProto_LIMIT`).
*
* Category: Functions
* Type: Creating constructors
* Operands:
* Stack: => %FunctionPrototype%
*/ \
MACRO(FunctionProto, function_proto, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Invoke `callee` with `this` and `args`, and push the return value. Throw
* a TypeError if `callee` isn't a function.
*
* `JSOp::CallIter` is used for implicit calls to @@iterator methods, to
* ensure error messages are formatted with `JSMSG_NOT_ITERABLE` ("x is not
* iterable") rather than `JSMSG_NOT_FUNCTION` ("x[Symbol.iterator] is not
* a function"). The `argc` operand must be 0 for this variation.
*
* `JSOp::FunApply` hints to the VM that this is likely a call to the
* builtin method `Function.prototype.apply`, an easy optimization target.
*
* `JSOp::FunCall` similarly hints to the VM that the callee is likely
* `Function.prototype.call`.
*
* `JSOp::CallIgnoresRv` hints to the VM that the return value is ignored.
* This allows alternate faster implementations to be used that avoid
* unnecesary allocations.
*
* Implements: [EvaluateCall][1] steps 4, 5, and 7.
*
*
* Category: Functions
* Type: Calls
* Operands: uint16_t argc
* Stack: callee, this, args[0], ..., args[argc-1] => rval
*/ \
MACRO(Call, call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_IC) \
MACRO(CallIter, call_iter, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_IC) \
MACRO(FunApply, fun_apply, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_IC) \
MACRO(FunCall, fun_call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_IC) \
MACRO(CallIgnoresRv, call_ignores_rv, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_IC) \
/*
* Like `JSOp::Call`, but the arguments are provided in an array rather than
* a span of stack slots. Used to implement spread-call syntax:
* `f(...args)`.
*
* `args` must be an Array object containing the actual arguments. The
* array must be packed (dense and free of holes; see IsPackedArray).
* This can be ensured by creating the array with `JSOp::NewArray` and
* populating it using `JSOp::InitElemArray`.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: callee, this, args => rval
*/ \
MACRO(SpreadCall, spread_call, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_TYPESET|JOF_IC) \
/*
* Push true if `arr` is an array object that can be passed directly as the
* `args` argument to `JSOp::SpreadCall`.
*
* This instruction and the branch around the iterator loop are emitted
* only when `arr` is itself a rest parameter, as in `(...arr) =>
* f(...arr)`, a strong hint that it's a packed Array whose prototype is
* `Array.prototype`.
*
* See `js::OptimizeSpreadCall`.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: arr => arr, optimized
*/ \
MACRO(OptimizeSpreadCall, optimize_spread_call, NULL, 1, 1, 2, JOF_BYTE) \
/*
* Perform a direct eval in the current environment if `callee` is the
* builtin `eval` function, otherwise follow same behaviour as `JSOp::Call`.
*
* All direct evals use one of the JSOp::*Eval instructions here and these
* opcodes are only used when the syntactic conditions for a direct eval
* are met. If the builtin `eval` function is called though other means, it
* becomes an indirect eval.
*
* Direct eval causes all bindings in *enclosing* non-global scopes to be
* marked "aliased". The optimization that puts bindings in stack slots has
* to prove that the bindings won't need to be captured by closures or
* accessed using `JSOp::{Get,Bind,Set,Del}Name` instructions. Direct eval
* makes that analysis impossible.
*
* The instruction immediately following any `JSOp::*Eval` instruction must
* be `JSOp::Lineno`.
*
* Implements: [Function Call Evaluation][1], steps 5-7 and 9, when the
* syntactic critera for direct eval in step 6 are all met.
*
*
* Category: Functions
* Type: Calls
* Operands: uint16_t argc
* Stack: callee, this, args[0], ..., args[argc-1] => rval
*/ \
MACRO(Eval, eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSLOPPY|JOF_IC) \
/*
* Spread-call variant of `JSOp::Eval`.
*
* See `JSOp::SpreadCall` for restrictions on `args`.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: callee, this, args => rval
*/ \
MACRO(SpreadEval, spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_TYPESET|JOF_CHECKSLOPPY|JOF_IC) \
/*
* Like `JSOp::Eval`, but for strict mode code.
*
* Category: Functions
* Type: Calls
* Operands: uint16_t argc
* Stack: evalFn, this, args[0], ..., args[argc-1] => rval
*/ \
MACRO(StrictEval, strict_eval, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_TYPESET|JOF_CHECKSTRICT|JOF_IC) \
/*
* Spread-call variant of `JSOp::StrictEval`.
*
* See `JSOp::SpreadCall` for restrictions on `args`.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: callee, this, args => rval
*/ \
MACRO(StrictSpreadEval, strict_spread_eval, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE|JOF_SPREAD|JOF_TYPESET|JOF_CHECKSTRICT|JOF_IC) \
/*
* Push the implicit `this` value for an unqualified function call, like
* `foo()`. `nameIndex` gives the name of the function we're calling.
*
* The result is always `undefined` except when the name refers to a `with`
* binding. For example, in `with (date) { getFullYear(); }`, the
* implicit `this` passed to `getFullYear` is `date`, not `undefined`.
*
* This walks the run-time environment chain looking for the environment
* record that contains the function. If the function call is not inside a
* `with` statement, use `JSOp::GImplicitThis` instead. If the function call
* definitely refers to a local binding, use `JSOp::Undefined`.
*
* Implements: [EvaluateCall][1] step 1.b. But not entirely correctly.
* See [bug 1166408][2].
*
*
* Category: Functions
* Type: Calls
* Operands: uint32_t nameIndex
* Stack: => this
*/ \
MACRO(ImplicitThis, implicit_this, "", 5, 0, 1, JOF_ATOM) \
/*
* Like `JSOp::ImplicitThis`, but the name must not be bound in any local
* environments.
*
* The result is always `undefined` except when the name refers to a
* binding in a non-syntactic `with` environment.
*
* Note: The frontend has to emit `JSOp::GImplicitThis` (and not
* `JSOp::Undefined`) for global unqualified function calls, even when
* `CompileOptions::nonSyntacticScope == false`, because later
* `js::CloneGlobalScript` can be called with `ScopeKind::NonSyntactic` to
* clone the script into a non-syntactic environment, with the bytecode
* reused, unchanged.
*
* Category: Functions
* Type: Calls
* Operands: uint32_t nameIndex
* Stack: => this
*/ \
MACRO(GImplicitThis, g_implicit_this, "", 5, 0, 1, JOF_ATOM) \
/*
* Push the call site object for a tagged template call.
*
* `script->getObject(objectIndex)` is the call site object;
* `script->getObject(objectIndex + 1)` is the raw object.
*
* The first time this instruction runs for a given template, it assembles
* the final value, defining the `.raw` property on the call site object
* and freezing both objects.
*
* Implements: [GetTemplateObject][1], steps 4 and 12-16.
*
*
* Category: Functions
* Type: Calls
* Operands: uint32_t objectIndex
* Stack: => callSiteObj
*/ \
MACRO(CallSiteObj, call_site_obj, NULL, 5, 0, 1, JOF_OBJECT) \
/*
* Push `MagicValue(JS_IS_CONSTRUCTING)`.
*
* This magic value is a required argument to the `JSOp::New` and
* `JSOp::SuperCall` instructions and must not be used any other way.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: => JS_IS_CONSTRUCTING
*/ \
MACRO(IsConstructing, is_constructing, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Invoke `callee` as a constructor with `args` and `newTarget`, and push
* the return value. Throw a TypeError if `callee` isn't a constructor.
*
* `isConstructing` must be the value pushed by `JSOp::IsConstructing`.
*
* `JSOp::SuperCall` behaves exactly like `JSOp::New`, but is used for
* *SuperCall* expressions, to allow JITs to distinguish them from `new`
* expressions.
*
* Implements: [EvaluateConstruct][1] steps 7 and 8.
*
*
* Category: Functions
* Type: Calls
* Operands: uint16_t argc
* Stack: callee, isConstructing, args[0], ..., args[argc-1], newTarget => rval
*/ \
MACRO(New, new_, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_TYPESET|JOF_IC) \
MACRO(SuperCall, super_call, NULL, 3, -1, 1, JOF_ARGC|JOF_INVOKE|JOF_CONSTRUCT|JOF_TYPESET|JOF_IC) \
/*
* Spread-call variant of `JSOp::New`.
*
* Invokes `callee` as a constructor with `args` and `newTarget`, and
* pushes the return value onto the stack.
*
* `isConstructing` must be the value pushed by `JSOp::IsConstructing`.
* See `JSOp::SpreadCall` for restrictions on `args`.
*
* `JSOp::SpreadSuperCall` behaves exactly like `JSOp::SpreadNew`, but is
* used for *SuperCall* expressions.
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: callee, isConstructing, args, newTarget => rval
*/ \
MACRO(SpreadNew, spread_new, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_TYPESET|JOF_IC) \
MACRO(SpreadSuperCall, spread_super_call, NULL, 1, 4, 1, JOF_BYTE|JOF_INVOKE|JOF_CONSTRUCT|JOF_SPREAD|JOF_TYPESET|JOF_IC) \
/*
* Push the prototype of `callee` in preparation for calling `super()`.
*
* `callee` must be a derived class constructor.
*
* Implements: [GetSuperConstructor][1], steps 4-7.
*
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: callee => superFun
*/ \
MACRO(SuperFun, super_fun, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Throw a ReferenceError if `thisval` is not
* `MagicValue(JS_UNINITIALIZED_LEXICAL)`. Used in derived class
* constructors to prohibit calling `super` more than once.
*
* Implements: [BindThisValue][1], step 3.
*
*
* Category: Functions
* Type: Calls
* Operands:
* Stack: thisval => thisval
*/ \
MACRO(CheckThisReinit, check_this_reinit, NULL, 1, 1, 1, JOF_BYTE) \
/*
* Create and push a generator object for the current frame.
*
* This instruction must appear only in scripts for generators, async
* functions, and async generators. There must not already be a generator
* object for the current frame (that is, this instruction must execute at
* most once per generator or async call).
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: => gen
*/ \
MACRO(Generator, generator, NULL, 1, 0, 1, JOF_BYTE) \
/*
* Suspend the current generator and return to the caller.
*
* When a generator is called, its script starts running, like any other JS
* function, because [FunctionDeclarationInstantation][1] and other
* [generator object setup][2] are implemented mostly in bytecode. However,
* the *FunctionBody* of the generator is not supposed to start running
* until the first `.next()` call, so after setup the script suspends
* itself: the "initial yield".
*
* Later, when resuming execution, `rval`, `gen` and `resumeKind` will
* receive the values passed in by `JSOp::Resume`. `resumeKind` is the
* `GeneratorResumeKind` stored as an Int32 value.
*
* This instruction must appear only in scripts for generators and async
* generators. `gen` must be the generator object for the current frame. It
* must not have been previously suspended. The resume point indicated by
* `resumeIndex` must be the next instruction in the script, which must be
* `AfterYield`.
*
* Implements: [GeneratorStart][3], steps 4-7.
*
*
* Category: Functions
* Type: Generators and async functions
* Operands: uint24_t resumeIndex
* Stack: gen => rval, gen, resumeKind
*/ \
MACRO(InitialYield, initial_yield, NULL, 4, 1, 3, JOF_RESUMEINDEX) \
/*
* Bytecode emitted after `yield` expressions. This is useful for the
* Debugger and `AbstractGeneratorObject::isAfterYieldOrAwait`. It's
* treated as jump target op so that the Baseline Interpreter can
* efficiently restore the frame's interpreterICEntry when resuming a
* generator.
*
* The preceding instruction in the script must be `Yield`, `InitialYield`,
* or `Await`.
*
* Category: Functions
* Type: Generators and async functions
* Operands: uint32_t icIndex
* Stack: =>
*/ \
MACRO(AfterYield, after_yield, NULL, 5, 0, 0, JOF_ICINDEX) \
/*
* Suspend and close the current generator, async function, or async
* generator.
*
* `gen` must be the generator object for the current frame.
*
* If the current function is a non-async generator, then the value in the
* frame's return value slot is returned to the caller. It should be an
* object of the form `{value: returnValue, done: true}`.
*
* If the current function is an async function or async generator, the
* frame's return value slot must contain the current frame's result
* promise, which must already be resolved or rejected.
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: gen =>
*/ \
MACRO(FinalYieldRval, final_yield_rval, NULL, 1, 1, 0, JOF_BYTE) \
/*
* Suspend execution of the current generator or async generator, returning
* `rval1`.
*
* For non-async generators, `rval1` should be an object of the form
* `{value: valueToYield, done: true}`. For async generators, `rval1`
* should be the value to yield, and the caller is responsible for creating
* the iterator result object (under `js::AsyncGeneratorYield`).
*
* This instruction must appear only in scripts for generators and async
* generators. `gen` must be the generator object for the current stack
* frame. The resume point indicated by `resumeIndex` must be the next
* instruction in the script, which must be `AfterYield`.
*
* When resuming execution, `rval2`, `gen` and `resumeKind` receive the
* values passed in by `JSOp::Resume`.
*
* Implements: [GeneratorYield][1] and [AsyncGeneratorYield][2].
*
*
* Category: Functions
* Type: Generators and async functions
* Operands: uint24_t resumeIndex
* Stack: rval1, gen => rval2, gen, resumeKind
*/ \
MACRO(Yield, yield, NULL, 4, 2, 3, JOF_RESUMEINDEX) \
/*
* Pushes a boolean indicating whether the top of the stack is
* `MagicValue(JS_GENERATOR_CLOSING)`.
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: val => val, res
*/ \
MACRO(IsGenClosing, is_gen_closing, NULL, 1, 1, 2, JOF_BYTE) \
/*
* Arrange for this async function to resume asynchronously when `value`
* becomes resolved.
*
* This is the last thing an async function does before suspending for an
* `await` expression. It coerces the awaited `value` to a promise and
* effectively calls `.then()` on it, passing handler functions that will
* resume this async function call later. See `js::AsyncFunctionAwait`.
*
* This instruction must appear only in non-generator async function
* scripts. `gen` must be the internal generator object for the current
* frame. After this instruction, the script should suspend itself with
* `Await` (rather than exiting any other way).
*
* The result `promise` is the async function's result promise,
* `gen->as<AsyncFunctionGeneratorObject>().promise()`.
*
* Implements: [Await][1], steps 2-9.
*
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: value, gen => promise
*/ \
MACRO(AsyncAwait, async_await, NULL, 1, 2, 1, JOF_BYTE) \
/*
* Resolve or reject the current async function's result promise with
* 'valueOrReason'.
*
* This instruction must appear only in non-generator async function
* scripts. `gen` must be the internal generator object for the current
* frame. This instruction must run at most once per async function call,
* as resolving/rejecting an already resolved/rejected promise is not
* permitted.
*
* The result `promise` is the async function's result promise,
* `gen->as<AsyncFunctionGeneratorObject>().promise()`.
*
* Implements: [AsyncFunctionStart][1], step 4.d.i. and 4.e.i.
*
*
* Category: Functions
* Type: Generators and async functions
* Operands: AsyncFunctionResolveKind fulfillOrReject
* Stack: valueOrReason, gen => promise
*/ \
MACRO(AsyncResolve, async_resolve, NULL, 2, 2, 1, JOF_UINT8) \
/*
* Suspend the current frame for an `await` expression.
*
* This instruction must appear only in scripts for async functions and
* async generators. `gen` must be the internal generator object for the
* current frame.
*
* This returns `promise` to the caller. Later, when this async call is
* resumed, `resolved`, `gen` and `resumeKind` receive the values passed in
* by `JSOp::Resume`, and execution continues at the next instruction,
* which must be `AfterYield`.
*
* This instruction is used in two subtly different ways.
*
* 1. In async functions:
*
* ... # valueToAwait
* GetAliasedVar ".generator" # valueToAwait gen
* AsyncAwait # resultPromise
* GetAliasedVar ".generator" # resultPromise gen
* Await # resolved gen resumeKind
* AfterYield
*
* `AsyncAwait` arranges for this frame to be resumed later and pushes
* its result promise. `Await` then suspends the frame and removes it
* from the stack, returning the result promise to the caller. (If this
* async call hasn't awaited before, the caller may be user code.
* Otherwise, the caller is self-hosted code using `resumeGenerator`.)
*
* 2. In async generators:
*
* ... # valueToAwait
* GetAliasedVar ".generator" # valueToAwait gen
* Await # resolved gen resumeKind
* AfterYield
*
* `AsyncAwait` is not used, so (1) the value returned to the caller by
* `Await` is `valueToAwait`, not `resultPromise`; and (2) the caller
* is responsible for doing the async-generator equivalent of
* `AsyncAwait` (namely, `js::AsyncGeneratorAwait`, called from
* `js::AsyncGeneratorResume` after `js::CallSelfHostedFunction`
* returns).
*
* Implements: [Await][1], steps 10-12.
*
*
* Category: Functions
* Type: Generators and async functions
* Operands: uint24_t resumeIndex
* Stack: promise, gen => resolved, gen, resumeKind
*/ \
MACRO(Await, await, NULL, 4, 2, 3, JOF_RESUMEINDEX) \
/*
* Decide whether awaiting 'value' can be skipped.
*
* This is part of an optimization for `await` expressions. Programs very
* often await values that aren't promises, or promises that are already
* resolved. We can then sometimes skip suspending the current frame and
* returning to the microtask loop. If the circumstances permit the
* optimization, `TrySkipAwait` replaces `value` with the result of the
* `await` expression (unwrapping the resolved promise, if any) and pushes
* `true`. Otherwise, it leaves `value` unchanged and pushes 'false'.
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: value => value_or_resolved, can_skip
*/ \
MACRO(TrySkipAwait, try_skip_await, NULL, 1, 1, 2, JOF_BYTE) \
/*
* Pushes one of the GeneratorResumeKind values as Int32Value.
*
* Category: Functions
* Type: Generators and async functions
* Operands: GeneratorResumeKind resumeKind (encoded as uint8_t)
* Stack: => resumeKind
*/ \
MACRO(ResumeKind, resume_kind, NULL, 2, 0, 1, JOF_UINT8) \
/*
* Handle Throw and Return resumption.
*
* `gen` must be the generator object for the current frame. `resumeKind`
* must be a `GeneratorResumeKind` stored as an `Int32` value. If it is
* `Next`, continue to the next instruction. If `resumeKind` is `Throw` or
* `Return`, these completions are handled by throwing an exception. See
* `GeneratorThrowOrReturn`.
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: rval, gen, resumeKind => rval
*/ \
MACRO(CheckResumeKind, check_resume_kind, NULL, 1, 3, 1, JOF_BYTE) \
/*
* Resume execution of a generator, async function, or async generator.
*
* This behaves something like a call instruction. It pushes a stack frame
* (the one saved when `gen` was suspended, rather than a fresh one) and
* runs instructions in it. Once `gen` returns or yields, its return value
* is pushed to this frame's stack and execution continues in this script.
*
* This instruction is emitted only for the `resumeGenerator` self-hosting
* intrinsic. It is used in the implementation of
* `%GeneratorPrototype%.next`, `.throw`, and `.return`.
*
* `gen` must be a suspended generator object. `resumeKind` must be in
* range for `GeneratorResumeKind`.
*
* Category: Functions
* Type: Generators and async functions
* Operands:
* Stack: gen, val, resumeKind => rval
*/ \
MACRO(Resume, resume, NULL, 1, 3, 1, JOF_BYTE|JOF_INVOKE) \