use crate::parser::ast::{
AssignmentOperator, BinaryOperator, ClassData, ExpressionOrSpreadElement, ExpressionOrSuper,
ExpressionType, ExpressionPatternType, FunctionData, LiteralData, LiteralType, MemberExpressionType,
MethodDefinitionKind, PatternOrExpression, PatternType, PropertyData, PropertyKind, UnaryOperator,
UpdateOperator, LogicalOperator,
};
use crate::runner::ds::error::JErrorType;
use crate::runner::ds::object::{JsObject, JsObjectType, ObjectBase, ObjectType};
use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorAccessor, PropertyDescriptorData, PropertyKey};
use crate::runner::ds::value::{JsValue, JsNumberType};
use crate::runner::plugin::types::{EvalContext, SimpleObject};
use std::cell::RefCell;
use std::rc::Rc;
use super::types::ValueResult;
pub fn evaluate_expression(
expr: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
match expr {
ExpressionType::Literal(lit) => evaluate_literal(lit),
ExpressionType::ExpressionWhichCanBePattern(pattern) => {
evaluate_expression_pattern(pattern, ctx)
}
ExpressionType::ThisExpression { .. } => {
Ok(ctx.global_this.clone().unwrap_or(JsValue::Undefined))
}
ExpressionType::ArrayExpression { elements, .. } => {
evaluate_array_expression(elements, ctx)
}
ExpressionType::ObjectExpression { properties, .. } => {
evaluate_object_expression(properties, ctx)
}
ExpressionType::FunctionOrGeneratorExpression(func_data) => {
create_function_object(func_data, ctx)
}
ExpressionType::UnaryExpression { operator, argument, .. } => {
evaluate_unary_expression(operator, argument, ctx)
}
ExpressionType::BinaryExpression { operator, left, right, .. } => {
evaluate_binary_expression(operator, left, right, ctx)
}
ExpressionType::LogicalExpression { operator, left, right, .. } => {
evaluate_logical_expression(operator, left, right, ctx)
}
ExpressionType::UpdateExpression { operator, argument, prefix, .. } => {
evaluate_update_expression(operator, argument, *prefix, ctx)
}
ExpressionType::AssignmentExpression { operator, left, right, .. } => {
evaluate_assignment_expression(operator, left, right, ctx)
}
ExpressionType::ConditionalExpression { test, consequent, alternate, .. } => {
evaluate_conditional_expression(test, consequent, alternate, ctx)
}
ExpressionType::CallExpression { callee, arguments, .. } => {
evaluate_call_expression(callee, arguments, ctx)
}
ExpressionType::NewExpression { callee, arguments, .. } => {
evaluate_new_expression(callee, arguments, ctx)
}
ExpressionType::SequenceExpression { expressions, .. } => {
evaluate_sequence_expression(expressions, ctx)
}
ExpressionType::TemplateLiteral(_) => {
Err(JErrorType::TypeError("Template literal not yet implemented".to_string()))
}
ExpressionType::TaggedTemplateExpression { .. } => {
Err(JErrorType::TypeError("Tagged template expression not yet implemented".to_string()))
}
ExpressionType::ClassExpression(class_data) => {
evaluate_class_expression(class_data, ctx)
}
ExpressionType::YieldExpression { argument, delegate, .. } => {
if *delegate {
return Err(JErrorType::TypeError("yield* not yet implemented".to_string()));
}
let value = if let Some(arg) = argument {
evaluate_expression(arg, ctx)?
} else {
JsValue::Undefined
};
Err(JErrorType::YieldValue(value))
}
ExpressionType::MetaProperty { .. } => {
Err(JErrorType::TypeError("Meta property not yet implemented".to_string()))
}
ExpressionType::ArrowFunctionExpression { .. } => {
Err(JErrorType::TypeError("Arrow function expression not yet implemented".to_string()))
}
ExpressionType::MemberExpression(member_expr) => {
evaluate_member_expression(member_expr, ctx)
}
}
}
fn evaluate_literal(lit: &LiteralData) -> ValueResult {
Ok(match &lit.value {
LiteralType::NullLiteral => JsValue::Null,
LiteralType::BooleanLiteral(b) => JsValue::Boolean(*b),
LiteralType::StringLiteral(s) => JsValue::String(s.clone()),
LiteralType::NumberLiteral(n) => {
use crate::parser::ast::NumberLiteralType;
match n {
NumberLiteralType::IntegerLiteral(i) => JsValue::Number(JsNumberType::Integer(*i)),
NumberLiteralType::FloatLiteral(f) => JsValue::Number(JsNumberType::Float(*f)),
}
}
LiteralType::RegExpLiteral { .. } => {
return Err(JErrorType::TypeError("RegExp literal not yet implemented".to_string()));
}
})
}
fn evaluate_array_expression(
elements: &[Option<ExpressionOrSpreadElement>],
ctx: &mut EvalContext,
) -> ValueResult {
use crate::runner::ds::object::JsObject;
let mut array_obj = ctx.new_tracked_object()?;
let mut index = 0;
for element in elements {
if let Some(elem) = element {
match elem {
ExpressionOrSpreadElement::Expression(expr) => {
let value = evaluate_expression(expr, ctx)?;
let key = PropertyKey::Str(index.to_string());
array_obj.get_object_base_mut().properties.insert(
key,
PropertyDescriptor::Data(PropertyDescriptorData {
value,
writable: true,
enumerable: true,
configurable: true,
}),
);
index += 1;
}
ExpressionOrSpreadElement::SpreadElement(spread_expr) => {
let spread_value = evaluate_expression(spread_expr, ctx)?;
if let JsValue::Object(spread_obj) = spread_value {
let borrowed = spread_obj.borrow();
let base = borrowed.as_js_object().get_object_base();
let length = if let Some(PropertyDescriptor::Data(prop)) =
base.properties.get(&PropertyKey::Str("length".to_string()))
{
match &prop.value {
JsValue::Number(JsNumberType::Integer(n)) => *n as usize,
JsValue::Number(JsNumberType::Float(n)) => *n as usize,
_ => 0,
}
} else {
0
};
for i in 0..length {
let elem_key = PropertyKey::Str(i.to_string());
let elem_value = if let Some(PropertyDescriptor::Data(prop)) =
base.properties.get(&elem_key)
{
prop.value.clone()
} else {
JsValue::Undefined
};
let key = PropertyKey::Str(index.to_string());
array_obj.get_object_base_mut().properties.insert(
key,
PropertyDescriptor::Data(PropertyDescriptorData {
value: elem_value,
writable: true,
enumerable: true,
configurable: true,
}),
);
index += 1;
}
} else if let JsValue::String(s) = spread_value {
for ch in s.chars() {
let key = PropertyKey::Str(index.to_string());
array_obj.get_object_base_mut().properties.insert(
key,
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::String(ch.to_string()),
writable: true,
enumerable: true,
configurable: true,
}),
);
index += 1;
}
} else {
return Err(JErrorType::TypeError(
"Spread requires an iterable".to_string(),
));
}
}
}
} else {
index += 1;
}
}
array_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("length".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Number(JsNumberType::Integer(index as i64)),
writable: true,
enumerable: false,
configurable: false,
}),
);
let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(array_obj))));
Ok(JsValue::Object(obj))
}
fn evaluate_object_expression(
properties: &[PropertyData<Box<ExpressionType>>],
ctx: &mut EvalContext,
) -> ValueResult {
use std::collections::HashMap;
let mut obj = ctx.new_tracked_object()?;
let mut accessors: HashMap<String, (Option<JsObjectType>, Option<JsObjectType>)> = HashMap::new();
for prop in properties {
let key = get_object_property_key(&prop.key, prop.computed, ctx)?;
match prop.kind {
PropertyKind::Init => {
let value = evaluate_expression(&prop.value, ctx)?;
obj.get_object_base_mut().properties.insert(
PropertyKey::Str(key),
PropertyDescriptor::Data(PropertyDescriptorData {
value,
writable: true,
enumerable: true,
configurable: true,
}),
);
}
PropertyKind::Get => {
if let ExpressionType::FunctionOrGeneratorExpression(func_data) = prop.value.as_ref() {
let getter_fn = create_function_object(func_data, ctx)?;
if let JsValue::Object(getter_obj) = getter_fn {
let entry = accessors.entry(key).or_insert((None, None));
entry.0 = Some(getter_obj);
}
} else {
return Err(JErrorType::TypeError("Getter must be a function".to_string()));
}
}
PropertyKind::Set => {
if let ExpressionType::FunctionOrGeneratorExpression(func_data) = prop.value.as_ref() {
let setter_fn = create_function_object(func_data, ctx)?;
if let JsValue::Object(setter_obj) = setter_fn {
let entry = accessors.entry(key).or_insert((None, None));
entry.1 = Some(setter_obj);
}
} else {
return Err(JErrorType::TypeError("Setter must be a function".to_string()));
}
}
}
}
for (key, (getter, setter)) in accessors {
obj.get_object_base_mut().properties.insert(
PropertyKey::Str(key),
PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
get: getter,
set: setter,
enumerable: true,
configurable: true,
}),
);
}
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(obj))));
Ok(JsValue::Object(obj_ref))
}
fn get_object_property_key(
key_expr: &ExpressionType,
computed: bool,
ctx: &mut EvalContext,
) -> Result<String, JErrorType> {
if computed {
let key_value = evaluate_expression(key_expr, ctx)?;
Ok(to_string(&key_value))
} else {
match key_expr {
ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
Ok(id.name.clone())
}
ExpressionType::Literal(lit) => match &lit.value {
LiteralType::StringLiteral(s) => Ok(s.clone()),
LiteralType::NumberLiteral(n) => {
use crate::parser::ast::NumberLiteralType;
match n {
NumberLiteralType::IntegerLiteral(i) => Ok(i.to_string()),
NumberLiteralType::FloatLiteral(f) => Ok(f.to_string()),
}
}
_ => Err(JErrorType::TypeError("Invalid property key".to_string())),
},
_ => Err(JErrorType::TypeError("Invalid property key".to_string())),
}
}
}
fn evaluate_expression_pattern(
pattern: &ExpressionPatternType,
ctx: &mut EvalContext,
) -> ValueResult {
match pattern {
ExpressionPatternType::Identifier(id) => {
ctx.get_binding(&id.name)
}
}
}
fn evaluate_member_expression(
member_expr: &MemberExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
match member_expr {
MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
let obj_value = evaluate_expression_or_super(object, ctx)?;
let prop_name = &property.name;
get_property_with_ctx(&obj_value, prop_name, ctx)
}
MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
let obj_value = evaluate_expression_or_super(object, ctx)?;
let prop_value = evaluate_expression(property, ctx)?;
let prop_name = to_property_key(&prop_value);
get_property_with_ctx(&obj_value, &prop_name, ctx)
}
}
}
fn evaluate_expression_or_super(
expr_or_super: &ExpressionOrSuper,
ctx: &mut EvalContext,
) -> ValueResult {
match expr_or_super {
ExpressionOrSuper::Expression(expr) => evaluate_expression(expr, ctx),
ExpressionOrSuper::Super => {
Err(JErrorType::TypeError("super not yet supported".to_string()))
}
}
}
fn evaluate_call_expression(
callee: &ExpressionOrSuper,
arguments: &[ExpressionOrSpreadElement],
ctx: &mut EvalContext,
) -> ValueResult {
if let ExpressionOrSuper::Expression(expr) = callee {
if let ExpressionType::MemberExpression(member) = expr.as_ref() {
if let MemberExpressionType::SimpleMemberExpression { object, property, .. } = member {
if let ExpressionOrSuper::Expression(obj_expr) = object {
if let ExpressionType::ExpressionWhichCanBePattern(
ExpressionPatternType::Identifier(id)
) = obj_expr.as_ref() {
let obj_name = &id.name;
let method_name = &property.name;
let args = evaluate_arguments(arguments, ctx)?;
let sg = ctx.super_global.clone();
let this_value = ctx.resolve_binding(obj_name).unwrap_or(JsValue::Undefined);
let sg_result = sg.borrow().call_method(
obj_name,
method_name,
ctx,
this_value.clone(),
args.clone(),
);
if let Some(result) = sg_result {
return result;
}
}
}
}
}
}
let callee_value = evaluate_expression_or_super(callee, ctx)?;
let this_value = get_call_this_value(callee, ctx);
let args = evaluate_arguments(arguments, ctx)?;
call_value(&callee_value, this_value, args, ctx)
}
fn evaluate_new_expression(
callee: &ExpressionType,
arguments: &[ExpressionOrSpreadElement],
ctx: &mut EvalContext,
) -> ValueResult {
if let ExpressionType::ExpressionWhichCanBePattern(
ExpressionPatternType::Identifier(id)
) = callee {
let ctor_name = &id.name;
let args = evaluate_arguments(arguments, ctx)?;
let sg = ctx.super_global.clone();
let sg_result = sg.borrow().call_constructor(ctor_name, ctx, args.clone());
if let Some(result) = sg_result {
return result;
}
}
let constructor = evaluate_expression(callee, ctx)?;
let ctor_obj = match &constructor {
JsValue::Object(obj) => {
if !obj.borrow().is_callable() {
return Err(JErrorType::TypeError(format!(
"{} is not a constructor",
constructor
)));
}
obj.clone()
}
_ => {
return Err(JErrorType::TypeError(format!(
"{} is not a constructor",
constructor
)));
}
};
let new_obj = create_new_object_for_constructor(&ctor_obj)?;
let args = evaluate_arguments(arguments, ctx)?;
let result = call_value(&constructor, JsValue::Object(new_obj.clone()), args, ctx)?;
match result {
JsValue::Object(_) => Ok(result),
_ => Ok(JsValue::Object(new_obj)),
}
}
fn create_new_object_for_constructor(
constructor: &JsObjectType,
) -> Result<JsObjectType, JErrorType> {
use crate::runner::ds::object::{JsObject, ObjectType};
use crate::runner::ds::object_property::{PropertyDescriptor, PropertyKey};
let mut new_obj = SimpleObject::new();
let ctor_borrowed = constructor.borrow();
let prototype_key = PropertyKey::Str("prototype".to_string());
if let Some(PropertyDescriptor::Data(data)) =
ctor_borrowed.as_js_object().get_object_base().properties.get(&prototype_key)
{
if let JsValue::Object(proto_obj) = &data.value {
new_obj.get_object_base_mut().prototype = Some(proto_obj.clone());
}
}
drop(ctor_borrowed);
let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(new_obj))));
Ok(obj)
}
fn get_call_this_value(callee: &ExpressionOrSuper, ctx: &mut EvalContext) -> JsValue {
match callee {
ExpressionOrSuper::Expression(expr) => {
match expr.as_ref() {
ExpressionType::MemberExpression(member) => {
match member {
MemberExpressionType::SimpleMemberExpression { object, .. } |
MemberExpressionType::ComputedMemberExpression { object, .. } => {
match object {
ExpressionOrSuper::Expression(obj_expr) => {
evaluate_expression(obj_expr, ctx).unwrap_or(JsValue::Undefined)
}
ExpressionOrSuper::Super => JsValue::Undefined,
}
}
}
}
_ => ctx.global_this.clone().unwrap_or(JsValue::Undefined),
}
}
ExpressionOrSuper::Super => JsValue::Undefined,
}
}
fn evaluate_arguments(
arguments: &[ExpressionOrSpreadElement],
ctx: &mut EvalContext,
) -> Result<Vec<JsValue>, JErrorType> {
let mut args = Vec::with_capacity(arguments.len());
for arg in arguments {
match arg {
ExpressionOrSpreadElement::Expression(expr) => {
args.push(evaluate_expression(expr, ctx)?);
}
ExpressionOrSpreadElement::SpreadElement(spread_expr) => {
let spread_value = evaluate_expression(spread_expr, ctx)?;
if let JsValue::Object(spread_obj) = spread_value {
let borrowed = spread_obj.borrow();
let base = borrowed.as_js_object().get_object_base();
let length = if let Some(PropertyDescriptor::Data(prop)) =
base.properties.get(&PropertyKey::Str("length".to_string()))
{
match &prop.value {
JsValue::Number(JsNumberType::Integer(n)) => *n as usize,
JsValue::Number(JsNumberType::Float(n)) => *n as usize,
_ => 0,
}
} else {
0
};
for i in 0..length {
let elem_key = PropertyKey::Str(i.to_string());
let elem_value = if let Some(PropertyDescriptor::Data(prop)) =
base.properties.get(&elem_key)
{
prop.value.clone()
} else {
JsValue::Undefined
};
args.push(elem_value);
}
} else if let JsValue::String(s) = spread_value {
for ch in s.chars() {
args.push(JsValue::String(ch.to_string()));
}
} else {
return Err(JErrorType::TypeError(
"Spread requires an iterable".to_string(),
));
}
}
}
}
Ok(args)
}
fn call_value(
callee: &JsValue,
this_value: JsValue,
args: Vec<JsValue>,
ctx: &mut EvalContext,
) -> ValueResult {
match callee {
JsValue::Object(obj) => {
let obj_ref = obj.borrow();
if obj_ref.is_callable() {
drop(obj_ref);
call_function_object(obj, this_value, args, ctx)
} else {
Err(JErrorType::TypeError(format!(
"{} is not a function",
callee
)))
}
}
_ => Err(JErrorType::TypeError(format!(
"{} is not a function",
callee
))),
}
}
pub fn call_function_object(
func: &crate::runner::ds::object::JsObjectType,
this_value: JsValue,
args: Vec<JsValue>,
ctx: &mut EvalContext,
) -> ValueResult {
let func_ref = func.borrow();
match &*func_ref {
ObjectType::Function(func_obj) => {
let body = func_obj.get_function_object_base().body_code.clone();
let params = func_obj.get_function_object_base().formal_parameters.clone();
let func_env = func_obj.get_function_object_base().environment.clone();
drop(func_ref);
let saved_lex_env = ctx.lex_env.clone();
let saved_var_env = ctx.var_env.clone();
use crate::runner::ds::env_record::new_declarative_environment;
let func_scope = new_declarative_environment(Some(func_env));
ctx.lex_env = func_scope.clone();
ctx.var_env = func_scope;
for (i, param) in params.iter().enumerate() {
if let crate::parser::ast::PatternType::PatternWhichCanBeExpression(
ExpressionPatternType::Identifier(id)
) = param {
let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
ctx.create_binding(&id.name, false)?;
ctx.initialize_binding(&id.name, arg_value)?;
}
}
use super::statement::execute_statement;
use super::types::CompletionType;
let mut result_completion = super::types::Completion::normal();
for stmt in body.body.iter() {
let completion = execute_statement(stmt, ctx)?;
match completion.completion_type {
CompletionType::Return => {
result_completion = completion;
break;
}
CompletionType::Throw => {
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
return Err(JErrorType::TypeError(format!(
"Uncaught exception: {:?}",
completion.value
)));
}
CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
result_completion = completion;
break;
}
CompletionType::Normal => {
result_completion = completion;
}
}
}
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
match result_completion.completion_type {
CompletionType::Return => Ok(result_completion.get_value()),
_ => Ok(JsValue::Undefined),
}
}
ObjectType::Ordinary(obj) => {
let marker = PropertyKey::Str("__simple_function__".to_string());
let default_ctor_marker = PropertyKey::Str("__default_constructor__".to_string());
let generator_marker = PropertyKey::Str("__generator__".to_string());
let generator_next_marker = PropertyKey::Str("__generator_next__".to_string());
if obj.get_object_base().properties.contains_key(&default_ctor_marker) {
drop(func_ref);
Ok(JsValue::Undefined)
} else if let Some(PropertyDescriptor::Data(data)) = obj.get_object_base().properties.get(&generator_next_marker) {
let gen_obj = match &data.value {
JsValue::Object(o) => o.clone(),
_ => return Err(JErrorType::TypeError("Invalid generator reference".to_string())),
};
drop(func_ref);
let mut gen_borrowed = gen_obj.borrow_mut();
match &mut *gen_borrowed {
ObjectType::Ordinary(inner_obj) => {
let gen_marker = PropertyKey::Str("__generator_object__".to_string());
if inner_obj.get_object_base().properties.contains_key(&gen_marker) {
let gen_ptr = inner_obj.as_mut() as *mut dyn JsObject as *mut GeneratorObject;
drop(gen_borrowed);
unsafe {
let gen = &mut *gen_ptr;
gen.next(ctx)
}
} else {
Err(JErrorType::TypeError("Not a generator object".to_string()))
}
}
_ => Err(JErrorType::TypeError("Not a generator object".to_string())),
}
} else if obj.get_object_base().properties.contains_key(&generator_marker) {
let obj_ptr = obj.as_ref() as *const dyn JsObject;
drop(func_ref);
unsafe {
let simple_func = &*(obj_ptr as *const SimpleFunctionObject);
create_generator_object(
simple_func.body_ptr,
simple_func.params_ptr,
simple_func.environment.clone(),
args,
ctx,
)
}
} else if obj.get_object_base().properties.contains_key(&marker) {
let obj_ptr = obj.as_ref() as *const dyn JsObject;
drop(func_ref);
unsafe {
let simple_func = &*(obj_ptr as *const SimpleFunctionObject);
simple_func.call_with_this(this_value, args, ctx)
}
} else {
Err(JErrorType::TypeError("Object is not callable".to_string()))
}
}
}
}
fn get_property_with_ctx(value: &JsValue, prop_name: &str, ctx: &mut EvalContext) -> ValueResult {
get_property_with_receiver(value, value, prop_name, ctx)
}
fn get_property_with_receiver(
receiver: &JsValue,
lookup_target: &JsValue,
prop_name: &str,
ctx: &mut EvalContext,
) -> ValueResult {
match lookup_target {
JsValue::Object(obj) => {
let is_gen_obj = {
let obj_ref = obj.borrow();
let marker = PropertyKey::Str("__generator_object__".to_string());
obj_ref.as_js_object().get_object_base().properties.contains_key(&marker)
};
if is_gen_obj && prop_name == "next" {
return create_generator_next_method(obj.clone());
}
let prop_key = PropertyKey::Str(prop_name.to_string());
let desc = {
let obj_ref = obj.borrow();
obj_ref.as_js_object().get_own_property(&prop_key)?.cloned()
};
if let Some(desc) = desc {
match desc {
PropertyDescriptor::Data(data) => Ok(data.value.clone()),
PropertyDescriptor::Accessor(accessor) => {
if let Some(getter) = accessor.get {
call_accessor_function(&getter, receiver.clone(), vec![], ctx)
} else {
Ok(JsValue::Undefined)
}
}
}
} else {
let proto = obj.borrow().as_js_object().get_prototype_of();
if let Some(proto) = proto {
get_property_with_receiver(receiver, &JsValue::Object(proto), prop_name, ctx)
} else {
Ok(JsValue::Undefined)
}
}
}
JsValue::String(s) => {
if prop_name == "length" {
Ok(JsValue::Number(JsNumberType::Integer(s.len() as i64)))
} else if let Ok(index) = prop_name.parse::<usize>() {
if index < s.len() {
Ok(JsValue::String(s.chars().nth(index).unwrap().to_string()))
} else {
Ok(JsValue::Undefined)
}
} else {
Ok(JsValue::Undefined)
}
}
JsValue::Undefined => {
Err(JErrorType::TypeError("Cannot read property of undefined".to_string()))
}
JsValue::Null => {
Err(JErrorType::TypeError("Cannot read property of null".to_string()))
}
_ => {
Ok(JsValue::Undefined)
}
}
}
fn call_accessor_function(
func: &JsObjectType,
this_value: JsValue,
args: Vec<JsValue>,
ctx: &mut EvalContext,
) -> ValueResult {
let func_ref = func.borrow();
if let ObjectType::Ordinary(obj) = &*func_ref {
if is_simple_function_object(obj.as_ref()) {
let simple_func = unsafe {
let ptr = obj.as_ref() as *const dyn JsObject as *const SimpleFunctionObject;
&*ptr
};
drop(func_ref);
return simple_func.call_with_this(this_value, args, ctx);
}
drop(func_ref);
call_function_object(func, this_value, args, ctx)
} else if let ObjectType::Function(func_obj) = &*func_ref {
let body = func_obj.get_function_object_base().body_code.clone();
let params = func_obj.get_function_object_base().formal_parameters.clone();
let func_env = func_obj.get_function_object_base().environment.clone();
drop(func_ref);
call_function_with_body(body, params, func_env, this_value, args, ctx)
} else {
Err(JErrorType::TypeError("Not a function".to_string()))
}
}
fn call_function_with_body(
body: Rc<crate::parser::ast::FunctionBodyData>,
params: Rc<Vec<PatternType>>,
func_env: crate::runner::ds::lex_env::JsLexEnvironmentType,
this_value: JsValue,
args: Vec<JsValue>,
ctx: &mut EvalContext,
) -> ValueResult {
use crate::runner::ds::env_record::new_declarative_environment;
use super::statement::execute_statement;
use super::types::CompletionType;
let saved_lex_env = ctx.lex_env.clone();
let saved_var_env = ctx.var_env.clone();
let saved_this = ctx.global_this.clone();
let func_scope = new_declarative_environment(Some(func_env));
ctx.lex_env = func_scope.clone();
ctx.var_env = func_scope;
ctx.global_this = Some(this_value);
for (i, param) in params.iter().enumerate() {
if let PatternType::PatternWhichCanBeExpression(
ExpressionPatternType::Identifier(id)
) = param {
let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
ctx.create_binding(&id.name, false)?;
ctx.initialize_binding(&id.name, arg_value)?;
}
}
let mut result_completion = super::types::Completion::normal();
for stmt in body.body.iter() {
let completion = execute_statement(stmt, ctx)?;
match completion.completion_type {
CompletionType::Return => {
result_completion = completion;
break;
}
CompletionType::Throw => {
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
ctx.global_this = saved_this;
return Err(JErrorType::TypeError(format!(
"Uncaught exception: {:?}",
completion.value
)));
}
CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
result_completion = completion;
break;
}
CompletionType::Normal => {
result_completion = completion;
}
}
}
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
ctx.global_this = saved_this;
match result_completion.completion_type {
CompletionType::Return => Ok(result_completion.get_value()),
_ => Ok(JsValue::Undefined),
}
}
fn to_property_key(value: &JsValue) -> String {
to_string(value)
}
fn evaluate_assignment_expression(
operator: &AssignmentOperator,
left: &PatternOrExpression,
right: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
if let PatternOrExpression::Expression(expr) = left {
if let ExpressionType::MemberExpression(member) = expr.as_ref() {
return evaluate_member_assignment(operator, member, right, ctx);
}
}
if let PatternOrExpression::Pattern(pattern) = left {
if !matches!(
&**pattern,
PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(_))
) {
if !matches!(operator, AssignmentOperator::Equals) {
return Err(JErrorType::TypeError(
"Compound assignment not supported for destructuring patterns".to_string(),
));
}
let rhs_value = evaluate_expression(right, ctx)?;
assign_pattern(pattern, rhs_value.clone(), ctx)?;
return Ok(rhs_value);
}
}
let name = match left {
PatternOrExpression::Pattern(pattern) => get_pattern_name(pattern)?,
PatternOrExpression::Expression(expr) => get_expression_name(expr)?,
};
let rhs_value = evaluate_expression(right, ctx)?;
let final_value = match operator {
AssignmentOperator::Equals => rhs_value,
AssignmentOperator::AddEquals => {
let current = ctx.get_binding(&name)?;
add_values(¤t, &rhs_value)?
}
AssignmentOperator::SubtractEquals => {
let current = ctx.get_binding(&name)?;
subtract_values(¤t, &rhs_value)?
}
AssignmentOperator::MultiplyEquals => {
let current = ctx.get_binding(&name)?;
multiply_values(¤t, &rhs_value)?
}
AssignmentOperator::DivideEquals => {
let current = ctx.get_binding(&name)?;
divide_values(¤t, &rhs_value)?
}
AssignmentOperator::ModuloEquals => {
let current = ctx.get_binding(&name)?;
modulo_values(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseLeftShiftEquals => {
let current = ctx.get_binding(&name)?;
left_shift(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseRightShiftEquals => {
let current = ctx.get_binding(&name)?;
right_shift(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseUnsignedRightShiftEquals => {
let current = ctx.get_binding(&name)?;
unsigned_right_shift(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseOrEquals => {
let current = ctx.get_binding(&name)?;
bitwise_or(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseAndEquals => {
let current = ctx.get_binding(&name)?;
bitwise_and(¤t, &rhs_value)?
}
AssignmentOperator::BitwiseXorEquals => {
let current = ctx.get_binding(&name)?;
bitwise_xor(¤t, &rhs_value)?
}
};
ctx.set_binding(&name, final_value.clone())?;
Ok(final_value)
}
fn assign_pattern(
pattern: &PatternType,
value: JsValue,
ctx: &mut EvalContext,
) -> Result<(), JErrorType> {
match pattern {
PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
ctx.set_binding(&id.name, value)?;
Ok(())
}
PatternType::ObjectPattern { properties, .. } => {
for prop in properties {
let prop_data = &prop.0;
let key_name = get_assignment_property_key(prop_data, ctx)?;
let prop_value = get_object_property_value(&value, &key_name, ctx)?;
assign_pattern(&prop_data.value, prop_value, ctx)?;
}
Ok(())
}
PatternType::ArrayPattern { elements, .. } => {
for (index, element) in elements.iter().enumerate() {
if let Some(elem_pattern) = element {
if let PatternType::RestElement { argument, .. } = elem_pattern.as_ref() {
let rest_value = get_rest_elements_for_assignment(&value, index, ctx)?;
assign_pattern(argument, rest_value, ctx)?;
} else {
let elem_value = get_array_element_for_assignment(&value, index, ctx)?;
assign_pattern(elem_pattern, elem_value, ctx)?;
}
}
}
Ok(())
}
PatternType::AssignmentPattern { left, right, .. } => {
let actual_value = if matches!(value, JsValue::Undefined) {
evaluate_expression(right, ctx)?
} else {
value
};
assign_pattern(left, actual_value, ctx)
}
PatternType::RestElement { argument, .. } => assign_pattern(argument, value, ctx),
}
}
fn get_assignment_property_key(
prop: &PropertyData<Box<PatternType>>,
ctx: &mut EvalContext,
) -> Result<String, JErrorType> {
if prop.computed {
let key_value = evaluate_expression(prop.key.as_ref(), ctx)?;
Ok(to_string(&key_value))
} else {
match prop.key.as_ref() {
ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
Ok(id.name.clone())
}
ExpressionType::Literal(lit_data) => match &lit_data.value {
LiteralType::StringLiteral(s) => Ok(s.clone()),
LiteralType::NumberLiteral(num) => {
use crate::parser::ast::NumberLiteralType;
match num {
NumberLiteralType::IntegerLiteral(n) => Ok(n.to_string()),
NumberLiteralType::FloatLiteral(n) => Ok(n.to_string()),
}
}
_ => Err(JErrorType::TypeError(
"Invalid property key in destructuring assignment".to_string(),
)),
},
_ => Err(JErrorType::TypeError(
"Invalid property key in destructuring assignment".to_string(),
)),
}
}
}
fn get_object_property_value(
obj: &JsValue,
key: &str,
ctx: &mut EvalContext,
) -> Result<JsValue, JErrorType> {
match obj {
JsValue::Object(_) => get_property_with_ctx(obj, key, ctx),
_ => Err(JErrorType::TypeError(
"Cannot destructure non-object".to_string(),
)),
}
}
fn get_array_element_for_assignment(
arr: &JsValue,
index: usize,
ctx: &mut EvalContext,
) -> Result<JsValue, JErrorType> {
match arr {
JsValue::Object(_) => {
let key = index.to_string();
get_property_with_ctx(arr, &key, ctx)
}
_ => Err(JErrorType::TypeError(
"Cannot destructure non-array".to_string(),
)),
}
}
fn get_rest_elements_for_assignment(
arr: &JsValue,
start_index: usize,
ctx: &mut EvalContext,
) -> Result<JsValue, JErrorType> {
use crate::runner::ds::object::{JsObject, JsObjectType, ObjectType};
use crate::runner::ds::object_property::{PropertyDescriptor, PropertyDescriptorData, PropertyKey};
use std::cell::RefCell;
use std::rc::Rc;
let length = match arr {
JsValue::Object(_) => {
let length_value = get_property_with_ctx(arr, "length", ctx)?;
match length_value {
JsValue::Number(JsNumberType::Integer(n)) => n.max(0) as usize,
JsValue::Number(JsNumberType::Float(n)) => {
if n.is_nan() || n < 0.0 {
0
} else {
n as usize
}
}
_ => 0,
}
}
_ => {
return Err(JErrorType::TypeError(
"Cannot use rest with non-array".to_string(),
))
}
};
let mut rest_obj = ctx.new_tracked_object()?;
let mut rest_index = 0;
for i in start_index..length {
let key = i.to_string();
let value = get_property_with_ctx(arr, &key, ctx)?;
rest_obj.get_object_base_mut().properties.insert(
PropertyKey::Str(rest_index.to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value,
writable: true,
enumerable: true,
configurable: true,
}),
);
rest_index += 1;
}
rest_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("length".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Number(JsNumberType::Integer(rest_index as i64)),
writable: true,
enumerable: false,
configurable: false,
}),
);
let obj: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(rest_obj))));
Ok(JsValue::Object(obj))
}
fn evaluate_member_assignment(
operator: &AssignmentOperator,
member: &MemberExpressionType,
right: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
let (obj_value, prop_name) = match member {
MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
let obj = evaluate_expression_or_super(object, ctx)?;
(obj, property.name.clone())
}
MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
let obj = evaluate_expression_or_super(object, ctx)?;
let prop_val = evaluate_expression(property.as_ref(), ctx)?;
(obj, to_property_key(&prop_val))
}
};
let rhs_value = evaluate_expression(right, ctx)?;
let final_value = if matches!(operator, AssignmentOperator::Equals) {
rhs_value
} else {
let current = get_property_with_ctx(&obj_value, &prop_name, ctx)?;
match operator {
AssignmentOperator::Equals => unreachable!(),
AssignmentOperator::AddEquals => add_values(¤t, &rhs_value)?,
AssignmentOperator::SubtractEquals => subtract_values(¤t, &rhs_value)?,
AssignmentOperator::MultiplyEquals => multiply_values(¤t, &rhs_value)?,
AssignmentOperator::DivideEquals => divide_values(¤t, &rhs_value)?,
AssignmentOperator::ModuloEquals => modulo_values(¤t, &rhs_value)?,
AssignmentOperator::BitwiseLeftShiftEquals => left_shift(¤t, &rhs_value)?,
AssignmentOperator::BitwiseRightShiftEquals => right_shift(¤t, &rhs_value)?,
AssignmentOperator::BitwiseUnsignedRightShiftEquals => unsigned_right_shift(¤t, &rhs_value)?,
AssignmentOperator::BitwiseOrEquals => bitwise_or(¤t, &rhs_value)?,
AssignmentOperator::BitwiseAndEquals => bitwise_and(¤t, &rhs_value)?,
AssignmentOperator::BitwiseXorEquals => bitwise_xor(¤t, &rhs_value)?,
}
};
set_property_with_ctx(&obj_value, &prop_name, final_value.clone(), ctx)?;
Ok(final_value)
}
fn set_property_with_ctx(
value: &JsValue,
prop_name: &str,
new_value: JsValue,
ctx: &mut EvalContext,
) -> Result<(), JErrorType> {
match value {
JsValue::Object(obj) => {
let prop_key = PropertyKey::Str(prop_name.to_string());
let desc = {
let obj_ref = obj.borrow();
obj_ref.as_js_object().get_own_property(&prop_key)?.cloned()
};
if let Some(PropertyDescriptor::Accessor(accessor)) = desc {
if let Some(setter) = accessor.set {
call_accessor_function(&setter, value.clone(), vec![new_value], ctx)?;
return Ok(());
}
}
let mut obj_mut = obj.borrow_mut();
obj_mut.as_js_object_mut().get_object_base_mut().properties.insert(
prop_key,
PropertyDescriptor::Data(PropertyDescriptorData {
value: new_value,
writable: true,
enumerable: true,
configurable: true,
}),
);
Ok(())
}
_ => Err(JErrorType::TypeError("Cannot set property on non-object".to_string())),
}
}
fn get_pattern_name(pattern: &PatternType) -> Result<String, JErrorType> {
match pattern {
PatternType::PatternWhichCanBeExpression(ExpressionPatternType::Identifier(id)) => {
Ok(id.name.clone())
}
_ => Err(JErrorType::TypeError(
"Complex patterns in assignment not yet supported".to_string(),
)),
}
}
fn get_expression_name(expr: &ExpressionType) -> Result<String, JErrorType> {
match expr {
ExpressionType::ExpressionWhichCanBePattern(ExpressionPatternType::Identifier(id)) => {
Ok(id.name.clone())
}
_ => Err(JErrorType::TypeError(
"Assignment to non-identifier expressions not yet supported".to_string(),
)),
}
}
fn evaluate_unary_expression(
operator: &UnaryOperator,
argument: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
match operator {
UnaryOperator::TypeOf => {
let value = evaluate_expression(argument, ctx).unwrap_or(JsValue::Undefined);
Ok(JsValue::String(get_typeof_string(&value)))
}
UnaryOperator::Void => {
let _ = evaluate_expression(argument, ctx)?;
Ok(JsValue::Undefined)
}
UnaryOperator::LogicalNot => {
let value = evaluate_expression(argument, ctx)?;
Ok(JsValue::Boolean(!to_boolean(&value)))
}
UnaryOperator::Minus => {
let value = evaluate_expression(argument, ctx)?;
negate_number(&value)
}
UnaryOperator::Plus => {
let value = evaluate_expression(argument, ctx)?;
to_number(&value)
}
UnaryOperator::BitwiseNot => {
let value = evaluate_expression(argument, ctx)?;
bitwise_not(&value)
}
UnaryOperator::Delete => {
match argument {
ExpressionType::MemberExpression(member) => {
match member {
MemberExpressionType::SimpleMemberExpression { object, property, .. } => {
let obj_value = evaluate_expression_or_super(object, ctx)?;
match obj_value {
JsValue::Object(obj) => {
let prop_key = PropertyKey::Str(property.name.clone());
let result = obj.borrow_mut().as_js_object_mut().delete(&prop_key)?;
Ok(JsValue::Boolean(result))
}
_ => Ok(JsValue::Boolean(true))
}
}
MemberExpressionType::ComputedMemberExpression { object, property, .. } => {
let obj_value = evaluate_expression_or_super(object, ctx)?;
let prop_value = evaluate_expression(property.as_ref(), ctx)?;
let prop_name = to_property_key(&prop_value);
match obj_value {
JsValue::Object(obj) => {
let prop_key = PropertyKey::Str(prop_name);
let result = obj.borrow_mut().as_js_object_mut().delete(&prop_key)?;
Ok(JsValue::Boolean(result))
}
_ => Ok(JsValue::Boolean(true))
}
}
}
}
_ => {
let _ = evaluate_expression(argument, ctx)?;
Ok(JsValue::Boolean(true))
}
}
}
}
}
fn evaluate_binary_expression(
operator: &BinaryOperator,
left: &ExpressionType,
right: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
let left_val = evaluate_expression(left, ctx)?;
let right_val = evaluate_expression(right, ctx)?;
match operator {
BinaryOperator::Add => add_values(&left_val, &right_val),
BinaryOperator::Subtract => subtract_values(&left_val, &right_val),
BinaryOperator::Multiply => multiply_values(&left_val, &right_val),
BinaryOperator::Divide => divide_values(&left_val, &right_val),
BinaryOperator::Modulo => modulo_values(&left_val, &right_val),
BinaryOperator::LessThan => compare_values(&left_val, &right_val, |a, b| a < b),
BinaryOperator::GreaterThan => compare_values(&left_val, &right_val, |a, b| a > b),
BinaryOperator::LessThanEqual => compare_values(&left_val, &right_val, |a, b| a <= b),
BinaryOperator::GreaterThanEqual => compare_values(&left_val, &right_val, |a, b| a >= b),
BinaryOperator::StrictlyEqual => Ok(JsValue::Boolean(strict_equality(&left_val, &right_val))),
BinaryOperator::StrictlyUnequal => Ok(JsValue::Boolean(!strict_equality(&left_val, &right_val))),
BinaryOperator::LooselyEqual => Ok(JsValue::Boolean(loose_equality(&left_val, &right_val))),
BinaryOperator::LooselyUnequal => Ok(JsValue::Boolean(!loose_equality(&left_val, &right_val))),
BinaryOperator::BitwiseAnd => bitwise_and(&left_val, &right_val),
BinaryOperator::BitwiseOr => bitwise_or(&left_val, &right_val),
BinaryOperator::BitwiseXor => bitwise_xor(&left_val, &right_val),
BinaryOperator::BitwiseLeftShift => left_shift(&left_val, &right_val),
BinaryOperator::BitwiseRightShift => right_shift(&left_val, &right_val),
BinaryOperator::BitwiseUnsignedRightShift => unsigned_right_shift(&left_val, &right_val),
BinaryOperator::InstanceOf => {
let left_obj = match &left_val {
JsValue::Object(obj) => obj.clone(),
_ => return Ok(JsValue::Boolean(false)),
};
let right_obj = match &right_val {
JsValue::Object(obj) => obj.clone(),
_ => return Err(JErrorType::TypeError("Right-hand side of 'instanceof' is not an object".to_string())),
};
let proto_key = PropertyKey::Str("prototype".to_string());
let right_prototype = {
let right_borrowed = right_obj.borrow();
if let Some(desc) = right_borrowed.as_js_object().get_own_property(&proto_key)? {
match desc {
PropertyDescriptor::Data(data) => {
match &data.value {
JsValue::Object(p) => Some(p.clone()),
_ => None,
}
}
_ => None,
}
} else {
None
}
};
let target_proto = match right_prototype {
Some(p) => p,
None => return Ok(JsValue::Boolean(false)),
};
let mut current_proto = left_obj.borrow().as_js_object().get_prototype_of();
while let Some(proto) = current_proto {
if Rc::ptr_eq(&proto, &target_proto) {
return Ok(JsValue::Boolean(true));
}
current_proto = proto.borrow().as_js_object().get_prototype_of();
}
Ok(JsValue::Boolean(false))
}
BinaryOperator::In => {
let prop_key = PropertyKey::Str(to_property_key(&left_val));
match &right_val {
JsValue::Object(obj) => {
let has = obj.borrow().as_js_object().has_property(&prop_key);
Ok(JsValue::Boolean(has))
}
_ => Err(JErrorType::TypeError("Cannot use 'in' operator with non-object".to_string()))
}
}
}
}
fn evaluate_logical_expression(
operator: &LogicalOperator,
left: &ExpressionType,
right: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
let left_val = evaluate_expression(left, ctx)?;
match operator {
LogicalOperator::And => {
if !to_boolean(&left_val) {
Ok(left_val)
} else {
evaluate_expression(right, ctx)
}
}
LogicalOperator::Or => {
if to_boolean(&left_val) {
Ok(left_val)
} else {
evaluate_expression(right, ctx)
}
}
}
}
fn evaluate_conditional_expression(
test: &ExpressionType,
consequent: &ExpressionType,
alternate: &ExpressionType,
ctx: &mut EvalContext,
) -> ValueResult {
let test_val = evaluate_expression(test, ctx)?;
if to_boolean(&test_val) {
evaluate_expression(consequent, ctx)
} else {
evaluate_expression(alternate, ctx)
}
}
fn evaluate_sequence_expression(
expressions: &[Box<ExpressionType>],
ctx: &mut EvalContext,
) -> ValueResult {
let mut result = JsValue::Undefined;
for expr in expressions {
result = evaluate_expression(expr.as_ref(), ctx)?;
}
Ok(result)
}
fn evaluate_update_expression(
operator: &UpdateOperator,
argument: &ExpressionType,
prefix: bool,
ctx: &mut EvalContext,
) -> ValueResult {
let name = get_expression_name(argument)?;
let current_value = ctx.get_binding(&name)?;
let old_number = to_number(¤t_value)?;
let old_f64 = match &old_number {
JsValue::Number(n) => number_to_f64(n),
_ => f64::NAN,
};
let new_f64 = match operator {
UpdateOperator::PlusPlus => old_f64 + 1.0,
UpdateOperator::MinusMinus => old_f64 - 1.0,
};
let new_value = if new_f64.fract() == 0.0 && new_f64.abs() < i64::MAX as f64 {
JsValue::Number(JsNumberType::Integer(new_f64 as i64))
} else {
JsValue::Number(JsNumberType::Float(new_f64))
};
ctx.set_binding(&name, new_value.clone())?;
if prefix {
Ok(new_value)
} else {
Ok(old_number)
}
}
pub fn to_boolean(value: &JsValue) -> bool {
match value {
JsValue::Undefined => false,
JsValue::Null => false,
JsValue::Boolean(b) => *b,
JsValue::Number(n) => match n {
JsNumberType::Integer(0) => false,
JsNumberType::Float(f) if *f == 0.0 || f.is_nan() => false,
JsNumberType::NaN => false,
_ => true,
},
JsValue::String(s) => !s.is_empty(),
JsValue::Symbol(_) => true,
JsValue::Object(_) => true,
}
}
fn get_typeof_string(value: &JsValue) -> String {
match value {
JsValue::Undefined => "undefined".to_string(),
JsValue::Null => "object".to_string(),
JsValue::Boolean(_) => "boolean".to_string(),
JsValue::Number(_) => "number".to_string(),
JsValue::String(_) => "string".to_string(),
JsValue::Symbol(_) => "symbol".to_string(),
JsValue::Object(_) => "object".to_string(),
}
}
fn to_number(value: &JsValue) -> ValueResult {
Ok(match value {
JsValue::Undefined => JsValue::Number(JsNumberType::NaN),
JsValue::Null => JsValue::Number(JsNumberType::Integer(0)),
JsValue::Boolean(true) => JsValue::Number(JsNumberType::Integer(1)),
JsValue::Boolean(false) => JsValue::Number(JsNumberType::Integer(0)),
JsValue::Number(n) => JsValue::Number(n.clone()),
JsValue::String(s) => {
if s.is_empty() {
JsValue::Number(JsNumberType::Integer(0))
} else if let Ok(i) = s.trim().parse::<i64>() {
JsValue::Number(JsNumberType::Integer(i))
} else if let Ok(f) = s.trim().parse::<f64>() {
JsValue::Number(JsNumberType::Float(f))
} else {
JsValue::Number(JsNumberType::NaN)
}
}
JsValue::Symbol(_) => {
return Err(JErrorType::TypeError("Cannot convert Symbol to number".to_string()));
}
JsValue::Object(_) => JsValue::Number(JsNumberType::NaN),
})
}
fn negate_number(value: &JsValue) -> ValueResult {
let num_value = to_number(value)?;
Ok(match num_value {
JsValue::Number(JsNumberType::Integer(i)) => JsValue::Number(JsNumberType::Integer(-i)),
JsValue::Number(JsNumberType::Float(f)) => JsValue::Number(JsNumberType::Float(-f)),
JsValue::Number(JsNumberType::PositiveInfinity) => JsValue::Number(JsNumberType::NegativeInfinity),
JsValue::Number(JsNumberType::NegativeInfinity) => JsValue::Number(JsNumberType::PositiveInfinity),
JsValue::Number(JsNumberType::NaN) => JsValue::Number(JsNumberType::NaN),
_ => JsValue::Number(JsNumberType::NaN),
})
}
fn bitwise_not(value: &JsValue) -> ValueResult {
let num = to_i32(value)?;
Ok(JsValue::Number(JsNumberType::Integer(!num as i64)))
}
fn to_i32(value: &JsValue) -> Result<i32, JErrorType> {
match to_number(value)? {
JsValue::Number(JsNumberType::Integer(i)) => Ok(i as i32),
JsValue::Number(JsNumberType::Float(f)) => Ok(f as i32),
JsValue::Number(JsNumberType::NaN) => Ok(0),
JsValue::Number(JsNumberType::PositiveInfinity) => Ok(0),
JsValue::Number(JsNumberType::NegativeInfinity) => Ok(0),
_ => Ok(0),
}
}
fn to_u32(value: &JsValue) -> Result<u32, JErrorType> {
Ok(to_i32(value)? as u32)
}
fn add_values(left: &JsValue, right: &JsValue) -> ValueResult {
if matches!(left, JsValue::String(_)) || matches!(right, JsValue::String(_)) {
let left_str = to_string(left);
let right_str = to_string(right);
return Ok(JsValue::String(format!("{}{}", left_str, right_str)));
}
let left_num = to_number(left)?;
let right_num = to_number(right)?;
apply_numeric_op(&left_num, &right_num, |a, b| a + b, |a, b| a + b)
}
fn subtract_values(left: &JsValue, right: &JsValue) -> ValueResult {
let left_num = to_number(left)?;
let right_num = to_number(right)?;
apply_numeric_op(&left_num, &right_num, |a, b| a - b, |a, b| a - b)
}
fn multiply_values(left: &JsValue, right: &JsValue) -> ValueResult {
let left_num = to_number(left)?;
let right_num = to_number(right)?;
apply_numeric_op(&left_num, &right_num, |a, b| a * b, |a, b| a * b)
}
fn divide_values(left: &JsValue, right: &JsValue) -> ValueResult {
let left_num = to_number(left)?;
let right_num = to_number(right)?;
if matches!(right_num, JsValue::Number(JsNumberType::Integer(0)))
|| matches!(right_num, JsValue::Number(JsNumberType::Float(f)) if f == 0.0)
{
let left_f = match &left_num {
JsValue::Number(n) => number_to_f64(n),
_ => f64::NAN,
};
return Ok(if left_f.is_nan() || left_f == 0.0 {
JsValue::Number(JsNumberType::NaN)
} else if left_f > 0.0 {
JsValue::Number(JsNumberType::PositiveInfinity)
} else {
JsValue::Number(JsNumberType::NegativeInfinity)
});
}
apply_numeric_op(&left_num, &right_num, |a, b| a / b, |a, b| a / b)
}
fn modulo_values(left: &JsValue, right: &JsValue) -> ValueResult {
let left_num = to_number(left)?;
let right_num = to_number(right)?;
apply_numeric_op(&left_num, &right_num, |a, b| a % b, |a, b| a % b)
}
fn apply_numeric_op<F, G>(left: &JsValue, right: &JsValue, int_op: F, float_op: G) -> ValueResult
where
F: Fn(i64, i64) -> i64,
G: Fn(f64, f64) -> f64,
{
match (left, right) {
(JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => {
Ok(JsValue::Number(JsNumberType::NaN))
}
(JsValue::Number(JsNumberType::Integer(a)), JsValue::Number(JsNumberType::Integer(b))) => {
Ok(JsValue::Number(JsNumberType::Integer(int_op(*a, *b))))
}
(JsValue::Number(a), JsValue::Number(b)) => {
let a_f64 = number_to_f64(a);
let b_f64 = number_to_f64(b);
Ok(JsValue::Number(JsNumberType::Float(float_op(a_f64, b_f64))))
}
_ => Ok(JsValue::Number(JsNumberType::NaN)),
}
}
fn number_to_f64(n: &JsNumberType) -> f64 {
match n {
JsNumberType::Integer(i) => *i as f64,
JsNumberType::Float(f) => *f,
JsNumberType::NaN => f64::NAN,
JsNumberType::PositiveInfinity => f64::INFINITY,
JsNumberType::NegativeInfinity => f64::NEG_INFINITY,
}
}
fn compare_values<F>(left: &JsValue, right: &JsValue, cmp: F) -> ValueResult
where
F: Fn(f64, f64) -> bool,
{
let left_num = to_number(left)?;
let right_num = to_number(right)?;
let result = match (&left_num, &right_num) {
(JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => false,
(JsValue::Number(a), JsValue::Number(b)) => {
cmp(number_to_f64(a), number_to_f64(b))
}
_ => false,
};
Ok(JsValue::Boolean(result))
}
pub fn strict_equality(left: &JsValue, right: &JsValue) -> bool {
match (left, right) {
(JsValue::Undefined, JsValue::Undefined) => true,
(JsValue::Null, JsValue::Null) => true,
(JsValue::Boolean(a), JsValue::Boolean(b)) => a == b,
(JsValue::String(a), JsValue::String(b)) => a == b,
(JsValue::Number(JsNumberType::NaN), _) | (_, JsValue::Number(JsNumberType::NaN)) => false,
(JsValue::Number(a), JsValue::Number(b)) => {
number_to_f64(a) == number_to_f64(b)
}
(JsValue::Object(a), JsValue::Object(b)) => std::rc::Rc::ptr_eq(a, b),
_ => false,
}
}
fn loose_equality(left: &JsValue, right: &JsValue) -> bool {
if std::mem::discriminant(left) == std::mem::discriminant(right) {
return strict_equality(left, right);
}
match (left, right) {
(JsValue::Null, JsValue::Undefined) | (JsValue::Undefined, JsValue::Null) => true,
(JsValue::Number(_), JsValue::String(_)) => {
if let Ok(r) = to_number(right) {
strict_equality(left, &r)
} else {
false
}
}
(JsValue::String(_), JsValue::Number(_)) => {
if let Ok(l) = to_number(left) {
strict_equality(&l, right)
} else {
false
}
}
(JsValue::Boolean(_), _) => {
if let Ok(l) = to_number(left) {
loose_equality(&l, right)
} else {
false
}
}
(_, JsValue::Boolean(_)) => {
if let Ok(r) = to_number(right) {
loose_equality(left, &r)
} else {
false
}
}
_ => false,
}
}
fn bitwise_and(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_i32(left)?;
let r = to_i32(right)?;
Ok(JsValue::Number(JsNumberType::Integer((l & r) as i64)))
}
fn bitwise_or(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_i32(left)?;
let r = to_i32(right)?;
Ok(JsValue::Number(JsNumberType::Integer((l | r) as i64)))
}
fn bitwise_xor(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_i32(left)?;
let r = to_i32(right)?;
Ok(JsValue::Number(JsNumberType::Integer((l ^ r) as i64)))
}
fn left_shift(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_i32(left)?;
let r = to_u32(right)? & 0x1f;
Ok(JsValue::Number(JsNumberType::Integer((l << r) as i64)))
}
fn right_shift(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_i32(left)?;
let r = to_u32(right)? & 0x1f;
Ok(JsValue::Number(JsNumberType::Integer((l >> r) as i64)))
}
fn unsigned_right_shift(left: &JsValue, right: &JsValue) -> ValueResult {
let l = to_u32(left)?;
let r = to_u32(right)? & 0x1f;
Ok(JsValue::Number(JsNumberType::Integer((l >> r) as i64)))
}
fn to_string(value: &JsValue) -> String {
match value {
JsValue::Undefined => "undefined".to_string(),
JsValue::Null => "null".to_string(),
JsValue::Boolean(true) => "true".to_string(),
JsValue::Boolean(false) => "false".to_string(),
JsValue::Number(n) => n.to_string(),
JsValue::String(s) => s.clone(),
JsValue::Symbol(_) => "[Symbol]".to_string(),
JsValue::Object(_) => "[object Object]".to_string(),
}
}
pub struct SimpleFunctionObject {
base: ObjectBase,
body_ptr: *const crate::parser::ast::FunctionBodyData,
params_ptr: *const Vec<PatternType>,
environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
}
unsafe impl Send for SimpleFunctionObject {}
unsafe impl Sync for SimpleFunctionObject {}
impl SimpleFunctionObject {
pub fn new(
body_ptr: *const crate::parser::ast::FunctionBodyData,
params_ptr: *const Vec<PatternType>,
environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
) -> Self {
SimpleFunctionObject {
base: ObjectBase::new(),
body_ptr,
params_ptr,
environment,
}
}
pub fn call_with_this(
&self,
this_value: JsValue,
args: Vec<JsValue>,
ctx: &mut EvalContext,
) -> ValueResult {
use crate::runner::ds::env_record::new_declarative_environment;
use super::statement::execute_statement;
use super::types::CompletionType;
let (body, params) = unsafe {
(&*self.body_ptr, &*self.params_ptr)
};
let saved_lex_env = ctx.lex_env.clone();
let saved_var_env = ctx.var_env.clone();
let saved_this = ctx.global_this.clone();
let func_scope = new_declarative_environment(Some(self.environment.clone()));
ctx.lex_env = func_scope.clone();
ctx.var_env = func_scope;
ctx.global_this = Some(this_value);
for (i, param) in params.iter().enumerate() {
if let PatternType::PatternWhichCanBeExpression(
ExpressionPatternType::Identifier(id)
) = param {
let arg_value = args.get(i).cloned().unwrap_or(JsValue::Undefined);
ctx.create_binding(&id.name, false)?;
ctx.initialize_binding(&id.name, arg_value)?;
}
}
let mut result_completion = super::types::Completion::normal();
for stmt in body.body.iter() {
let completion = execute_statement(stmt, ctx)?;
match completion.completion_type {
CompletionType::Return => {
result_completion = completion;
break;
}
CompletionType::Throw => {
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
ctx.global_this = saved_this;
return Err(JErrorType::TypeError(format!(
"Uncaught exception: {:?}",
completion.value
)));
}
CompletionType::Break | CompletionType::Continue | CompletionType::Yield => {
result_completion = completion;
break;
}
CompletionType::Normal => {
result_completion = completion;
}
}
}
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
ctx.global_this = saved_this;
match result_completion.completion_type {
CompletionType::Return => Ok(result_completion.get_value()),
_ => Ok(JsValue::Undefined),
}
}
}
impl JsObject for SimpleFunctionObject {
fn get_object_base_mut(&mut self) -> &mut ObjectBase {
&mut self.base
}
fn get_object_base(&self) -> &ObjectBase {
&self.base
}
fn as_js_object(&self) -> &dyn JsObject {
self
}
fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
self
}
}
pub fn is_simple_function_object(obj: &dyn JsObject) -> bool {
let marker = PropertyKey::Str("__simple_function__".to_string());
obj.get_object_base().properties.contains_key(&marker)
}
pub fn create_function_object(func_data: &FunctionData, ctx: &EvalContext) -> ValueResult {
let body_ptr = func_data.body.as_ref() as *const _;
let params_ptr = &func_data.params.list as *const _;
let environment = ctx.lex_env.clone();
let mut func_obj = SimpleFunctionObject::new(body_ptr, params_ptr, environment);
let prototype = SimpleObject::new();
let prototype_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(prototype))));
func_obj.base.properties.insert(
PropertyKey::Str("prototype".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Object(prototype_ref),
writable: true,
enumerable: false,
configurable: false,
}),
);
func_obj.base.properties.insert(
PropertyKey::Str("__simple_function__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
if func_data.generator {
func_obj.base.properties.insert(
PropertyKey::Str("__generator__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
}
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
Ok(JsValue::Object(obj_ref))
}
#[derive(Debug, Clone, PartialEq)]
pub enum GeneratorState {
SuspendedStart,
SuspendedYield,
Executing,
Completed,
}
pub struct GeneratorObject {
base: ObjectBase,
body_ptr: *const crate::parser::ast::FunctionBodyData,
_params_ptr: *const Vec<PatternType>,
environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
state: GeneratorState,
current_index: usize,
local_bindings: std::collections::HashMap<String, JsValue>,
}
unsafe impl Send for GeneratorObject {}
unsafe impl Sync for GeneratorObject {}
impl GeneratorObject {
pub fn new(
body_ptr: *const crate::parser::ast::FunctionBodyData,
params_ptr: *const Vec<PatternType>,
environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
) -> Self {
GeneratorObject {
base: ObjectBase::new(),
body_ptr,
_params_ptr: params_ptr,
environment,
state: GeneratorState::SuspendedStart,
current_index: 0,
local_bindings: std::collections::HashMap::new(),
}
}
pub fn next(&mut self, ctx: &mut EvalContext) -> ValueResult {
use crate::runner::ds::env_record::new_declarative_environment;
use super::statement::execute_statement;
use super::types::CompletionType;
match self.state {
GeneratorState::Completed => {
return create_iterator_result(JsValue::Undefined, true);
}
GeneratorState::Executing => {
return Err(JErrorType::TypeError("Generator is already executing".to_string()));
}
GeneratorState::SuspendedStart | GeneratorState::SuspendedYield => {
}
}
self.state = GeneratorState::Executing;
let body = unsafe { &*self.body_ptr };
let saved_lex_env = ctx.lex_env.clone();
let saved_var_env = ctx.var_env.clone();
let func_scope = new_declarative_environment(Some(self.environment.clone()));
ctx.lex_env = func_scope.clone();
ctx.var_env = func_scope;
for (name, value) in &self.local_bindings {
let _ = ctx.create_binding(name, false);
let _ = ctx.initialize_binding(name, value.clone());
}
let mut result_value = JsValue::Undefined;
let mut yielded = false;
for (idx, stmt) in body.body.iter().enumerate() {
if idx < self.current_index {
continue; }
match execute_statement(stmt, ctx) {
Ok(completion) => {
match completion.completion_type {
CompletionType::Return => {
result_value = completion.get_value();
self.state = GeneratorState::Completed;
break;
}
CompletionType::Yield => {
result_value = completion.get_value();
self.current_index = idx + 1;
self.state = GeneratorState::SuspendedYield;
self.save_bindings(ctx);
yielded = true;
break;
}
CompletionType::Throw => {
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
self.state = GeneratorState::Completed;
return Err(JErrorType::TypeError(format!(
"Uncaught exception: {:?}",
completion.value
)));
}
_ => {
result_value = completion.get_value();
}
}
}
Err(JErrorType::YieldValue(value)) => {
result_value = value;
self.current_index = idx + 1;
self.state = GeneratorState::SuspendedYield;
self.save_bindings(ctx);
yielded = true;
break;
}
Err(e) => {
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
self.state = GeneratorState::Completed;
return Err(e);
}
}
}
if !yielded && self.state == GeneratorState::Executing {
self.state = GeneratorState::Completed;
}
ctx.lex_env = saved_lex_env;
ctx.var_env = saved_var_env;
let done = self.state == GeneratorState::Completed;
create_iterator_result(result_value, done)
}
fn save_bindings(&mut self, ctx: &EvalContext) {
self.local_bindings.clear();
let env = ctx.lex_env.borrow();
if let Some(bindings) = env.inner.as_env_record().get_all_bindings() {
for (name, value) in bindings {
self.local_bindings.insert(name, value);
}
}
}
}
impl JsObject for GeneratorObject {
fn get_object_base_mut(&mut self) -> &mut ObjectBase {
&mut self.base
}
fn get_object_base(&self) -> &ObjectBase {
&self.base
}
fn as_js_object(&self) -> &dyn JsObject {
self
}
fn as_js_object_mut(&mut self) -> &mut dyn JsObject {
self
}
}
fn create_iterator_result(value: JsValue, done: bool) -> ValueResult {
let mut obj = SimpleObject::new();
obj.get_object_base_mut().properties.insert(
PropertyKey::Str("value".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value,
writable: true,
enumerable: true,
configurable: true,
}),
);
obj.get_object_base_mut().properties.insert(
PropertyKey::Str("done".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(done),
writable: true,
enumerable: true,
configurable: true,
}),
);
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(obj))));
Ok(JsValue::Object(obj_ref))
}
fn create_generator_object(
body_ptr: *const crate::parser::ast::FunctionBodyData,
params_ptr: *const Vec<PatternType>,
environment: crate::runner::ds::lex_env::JsLexEnvironmentType,
_args: Vec<JsValue>,
_ctx: &mut EvalContext,
) -> ValueResult {
let mut gen_obj = GeneratorObject::new(body_ptr, params_ptr, environment);
gen_obj.base.properties.insert(
PropertyKey::Str("__generator_object__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
let params = unsafe { &*params_ptr };
for (i, param) in params.iter().enumerate() {
if let PatternType::PatternWhichCanBeExpression(
ExpressionPatternType::Identifier(id)
) = param {
let arg_value = _args.get(i).cloned().unwrap_or(JsValue::Undefined);
gen_obj.local_bindings.insert(id.name.clone(), arg_value);
}
}
let gen_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(gen_obj))));
Ok(JsValue::Object(gen_ref))
}
fn create_generator_next_method(gen_obj: JsObjectType) -> ValueResult {
let mut next_obj = SimpleObject::new();
next_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("__generator_next__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Object(gen_obj),
writable: false,
enumerable: false,
configurable: false,
}),
);
next_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("__simple_function__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(next_obj))));
Ok(JsValue::Object(obj_ref))
}
pub fn evaluate_class_expression(class_data: &ClassData, ctx: &mut EvalContext) -> ValueResult {
evaluate_class(class_data, ctx)
}
pub fn evaluate_class(class_data: &ClassData, ctx: &mut EvalContext) -> ValueResult {
let parent_proto = if let Some(super_class) = &class_data.super_class {
let parent = evaluate_expression(super_class, ctx)?;
match &parent {
JsValue::Object(parent_obj) => {
let borrowed = parent_obj.borrow();
let proto_key = PropertyKey::Str("prototype".to_string());
if let Some(PropertyDescriptor::Data(data)) = borrowed.as_js_object().get_object_base().properties.get(&proto_key) {
if let JsValue::Object(proto) = &data.value {
Some((parent_obj.clone(), proto.clone()))
} else {
return Err(JErrorType::TypeError("Parent class prototype is not an object".to_string()));
}
} else {
return Err(JErrorType::TypeError("Parent class has no prototype".to_string()));
}
}
_ => return Err(JErrorType::TypeError("Class extends value is not a constructor".to_string())),
}
} else {
None
};
let constructor_method = class_data.body.body.iter()
.find(|m| matches!(m.kind, MethodDefinitionKind::Constructor));
let constructor_obj = if let Some(ctor_method) = constructor_method {
create_class_constructor(&ctor_method.value, parent_proto.as_ref().map(|(p, _)| p.clone()), ctx)?
} else {
create_default_constructor(parent_proto.as_ref().map(|(p, _)| p.clone()), ctx)?
};
let prototype = if let Some((_, parent_proto)) = &parent_proto {
let mut proto_obj = SimpleObject::new();
proto_obj.get_object_base_mut().prototype = Some(parent_proto.clone());
Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(proto_obj))))
} else {
Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(SimpleObject::new()))))
};
for method in &class_data.body.body {
if matches!(method.kind, MethodDefinitionKind::Constructor) {
continue; }
let key = get_object_property_key(&method.key, method.computed, ctx)?;
let method_fn = create_function_object(&method.value, ctx)?;
let target = if method.static_flag {
&constructor_obj
} else {
&JsValue::Object(prototype.clone())
};
match method.kind {
MethodDefinitionKind::Method => {
if let JsValue::Object(target_obj) = target {
target_obj.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
PropertyKey::Str(key),
PropertyDescriptor::Data(PropertyDescriptorData {
value: method_fn,
writable: true,
enumerable: false,
configurable: true,
}),
);
}
}
MethodDefinitionKind::Get => {
if let JsValue::Object(getter_fn) = method_fn {
if let JsValue::Object(target_obj) = target {
let mut borrowed = target_obj.borrow_mut();
let prop_key = PropertyKey::Str(key.clone());
let existing = borrowed.as_js_object().get_object_base().properties.get(&prop_key).cloned();
let setter = if let Some(PropertyDescriptor::Accessor(acc)) = existing {
acc.set
} else {
None
};
borrowed.as_js_object_mut().get_object_base_mut().properties.insert(
prop_key,
PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
get: Some(getter_fn),
set: setter,
enumerable: false,
configurable: true,
}),
);
}
}
}
MethodDefinitionKind::Set => {
if let JsValue::Object(setter_fn) = method_fn {
if let JsValue::Object(target_obj) = target {
let mut borrowed = target_obj.borrow_mut();
let prop_key = PropertyKey::Str(key.clone());
let existing = borrowed.as_js_object().get_object_base().properties.get(&prop_key).cloned();
let getter = if let Some(PropertyDescriptor::Accessor(acc)) = existing {
acc.get
} else {
None
};
borrowed.as_js_object_mut().get_object_base_mut().properties.insert(
prop_key,
PropertyDescriptor::Accessor(PropertyDescriptorAccessor {
get: getter,
set: Some(setter_fn),
enumerable: false,
configurable: true,
}),
);
}
}
}
MethodDefinitionKind::Constructor => unreachable!(),
}
}
if let JsValue::Object(ctor_obj) = &constructor_obj {
ctor_obj.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
PropertyKey::Str("prototype".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Object(prototype.clone()),
writable: false,
enumerable: false,
configurable: false,
}),
);
prototype.borrow_mut().as_js_object_mut().get_object_base_mut().properties.insert(
PropertyKey::Str("constructor".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: constructor_obj.clone(),
writable: true,
enumerable: false,
configurable: true,
}),
);
}
Ok(constructor_obj)
}
fn create_class_constructor(
func_data: &FunctionData,
_parent_ctor: Option<JsObjectType>,
ctx: &EvalContext,
) -> ValueResult {
let body_ptr = func_data.body.as_ref() as *const _;
let params_ptr = &func_data.params.list as *const _;
let environment = ctx.lex_env.clone();
let mut func_obj = SimpleFunctionObject::new(body_ptr, params_ptr, environment);
func_obj.base.properties.insert(
PropertyKey::Str("__simple_function__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
func_obj.base.properties.insert(
PropertyKey::Str("__class_constructor__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
Ok(JsValue::Object(obj_ref))
}
fn create_default_constructor(
_parent_ctor: Option<JsObjectType>,
_ctx: &EvalContext,
) -> ValueResult {
let mut func_obj = SimpleObject::new();
func_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("__simple_function__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
func_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("__class_constructor__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
func_obj.get_object_base_mut().properties.insert(
PropertyKey::Str("__default_constructor__".to_string()),
PropertyDescriptor::Data(PropertyDescriptorData {
value: JsValue::Boolean(true),
writable: false,
enumerable: false,
configurable: false,
}),
);
let obj_ref: JsObjectType = Rc::new(RefCell::new(ObjectType::Ordinary(Box::new(func_obj))));
Ok(JsValue::Object(obj_ref))
}