mirror of https://github.com/boa-dev/boa.git
Browse Source
<!--- Thank you for contributing to Boa! Please fill out the template below, and remove or add any information as you feel necessary. ---> Hi! This isn't really related to a pull request that I know of. I was trying to better wrap my head around Boa's VM and thought I'd break it apart so that the file wasn't 2500+ lines. I figured I'd submit it as a draft and get feedback/see if anyone was interested in it. The way the modules were broken apart was primarily based off the opcode name (`GetFunction` & `GetFunctionAsync` -> `./get/function.rs`). It changes the following: - Adds an `Operation` trait to opcode/mod.rs - Implements `Operation` for each Opcode variant, moving the executable instruction code from `vm/mod.rs` to the respective module Co-authored-by: raskad <32105367+raskad@users.noreply.github.com>pull/2370/head
Kevin
2 years ago
67 changed files with 5431 additions and 2853 deletions
@ -0,0 +1,119 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{JsArgs, Promise}, |
||||||
|
object::FunctionBuilder, |
||||||
|
vm::{call_frame::GeneratorResumeKind, opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Await` implements the Opcode Operation for `Opcode::Await`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Stops the current Async function and schedules it to resume later.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Await; |
||||||
|
|
||||||
|
impl Operation for Await { |
||||||
|
const NAME: &'static str = "Await"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Await"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
|
||||||
|
// 2. Let promise be ? PromiseResolve(%Promise%, value).
|
||||||
|
let promise = Promise::promise_resolve( |
||||||
|
context.intrinsics().constructors().promise().constructor(), |
||||||
|
value, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
// 3. Let fulfilledClosure be a new Abstract Closure with parameters (value) that captures asyncContext and performs the following steps when called:
|
||||||
|
// 4. Let onFulfilled be CreateBuiltinFunction(fulfilledClosure, 1, "", « »).
|
||||||
|
let on_fulfilled = FunctionBuilder::closure_with_captures( |
||||||
|
context, |
||||||
|
|_this, args, (environment, stack, frame), context| { |
||||||
|
// a. Let prevContext be the running execution context.
|
||||||
|
// b. Suspend prevContext.
|
||||||
|
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
|
||||||
|
// d. Resume the suspended evaluation of asyncContext using NormalCompletion(value) as the result of the operation that suspended it.
|
||||||
|
// e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
|
||||||
|
// f. Return undefined.
|
||||||
|
|
||||||
|
std::mem::swap(&mut context.realm.environments, environment); |
||||||
|
std::mem::swap(&mut context.vm.stack, stack); |
||||||
|
context.vm.push_frame(frame.clone()); |
||||||
|
|
||||||
|
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Normal; |
||||||
|
context.vm.push(args.get_or_undefined(0)); |
||||||
|
context.run()?; |
||||||
|
|
||||||
|
*frame = context |
||||||
|
.vm |
||||||
|
.pop_frame() |
||||||
|
.expect("generator call frame must exist"); |
||||||
|
std::mem::swap(&mut context.realm.environments, environment); |
||||||
|
std::mem::swap(&mut context.vm.stack, stack); |
||||||
|
|
||||||
|
Ok(JsValue::undefined()) |
||||||
|
}, |
||||||
|
( |
||||||
|
context.realm.environments.clone(), |
||||||
|
context.vm.stack.clone(), |
||||||
|
context.vm.frame().clone(), |
||||||
|
), |
||||||
|
) |
||||||
|
.name("") |
||||||
|
.length(1) |
||||||
|
.build(); |
||||||
|
|
||||||
|
// 5. Let rejectedClosure be a new Abstract Closure with parameters (reason) that captures asyncContext and performs the following steps when called:
|
||||||
|
// 6. Let onRejected be CreateBuiltinFunction(rejectedClosure, 1, "", « »).
|
||||||
|
let on_rejected = FunctionBuilder::closure_with_captures( |
||||||
|
context, |
||||||
|
|_this, args, (environment, stack, frame), context| { |
||||||
|
// a. Let prevContext be the running execution context.
|
||||||
|
// b. Suspend prevContext.
|
||||||
|
// c. Push asyncContext onto the execution context stack; asyncContext is now the running execution context.
|
||||||
|
// d. Resume the suspended evaluation of asyncContext using ThrowCompletion(reason) as the result of the operation that suspended it.
|
||||||
|
// e. Assert: When we reach this step, asyncContext has already been removed from the execution context stack and prevContext is the currently running execution context.
|
||||||
|
// f. Return undefined.
|
||||||
|
|
||||||
|
std::mem::swap(&mut context.realm.environments, environment); |
||||||
|
std::mem::swap(&mut context.vm.stack, stack); |
||||||
|
context.vm.push_frame(frame.clone()); |
||||||
|
|
||||||
|
context.vm.frame_mut().generator_resume_kind = GeneratorResumeKind::Throw; |
||||||
|
context.vm.push(args.get_or_undefined(0)); |
||||||
|
context.run()?; |
||||||
|
|
||||||
|
*frame = context |
||||||
|
.vm |
||||||
|
.pop_frame() |
||||||
|
.expect("generator call frame must exist"); |
||||||
|
std::mem::swap(&mut context.realm.environments, environment); |
||||||
|
std::mem::swap(&mut context.vm.stack, stack); |
||||||
|
|
||||||
|
Ok(JsValue::undefined()) |
||||||
|
}, |
||||||
|
( |
||||||
|
context.realm.environments.clone(), |
||||||
|
context.vm.stack.clone(), |
||||||
|
context.vm.frame().clone(), |
||||||
|
), |
||||||
|
) |
||||||
|
.name("") |
||||||
|
.length(1) |
||||||
|
.build(); |
||||||
|
|
||||||
|
// 7. Perform PerformPromiseThen(promise, onFulfilled, onRejected).
|
||||||
|
promise |
||||||
|
.as_object() |
||||||
|
.expect("promise was not an object") |
||||||
|
.borrow_mut() |
||||||
|
.as_promise_mut() |
||||||
|
.expect("promise was not a promise") |
||||||
|
.perform_promise_then(&on_fulfilled.into(), &on_rejected.into(), None, context); |
||||||
|
|
||||||
|
context.vm.push(JsValue::undefined()); |
||||||
|
Ok(ShouldExit::Await) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,70 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `LogicalAnd` implements the Opcode Operation for `Opcode::LogicalAnd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary logical `&&` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LogicalAnd; |
||||||
|
|
||||||
|
impl Operation for LogicalAnd { |
||||||
|
const NAME: &'static str = "LogicalAnd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LogicalAnd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let exit = context.vm.read::<u32>(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
if !lhs.to_boolean() { |
||||||
|
context.vm.frame_mut().pc = exit as usize; |
||||||
|
context.vm.push(lhs); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `LogicalOr` implements the Opcode Operation for `Opcode::LogicalOr`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary logical `||` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LogicalOr; |
||||||
|
|
||||||
|
impl Operation for LogicalOr { |
||||||
|
const NAME: &'static str = "LogicalOr"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LogicalOr"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let exit = context.vm.read::<u32>(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
if lhs.to_boolean() { |
||||||
|
context.vm.frame_mut().pc = exit as usize; |
||||||
|
context.vm.push(lhs); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Coalesce` implements the Opcode Operation for `Opcode::Coalesce`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary logical `||` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Coalesce; |
||||||
|
|
||||||
|
impl Operation for Coalesce { |
||||||
|
const NAME: &'static str = "Coalesce"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Coalesce"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let exit = context.vm.read::<u32>(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
if !lhs.is_null_or_undefined() { |
||||||
|
context.vm.frame_mut().pc = exit as usize; |
||||||
|
context.vm.push(lhs); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,46 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
macro_rules! implement_bin_ops { |
||||||
|
($name:ident, $op:ident, $doc_string:literal) => { |
||||||
|
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] |
||||||
|
#[doc= "\n"] |
||||||
|
#[doc="Operation:\n"] |
||||||
|
#[doc= concat!(" - ", $doc_string)] |
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct $name; |
||||||
|
|
||||||
|
impl Operation for $name { |
||||||
|
const NAME: &'static str = stringify!($name); |
||||||
|
const INSTRUCTION: &'static str = stringify!("INST - " + $name); |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let rhs = context.vm.pop(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
let value = lhs.$op(&rhs, context)?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
implement_bin_ops!(Add, add, "Binary `+` operator."); |
||||||
|
implement_bin_ops!(Sub, sub, "Binary `-` operator."); |
||||||
|
implement_bin_ops!(Mul, mul, "Binary `*` operator."); |
||||||
|
implement_bin_ops!(Div, div, "Binary `/` operator."); |
||||||
|
implement_bin_ops!(Pow, pow, "Binary `**` operator."); |
||||||
|
implement_bin_ops!(Mod, rem, "Binary `%` operator."); |
||||||
|
implement_bin_ops!(BitAnd, bitand, "Binary `&` operator."); |
||||||
|
implement_bin_ops!(BitOr, bitor, "Binary `|` operator."); |
||||||
|
implement_bin_ops!(BitXor, bitxor, "Binary `^` operator."); |
||||||
|
implement_bin_ops!(ShiftLeft, shl, "Binary `<<` operator."); |
||||||
|
implement_bin_ops!(ShiftRight, shr, "Binary `>>` operator."); |
||||||
|
implement_bin_ops!(UnsignedShiftRight, ushr, "Binary `>>>` operator."); |
||||||
|
implement_bin_ops!(Eq, equals, "Binary `==` operator."); |
||||||
|
implement_bin_ops!(GreaterThan, gt, "Binary `>` operator."); |
||||||
|
implement_bin_ops!(GreaterThanOrEq, ge, "Binary `>=` operator."); |
||||||
|
implement_bin_ops!(LessThan, lt, "Binary `<` operator."); |
||||||
|
implement_bin_ops!(LessThanOrEq, le, "Binary `<=` operator."); |
@ -0,0 +1,120 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) mod logical; |
||||||
|
pub(crate) mod macro_defined; |
||||||
|
|
||||||
|
pub(crate) use logical::*; |
||||||
|
pub(crate) use macro_defined::*; |
||||||
|
|
||||||
|
/// `NotEq` implements the Opcode Operation for `Opcode::NotEq`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary `!=` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct NotEq; |
||||||
|
|
||||||
|
impl Operation for NotEq { |
||||||
|
const NAME: &'static str = "NotEq"; |
||||||
|
const INSTRUCTION: &'static str = "INST - NotEq"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let rhs = context.vm.pop(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
let value = !lhs.equals(&rhs, context)?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `StrictEq` implements the Opcode Operation for `Opcode::StrictEq`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary `===` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct StrictEq; |
||||||
|
|
||||||
|
impl Operation for StrictEq { |
||||||
|
const NAME: &'static str = "StrictEq"; |
||||||
|
const INSTRUCTION: &'static str = "INST - StrictEq"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let rhs = context.vm.pop(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
context.vm.push(lhs.strict_equals(&rhs)); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `StrictNotEq` implements the Opcode Operation for `Opcode::StrictNotEq`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary `!==` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct StrictNotEq; |
||||||
|
|
||||||
|
impl Operation for StrictNotEq { |
||||||
|
const NAME: &'static str = "StrictNotEq"; |
||||||
|
const INSTRUCTION: &'static str = "INST - StrictNotEq"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let rhs = context.vm.pop(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
context.vm.push(!lhs.strict_equals(&rhs)); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `In` implements the Opcode Operation for `Opcode::In`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary `in` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct In; |
||||||
|
|
||||||
|
impl Operation for In { |
||||||
|
const NAME: &'static str = "In"; |
||||||
|
const INSTRUCTION: &'static str = "INST - In"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let rhs = context.vm.pop(); |
||||||
|
let lhs = context.vm.pop(); |
||||||
|
|
||||||
|
if !rhs.is_object() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message(format!( |
||||||
|
"right-hand side of 'in' should be an object, got {}", |
||||||
|
rhs.type_of().to_std_string_escaped() |
||||||
|
)) |
||||||
|
.into()); |
||||||
|
} |
||||||
|
let key = lhs.to_property_key(context)?; |
||||||
|
let value = context.has_property(&rhs, &key)?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `InstanceOf` implements the Opcode Operation for `Opcode::InstanceOf`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Binary `instanceof` operation
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct InstanceOf; |
||||||
|
|
||||||
|
impl Operation for InstanceOf { |
||||||
|
const NAME: &'static str = "InstanceOf"; |
||||||
|
const INSTRUCTION: &'static str = "INST - InstanceOf"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let target = context.vm.pop(); |
||||||
|
let v = context.vm.pop(); |
||||||
|
let value = v.instance_of(&target, context)?; |
||||||
|
|
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,212 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::function::Function, |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `CallEval` implements the Opcode Operation for `Opcode::CallEval`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call a function named "eval".
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CallEval; |
||||||
|
|
||||||
|
impl Operation for CallEval { |
||||||
|
const NAME: &'static str = "CallEval"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CallEval"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
let argument_count = context.vm.read::<u32>(); |
||||||
|
let mut arguments = Vec::with_capacity(argument_count as usize); |
||||||
|
for _ in 0..argument_count { |
||||||
|
arguments.push(context.vm.pop()); |
||||||
|
} |
||||||
|
arguments.reverse(); |
||||||
|
|
||||||
|
let func = context.vm.pop(); |
||||||
|
let this = context.vm.pop(); |
||||||
|
|
||||||
|
let object = match func { |
||||||
|
JsValue::Object(ref object) if object.is_callable() => object.clone(), |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("not a callable function") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// A native function with the name "eval" implies, that is this the built-in eval function.
|
||||||
|
let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. })); |
||||||
|
|
||||||
|
let strict = context.vm.frame().code.strict; |
||||||
|
|
||||||
|
if eval { |
||||||
|
if let Some(x) = arguments.get(0) { |
||||||
|
let result = crate::builtins::eval::Eval::perform_eval(x, true, strict, context)?; |
||||||
|
context.vm.push(result); |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::Undefined); |
||||||
|
} |
||||||
|
} else { |
||||||
|
let result = object.__call__(&this, &arguments, context)?; |
||||||
|
context.vm.push(result); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `CallEvalSpread` implements the Opcode Operation for `Opcode::CallEvalSpread`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call a function named "eval" where the arguments contain spreads.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CallEvalSpread; |
||||||
|
|
||||||
|
impl Operation for CallEvalSpread { |
||||||
|
const NAME: &'static str = "CallEvalSpread"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CallEvalSpread"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
// Get the arguments that are stored as an array object on the stack.
|
||||||
|
let arguments_array = context.vm.pop(); |
||||||
|
let arguments_array_object = arguments_array |
||||||
|
.as_object() |
||||||
|
.expect("arguments array in call spread function must be an object"); |
||||||
|
let arguments = arguments_array_object |
||||||
|
.borrow() |
||||||
|
.properties() |
||||||
|
.dense_indexed_properties() |
||||||
|
.expect("arguments array in call spread function must be dense") |
||||||
|
.clone(); |
||||||
|
|
||||||
|
let func = context.vm.pop(); |
||||||
|
let this = context.vm.pop(); |
||||||
|
|
||||||
|
let object = match func { |
||||||
|
JsValue::Object(ref object) if object.is_callable() => object.clone(), |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("not a callable function") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
// A native function with the name "eval" implies, that is this the built-in eval function.
|
||||||
|
let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. })); |
||||||
|
|
||||||
|
let strict = context.vm.frame().code.strict; |
||||||
|
|
||||||
|
if eval { |
||||||
|
if let Some(x) = arguments.get(0) { |
||||||
|
let result = crate::builtins::eval::Eval::perform_eval(x, true, strict, context)?; |
||||||
|
context.vm.push(result); |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::Undefined); |
||||||
|
} |
||||||
|
} else { |
||||||
|
let result = object.__call__(&this, &arguments, context)?; |
||||||
|
context.vm.push(result); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Call` implements the Opcode Operation for `Opcode::Call`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call a function
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Call; |
||||||
|
|
||||||
|
impl Operation for Call { |
||||||
|
const NAME: &'static str = "Call"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Call"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
let argument_count = context.vm.read::<u32>(); |
||||||
|
let mut arguments = Vec::with_capacity(argument_count as usize); |
||||||
|
for _ in 0..argument_count { |
||||||
|
arguments.push(context.vm.pop()); |
||||||
|
} |
||||||
|
arguments.reverse(); |
||||||
|
|
||||||
|
let func = context.vm.pop(); |
||||||
|
let this = context.vm.pop(); |
||||||
|
|
||||||
|
let object = match func { |
||||||
|
JsValue::Object(ref object) if object.is_callable() => object.clone(), |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("not a callable function") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let result = object.__call__(&this, &arguments, context)?; |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CallSpread; |
||||||
|
|
||||||
|
impl Operation for CallSpread { |
||||||
|
const NAME: &'static str = "CallSpread"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CallSpread"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
// Get the arguments that are stored as an array object on the stack.
|
||||||
|
let arguments_array = context.vm.pop(); |
||||||
|
let arguments_array_object = arguments_array |
||||||
|
.as_object() |
||||||
|
.expect("arguments array in call spread function must be an object"); |
||||||
|
let arguments = arguments_array_object |
||||||
|
.borrow() |
||||||
|
.properties() |
||||||
|
.dense_indexed_properties() |
||||||
|
.expect("arguments array in call spread function must be dense") |
||||||
|
.clone(); |
||||||
|
|
||||||
|
let func = context.vm.pop(); |
||||||
|
let this = context.vm.pop(); |
||||||
|
|
||||||
|
let object = match func { |
||||||
|
JsValue::Object(ref object) if object.is_callable() => object.clone(), |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("not a callable function") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
let result = object.__call__(&this, &arguments, context)?; |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `ConcatToString` implements the Opcode Operation for `Opcode::ConcatToString`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Concat multiple stack objects into a string.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ConcatToString; |
||||||
|
|
||||||
|
impl Operation for ConcatToString { |
||||||
|
const NAME: &'static str = "ConcatToString"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ConcatToString"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value_count = context.vm.read::<u32>(); |
||||||
|
let mut strings = Vec::with_capacity(value_count as usize); |
||||||
|
for _ in 0..value_count { |
||||||
|
strings.push(context.vm.pop().to_string(context)?); |
||||||
|
} |
||||||
|
strings.reverse(); |
||||||
|
let s = JsString::concat_array( |
||||||
|
&strings |
||||||
|
.iter() |
||||||
|
.map(JsString::as_slice) |
||||||
|
.collect::<Vec<&[u16]>>(), |
||||||
|
); |
||||||
|
context.vm.push(s); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `CopyDataProperties` implements the Opcode Operation for `Opcode::CopyDataProperties`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Copy all properties of one object to another object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CopyDataProperties; |
||||||
|
|
||||||
|
impl Operation for CopyDataProperties { |
||||||
|
const NAME: &'static str = "CopyDataProperties"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CopyDataProperties"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let excluded_key_count = context.vm.read::<u32>(); |
||||||
|
let excluded_key_count_computed = context.vm.read::<u32>(); |
||||||
|
let mut excluded_keys = Vec::with_capacity(excluded_key_count as usize); |
||||||
|
for _ in 0..excluded_key_count { |
||||||
|
let key = context.vm.pop(); |
||||||
|
excluded_keys.push( |
||||||
|
key.to_property_key(context) |
||||||
|
.expect("key must be property key"), |
||||||
|
); |
||||||
|
} |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = value.as_object().expect("not an object"); |
||||||
|
let source = context.vm.pop(); |
||||||
|
for _ in 0..excluded_key_count_computed { |
||||||
|
let key = context.vm.pop(); |
||||||
|
excluded_keys.push( |
||||||
|
key.to_property_key(context) |
||||||
|
.expect("key must be property key"), |
||||||
|
); |
||||||
|
} |
||||||
|
object.copy_data_properties(&source, excluded_keys, context)?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `DefineClassGetterByName` implements the Opcode Operation for `Opcode::DefineClassGetterByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class getter by name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassGetterByName; |
||||||
|
|
||||||
|
impl Operation for DefineClassGetterByName { |
||||||
|
const NAME: &'static str = "DefineClassGetterByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassGetterByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let set = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::set) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_get(Some(value)) |
||||||
|
.maybe_set(set) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefineClassGetterByValue` implements the Opcode Operation for `Opcode::DefineClassGetterByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class getter by value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassGetterByValue; |
||||||
|
|
||||||
|
impl Operation for DefineClassGetterByValue { |
||||||
|
const NAME: &'static str = "DefineClassGetterByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassGetterByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let name = key.to_property_key(context)?; |
||||||
|
let set = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::set) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_get(Some(value)) |
||||||
|
.maybe_set(set) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,90 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `DefineClassMethodByName` implements the Opcode Operation for `Opcode::DefineClassMethodByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class method by name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassMethodByName; |
||||||
|
|
||||||
|
impl Operation for DefineClassMethodByName { |
||||||
|
const NAME: &'static str = "DefineClassMethodByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassMethodByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context.interner().resolve_expect(name.sym()); |
||||||
|
object.__define_own_property__( |
||||||
|
name.into_common::<JsString>(false).into(), |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.value(value) |
||||||
|
.writable(true) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefineClassMethodByValue` implements the Opcode Operation for `Opcode::DefineClassMethodByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class method by value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassMethodByValue; |
||||||
|
|
||||||
|
impl Operation for DefineClassMethodByValue { |
||||||
|
const NAME: &'static str = "DefineClassMethodByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassMethodByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let key = key.to_property_key(context)?; |
||||||
|
object.__define_own_property__( |
||||||
|
key, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.value(value) |
||||||
|
.writable(true) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
pub(crate) mod getter; |
||||||
|
pub(crate) mod method; |
||||||
|
pub(crate) mod setter; |
||||||
|
|
||||||
|
pub(crate) use getter::*; |
||||||
|
pub(crate) use method::*; |
||||||
|
pub(crate) use setter::*; |
@ -0,0 +1,96 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `DefineClassSetterByName` implements the Opcode Operation for `Opcode::DefineClassSetterByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class setter by name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassSetterByName; |
||||||
|
|
||||||
|
impl Operation for DefineClassSetterByName { |
||||||
|
const NAME: &'static str = "DefineClassSetterByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassSetterByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let get = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::get) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_set(Some(value)) |
||||||
|
.maybe_get(get) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefineClassSetterByValue` implements the Opcode Operation for `Opcode::DefineClassSetterByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a class setter by value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineClassSetterByValue; |
||||||
|
|
||||||
|
impl Operation for DefineClassSetterByValue { |
||||||
|
const NAME: &'static str = "DefineClassSetterByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineClassSetterByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
value |
||||||
|
.as_object() |
||||||
|
.expect("method must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("method must be function object") |
||||||
|
.set_home_object(object.clone()); |
||||||
|
let name = key.to_property_key(context)?; |
||||||
|
let get = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::get) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_set(Some(value)) |
||||||
|
.maybe_get(get) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,140 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) mod class; |
||||||
|
pub(crate) mod own_property; |
||||||
|
|
||||||
|
pub(crate) use class::*; |
||||||
|
pub(crate) use own_property::*; |
||||||
|
|
||||||
|
/// `DefVar` implements the Opcode Operation for `Opcode::DefVar`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Declare `var` type variable.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefVar; |
||||||
|
|
||||||
|
impl Operation for DefVar { |
||||||
|
const NAME: &'static str = "DefVar"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefVar"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
|
||||||
|
if binding_locator.is_global() { |
||||||
|
let key = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.into_common(false); |
||||||
|
context.global_bindings_mut().entry(key).or_insert( |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.value(JsValue::Undefined) |
||||||
|
.writable(true) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
); |
||||||
|
} else { |
||||||
|
context.realm.environments.put_value_if_uninitialized( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
JsValue::Undefined, |
||||||
|
); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefInitVar` implements the Opcode Operation for `Opcode::DefInitVar`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Declare and initialize a function argument.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefInitVar; |
||||||
|
|
||||||
|
impl Operation for DefInitVar { |
||||||
|
const NAME: &'static str = "DefInitVar"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefInitVar"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
binding_locator.throw_mutate_immutable(context)?; |
||||||
|
|
||||||
|
if binding_locator.is_global() { |
||||||
|
let key = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
crate::object::internal_methods::global::global_set_no_receiver(&key, value, context)?; |
||||||
|
} else { |
||||||
|
context.realm.environments.put_value( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
value, |
||||||
|
); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefLet` implements the Opcode Operation for `Opcode::DefLet`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Declare `let` type variable.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefLet; |
||||||
|
|
||||||
|
impl Operation for DefLet { |
||||||
|
const NAME: &'static str = "DefLet"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefLet"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
context.realm.environments.put_value( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
JsValue::Undefined, |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
macro_rules! implement_declaritives { |
||||||
|
($name:ident, $doc_string:literal) => { |
||||||
|
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] |
||||||
|
#[doc= "\n"] |
||||||
|
#[doc="Operation:\n"] |
||||||
|
#[doc= concat!(" - ", $doc_string)] |
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct $name; |
||||||
|
|
||||||
|
impl Operation for $name { |
||||||
|
const NAME: &'static str = stringify!($name); |
||||||
|
const INSTRUCTION: &'static str = stringify!("INST - " + $name); |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
context.realm.environments.put_value( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
value, |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
implement_declaritives!(DefInitLet, "Declare and initialize `let` type variable"); |
||||||
|
implement_declaritives!(DefInitConst, "Declare and initialize `const` type variable"); |
||||||
|
implement_declaritives!(DefInitArg, "Declare and initialize function arguments"); |
@ -0,0 +1,79 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `DefineOwnPropertyByName` implements the Opcode Operation for `Opcode::DefineOwnPropertyByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a own property of an object by name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineOwnPropertyByName; |
||||||
|
|
||||||
|
impl Operation for DefineOwnPropertyByName { |
||||||
|
const NAME: &'static str = "DefineOwnPropertyByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false); |
||||||
|
object.__define_own_property__( |
||||||
|
name.into(), |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.value(value) |
||||||
|
.writable(true) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DefineOwnPropertyByValue` implements the Opcode Operation for `Opcode::DefineOwnPropertyByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Defines a own property of an object by value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DefineOwnPropertyByValue; |
||||||
|
|
||||||
|
impl Operation for DefineOwnPropertyByValue { |
||||||
|
const NAME: &'static str = "DefineOwnPropertyByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DefineOwnPropertyByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
let key = key.to_property_key(context)?; |
||||||
|
object.__define_own_property__( |
||||||
|
key, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.value(value) |
||||||
|
.writable(true) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,63 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `DeletePropertyByName` implements the Opcode Operation for `Opcode::DeletePropertyByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Deletes a property by name of an object
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DeletePropertyByName; |
||||||
|
|
||||||
|
impl Operation for DeletePropertyByName { |
||||||
|
const NAME: &'static str = "DeletePropertyByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DeletePropertyByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let key = context.vm.frame().code.names[index as usize]; |
||||||
|
let key = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(key.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let result = object.to_object(context)?.__delete__(&key, context)?; |
||||||
|
if !result && context.vm.frame().code.strict { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("Cannot delete property") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DeletePropertyByValue` implements the Opcode Operation for `Opcode::DeletePropertyByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Deletes a property by value of an object
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DeletePropertyByValue; |
||||||
|
|
||||||
|
impl Operation for DeletePropertyByValue { |
||||||
|
const NAME: &'static str = "DeletePropertyByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DeletePropertyByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let result = object |
||||||
|
.to_object(context)? |
||||||
|
.__delete__(&key.to_property_key(context)?, context)?; |
||||||
|
if !result && context.vm.frame().code.strict { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("Cannot delete property") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Dup` implements the Opcode Operation for `Opcode::Dup`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a copy of the top value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Dup; |
||||||
|
|
||||||
|
impl Operation for Dup { |
||||||
|
const NAME: &'static str = "Dup"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Dup"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
context.vm.push(value.clone()); |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,275 @@ |
|||||||
|
use crate::{ |
||||||
|
environments::EnvironmentSlots, |
||||||
|
error::JsNativeError, |
||||||
|
vm::{code_block::initialize_instance_elements, opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `This` implements the Opcode Operation for `Opcode::This`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pushes `this` value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct This; |
||||||
|
|
||||||
|
impl Operation for This { |
||||||
|
const NAME: &'static str = "This"; |
||||||
|
const INSTRUCTION: &'static str = "INST - This"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let env = context.realm.environments.get_this_environment(); |
||||||
|
match env { |
||||||
|
EnvironmentSlots::Function(env) => context.vm.push(env.borrow().get_this_binding()?), |
||||||
|
EnvironmentSlots::Global => { |
||||||
|
let this = context.realm.global_object(); |
||||||
|
context.vm.push(this.clone()); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Super` implements the Opcode Operation for `Opcode::Super`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pushes the current `super` value to the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Super; |
||||||
|
|
||||||
|
impl Operation for Super { |
||||||
|
const NAME: &'static str = "Super"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Super"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let home = { |
||||||
|
let env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super access must be in a function environment"); |
||||||
|
let env = env.borrow(); |
||||||
|
let this = env.get_this_binding()?; |
||||||
|
let function_object = env.function_object().borrow(); |
||||||
|
let function = function_object |
||||||
|
.as_function() |
||||||
|
.expect("must be function object"); |
||||||
|
|
||||||
|
function.get_home_object().or(this.as_object()).cloned() |
||||||
|
}; |
||||||
|
|
||||||
|
if let Some(home) = home { |
||||||
|
if let Some(proto) = home.__get_prototype_of__(context)? { |
||||||
|
context.vm.push(JsValue::from(proto)); |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::Null); |
||||||
|
} |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::Null); |
||||||
|
}; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SuperCall` implements the Opcode Operation for `Opcode::SuperCall`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Execute the `super()` method.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SuperCall; |
||||||
|
|
||||||
|
impl Operation for SuperCall { |
||||||
|
const NAME: &'static str = "SuperCall"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SuperCall"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let argument_count = context.vm.read::<u32>(); |
||||||
|
let mut arguments = Vec::with_capacity(argument_count as usize); |
||||||
|
for _ in 0..argument_count { |
||||||
|
arguments.push(context.vm.pop()); |
||||||
|
} |
||||||
|
arguments.reverse(); |
||||||
|
|
||||||
|
let (new_target, active_function) = { |
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
let this_env_borrow = this_env.borrow(); |
||||||
|
let new_target = this_env_borrow |
||||||
|
.new_target() |
||||||
|
.expect("must have new target") |
||||||
|
.clone(); |
||||||
|
let active_function = this_env.borrow().function_object().clone(); |
||||||
|
(new_target, active_function) |
||||||
|
}; |
||||||
|
let super_constructor = active_function |
||||||
|
.__get_prototype_of__(context) |
||||||
|
.expect("function object must have prototype") |
||||||
|
.expect("function object must have prototype"); |
||||||
|
|
||||||
|
if !super_constructor.is_constructor() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("super constructor object must be constructor") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
let result = super_constructor.__construct__(&arguments, &new_target, context)?; |
||||||
|
|
||||||
|
initialize_instance_elements(&result, &active_function, context)?; |
||||||
|
|
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
|
||||||
|
if !this_env.borrow_mut().bind_this_value(&result) { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message("this already initialized") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SuperCallSpread` implements the Opcode Operation for `Opcode::SuperCallSpread`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Execute the `super()` method where the arguments contain spreads.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SuperCallSpread; |
||||||
|
|
||||||
|
impl Operation for SuperCallSpread { |
||||||
|
const NAME: &'static str = "SuperCallWithRest"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SuperCallWithRest"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
// Get the arguments that are stored as an array object on the stack.
|
||||||
|
let arguments_array = context.vm.pop(); |
||||||
|
let arguments_array_object = arguments_array |
||||||
|
.as_object() |
||||||
|
.expect("arguments array in call spread function must be an object"); |
||||||
|
let arguments = arguments_array_object |
||||||
|
.borrow() |
||||||
|
.properties() |
||||||
|
.dense_indexed_properties() |
||||||
|
.expect("arguments array in call spread function must be dense") |
||||||
|
.clone(); |
||||||
|
|
||||||
|
let (new_target, active_function) = { |
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
let this_env_borrow = this_env.borrow(); |
||||||
|
let new_target = this_env_borrow |
||||||
|
.new_target() |
||||||
|
.expect("must have new target") |
||||||
|
.clone(); |
||||||
|
let active_function = this_env.borrow().function_object().clone(); |
||||||
|
(new_target, active_function) |
||||||
|
}; |
||||||
|
let super_constructor = active_function |
||||||
|
.__get_prototype_of__(context) |
||||||
|
.expect("function object must have prototype") |
||||||
|
.expect("function object must have prototype"); |
||||||
|
|
||||||
|
if !super_constructor.is_constructor() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("super constructor object must be constructor") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
let result = super_constructor.__construct__(&arguments, &new_target, context)?; |
||||||
|
|
||||||
|
initialize_instance_elements(&result, &active_function, context)?; |
||||||
|
|
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
|
||||||
|
if !this_env.borrow_mut().bind_this_value(&result) { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message("this already initialized") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SuperCallDerived` implements the Opcode Operation for `Opcode::SuperCallDerived`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Execute the `super()` method when no constructor of the class is defined.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SuperCallDerived; |
||||||
|
|
||||||
|
impl Operation for SuperCallDerived { |
||||||
|
const NAME: &'static str = "SuperCallDerived"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SuperCallDerived"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let argument_count = context.vm.frame().arg_count; |
||||||
|
let mut arguments = Vec::with_capacity(argument_count); |
||||||
|
for _ in 0..argument_count { |
||||||
|
arguments.push(context.vm.pop()); |
||||||
|
} |
||||||
|
arguments.reverse(); |
||||||
|
|
||||||
|
let (new_target, active_function) = { |
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
let this_env_borrow = this_env.borrow(); |
||||||
|
let new_target = this_env_borrow |
||||||
|
.new_target() |
||||||
|
.expect("must have new target") |
||||||
|
.clone(); |
||||||
|
let active_function = this_env.borrow().function_object().clone(); |
||||||
|
(new_target, active_function) |
||||||
|
}; |
||||||
|
let super_constructor = active_function |
||||||
|
.__get_prototype_of__(context) |
||||||
|
.expect("function object must have prototype") |
||||||
|
.expect("function object must have prototype"); |
||||||
|
|
||||||
|
if !super_constructor.is_constructor() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("super constructor object must be constructor") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
let result = super_constructor.__construct__(&arguments, &new_target, context)?; |
||||||
|
|
||||||
|
initialize_instance_elements(&result, &active_function, context)?; |
||||||
|
|
||||||
|
let this_env = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
.expect("super call must be in function environment"); |
||||||
|
if !this_env.borrow_mut().bind_this_value(&result) { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message("this already initialized") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,226 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{ |
||||||
|
async_generator::{AsyncGenerator, AsyncGeneratorState}, |
||||||
|
iterable::IteratorRecord, |
||||||
|
}, |
||||||
|
error::JsNativeError, |
||||||
|
vm::{ |
||||||
|
call_frame::{FinallyReturn, GeneratorResumeKind}, |
||||||
|
opcode::Operation, |
||||||
|
ShouldExit, |
||||||
|
}, |
||||||
|
Context, JsError, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) mod yield_stm; |
||||||
|
|
||||||
|
pub(crate) use yield_stm::*; |
||||||
|
|
||||||
|
/// `GeneratorNext` implements the Opcode Operation for `Opcode::GeneratorNext`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Resumes the current generator function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GeneratorNext; |
||||||
|
|
||||||
|
impl Operation for GeneratorNext { |
||||||
|
const NAME: &'static str = "GeneratorNext"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GeneratorNext"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
match context.vm.frame().generator_resume_kind { |
||||||
|
GeneratorResumeKind::Normal => Ok(ShouldExit::False), |
||||||
|
GeneratorResumeKind::Throw => { |
||||||
|
let received = context.vm.pop(); |
||||||
|
Err(JsError::from_opaque(received)) |
||||||
|
} |
||||||
|
GeneratorResumeKind::Return => { |
||||||
|
let mut finally_left = false; |
||||||
|
|
||||||
|
while let Some(catch_addresses) = context.vm.frame().catch.last() { |
||||||
|
if let Some(finally_address) = catch_addresses.finally { |
||||||
|
let frame = context.vm.frame_mut(); |
||||||
|
frame.pc = finally_address as usize; |
||||||
|
frame.finally_return = FinallyReturn::Ok; |
||||||
|
frame.catch.pop(); |
||||||
|
finally_left = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
context.vm.frame_mut().catch.pop(); |
||||||
|
} |
||||||
|
|
||||||
|
if finally_left { |
||||||
|
return Ok(ShouldExit::False); |
||||||
|
} |
||||||
|
Ok(ShouldExit::True) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `AsyncGeneratorNext` implements the Opcode Operation for `Opcode::AsyncGeneratorNext`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Resumes the current generator function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct AsyncGeneratorNext; |
||||||
|
|
||||||
|
impl Operation for AsyncGeneratorNext { |
||||||
|
const NAME: &'static str = "AsyncGeneratorNext"; |
||||||
|
const INSTRUCTION: &'static str = "INST - AsyncGeneratorNext"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
|
||||||
|
if context.vm.frame().generator_resume_kind == GeneratorResumeKind::Throw { |
||||||
|
return Err(JsError::from_opaque(value)); |
||||||
|
} |
||||||
|
|
||||||
|
let completion = Ok(value); |
||||||
|
let generator_object = context |
||||||
|
.vm |
||||||
|
.frame() |
||||||
|
.async_generator |
||||||
|
.as_ref() |
||||||
|
.expect("must be in generator context here") |
||||||
|
.clone(); |
||||||
|
let next = generator_object |
||||||
|
.borrow_mut() |
||||||
|
.as_async_generator_mut() |
||||||
|
.expect("must be async generator object") |
||||||
|
.queue |
||||||
|
.pop_front() |
||||||
|
.expect("must have item in queue"); |
||||||
|
AsyncGenerator::complete_step(&next, completion, false, context); |
||||||
|
|
||||||
|
let mut generator_object_mut = generator_object.borrow_mut(); |
||||||
|
let gen = generator_object_mut |
||||||
|
.as_async_generator_mut() |
||||||
|
.expect("must be async generator object"); |
||||||
|
|
||||||
|
if let Some(next) = gen.queue.front() { |
||||||
|
let (completion, r#return) = &next.completion; |
||||||
|
if *r#return { |
||||||
|
let value = match completion { |
||||||
|
Ok(value) => value.clone(), |
||||||
|
Err(e) => e.clone().to_opaque(context), |
||||||
|
}; |
||||||
|
context.vm.push(value); |
||||||
|
context.vm.push(true); |
||||||
|
} else { |
||||||
|
context.vm.push(completion.clone()?); |
||||||
|
context.vm.push(false); |
||||||
|
} |
||||||
|
|
||||||
|
context.vm.push(false); |
||||||
|
} else { |
||||||
|
gen.state = AsyncGeneratorState::SuspendedYield; |
||||||
|
context.vm.push(true); |
||||||
|
context.vm.push(true); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GeneratorNextDelegate` implements the Opcode Operation for `Opcode::GeneratorNextDelegate`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Delegates the current generator function another generator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GeneratorNextDelegate; |
||||||
|
|
||||||
|
impl Operation for GeneratorNextDelegate { |
||||||
|
const NAME: &'static str = "GeneratorNextDelegate"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GeneratorNextDelegate"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let done_address = context.vm.read::<u32>(); |
||||||
|
let received = context.vm.pop(); |
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
|
||||||
|
match context.vm.frame().generator_resume_kind { |
||||||
|
GeneratorResumeKind::Normal => { |
||||||
|
let result = context.call(&next_method, &iterator.clone().into(), &[received])?; |
||||||
|
let result_object = result.as_object().ok_or_else(|| { |
||||||
|
JsNativeError::typ().with_message("generator next method returned non-object") |
||||||
|
})?; |
||||||
|
let done = result_object.get("done", context)?.to_boolean(); |
||||||
|
if done { |
||||||
|
context.vm.frame_mut().pc = done_address as usize; |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(value); |
||||||
|
return Ok(ShouldExit::False); |
||||||
|
} |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method.clone()); |
||||||
|
context.vm.push(done); |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::Yield) |
||||||
|
} |
||||||
|
GeneratorResumeKind::Throw => { |
||||||
|
let throw = iterator.get_method("throw", context)?; |
||||||
|
if let Some(throw) = throw { |
||||||
|
let result = throw.call(&iterator.clone().into(), &[received], context)?; |
||||||
|
let result_object = result.as_object().ok_or_else(|| { |
||||||
|
JsNativeError::typ() |
||||||
|
.with_message("generator throw method returned non-object") |
||||||
|
})?; |
||||||
|
let done = result_object.get("done", context)?.to_boolean(); |
||||||
|
if done { |
||||||
|
context.vm.frame_mut().pc = done_address as usize; |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(value); |
||||||
|
return Ok(ShouldExit::False); |
||||||
|
} |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method.clone()); |
||||||
|
context.vm.push(done); |
||||||
|
context.vm.push(value); |
||||||
|
return Ok(ShouldExit::Yield); |
||||||
|
} |
||||||
|
context.vm.frame_mut().pc = done_address as usize; |
||||||
|
let iterator_record = IteratorRecord::new(iterator.clone(), next_method, done); |
||||||
|
iterator_record.close(Ok(JsValue::Undefined), context)?; |
||||||
|
|
||||||
|
Err(JsNativeError::typ() |
||||||
|
.with_message("iterator does not have a throw method") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
GeneratorResumeKind::Return => { |
||||||
|
let r#return = iterator.get_method("return", context)?; |
||||||
|
if let Some(r#return) = r#return { |
||||||
|
let result = r#return.call(&iterator.clone().into(), &[received], context)?; |
||||||
|
let result_object = result.as_object().ok_or_else(|| { |
||||||
|
JsNativeError::typ() |
||||||
|
.with_message("generator return method returned non-object") |
||||||
|
})?; |
||||||
|
let done = result_object.get("done", context)?.to_boolean(); |
||||||
|
if done { |
||||||
|
context.vm.frame_mut().pc = done_address as usize; |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(value); |
||||||
|
return Ok(ShouldExit::True); |
||||||
|
} |
||||||
|
let value = result_object.get("value", context)?; |
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method.clone()); |
||||||
|
context.vm.push(done); |
||||||
|
context.vm.push(value); |
||||||
|
return Ok(ShouldExit::Yield); |
||||||
|
} |
||||||
|
context.vm.frame_mut().pc = done_address as usize; |
||||||
|
context.vm.push(received); |
||||||
|
Ok(ShouldExit::True) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Yield` implements the Opcode Operation for `Opcode::Yield`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Yield from the current execution.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Yield; |
||||||
|
|
||||||
|
impl Operation for Yield { |
||||||
|
const NAME: &'static str = "Yield"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Yield"; |
||||||
|
|
||||||
|
fn execute(_context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
Ok(ShouldExit::Yield) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{code_block::create_function_object, opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `GetFunction` implements the Opcode Operation for `Opcode::GetFunction`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get function from the pre-compiled inner functions.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetFunction; |
||||||
|
|
||||||
|
impl Operation for GetFunction { |
||||||
|
const NAME: &'static str = "GetFunction"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetFunction"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let code = context.vm.frame().code.functions[index as usize].clone(); |
||||||
|
let function = create_function_object(code, false, None, context); |
||||||
|
context.vm.push(function); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GetFunctionAsync` implements the Opcode Operation for `Opcode::GetFunctionAsync`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get async function from the pre-compiled inner functions.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetFunctionAsync; |
||||||
|
|
||||||
|
impl Operation for GetFunctionAsync { |
||||||
|
const NAME: &'static str = "GetFunctionAsync"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetFunctionAsync"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let code = context.vm.frame().code.functions[index as usize].clone(); |
||||||
|
let function = create_function_object(code, true, None, context); |
||||||
|
context.vm.push(function); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,44 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{code_block::create_generator_function_object, opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `GetGenerator` implements the Opcode Operation for `Opcode::GetGenerator`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get generator function from the pre-compiled inner functions.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetGenerator; |
||||||
|
|
||||||
|
impl Operation for GetGenerator { |
||||||
|
const NAME: &'static str = "GetGenerator"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetGenerator"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let code = context.vm.frame().code.functions[index as usize].clone(); |
||||||
|
let function = create_generator_function_object(code, false, context); |
||||||
|
context.vm.push(function); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GetGeneratorAsync` implements the Opcode Operation for `Opcode::GetGeneratorAsync`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get async generator function from the pre-compiled inner functions.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetGeneratorAsync; |
||||||
|
|
||||||
|
impl Operation for GetGeneratorAsync { |
||||||
|
const NAME: &'static str = "GetGeneratorAsync"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetGeneratorAsync"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let code = context.vm.frame().code.functions[index as usize].clone(); |
||||||
|
let function = create_generator_function_object(code, true, context); |
||||||
|
context.vm.push(function); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
pub(crate) mod function; |
||||||
|
pub(crate) mod generator; |
||||||
|
pub(crate) mod name; |
||||||
|
pub(crate) mod private; |
||||||
|
pub(crate) mod property; |
||||||
|
|
||||||
|
pub(crate) use function::*; |
||||||
|
pub(crate) use generator::*; |
||||||
|
pub(crate) use name::*; |
||||||
|
pub(crate) use private::*; |
||||||
|
pub(crate) use property::*; |
@ -0,0 +1,136 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
property::DescriptorKind, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `GetName` implements the Opcode Operation for `Opcode::GetName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Find a binding on the environment chain and push its value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetName; |
||||||
|
|
||||||
|
impl Operation for GetName { |
||||||
|
const NAME: &'static str = "GetName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
binding_locator.throw_mutate_immutable(context)?; |
||||||
|
|
||||||
|
let value = if binding_locator.is_global() { |
||||||
|
if let Some(value) = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_value_global_poisoned(binding_locator.name()) |
||||||
|
{ |
||||||
|
value |
||||||
|
} else { |
||||||
|
let key: JsString = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.into_common(false); |
||||||
|
match context.global_bindings_mut().get(&key) { |
||||||
|
Some(desc) => match desc.kind() { |
||||||
|
DescriptorKind::Data { |
||||||
|
value: Some(value), .. |
||||||
|
} => value.clone(), |
||||||
|
DescriptorKind::Accessor { get: Some(get), .. } if !get.is_undefined() => { |
||||||
|
let get = get.clone(); |
||||||
|
context.call(&get, &context.global_object().clone().into(), &[])? |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message(format!( |
||||||
|
"{} is not defined", |
||||||
|
key.to_std_string_escaped() |
||||||
|
)) |
||||||
|
.into()) |
||||||
|
} |
||||||
|
}, |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message(format!("{} is not defined", key.to_std_string_escaped())) |
||||||
|
.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} else if let Some(value) = context.realm.environments.get_value_optional( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
binding_locator.name(), |
||||||
|
) { |
||||||
|
value |
||||||
|
} else { |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.to_string(); |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message(format!("{name} is not initialized")) |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GetNameOrUndefined` implements the Opcode Operation for `Opcode::GetNameOrUndefined`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Find a binding on the environment chain and push its value. If the binding does not exist push undefined.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetNameOrUndefined; |
||||||
|
|
||||||
|
impl Operation for GetNameOrUndefined { |
||||||
|
const NAME: &'static str = "GetNameOrUndefined"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetNameOrUndefined"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
binding_locator.throw_mutate_immutable(context)?; |
||||||
|
let value = if binding_locator.is_global() { |
||||||
|
if let Some(value) = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_value_global_poisoned(binding_locator.name()) |
||||||
|
{ |
||||||
|
value |
||||||
|
} else { |
||||||
|
let key: JsString = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.into_common(false); |
||||||
|
match context.global_bindings_mut().get(&key) { |
||||||
|
Some(desc) => match desc.kind() { |
||||||
|
DescriptorKind::Data { |
||||||
|
value: Some(value), .. |
||||||
|
} => value.clone(), |
||||||
|
DescriptorKind::Accessor { get: Some(get), .. } if !get.is_undefined() => { |
||||||
|
let get = get.clone(); |
||||||
|
context.call(&get, &context.global_object().clone().into(), &[])? |
||||||
|
} |
||||||
|
_ => JsValue::undefined(), |
||||||
|
}, |
||||||
|
_ => JsValue::undefined(), |
||||||
|
} |
||||||
|
} |
||||||
|
} else if let Some(value) = context.realm.environments.get_value_optional( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
binding_locator.name(), |
||||||
|
) { |
||||||
|
value |
||||||
|
} else { |
||||||
|
JsValue::undefined() |
||||||
|
}; |
||||||
|
|
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
object::PrivateElement, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `GetPrivateField` implements the Opcode Operation for `Opcode::GetPrivateField`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get a private property by name from an object an push it on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetPrivateField; |
||||||
|
|
||||||
|
impl Operation for GetPrivateField { |
||||||
|
const NAME: &'static str = "GetPrivateField"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetPrivateField"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
if let Some(object) = value.as_object() { |
||||||
|
let object_borrow_mut = object.borrow(); |
||||||
|
if let Some(element) = object_borrow_mut.get_private_element(name.sym()) { |
||||||
|
match element { |
||||||
|
PrivateElement::Field(value) => context.vm.push(value), |
||||||
|
PrivateElement::Method(method) => context.vm.push(method.clone()), |
||||||
|
PrivateElement::Accessor { |
||||||
|
getter: Some(getter), |
||||||
|
setter: _, |
||||||
|
} => { |
||||||
|
let value = getter.call(&value, &[], context)?; |
||||||
|
context.vm.push(value); |
||||||
|
} |
||||||
|
PrivateElement::Accessor { .. } => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("private property was defined without a getter") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("private property does not exist") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot read private property from non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
use crate::{ |
||||||
|
property::PropertyKey, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `GetPropertyByName` implements the Opcode Operation for `Opcode::GetPropertyByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get a property by name from an object an push it on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetPropertyByName; |
||||||
|
|
||||||
|
impl Operation for GetPropertyByName { |
||||||
|
const NAME: &'static str = "GetPropertyName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetPropertyName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
|
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = if let Some(object) = value.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
value.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name: PropertyKey = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let result = object.get(name, context)?; |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GetPropertyByValue` implements the Opcode Operation for `Opcode::GetPropertyByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get a property by value from an object an push it on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetPropertyByValue; |
||||||
|
|
||||||
|
impl Operation for GetPropertyByValue { |
||||||
|
const NAME: &'static str = "GetPropertyByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetPropertyByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
let key = key.to_property_key(context)?; |
||||||
|
let value = object.get(key, context)?; |
||||||
|
|
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `GetPropertyByValuePush` implements the Opcode Operation for `Opcode::GetPropertyByValuePush`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get a property by value from an object an push the key and value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct GetPropertyByValuePush; |
||||||
|
|
||||||
|
impl Operation for GetPropertyByValuePush { |
||||||
|
const NAME: &'static str = "GetPropertyByValuePush"; |
||||||
|
const INSTRUCTION: &'static str = "INST - GetPropertyByValuePush"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
let property_key = key.to_property_key(context)?; |
||||||
|
let value = object.get(property_key, context)?; |
||||||
|
|
||||||
|
context.vm.push(key); |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,78 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::iterable::IteratorResult, |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `ForAwaitOfLoopIterate` implements the Opcode Operation for `Opcode::ForAwaitOfLoopIterator`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Move to the next value in a for await..of loop.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ForAwaitOfLoopIterate; |
||||||
|
|
||||||
|
impl Operation for ForAwaitOfLoopIterate { |
||||||
|
const NAME: &'static str = "ForAwaitOfLoopIterate"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ForAwaitOfLoopIterate"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let _done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let next_method_object = if let Some(object) = next_method.as_callable() { |
||||||
|
object |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("iterable next method not a function") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let next_result = next_method_object.call(&iterator, &[], context)?; |
||||||
|
context.vm.push(iterator); |
||||||
|
context.vm.push(next_method); |
||||||
|
context.vm.push(next_result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `ForAwaitOfLoopNext` implements the Opcode Operation for `Opcode::ForAwaitOfLoopNext`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get the value from a for await..of loop next result.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ForAwaitOfLoopNext; |
||||||
|
|
||||||
|
impl Operation for ForAwaitOfLoopNext { |
||||||
|
const NAME: &'static str = "ForAwaitOfLoopNext"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ForAwaitOfLoopNext"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
|
||||||
|
let next_result = context.vm.pop(); |
||||||
|
let next_result = if let Some(next_result) = next_result.as_object() { |
||||||
|
IteratorResult::new(next_result.clone()) |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("next value should be an object") |
||||||
|
.into()); |
||||||
|
}; |
||||||
|
|
||||||
|
if next_result.complete(context)? { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
context.vm.frame_mut().loop_env_stack_dec(); |
||||||
|
context.vm.frame_mut().try_env_stack_dec(); |
||||||
|
context.realm.environments.pop(); |
||||||
|
context.vm.push(true); |
||||||
|
} else { |
||||||
|
context.vm.push(false); |
||||||
|
let value = next_result.value(context)?; |
||||||
|
context.vm.push(value); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,86 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{iterable::IteratorRecord, ForInIterator}, |
||||||
|
error::JsNativeError, |
||||||
|
property::PropertyDescriptor, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `ForInLoopInitIterator` implements the Opcode Operation for `Opcode::ForInLoopInitIterator`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Initialize the iterator for a for..in loop or jump to after the loop if object is null or undefined.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ForInLoopInitIterator; |
||||||
|
|
||||||
|
impl Operation for ForInLoopInitIterator { |
||||||
|
const NAME: &'static str = "ForInLoopInitIterator"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ForInLoopInitIterator"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
|
||||||
|
let object = context.vm.pop(); |
||||||
|
if object.is_null_or_undefined() { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
return Ok(ShouldExit::False); |
||||||
|
} |
||||||
|
|
||||||
|
let object = object.to_object(context)?; |
||||||
|
let iterator = ForInIterator::create_for_in_iterator(JsValue::new(object), context); |
||||||
|
let next_method = iterator |
||||||
|
.get_property("next") |
||||||
|
.as_ref() |
||||||
|
.map(PropertyDescriptor::expect_value) |
||||||
|
.cloned() |
||||||
|
.ok_or_else(|| JsNativeError::typ().with_message("Could not find property `next`"))?; |
||||||
|
|
||||||
|
context.vm.push(iterator); |
||||||
|
context.vm.push(next_method); |
||||||
|
context.vm.push(false); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `ForInLoopNext` implements the Opcode Operation for `Opcode::ForInLoopNext`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Move to the next value in a for..in loop or jump to exit of the loop if done.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ForInLoopNext; |
||||||
|
|
||||||
|
impl Operation for ForInLoopNext { |
||||||
|
const NAME: &'static str = "ForInLoopInitIterator"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ForInLoopInitIterator"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
|
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
|
||||||
|
let iterator_record = IteratorRecord::new(iterator.clone(), next_method.clone(), done); |
||||||
|
if let Some(next) = iterator_record.step(context)? { |
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method); |
||||||
|
context.vm.push(done); |
||||||
|
let value = next.value(context)?; |
||||||
|
context.vm.push(value); |
||||||
|
} else { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
context.vm.frame_mut().loop_env_stack_dec(); |
||||||
|
context.vm.frame_mut().try_env_stack_dec(); |
||||||
|
context.realm.environments.pop(); |
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method); |
||||||
|
context.vm.push(done); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::iterable::IteratorHint, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `InitIterator` implements the Opcode Operation for `Opcode::InitIterator`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Initialize an iterator
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct InitIterator; |
||||||
|
|
||||||
|
impl Operation for InitIterator { |
||||||
|
const NAME: &'static str = "InitIterator"; |
||||||
|
const INSTRUCTION: &'static str = "INST - InitIterator"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let iterator = object.get_iterator(context, None, None)?; |
||||||
|
context.vm.push(iterator.iterator().clone()); |
||||||
|
context.vm.push(iterator.next_method().clone()); |
||||||
|
context.vm.push(iterator.done()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `InitIteratorAsync` implements the Opcode Operation for `Opcode::InitIteratorAsync`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Initialize an async iterator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct InitIteratorAsync; |
||||||
|
|
||||||
|
impl Operation for InitIteratorAsync { |
||||||
|
const NAME: &'static str = "InitIteratorAsync"; |
||||||
|
const INSTRUCTION: &'static str = "INST - InitIteratorAsync"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let iterator = object.get_iterator(context, Some(IteratorHint::Async), None)?; |
||||||
|
context.vm.push(iterator.iterator().clone()); |
||||||
|
context.vm.push(iterator.next_method().clone()); |
||||||
|
context.vm.push(iterator.done()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,109 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{iterable::IteratorRecord, Array}, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `IteratorNext` implements the Opcode Operation for `Opcode::IteratorNext`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Advance the iterator by one and put the value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct IteratorNext; |
||||||
|
|
||||||
|
impl Operation for IteratorNext { |
||||||
|
const NAME: &'static str = "IteratorNext"; |
||||||
|
const INSTRUCTION: &'static str = "INST - IteratorNext"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
|
||||||
|
let iterator_record = IteratorRecord::new(iterator.clone(), next_method.clone(), done); |
||||||
|
let next = iterator_record.step(context)?; |
||||||
|
|
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method); |
||||||
|
if let Some(next) = next { |
||||||
|
let value = next.value(context)?; |
||||||
|
context.vm.push(false); |
||||||
|
context.vm.push(value); |
||||||
|
} else { |
||||||
|
context.vm.push(true); |
||||||
|
context.vm.push(JsValue::undefined()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `IteratorClose` implements the Opcode Operation for `Opcode::IteratorClose`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Close an iterator
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct IteratorClose; |
||||||
|
|
||||||
|
impl Operation for IteratorClose { |
||||||
|
const NAME: &'static str = "IteratorClose"; |
||||||
|
const INSTRUCTION: &'static str = "INST - IteratorClose"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
if !done { |
||||||
|
let iterator_record = IteratorRecord::new(iterator.clone(), next_method, done); |
||||||
|
iterator_record.close(Ok(JsValue::Null), context)?; |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `IteratorToArray` implements the Opcode Operation for `Opcode::IteratorToArray`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Consume the iterator and construct and array with all the values.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct IteratorToArray; |
||||||
|
|
||||||
|
impl Operation for IteratorToArray { |
||||||
|
const NAME: &'static str = "IteratorToArray"; |
||||||
|
const INSTRUCTION: &'static str = "INST - IteratorToArray"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
|
||||||
|
let iterator_record = IteratorRecord::new(iterator.clone(), next_method.clone(), done); |
||||||
|
let mut values = Vec::new(); |
||||||
|
|
||||||
|
while let Some(result) = iterator_record.step(context)? { |
||||||
|
values.push(result.value(context)?); |
||||||
|
} |
||||||
|
|
||||||
|
let array = Array::create_array_from_list(values, context); |
||||||
|
|
||||||
|
context.vm.push(iterator.clone()); |
||||||
|
context.vm.push(next_method); |
||||||
|
context.vm.push(true); |
||||||
|
context.vm.push(array); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,76 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `LoopStart` implements the Opcode Operation for `Opcode::LoopStart`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push loop start marker.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LoopStart; |
||||||
|
|
||||||
|
impl Operation for LoopStart { |
||||||
|
const NAME: &'static str = "LoopStart"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LoopStart"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.frame_mut().loop_env_stack.push(0); |
||||||
|
context.vm.frame_mut().try_env_stack_loop_inc(); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `LoopContinue` implements the Opcode Operation for `Opcode::LoopContinue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Clean up environments when a loop continues.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LoopContinue; |
||||||
|
|
||||||
|
impl Operation for LoopContinue { |
||||||
|
const NAME: &'static str = "LoopContinue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LoopContinue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let env_num = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.last_mut() |
||||||
|
.expect("loop env stack entry must exist"); |
||||||
|
let env_num_copy = *env_num; |
||||||
|
*env_num = 0; |
||||||
|
for _ in 0..env_num_copy { |
||||||
|
context.realm.environments.pop(); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `LoopEnd` implements the Opcode Operation for `Opcode::LoopEnd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Clean up enviroments at the end of a lopp.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LoopEnd; |
||||||
|
|
||||||
|
impl Operation for LoopEnd { |
||||||
|
const NAME: &'static str = "LoopEnd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LoopEnd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let env_num = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.pop() |
||||||
|
.expect("loop env stack entry must exist"); |
||||||
|
for _ in 0..env_num { |
||||||
|
context.realm.environments.pop(); |
||||||
|
context.vm.frame_mut().try_env_stack_dec(); |
||||||
|
} |
||||||
|
context.vm.frame_mut().try_env_stack_loop_dec(); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
pub(crate) mod for_await; |
||||||
|
pub(crate) mod for_in; |
||||||
|
pub(crate) mod init; |
||||||
|
pub(crate) mod iterator; |
||||||
|
pub(crate) mod loop_ops; |
||||||
|
|
||||||
|
pub(crate) use for_await::*; |
||||||
|
pub(crate) use for_in::*; |
||||||
|
pub(crate) use init::*; |
||||||
|
pub(crate) use iterator::*; |
||||||
|
pub(crate) use loop_ops::*; |
@ -0,0 +1,64 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Jump` implements the Opcode Operation for `Opcode::Jump`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unconditional jump to address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Jump; |
||||||
|
|
||||||
|
impl Operation for Jump { |
||||||
|
const NAME: &'static str = "Jump"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Jump"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `JumpIfFalse` implements the Opcode Operation for `Opcode::JumpIfFalse`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Conditional jump to address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct JumpIfFalse; |
||||||
|
|
||||||
|
impl Operation for JumpIfFalse { |
||||||
|
const NAME: &'static str = "JumpIfFalse"; |
||||||
|
const INSTRUCTION: &'static str = "INST - JumpIfFalse"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
if !context.vm.pop().to_boolean() { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `JumpIfNotUndefined` implements the Opcode Operation for `Opcode::JumpIfNotUndefined`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Conditional jump to address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct JumpIfNotUndefined; |
||||||
|
|
||||||
|
impl Operation for JumpIfNotUndefined { |
||||||
|
const NAME: &'static str = "JumpIfNotUndefined"; |
||||||
|
const INSTRUCTION: &'static str = "INST - JumpIfNotUndefined"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
if !value.is_undefined() { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
context.vm.push(value); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,89 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `New` implements the Opcode Operation for `Opcode::New`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call construct on a function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct New; |
||||||
|
|
||||||
|
impl Operation for New { |
||||||
|
const NAME: &'static str = "New"; |
||||||
|
const INSTRUCTION: &'static str = "INST - New"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
let argument_count = context.vm.read::<u32>(); |
||||||
|
let mut arguments = Vec::with_capacity(argument_count as usize); |
||||||
|
for _ in 0..argument_count { |
||||||
|
arguments.push(context.vm.pop()); |
||||||
|
} |
||||||
|
arguments.reverse(); |
||||||
|
let func = context.vm.pop(); |
||||||
|
|
||||||
|
let result = func |
||||||
|
.as_constructor() |
||||||
|
.ok_or_else(|| { |
||||||
|
JsNativeError::typ() |
||||||
|
.with_message("not a constructor") |
||||||
|
.into() |
||||||
|
}) |
||||||
|
.and_then(|cons| cons.__construct__(&arguments, cons, context))?; |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `NewSpread` implements the Opcode Operation for `Opcode::NewSpread`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call construct on a function where the arguments contain spreads.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct NewSpread; |
||||||
|
|
||||||
|
impl Operation for NewSpread { |
||||||
|
const NAME: &'static str = "NewSpread"; |
||||||
|
const INSTRUCTION: &'static str = "INST - NewSpread"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if context.vm.stack_size_limit <= context.vm.stack.len() { |
||||||
|
return Err(JsNativeError::range() |
||||||
|
.with_message("Maximum call stack size exceeded") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
// Get the arguments that are stored as an array object on the stack.
|
||||||
|
let arguments_array = context.vm.pop(); |
||||||
|
let arguments_array_object = arguments_array |
||||||
|
.as_object() |
||||||
|
.expect("arguments array in call spread function must be an object"); |
||||||
|
let arguments = arguments_array_object |
||||||
|
.borrow() |
||||||
|
.properties() |
||||||
|
.dense_indexed_properties() |
||||||
|
.expect("arguments array in call spread function must be dense") |
||||||
|
.clone(); |
||||||
|
|
||||||
|
let func = context.vm.pop(); |
||||||
|
|
||||||
|
let result = func |
||||||
|
.as_constructor() |
||||||
|
.ok_or_else(|| { |
||||||
|
JsNativeError::typ() |
||||||
|
.with_message("not a constructor") |
||||||
|
.into() |
||||||
|
}) |
||||||
|
.and_then(|cons| cons.__construct__(&arguments, cons, context))?; |
||||||
|
|
||||||
|
context.vm.push(result); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,20 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Nop` implements the Opcode Operation for `Opcode::Nop`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - No-operation instruction, does nothing
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Nop; |
||||||
|
|
||||||
|
impl Operation for Nop { |
||||||
|
const NAME: &'static str = "Nop"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Nop"; |
||||||
|
|
||||||
|
fn execute(_context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Pop` implements the Opcode Operation for `Opcode::Pop`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pop the top value from the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Pop; |
||||||
|
|
||||||
|
impl Operation for Pop { |
||||||
|
const NAME: &'static str = "Pop"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Pop"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let _val = context.vm.pop(); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PopIfThrown` implements the Opcode Operation for `Opcode::PopIfThrown`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pop the top value from the stack if the last try block has thrown a value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PopIfThrown; |
||||||
|
|
||||||
|
impl Operation for PopIfThrown { |
||||||
|
const NAME: &'static str = "PopIfThrown"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PopIfThrown"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let frame = context.vm.frame_mut(); |
||||||
|
if frame.thrown { |
||||||
|
frame.thrown = false; |
||||||
|
context.vm.pop(); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PopEnvironment` implements the Opcode Operation for `Opcode::PopEnvironment`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pop the current environment.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PopEnvironment; |
||||||
|
|
||||||
|
impl Operation for PopEnvironment { |
||||||
|
const NAME: &'static str = "PopEnvironment"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PopEnvironment"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.realm.environments.pop(); |
||||||
|
context.vm.frame_mut().loop_env_stack_dec(); |
||||||
|
context.vm.frame_mut().try_env_stack_dec(); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PopReturnAdd` implements the Opcode Operation for `Opcode::PopReturnAdd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Add one to the pop on return count.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PopOnReturnAdd; |
||||||
|
|
||||||
|
impl Operation for PopOnReturnAdd { |
||||||
|
const NAME: &'static str = "PopOnReturnAdd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PopOnReturnAdd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.frame_mut().pop_on_return += 1; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PopOnReturnSub` implements the Opcode Operation for `Opcode::PopOnReturnSub`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Subtract one from the pop on return count.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PopOnReturnSub; |
||||||
|
|
||||||
|
impl Operation for PopOnReturnSub { |
||||||
|
const NAME: &'static str = "PopOnReturnSub"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PopOnReturnSub"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.frame_mut().pop_on_return -= 1; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,80 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, FinallyReturn, ShouldExit}, |
||||||
|
Context, JsError, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `FinallyStart` implements the Opcode Operation for `Opcode::FinallyStart`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Start of a finally block.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct FinallyStart; |
||||||
|
|
||||||
|
impl Operation for FinallyStart { |
||||||
|
const NAME: &'static str = "FinallyStart"; |
||||||
|
const INSTRUCTION: &'static str = "INST - FinallyStart"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
*context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.finally_jump |
||||||
|
.last_mut() |
||||||
|
.expect("finally jump must exist here") = None; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `FinallyEnd` implements the Opcode Operation for `Opcode::FinallyEnd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - End of a finally block.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct FinallyEnd; |
||||||
|
|
||||||
|
impl Operation for FinallyEnd { |
||||||
|
const NAME: &'static str = "FinallyEnd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - FinallyEnd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.finally_jump |
||||||
|
.pop() |
||||||
|
.expect("finally jump must exist here"); |
||||||
|
match context.vm.frame_mut().finally_return { |
||||||
|
FinallyReturn::None => { |
||||||
|
if let Some(address) = address { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
FinallyReturn::Ok => Ok(ShouldExit::True), |
||||||
|
FinallyReturn::Err => Err(JsError::from_opaque(context.vm.pop())), |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `FinallySetJump` implements the Opcode Operation for `Opcode::FinallySetJump`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set the address for a finally jump.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct FinallySetJump; |
||||||
|
|
||||||
|
impl Operation for FinallySetJump { |
||||||
|
const NAME: &'static str = "FinallySetJump"; |
||||||
|
const INSTRUCTION: &'static str = "INST - FinallySetJump"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
*context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.finally_jump |
||||||
|
.last_mut() |
||||||
|
.expect("finally jump must exist here") = Some(address); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::{iterable::IteratorRecord, Array}, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushNewArray` implements the Opcode Operation for `Opcode::PushNewArray`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push an empty array value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushNewArray; |
||||||
|
|
||||||
|
impl Operation for PushNewArray { |
||||||
|
const NAME: &'static str = "PushNewArray"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushNewArray"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let array = Array::array_create(0, None, context) |
||||||
|
.expect("Array creation with 0 length should never fail"); |
||||||
|
context.vm.push(array); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushValueToArray` implements the Opcode Operation for `Opcode::PushValueToArray`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a value to an array.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushValueToArray; |
||||||
|
|
||||||
|
impl Operation for PushValueToArray { |
||||||
|
const NAME: &'static str = "PushValueToArray"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushValueToArray"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let array = context.vm.pop(); |
||||||
|
let o = array.as_object().expect("should be an object"); |
||||||
|
let len = o |
||||||
|
.length_of_array_like(context) |
||||||
|
.expect("should have 'length' property"); |
||||||
|
o.create_data_property_or_throw(len, value, context) |
||||||
|
.expect("should be able to create new data property"); |
||||||
|
context.vm.push(array); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushEllisionToArray` implements the Opcode Operation for `Opcode::PushEllisionToArray`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push an empty element/hole to an array.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushElisionToArray; |
||||||
|
|
||||||
|
impl Operation for PushElisionToArray { |
||||||
|
const NAME: &'static str = "PushElisionToArray"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushElisionToArray"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let array = context.vm.pop(); |
||||||
|
let o = array.as_object().expect("should always be an object"); |
||||||
|
|
||||||
|
let len = o |
||||||
|
.length_of_array_like(context) |
||||||
|
.expect("arrays should always have a 'length' property"); |
||||||
|
|
||||||
|
o.set("length", len + 1, true, context)?; |
||||||
|
context.vm.push(array); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushIteratorToArray` implements the Opcode Operation for `Opcode::PushIteratorToArray`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push all iterator values to an array.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushIteratorToArray; |
||||||
|
|
||||||
|
impl Operation for PushIteratorToArray { |
||||||
|
const NAME: &'static str = "PushIteratorToArray"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushIteratorToArray"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let done = context |
||||||
|
.vm |
||||||
|
.pop() |
||||||
|
.as_boolean() |
||||||
|
.expect("iterator [[Done]] was not a boolean"); |
||||||
|
let next_method = context.vm.pop(); |
||||||
|
let iterator = context.vm.pop(); |
||||||
|
let iterator = iterator.as_object().expect("iterator was not an object"); |
||||||
|
let array = context.vm.pop(); |
||||||
|
|
||||||
|
let iterator = IteratorRecord::new(iterator.clone(), next_method, done); |
||||||
|
while let Some(next) = iterator.step(context)? { |
||||||
|
Array::push(&array, &[next.value(context)?], context)?; |
||||||
|
} |
||||||
|
|
||||||
|
context.vm.push(array); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,85 @@ |
|||||||
|
use crate::{ |
||||||
|
object::JsFunction, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushClassField` implements the Opcode Operation for `Opcode::PushClassField`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a field to a class.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassField; |
||||||
|
|
||||||
|
impl Operation for PushClassField { |
||||||
|
const NAME: &'static str = "PushClassField"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassField"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let field_function_value = context.vm.pop(); |
||||||
|
let field_name_value = context.vm.pop(); |
||||||
|
let class_value = context.vm.pop(); |
||||||
|
|
||||||
|
let field_name_key = field_name_value.to_property_key(context)?; |
||||||
|
let field_function_object = field_function_value |
||||||
|
.as_object() |
||||||
|
.expect("field value must be function object"); |
||||||
|
let mut field_function_object_borrow = field_function_object.borrow_mut(); |
||||||
|
let field_function = field_function_object_borrow |
||||||
|
.as_function_mut() |
||||||
|
.expect("field value must be function object"); |
||||||
|
let class_object = class_value |
||||||
|
.as_object() |
||||||
|
.expect("class must be function object"); |
||||||
|
field_function.set_home_object(class_object.clone()); |
||||||
|
class_object |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object") |
||||||
|
.push_field( |
||||||
|
field_name_key, |
||||||
|
JsFunction::from_object_unchecked(field_function_object.clone()), |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushClassFieldPrivate` implements the Opcode Operation for `Opcode::PushClassFieldPrivate`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a private field to the class.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassFieldPrivate; |
||||||
|
|
||||||
|
impl Operation for PushClassFieldPrivate { |
||||||
|
const NAME: &'static str = "PushClassFieldPrivate"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassFieldPrivate"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let field_function_value = context.vm.pop(); |
||||||
|
let class_value = context.vm.pop(); |
||||||
|
|
||||||
|
let field_function_object = field_function_value |
||||||
|
.as_object() |
||||||
|
.expect("field value must be function object"); |
||||||
|
let mut field_function_object_borrow = field_function_object.borrow_mut(); |
||||||
|
let field_function = field_function_object_borrow |
||||||
|
.as_function_mut() |
||||||
|
.expect("field value must be function object"); |
||||||
|
let class_object = class_value |
||||||
|
.as_object() |
||||||
|
.expect("class must be function object"); |
||||||
|
field_function.set_home_object(class_object.clone()); |
||||||
|
class_object |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object") |
||||||
|
.push_field_private( |
||||||
|
name.sym(), |
||||||
|
JsFunction::from_object_unchecked(field_function_object.clone()), |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,65 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::function::{ConstructorKind, Function}, |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) mod field; |
||||||
|
pub(crate) mod private; |
||||||
|
|
||||||
|
pub(crate) use field::*; |
||||||
|
pub(crate) use private::*; |
||||||
|
|
||||||
|
/// `PushClassPrototype` implements the Opcode Operation for `Opcode::PushClassPrototype`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Get the prototype of a superclass and push it on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassPrototype; |
||||||
|
|
||||||
|
impl Operation for PushClassPrototype { |
||||||
|
const NAME: &'static str = "PushClassPrototype"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassPrototype"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let superclass = context.vm.pop(); |
||||||
|
|
||||||
|
if let Some(superclass) = superclass.as_constructor() { |
||||||
|
let proto = superclass.get("prototype", context)?; |
||||||
|
if !proto.is_object() && !proto.is_null() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("superclass prototype must be an object or null") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
let class = context.vm.pop(); |
||||||
|
{ |
||||||
|
let class_object = class.as_object().expect("class must be object"); |
||||||
|
class_object.set_prototype(Some(superclass.clone())); |
||||||
|
|
||||||
|
let mut class_object_mut = class_object.borrow_mut(); |
||||||
|
let class_function = class_object_mut |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object"); |
||||||
|
if let Function::Ordinary { |
||||||
|
constructor_kind, .. |
||||||
|
} = class_function |
||||||
|
{ |
||||||
|
*constructor_kind = ConstructorKind::Derived; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
context.vm.push(class); |
||||||
|
context.vm.push(proto); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} else if superclass.is_null() { |
||||||
|
context.vm.push(JsValue::Null); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} else { |
||||||
|
Err(JsNativeError::typ() |
||||||
|
.with_message("superclass must be a constructor") |
||||||
|
.into()) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,101 @@ |
|||||||
|
use crate::{ |
||||||
|
object::PrivateElement, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushClassPrivateMethod` implements the Opcode Operation for `Opcode::PushClassPrivateMethod`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a private method to the class.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassPrivateMethod; |
||||||
|
|
||||||
|
impl Operation for PushClassPrivateMethod { |
||||||
|
const NAME: &'static str = "PushClassPrivateMethod"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassPrivateMethod"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let method = context.vm.pop(); |
||||||
|
let method_object = method.as_callable().expect("method must be callable"); |
||||||
|
let class = context.vm.pop(); |
||||||
|
class |
||||||
|
.as_object() |
||||||
|
.expect("class must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object") |
||||||
|
.push_private_method(name.sym(), PrivateElement::Method(method_object.clone())); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushClassPrivateGetter` implements the Opcode Operation for `Opcode::PushClassPrivateGetter`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a private getter to the class.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassPrivateGetter; |
||||||
|
|
||||||
|
impl Operation for PushClassPrivateGetter { |
||||||
|
const NAME: &'static str = "PushClassPrivateGetter"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassPrivateGetter"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let getter = context.vm.pop(); |
||||||
|
let getter_object = getter.as_callable().expect("getter must be callable"); |
||||||
|
let class = context.vm.pop(); |
||||||
|
class |
||||||
|
.as_object() |
||||||
|
.expect("class must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object") |
||||||
|
.push_private_method( |
||||||
|
name.sym(), |
||||||
|
PrivateElement::Accessor { |
||||||
|
getter: Some(getter_object.clone()), |
||||||
|
setter: None, |
||||||
|
}, |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushClassPrivateSetter` implements the Opcode Operation for `Opcode::PushClassPrivateSetter`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a private setter to the class.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushClassPrivateSetter; |
||||||
|
|
||||||
|
impl Operation for PushClassPrivateSetter { |
||||||
|
const NAME: &'static str = "PushClassPrivateSetter"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushClassPrivateSetter"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let setter = context.vm.pop(); |
||||||
|
let setter_object = setter.as_callable().expect("getter must be callable"); |
||||||
|
let class = context.vm.pop(); |
||||||
|
class |
||||||
|
.as_object() |
||||||
|
.expect("class must be function object") |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object") |
||||||
|
.push_private_method( |
||||||
|
name.sym(), |
||||||
|
PrivateElement::Accessor { |
||||||
|
getter: None, |
||||||
|
setter: Some(setter_object.clone()), |
||||||
|
}, |
||||||
|
); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,56 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushDeclarativeEnvironment` implements the Opcode Operation for `Opcode::PushDeclarativeEnvironment`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a declarative environment
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushDeclarativeEnvironment; |
||||||
|
|
||||||
|
impl Operation for PushDeclarativeEnvironment { |
||||||
|
const NAME: &'static str = "PushDeclarativeEnvironment"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushDeclarativeEnvironment"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let num_bindings = context.vm.read::<u32>(); |
||||||
|
let compile_environments_index = context.vm.read::<u32>(); |
||||||
|
let compile_environment = context.vm.frame().code.compile_environments |
||||||
|
[compile_environments_index as usize] |
||||||
|
.clone(); |
||||||
|
context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.push_declarative(num_bindings as usize, compile_environment); |
||||||
|
context.vm.frame_mut().loop_env_stack_inc(); |
||||||
|
context.vm.frame_mut().try_env_stack_inc(); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `PushFunctionEnvironment` implements the Opcode Operation for `Opcode::PushFunctionEnvironment`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push a function environment.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushFunctionEnvironment; |
||||||
|
|
||||||
|
impl Operation for PushFunctionEnvironment { |
||||||
|
const NAME: &'static str = "PushFunctionEnvironment"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushFunctionEnvironment"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let num_bindings = context.vm.read::<u32>(); |
||||||
|
let compile_environments_index = context.vm.read::<u32>(); |
||||||
|
let compile_environment = context.vm.frame().code.compile_environments |
||||||
|
[compile_environments_index as usize] |
||||||
|
.clone(); |
||||||
|
context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.push_function_inherit(num_bindings as usize, compile_environment); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushLiteral` implements the Opcode Operation for `Opcode::PushLiteral`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push literal value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushLiteral; |
||||||
|
|
||||||
|
impl Operation for PushLiteral { |
||||||
|
const NAME: &'static str = "PushLiteral"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushLiteral"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>() as usize; |
||||||
|
let value = context.vm.frame().code.literals[index].clone(); |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,67 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
pub(crate) mod array; |
||||||
|
pub(crate) mod class; |
||||||
|
pub(crate) mod environment; |
||||||
|
pub(crate) mod literal; |
||||||
|
pub(crate) mod new_target; |
||||||
|
pub(crate) mod numbers; |
||||||
|
pub(crate) mod object; |
||||||
|
|
||||||
|
pub(crate) use array::*; |
||||||
|
pub(crate) use class::*; |
||||||
|
pub(crate) use environment::*; |
||||||
|
pub(crate) use literal::*; |
||||||
|
pub(crate) use new_target::*; |
||||||
|
pub(crate) use numbers::*; |
||||||
|
pub(crate) use object::*; |
||||||
|
|
||||||
|
macro_rules! implement_push_generics { |
||||||
|
($name:ident, $push_value:expr, $doc_string:literal) => { |
||||||
|
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] |
||||||
|
#[doc= "\n"] |
||||||
|
#[doc="Operation:\n"] |
||||||
|
#[doc= concat!(" - ", $doc_string)] |
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct $name; |
||||||
|
|
||||||
|
impl Operation for $name { |
||||||
|
const NAME: &'static str = stringify!($name); |
||||||
|
const INSTRUCTION: &'static str = stringify!("INST - " + $name); |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.push($push_value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
implement_push_generics!( |
||||||
|
PushUndefined, |
||||||
|
JsValue::undefined(), |
||||||
|
"Push integer `undefined` on the stack." |
||||||
|
); |
||||||
|
implement_push_generics!( |
||||||
|
PushNull, |
||||||
|
JsValue::null(), |
||||||
|
"Push integer `null` on the stack." |
||||||
|
); |
||||||
|
implement_push_generics!(PushTrue, true, "Push integer `true` on the stack."); |
||||||
|
implement_push_generics!(PushFalse, false, "Push integer `false` on the stack."); |
||||||
|
implement_push_generics!(PushZero, 0, "Push integer `0` on the stack."); |
||||||
|
implement_push_generics!(PushOne, 1, "Push integer `1` on the stack."); |
||||||
|
implement_push_generics!(PushNaN, JsValue::nan(), "Push integer `NaN` on the stack."); |
||||||
|
implement_push_generics!( |
||||||
|
PushPositiveInfinity, |
||||||
|
JsValue::positive_infinity(), |
||||||
|
"Push integer `Infinity` on the stack." |
||||||
|
); |
||||||
|
implement_push_generics!( |
||||||
|
PushNegativeInfinity, |
||||||
|
JsValue::negative_infinity(), |
||||||
|
"Push integer `-Infinity` on the stack." |
||||||
|
); |
@ -0,0 +1,34 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushNewTarget` implements the Opcode Operation for `Opcode::PushNewTarget`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push the current new target to the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushNewTarget; |
||||||
|
|
||||||
|
impl Operation for PushNewTarget { |
||||||
|
const NAME: &'static str = "PushNewTarget"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushNewTarget"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if let Some(env) = context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.get_this_environment() |
||||||
|
.as_function_slots() |
||||||
|
{ |
||||||
|
if let Some(new_target) = env.borrow().new_target() { |
||||||
|
context.vm.push(new_target.clone()); |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::undefined()); |
||||||
|
} |
||||||
|
} else { |
||||||
|
context.vm.push(JsValue::undefined()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
macro_rules! implement_push_numbers_with_conversion { |
||||||
|
($name:ident, $num_type:ty, $doc_string:literal) => { |
||||||
|
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] |
||||||
|
#[doc= "\n"] |
||||||
|
#[doc="Operation:\n"] |
||||||
|
#[doc= concat!(" - ", $doc_string)] |
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct $name; |
||||||
|
|
||||||
|
impl Operation for $name { |
||||||
|
const NAME: &'static str = stringify!($name); |
||||||
|
const INSTRUCTION: &'static str = stringify!("INST - " + $name); |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.read::<$num_type>(); |
||||||
|
context.vm.push(i32::from(value)); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
macro_rules! implement_push_numbers_no_conversion { |
||||||
|
($name:ident, $num_type:ty, $doc_string:literal) => { |
||||||
|
#[doc= concat!("`", stringify!($name), "` implements the OpCode Operation for `Opcode::", stringify!($name), "`\n")] |
||||||
|
#[doc= "\n"] |
||||||
|
#[doc="Operation:\n"] |
||||||
|
#[doc= concat!(" - ", $doc_string)] |
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct $name; |
||||||
|
|
||||||
|
impl Operation for $name { |
||||||
|
const NAME: &'static str = stringify!($name); |
||||||
|
const INSTRUCTION: &'static str = stringify!("INST - " + $name); |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.read::<$num_type>(); |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
implement_push_numbers_with_conversion!(PushInt8, i8, "Push `i8` value on the stack"); |
||||||
|
implement_push_numbers_with_conversion!(PushInt16, i16, "Push `i16` value on the stack"); |
||||||
|
|
||||||
|
implement_push_numbers_no_conversion!(PushInt32, i32, "Push `i32` value on the stack"); |
||||||
|
implement_push_numbers_no_conversion!(PushRational, f64, "Push `f64` value on the stack"); |
@ -0,0 +1,21 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `PushEmptyObject` implements the Opcode Operation for `Opcode::PushEmptyObject`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Push empty object `{}` value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct PushEmptyObject; |
||||||
|
|
||||||
|
impl Operation for PushEmptyObject { |
||||||
|
const NAME: &'static str = "PushEmptyObject"; |
||||||
|
const INSTRUCTION: &'static str = "INST - PushEmptyObject"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.push(context.construct_object()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,23 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `RequireObjectCoercible` implements the Opcode Operation for `Opcode::RequireObjectCoercible`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call `RequireObjectCoercible` on the stack value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct RequireObjectCoercible; |
||||||
|
|
||||||
|
impl Operation for RequireObjectCoercible { |
||||||
|
const NAME: &'static str = "RequireObjectCoercible"; |
||||||
|
const INSTRUCTION: &'static str = "INST - RequireObjectCoercible"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.require_object_coercible()?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,62 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::Array, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `RestParameterInit` implements the Opcode Operation for `Opcode::RestParameterInit`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Initialize the rest parameter value of a function from the remaining arguments.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct RestParameterInit; |
||||||
|
|
||||||
|
impl Operation for RestParameterInit { |
||||||
|
const NAME: &'static str = "FunctionRestParameter"; |
||||||
|
const INSTRUCTION: &'static str = "INST - FunctionRestParameter"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let arg_count = context.vm.frame().arg_count; |
||||||
|
let param_count = context.vm.frame().param_count; |
||||||
|
if arg_count >= param_count { |
||||||
|
let rest_count = arg_count - param_count + 1; |
||||||
|
let mut args = Vec::with_capacity(rest_count); |
||||||
|
for _ in 0..rest_count { |
||||||
|
args.push(context.vm.pop()); |
||||||
|
} |
||||||
|
let array: _ = Array::create_array_from_list(args, context); |
||||||
|
|
||||||
|
context.vm.push(array); |
||||||
|
} else { |
||||||
|
context.vm.pop(); |
||||||
|
|
||||||
|
let array = |
||||||
|
Array::array_create(0, None, context).expect("could not create an empty array"); |
||||||
|
context.vm.push(array); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `RestParameterPop` implements the Opcode Operation for `Opcode::RestParameterPop`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pop the remaining arguments of a function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct RestParameterPop; |
||||||
|
|
||||||
|
impl Operation for RestParameterPop { |
||||||
|
const NAME: &'static str = "RestParameterPop"; |
||||||
|
const INSTRUCTION: &'static str = "INST - RestParameterPop"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let arg_count = context.vm.frame().arg_count; |
||||||
|
let param_count = context.vm.frame().param_count; |
||||||
|
if arg_count > param_count { |
||||||
|
for _ in 0..(arg_count - param_count) { |
||||||
|
context.vm.pop(); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,52 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, FinallyReturn, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Return` implements the Opcode Operation for `Opcode::Return`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Return from a function.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Return; |
||||||
|
|
||||||
|
impl Operation for Return { |
||||||
|
const NAME: &'static str = "Return"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Return"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
if let Some(finally_address) = context.vm.frame().catch.last().and_then(|c| c.finally) { |
||||||
|
let frame = context.vm.frame_mut(); |
||||||
|
frame.pc = finally_address as usize; |
||||||
|
frame.finally_return = FinallyReturn::Ok; |
||||||
|
frame.catch.pop(); |
||||||
|
let try_stack_entry = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.try_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
for _ in 0..try_stack_entry.num_env { |
||||||
|
context.realm.environments.pop(); |
||||||
|
} |
||||||
|
let mut num_env = try_stack_entry.num_env; |
||||||
|
for _ in 0..try_stack_entry.num_loop_stack_entries { |
||||||
|
num_env -= context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
} |
||||||
|
*context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.last_mut() |
||||||
|
.expect("must exist") -= num_env; |
||||||
|
} else { |
||||||
|
return Ok(ShouldExit::True); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
use crate::{ |
||||||
|
object::{JsObject, ObjectData}, |
||||||
|
property::PropertyDescriptorBuilder, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `SetClassProtoType` implements the Opcode Operation for `Opcode::SetClassPrototype`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set the prototype of a class object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetClassPrototype; |
||||||
|
|
||||||
|
impl Operation for SetClassPrototype { |
||||||
|
const NAME: &'static str = "SetClassPrototype"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetClassPrototype"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let prototype_value = context.vm.pop(); |
||||||
|
let prototype = match &prototype_value { |
||||||
|
JsValue::Object(proto) => Some(proto.clone()), |
||||||
|
JsValue::Null => None, |
||||||
|
JsValue::Undefined => Some( |
||||||
|
context |
||||||
|
.intrinsics() |
||||||
|
.constructors() |
||||||
|
.object() |
||||||
|
.prototype |
||||||
|
.clone(), |
||||||
|
), |
||||||
|
_ => unreachable!(), |
||||||
|
}; |
||||||
|
|
||||||
|
let proto = JsObject::from_proto_and_data(prototype, ObjectData::ordinary()); |
||||||
|
let class = context.vm.pop(); |
||||||
|
|
||||||
|
{ |
||||||
|
let class_object = class.as_object().expect("class must be object"); |
||||||
|
class_object |
||||||
|
.define_property_or_throw( |
||||||
|
"prototype", |
||||||
|
PropertyDescriptorBuilder::new() |
||||||
|
.value(proto.clone()) |
||||||
|
.writable(false) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(false), |
||||||
|
context, |
||||||
|
) |
||||||
|
.expect("cannot fail per spec"); |
||||||
|
let mut class_object_mut = class_object.borrow_mut(); |
||||||
|
let class_function = class_object_mut |
||||||
|
.as_function_mut() |
||||||
|
.expect("class must be function object"); |
||||||
|
class_function.set_home_object(proto.clone()); |
||||||
|
} |
||||||
|
|
||||||
|
proto |
||||||
|
.__define_own_property__( |
||||||
|
"constructor".into(), |
||||||
|
PropertyDescriptorBuilder::new() |
||||||
|
.value(class) |
||||||
|
.writable(true) |
||||||
|
.enumerable(false) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
) |
||||||
|
.expect("cannot fail per spec"); |
||||||
|
|
||||||
|
context.vm.push(proto); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `SetHomeObject` implements the Opcode Operation for `Opcode::SetHomeObject`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set home object internal slot of a function object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetHomeObject; |
||||||
|
|
||||||
|
impl Operation for SetHomeObject { |
||||||
|
const NAME: &'static str = "SetHomeObject"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetHomeObject"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let function = context.vm.pop(); |
||||||
|
let function_object = function.as_object().expect("must be object"); |
||||||
|
let home = context.vm.pop(); |
||||||
|
let home_object = home.as_object().expect("must be object"); |
||||||
|
|
||||||
|
function_object |
||||||
|
.borrow_mut() |
||||||
|
.as_function_mut() |
||||||
|
.expect("must be function object") |
||||||
|
.set_home_object(home_object.clone()); |
||||||
|
|
||||||
|
context.vm.push(home); |
||||||
|
context.vm.push(function); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
pub(crate) mod class_prototype; |
||||||
|
pub(crate) mod home_object; |
||||||
|
pub(crate) mod name; |
||||||
|
pub(crate) mod private; |
||||||
|
pub(crate) mod property; |
||||||
|
|
||||||
|
pub(crate) use class_prototype::*; |
||||||
|
pub(crate) use home_object::*; |
||||||
|
pub(crate) use name::*; |
||||||
|
pub(crate) use private::*; |
||||||
|
pub(crate) use property::*; |
@ -0,0 +1,77 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `SetName` implements the Opcode Operation for `Opcode::SetName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Find a binding on the environment chain and assign its value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetName; |
||||||
|
|
||||||
|
impl Operation for SetName { |
||||||
|
const NAME: &'static str = "SetName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let binding_locator = context.vm.frame().code.bindings[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
binding_locator.throw_mutate_immutable(context)?; |
||||||
|
|
||||||
|
if binding_locator.is_global() { |
||||||
|
if !context |
||||||
|
.realm |
||||||
|
.environments |
||||||
|
.put_value_global_poisoned(binding_locator.name(), &value) |
||||||
|
{ |
||||||
|
let key: JsString = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
.into_common(false); |
||||||
|
let exists = context.global_bindings_mut().contains_key(&key); |
||||||
|
|
||||||
|
if !exists && context.vm.frame().code.strict { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message(format!( |
||||||
|
"assignment to undeclared variable {}", |
||||||
|
key.to_std_string_escaped() |
||||||
|
)) |
||||||
|
.into()); |
||||||
|
} |
||||||
|
|
||||||
|
let success = crate::object::internal_methods::global::global_set_no_receiver( |
||||||
|
&key.clone().into(), |
||||||
|
value, |
||||||
|
context, |
||||||
|
)?; |
||||||
|
|
||||||
|
if !success && context.vm.frame().code.strict { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message(format!( |
||||||
|
"cannot set non-writable property: {}", |
||||||
|
key.to_std_string_escaped() |
||||||
|
)) |
||||||
|
.into()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if !context.realm.environments.put_value_if_initialized( |
||||||
|
binding_locator.environment_index(), |
||||||
|
binding_locator.binding_index(), |
||||||
|
binding_locator.name(), |
||||||
|
value, |
||||||
|
) { |
||||||
|
return Err(JsNativeError::reference() |
||||||
|
.with_message(format!( |
||||||
|
"cannot access '{}' before initialization", |
||||||
|
context |
||||||
|
.interner() |
||||||
|
.resolve_expect(binding_locator.name().sym()) |
||||||
|
)) |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,187 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
object::PrivateElement, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `AssignPrivateField` implements the Opcode Operation for `Opcode::AssignPrivateField`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Assign the value of a private property of an object by it's name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct AssignPrivateField; |
||||||
|
|
||||||
|
impl Operation for AssignPrivateField { |
||||||
|
const NAME: &'static str = "AssignPrivateField"; |
||||||
|
const INSTRUCTION: &'static str = "INST - AssignPrivateField"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
if let Some(object) = object.as_object() { |
||||||
|
let mut object_borrow_mut = object.borrow_mut(); |
||||||
|
match object_borrow_mut.get_private_element(name.sym()) { |
||||||
|
Some(PrivateElement::Field(_)) => { |
||||||
|
object_borrow_mut.set_private_element(name.sym(), PrivateElement::Field(value)); |
||||||
|
} |
||||||
|
Some(PrivateElement::Method(_)) => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("private method is not writable") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Some(PrivateElement::Accessor { |
||||||
|
setter: Some(setter), |
||||||
|
.. |
||||||
|
}) => { |
||||||
|
let setter = setter.clone(); |
||||||
|
drop(object_borrow_mut); |
||||||
|
setter.call(&object.clone().into(), &[value], context)?; |
||||||
|
} |
||||||
|
None => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("private field not defined") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
_ => { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("private field defined without a setter") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot set private property on non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPrivateField` implements the Opcode Operation for `Opcode::SetPrivateField`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set a private property of a class constructor by it's name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPrivateField; |
||||||
|
|
||||||
|
impl Operation for SetPrivateField { |
||||||
|
const NAME: &'static str = "SetPrivateValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPrivateValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
if let Some(object) = object.as_object() { |
||||||
|
let mut object_borrow_mut = object.borrow_mut(); |
||||||
|
if let Some(PrivateElement::Accessor { |
||||||
|
getter: _, |
||||||
|
setter: Some(setter), |
||||||
|
}) = object_borrow_mut.get_private_element(name.sym()) |
||||||
|
{ |
||||||
|
let setter = setter.clone(); |
||||||
|
drop(object_borrow_mut); |
||||||
|
setter.call(&object.clone().into(), &[value], context)?; |
||||||
|
} else { |
||||||
|
object_borrow_mut.set_private_element(name.sym(), PrivateElement::Field(value)); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot set private property on non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPrivateMethod` implements the Opcode Operation for `Opcode::SetPrivateMethod`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set a private method of a class constructor by it's name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPrivateMethod; |
||||||
|
|
||||||
|
impl Operation for SetPrivateMethod { |
||||||
|
const NAME: &'static str = "SetPrivateMethod"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPrivateMethod"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.as_callable().expect("method must be callable"); |
||||||
|
let object = context.vm.pop(); |
||||||
|
if let Some(object) = object.as_object() { |
||||||
|
let mut object_borrow_mut = object.borrow_mut(); |
||||||
|
object_borrow_mut |
||||||
|
.set_private_element(name.sym(), PrivateElement::Method(value.clone())); |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot set private setter on non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPrivateSetter` implements the Opcode Operation for `Opcode::SetPrivateSetter`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set a private setter property of a class constructor by it's name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPrivateSetter; |
||||||
|
|
||||||
|
impl Operation for SetPrivateSetter { |
||||||
|
const NAME: &'static str = "SetPrivateSetter"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPrivateSetter"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.as_callable().expect("setter must be callable"); |
||||||
|
let object = context.vm.pop(); |
||||||
|
if let Some(object) = object.as_object() { |
||||||
|
let mut object_borrow_mut = object.borrow_mut(); |
||||||
|
object_borrow_mut.set_private_element_setter(name.sym(), value.clone()); |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot set private setter on non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPrivateGetter` implements the Opcode Operation for `Opcode::SetPrivateGetter`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Set a private getter property of a class constructor by it's name.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPrivateGetter; |
||||||
|
|
||||||
|
impl Operation for SetPrivateGetter { |
||||||
|
const NAME: &'static str = "SetPrivateGetter"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPrivateGetter"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.as_callable().expect("getter must be callable"); |
||||||
|
let object = context.vm.pop(); |
||||||
|
if let Some(object) = object.as_object() { |
||||||
|
let mut object_borrow_mut = object.borrow_mut(); |
||||||
|
object_borrow_mut.set_private_element_getter(name.sym(), value.clone()); |
||||||
|
} else { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("cannot set private getter on non-object") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,220 @@ |
|||||||
|
use crate::{ |
||||||
|
property::{PropertyDescriptor, PropertyKey}, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsString, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a property by name of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertyByName; |
||||||
|
|
||||||
|
impl Operation for SetPropertyByName { |
||||||
|
const NAME: &'static str = "SetPropertyByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertyByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
|
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name: PropertyKey = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
|
||||||
|
object.set(name, value, context.vm.frame().code.strict, context)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPropertyByValue` implements the Opcode Operation for `Opcode::SetPropertyByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a property by value of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertyByValue; |
||||||
|
|
||||||
|
impl Operation for SetPropertyByValue { |
||||||
|
const NAME: &'static str = "SetPropertyByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertyByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let object = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = if let Some(object) = object.as_object() { |
||||||
|
object.clone() |
||||||
|
} else { |
||||||
|
object.to_object(context)? |
||||||
|
}; |
||||||
|
|
||||||
|
let key = key.to_property_key(context)?; |
||||||
|
object.set(key, value, context.vm.frame().code.strict, context)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPropertyGetterByName` implements the Opcode Operation for `Opcode::SetPropertyGetterByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a getter property by name of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertyGetterByName; |
||||||
|
|
||||||
|
impl Operation for SetPropertyGetterByName { |
||||||
|
const NAME: &'static str = "SetPropertyGetterByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertyGetterByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let set = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::set) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_get(Some(value)) |
||||||
|
.maybe_set(set) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPropertyGetterByValue` implements the Opcode Operation for `Opcode::SetPropertyGetterByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a getter property by value of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertyGetterByValue; |
||||||
|
|
||||||
|
impl Operation for SetPropertyGetterByValue { |
||||||
|
const NAME: &'static str = "SetPropertyGetterByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertyGetterByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
let name = key.to_property_key(context)?; |
||||||
|
let set = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::set) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_get(Some(value)) |
||||||
|
.maybe_set(set) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPropertySetterByName` implements the Opcode Operation for `Opcode::SetPropertySetterByName`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a setter property by name of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertySetterByName; |
||||||
|
|
||||||
|
impl Operation for SetPropertySetterByName { |
||||||
|
const NAME: &'static str = "SetPropertySetterByName"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertySetterByName"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let index = context.vm.read::<u32>(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
let name = context.vm.frame().code.names[index as usize]; |
||||||
|
let name = context |
||||||
|
.interner() |
||||||
|
.resolve_expect(name.sym()) |
||||||
|
.into_common::<JsString>(false) |
||||||
|
.into(); |
||||||
|
let get = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::get) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_set(Some(value)) |
||||||
|
.maybe_get(get) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `SetPropertySetterByValue` implements the Opcode Operation for `Opcode::SetPropertySetterByValue`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Sets a setter property by value of an object.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct SetPropertySetterByValue; |
||||||
|
|
||||||
|
impl Operation for SetPropertySetterByValue { |
||||||
|
const NAME: &'static str = "SetPropertySetterByValue"; |
||||||
|
const INSTRUCTION: &'static str = "INST - SetPropertySetterByValue"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = context.vm.pop(); |
||||||
|
let object = context.vm.pop(); |
||||||
|
let object = object.to_object(context)?; |
||||||
|
let name = key.to_property_key(context)?; |
||||||
|
let get = object |
||||||
|
.__get_own_property__(&name, context)? |
||||||
|
.as_ref() |
||||||
|
.and_then(PropertyDescriptor::get) |
||||||
|
.cloned(); |
||||||
|
object.__define_own_property__( |
||||||
|
name, |
||||||
|
PropertyDescriptor::builder() |
||||||
|
.maybe_set(Some(value)) |
||||||
|
.maybe_get(get) |
||||||
|
.enumerable(true) |
||||||
|
.configurable(true) |
||||||
|
.build(), |
||||||
|
context, |
||||||
|
)?; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Swap` implements the Opcode Operation for `Opcode::Swap`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Swap the top two values on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Swap; |
||||||
|
|
||||||
|
impl Operation for Swap { |
||||||
|
const NAME: &'static str = "Swap"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Swap"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let first = context.vm.pop(); |
||||||
|
let second = context.vm.pop(); |
||||||
|
|
||||||
|
context.vm.push(first); |
||||||
|
context.vm.push(second); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,49 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Case` implements the Opcode Operation for `Opcode::Case`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pop the two values of the stack, strict equal compares the two values,
|
||||||
|
/// if true jumps to address, otherwise push the second pop'ed value.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Case; |
||||||
|
|
||||||
|
impl Operation for Case { |
||||||
|
const NAME: &'static str = "Case"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Case"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let address = context.vm.read::<u32>(); |
||||||
|
let cond = context.vm.pop(); |
||||||
|
let value = context.vm.pop(); |
||||||
|
|
||||||
|
if value.strict_equals(&cond) { |
||||||
|
context.vm.frame_mut().pc = address as usize; |
||||||
|
} else { |
||||||
|
context.vm.push(value); |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Default` implements the Opcode Operation for `Opcode::Default`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pops the top of stack and jump to address.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Default; |
||||||
|
|
||||||
|
impl Operation for Default { |
||||||
|
const NAME: &'static str = "Default"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Default"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let exit = context.vm.read::<u32>(); |
||||||
|
let _val = context.vm.pop(); |
||||||
|
context.vm.frame_mut().pc = exit as usize; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsError, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Throw` implements the Opcode Operation for `Opcode::Throw`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Throw exception.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Throw; |
||||||
|
|
||||||
|
impl Operation for Throw { |
||||||
|
const NAME: &'static str = "Throw"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Throw"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
Err(JsError::from_opaque(value)) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,41 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `ToBoolean` implements the Opcode Operation for `Opcode::ToBoolean`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Pops value converts it to boolean and pushes it back.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ToBoolean; |
||||||
|
|
||||||
|
impl Operation for ToBoolean { |
||||||
|
const NAME: &'static str = "ToBoolean"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ToBoolean"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
context.vm.push(value.to_boolean()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `ToPropertyKey` implements the Opcode Operation for `Opcode::ToPropertyKey`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Call `ToPropertyKey` on the value on the stack.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ToPropertyKey; |
||||||
|
|
||||||
|
impl Operation for ToPropertyKey { |
||||||
|
const NAME: &'static str = "ToPropertyKey"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ToPropertyKey"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let key = value.to_property_key(context)?; |
||||||
|
context.vm.push(key); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,164 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, CatchAddresses, FinallyReturn, ShouldExit, TryStackEntry}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `TryStart` implements the Opcode Operation for `Opcode::TryStart`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Start of a try block.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct TryStart; |
||||||
|
|
||||||
|
impl Operation for TryStart { |
||||||
|
const NAME: &'static str = "TryStart"; |
||||||
|
const INSTRUCTION: &'static str = "INST - TryStart"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let next = context.vm.read::<u32>(); |
||||||
|
let finally = context.vm.read::<u32>(); |
||||||
|
let finally = if finally == 0 { None } else { Some(finally) }; |
||||||
|
context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.catch |
||||||
|
.push(CatchAddresses { next, finally }); |
||||||
|
context.vm.frame_mut().finally_jump.push(None); |
||||||
|
context.vm.frame_mut().finally_return = FinallyReturn::None; |
||||||
|
context.vm.frame_mut().try_env_stack.push(TryStackEntry { |
||||||
|
num_env: 0, |
||||||
|
num_loop_stack_entries: 0, |
||||||
|
}); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `TryEnd` implements the Opcode Operation for `Opcode::TryEnd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - End of a try block
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct TryEnd; |
||||||
|
|
||||||
|
impl Operation for TryEnd { |
||||||
|
const NAME: &'static str = "TryEnd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - TryEnd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.frame_mut().catch.pop(); |
||||||
|
let try_stack_entry = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.try_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
for _ in 0..try_stack_entry.num_env { |
||||||
|
context.realm.environments.pop(); |
||||||
|
} |
||||||
|
let mut num_env = try_stack_entry.num_env; |
||||||
|
for _ in 0..try_stack_entry.num_loop_stack_entries { |
||||||
|
num_env -= context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
} |
||||||
|
*context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.last_mut() |
||||||
|
.expect("must exist") -= num_env; |
||||||
|
context.vm.frame_mut().finally_return = FinallyReturn::None; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `CatchStart` implements the Opcode Operation for `Opcode::CatchStart`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Start of a catch block.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CatchStart; |
||||||
|
|
||||||
|
impl Operation for CatchStart { |
||||||
|
const NAME: &'static str = "CatchStart"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CatchStart"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let finally = context.vm.read::<u32>(); |
||||||
|
context.vm.frame_mut().catch.push(CatchAddresses { |
||||||
|
next: finally, |
||||||
|
finally: Some(finally), |
||||||
|
}); |
||||||
|
context.vm.frame_mut().try_env_stack.push(TryStackEntry { |
||||||
|
num_env: 0, |
||||||
|
num_loop_stack_entries: 0, |
||||||
|
}); |
||||||
|
context.vm.frame_mut().thrown = false; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `CatchEnd` implements the Opcode Operation for `Opcode::CatchEnd`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - End of a catch block.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CatchEnd; |
||||||
|
|
||||||
|
impl Operation for CatchEnd { |
||||||
|
const NAME: &'static str = "CatchEnd"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CatchEnd"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
context.vm.frame_mut().catch.pop(); |
||||||
|
let try_stack_entry = context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.try_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
for _ in 0..try_stack_entry.num_env { |
||||||
|
context.realm.environments.pop(); |
||||||
|
} |
||||||
|
let mut num_env = try_stack_entry.num_env; |
||||||
|
for _ in 0..try_stack_entry.num_loop_stack_entries { |
||||||
|
num_env -= context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.pop() |
||||||
|
.expect("must exist"); |
||||||
|
} |
||||||
|
*context |
||||||
|
.vm |
||||||
|
.frame_mut() |
||||||
|
.loop_env_stack |
||||||
|
.last_mut() |
||||||
|
.expect("must exist") -= num_env; |
||||||
|
context.vm.frame_mut().finally_return = FinallyReturn::None; |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `CatchEnd2` implements the Opcode Operation for `Opcode::CatchEnd2`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - End of a catch block
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct CatchEnd2; |
||||||
|
|
||||||
|
impl Operation for CatchEnd2 { |
||||||
|
const NAME: &'static str = "CatchEnd2"; |
||||||
|
const INSTRUCTION: &'static str = "INST - CatchEnd2"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let frame = context.vm.frame_mut(); |
||||||
|
if frame.finally_return == FinallyReturn::Err { |
||||||
|
frame.finally_return = FinallyReturn::None; |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
use crate::{ |
||||||
|
value::Numeric, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsBigInt, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Dec` implements the Opcode Operation for `Opcode::Dec`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `--` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Dec; |
||||||
|
|
||||||
|
impl Operation for Dec { |
||||||
|
const NAME: &'static str = "Dec"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Dec"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
match value.to_numeric(context)? { |
||||||
|
Numeric::Number(number) => context.vm.push(number - 1f64), |
||||||
|
Numeric::BigInt(bigint) => { |
||||||
|
context.vm.push(JsBigInt::sub(&bigint, &JsBigInt::one())); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `DecPost` implements the Opcode Operation for `Opcode::DecPost`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary postfix `--` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct DecPost; |
||||||
|
|
||||||
|
impl Operation for DecPost { |
||||||
|
const NAME: &'static str = "DecPost"; |
||||||
|
const INSTRUCTION: &'static str = "INST - DecPost"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.to_numeric(context)?; |
||||||
|
context.vm.push(value.clone()); |
||||||
|
match value { |
||||||
|
Numeric::Number(number) => context.vm.push(number - 1f64), |
||||||
|
Numeric::BigInt(bigint) => { |
||||||
|
context.vm.push(JsBigInt::sub(&bigint, &JsBigInt::one())); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
use crate::{ |
||||||
|
value::Numeric, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsBigInt, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Inc` implements the Opcode Operation for `Opcode::Inc`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `++` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Inc; |
||||||
|
|
||||||
|
impl Operation for Inc { |
||||||
|
const NAME: &'static str = "Inc"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Inc"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
match value.to_numeric(context)? { |
||||||
|
Numeric::Number(number) => context.vm.push(number + 1f64), |
||||||
|
Numeric::BigInt(bigint) => { |
||||||
|
context.vm.push(JsBigInt::add(&bigint, &JsBigInt::one())); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Inc` implements the Opcode Operation for `Opcode::Inc`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary postfix `++` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct IncPost; |
||||||
|
|
||||||
|
impl Operation for IncPost { |
||||||
|
const NAME: &'static str = "IncPost"; |
||||||
|
const INSTRUCTION: &'static str = "INST - IncPost"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.to_numeric(context)?; |
||||||
|
context.vm.push(value.clone()); |
||||||
|
match value { |
||||||
|
Numeric::Number(number) => context.vm.push(number + 1f64), |
||||||
|
Numeric::BigInt(bigint) => { |
||||||
|
context.vm.push(JsBigInt::add(&bigint, &JsBigInt::one())); |
||||||
|
} |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `LogicalNot` implements the Opcode Operation for `Opcode::LogicalNot`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary logical `!` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct LogicalNot; |
||||||
|
|
||||||
|
impl Operation for LogicalNot { |
||||||
|
const NAME: &'static str = "LogicalNot"; |
||||||
|
const INSTRUCTION: &'static str = "INST - LogicalNot"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
context.vm.push(!value.to_boolean()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,96 @@ |
|||||||
|
use crate::{ |
||||||
|
builtins::Number, |
||||||
|
value::Numeric, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsBigInt, JsResult, |
||||||
|
}; |
||||||
|
use std::ops::Neg as StdNeg; |
||||||
|
|
||||||
|
pub(crate) mod decrement; |
||||||
|
pub(crate) mod increment; |
||||||
|
pub(crate) mod logical; |
||||||
|
pub(crate) mod void; |
||||||
|
|
||||||
|
pub(crate) use decrement::*; |
||||||
|
pub(crate) use increment::*; |
||||||
|
pub(crate) use logical::*; |
||||||
|
pub(crate) use void::*; |
||||||
|
|
||||||
|
/// `TypeOf` implements the Opcode Operation for `Opcode::TypeOf`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `typeof` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct TypeOf; |
||||||
|
|
||||||
|
impl Operation for TypeOf { |
||||||
|
const NAME: &'static str = "TypeOf"; |
||||||
|
const INSTRUCTION: &'static str = "INST - TypeOf"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
context.vm.push(value.type_of()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Pos` implements the Opcode Operation for `Opcode::Pos`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `+` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Pos; |
||||||
|
|
||||||
|
impl Operation for Pos { |
||||||
|
const NAME: &'static str = "Pos"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Pos"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
let value = value.to_number(context)?; |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `Neg` implements the Opcode Operation for `Opcode::Neg`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `-` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Neg; |
||||||
|
|
||||||
|
impl Operation for Neg { |
||||||
|
const NAME: &'static str = "Neg"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Neg"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
match value.to_numeric(context)? { |
||||||
|
Numeric::Number(number) => context.vm.push(number.neg()), |
||||||
|
Numeric::BigInt(bigint) => context.vm.push(JsBigInt::neg(&bigint)), |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/// `BitNot` implements the Opcode Operation for `Opcode::BitNot`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary bitwise `~` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct BitNot; |
||||||
|
|
||||||
|
impl Operation for BitNot { |
||||||
|
const NAME: &'static str = "BitNot"; |
||||||
|
const INSTRUCTION: &'static str = "INST - BitNot"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
match value.to_numeric(context)? { |
||||||
|
Numeric::Number(number) => context.vm.push(Number::not(number)), |
||||||
|
Numeric::BigInt(bigint) => context.vm.push(JsBigInt::not(&bigint)), |
||||||
|
} |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
use crate::{ |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, JsValue, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `Void` implements the Opcode Operation for `Opcode::Void`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Unary `void` operator.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct Void; |
||||||
|
|
||||||
|
impl Operation for Void { |
||||||
|
const NAME: &'static str = "Void"; |
||||||
|
const INSTRUCTION: &'static str = "INST - Void"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let _old = context.vm.pop(); |
||||||
|
context.vm.push(JsValue::undefined()); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,33 @@ |
|||||||
|
use crate::{ |
||||||
|
error::JsNativeError, |
||||||
|
vm::{opcode::Operation, ShouldExit}, |
||||||
|
Context, JsResult, |
||||||
|
}; |
||||||
|
|
||||||
|
/// `ValueNotNullOrUndefined` implements the Opcode Operation for `Opcode::ValueNotNullOrUndefined`
|
||||||
|
///
|
||||||
|
/// Operation:
|
||||||
|
/// - Require the stack value to be neither null nor undefined.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] |
||||||
|
pub(crate) struct ValueNotNullOrUndefined; |
||||||
|
|
||||||
|
impl Operation for ValueNotNullOrUndefined { |
||||||
|
const NAME: &'static str = "ValueNotNullOrUndefined"; |
||||||
|
const INSTRUCTION: &'static str = "INST - ValueNotNullOrUndefined"; |
||||||
|
|
||||||
|
fn execute(context: &mut Context) -> JsResult<ShouldExit> { |
||||||
|
let value = context.vm.pop(); |
||||||
|
if value.is_null() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("Cannot destructure 'null' value") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
if value.is_undefined() { |
||||||
|
return Err(JsNativeError::typ() |
||||||
|
.with_message("Cannot destructure 'undefined' value") |
||||||
|
.into()); |
||||||
|
} |
||||||
|
context.vm.push(value); |
||||||
|
Ok(ShouldExit::False) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue