mirror of
https://github.com/pjh456/OxideJS.git
synced 2026-10-23 18:21:13 +08:00
style: 减少自动格式化换行
This commit is contained in:
@@ -44,12 +44,5 @@ pub fn bind_array(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
);
|
||||
|
||||
let ctor_ptr = kernel.builtin_world().array_constructor.as_ptr() as *mut JsObject;
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"Array",
|
||||
ctor_ptr,
|
||||
crate::builtins::array::array_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "Array", ctor_ptr, crate::builtins::array::array_constructor, 1);
|
||||
}
|
||||
|
||||
@@ -11,36 +11,17 @@ pub fn bind_boolean(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
let proto_ptr = kernel.builtin_world().boolean_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
|
||||
configure_native_constructor(
|
||||
ctor,
|
||||
crate::builtins::boolean::boolean_constructor as *const (),
|
||||
1,
|
||||
);
|
||||
configure_native_constructor(ctor, crate::builtins::boolean::boolean_constructor as *const (), 1);
|
||||
|
||||
apply_binding_table(
|
||||
kernel.builtin_world(),
|
||||
proto,
|
||||
kernel,
|
||||
&[
|
||||
(
|
||||
"valueOf",
|
||||
crate::builtins::boolean::boolean_prototype_value_of as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"toString",
|
||||
crate::builtins::boolean::boolean_prototype_to_string as *const (),
|
||||
0,
|
||||
),
|
||||
("valueOf", crate::builtins::boolean::boolean_prototype_value_of as *const (), 0),
|
||||
("toString", crate::builtins::boolean::boolean_prototype_to_string as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"Boolean",
|
||||
ctor_ptr,
|
||||
crate::builtins::boolean::boolean_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "Boolean", ctor_ptr, crate::builtins::boolean::boolean_constructor, 1);
|
||||
}
|
||||
|
||||
@@ -11,11 +11,7 @@ pub fn bind_date(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
let proto_ptr = kernel.builtin_world().date_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
|
||||
configure_native_constructor(
|
||||
ctor,
|
||||
crate::builtins::date::date_constructor as *const (),
|
||||
7,
|
||||
);
|
||||
configure_native_constructor(ctor, crate::builtins::date::date_constructor as *const (), 7);
|
||||
|
||||
apply_binding_table(
|
||||
kernel.builtin_world(),
|
||||
@@ -33,171 +29,39 @@ pub fn bind_date(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
proto,
|
||||
kernel,
|
||||
&[
|
||||
(
|
||||
"getTime",
|
||||
crate::builtins::date::date_get_time as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getFullYear",
|
||||
crate::builtins::date::date_get_full_year as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getMonth",
|
||||
crate::builtins::date::date_get_month as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getDate",
|
||||
crate::builtins::date::date_get_date as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getDay",
|
||||
crate::builtins::date::date_get_day as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getHours",
|
||||
crate::builtins::date::date_get_hours as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getMinutes",
|
||||
crate::builtins::date::date_get_minutes as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getSeconds",
|
||||
crate::builtins::date::date_get_seconds as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getMilliseconds",
|
||||
crate::builtins::date::date_get_milliseconds as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCFullYear",
|
||||
crate::builtins::date::date_get_utc_full_year as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCMonth",
|
||||
crate::builtins::date::date_get_utc_month as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCDate",
|
||||
crate::builtins::date::date_get_utc_date as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCDay",
|
||||
crate::builtins::date::date_get_utc_day as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCHours",
|
||||
crate::builtins::date::date_get_utc_hours as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCMinutes",
|
||||
crate::builtins::date::date_get_utc_minutes as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCSeconds",
|
||||
crate::builtins::date::date_get_utc_seconds as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getUTCMilliseconds",
|
||||
crate::builtins::date::date_get_utc_milliseconds as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"getTimezoneOffset",
|
||||
crate::builtins::date::date_get_timezone_offset as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"setTime",
|
||||
crate::builtins::date::date_set_time as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"setFullYear",
|
||||
crate::builtins::date::date_set_full_year as *const (),
|
||||
3,
|
||||
),
|
||||
(
|
||||
"setMonth",
|
||||
crate::builtins::date::date_set_month as *const (),
|
||||
2,
|
||||
),
|
||||
(
|
||||
"setDate",
|
||||
crate::builtins::date::date_set_date as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"setHours",
|
||||
crate::builtins::date::date_set_hours as *const (),
|
||||
4,
|
||||
),
|
||||
(
|
||||
"setMinutes",
|
||||
crate::builtins::date::date_set_minutes as *const (),
|
||||
3,
|
||||
),
|
||||
(
|
||||
"setSeconds",
|
||||
crate::builtins::date::date_set_seconds as *const (),
|
||||
2,
|
||||
),
|
||||
(
|
||||
"setMilliseconds",
|
||||
crate::builtins::date::date_set_milliseconds as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"toISOString",
|
||||
crate::builtins::date::date_to_iso_string as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"toJSON",
|
||||
crate::builtins::date::date_to_json as *const (),
|
||||
1,
|
||||
),
|
||||
(
|
||||
"toString",
|
||||
crate::builtins::date::date_to_string as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"toDateString",
|
||||
crate::builtins::date::date_to_date_string as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"toTimeString",
|
||||
crate::builtins::date::date_to_time_string as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"toUTCString",
|
||||
crate::builtins::date::date_to_utc_string as *const (),
|
||||
0,
|
||||
),
|
||||
(
|
||||
"valueOf",
|
||||
crate::builtins::date::date_value_of as *const (),
|
||||
0,
|
||||
),
|
||||
("getTime", crate::builtins::date::date_get_time as *const (), 0),
|
||||
("getFullYear", crate::builtins::date::date_get_full_year as *const (), 0),
|
||||
("getMonth", crate::builtins::date::date_get_month as *const (), 0),
|
||||
("getDate", crate::builtins::date::date_get_date as *const (), 0),
|
||||
("getDay", crate::builtins::date::date_get_day as *const (), 0),
|
||||
("getHours", crate::builtins::date::date_get_hours as *const (), 0),
|
||||
("getMinutes", crate::builtins::date::date_get_minutes as *const (), 0),
|
||||
("getSeconds", crate::builtins::date::date_get_seconds as *const (), 0),
|
||||
("getMilliseconds", crate::builtins::date::date_get_milliseconds as *const (), 0),
|
||||
("getUTCFullYear", crate::builtins::date::date_get_utc_full_year as *const (), 0),
|
||||
("getUTCMonth", crate::builtins::date::date_get_utc_month as *const (), 0),
|
||||
("getUTCDate", crate::builtins::date::date_get_utc_date as *const (), 0),
|
||||
("getUTCDay", crate::builtins::date::date_get_utc_day as *const (), 0),
|
||||
("getUTCHours", crate::builtins::date::date_get_utc_hours as *const (), 0),
|
||||
("getUTCMinutes", crate::builtins::date::date_get_utc_minutes as *const (), 0),
|
||||
("getUTCSeconds", crate::builtins::date::date_get_utc_seconds as *const (), 0),
|
||||
("getUTCMilliseconds", crate::builtins::date::date_get_utc_milliseconds as *const (), 0),
|
||||
("getTimezoneOffset", crate::builtins::date::date_get_timezone_offset as *const (), 0),
|
||||
("setTime", crate::builtins::date::date_set_time as *const (), 1),
|
||||
("setFullYear", crate::builtins::date::date_set_full_year as *const (), 3),
|
||||
("setMonth", crate::builtins::date::date_set_month as *const (), 2),
|
||||
("setDate", crate::builtins::date::date_set_date as *const (), 1),
|
||||
("setHours", crate::builtins::date::date_set_hours as *const (), 4),
|
||||
("setMinutes", crate::builtins::date::date_set_minutes as *const (), 3),
|
||||
("setSeconds", crate::builtins::date::date_set_seconds as *const (), 2),
|
||||
("setMilliseconds", crate::builtins::date::date_set_milliseconds as *const (), 1),
|
||||
("toISOString", crate::builtins::date::date_to_iso_string as *const (), 0),
|
||||
("toJSON", crate::builtins::date::date_to_json as *const (), 1),
|
||||
("toString", crate::builtins::date::date_to_string as *const (), 0),
|
||||
("toDateString", crate::builtins::date::date_to_date_string as *const (), 0),
|
||||
("toTimeString", crate::builtins::date::date_to_time_string as *const (), 0),
|
||||
("toUTCString", crate::builtins::date::date_to_utc_string as *const (), 0),
|
||||
("valueOf", crate::builtins::date::date_value_of as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -7,20 +7,13 @@ use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
|
||||
fn bind_error_subtype_constructor(
|
||||
kernel: &Arc<OxideKernel>,
|
||||
global: &mut JsObject,
|
||||
name: &str,
|
||||
proto_ptr: *mut JsObject,
|
||||
ctor_fn: *const (),
|
||||
kernel: &Arc<OxideKernel>, global: &mut JsObject, name: &str, proto_ptr: *mut JsObject, ctor_fn: *const (),
|
||||
) {
|
||||
let sf = kernel.string_forge().as_ref();
|
||||
let sh = kernel.shape_forge().as_ref();
|
||||
let function_proto_ptr = kernel.builtin_world().function_proto.as_ptr() as *mut JsObject;
|
||||
|
||||
let mut ctor = Box::new(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(function_proto_ptr),
|
||||
));
|
||||
let mut ctor = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto_ptr)));
|
||||
ctor.set_function(true);
|
||||
ctor.set_native_fn(Some(ctor_fn));
|
||||
ctor.set_native_arg_count(1);
|
||||
@@ -32,25 +25,19 @@ fn bind_error_subtype_constructor(
|
||||
let ctor_shape1 = sh.make_shape(EMPTY_SHAPE_ID, si_prototype);
|
||||
let ctor_shape2 = sh.make_shape(ctor_shape1, si_name);
|
||||
ctor.set_shape_id(ctor_shape2);
|
||||
ctor.ensure_hash_props()
|
||||
.push(Box::new(JsValue::from_js_object(proto_ptr)));
|
||||
ctor.ensure_hash_props()
|
||||
.push(Box::new(JsValue::string(name_si, 0)));
|
||||
ctor.ensure_hash_props().push(Box::new(JsValue::from_js_object(proto_ptr)));
|
||||
ctor.ensure_hash_props().push(Box::new(JsValue::string(name_si, 0)));
|
||||
|
||||
let ctor_ptr = Box::into_raw(ctor);
|
||||
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
let proto_ctor_shape = sh.make_shape(proto.shape_id(), sf.intern("constructor").0);
|
||||
proto.set_shape_id(proto_ctor_shape);
|
||||
proto
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::from_js_object(ctor_ptr)));
|
||||
proto.ensure_hash_props().push(Box::new(JsValue::from_js_object(ctor_ptr)));
|
||||
|
||||
let global_shape = sh.make_shape(global.shape_id(), name_si);
|
||||
global.set_shape_id(global_shape);
|
||||
global
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::from_js_object(ctor_ptr)));
|
||||
global.ensure_hash_props().push(Box::new(JsValue::from_js_object(ctor_ptr)));
|
||||
global.bump_generation();
|
||||
}
|
||||
|
||||
@@ -74,8 +61,7 @@ pub fn bind_error(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
|
||||
let si_err = kernel.string_forge().intern("Error").0;
|
||||
let err_shape = kernel.shape_forge().make_shape(global.shape_id(), si_err);
|
||||
let err_val =
|
||||
JsValue::from_js_object(kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
||||
let err_val = JsValue::from_js_object(kernel.builtin_world().error_constructor.as_ptr() as *mut JsObject);
|
||||
global.set_shape_id(err_shape);
|
||||
global.ensure_hash_props().push(Box::new(err_val));
|
||||
global.bump_generation();
|
||||
|
||||
@@ -15,11 +15,7 @@ pub fn bind_json(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
kernel,
|
||||
&[
|
||||
("parse", crate::builtins::json::json_parse as *const (), 1),
|
||||
(
|
||||
"stringify",
|
||||
crate::builtins::json::json_stringify as *const (),
|
||||
1,
|
||||
),
|
||||
("stringify", crate::builtins::json::json_stringify as *const (), 1),
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@@ -27,12 +27,5 @@ pub fn bind_map(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
],
|
||||
);
|
||||
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"Map",
|
||||
ctor_ptr,
|
||||
crate::builtins::map::map_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "Map", ctor_ptr, crate::builtins::map::map_constructor, 1);
|
||||
}
|
||||
|
||||
@@ -66,18 +66,14 @@ pub fn bind_math(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
("SQRT2", std::f64::consts::SQRT_2),
|
||||
] {
|
||||
let si = kernel.string_forge().as_ref().intern(name).0;
|
||||
let sh_c = kernel
|
||||
.shape_forge()
|
||||
.as_ref()
|
||||
.make_shape(math.shape_id(), si);
|
||||
let sh_c = kernel.shape_forge().as_ref().make_shape(math.shape_id(), si);
|
||||
math.set_shape_id(sh_c);
|
||||
math.ensure_hash_props().push(Box::new(JsValue::float(val)));
|
||||
}
|
||||
|
||||
let si_m = kernel.string_forge().intern("Math").0;
|
||||
let m_shape = kernel.shape_forge().make_shape(global.shape_id(), si_m);
|
||||
let m_val =
|
||||
JsValue::from_js_object(kernel.builtin_world().math_object.as_ptr() as *mut JsObject);
|
||||
let m_val = JsValue::from_js_object(kernel.builtin_world().math_object.as_ptr() as *mut JsObject);
|
||||
global.set_shape_id(m_shape);
|
||||
global.ensure_hash_props().push(Box::new(m_val));
|
||||
global.bump_generation();
|
||||
|
||||
@@ -28,9 +28,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
|
||||
let si_num = kernel.string_forge().intern("Number").0;
|
||||
let num_shape = kernel.shape_forge().make_shape(global.shape_id(), si_num);
|
||||
let num_val = JsValue::from_js_object(
|
||||
kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject
|
||||
);
|
||||
let num_val = JsValue::from_js_object(kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject);
|
||||
global.set_shape_id(num_shape);
|
||||
global.ensure_hash_props().push(Box::new(num_val));
|
||||
global.bump_generation();
|
||||
@@ -38,9 +36,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
{
|
||||
let num_ctor_ptr = kernel.builtin_world().number_constructor.as_ptr() as *mut JsObject;
|
||||
let num_ctor = unsafe { &mut *num_ctor_ptr };
|
||||
num_ctor.set_native_fn(Some(
|
||||
crate::builtins::number::number_constructor as *const (),
|
||||
));
|
||||
num_ctor.set_native_fn(Some(crate::builtins::number::number_constructor as *const ()));
|
||||
num_ctor.set_native_arg_count(1);
|
||||
|
||||
for (name, value) in [
|
||||
@@ -55,9 +51,7 @@ pub fn bind_number(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
] {
|
||||
num_ctor.ensure_hash_props().push(Box::new(value));
|
||||
let prop_si = kernel.string_forge().intern(name).0;
|
||||
let next_shape = kernel
|
||||
.shape_forge()
|
||||
.make_shape(num_ctor.shape_id(), prop_si);
|
||||
let next_shape = kernel.shape_forge().make_shape(num_ctor.shape_id(), prop_si);
|
||||
num_ctor.set_shape_id(next_shape);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ pub fn bind_object(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
create: crate::builtins::object::object_create as *const (),
|
||||
assign: crate::builtins::object::object_assign as *const (),
|
||||
define_property: crate::builtins::object::object_define_property as *const (),
|
||||
get_own_property_descriptor: crate::builtins::object::object_get_own_property_descriptor
|
||||
as *const (),
|
||||
get_own_property_descriptor: crate::builtins::object::object_get_own_property_descriptor as *const (),
|
||||
freeze: crate::builtins::object::object_freeze as *const (),
|
||||
seal: crate::builtins::object::object_seal as *const (),
|
||||
prevent_extensions: crate::builtins::object::object_prevent_extensions as *const (),
|
||||
@@ -27,19 +26,10 @@ pub fn bind_object(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
entries: crate::builtins::object::object_entries as *const (),
|
||||
values: crate::builtins::object::object_values as *const (),
|
||||
};
|
||||
kernel.builtin_world().bind_object_methods(
|
||||
&methods,
|
||||
kernel.string_forge().as_ref(),
|
||||
kernel.shape_forge().as_ref(),
|
||||
);
|
||||
kernel
|
||||
.builtin_world()
|
||||
.bind_object_methods(&methods, kernel.string_forge().as_ref(), kernel.shape_forge().as_ref());
|
||||
|
||||
let ctor_ptr = kernel.builtin_world().object_constructor.as_ptr() as *mut JsObject;
|
||||
bind_constructor!(
|
||||
kernel,
|
||||
global,
|
||||
"Object",
|
||||
ctor_ptr,
|
||||
crate::builtins::object::object_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor!(kernel, global, "Object", ctor_ptr, crate::builtins::object::object_constructor, 1);
|
||||
}
|
||||
|
||||
@@ -12,11 +12,7 @@ pub fn bind_regexp(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
let proto_ptr = kernel.builtin_world().regexp_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
|
||||
configure_native_constructor(
|
||||
ctor,
|
||||
crate::builtins::regexp::regexp_constructor as *const (),
|
||||
2,
|
||||
);
|
||||
configure_native_constructor(ctor, crate::builtins::regexp::regexp_constructor as *const (), 2);
|
||||
|
||||
apply_binding_table(
|
||||
kernel.builtin_world(),
|
||||
@@ -25,20 +21,9 @@ pub fn bind_regexp(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
&[
|
||||
("exec", crate::builtins::regexp::regexp_exec as *const (), 1),
|
||||
("test", crate::builtins::regexp::regexp_test as *const (), 1),
|
||||
(
|
||||
"toString",
|
||||
crate::builtins::regexp::regexp_to_string as *const (),
|
||||
0,
|
||||
),
|
||||
("toString", crate::builtins::regexp::regexp_to_string as *const (), 0),
|
||||
],
|
||||
);
|
||||
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"RegExp",
|
||||
ctor_ptr,
|
||||
crate::builtins::regexp::regexp_constructor,
|
||||
2
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "RegExp", ctor_ptr, crate::builtins::regexp::regexp_constructor, 2);
|
||||
}
|
||||
|
||||
@@ -26,12 +26,5 @@ pub fn bind_set(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
],
|
||||
);
|
||||
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"Set",
|
||||
ctor_ptr,
|
||||
crate::builtins::set::set_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "Set", ctor_ptr, crate::builtins::set::set_constructor, 1);
|
||||
}
|
||||
|
||||
@@ -41,9 +41,7 @@ pub fn bind_string(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
|
||||
let si_str = kernel.string_forge().intern("String").0;
|
||||
let str_shape = kernel.shape_forge().make_shape(global.shape_id(), si_str);
|
||||
let str_val = JsValue::from_js_object(
|
||||
kernel.builtin_world().string_constructor.as_ptr() as *mut JsObject
|
||||
);
|
||||
let str_val = JsValue::from_js_object(kernel.builtin_world().string_constructor.as_ptr() as *mut JsObject);
|
||||
global.set_shape_id(str_shape);
|
||||
global.ensure_hash_props().push(Box::new(str_val));
|
||||
global.bump_generation();
|
||||
|
||||
@@ -13,21 +13,13 @@ pub fn bind_symbol(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
let proto_ptr = kernel.builtin_world().symbol_proto.as_ptr() as *mut JsObject;
|
||||
let proto = unsafe { &mut *proto_ptr };
|
||||
|
||||
configure_native_constructor(
|
||||
ctor,
|
||||
crate::builtins::symbol::symbol_constructor as *const (),
|
||||
1,
|
||||
);
|
||||
configure_native_constructor(ctor, crate::builtins::symbol::symbol_constructor as *const (), 1);
|
||||
|
||||
apply_binding_table(
|
||||
kernel.builtin_world(),
|
||||
proto,
|
||||
kernel,
|
||||
&[(
|
||||
"toString",
|
||||
crate::builtins::symbol::symbol_to_string as *const (),
|
||||
0,
|
||||
)],
|
||||
&[("toString", crate::builtins::symbol::symbol_to_string as *const (), 0)],
|
||||
);
|
||||
|
||||
for (name, val) in [
|
||||
@@ -55,22 +47,10 @@ pub fn bind_symbol(kernel: &Arc<OxideKernel>, global: &mut JsObject) {
|
||||
bind_well_known_symbol(kernel, ctor, name, val);
|
||||
}
|
||||
|
||||
bind_constructor_hash!(
|
||||
kernel,
|
||||
global,
|
||||
"Symbol",
|
||||
ctor_ptr,
|
||||
crate::builtins::symbol::symbol_constructor,
|
||||
1
|
||||
);
|
||||
bind_constructor_hash!(kernel, global, "Symbol", ctor_ptr, crate::builtins::symbol::symbol_constructor, 1);
|
||||
}
|
||||
|
||||
fn bind_well_known_symbol(
|
||||
kernel: &Arc<OxideKernel>,
|
||||
ctor: &mut JsObject,
|
||||
name: &str,
|
||||
val: JsValue,
|
||||
) {
|
||||
fn bind_well_known_symbol(kernel: &Arc<OxideKernel>, ctor: &mut JsObject, name: &str, val: JsValue) {
|
||||
let si = kernel.string_forge().intern(name).0;
|
||||
let shape_id = kernel.shape_forge().make_shape(ctor.shape_id(), si);
|
||||
ctor.set_shape_id(shape_id);
|
||||
|
||||
@@ -49,20 +49,13 @@ macro_rules! bind_constructor_hash {
|
||||
}};
|
||||
}
|
||||
|
||||
pub(crate) fn configure_native_constructor(
|
||||
ctor: &mut JsObject,
|
||||
native_fn: *const (),
|
||||
arg_count: u8,
|
||||
) {
|
||||
pub(crate) fn configure_native_constructor(ctor: &mut JsObject, native_fn: *const (), arg_count: u8) {
|
||||
ctor.set_native_fn(Some(native_fn));
|
||||
ctor.set_native_arg_count(arg_count);
|
||||
}
|
||||
|
||||
pub(crate) fn apply_binding_table(
|
||||
world: &BuiltinWorld,
|
||||
target: &mut JsObject,
|
||||
kernel: &Arc<OxideKernel>,
|
||||
bindings: &[(&'static str, *const (), u8)],
|
||||
world: &BuiltinWorld, target: &mut JsObject, kernel: &Arc<OxideKernel>, bindings: &[(&'static str, *const (), u8)],
|
||||
) {
|
||||
let shape_forge = kernel.shape_forge().as_ref();
|
||||
let string_forge = kernel.string_forge().as_ref();
|
||||
@@ -71,12 +64,7 @@ pub(crate) fn apply_binding_table(
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn bind_global_value(
|
||||
kernel: &Arc<OxideKernel>,
|
||||
global: &mut JsObject,
|
||||
name: &str,
|
||||
value: JsValue,
|
||||
) {
|
||||
pub(crate) fn bind_global_value(kernel: &Arc<OxideKernel>, global: &mut JsObject, name: &str, value: JsValue) {
|
||||
let si = kernel.string_forge().intern(name).0;
|
||||
let shape = kernel.shape_forge().make_shape(global.shape_id(), si);
|
||||
global.set_shape_id(shape);
|
||||
|
||||
@@ -37,20 +37,11 @@ fn get_this_array_ref(val: JsValue) -> Result<*mut JsObject, JsValue> {
|
||||
|
||||
fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject {
|
||||
let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
vm.epoch().alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
n,
|
||||
vm.epoch().bump(),
|
||||
))
|
||||
vm.epoch()
|
||||
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump()))
|
||||
}
|
||||
|
||||
fn invoke_native_callback(
|
||||
vm: &mut Vm,
|
||||
callback_val: JsValue,
|
||||
this_val: JsValue,
|
||||
cb_args: &[JsValue],
|
||||
) -> NativeResult {
|
||||
fn invoke_native_callback(vm: &mut Vm, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
@@ -85,33 +76,24 @@ pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let val = vm.reg(args[1]);
|
||||
if val.is_int() {
|
||||
let n = val.as_int().max(0) as usize;
|
||||
let arr = vm.epoch().alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
proto_val,
|
||||
n,
|
||||
vm.epoch().bump(),
|
||||
));
|
||||
let arr = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
|
||||
return Ok(JsValue::from_js_object(arr));
|
||||
}
|
||||
if val.is_double() {
|
||||
let n = val.as_double() as usize;
|
||||
let arr = vm.epoch().alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
proto_val,
|
||||
n,
|
||||
vm.epoch().bump(),
|
||||
));
|
||||
let arr = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
|
||||
return Ok(JsValue::from_js_object(arr));
|
||||
}
|
||||
}
|
||||
|
||||
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
|
||||
let arr = vm.epoch().alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
proto_val,
|
||||
n_elems,
|
||||
vm.epoch().bump(),
|
||||
));
|
||||
let arr = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n_elems, vm.epoch().bump()));
|
||||
for i in 0..n_elems {
|
||||
unsafe {
|
||||
(*arr).set_prop_at(i, vm.reg(args[1 + i]));
|
||||
@@ -359,8 +341,7 @@ pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let arr = unsafe { &*arr_ptr };
|
||||
let n = arr.prop_count() as usize;
|
||||
let depth = if args.len() > 1 {
|
||||
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32).max(1)
|
||||
as usize
|
||||
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as i32).max(1) as usize
|
||||
} else {
|
||||
1
|
||||
};
|
||||
@@ -407,19 +388,10 @@ pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
let _ = invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
);
|
||||
let _ = invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]);
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
@@ -433,20 +405,11 @@ pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let new_arr = create_new_array(vm, n);
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(mapped) => unsafe {
|
||||
(*new_arr).set_prop_at(i, mapped);
|
||||
},
|
||||
@@ -468,20 +431,11 @@ pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let mut kept: Vec<JsValue> = Vec::new();
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(result_val) => {
|
||||
let sf = vm.kernel().string_forge().as_ref();
|
||||
if coercion::to_boolean(result_val, sf) {
|
||||
@@ -548,19 +502,10 @@ pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(result_val) => {
|
||||
let sf = vm.kernel().string_forge().as_ref();
|
||||
if coercion::to_boolean(result_val, sf) {
|
||||
@@ -582,19 +527,10 @@ pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Ok(JsValue::bool(false));
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(result_val) => {
|
||||
let sf = vm.kernel().string_forge().as_ref();
|
||||
if coercion::to_boolean(result_val, sf) {
|
||||
@@ -616,19 +552,10 @@ pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Ok(JsValue::bool(false));
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(result_val) => {
|
||||
let sf = vm.kernel().string_forge().as_ref();
|
||||
if !coercion::to_boolean(result_val, sf) {
|
||||
@@ -650,20 +577,11 @@ pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !callback_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
let mut flat: Vec<JsValue> = Vec::new();
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(result) => {
|
||||
if result.is_object() {
|
||||
let r_ptr = result.as_js_object_ptr();
|
||||
@@ -730,14 +648,9 @@ pub fn array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let arr_ptr = array_ptr!(vm, args);
|
||||
let arr = unsafe { &mut *arr_ptr };
|
||||
let len = arr.prop_count() as usize;
|
||||
let value = if args.len() > 1 {
|
||||
vm.reg(args[1])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let start = if args.len() > 2 {
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0)
|
||||
as usize
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as i32).max(0) as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -771,8 +684,7 @@ pub fn array_copy_within(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
0
|
||||
};
|
||||
let end = if args.len() > 3 {
|
||||
(coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize)
|
||||
.min(len)
|
||||
(coercion::to_number(vm.reg(args[3]), vm.kernel().string_forge().as_ref()) as i32 as usize).min(len)
|
||||
} else {
|
||||
len
|
||||
};
|
||||
@@ -810,11 +722,7 @@ pub fn array_last_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let arr_ptr = array_ptr!(vm, args);
|
||||
let arr = unsafe { &*arr_ptr };
|
||||
let len = arr.prop_count() as i32;
|
||||
let search = if args.len() > 1 {
|
||||
vm.reg(args[1])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let search = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
for i in (0..len).rev() {
|
||||
if coercion::strict_equality(arr.get_prop_at(i), search) {
|
||||
return Ok(JsValue::int(i));
|
||||
@@ -829,19 +737,10 @@ pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(JsValue::int(-1));
|
||||
}
|
||||
let callback_val = vm.reg(args[1]);
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in 0..n {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i as i32), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i as i32), vm.reg(args[0])]) {
|
||||
Ok(r) => {
|
||||
if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) {
|
||||
return Ok(JsValue::int(i as i32));
|
||||
@@ -862,19 +761,10 @@ pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
let callback_val = vm.reg(args[1]);
|
||||
let this_val = if args.len() > 2 {
|
||||
vm.reg(args[2])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
|
||||
for i in (0..n).rev() {
|
||||
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
|
||||
match invoke_native_callback(
|
||||
vm,
|
||||
callback_val,
|
||||
this_val,
|
||||
&[elem, JsValue::int(i), vm.reg(args[0])],
|
||||
) {
|
||||
match invoke_native_callback(vm, callback_val, this_val, &[elem, JsValue::int(i), vm.reg(args[0])]) {
|
||||
Ok(r) => {
|
||||
if coercion::to_boolean(r, vm.kernel().string_forge().as_ref()) {
|
||||
return Ok(elem);
|
||||
|
||||
@@ -13,11 +13,7 @@ pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
} else if arg.is_bool() {
|
||||
arg.as_bool()
|
||||
} else if arg.is_string() {
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(arg.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(arg.as_string_index()).unwrap_or_default();
|
||||
!s.is_empty()
|
||||
} else if arg.is_object() {
|
||||
true
|
||||
|
||||
@@ -30,10 +30,7 @@ fn is_date(vm: &Vm, obj: &JsObject) -> bool {
|
||||
|
||||
fn ensure_date(vm: &mut Vm, obj: &JsObject) -> NativeResult {
|
||||
if !is_date(vm, obj) {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
@@ -114,11 +111,7 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
let val = vm.reg(args[1]);
|
||||
if val.is_string() {
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
|
||||
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
|
||||
let mut ts = f64::NAN;
|
||||
for fmt in &formats {
|
||||
@@ -129,10 +122,7 @@ pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
if ts.is_nan() {
|
||||
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
|
||||
if let Some(ndt) = nd
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.and_then(|n| n.and_local_timezone(Utc).earliest())
|
||||
{
|
||||
if let Some(ndt) = nd.and_hms_opt(0, 0, 0).and_then(|n| n.and_local_timezone(Utc).earliest()) {
|
||||
ts = ndt.timestamp_millis() as f64;
|
||||
}
|
||||
}
|
||||
@@ -192,11 +182,7 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !val.is_string() {
|
||||
return Ok(JsValue::float(f64::NAN));
|
||||
}
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
|
||||
let formats = ["%Y-%m-%dT%H:%M:%S%.fZ", "%Y-%m-%dT%H:%M:%S%.f"];
|
||||
let mut ts = f64::NAN;
|
||||
for fmt in &formats {
|
||||
@@ -207,10 +193,7 @@ pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
if ts.is_nan() {
|
||||
if let Ok(nd) = NaiveDate::parse_from_str(&s, "%Y-%m-%d") {
|
||||
if let Some(ndt) = nd
|
||||
.and_hms_opt(0, 0, 0)
|
||||
.and_then(|n| n.and_local_timezone(Utc).earliest())
|
||||
{
|
||||
if let Some(ndt) = nd.and_hms_opt(0, 0, 0).and_then(|n| n.and_local_timezone(Utc).earliest()) {
|
||||
ts = ndt.timestamp_millis() as f64;
|
||||
}
|
||||
}
|
||||
@@ -227,10 +210,7 @@ macro_rules! make_getter {
|
||||
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -251,10 +231,7 @@ macro_rules! make_utc_getter {
|
||||
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -270,83 +247,27 @@ macro_rules! make_utc_getter {
|
||||
};
|
||||
}
|
||||
|
||||
make_getter!(
|
||||
date_get_time,
|
||||
|dt: DateTime<Utc>| dt.timestamp_millis() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_full_year,
|
||||
|dt: DateTime<Utc>| dt.year() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_month,
|
||||
|dt: DateTime<Utc>| dt.month0() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(date_get_time, |dt: DateTime<Utc>| dt.timestamp_millis() as f64, f64::NAN);
|
||||
make_getter!(date_get_full_year, |dt: DateTime<Utc>| dt.year() as f64, f64::NAN);
|
||||
make_getter!(date_get_month, |dt: DateTime<Utc>| dt.month0() as f64, f64::NAN);
|
||||
make_getter!(date_get_date, |dt: DateTime<Utc>| dt.day() as f64, f64::NAN);
|
||||
make_getter!(
|
||||
date_get_day,
|
||||
|dt: DateTime<Utc>| dt.weekday().num_days_from_sunday() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_hours,
|
||||
|dt: DateTime<Utc>| dt.hour() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_minutes,
|
||||
|dt: DateTime<Utc>| dt.minute() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_seconds,
|
||||
|dt: DateTime<Utc>| dt.second() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(
|
||||
date_get_milliseconds,
|
||||
|dt: DateTime<Utc>| dt.timestamp_subsec_millis() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_getter!(date_get_day, |dt: DateTime<Utc>| dt.weekday().num_days_from_sunday() as f64, f64::NAN);
|
||||
make_getter!(date_get_hours, |dt: DateTime<Utc>| dt.hour() as f64, f64::NAN);
|
||||
make_getter!(date_get_minutes, |dt: DateTime<Utc>| dt.minute() as f64, f64::NAN);
|
||||
make_getter!(date_get_seconds, |dt: DateTime<Utc>| dt.second() as f64, f64::NAN);
|
||||
make_getter!(date_get_milliseconds, |dt: DateTime<Utc>| dt.timestamp_subsec_millis() as f64, f64::NAN);
|
||||
|
||||
make_utc_getter!(
|
||||
date_get_utc_full_year,
|
||||
|ndt: NaiveDateTime| ndt.date().year() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(
|
||||
date_get_utc_month,
|
||||
|ndt: NaiveDateTime| ndt.date().month0() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(
|
||||
date_get_utc_date,
|
||||
|ndt: NaiveDateTime| ndt.date().day() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(date_get_utc_full_year, |ndt: NaiveDateTime| ndt.date().year() as f64, f64::NAN);
|
||||
make_utc_getter!(date_get_utc_month, |ndt: NaiveDateTime| ndt.date().month0() as f64, f64::NAN);
|
||||
make_utc_getter!(date_get_utc_date, |ndt: NaiveDateTime| ndt.date().day() as f64, f64::NAN);
|
||||
make_utc_getter!(
|
||||
date_get_utc_day,
|
||||
|ndt: NaiveDateTime| ndt.date().weekday().num_days_from_sunday() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(
|
||||
date_get_utc_hours,
|
||||
|ndt: NaiveDateTime| ndt.time().hour() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(
|
||||
date_get_utc_minutes,
|
||||
|ndt: NaiveDateTime| ndt.time().minute() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(
|
||||
date_get_utc_seconds,
|
||||
|ndt: NaiveDateTime| ndt.time().second() as f64,
|
||||
f64::NAN
|
||||
);
|
||||
make_utc_getter!(date_get_utc_hours, |ndt: NaiveDateTime| ndt.time().hour() as f64, f64::NAN);
|
||||
make_utc_getter!(date_get_utc_minutes, |ndt: NaiveDateTime| ndt.time().minute() as f64, f64::NAN);
|
||||
make_utc_getter!(date_get_utc_seconds, |ndt: NaiveDateTime| ndt.time().second() as f64, f64::NAN);
|
||||
make_utc_getter!(
|
||||
date_get_utc_milliseconds,
|
||||
|ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0,
|
||||
@@ -356,10 +277,7 @@ make_utc_getter!(
|
||||
pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -382,10 +300,7 @@ fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
|
||||
pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &mut *raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -438,10 +353,7 @@ pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
None => return Ok(JsValue::float(f64::NAN)),
|
||||
};
|
||||
let d = get_opt_arg(vm, args, 2, dt.day0());
|
||||
let nd = dt
|
||||
.with_month0(v as u32)
|
||||
.and_then(|x| x.with_day0(d))
|
||||
.unwrap_or(dt);
|
||||
let nd = dt.with_month0(v as u32).and_then(|x| x.with_day0(d)).unwrap_or(dt);
|
||||
let ts = nd.timestamp_millis() as f64;
|
||||
set_timestamp(obj, ts);
|
||||
Ok(JsValue::float(ts))
|
||||
@@ -563,10 +475,7 @@ pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -588,10 +497,7 @@ pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
@@ -625,10 +531,7 @@ pub fn date_to_utc_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub fn date_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let raw = vm.reg(if args.is_empty() { 0 } else { args[0] });
|
||||
if !raw.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on incompatible receiver",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
|
||||
}
|
||||
let obj = unsafe { &*raw.as_js_object_ptr() };
|
||||
ensure_date(vm, obj)?;
|
||||
|
||||
@@ -9,16 +9,10 @@ use crate::coercion;
|
||||
use crate::native::NativeResult;
|
||||
use crate::vm::Vm;
|
||||
|
||||
fn create_error_object(
|
||||
vm: &mut Vm,
|
||||
error_proto_ptr: *mut JsObject,
|
||||
name: &str,
|
||||
message: &str,
|
||||
) -> JsValue {
|
||||
let obj = vm.epoch().alloc(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(error_proto_ptr),
|
||||
));
|
||||
fn create_error_object(vm: &mut Vm, error_proto_ptr: *mut JsObject, name: &str, message: &str) -> JsValue {
|
||||
let obj = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(error_proto_ptr)));
|
||||
|
||||
let sf = Arc::clone(vm.kernel().string_forge());
|
||||
let sh = Arc::clone(vm.kernel().shape_forge());
|
||||
@@ -132,18 +126,13 @@ pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
.map(|b| **b)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
(
|
||||
vm.lookup_str(name_val)
|
||||
.unwrap_or_else(|| "Error".to_string()),
|
||||
vm.lookup_str(name_val).unwrap_or_else(|| "Error".to_string()),
|
||||
vm.lookup_str(msg_val).unwrap_or_default(),
|
||||
)
|
||||
} else {
|
||||
("Error".to_string(), String::new())
|
||||
};
|
||||
let result = if msg.is_empty() {
|
||||
name
|
||||
} else {
|
||||
format!("{}: {}", name, msg)
|
||||
};
|
||||
let result = if msg.is_empty() { name } else { format!("{}: {}", name, msg) };
|
||||
let si = vm.kernel().string_forge().intern(&result).0;
|
||||
Ok(JsValue::string(si, 0))
|
||||
}
|
||||
@@ -184,14 +173,7 @@ mod tests {
|
||||
});
|
||||
|
||||
let stack = error_stack_getter(&mut vm, &[0]).unwrap();
|
||||
let stack_str = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(stack.as_string_index())
|
||||
.unwrap_or_default();
|
||||
assert!(
|
||||
stack_str.contains("foo"),
|
||||
"expected function name in stack: {stack_str}"
|
||||
);
|
||||
let stack_str = vm.kernel().string_forge().lookup(stack.as_string_index()).unwrap_or_default();
|
||||
assert!(stack_str.contains("foo"), "expected function name in stack: {stack_str}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,7 @@ use oxide_types::value::JsValue;
|
||||
use crate::native::{NativeFn, NativeResult};
|
||||
use crate::vm::Vm;
|
||||
|
||||
fn invoke_target(
|
||||
vm: &mut Vm,
|
||||
target_val: JsValue,
|
||||
this_val: JsValue,
|
||||
arg_regs: &[u8],
|
||||
) -> NativeResult {
|
||||
fn invoke_target(vm: &mut Vm, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult {
|
||||
if !target_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
@@ -79,11 +74,7 @@ pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let target_val = vm.reg(args[0]);
|
||||
let this_val = if args.len() > 1 {
|
||||
vm.reg(args[1])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
let arg_regs: Vec<u8> = args.iter().skip(2).copied().collect();
|
||||
invoke_target(vm, target_val, this_val, &arg_regs)
|
||||
}
|
||||
@@ -93,11 +84,7 @@ pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let target_val = vm.reg(args[0]);
|
||||
let this_val = if args.len() > 1 {
|
||||
vm.reg(args[1])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let this_val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
|
||||
let arg_regs: Vec<u8>;
|
||||
if args.len() > 2 {
|
||||
@@ -139,15 +126,9 @@ pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !target_val.is_object() {
|
||||
return Err(JsValue::undefined());
|
||||
}
|
||||
let bound_this = if args.len() > 1 {
|
||||
vm.reg(args[1])
|
||||
} else {
|
||||
JsValue::undefined()
|
||||
};
|
||||
let bound_this = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
|
||||
|
||||
let wrapper = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
let wrapper = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
unsafe {
|
||||
(*wrapper).set_function(true);
|
||||
(*wrapper).set_native_fn(Some(bind_dispatcher as *const ()));
|
||||
@@ -189,20 +170,13 @@ pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
.unwrap_or_else(|| {
|
||||
let sub_idx = func.sub_module_index();
|
||||
if sub_idx > 0 && (sub_idx as usize) <= vm.sub_modules.len() {
|
||||
vm.sub_modules[sub_idx as usize - 1]
|
||||
.function_name
|
||||
.clone()
|
||||
.unwrap_or_default()
|
||||
vm.sub_modules[sub_idx as usize - 1].function_name.clone().unwrap_or_default()
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
});
|
||||
|
||||
let body = if func.native_fn().is_some() {
|
||||
"[native code]"
|
||||
} else {
|
||||
"[bytecode]"
|
||||
};
|
||||
let body = if func.native_fn().is_some() { "[native code]" } else { "[bytecode]" };
|
||||
let result = if name.is_empty() {
|
||||
format!("function () {{ {body} }}")
|
||||
} else {
|
||||
|
||||
@@ -2,9 +2,6 @@ use std::sync::Arc;
|
||||
|
||||
use oxide_kernel::kernel::OxideKernel;
|
||||
|
||||
pub fn register_global_properties(
|
||||
_kernel: &Arc<OxideKernel>,
|
||||
_bump: &bumpalo::Bump,
|
||||
) -> Result<(), String> {
|
||||
pub fn register_global_properties(_kernel: &Arc<OxideKernel>, _bump: &bumpalo::Bump) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -11,25 +11,19 @@ use crate::vm::Vm;
|
||||
|
||||
pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if args.len() < 2 {
|
||||
return Err(crate::builtins::error::create_syntax_error(
|
||||
vm,
|
||||
"JSON.parse requires 1 argument",
|
||||
));
|
||||
return Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse requires 1 argument"));
|
||||
}
|
||||
let val = vm.reg(args[1]);
|
||||
if !val.is_string() {
|
||||
return Err(crate::builtins::error::create_syntax_error(
|
||||
vm,
|
||||
"JSON.parse: argument is not a string",
|
||||
));
|
||||
return Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse: argument is not a string"));
|
||||
}
|
||||
let text = {
|
||||
let si = val.as_string_index();
|
||||
vm.kernel().string_forge().lookup(si).unwrap_or_default()
|
||||
};
|
||||
|
||||
let parsed: serde_json::Value = serde_json::from_str(&text)
|
||||
.map_err(|e| crate::builtins::error::create_syntax_error(vm, &format!("{}", e)))?;
|
||||
let parsed: serde_json::Value =
|
||||
serde_json::from_str(&text).map_err(|e| crate::builtins::error::create_syntax_error(vm, &format!("{}", e)))?;
|
||||
|
||||
Ok(value_to_jsvalue(vm, &parsed))
|
||||
}
|
||||
@@ -65,8 +59,7 @@ fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
|
||||
}
|
||||
serde_json::Value::Object(map) => {
|
||||
let object_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let mut obj =
|
||||
JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
|
||||
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
|
||||
for (key, val) in map {
|
||||
let si = vm.kernel().string_forge().intern(key).0;
|
||||
let jsv = value_to_jsvalue(vm, val);
|
||||
@@ -95,12 +88,7 @@ pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
Ok(vm.intern(&output))
|
||||
}
|
||||
|
||||
fn jsvalue_to_json(
|
||||
vm: &Vm,
|
||||
val: JsValue,
|
||||
visited: &mut HashSet<*const u8>,
|
||||
out: &mut String,
|
||||
) -> Result<(), ()> {
|
||||
fn jsvalue_to_json(vm: &Vm, val: JsValue, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
|
||||
if val.is_null() {
|
||||
out.push_str("null");
|
||||
} else if val.is_undefined() {
|
||||
@@ -169,12 +157,7 @@ fn stringify_string(s: &str, out: &mut String) {
|
||||
out.push('"');
|
||||
}
|
||||
|
||||
fn stringify_object(
|
||||
vm: &Vm,
|
||||
obj: &JsObject,
|
||||
visited: &mut HashSet<*const u8>,
|
||||
out: &mut String,
|
||||
) -> Result<(), ()> {
|
||||
fn stringify_object(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
|
||||
out.push('{');
|
||||
let keys = walk_own_keys(vm, obj);
|
||||
let mut first = true;
|
||||
@@ -204,12 +187,7 @@ fn stringify_object(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stringify_array(
|
||||
vm: &Vm,
|
||||
obj: &JsObject,
|
||||
visited: &mut HashSet<*const u8>,
|
||||
out: &mut String,
|
||||
) -> Result<(), ()> {
|
||||
fn stringify_array(vm: &Vm, obj: &JsObject, visited: &mut HashSet<*const u8>, out: &mut String) -> Result<(), ()> {
|
||||
out.push('[');
|
||||
let len = obj.prop_vec_len();
|
||||
for i in 0..len {
|
||||
|
||||
@@ -8,37 +8,22 @@ use crate::vm::Vm;
|
||||
|
||||
const MAP_PROP_INDEX: u8 = 0;
|
||||
|
||||
fn get_map_inner(
|
||||
vm: &mut Vm,
|
||||
this_val: JsValue,
|
||||
) -> Result<*mut indexmap::IndexMap<SetKey, JsValue>, JsValue> {
|
||||
fn get_map_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut indexmap::IndexMap<SetKey, JsValue>, JsValue> {
|
||||
if !this_val.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on non-Map object",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on non-Map object"));
|
||||
}
|
||||
let map_ptr = this_val.as_js_object_ptr();
|
||||
if map_ptr.is_null() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Map internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
|
||||
}
|
||||
let map_obj = unsafe { &*map_ptr };
|
||||
let inner_val = map_obj.get_prop_at(MAP_PROP_INDEX);
|
||||
if !inner_val.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Map internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
|
||||
}
|
||||
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexMap<SetKey, JsValue>;
|
||||
if inner_ptr.is_null() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Map internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
|
||||
}
|
||||
Ok(inner_ptr)
|
||||
}
|
||||
|
||||
@@ -18,16 +18,8 @@ fn arg1(vm: &Vm, args: &[u8]) -> f64 {
|
||||
|
||||
fn arg2(vm: &Vm, args: &[u8]) -> (f64, f64) {
|
||||
(
|
||||
if args.len() > 1 {
|
||||
num(vm, args[1])
|
||||
} else {
|
||||
f64::NAN
|
||||
},
|
||||
if args.len() > 2 {
|
||||
num(vm, args[2])
|
||||
} else {
|
||||
f64::NAN
|
||||
},
|
||||
if args.len() > 1 { num(vm, args[1]) } else { f64::NAN },
|
||||
if args.len() > 2 { num(vm, args[2]) } else { f64::NAN },
|
||||
)
|
||||
}
|
||||
|
||||
@@ -82,11 +74,7 @@ pub fn math_atan2(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
|
||||
pub fn math_round(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let x = arg1(vm, args);
|
||||
let r = if x < 0.0 {
|
||||
(x - 0.5).ceil()
|
||||
} else {
|
||||
(x + 0.5).floor()
|
||||
};
|
||||
let r = if x < 0.0 { (x - 0.5).ceil() } else { (x + 0.5).floor() };
|
||||
Ok(JsValue::float(r))
|
||||
}
|
||||
|
||||
|
||||
@@ -177,10 +177,7 @@ pub fn number_is_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub fn number_is_safe_integer(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let val = vm.reg(if args.len() > 1 { args[1] } else { args[0] });
|
||||
let n = coercion::to_number(val, vm.kernel().string_forge().as_ref());
|
||||
let safe = n.trunc() == n
|
||||
&& n.is_finite()
|
||||
&& n >= -9007199254740991i64 as f64
|
||||
&& n <= 9007199254740991i64 as f64;
|
||||
let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64;
|
||||
Ok(JsValue::bool(safe))
|
||||
}
|
||||
|
||||
@@ -196,11 +193,7 @@ pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(vm.intern("NaN"));
|
||||
}
|
||||
if n.is_infinite() {
|
||||
return Ok(vm.intern(if n.is_sign_positive() {
|
||||
"Infinity"
|
||||
} else {
|
||||
"-Infinity"
|
||||
}));
|
||||
return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
|
||||
}
|
||||
let formatted = format!("{:.digits$e}", n);
|
||||
Ok(vm.intern(&formatted))
|
||||
@@ -218,11 +211,7 @@ pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(vm.intern("NaN"));
|
||||
}
|
||||
if n.is_infinite() {
|
||||
return Ok(vm.intern(if n.is_sign_positive() {
|
||||
"Infinity"
|
||||
} else {
|
||||
"-Infinity"
|
||||
}));
|
||||
return Ok(vm.intern(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
|
||||
}
|
||||
let formatted = format!("{:.precision$}", n);
|
||||
Ok(vm.intern(&formatted))
|
||||
|
||||
@@ -38,9 +38,7 @@ pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> {
|
||||
}
|
||||
|
||||
pub fn object_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
|
||||
let obj = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
let obj = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
|
||||
Ok(JsValue::from_js_object(obj))
|
||||
}
|
||||
|
||||
@@ -95,9 +93,7 @@ pub fn object_create(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if !proto_val.is_null() && !proto_val.is_object() {
|
||||
return Ok(JsValue::undefined());
|
||||
}
|
||||
let obj = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
let obj = vm.epoch().alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
Ok(JsValue::from_js_object(obj))
|
||||
}
|
||||
|
||||
@@ -210,10 +206,9 @@ pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeRes
|
||||
let sf_ptr = vm.kernel().string_forge().as_ref() as *const StringForge;
|
||||
let sh_ptr = vm.kernel().shape_forge().as_ref() as *const ShapeForge;
|
||||
let desc_proto = vm.kernel().builtin_world().object_proto.as_ptr() as *mut JsObject;
|
||||
let desc = vm.epoch().alloc(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(desc_proto),
|
||||
));
|
||||
let desc = vm
|
||||
.epoch()
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(desc_proto)));
|
||||
let true_val = JsValue::bool(true);
|
||||
let sf = unsafe { &*sf_ptr };
|
||||
let sh = unsafe { &*sh_ptr };
|
||||
|
||||
@@ -19,13 +19,7 @@ fn get_regexp_ptr(vm: &Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> {
|
||||
}
|
||||
let ptr = this_val.as_js_object_ptr();
|
||||
if ptr.is_null() {
|
||||
return Err(JsValue::string(
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.intern("TypeError: null object")
|
||||
.0,
|
||||
0,
|
||||
));
|
||||
return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: null object").0, 0));
|
||||
}
|
||||
Ok(ptr)
|
||||
}
|
||||
@@ -101,10 +95,7 @@ pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
Err(e) => {
|
||||
let msg = format!("SyntaxError: Invalid regular expression: {}", e);
|
||||
return Err(JsValue::string(
|
||||
vm.kernel().string_forge().intern(&msg).0,
|
||||
0,
|
||||
));
|
||||
return Err(JsValue::string(vm.kernel().string_forge().intern(&msg).0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,13 +119,7 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
|
||||
let fn_ptr = match re.native_fn() {
|
||||
None => {
|
||||
return Err(JsValue::string(
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.intern("TypeError: invalid RegExp")
|
||||
.0,
|
||||
0,
|
||||
));
|
||||
return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: invalid RegExp").0, 0));
|
||||
}
|
||||
Some(p) => p,
|
||||
};
|
||||
@@ -144,8 +129,7 @@ pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
vm.kernel().string_forge().as_ref(),
|
||||
vm.reg(if args.len() > 1 { args[1] } else { args[0] }),
|
||||
);
|
||||
let last_index =
|
||||
coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize;
|
||||
let last_index = coercion::to_number(get_prop(re, 0), vm.kernel().string_forge().as_ref()) as usize;
|
||||
let is_global = get_prop(re, 3).as_bool();
|
||||
|
||||
if is_global {
|
||||
@@ -163,13 +147,7 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let re = unsafe { &*re_ptr };
|
||||
match re.native_fn() {
|
||||
None => {
|
||||
return Err(JsValue::string(
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.intern("TypeError: invalid RegExp")
|
||||
.0,
|
||||
0,
|
||||
));
|
||||
return Err(JsValue::string(vm.kernel().string_forge().intern("TypeError: invalid RegExp").0, 0));
|
||||
}
|
||||
Some(p) => p,
|
||||
}
|
||||
@@ -200,25 +178,19 @@ pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
if let Some(m) = match_result {
|
||||
let mut match_obj = JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(
|
||||
vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject
|
||||
),
|
||||
JsValue::from_js_object(vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject),
|
||||
);
|
||||
|
||||
let full_match = &haystack[m.start()..m.end()];
|
||||
let full_si = vm.kernel().string_forge().intern(full_match).0;
|
||||
match_obj
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::string(full_si, 0)));
|
||||
match_obj.ensure_hash_props().push(Box::new(JsValue::string(full_si, 0)));
|
||||
|
||||
let captures = regex.captures(&haystack[m.start()..m.end()]);
|
||||
if let Some(caps) = &captures {
|
||||
for i in 1..caps.len() {
|
||||
let cap_str = caps.get(i).map(|cm| cm.as_str()).unwrap_or("");
|
||||
let si = vm.kernel().string_forge().intern(cap_str).0;
|
||||
match_obj
|
||||
.ensure_hash_props()
|
||||
.push(Box::new(JsValue::string(si, 0)));
|
||||
match_obj.ensure_hash_props().push(Box::new(JsValue::string(si, 0)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -25,10 +25,7 @@ impl PartialEq for SetKey {
|
||||
}
|
||||
return a == b;
|
||||
}
|
||||
unsafe {
|
||||
std::mem::transmute::<JsValue, u64>(self.0)
|
||||
== std::mem::transmute::<JsValue, u64>(other.0)
|
||||
}
|
||||
unsafe { std::mem::transmute::<JsValue, u64>(self.0) == std::mem::transmute::<JsValue, u64>(other.0) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,37 +48,22 @@ impl Hash for SetKey {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_set_inner(
|
||||
vm: &mut Vm,
|
||||
this_val: JsValue,
|
||||
) -> Result<*mut indexmap::IndexSet<SetKey>, JsValue> {
|
||||
fn get_set_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut indexmap::IndexSet<SetKey>, JsValue> {
|
||||
if !this_val.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"called on non-Set object",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "called on non-Set object"));
|
||||
}
|
||||
let set_ptr = this_val.as_js_object_ptr();
|
||||
if set_ptr.is_null() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Set internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
|
||||
}
|
||||
let set_obj = unsafe { &*set_ptr };
|
||||
let inner_val = set_obj.get_prop_at(SET_PROP_INDEX);
|
||||
if !inner_val.is_object() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Set internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
|
||||
}
|
||||
let inner_ptr = inner_val.as_js_object_ptr() as *mut indexmap::IndexSet<SetKey>;
|
||||
if inner_ptr.is_null() {
|
||||
return Err(crate::builtins::error::create_type_error(
|
||||
vm,
|
||||
"Set internal state invalid",
|
||||
));
|
||||
return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
|
||||
}
|
||||
Ok(inner_ptr)
|
||||
}
|
||||
|
||||
@@ -14,12 +14,9 @@ fn this_string(vm: &Vm, args: &[u8]) -> String {
|
||||
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
|
||||
let proto = vm.kernel().builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let n = parts.len();
|
||||
let arr = vm.epoch().alloc(JsObject::new_array(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto),
|
||||
n,
|
||||
vm.epoch().bump(),
|
||||
));
|
||||
let arr =
|
||||
vm.epoch()
|
||||
.alloc(JsObject::new_array(EMPTY_SHAPE_ID, JsValue::from_js_object(proto), n, vm.epoch().bump()));
|
||||
unsafe {
|
||||
for (i, s) in parts.iter().enumerate() {
|
||||
let sv = vm.intern(s);
|
||||
@@ -62,8 +59,7 @@ pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
let search = as_string(vm, vm.reg(args[1]));
|
||||
let pos = if args.len() > 2 {
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize)
|
||||
.min(s.len())
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -90,8 +86,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
let search = as_string(vm, vm.reg(args[1]));
|
||||
let pos = if args.len() > 2 {
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize)
|
||||
.min(s.len())
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -102,9 +97,7 @@ pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
|
||||
let haystack = &s.as_bytes()[pos..];
|
||||
if search.len() == 1 {
|
||||
Ok(JsValue::bool(
|
||||
memchr(search.as_bytes()[0], haystack).is_some(),
|
||||
))
|
||||
Ok(JsValue::bool(memchr(search.as_bytes()[0], haystack).is_some()))
|
||||
} else {
|
||||
Ok(JsValue::bool(s[pos..].contains(&search)))
|
||||
}
|
||||
@@ -221,8 +214,7 @@ pub fn string_trim(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub fn string_repeat(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let s = this_string(vm, args);
|
||||
let n = if args.len() > 1 {
|
||||
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize)
|
||||
.min(10000)
|
||||
(coercion::to_number(vm.reg(args[1]), vm.kernel().string_forge().as_ref()) as usize).min(10000)
|
||||
} else {
|
||||
1
|
||||
};
|
||||
@@ -236,11 +228,7 @@ pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
s.len()
|
||||
};
|
||||
let pad = if args.len() > 2 {
|
||||
as_string(vm, vm.reg(args[2]))
|
||||
} else {
|
||||
" ".to_string()
|
||||
};
|
||||
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
|
||||
if s.len() >= target || pad.is_empty() {
|
||||
return Ok(vm.intern(&s));
|
||||
}
|
||||
@@ -259,11 +247,7 @@ pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
} else {
|
||||
s.len()
|
||||
};
|
||||
let pad = if args.len() > 2 {
|
||||
as_string(vm, vm.reg(args[2]))
|
||||
} else {
|
||||
" ".to_string()
|
||||
};
|
||||
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
|
||||
if s.len() >= target || pad.is_empty() {
|
||||
return Ok(vm.intern(&s));
|
||||
}
|
||||
@@ -282,8 +266,7 @@ pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
let search = as_string(vm, vm.reg(args[1]));
|
||||
let pos = if args.len() > 2 {
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize)
|
||||
.min(s.len())
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
|
||||
} else {
|
||||
0
|
||||
};
|
||||
@@ -297,8 +280,7 @@ pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
let search = as_string(vm, vm.reg(args[1]));
|
||||
let end_pos = if args.len() > 2 {
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize)
|
||||
.min(s.len())
|
||||
(coercion::to_number(vm.reg(args[2]), vm.kernel().string_forge().as_ref()) as usize).min(s.len())
|
||||
} else {
|
||||
s.len()
|
||||
};
|
||||
@@ -349,11 +331,7 @@ pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(vm.intern(&s));
|
||||
}
|
||||
let pattern_val = vm.reg(args[1]);
|
||||
let replacement = if args.len() > 2 {
|
||||
as_string(vm, vm.reg(args[2]))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
|
||||
|
||||
if is_regexp_obj(pattern_val, vm) {
|
||||
let re_ptr = pattern_val.as_js_object_ptr();
|
||||
@@ -387,19 +365,13 @@ pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
None => return Ok(JsValue::undefined()),
|
||||
};
|
||||
let regex = unsafe { &*(fn_ptr as *const regex::Regex) };
|
||||
let matches: Vec<String> = regex
|
||||
.find_iter(&s)
|
||||
.map(|m| m.as_str().to_string())
|
||||
.collect();
|
||||
let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
|
||||
return Ok(make_string_array(vm, &matches));
|
||||
}
|
||||
|
||||
let pattern = as_string(vm, pattern_val);
|
||||
if let Some(pos) = s.find(&pattern) {
|
||||
return Ok(make_string_array(
|
||||
vm,
|
||||
&[s[pos..pos + pattern.len()].to_string()],
|
||||
));
|
||||
return Ok(make_string_array(vm, &[s[pos..pos + pattern.len()].to_string()]));
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
}
|
||||
@@ -467,11 +439,7 @@ pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
pub fn string_normalize(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
use unicode_normalization::UnicodeNormalization;
|
||||
let s = this_string(vm, args);
|
||||
let form = if args.len() > 1 {
|
||||
as_string(vm, vm.reg(args[1]))
|
||||
} else {
|
||||
"NFC".to_string()
|
||||
};
|
||||
let form = if args.len() > 1 { as_string(vm, vm.reg(args[1])) } else { "NFC".to_string() };
|
||||
let result: String = match form.as_str() {
|
||||
"NFD" => s.nfd().collect(),
|
||||
"NFKC" => s.nfkc().collect(),
|
||||
@@ -495,10 +463,7 @@ pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
None => return Ok(JsValue::undefined()),
|
||||
};
|
||||
let regex = unsafe { &*(fn_ptr as *const regex::Regex) };
|
||||
let matches: Vec<String> = regex
|
||||
.find_iter(&s)
|
||||
.map(|m| m.as_str().to_string())
|
||||
.collect();
|
||||
let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
|
||||
return Ok(make_string_array(vm, &matches));
|
||||
}
|
||||
Ok(JsValue::undefined())
|
||||
@@ -510,11 +475,7 @@ pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
return Ok(vm.intern(&s));
|
||||
}
|
||||
let pattern_val = vm.reg(args[1]);
|
||||
let replacement = if args.len() > 2 {
|
||||
as_string(vm, vm.reg(args[2]))
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
let replacement = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { String::new() };
|
||||
if is_regexp_obj(pattern_val, vm) {
|
||||
let re_ptr = pattern_val.as_js_object_ptr();
|
||||
let re = unsafe { &*re_ptr };
|
||||
|
||||
@@ -18,10 +18,7 @@ pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
let sp = vm.kernel().builtin_world().symbol_proto.as_ptr() as *mut JsObject;
|
||||
if std::ptr::eq(proto_ptr, sp) {
|
||||
return Err(JsValue::string(
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.intern("TypeError: Symbol is not a constructor")
|
||||
.0,
|
||||
vm.kernel().string_forge().intern("TypeError: Symbol is not a constructor").0,
|
||||
0,
|
||||
));
|
||||
}
|
||||
@@ -55,11 +52,7 @@ pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
|
||||
}
|
||||
|
||||
let idx = this_val.as_symbol_index();
|
||||
let desc = vm
|
||||
.symbol_descriptions
|
||||
.get(idx as usize)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
let desc = vm.symbol_descriptions.get(idx as usize).cloned().unwrap_or_default();
|
||||
let result = format!("Symbol({})", desc);
|
||||
let si = vm.kernel().string_forge().intern(&result).0;
|
||||
Ok(JsValue::string(si, 0))
|
||||
|
||||
@@ -25,9 +25,7 @@ pub fn to_number(val: JsValue, string_forge: &StringForge) -> f64 {
|
||||
return f64::NAN;
|
||||
}
|
||||
if val.is_string() {
|
||||
let s = string_forge
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
|
||||
return s.parse::<f64>().unwrap_or(f64::NAN);
|
||||
}
|
||||
if val.is_object() {
|
||||
@@ -68,9 +66,7 @@ pub fn to_string(string_forge: &StringForge, val: JsValue) -> String {
|
||||
return "undefined".to_string();
|
||||
}
|
||||
if val.is_string() {
|
||||
return string_forge
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
return string_forge.lookup(val.as_string_index()).unwrap_or_default();
|
||||
}
|
||||
if val.is_object() {
|
||||
return "[object]".to_string();
|
||||
@@ -93,9 +89,7 @@ pub fn to_boolean(val: JsValue, string_forge: &StringForge) -> bool {
|
||||
return !(d == 0.0 || d == -0.0 || d.is_nan());
|
||||
}
|
||||
if val.is_string() {
|
||||
let s = string_forge
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = string_forge.lookup(val.as_string_index()).unwrap_or_default();
|
||||
return !s.is_empty();
|
||||
}
|
||||
if val.is_object() {
|
||||
@@ -190,12 +184,8 @@ fn strict_double_eq(a: f64, b: f64) -> bool {
|
||||
|
||||
pub fn relational_compare(string_forge: &StringForge, lhs: JsValue, rhs: JsValue) -> Option<bool> {
|
||||
if lhs.is_string() && rhs.is_string() {
|
||||
let ls = string_forge
|
||||
.lookup(lhs.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let rs = string_forge
|
||||
.lookup(rhs.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let ls = string_forge.lookup(lhs.as_string_index()).unwrap_or_default();
|
||||
let rs = string_forge.lookup(rhs.as_string_index()).unwrap_or_default();
|
||||
return Some(ls < rs);
|
||||
}
|
||||
let l = to_number(lhs, string_forge);
|
||||
@@ -288,9 +278,7 @@ pub fn to_object(val: JsValue, vm: &mut Vm) -> Result<JsValue, &'static str> {
|
||||
}
|
||||
let proto_ptr = &*vm.object_prototype as *const JsObject as *mut JsObject;
|
||||
let proto_val = JsValue::from_js_object(proto_ptr);
|
||||
let obj = vm
|
||||
.epoch
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
let obj = vm.epoch.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
|
||||
let obj_val = JsValue::from_js_object(obj);
|
||||
let obj_ref = unsafe { &mut *obj };
|
||||
obj_ref.ensure_hash_props().push(Box::new(val));
|
||||
|
||||
@@ -5,11 +5,7 @@ use oxide_types::value::JsValue;
|
||||
|
||||
impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn build_native_args(
|
||||
first_arg_reg: u8,
|
||||
arg_count: usize,
|
||||
this_reg: u8,
|
||||
) -> ([u8; 257], usize) {
|
||||
pub(crate) fn build_native_args(first_arg_reg: u8, arg_count: usize, this_reg: u8) -> ([u8; 257], usize) {
|
||||
let mut args_buf = [0u8; 257];
|
||||
args_buf[0] = this_reg;
|
||||
let n = arg_count.min(256);
|
||||
@@ -21,12 +17,7 @@ impl Vm {
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_native_call(
|
||||
&mut self,
|
||||
obj: &JsObject,
|
||||
callee: JsValue,
|
||||
this_reg: u8,
|
||||
first_arg_reg: u8,
|
||||
arg_count: usize,
|
||||
&mut self, obj: &JsObject, callee: JsValue, this_reg: u8, first_arg_reg: u8, arg_count: usize,
|
||||
) -> Result<(), String> {
|
||||
let (args_buf, len) = Self::build_native_args(first_arg_reg, arg_count, this_reg);
|
||||
let args_slice = &args_buf[..len];
|
||||
|
||||
@@ -5,61 +5,37 @@ use oxide_types::value::JsValue;
|
||||
impl Vm {
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_eq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let eq = coercion::abstract_eq(
|
||||
self.regs[a],
|
||||
self.regs[b],
|
||||
self.kernel.string_forge().as_ref(),
|
||||
);
|
||||
let eq = coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel.string_forge().as_ref());
|
||||
self.regs[rd] = JsValue::bool(eq);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_neq(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let ne = !coercion::abstract_eq(
|
||||
self.regs[a],
|
||||
self.regs[b],
|
||||
self.kernel.string_forge().as_ref(),
|
||||
);
|
||||
let ne = !coercion::abstract_eq(self.regs[a], self.regs[b], self.kernel.string_forge().as_ref());
|
||||
self.regs[rd] = JsValue::bool(ne);
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_lt(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let rel = coercion::relational_compare(
|
||||
self.kernel.string_forge().as_ref(),
|
||||
self.regs[a],
|
||||
self.regs[b],
|
||||
);
|
||||
let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_gt(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let rel = coercion::relational_compare(
|
||||
self.kernel.string_forge().as_ref(),
|
||||
self.regs[b],
|
||||
self.regs[a],
|
||||
);
|
||||
let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[b], self.regs[a]);
|
||||
self.regs[rd] = JsValue::bool(rel.unwrap_or(false));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_lte(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let rel = coercion::relational_compare(
|
||||
self.kernel.string_forge().as_ref(),
|
||||
self.regs[b],
|
||||
self.regs[a],
|
||||
);
|
||||
let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[b], self.regs[a]);
|
||||
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn dispatch_gte(&mut self, rd: usize, a: usize, b: usize) {
|
||||
let rel = coercion::relational_compare(
|
||||
self.kernel.string_forge().as_ref(),
|
||||
self.regs[a],
|
||||
self.regs[b],
|
||||
);
|
||||
let rel = coercion::relational_compare(self.kernel.string_forge().as_ref(), self.regs[a], self.regs[b]);
|
||||
self.regs[rd] = JsValue::bool(!rel.unwrap_or(true));
|
||||
}
|
||||
|
||||
|
||||
@@ -6,64 +6,33 @@ use crate::coercion;
|
||||
use crate::vm::Vm;
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_member_op(
|
||||
&mut self,
|
||||
op: OpCode,
|
||||
rd: usize,
|
||||
a: usize,
|
||||
b: usize,
|
||||
) -> Result<(), String> {
|
||||
pub(crate) fn dispatch_member_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
match op {
|
||||
OpCode::MEMBER_INC => self.dispatch_member_inc(rd, a, b),
|
||||
OpCode::MEMBER_DEC => self.dispatch_member_dec(rd, a, b),
|
||||
OpCode::DYN_MEMBER_INC => self.dispatch_dyn_member_inc(rd, a, b),
|
||||
OpCode::DYN_MEMBER_DEC => self.dispatch_dyn_member_dec(rd, a, b),
|
||||
OpCode::COMPOUND_MEMBER_ADD => self.dispatch_compound_member_add(rd, a, b),
|
||||
OpCode::COMPOUND_MEMBER_SUB => self.dispatch_compound_member_numeric(
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_SUB on non-object",
|
||||
|l, r| l - r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_MUL => self.dispatch_compound_member_numeric(
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_MUL on non-object",
|
||||
|l, r| l * r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_DIV => self.dispatch_compound_member_numeric(
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_DIV on non-object",
|
||||
|l, r| l / r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_MOD => self.dispatch_compound_member_numeric(
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_MOD on non-object",
|
||||
|l, r| l % r,
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_EXP => self.dispatch_compound_member_numeric(
|
||||
rd,
|
||||
a,
|
||||
b,
|
||||
"COMPOUND_MEMBER_EXP on non-object",
|
||||
|l, r| l.powf(r),
|
||||
),
|
||||
OpCode::COMPOUND_MEMBER_SUB => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_SUB on non-object", |l, r| l - r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MUL => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MUL on non-object", |l, r| l * r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_DIV => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_DIV on non-object", |l, r| l / r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_MOD => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_MOD on non-object", |l, r| l % r)
|
||||
}
|
||||
OpCode::COMPOUND_MEMBER_EXP => {
|
||||
self.dispatch_compound_member_numeric(rd, a, b, "COMPOUND_MEMBER_EXP on non-object", |l, r| l.powf(r))
|
||||
}
|
||||
_ => unreachable!("non-member opcode passed to dispatch_member_op"),
|
||||
}
|
||||
}
|
||||
|
||||
fn member_target(
|
||||
&mut self,
|
||||
rd: usize,
|
||||
b: usize,
|
||||
error_msg: &str,
|
||||
) -> Result<(*mut JsObject, u32), String> {
|
||||
fn member_target(&mut self, rd: usize, b: usize, error_msg: &str) -> Result<(*mut JsObject, u32), String> {
|
||||
let obj_ptr = self.regs[rd].as_object_ptr() as *mut JsObject;
|
||||
if obj_ptr.is_null() {
|
||||
self.raise_type_error(error_msg)?;
|
||||
@@ -74,8 +43,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_member_inc(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object")
|
||||
else {
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_INC on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
@@ -88,8 +56,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_member_dec(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object")
|
||||
else {
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "MEMBER_DEC on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
@@ -109,9 +76,7 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[a]);
|
||||
let prop_val = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
let prop_val = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
|
||||
let new_val = JsValue::float(n + 1.0);
|
||||
self.set_or_create_prop_value(obj, prop_name_si, new_val);
|
||||
@@ -127,9 +92,7 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[a]);
|
||||
let prop_val = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
let prop_val = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
let n = coercion::to_number(prop_val, self.kernel.string_forge().as_ref());
|
||||
let new_val = JsValue::float(n - 1.0);
|
||||
self.set_or_create_prop_value(obj, prop_name_si, new_val);
|
||||
@@ -137,15 +100,8 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn dispatch_compound_member_add(
|
||||
&mut self,
|
||||
rd: usize,
|
||||
a: usize,
|
||||
b: usize,
|
||||
) -> Result<(), String> {
|
||||
let Ok((obj_ptr, prop_name_si)) =
|
||||
self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object")
|
||||
else {
|
||||
fn dispatch_compound_member_add(&mut self, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
let Ok((obj_ptr, prop_name_si)) = self.member_target(rd, b, "COMPOUND_MEMBER_ADD on non-object") else {
|
||||
return Ok(());
|
||||
};
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
@@ -166,12 +122,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
fn dispatch_compound_member_numeric<F>(
|
||||
&mut self,
|
||||
rd: usize,
|
||||
a: usize,
|
||||
b: usize,
|
||||
error_msg: &str,
|
||||
op: F,
|
||||
&mut self, rd: usize, a: usize, b: usize, error_msg: &str, op: F,
|
||||
) -> Result<(), String>
|
||||
where
|
||||
F: FnOnce(f64, f64) -> f64,
|
||||
|
||||
@@ -6,13 +6,7 @@ use oxide_types::value::JsValue;
|
||||
use crate::vm::Vm;
|
||||
|
||||
impl Vm {
|
||||
pub(crate) fn dispatch_property_op(
|
||||
&mut self,
|
||||
op: OpCode,
|
||||
rd: usize,
|
||||
a: usize,
|
||||
b: usize,
|
||||
) -> Result<(), String> {
|
||||
pub(crate) fn dispatch_property_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
|
||||
match op {
|
||||
OpCode::IC_GET_PROP => self.dispatch_ic_get_prop(a, b),
|
||||
OpCode::IC_SET_PROP => self.dispatch_ic_set_prop(rd, a, b),
|
||||
@@ -70,21 +64,13 @@ impl Vm {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
self.regs[a] = unsafe { *ptr };
|
||||
} else {
|
||||
self.regs[a] = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
}
|
||||
} else {
|
||||
self.regs[a] = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
}
|
||||
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if let Some(ptr) = obj.prop_ptr_at(pos) {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
}
|
||||
@@ -117,20 +103,13 @@ impl Vm {
|
||||
unsafe {
|
||||
*(cached_ptr as *mut JsValue) = self.regs[a];
|
||||
}
|
||||
} else if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
} else if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, self.regs[a]);
|
||||
if let Some(ptr) = obj.prop_ptr_at(pos) {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
}
|
||||
} else {
|
||||
let new_shape_id = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.make_shape(obj.shape_id(), prop_name_si);
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
let new_pos = obj.push_prop(self.regs[a]);
|
||||
obj.bump_generation();
|
||||
@@ -159,9 +138,7 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
self.regs[a] = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
self.regs[a] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -173,17 +150,10 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[b]);
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, self.regs[a]);
|
||||
} else {
|
||||
let new_shape_id = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.make_shape(obj.shape_id(), prop_name_si);
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(self.regs[a]);
|
||||
obj.bump_generation();
|
||||
@@ -199,9 +169,7 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[a]);
|
||||
self.regs[b] = self
|
||||
.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined());
|
||||
self.regs[b] = self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -213,17 +181,10 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &mut *obj_ptr };
|
||||
let prop_name_si = self.property_key_si(self.regs[a]);
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, self.regs[b]);
|
||||
} else {
|
||||
let new_shape_id = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.make_shape(obj.shape_id(), prop_name_si);
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(self.regs[b]);
|
||||
obj.bump_generation();
|
||||
|
||||
+60
-209
@@ -275,11 +275,7 @@ impl Vm {
|
||||
.resolve_property(obj, message_si)
|
||||
.and_then(|v| self.lookup_str(v))
|
||||
.unwrap_or_default();
|
||||
return if message.is_empty() {
|
||||
name
|
||||
} else {
|
||||
format!("{name}: {message}")
|
||||
};
|
||||
return if message.is_empty() { name } else { format!("{name}: {message}") };
|
||||
}
|
||||
format!("{val}")
|
||||
}
|
||||
@@ -307,8 +303,7 @@ impl Vm {
|
||||
let pat_val = JsValue::string(pat_si, 0);
|
||||
let flags_val = JsValue::string(flags_si, 0);
|
||||
|
||||
let ctor_ptr =
|
||||
self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
let ctor_ptr = self.kernel.builtin_world().regexp_constructor.as_ptr() as *mut JsObject;
|
||||
let ctor = unsafe { &*ctor_ptr };
|
||||
let Some(native_fn) = ctor.native_fn() else {
|
||||
return Err("SyntaxError: RegExp constructor unavailable".into());
|
||||
@@ -378,19 +373,10 @@ impl Vm {
|
||||
self.kernel.string_forge().intern(&key).0
|
||||
}
|
||||
|
||||
pub(crate) fn template_prop_ptr(
|
||||
&self,
|
||||
obj: &JsObject,
|
||||
template: &PropTemplate,
|
||||
) -> Option<*const JsValue> {
|
||||
pub(crate) fn template_prop_ptr(&self, obj: &JsObject, template: &PropTemplate) -> Option<*const JsValue> {
|
||||
let pos = template.position as usize;
|
||||
obj.hash_props_vec().and_then(|vec| {
|
||||
if pos < vec.len() {
|
||||
Some(&*vec[pos] as *const JsValue)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
obj.hash_props_vec()
|
||||
.and_then(|vec| if pos < vec.len() { Some(&*vec[pos] as *const JsValue) } else { None })
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
|
||||
@@ -398,11 +384,7 @@ impl Vm {
|
||||
if obj.is_array() && prop_name_si == length_si {
|
||||
return Some(JsValue::int(obj.prop_count() as i32));
|
||||
}
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
let val = obj.get_prop_at(pos);
|
||||
if !val.is_undefined() || obj.prop_vec_len() > pos as usize {
|
||||
return Some(val);
|
||||
@@ -411,11 +393,7 @@ impl Vm {
|
||||
let mut proto = obj.proto();
|
||||
while proto.is_object() {
|
||||
let proto_obj = unsafe { &*proto.as_js_object_ptr() };
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(proto_obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(proto_obj.shape_id(), prop_name_si) {
|
||||
let val = proto_obj.get_prop_at(pos);
|
||||
if !val.is_undefined() || proto_obj.prop_vec_len() > pos as usize {
|
||||
return Some(val);
|
||||
@@ -438,21 +416,15 @@ impl Vm {
|
||||
unsafe { *(cached_ptr as *const JsValue) }
|
||||
} else if let Some(template) = self.kernel.prop_forge().get_template(obj.shape_id()) {
|
||||
if template.prop_name != prop_name_si {
|
||||
self.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined())
|
||||
self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined())
|
||||
} else if let Some(ptr) = self.template_prop_ptr(obj, &template) {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
unsafe { *ptr }
|
||||
} else {
|
||||
self.resolve_property(obj, prop_name_si)
|
||||
.unwrap_or(JsValue::undefined())
|
||||
self.resolve_property(obj, prop_name_si).unwrap_or(JsValue::undefined())
|
||||
}
|
||||
} else if let Some(val) = self.resolve_property(obj, prop_name_si) {
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
if let Some(ptr) = obj.prop_ptr_at(pos) {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
}
|
||||
@@ -464,26 +436,16 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn set_member_prop(
|
||||
&mut self,
|
||||
obj: &mut JsObject,
|
||||
prop_name_si: u32,
|
||||
val: JsValue,
|
||||
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue,
|
||||
) -> Result<(), String> {
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, val);
|
||||
// IC write-back: 3 extension words (shape_id + ptr_lo + ptr_hi)
|
||||
if let Some(ptr) = obj.prop_ptr_at(pos) {
|
||||
self.write_ic_back(obj.shape_id(), ptr);
|
||||
}
|
||||
} else {
|
||||
let new_shape_id = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.make_shape(obj.shape_id(), prop_name_si);
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
let new_pos = obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
@@ -503,23 +465,11 @@ impl Vm {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn set_or_create_prop_value(
|
||||
&mut self,
|
||||
obj: &mut JsObject,
|
||||
prop_name_si: u32,
|
||||
val: JsValue,
|
||||
) {
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(obj.shape_id(), prop_name_si)
|
||||
{
|
||||
pub(crate) fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(obj.shape_id(), prop_name_si) {
|
||||
obj.set_prop_at(pos, val);
|
||||
} else {
|
||||
let new_shape_id = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.make_shape(obj.shape_id(), prop_name_si);
|
||||
let new_shape_id = self.kernel.shape_forge().make_shape(obj.shape_id(), prop_name_si);
|
||||
obj.set_shape_id(new_shape_id);
|
||||
obj.push_prop(val);
|
||||
obj.bump_generation();
|
||||
@@ -527,10 +477,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
pub(crate) fn write_ic_back(&mut self, shape_id: u32, ptr: *const JsValue) {
|
||||
debug_assert!(
|
||||
self.pc >= 3,
|
||||
"IC write-back requires 3 extension words before pc"
|
||||
);
|
||||
debug_assert!(self.pc >= 3, "IC write-back requires 3 extension words before pc");
|
||||
self.bytecode[self.pc - 3] = shape_id & 0x00FF_FFFF;
|
||||
self.bytecode[self.pc - 2] = ptr as u32;
|
||||
self.bytecode[self.pc - 1] = (ptr as u64 >> 32) as u32;
|
||||
@@ -572,11 +519,7 @@ impl Vm {
|
||||
for (name, reg) in &module.builtin_reg_map {
|
||||
let si = self.kernel.string_forge().intern(name.as_str()).0;
|
||||
let global = self.kernel.global_object();
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(global.shape_id(), si)
|
||||
{
|
||||
if let Some(pos) = self.kernel.shape_forge().lookup_position(global.shape_id(), si) {
|
||||
self.regs[*reg as usize] = global.get_prop_at(pos);
|
||||
}
|
||||
}
|
||||
@@ -587,11 +530,7 @@ impl Vm {
|
||||
/// Call a bytecode function from native code (D-09).
|
||||
/// Stub: sub_module storage not yet wired (plan 12.1-03).
|
||||
#[allow(dead_code)]
|
||||
pub fn call_bytecode_func(
|
||||
&mut self,
|
||||
_callback_obj: &JsObject,
|
||||
_args_regs: &[u8],
|
||||
) -> Result<JsValue, String> {
|
||||
pub fn call_bytecode_func(&mut self, _callback_obj: &JsObject, _args_regs: &[u8]) -> Result<JsValue, String> {
|
||||
Err("bytecode function calls not yet supported".into())
|
||||
}
|
||||
|
||||
@@ -715,8 +654,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
OpCode::JMP_IF_FALSE => {
|
||||
let cond =
|
||||
coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
|
||||
let cond = coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
|
||||
if !cond {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
self.pc = ((self.pc as isize) + offset - 1) as usize;
|
||||
@@ -724,8 +662,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
OpCode::JMP_IF_TRUE => {
|
||||
let cond =
|
||||
coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
|
||||
let cond = coercion::to_boolean(self.regs[rd], self.kernel.string_forge().as_ref());
|
||||
if cond {
|
||||
let offset = opcode::offset16(instr) as isize;
|
||||
self.pc = ((self.pc as isize) + offset - 1) as usize;
|
||||
@@ -763,13 +700,7 @@ impl Vm {
|
||||
let arg_count = (ext & 0xFF) as usize;
|
||||
|
||||
if obj.native_fn().is_some() {
|
||||
match self.dispatch_native_call(
|
||||
obj,
|
||||
callee,
|
||||
this_reg,
|
||||
first_arg_reg,
|
||||
arg_count,
|
||||
) {
|
||||
match self.dispatch_native_call(obj, callee, this_reg, first_arg_reg, arg_count) {
|
||||
Ok(()) => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
@@ -785,9 +716,7 @@ impl Vm {
|
||||
}
|
||||
|
||||
if self.frames.len() >= self.kernel.config.max_call_depth {
|
||||
return Err(
|
||||
"RangeError: Maximum call stack size exceeded".into()
|
||||
);
|
||||
return Err("RangeError: Maximum call stack size exceeded".into());
|
||||
}
|
||||
|
||||
// Clone sub_module data before mutably borrowing self
|
||||
@@ -795,13 +724,10 @@ impl Vm {
|
||||
let sub_n_args = self.sub_modules[sub_idx].n_args as usize;
|
||||
let sub_n_registers = self.sub_modules[sub_idx].n_registers;
|
||||
let sub_constants = self.sub_modules[sub_idx].constants.clone();
|
||||
let sub_param_base =
|
||||
self.sub_modules[sub_idx].param_base as usize;
|
||||
let sub_param_base = self.sub_modules[sub_idx].param_base as usize;
|
||||
let sub_is_arrow = self.sub_modules[sub_idx].is_arrow;
|
||||
let caller_reg_limit = self.active_reg_limit.max(1);
|
||||
let saved_regs = self.regs[..caller_reg_limit as usize]
|
||||
.to_vec()
|
||||
.into_boxed_slice();
|
||||
let saved_regs = self.regs[..caller_reg_limit as usize].to_vec().into_boxed_slice();
|
||||
let saved_this = self.regs[254];
|
||||
let saved_new_target = self.regs[255];
|
||||
|
||||
@@ -812,21 +738,14 @@ impl Vm {
|
||||
}
|
||||
// Set this (reg 254 convention).
|
||||
// Arrow functions use captured lexical `this` (D-01).
|
||||
self.regs[254] = if sub_is_arrow {
|
||||
obj.captured_this()
|
||||
} else {
|
||||
callee
|
||||
};
|
||||
self.regs[254] = if sub_is_arrow { obj.captured_this() } else { callee };
|
||||
self.regs[255] = JsValue::undefined();
|
||||
|
||||
let converted_sub_constants =
|
||||
self.convert_constants(&sub_constants)?;
|
||||
let converted_sub_constants = self.convert_constants(&sub_constants)?;
|
||||
|
||||
// Save current bytecode/constants
|
||||
self.saved_bytecode_stack
|
||||
.push(std::mem::take(&mut self.bytecode));
|
||||
self.saved_constants_stack
|
||||
.push(std::mem::take(&mut self.constants));
|
||||
self.saved_bytecode_stack.push(std::mem::take(&mut self.bytecode));
|
||||
self.saved_constants_stack.push(std::mem::take(&mut self.constants));
|
||||
|
||||
// Push call frame
|
||||
self.frames.push(CallFrame {
|
||||
@@ -850,10 +769,8 @@ impl Vm {
|
||||
for (name, reg) in &self.sub_modules[sub_idx].builtin_reg_map {
|
||||
let si = self.kernel.string_forge().intern(name.as_str()).0;
|
||||
let global = self.kernel.global_object();
|
||||
if let Some(pos) = self
|
||||
.kernel
|
||||
.shape_forge()
|
||||
.lookup_position(global.shape_id(), si)
|
||||
if let Some(pos) =
|
||||
self.kernel.shape_forge().lookup_position(global.shape_id(), si)
|
||||
{
|
||||
self.regs[*reg as usize] = global.get_prop_at(pos);
|
||||
}
|
||||
@@ -886,11 +803,7 @@ impl Vm {
|
||||
}
|
||||
let obj = unsafe { &*obj_ptr };
|
||||
if !obj.is_function() || obj.native_fn().is_none() {
|
||||
throw_err!(
|
||||
self,
|
||||
TypeError,
|
||||
"CALL_NATIVE target is not a native function"
|
||||
);
|
||||
throw_err!(self, TypeError, "CALL_NATIVE target is not a native function");
|
||||
}
|
||||
|
||||
let ext = self.bytecode[self.pc];
|
||||
@@ -906,11 +819,7 @@ impl Vm {
|
||||
|
||||
let constructor = self.regs[constructor_reg];
|
||||
if !constructor.is_object() {
|
||||
throw_err!(
|
||||
self,
|
||||
TypeError,
|
||||
"NEW_EXPRESSION: constructor is not an object"
|
||||
);
|
||||
throw_err!(self, TypeError, "NEW_EXPRESSION: constructor is not an object");
|
||||
}
|
||||
let ctor_ptr = constructor.as_js_object_ptr();
|
||||
if ctor_ptr.is_null() {
|
||||
@@ -918,19 +827,11 @@ impl Vm {
|
||||
}
|
||||
let ctor_obj = unsafe { &*ctor_ptr };
|
||||
if !ctor_obj.is_function() {
|
||||
throw_err!(
|
||||
self,
|
||||
TypeError,
|
||||
"NEW_EXPRESSION: constructor is not a function"
|
||||
);
|
||||
throw_err!(self, TypeError, "NEW_EXPRESSION: constructor is not a function");
|
||||
}
|
||||
// Arrow functions cannot be used as constructors (D-03)
|
||||
if ctor_obj.is_arrow() {
|
||||
throw_err!(
|
||||
self,
|
||||
TypeError,
|
||||
"arrow functions cannot be used as constructors"
|
||||
);
|
||||
throw_err!(self, TypeError, "arrow functions cannot be used as constructors");
|
||||
}
|
||||
|
||||
// Read extension word for arg_count
|
||||
@@ -940,10 +841,9 @@ impl Vm {
|
||||
|
||||
// Create new empty object
|
||||
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
|
||||
let new_obj = self.epoch.alloc(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
));
|
||||
let new_obj = self
|
||||
.epoch
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
|
||||
|
||||
// Look up constructor.prototype and set as proto of new object
|
||||
let proto_si = self.kernel.string_forge().intern("prototype").0;
|
||||
@@ -967,8 +867,7 @@ impl Vm {
|
||||
}
|
||||
let args_slice = &args_buf[..arg_count + 1];
|
||||
|
||||
let func: NativeFn =
|
||||
unsafe { std::mem::transmute(ctor_obj.native_fn().unwrap()) };
|
||||
let func: NativeFn = unsafe { std::mem::transmute(ctor_obj.native_fn().unwrap()) };
|
||||
self.regs[254] = constructor;
|
||||
match func(self, args_slice) {
|
||||
Ok(val) => {
|
||||
@@ -1036,16 +935,14 @@ impl Vm {
|
||||
|
||||
OpCode::NEW_OBJECT => {
|
||||
let proto_ptr = &*self.object_prototype as *const JsObject as *mut JsObject;
|
||||
let obj = self.epoch.alloc(JsObject::new_empty(
|
||||
EMPTY_SHAPE_ID,
|
||||
JsValue::from_js_object(proto_ptr),
|
||||
));
|
||||
let obj = self
|
||||
.epoch
|
||||
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto_ptr)));
|
||||
self.regs[rd] = JsValue::object(obj as *mut u8);
|
||||
}
|
||||
|
||||
OpCode::NEW_ARRAY => {
|
||||
let proto_ptr =
|
||||
self.kernel.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let proto_ptr = self.kernel.builtin_world().array_proto.as_ptr() as *mut JsObject;
|
||||
let n = opcode::imm16(instr) as usize;
|
||||
let bump = self.epoch.bump();
|
||||
let obj = self.epoch.alloc(JsObject::new_array(
|
||||
@@ -1119,10 +1016,7 @@ impl Vm {
|
||||
let reg = (seg & 0x7F) as u8;
|
||||
let val = self.regs[reg as usize];
|
||||
let s = if val.is_string() {
|
||||
self.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
self.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
} else {
|
||||
format!("{}", val)
|
||||
};
|
||||
@@ -1133,11 +1027,8 @@ impl Vm {
|
||||
if const_idx < self.constants.len() {
|
||||
let val = self.constants[const_idx];
|
||||
if val.is_string() {
|
||||
let s = self
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s =
|
||||
self.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default();
|
||||
result.push_str(&s);
|
||||
}
|
||||
}
|
||||
@@ -1164,11 +1055,7 @@ impl Vm {
|
||||
let rhs_val = self.regs[b];
|
||||
|
||||
if !rhs_val.is_object() {
|
||||
throw_err!(
|
||||
self,
|
||||
TypeError,
|
||||
"INSTANCEOF right-hand side is not callable"
|
||||
);
|
||||
throw_err!(self, TypeError, "INSTANCEOF right-hand side is not callable");
|
||||
}
|
||||
if !lhs_val.is_object() {
|
||||
self.regs[rd] = JsValue::bool(false);
|
||||
@@ -1286,14 +1173,8 @@ impl Vm {
|
||||
break;
|
||||
}
|
||||
if let Some(shape) = self.kernel.shape_forge().get_shape(id) {
|
||||
if shape.property_name != u32::MAX
|
||||
&& seen.insert(shape.property_name)
|
||||
{
|
||||
let hash = self
|
||||
.kernel
|
||||
.string_forge()
|
||||
.get_hash(shape.property_name)
|
||||
.unwrap_or(0);
|
||||
if shape.property_name != u32::MAX && seen.insert(shape.property_name) {
|
||||
let hash = self.kernel.string_forge().get_hash(shape.property_name).unwrap_or(0);
|
||||
keys_vec.push(JsValue::string(shape.property_name, hash));
|
||||
}
|
||||
cursor = shape.parent;
|
||||
@@ -1304,10 +1185,7 @@ impl Vm {
|
||||
current = cur.proto();
|
||||
}
|
||||
|
||||
let iter = self.epoch.alloc(ForInIter {
|
||||
keys: keys_vec,
|
||||
index: 0,
|
||||
});
|
||||
let iter = self.epoch.alloc(ForInIter { keys: keys_vec, index: 0 });
|
||||
self.for_in_iters.push(iter as *mut u8);
|
||||
}
|
||||
|
||||
@@ -1480,35 +1358,18 @@ mod tests {
|
||||
#[test]
|
||||
fn for_of_opcodes_fail_explicitly() {
|
||||
for (op, expected) in [
|
||||
(
|
||||
opcode::OpCode::FOR_OF_INIT,
|
||||
"opcode FOR_OF_INIT not yet implemented",
|
||||
),
|
||||
(
|
||||
opcode::OpCode::FOR_OF_NEXT,
|
||||
"opcode FOR_OF_NEXT not yet implemented",
|
||||
),
|
||||
(
|
||||
opcode::OpCode::FOR_OF_DONE,
|
||||
"opcode FOR_OF_DONE not yet implemented",
|
||||
),
|
||||
(
|
||||
opcode::OpCode::FOR_OF_CLOSE,
|
||||
"opcode FOR_OF_CLOSE not yet implemented",
|
||||
),
|
||||
(opcode::OpCode::FOR_OF_INIT, "opcode FOR_OF_INIT not yet implemented"),
|
||||
(opcode::OpCode::FOR_OF_NEXT, "opcode FOR_OF_NEXT not yet implemented"),
|
||||
(opcode::OpCode::FOR_OF_DONE, "opcode FOR_OF_DONE not yet implemented"),
|
||||
(opcode::OpCode::FOR_OF_CLOSE, "opcode FOR_OF_CLOSE not yet implemented"),
|
||||
] {
|
||||
let module = CompiledModule {
|
||||
bytecode: vec![
|
||||
opcode::encode(op, 0, 0, 0),
|
||||
opcode::encode(opcode::OpCode::HALT, 0, 0, 0),
|
||||
],
|
||||
bytecode: vec![opcode::encode(op, 0, 0, 0), opcode::encode(opcode::OpCode::HALT, 0, 0, 0)],
|
||||
n_registers: 1,
|
||||
..CompiledModule::new()
|
||||
};
|
||||
let mut vm = Vm::new();
|
||||
let err = vm
|
||||
.run(&module)
|
||||
.expect_err("FOR_OF opcode should fail explicitly");
|
||||
let err = vm.run(&module).expect_err("FOR_OF opcode should fail explicitly");
|
||||
assert_eq!(err, expected);
|
||||
}
|
||||
}
|
||||
@@ -1522,13 +1383,8 @@ mod tests {
|
||||
..CompiledModule::new()
|
||||
};
|
||||
let mut vm = Vm::new();
|
||||
let err = vm
|
||||
.run(&module)
|
||||
.expect_err("invalid RegExp constant should fail explicitly");
|
||||
assert!(
|
||||
err.contains("SyntaxError: Invalid regular expression"),
|
||||
"unexpected error: {err}"
|
||||
);
|
||||
let err = vm.run(&module).expect_err("invalid RegExp constant should fail explicitly");
|
||||
assert!(err.contains("SyntaxError: Invalid regular expression"), "unexpected error: {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1556,10 +1412,7 @@ mod tests {
|
||||
let err = vm
|
||||
.run(&module)
|
||||
.expect_err("invalid submodule RegExp constant should fail explicitly");
|
||||
assert!(
|
||||
err.contains("SyntaxError: Invalid regular expression"),
|
||||
"unexpected error: {err}"
|
||||
);
|
||||
assert!(err.contains("SyntaxError: Invalid regular expression"), "unexpected error: {err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1586,9 +1439,7 @@ mod tests {
|
||||
..CompiledModule::new()
|
||||
};
|
||||
let mut vm = Vm::new();
|
||||
let err = vm
|
||||
.run(&module)
|
||||
.expect_err("unimplemented opcode should fail explicitly");
|
||||
let err = vm.run(&module).expect_err("unimplemented opcode should fail explicitly");
|
||||
assert_eq!(err, "opcode PROFILE_TYPE not yet implemented");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,7 @@ pub struct VmGuard {
|
||||
}
|
||||
|
||||
impl VmPool {
|
||||
pub fn new(
|
||||
kernel: Arc<OxideKernel>,
|
||||
#[allow(dead_code)] min_size: usize,
|
||||
max_size: Option<usize>,
|
||||
) -> Arc<Self> {
|
||||
pub fn new(kernel: Arc<OxideKernel>, #[allow(dead_code)] min_size: usize, max_size: Option<usize>) -> Arc<Self> {
|
||||
let pool = Arc::new(Self {
|
||||
kernel,
|
||||
inner: Mutex::new(VmPoolInner {
|
||||
|
||||
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
if val.is_string() {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
} else {
|
||||
format!("{}", val)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,7 @@ fn bench_1m_property_reads() {
|
||||
}
|
||||
let elapsed = start.elapsed();
|
||||
let ns_per = elapsed.as_nanos() as f64 / N as f64;
|
||||
println!(
|
||||
"1M IC property reads: {:.2}ms ({:.0} ns/read)",
|
||||
elapsed.as_secs_f64() * 1000.0,
|
||||
ns_per
|
||||
);
|
||||
println!("1M IC property reads: {:.2}ms ({:.0} ns/read)", elapsed.as_secs_f64() * 1000.0, ns_per);
|
||||
if cfg!(debug_assertions) {
|
||||
println!(" (debug build -- skipping timing assertion)");
|
||||
} else {
|
||||
|
||||
@@ -5,11 +5,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
let result = vm.run(&module)?;
|
||||
Ok((vm, result))
|
||||
|
||||
@@ -1,28 +1,20 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::object::JsObject;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::boolean::{
|
||||
boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of,
|
||||
};
|
||||
use oxide_vm::builtins::boolean::{boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn str_val(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// -- direct native fn tests --
|
||||
|
||||
@@ -3,19 +3,13 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<oxide_types::value::JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn str_val(vm: &Vm, val: oxide_types::value::JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// -- static methods --
|
||||
@@ -92,11 +86,7 @@ fn date_set_time() {
|
||||
#[test]
|
||||
fn date_set_full_year() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var d = new Date(2020, 0, 1); d.setFullYear(2025); d.getFullYear()",
|
||||
)
|
||||
.unwrap();
|
||||
let r = eval(&mut vm, "var d = new Date(2020, 0, 1); d.setFullYear(2025); d.getFullYear()").unwrap();
|
||||
assert_eq!(r.as_double(), 2025.0);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@ fn make_vm() -> Vm {
|
||||
fn eval(source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile: {}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
|
||||
let mut vm = make_vm();
|
||||
vm.run(&module)
|
||||
}
|
||||
@@ -140,22 +138,7 @@ fn reference_error_is_defined() {
|
||||
|
||||
#[test]
|
||||
fn error_subtype_constructors_produce_named_objects() {
|
||||
assert_eq!(
|
||||
format!("{}", eval("new Error('boom').name == 'Error'").unwrap()),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}",
|
||||
eval("new TypeError('boom').name == 'TypeError'").unwrap()
|
||||
),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
format!(
|
||||
"{}",
|
||||
eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()
|
||||
),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(format!("{}", eval("new Error('boom').name == 'Error'").unwrap()), "true");
|
||||
assert_eq!(format!("{}", eval("new TypeError('boom').name == 'TypeError'").unwrap()), "true");
|
||||
assert_eq!(format!("{}", eval("new SyntaxError('boom').name == 'SyntaxError'").unwrap()), "true");
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
@@ -38,11 +35,7 @@ fn function_to_string_includes_function() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "Math.max.toString()").unwrap();
|
||||
assert!(result.is_string());
|
||||
let rendered = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(result.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let rendered = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
|
||||
assert!(rendered.contains("function"), "got: {}", rendered);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(source: &str) -> Result<oxide_types::value::JsValue, String> {
|
||||
let allocator = Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::map::{
|
||||
map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size,
|
||||
};
|
||||
use oxide_vm::builtins::map::{map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn str_val(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// -- direct native fn tests --
|
||||
@@ -101,10 +93,6 @@ fn map_new_has() {
|
||||
#[test]
|
||||
fn map_new_delete() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var m = new Map(); m.set('x', 10); m.delete('x'); m.has('x')",
|
||||
)
|
||||
.unwrap();
|
||||
let r = eval(&mut vm, "var m = new Map(); m.set('x', 10); m.delete('x'); m.has('x')").unwrap();
|
||||
assert!(!r.as_bool());
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
@@ -79,11 +76,7 @@ fn number_constructor() {
|
||||
fn to_string_of_number() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n = 42; n.toString()").unwrap();
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(result.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
|
||||
assert_eq!(s, "42");
|
||||
}
|
||||
|
||||
@@ -91,11 +84,7 @@ fn to_string_of_number() {
|
||||
fn to_fixed_of_number() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n = 3.14159; n.toFixed(2)").unwrap();
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(result.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
|
||||
assert_eq!(s, "3.14");
|
||||
}
|
||||
|
||||
@@ -124,24 +113,13 @@ fn number_epsilon_is_positive() {
|
||||
fn number_to_exponential() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "var n = 123.456; n.toExponential(2)").unwrap();
|
||||
let s = vm
|
||||
.kernel()
|
||||
.string_forge()
|
||||
.lookup(result.as_string_index())
|
||||
.unwrap_or_default();
|
||||
let s = vm.kernel().string_forge().lookup(result.as_string_index()).unwrap_or_default();
|
||||
assert_eq!(s, "1.23e2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_static_constants_remain_bound() {
|
||||
let mut vm = Vm::new();
|
||||
assert_eq!(
|
||||
eval(&mut vm, "Number.MAX_SAFE_INTEGER")
|
||||
.unwrap()
|
||||
.as_double(),
|
||||
9007199254740991f64
|
||||
);
|
||||
assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0")
|
||||
.unwrap()
|
||||
.as_bool());
|
||||
assert_eq!(eval(&mut vm, "Number.MAX_SAFE_INTEGER").unwrap().as_double(), 9007199254740991f64);
|
||||
assert!(eval(&mut vm, "Number.POSITIVE_INFINITY > 0").unwrap().as_bool());
|
||||
}
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
let result = vm.run(&module)?;
|
||||
Ok((vm, result))
|
||||
|
||||
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// --- RegExp constructor ---
|
||||
@@ -154,11 +148,7 @@ fn regexp_exec_global_last_index_advances() {
|
||||
#[test]
|
||||
fn regexp_exec_global_resets_on_exhaustion() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var re = /a/g; re.exec('a'); re.exec('a') === null",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "var re = /a/g; re.exec('a'); re.exec('a') === null").unwrap();
|
||||
assert!(result.as_bool());
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
use oxide_compiler::compiler::Compiler;
|
||||
use oxide_types::value::JsValue;
|
||||
use oxide_vm::builtins::set::{
|
||||
set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size,
|
||||
};
|
||||
use oxide_vm::builtins::set::{set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size};
|
||||
use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
@@ -119,10 +114,6 @@ fn set_new_has_missing() {
|
||||
#[test]
|
||||
fn set_new_delete() {
|
||||
let mut vm = Vm::new();
|
||||
let r = eval(
|
||||
&mut vm,
|
||||
"var s = new Set(); s.add(1); s.add(2); s.delete(1); s.has(1)",
|
||||
)
|
||||
.unwrap();
|
||||
let r = eval(&mut vm, "var s = new Set(); s.add(1); s.add(2); s.delete(1); s.has(1)").unwrap();
|
||||
assert!(!r.as_bool());
|
||||
}
|
||||
|
||||
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// --- Symbol constructor ---
|
||||
|
||||
@@ -38,26 +38,17 @@ fn eval_not_falsy() {
|
||||
|
||||
#[test]
|
||||
fn test_same_value_signed_zero() {
|
||||
assert!(!coercion::same_value(
|
||||
JsValue::float(0.0),
|
||||
JsValue::float(-0.0)
|
||||
));
|
||||
assert!(!coercion::same_value(JsValue::float(0.0), JsValue::float(-0.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strict_equality_signed_zero() {
|
||||
assert!(coercion::strict_equality(
|
||||
JsValue::float(0.0),
|
||||
JsValue::float(-0.0)
|
||||
));
|
||||
assert!(coercion::strict_equality(JsValue::float(0.0), JsValue::float(-0.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_same_value_nan() {
|
||||
assert!(coercion::same_value(
|
||||
JsValue::float(f64::NAN),
|
||||
JsValue::float(f64::NAN)
|
||||
));
|
||||
assert!(coercion::same_value(JsValue::float(f64::NAN), JsValue::float(f64::NAN)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -73,36 +64,21 @@ fn test_same_value_int_float_equal() {
|
||||
|
||||
#[test]
|
||||
fn test_strict_equality_nan() {
|
||||
assert!(!coercion::strict_equality(
|
||||
JsValue::float(f64::NAN),
|
||||
JsValue::float(f64::NAN)
|
||||
));
|
||||
assert!(!coercion::strict_equality(JsValue::float(f64::NAN), JsValue::float(f64::NAN)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strict_equality_type_mismatch() {
|
||||
assert!(!coercion::strict_equality(
|
||||
JsValue::int(1),
|
||||
JsValue::bool(true)
|
||||
));
|
||||
assert!(!coercion::strict_equality(JsValue::int(1), JsValue::bool(true)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strict_equality_int_float_equal() {
|
||||
assert!(coercion::strict_equality(
|
||||
JsValue::int(1),
|
||||
JsValue::float(1.0)
|
||||
));
|
||||
assert!(!coercion::strict_equality(
|
||||
JsValue::int(1),
|
||||
JsValue::float(2.0)
|
||||
));
|
||||
assert!(coercion::strict_equality(JsValue::int(1), JsValue::float(1.0)));
|
||||
assert!(!coercion::strict_equality(JsValue::int(1), JsValue::float(2.0)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_strict_equality_null_undefined() {
|
||||
assert!(!coercion::strict_equality(
|
||||
JsValue::null(),
|
||||
JsValue::undefined()
|
||||
));
|
||||
assert!(!coercion::strict_equality(JsValue::null(), JsValue::undefined()));
|
||||
}
|
||||
|
||||
@@ -61,18 +61,12 @@ fn eval_while_result_undefined() {
|
||||
|
||||
#[test]
|
||||
fn eval_for_basic() {
|
||||
assert_eq!(
|
||||
eval("var r = 0; for (i = 0; i < 3; i = i + 1) { r = r + i; } r"),
|
||||
"3"
|
||||
);
|
||||
assert_eq!(eval("var r = 0; for (i = 0; i < 3; i = i + 1) { r = r + i; } r"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_no_test() {
|
||||
assert_eq!(
|
||||
eval("var r = 0; for (i = 0; ; i = i + 1) { if (i >= 3) { break; } r = r + i; } r"),
|
||||
"3"
|
||||
);
|
||||
assert_eq!(eval("var r = 0; for (i = 0; ; i = i + 1) { if (i >= 3) { break; } r = r + i; } r"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -97,10 +91,7 @@ fn eval_ternary_nested() {
|
||||
|
||||
#[test]
|
||||
fn eval_break_in_while() {
|
||||
assert_eq!(
|
||||
eval("var i = 0; while (true) { i = i + 1; if (i >= 3) { break; } } i"),
|
||||
"3"
|
||||
);
|
||||
assert_eq!(eval("var i = 0; while (true) { i = i + 1; if (i >= 3) { break; } } i"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -155,10 +146,7 @@ fn eval_do_while_multi_iteration() {
|
||||
|
||||
#[test]
|
||||
fn eval_do_while_break() {
|
||||
assert_eq!(
|
||||
eval("var i = 0; do { i = i + 1; if (i >= 3) break; } while (true); i"),
|
||||
"3"
|
||||
);
|
||||
assert_eq!(eval("var i = 0; do { i = i + 1; if (i >= 3) break; } while (true); i"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -182,34 +170,22 @@ fn eval_for_in_empty_object() {
|
||||
#[test]
|
||||
fn eval_for_in_non_object_throws() {
|
||||
let result = eval("for (k in 42) {}");
|
||||
assert!(
|
||||
result.contains("TypeError"),
|
||||
"expected TypeError, got: {result}"
|
||||
);
|
||||
assert!(result.contains("TypeError"), "expected TypeError, got: {result}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_nested() {
|
||||
assert_eq!(
|
||||
eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"),
|
||||
"2"
|
||||
);
|
||||
assert_eq!(eval("var r=0; for (k in {a:{x:1}}) { for (j in {y:2}) { r=r+1; } } r"), "2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_break() {
|
||||
assert_eq!(
|
||||
eval("var r=0; for (k in {a:1,b:2,c:3}) { if (k=='b') break; r=r+1; } r"),
|
||||
"1"
|
||||
);
|
||||
assert_eq!(eval("var r=0; for (k in {a:1,b:2,c:3}) { if (k=='b') break; r=r+1; } r"), "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_for_in_continue() {
|
||||
assert_eq!(
|
||||
eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"),
|
||||
"2"
|
||||
);
|
||||
assert_eq!(eval("var r=0; for (k in {a:1,b:2}) { if (k=='a') continue; r=r+1; } r"), "2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -220,10 +196,7 @@ fn eval_for_in_let_scoping() {
|
||||
|
||||
#[test]
|
||||
fn eval_switch_basic_match() {
|
||||
assert_eq!(
|
||||
eval("var x=0;switch(2){case 1:x=10;case 2:x=20;break;default:x=30;}x"),
|
||||
"20"
|
||||
);
|
||||
assert_eq!(eval("var x=0;switch(2){case 1:x=10;case 2:x=20;break;default:x=30;}x"), "20");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -243,18 +216,12 @@ fn eval_switch_no_match_no_default() {
|
||||
|
||||
#[test]
|
||||
fn eval_switch_break() {
|
||||
assert_eq!(
|
||||
eval("var x=0;switch(1){case 1:x=10;break;case 2:x=20;}x"),
|
||||
"10"
|
||||
);
|
||||
assert_eq!(eval("var x=0;switch(1){case 1:x=10;break;case 2:x=20;}x"), "10");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn eval_break_in_switch_in_loop() {
|
||||
assert_eq!(
|
||||
eval("var r=0;while(true){switch(1){case 1:r=1;break;}r=2;break;}r"),
|
||||
"2"
|
||||
);
|
||||
assert_eq!(eval("var r=0;while(true){switch(1){case 1:r=1;break;}r=2;break;}r"), "2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -4,11 +4,8 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
@@ -61,11 +58,7 @@ fn fe_basic() {
|
||||
#[test]
|
||||
fn fe_with_params() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"var mul = function(x,y) { return x * y; }; mul(6, 7)",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "var mul = function(x,y) { return x * y; }; mul(6, 7)").unwrap();
|
||||
assert!((result.as_double() - 42.0).abs() < 0.0001);
|
||||
}
|
||||
|
||||
@@ -74,22 +67,14 @@ fn fe_with_params() {
|
||||
#[test]
|
||||
fn cross_func_call() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function a() { return 1; } function b() { return a() + 2; } b()",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "function a() { return 1; } function b() { return a() + 2; } b()").unwrap();
|
||||
assert!((result.as_double() - 3.0).abs() < 0.0001);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn two_funcs_called_from_global() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function a() { return 1; } function b() { return 2; } a() + b()",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "function a() { return 1; } function b() { return 2; } a() + b()").unwrap();
|
||||
assert!(result.is_double(), "expected double, got: {:?}", result);
|
||||
assert!((result.as_double() - 3.0).abs() < 0.0001);
|
||||
}
|
||||
@@ -147,11 +132,7 @@ fn fd_calls_builtin_two_args() {
|
||||
#[test]
|
||||
fn fd_chain_call() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function a() { return 10; } function b() { return a(); } b()",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "function a() { return 10; } function b() { return a(); } b()").unwrap();
|
||||
assert_eq!(result.as_int(), 10);
|
||||
}
|
||||
|
||||
@@ -177,11 +158,7 @@ fn fd_returns_new_array() {
|
||||
fn this_in_function_reads_value() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(&mut vm, "function f() { return this; } f()").unwrap();
|
||||
assert!(
|
||||
result.is_object(),
|
||||
"expected object (function as this), got: {:?}",
|
||||
result
|
||||
);
|
||||
assert!(result.is_object(), "expected object (function as this), got: {:?}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -201,10 +178,6 @@ fn this_member_access() {
|
||||
#[test]
|
||||
fn this_in_constructor_sets_proto() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"function Ctor(m) { this.message = m; } Ctor.prototype = new Object(); 1",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "function Ctor(m) { this.message = m; } Ctor.prototype = new Object(); 1").unwrap();
|
||||
assert_eq!(result.as_int(), 1);
|
||||
}
|
||||
|
||||
@@ -4,19 +4,13 @@ use oxide_vm::vm::Vm;
|
||||
|
||||
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program =
|
||||
oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile error: {}", e))?;
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse error: {:?}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile error: {}", e))?;
|
||||
vm.run(&module)
|
||||
}
|
||||
|
||||
fn string_value(vm: &Vm, val: JsValue) -> String {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
}
|
||||
|
||||
// -- JSON.parse --
|
||||
@@ -90,11 +84,7 @@ fn json_parse_null() {
|
||||
#[test]
|
||||
fn json_parse_nested() {
|
||||
let mut vm = Vm::new();
|
||||
let result = eval(
|
||||
&mut vm,
|
||||
"JSON.parse('{\"a\":1,\"b\":[1,2],\"c\":{\"d\":\"hello\"}}')",
|
||||
)
|
||||
.unwrap();
|
||||
let result = eval(&mut vm, "JSON.parse('{\"a\":1,\"b\":[1,2],\"c\":{\"d\":\"hello\"}}')").unwrap();
|
||||
assert!(result.is_object());
|
||||
}
|
||||
|
||||
@@ -200,11 +190,7 @@ fn json_parse_stringify_roundtrip() {
|
||||
#[test]
|
||||
fn json_roundtrip_array() {
|
||||
let mut vm = Vm::new();
|
||||
let _ = eval(
|
||||
&mut vm,
|
||||
"var arr = JSON.parse(JSON.stringify([1,2,3])); arr",
|
||||
)
|
||||
.unwrap();
|
||||
let _ = eval(&mut vm, "var arr = JSON.parse(JSON.stringify([1,2,3])); arr").unwrap();
|
||||
}
|
||||
|
||||
// -- Edge cases --
|
||||
|
||||
@@ -43,10 +43,7 @@ fn epoch_benchmark_1m_allocations() {
|
||||
|
||||
let elapsed = start.elapsed();
|
||||
if cfg!(debug_assertions) {
|
||||
println!(
|
||||
"1M allocations took {}ms (debug build - skipping timing assertion)",
|
||||
elapsed.as_millis()
|
||||
);
|
||||
println!("1M allocations took {}ms (debug build - skipping timing assertion)", elapsed.as_millis());
|
||||
} else {
|
||||
assert!(
|
||||
elapsed.as_millis() < 200,
|
||||
|
||||
@@ -5,9 +5,7 @@ use oxide_vm::vm::Vm;
|
||||
fn eval(source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile: {}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
vm.run(&module)
|
||||
}
|
||||
@@ -15,36 +13,21 @@ fn eval(source: &str) -> Result<JsValue, String> {
|
||||
macro_rules! assert_bool {
|
||||
($expr:expr, $expected:expr, $msg:expr) => {
|
||||
let r = $expr.unwrap();
|
||||
assert!(
|
||||
r.is_bool() && r.as_bool() == $expected,
|
||||
"{}: expected {}, got {:?}",
|
||||
$msg,
|
||||
$expected,
|
||||
r
|
||||
);
|
||||
assert!(r.is_bool() && r.as_bool() == $expected, "{}: expected {}, got {:?}", $msg, $expected, r);
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! assert_undefined {
|
||||
($expr:expr, $msg:expr) => {
|
||||
let r = $expr.unwrap();
|
||||
assert!(
|
||||
r.is_undefined(),
|
||||
"{}: expected undefined, got {:?}",
|
||||
$msg,
|
||||
r
|
||||
);
|
||||
assert!(r.is_undefined(), "{}: expected undefined, got {:?}", $msg, r);
|
||||
};
|
||||
}
|
||||
|
||||
// -- Proto chains: every (non-Object) constructor.prototype should inherit from Object.prototype --
|
||||
#[test]
|
||||
fn array_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Array.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Array proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Array.prototype) === Object.prototype"), true, "Array proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -57,20 +40,12 @@ fn function_proto_chain() {
|
||||
|
||||
#[test]
|
||||
fn string_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(String.prototype) === Object.prototype"),
|
||||
true,
|
||||
"String proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(String.prototype) === Object.prototype"), true, "String proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn number_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Number.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Number proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Number.prototype) === Object.prototype"), true, "Number proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -84,56 +59,32 @@ fn boolean_proto_chain() {
|
||||
|
||||
#[test]
|
||||
fn error_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Error.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Error proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Error.prototype) === Object.prototype"), true, "Error proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn symbol_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Symbol.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Symbol proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Symbol.prototype) === Object.prototype"), true, "Symbol proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Date.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Date proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Date.prototype) === Object.prototype"), true, "Date proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Set.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Set proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Set.prototype) === Object.prototype"), true, "Set proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(Map.prototype) === Object.prototype"),
|
||||
true,
|
||||
"Map proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(Map.prototype) === Object.prototype"), true, "Map proto");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_proto_chain() {
|
||||
assert_bool!(
|
||||
eval("Object.getPrototypeOf(RegExp.prototype) === Object.prototype"),
|
||||
true,
|
||||
"RegExp proto"
|
||||
);
|
||||
assert_bool!(eval("Object.getPrototypeOf(RegExp.prototype) === Object.prototype"), true, "RegExp proto");
|
||||
}
|
||||
|
||||
// -- Object.prototype is the root, its __proto__ is null --
|
||||
@@ -147,20 +98,12 @@ fn object_proto_is_root() {
|
||||
#[test]
|
||||
fn array_instanceof_object() {
|
||||
assert_bool!(eval("[] instanceof Object"), true, "[] instanceof Object");
|
||||
assert_bool!(
|
||||
eval("new Array() instanceof Object"),
|
||||
true,
|
||||
"new Array instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Array() instanceof Object"), true, "new Array instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn function_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("(function() {}) instanceof Object"),
|
||||
true,
|
||||
"function instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("(function() {}) instanceof Object"), true, "function instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -179,110 +122,62 @@ fn boolean_not_instanceof_function() {
|
||||
|
||||
#[test]
|
||||
fn number_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Number(1) instanceof Object"),
|
||||
true,
|
||||
"new Number instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Number(1) instanceof Object"), true, "new Number instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn boolean_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Boolean(true) instanceof Object"),
|
||||
true,
|
||||
"new Boolean instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Boolean(true) instanceof Object"), true, "new Boolean instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Error() instanceof Object"),
|
||||
true,
|
||||
"new Error instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Error() instanceof Object"), true, "new Error instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn date_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Date() instanceof Object"),
|
||||
true,
|
||||
"new Date instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Date() instanceof Object"), true, "new Date instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Set() instanceof Object"),
|
||||
true,
|
||||
"new Set instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Set() instanceof Object"), true, "new Set instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn map_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("new Map() instanceof Object"),
|
||||
true,
|
||||
"new Map instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new Map() instanceof Object"), true, "new Map instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regexp_instanceof_object() {
|
||||
assert_bool!(eval("/a/ instanceof Object"), true, "/a/ instanceof Object");
|
||||
assert_bool!(
|
||||
eval("new RegExp('a') instanceof Object"),
|
||||
true,
|
||||
"new RegExp instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("new RegExp('a') instanceof Object"), true, "new RegExp instanceof Object");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_instanceof_object() {
|
||||
assert_bool!(
|
||||
eval("({}) instanceof Object"),
|
||||
true,
|
||||
"({}) instanceof Object"
|
||||
);
|
||||
assert_bool!(eval("({}) instanceof Object"), true, "({}) instanceof Object");
|
||||
}
|
||||
|
||||
// -- instanceof for native types should NOT respond to their own false positives --
|
||||
#[test]
|
||||
fn number_not_instanceof_array() {
|
||||
assert_bool!(
|
||||
eval("new Number(1) instanceof Array"),
|
||||
false,
|
||||
"Number instanceof Array"
|
||||
);
|
||||
assert_bool!(eval("new Number(1) instanceof Array"), false, "Number instanceof Array");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_not_instanceof_regexp() {
|
||||
assert_bool!(
|
||||
eval("({}) instanceof RegExp"),
|
||||
false,
|
||||
"({}) instanceof RegExp"
|
||||
);
|
||||
assert_bool!(eval("({}) instanceof RegExp"), false, "({}) instanceof RegExp");
|
||||
}
|
||||
|
||||
// -- Primitives should NOT be instanceof their wrapper constructors (no autoboxing) --
|
||||
#[test]
|
||||
fn primitives_not_instanceof() {
|
||||
assert_bool!(eval("1 instanceof Number"), false, "1 instanceof Number");
|
||||
assert_bool!(
|
||||
eval("'hi' instanceof String"),
|
||||
false,
|
||||
"'hi' instanceof String"
|
||||
);
|
||||
assert_bool!(
|
||||
eval("true instanceof Boolean"),
|
||||
false,
|
||||
"true instanceof Boolean"
|
||||
);
|
||||
assert_bool!(eval("'hi' instanceof String"), false, "'hi' instanceof String");
|
||||
assert_bool!(eval("true instanceof Boolean"), false, "true instanceof Boolean");
|
||||
}
|
||||
|
||||
// -- VOID regression --
|
||||
|
||||
@@ -42,11 +42,7 @@ fn eval_with_kernel(source: &str, kernel: Arc<OxideKernel>) -> String {
|
||||
#[test]
|
||||
fn regression_to_boolean_empty_string() {
|
||||
assert_eq!(eval("!''"), "true", "NOT empty string should be true");
|
||||
assert_eq!(
|
||||
eval("if ('') { 1 } else { 2 }"),
|
||||
"2",
|
||||
"empty string should be falsy"
|
||||
);
|
||||
assert_eq!(eval("if ('') { 1 } else { 2 }"), "2", "empty string should be falsy");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -71,19 +67,13 @@ fn regression_rerun_clears_ic_cache() {
|
||||
|
||||
#[test]
|
||||
fn regression_recursion_depth_limit() {
|
||||
assert_eq!(
|
||||
eval("function f(){f()} f()"),
|
||||
"vm error: RangeError: Maximum call stack size exceeded"
|
||||
);
|
||||
assert_eq!(eval("function f(){f()} f()"), "vm error: RangeError: Maximum call stack size exceeded");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_throw_statement_errors() {
|
||||
let result = eval("throw 'error'");
|
||||
assert!(
|
||||
result.contains("uncaught"),
|
||||
"expected uncaught, got: {result}"
|
||||
);
|
||||
assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
|
||||
assert!(
|
||||
result.contains("uncaught Error"),
|
||||
"expected string throw to default to Error, got: {result}"
|
||||
@@ -92,14 +82,8 @@ fn regression_throw_statement_errors() {
|
||||
|
||||
#[test]
|
||||
fn regression_throw_statement_preserves_type_error_kind() {
|
||||
assert_eq!(
|
||||
eval("try { throw new TypeError('boom') } catch (e) { e.name == 'TypeError' }"),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
eval("throw new TypeError('boom')"),
|
||||
"vm error: uncaught TypeError: {object}"
|
||||
);
|
||||
assert_eq!(eval("try { throw new TypeError('boom') } catch (e) { e.name == 'TypeError' }"), "true");
|
||||
assert_eq!(eval("throw new TypeError('boom')"), "vm error: uncaught TypeError: {object}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -108,10 +92,7 @@ fn regression_throw_statement_preserves_syntax_error_kind() {
|
||||
eval("try { throw new SyntaxError('boom') } catch (e) { e.name == 'SyntaxError' }"),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
eval("throw new SyntaxError('boom')"),
|
||||
"vm error: uncaught SyntaxError: {object}"
|
||||
);
|
||||
assert_eq!(eval("throw new SyntaxError('boom')"), "vm error: uncaught SyntaxError: {object}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -155,24 +136,12 @@ fn regression_new_expression_bytecode_constructor_error_is_catchable() {
|
||||
|
||||
#[test]
|
||||
fn regression_delete_static_property_throws_type_error() {
|
||||
assert_eq!(
|
||||
eval("try { var o={x:1}; delete o.x; 0 } catch (e) { e.name == 'TypeError' }"),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
eval("var o={x:1}; delete o.x"),
|
||||
"vm error: uncaught TypeError: {object}"
|
||||
);
|
||||
assert_eq!(eval("try { var o={x:1}; delete o.x; 0 } catch (e) { e.name == 'TypeError' }"), "true");
|
||||
assert_eq!(eval("var o={x:1}; delete o.x"), "vm error: uncaught TypeError: {object}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regression_delete_dynamic_property_throws_type_error() {
|
||||
assert_eq!(
|
||||
eval("try { var o={x:1}; delete o['x']; 0 } catch (e) { e.name == 'TypeError' }"),
|
||||
"true"
|
||||
);
|
||||
assert_eq!(
|
||||
eval("var o={x:1}; var k='x'; delete o[k]"),
|
||||
"vm error: uncaught TypeError: {object}"
|
||||
);
|
||||
assert_eq!(eval("try { var o={x:1}; delete o['x']; 0 } catch (e) { e.name == 'TypeError' }"), "true");
|
||||
assert_eq!(eval("var o={x:1}; var k='x'; delete o[k]"), "vm error: uncaught TypeError: {object}");
|
||||
}
|
||||
|
||||
@@ -5,9 +5,7 @@ use oxide_vm::vm::Vm;
|
||||
fn eval(source: &str) -> Result<JsValue, String> {
|
||||
let allocator = oxide_parser::Allocator::default();
|
||||
let program = oxide_parser::parse(&allocator, source).map_err(|e| format!("Parse: {:?}", e))?;
|
||||
let module = Compiler::new()
|
||||
.compile(&program)
|
||||
.map_err(|e| format!("Compile: {}", e))?;
|
||||
let module = Compiler::new().compile(&program).map_err(|e| format!("Compile: {}", e))?;
|
||||
let mut vm = Vm::new();
|
||||
vm.run(&module)
|
||||
}
|
||||
@@ -23,11 +21,7 @@ fn const_reassignment_throws() {
|
||||
let result = eval("const x = 1; x = 2");
|
||||
assert!(result.is_err());
|
||||
let err = result.unwrap_err();
|
||||
assert!(
|
||||
err.contains("TypeError") || err.contains("constant"),
|
||||
"got: {}",
|
||||
err
|
||||
);
|
||||
assert!(err.contains("TypeError") || err.contains("constant"), "got: {}", err);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -37,10 +37,7 @@ fn eval_val(source: &str) -> (Vm, Result<JsValue, String>) {
|
||||
|
||||
fn to_str(vm: &Vm, val: JsValue) -> String {
|
||||
if val.is_string() {
|
||||
vm.kernel()
|
||||
.string_forge()
|
||||
.lookup(val.as_string_index())
|
||||
.unwrap_or_default()
|
||||
vm.kernel().string_forge().lookup(val.as_string_index()).unwrap_or_default()
|
||||
} else {
|
||||
format!("{}", val)
|
||||
}
|
||||
@@ -99,11 +96,7 @@ fn tagged_template_basic() {
|
||||
// This avoids bytecode function call complexity.
|
||||
let result = eval("Math.max`hello ${42} world`");
|
||||
// Math.max on the args should work - just verify it doesn't crash
|
||||
assert!(
|
||||
!result.starts_with("vm error:"),
|
||||
"Tagged template should not crash, got: {}",
|
||||
result
|
||||
);
|
||||
assert!(!result.starts_with("vm error:"), "Tagged template should not crash, got: {}", result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -111,8 +104,5 @@ fn tagged_template_compiles_no_error() {
|
||||
// Verify that tagged templates compile without error (even if the tag
|
||||
// function behavior isn't fully tested)
|
||||
let result = eval("function t(s,v){ return s[0]+v; } t`x${1}`");
|
||||
assert!(
|
||||
!result.starts_with("compile error:"),
|
||||
"Tagged template should compile"
|
||||
);
|
||||
assert!(!result.starts_with("compile error:"), "Tagged template should compile");
|
||||
}
|
||||
|
||||
@@ -121,9 +121,7 @@ fn as_js_object_ptr_non_object_returns_null() {
|
||||
assert!(JsValue::null().as_js_object_ptr().is_null());
|
||||
assert!(JsValue::undefined().as_js_object_ptr().is_null());
|
||||
let x = 42u8;
|
||||
assert!(!JsValue::object(&x as *const u8)
|
||||
.as_js_object_ptr()
|
||||
.is_null());
|
||||
assert!(!JsValue::object(&x as *const u8).as_js_object_ptr().is_null());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -27,10 +27,7 @@ fn test_throw_caught_by_catch() {
|
||||
#[test]
|
||||
fn test_throw_without_catch() {
|
||||
let result = eval("throw 'err';");
|
||||
assert!(
|
||||
result.contains("uncaught"),
|
||||
"expected uncaught, got: {result}"
|
||||
);
|
||||
assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -41,34 +38,22 @@ fn test_try_finally_normal_path() {
|
||||
#[test]
|
||||
fn test_try_finally_exception_path() {
|
||||
let result = eval("var x = 0; try { throw 1; } finally { x = 2; }");
|
||||
assert!(
|
||||
result.contains("uncaught"),
|
||||
"expected uncaught, got: {result}"
|
||||
);
|
||||
assert!(result.contains("uncaught"), "expected uncaught, got: {result}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_try_inner_catches() {
|
||||
assert_eq!(
|
||||
eval("try { try { throw 1; } catch(e) { e; } } catch(e) { 2; }"),
|
||||
"1"
|
||||
);
|
||||
assert_eq!(eval("try { try { throw 1; } catch(e) { e; } } catch(e) { 2; }"), "1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nested_try_outer_catches() {
|
||||
assert_eq!(
|
||||
eval("try { try { throw 1; } catch(e) { throw 2; } } catch(e) { e; }"),
|
||||
"2"
|
||||
);
|
||||
assert_eq!(eval("try { try { throw 1; } catch(e) { throw 2; } } catch(e) { e; }"), "2");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_try_catch_finally() {
|
||||
assert_eq!(
|
||||
eval("var x = 0; try { x = 1; } catch(e) { x = 2; } finally { x = 3; } x;"),
|
||||
"3"
|
||||
);
|
||||
assert_eq!(eval("var x = 0; try { x = 1; } catch(e) { x = 2; } finally { x = 3; } x;"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -92,8 +77,5 @@ fn test_throw_err_type_error_is_object() {
|
||||
#[test]
|
||||
fn test_uncaught_type_error_message() {
|
||||
let result = eval("null.prop;");
|
||||
assert!(
|
||||
result.contains("TypeError"),
|
||||
"expected TypeError in message, got: {result}"
|
||||
);
|
||||
assert!(result.contains("TypeError"), "expected TypeError in message, got: {result}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user