Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

402 lines
14 KiB

use crate::{
builtins::function::set_function_name,
object::{internal_methods::InternalMethodContext, shape::slot::SlotAttributes},
property::{PropertyDescriptor, PropertyKey},
vm::{opcode::Operation, CompletionType},
Context, JsNativeError, JsResult, JsString, JsValue,
};
/// `SetPropertyByName` implements the Opcode Operation for `Opcode::SetPropertyByName`
///
/// Operation:
/// - Sets a property by name of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertyByName;
impl SetPropertyByName {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let value = context.vm.pop();
let receiver = 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 ic = &context.vm.frame().code_block().ic[index];
let object_borrowed = object.borrow();
if let Some((shape, slot)) = ic.match_or_reset(object_borrowed.shape()) {
let slot_index = slot.index as usize;
if slot.attributes.is_accessor_descriptor() {
let result = if slot.attributes.contains(SlotAttributes::PROTOTYPE) {
let prototype = shape.prototype().expect("prototype should have value");
let prototype = prototype.borrow();
prototype.properties().storage[slot_index + 1].clone()
} else {
object_borrowed.properties().storage[slot_index + 1].clone()
};
drop(object_borrowed);
if slot.attributes.has_set() && result.is_object() {
result.as_object().expect("should contain getter").call(
&receiver,
&[value.clone()],
context,
)?;
}
} else if slot.attributes.contains(SlotAttributes::PROTOTYPE) {
let prototype = shape.prototype().expect("prototype should have value");
let mut prototype = prototype.borrow_mut();
prototype.properties_mut().storage[slot_index] = value.clone();
} else {
drop(object_borrowed);
let mut object_borrowed = object.borrow_mut();
object_borrowed.properties_mut().storage[slot_index] = value.clone();
}
context.vm.push(value);
return Ok(CompletionType::Normal);
}
drop(object_borrowed);
let name: PropertyKey = ic.name.clone().into();
let context = &mut InternalMethodContext::new(context);
let succeeded = object.__set__(name.clone(), value.clone(), receiver, context)?;
if !succeeded && context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ()
.with_message(format!("cannot set non-writable property: {name}"))
.into());
}
// Cache the property.
let slot = *context.slot();
if succeeded && slot.is_cachable() {
let ic = &context.vm.frame().code_block.ic[index];
let object_borrowed = object.borrow();
let shape = object_borrowed.shape();
ic.set(shape, slot);
}
context.vm.stack.push(value);
Ok(CompletionType::Normal)
}
}
impl Operation for SetPropertyByName {
const NAME: &'static str = "SetPropertyByName";
const INSTRUCTION: &'static str = "INST - SetPropertyByName";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>();
Self::operation(context, index as usize)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>();
Self::operation(context, index as usize)
}
}
/// `SetPropertyByValue` implements the Opcode Operation for `Opcode::SetPropertyByValue`
///
/// Operation:
/// - Sets a property by value of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertyByValue;
impl Operation for SetPropertyByValue {
const NAME: &'static str = "SetPropertyByValue";
const INSTRUCTION: &'static str = "INST - SetPropertyByValue";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let value = context.vm.pop();
let key = context.vm.pop();
let receiver = 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)?;
// Fast Path:
'fast_path: {
if object.is_array() {
if let PropertyKey::Index(index) = &key {
let mut object_borrowed = object.borrow_mut();
// Cannot modify if not extensible.
if !object_borrowed.extensible {
break 'fast_path;
}
if object_borrowed
.properties_mut()
.set_dense_property(index.get(), &value)
{
context.vm.push(value);
return Ok(CompletionType::Normal);
}
}
}
}
// Slow path:
let succeeded =
object.__set__(key.clone(), value.clone(), receiver, &mut context.into())?;
if !succeeded && context.vm.frame().code_block.strict() {
return Err(JsNativeError::typ()
.with_message(format!("cannot set non-writable property: {key}"))
.into());
}
context.vm.stack.push(value);
Ok(CompletionType::Normal)
}
}
/// `SetPropertyGetterByName` implements the Opcode Operation for `Opcode::SetPropertyGetterByName`
///
/// Operation:
/// - Sets a getter property by name of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertyGetterByName;
impl SetPropertyGetterByName {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let value = context.vm.pop();
let object = context.vm.pop();
let object = object.to_object(context)?;
let name = context
.vm
.frame()
.code_block()
.constant_string(index)
.into();
let set = object
.__get_own_property__(&name, &mut InternalMethodContext::new(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(),
&mut InternalMethodContext::new(context),
)?;
Ok(CompletionType::Normal)
}
}
impl Operation for SetPropertyGetterByName {
const NAME: &'static str = "SetPropertyGetterByName";
const INSTRUCTION: &'static str = "INST - SetPropertyGetterByName";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `SetPropertyGetterByValue` implements the Opcode Operation for `Opcode::SetPropertyGetterByValue`
///
/// Operation:
/// - Sets a getter property by value of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertyGetterByValue;
impl Operation for SetPropertyGetterByValue {
const NAME: &'static str = "SetPropertyGetterByValue";
const INSTRUCTION: &'static str = "INST - SetPropertyGetterByValue";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
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, &mut InternalMethodContext::new(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(),
&mut InternalMethodContext::new(context),
)?;
Ok(CompletionType::Normal)
}
}
/// `SetPropertySetterByName` implements the Opcode Operation for `Opcode::SetPropertySetterByName`
///
/// Operation:
/// - Sets a setter property by name of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertySetterByName;
impl SetPropertySetterByName {
fn operation(context: &mut Context, index: usize) -> JsResult<CompletionType> {
let value = context.vm.pop();
let object = context.vm.pop();
let object = object.to_object(context)?;
let name = context
.vm
.frame()
.code_block()
.constant_string(index)
.into();
let get = object
.__get_own_property__(&name, &mut InternalMethodContext::new(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(),
&mut InternalMethodContext::new(context),
)?;
Ok(CompletionType::Normal)
}
}
impl Operation for SetPropertySetterByName {
const NAME: &'static str = "SetPropertySetterByName";
const INSTRUCTION: &'static str = "INST - SetPropertySetterByName";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u8>() as usize;
Self::operation(context, index)
}
fn execute_with_u16_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u16>() as usize;
Self::operation(context, index)
}
fn execute_with_u32_operands(context: &mut Context) -> JsResult<CompletionType> {
let index = context.vm.read::<u32>() as usize;
Self::operation(context, index)
}
}
/// `SetPropertySetterByValue` implements the Opcode Operation for `Opcode::SetPropertySetterByValue`
///
/// Operation:
/// - Sets a setter property by value of an object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetPropertySetterByValue;
impl Operation for SetPropertySetterByValue {
const NAME: &'static str = "SetPropertySetterByValue";
const INSTRUCTION: &'static str = "INST - SetPropertySetterByValue";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
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, &mut InternalMethodContext::new(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(),
&mut InternalMethodContext::new(context),
)?;
Ok(CompletionType::Normal)
}
}
/// `SetFunctionName` implements the Opcode Operation for `Opcode::SetFunctionName`
///
/// Operation:
/// - Sets the name of a function object.
#[derive(Debug, Clone, Copy)]
pub(crate) struct SetFunctionName;
impl Operation for SetFunctionName {
const NAME: &'static str = "SetFunctionName";
const INSTRUCTION: &'static str = "INST - SetFunctionName";
const COST: u8 = 4;
fn execute(context: &mut Context) -> JsResult<CompletionType> {
let prefix = context.vm.read::<u8>();
let function = context.vm.pop();
let name = context.vm.pop();
let name = match name {
JsValue::String(name) => name.into(),
JsValue::Symbol(name) => name.into(),
_ => unreachable!(),
};
let prefix = match prefix {
1 => Some(JsString::from("get")),
2 => Some(JsString::from("set")),
_ => None,
};
set_function_name(
function.as_object().expect("function is not an object"),
&name,
prefix,
context,
);
context.vm.stack.push(function);
Ok(CompletionType::Normal)
}
}