feat: 提取 oxide_builtins 独立 crate,泛型化 <H: VmHost>

This commit is contained in:
爱特每羊队
2026-06-25 22:38:23 +08:00
parent ffc0916a6b
commit 2f5d91bd48
72 changed files with 1983 additions and 1595 deletions
+1
View File
@@ -10,6 +10,7 @@ description = "OxideJS - Register-based VM with epoch arena memory"
oxide_bytecode = { path = "../oxide_bytecode" }
oxide_kernel = { path = "../oxide_kernel" }
oxide_runtime_api = { path = "../oxide_runtime_api" }
oxide_builtins = { path = "../oxide_builtins" }
oxide_types = { path = "../oxide_types" }
bumpalo.workspace = true
fast-float.workspace = true
+31 -31
View File
@@ -8,36 +8,36 @@ use crate::bind_constructor;
pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let _array_methods = ArrayMethods {
is_array: crate::builtins::array::array_is_array as *const (),
push: crate::builtins::array::array_push as *const (),
pop: crate::builtins::array::array_pop as *const (),
slice: crate::builtins::array::array_slice as *const (),
splice: crate::builtins::array::array_splice as *const (),
concat: crate::builtins::array::array_concat as *const (),
join: crate::builtins::array::array_join as *const (),
index_of: crate::builtins::array::array_index_of as *const (),
includes: crate::builtins::array::array_includes as *const (),
reverse: crate::builtins::array::array_reverse as *const (),
for_each: crate::builtins::array::array_for_each as *const (),
map: crate::builtins::array::array_map as *const (),
filter: crate::builtins::array::array_filter as *const (),
reduce: crate::builtins::array::array_reduce as *const (),
find: crate::builtins::array::array_find as *const (),
some: crate::builtins::array::array_some as *const (),
every: crate::builtins::array::array_every as *const (),
flat: crate::builtins::array::array_flat as *const (),
flat_map: crate::builtins::array::array_flat_map as *const (),
shift: crate::builtins::array::array_shift as *const (),
unshift: crate::builtins::array::array_unshift as *const (),
fill: crate::builtins::array::array_fill as *const (),
copy_within: crate::builtins::array::array_copy_within as *const (),
at: crate::builtins::array::array_at as *const (),
last_index_of: crate::builtins::array::array_last_index_of as *const (),
find_index: crate::builtins::array::array_find_index as *const (),
find_last: crate::builtins::array::array_find_last as *const (),
reduce_right: crate::builtins::array::array_reduce_right as *const (),
sort: crate::builtins::array::array_sort as *const (),
values: crate::builtins::array::array_values as *const (),
is_array: oxide_builtins::array::array_is_array::<crate::vm::Vm> as *const (),
push: oxide_builtins::array::array_push::<crate::vm::Vm> as *const (),
pop: oxide_builtins::array::array_pop::<crate::vm::Vm> as *const (),
slice: oxide_builtins::array::array_slice::<crate::vm::Vm> as *const (),
splice: oxide_builtins::array::array_splice::<crate::vm::Vm> as *const (),
concat: oxide_builtins::array::array_concat::<crate::vm::Vm> as *const (),
join: oxide_builtins::array::array_join::<crate::vm::Vm> as *const (),
index_of: oxide_builtins::array::array_index_of::<crate::vm::Vm> as *const (),
includes: oxide_builtins::array::array_includes::<crate::vm::Vm> as *const (),
reverse: oxide_builtins::array::array_reverse::<crate::vm::Vm> as *const (),
for_each: oxide_builtins::array::array_for_each::<crate::vm::Vm> as *const (),
map: oxide_builtins::array::array_map::<crate::vm::Vm> as *const (),
filter: oxide_builtins::array::array_filter::<crate::vm::Vm> as *const (),
reduce: oxide_builtins::array::array_reduce::<crate::vm::Vm> as *const (),
find: oxide_builtins::array::array_find::<crate::vm::Vm> as *const (),
some: oxide_builtins::array::array_some::<crate::vm::Vm> as *const (),
every: oxide_builtins::array::array_every::<crate::vm::Vm> as *const (),
flat: oxide_builtins::array::array_flat::<crate::vm::Vm> as *const (),
flat_map: oxide_builtins::array::array_flat_map::<crate::vm::Vm> as *const (),
shift: oxide_builtins::array::array_shift::<crate::vm::Vm> as *const (),
unshift: oxide_builtins::array::array_unshift::<crate::vm::Vm> as *const (),
fill: oxide_builtins::array::array_fill::<crate::vm::Vm> as *const (),
copy_within: oxide_builtins::array::array_copy_within::<crate::vm::Vm> as *const (),
at: oxide_builtins::array::array_at::<crate::vm::Vm> as *const (),
last_index_of: oxide_builtins::array::array_last_index_of::<crate::vm::Vm> as *const (),
find_index: oxide_builtins::array::array_find_index::<crate::vm::Vm> as *const (),
find_last: oxide_builtins::array::array_find_last::<crate::vm::Vm> as *const (),
reduce_right: oxide_builtins::array::array_reduce_right::<crate::vm::Vm> as *const (),
sort: oxide_builtins::array::array_sort::<crate::vm::Vm> as *const (),
values: oxide_builtins::array::array_values::<crate::vm::Vm> as *const (),
};
session.builtin_world().bind_array_methods(
@@ -47,5 +47,5 @@ pub fn bind_array(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
);
let ctor_ptr = session.builtin_world().array_constructor.as_ptr() as *mut JsObject;
bind_constructor!(core, global, "Array", ctor_ptr, crate::builtins::array::array_constructor, 1, hash: true);
bind_constructor!(core, global, "Array", ctor_ptr, oxide_builtins::array::array_constructor::<crate::vm::Vm>, 1, hash: true);
}
@@ -11,13 +11,21 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
let proto_ptr = session.builtin_world().array_buffer_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
configure_native_constructor(ctor, crate::builtins::array_buffer::array_buffer_constructor as *const (), 1);
configure_native_constructor(
ctor,
oxide_builtins::array_buffer::array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
);
apply_binding_table(
session.builtin_world(),
ctor,
core,
&[("isView", crate::builtins::array_buffer::array_buffer_is_view as *const (), 1)],
&[(
"isView",
oxide_builtins::array_buffer::array_buffer_is_view::<crate::vm::Vm> as *const (),
1,
)],
);
apply_binding_table(
@@ -25,9 +33,17 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
proto,
core,
&[
("byteLength", crate::builtins::array_buffer::array_buffer_byte_length as *const (), 0),
("slice", crate::builtins::array_buffer::array_buffer_slice as *const (), 2),
("toString", crate::builtins::array_buffer::array_buffer_to_string as *const (), 0),
(
"byteLength",
oxide_builtins::array_buffer::array_buffer_byte_length::<crate::vm::Vm> as *const (),
0,
),
("slice", oxide_builtins::array_buffer::array_buffer_slice::<crate::vm::Vm> as *const (), 2),
(
"toString",
oxide_builtins::array_buffer::array_buffer_to_string::<crate::vm::Vm> as *const (),
0,
),
],
);
@@ -36,7 +52,7 @@ pub fn bind_array_buffer(core: &Arc<KernelCore>, session: &KernelSession, global
global,
"ArrayBuffer",
ctor_ptr,
crate::builtins::array_buffer::array_buffer_constructor,
oxide_builtins::array_buffer::array_buffer_constructor::<crate::vm::Vm>,
1,
hash: true
);
+12 -4
View File
@@ -11,17 +11,25 @@ pub fn bind_boolean(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
let proto_ptr = session.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, oxide_builtins::boolean::boolean_constructor::<crate::vm::Vm> as *const (), 1);
apply_binding_table(
session.builtin_world(),
proto,
core,
&[
("valueOf", crate::builtins::boolean::boolean_prototype_value_of as *const (), 0),
("toString", crate::builtins::boolean::boolean_prototype_to_string as *const (), 0),
(
"valueOf",
oxide_builtins::boolean::boolean_prototype_value_of::<crate::vm::Vm> as *const (),
0,
),
(
"toString",
oxide_builtins::boolean::boolean_prototype_to_string::<crate::vm::Vm> as *const (),
0,
),
],
);
bind_constructor!(core, global, "Boolean", ctor_ptr, crate::builtins::boolean::boolean_constructor, 1, hash: true);
bind_constructor!(core, global, "Boolean", ctor_ptr, oxide_builtins::boolean::boolean_constructor::<crate::vm::Vm>, 1, hash: true);
}
+103 -23
View File
@@ -11,36 +11,116 @@ pub fn bind_data_view(core: &Arc<KernelCore>, session: &KernelSession, global: &
let proto_ptr = session.builtin_world().data_view_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
configure_native_constructor(ctor, crate::builtins::data_view::data_view_constructor as *const (), 3);
configure_native_constructor(
ctor,
oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm> as *const (),
3,
);
apply_binding_table(
session.builtin_world(),
proto,
core,
&[
("getInt8", crate::builtins::data_view::data_view_get_int8 as *const (), 1),
("getUint8", crate::builtins::data_view::data_view_get_uint8 as *const (), 1),
("getInt16", crate::builtins::data_view::data_view_get_int16 as *const (), 2),
("getUint16", crate::builtins::data_view::data_view_get_uint16 as *const (), 2),
("getInt32", crate::builtins::data_view::data_view_get_int32 as *const (), 2),
("getUint32", crate::builtins::data_view::data_view_get_uint32 as *const (), 2),
("getFloat32", crate::builtins::data_view::data_view_get_float32 as *const (), 2),
("getFloat64", crate::builtins::data_view::data_view_get_float64 as *const (), 2),
("getBigInt64", crate::builtins::data_view::data_view_get_big_int64 as *const (), 2),
("getBigUint64", crate::builtins::data_view::data_view_get_big_uint64 as *const (), 2),
("setInt8", crate::builtins::data_view::data_view_set_int8 as *const (), 2),
("setUint8", crate::builtins::data_view::data_view_set_uint8 as *const (), 2),
("setInt16", crate::builtins::data_view::data_view_set_int16 as *const (), 3),
("setUint16", crate::builtins::data_view::data_view_set_uint16 as *const (), 3),
("setInt32", crate::builtins::data_view::data_view_set_int32 as *const (), 3),
("setUint32", crate::builtins::data_view::data_view_set_uint32 as *const (), 3),
("setFloat32", crate::builtins::data_view::data_view_set_float32 as *const (), 3),
("setFloat64", crate::builtins::data_view::data_view_set_float64 as *const (), 3),
("setBigInt64", crate::builtins::data_view::data_view_set_big_int64 as *const (), 3),
("setBigUint64", crate::builtins::data_view::data_view_set_big_uint64 as *const (), 3),
("toString", crate::builtins::data_view::data_view_to_string as *const (), 0),
("getInt8", oxide_builtins::data_view::data_view_get_int8::<crate::vm::Vm> as *const (), 1),
(
"getUint8",
oxide_builtins::data_view::data_view_get_uint8::<crate::vm::Vm> as *const (),
1,
),
(
"getInt16",
oxide_builtins::data_view::data_view_get_int16::<crate::vm::Vm> as *const (),
2,
),
(
"getUint16",
oxide_builtins::data_view::data_view_get_uint16::<crate::vm::Vm> as *const (),
2,
),
(
"getInt32",
oxide_builtins::data_view::data_view_get_int32::<crate::vm::Vm> as *const (),
2,
),
(
"getUint32",
oxide_builtins::data_view::data_view_get_uint32::<crate::vm::Vm> as *const (),
2,
),
(
"getFloat32",
oxide_builtins::data_view::data_view_get_float32::<crate::vm::Vm> as *const (),
2,
),
(
"getFloat64",
oxide_builtins::data_view::data_view_get_float64::<crate::vm::Vm> as *const (),
2,
),
(
"getBigInt64",
oxide_builtins::data_view::data_view_get_big_int64::<crate::vm::Vm> as *const (),
2,
),
(
"getBigUint64",
oxide_builtins::data_view::data_view_get_big_uint64::<crate::vm::Vm> as *const (),
2,
),
("setInt8", oxide_builtins::data_view::data_view_set_int8::<crate::vm::Vm> as *const (), 2),
(
"setUint8",
oxide_builtins::data_view::data_view_set_uint8::<crate::vm::Vm> as *const (),
2,
),
(
"setInt16",
oxide_builtins::data_view::data_view_set_int16::<crate::vm::Vm> as *const (),
3,
),
(
"setUint16",
oxide_builtins::data_view::data_view_set_uint16::<crate::vm::Vm> as *const (),
3,
),
(
"setInt32",
oxide_builtins::data_view::data_view_set_int32::<crate::vm::Vm> as *const (),
3,
),
(
"setUint32",
oxide_builtins::data_view::data_view_set_uint32::<crate::vm::Vm> as *const (),
3,
),
(
"setFloat32",
oxide_builtins::data_view::data_view_set_float32::<crate::vm::Vm> as *const (),
3,
),
(
"setFloat64",
oxide_builtins::data_view::data_view_set_float64::<crate::vm::Vm> as *const (),
3,
),
(
"setBigInt64",
oxide_builtins::data_view::data_view_set_big_int64::<crate::vm::Vm> as *const (),
3,
),
(
"setBigUint64",
oxide_builtins::data_view::data_view_set_big_uint64::<crate::vm::Vm> as *const (),
3,
),
(
"toString",
oxide_builtins::data_view::data_view_to_string::<crate::vm::Vm> as *const (),
0,
),
],
);
bind_constructor!(core, global, "DataView", ctor_ptr, crate::builtins::data_view::data_view_constructor, 3, hash: true);
bind_constructor!(core, global, "DataView", ctor_ptr, oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm>, 3, hash: true);
}
+65 -37
View File
@@ -11,16 +11,16 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
let proto_ptr = session.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, oxide_builtins::date::date_constructor::<crate::vm::Vm> as *const (), 7);
apply_binding_table(
session.builtin_world(),
ctor,
core,
&[
("now", crate::builtins::date::date_now as *const (), 0),
("parse", crate::builtins::date::date_parse as *const (), 1),
("UTC", crate::builtins::date::date_utc as *const (), 7),
("now", oxide_builtins::date::date_now::<crate::vm::Vm> as *const (), 0),
("parse", oxide_builtins::date::date_parse::<crate::vm::Vm> as *const (), 1),
("UTC", oxide_builtins::date::date_utc::<crate::vm::Vm> as *const (), 7),
],
);
@@ -29,39 +29,67 @@ pub fn bind_date(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
proto,
core,
&[
("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", oxide_builtins::date::date_get_time::<crate::vm::Vm> as *const (), 0),
("getFullYear", oxide_builtins::date::date_get_full_year::<crate::vm::Vm> as *const (), 0),
("getMonth", oxide_builtins::date::date_get_month::<crate::vm::Vm> as *const (), 0),
("getDate", oxide_builtins::date::date_get_date::<crate::vm::Vm> as *const (), 0),
("getDay", oxide_builtins::date::date_get_day::<crate::vm::Vm> as *const (), 0),
("getHours", oxide_builtins::date::date_get_hours::<crate::vm::Vm> as *const (), 0),
("getMinutes", oxide_builtins::date::date_get_minutes::<crate::vm::Vm> as *const (), 0),
("getSeconds", oxide_builtins::date::date_get_seconds::<crate::vm::Vm> as *const (), 0),
(
"getMilliseconds",
oxide_builtins::date::date_get_milliseconds::<crate::vm::Vm> as *const (),
0,
),
(
"getUTCFullYear",
oxide_builtins::date::date_get_utc_full_year::<crate::vm::Vm> as *const (),
0,
),
("getUTCMonth", oxide_builtins::date::date_get_utc_month::<crate::vm::Vm> as *const (), 0),
("getUTCDate", oxide_builtins::date::date_get_utc_date::<crate::vm::Vm> as *const (), 0),
("getUTCDay", oxide_builtins::date::date_get_utc_day::<crate::vm::Vm> as *const (), 0),
("getUTCHours", oxide_builtins::date::date_get_utc_hours::<crate::vm::Vm> as *const (), 0),
(
"getUTCMinutes",
oxide_builtins::date::date_get_utc_minutes::<crate::vm::Vm> as *const (),
0,
),
(
"getUTCSeconds",
oxide_builtins::date::date_get_utc_seconds::<crate::vm::Vm> as *const (),
0,
),
(
"getUTCMilliseconds",
oxide_builtins::date::date_get_utc_milliseconds::<crate::vm::Vm> as *const (),
0,
),
(
"getTimezoneOffset",
oxide_builtins::date::date_get_timezone_offset::<crate::vm::Vm> as *const (),
0,
),
("setTime", oxide_builtins::date::date_set_time::<crate::vm::Vm> as *const (), 1),
("setFullYear", oxide_builtins::date::date_set_full_year::<crate::vm::Vm> as *const (), 3),
("setMonth", oxide_builtins::date::date_set_month::<crate::vm::Vm> as *const (), 2),
("setDate", oxide_builtins::date::date_set_date::<crate::vm::Vm> as *const (), 1),
("setHours", oxide_builtins::date::date_set_hours::<crate::vm::Vm> as *const (), 4),
("setMinutes", oxide_builtins::date::date_set_minutes::<crate::vm::Vm> as *const (), 3),
("setSeconds", oxide_builtins::date::date_set_seconds::<crate::vm::Vm> as *const (), 2),
(
"setMilliseconds",
oxide_builtins::date::date_set_milliseconds::<crate::vm::Vm> as *const (),
1,
),
("toISOString", oxide_builtins::date::date_to_iso_string::<crate::vm::Vm> as *const (), 0),
("toJSON", oxide_builtins::date::date_to_json::<crate::vm::Vm> as *const (), 1),
("toString", oxide_builtins::date::date_to_string::<crate::vm::Vm> as *const (), 0),
("toDateString", oxide_builtins::date::date_to_date_string::<crate::vm::Vm> as *const (), 0),
("toTimeString", oxide_builtins::date::date_to_time_string::<crate::vm::Vm> as *const (), 0),
("toUTCString", oxide_builtins::date::date_to_utc_string::<crate::vm::Vm> as *const (), 0),
("valueOf", oxide_builtins::date::date_value_of::<crate::vm::Vm> as *const (), 0),
],
);
+16 -16
View File
@@ -45,15 +45,15 @@ fn bind_error_subtype_constructor(
pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let error_methods = ErrorMethods {
error: crate::builtins::error::error_constructor as *const (),
type_error: crate::builtins::error::type_error_constructor as *const (),
reference_error: crate::builtins::error::reference_error_constructor as *const (),
range_error: crate::builtins::error::range_error_constructor as *const (),
syntax_error: crate::builtins::error::syntax_error_constructor as *const (),
uri_error: crate::builtins::error::uri_error_constructor as *const (),
eval_error: crate::builtins::error::eval_error_constructor as *const (),
to_string: crate::builtins::error::error_to_string as *const (),
stack: crate::builtins::error::error_stack_getter as *const (),
error: oxide_builtins::error::error_constructor::<crate::vm::Vm> as *const (),
type_error: oxide_builtins::error::type_error_constructor::<crate::vm::Vm> as *const (),
reference_error: oxide_builtins::error::reference_error_constructor::<crate::vm::Vm> as *const (),
range_error: oxide_builtins::error::range_error_constructor::<crate::vm::Vm> as *const (),
syntax_error: oxide_builtins::error::syntax_error_constructor::<crate::vm::Vm> as *const (),
uri_error: oxide_builtins::error::uri_error_constructor::<crate::vm::Vm> as *const (),
eval_error: oxide_builtins::error::eval_error_constructor::<crate::vm::Vm> as *const (),
to_string: oxide_builtins::error::error_to_string::<crate::vm::Vm> as *const (),
stack: oxide_builtins::error::error_stack_getter::<crate::vm::Vm> as *const (),
};
session.builtin_world().bind_error_methods(
&error_methods,
@@ -74,7 +74,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"TypeError",
session.builtin_world().type_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::type_error_constructor as *const (),
oxide_builtins::error::type_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_constructor(
core,
@@ -82,7 +82,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"ReferenceError",
session.builtin_world().reference_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::reference_error_constructor as *const (),
oxide_builtins::error::reference_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_constructor(
core,
@@ -90,7 +90,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"RangeError",
session.builtin_world().range_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::range_error_constructor as *const (),
oxide_builtins::error::range_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_constructor(
core,
@@ -98,7 +98,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"SyntaxError",
session.builtin_world().syntax_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::syntax_error_constructor as *const (),
oxide_builtins::error::syntax_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_constructor(
core,
@@ -106,7 +106,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"URIError",
session.builtin_world().uri_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::uri_error_constructor as *const (),
oxide_builtins::error::uri_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_constructor(
core,
@@ -114,7 +114,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
"EvalError",
session.builtin_world().eval_error_proto.as_ptr() as *mut JsObject,
crate::builtins::error::eval_error_constructor as *const (),
oxide_builtins::error::eval_error_constructor::<crate::vm::Vm> as *const (),
);
{
@@ -122,7 +122,7 @@ pub fn bind_error(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
let err_ctor = unsafe { &mut *err_ctor_ptr };
// SAFETY: error_constructor is a NativeFn fn-item.
err_ctor.set_native_fn(Some(unsafe {
NativeFnPtr::from_raw(crate::builtins::error::error_constructor as *const ())
NativeFnPtr::from_raw(oxide_builtins::error::error_constructor::<crate::vm::Vm> as *const ())
}));
}
}
@@ -9,10 +9,10 @@ use super::bind_global_value;
pub fn bind_function(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let function_methods = FunctionMethods {
call: crate::builtins::function::function_call as *const (),
apply: crate::builtins::function::function_apply as *const (),
bind: crate::builtins::function::function_bind as *const (),
to_string: crate::builtins::function::function_to_string as *const (),
call: oxide_builtins::function::function_call::<crate::vm::Vm> as *const (),
apply: oxide_builtins::function::function_apply::<crate::vm::Vm> as *const (),
bind: oxide_builtins::function::function_bind::<crate::vm::Vm> as *const (),
to_string: oxide_builtins::function::function_to_string::<crate::vm::Vm> as *const (),
};
session.builtin_world().bind_function_methods(
&function_methods,
+14 -6
View File
@@ -11,12 +11,20 @@ pub fn bind_global(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
global,
core,
&[
("escape", crate::builtins::global::js_escape as *const (), 1),
("unescape", crate::builtins::global::js_unescape as *const (), 1),
("encodeURI", crate::builtins::global::encode_uri as *const (), 1),
("decodeURI", crate::builtins::global::decode_uri as *const (), 1),
("encodeURIComponent", crate::builtins::global::encode_uri_component as *const (), 1),
("decodeURIComponent", crate::builtins::global::decode_uri_component as *const (), 1),
("escape", oxide_builtins::global::js_escape::<crate::vm::Vm> as *const (), 1),
("unescape", oxide_builtins::global::js_unescape::<crate::vm::Vm> as *const (), 1),
("encodeURI", oxide_builtins::global::encode_uri::<crate::vm::Vm> as *const (), 1),
("decodeURI", oxide_builtins::global::decode_uri::<crate::vm::Vm> as *const (), 1),
(
"encodeURIComponent",
oxide_builtins::global::encode_uri_component::<crate::vm::Vm> as *const (),
1,
),
(
"decodeURIComponent",
oxide_builtins::global::decode_uri_component::<crate::vm::Vm> as *const (),
1,
),
],
);
}
@@ -11,13 +11,17 @@ pub fn bind_iterator(core: &Arc<KernelCore>, session: &KernelSession, global: &m
let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)));
iterator.set_function(true);
configure_native_constructor(&mut iterator, crate::builtins::iterator::iterator_constructor as *const (), 0);
configure_native_constructor(
&mut iterator,
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
0,
);
apply_binding_table(
session.builtin_world(),
&mut iterator,
core,
&[("from", crate::builtins::iterator::iterator_from as *const (), 1)],
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
);
bind_global_value(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
+2 -2
View File
@@ -14,8 +14,8 @@ pub fn bind_json(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
json,
core,
&[
("parse", crate::builtins::json::json_parse as *const (), 1),
("stringify", crate::builtins::json::json_stringify as *const (), 1),
("parse", oxide_builtins::json::json_parse::<crate::vm::Vm> as *const (), 1),
("stringify", oxide_builtins::json::json_stringify::<crate::vm::Vm> as *const (), 1),
],
);
+8 -8
View File
@@ -10,7 +10,7 @@ pub fn bind_map(core: &Arc<KernelCore>, session: &KernelSession, global: &mut Js
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = session.builtin_world().map_proto.as_ptr() as *mut JsObject;
configure_native_constructor(ctor, crate::builtins::map::map_constructor as *const (), 1);
configure_native_constructor(ctor, oxide_builtins::map::map_constructor::<crate::vm::Vm> as *const (), 1);
let proto = unsafe { &mut *proto_ptr };
apply_binding_table(
@@ -18,14 +18,14 @@ pub fn bind_map(core: &Arc<KernelCore>, session: &KernelSession, global: &mut Js
proto,
core,
&[
("set", crate::builtins::map::map_set as *const (), 2),
("get", crate::builtins::map::map_get as *const (), 1),
("has", crate::builtins::map::map_has as *const (), 1),
("delete", crate::builtins::map::map_delete as *const (), 1),
("clear", crate::builtins::map::map_clear as *const (), 0),
("size", crate::builtins::map::map_size as *const (), 0),
("set", oxide_builtins::map::map_set::<crate::vm::Vm> as *const (), 2),
("get", oxide_builtins::map::map_get::<crate::vm::Vm> as *const (), 1),
("has", oxide_builtins::map::map_has::<crate::vm::Vm> as *const (), 1),
("delete", oxide_builtins::map::map_delete::<crate::vm::Vm> as *const (), 1),
("clear", oxide_builtins::map::map_clear::<crate::vm::Vm> as *const (), 0),
("size", oxide_builtins::map::map_size::<crate::vm::Vm> as *const (), 0),
],
);
bind_constructor!(core, global, "Map", ctor_ptr, crate::builtins::map::map_constructor, 1, hash: true);
bind_constructor!(core, global, "Map", ctor_ptr, oxide_builtins::map::map_constructor::<crate::vm::Vm>, 1, hash: true);
}
+35 -35
View File
@@ -18,41 +18,41 @@ pub fn bind_math(core: &Arc<KernelCore>, session: &KernelSession, global: &mut J
math,
sf,
sh,
("abs", crate::builtins::math::math_abs, 1),
("acos", crate::builtins::math::math_acos, 1),
("asin", crate::builtins::math::math_asin, 1),
("atan", crate::builtins::math::math_atan, 1),
("atan2", crate::builtins::math::math_atan2, 2),
("cbrt", crate::builtins::math::math_cbrt, 1),
("ceil", crate::builtins::math::math_ceil, 1),
("cos", crate::builtins::math::math_cos, 1),
("cosh", crate::builtins::math::math_cosh, 1),
("exp", crate::builtins::math::math_exp, 1),
("floor", crate::builtins::math::math_floor, 1),
("hypot", crate::builtins::math::math_hypot, 2),
("imul", crate::builtins::math::math_imul, 2),
("log", crate::builtins::math::math_log, 1),
("log10", crate::builtins::math::math_log10, 1),
("log2", crate::builtins::math::math_log2, 1),
("max", crate::builtins::math::math_max, 2),
("min", crate::builtins::math::math_min, 2),
("pow", crate::builtins::math::math_pow, 2),
("random", crate::builtins::math::math_random, 0),
("round", crate::builtins::math::math_round, 1),
("sign", crate::builtins::math::math_sign, 1),
("sin", crate::builtins::math::math_sin, 1),
("sinh", crate::builtins::math::math_sinh, 1),
("sqrt", crate::builtins::math::math_sqrt, 1),
("tan", crate::builtins::math::math_tan, 1),
("tanh", crate::builtins::math::math_tanh, 1),
("trunc", crate::builtins::math::math_trunc, 1),
("acosh", crate::builtins::math::math_acosh, 1),
("asinh", crate::builtins::math::math_asinh, 1),
("atanh", crate::builtins::math::math_atanh, 1),
("clz32", crate::builtins::math::math_clz32, 1),
("expm1", crate::builtins::math::math_expm1, 1),
("fround", crate::builtins::math::math_fround, 1),
("log1p", crate::builtins::math::math_log1p, 1),
("abs", oxide_builtins::math::math_abs::<crate::vm::Vm>, 1),
("acos", oxide_builtins::math::math_acos::<crate::vm::Vm>, 1),
("asin", oxide_builtins::math::math_asin::<crate::vm::Vm>, 1),
("atan", oxide_builtins::math::math_atan::<crate::vm::Vm>, 1),
("atan2", oxide_builtins::math::math_atan2::<crate::vm::Vm>, 2),
("cbrt", oxide_builtins::math::math_cbrt::<crate::vm::Vm>, 1),
("ceil", oxide_builtins::math::math_ceil::<crate::vm::Vm>, 1),
("cos", oxide_builtins::math::math_cos::<crate::vm::Vm>, 1),
("cosh", oxide_builtins::math::math_cosh::<crate::vm::Vm>, 1),
("exp", oxide_builtins::math::math_exp::<crate::vm::Vm>, 1),
("floor", oxide_builtins::math::math_floor::<crate::vm::Vm>, 1),
("hypot", oxide_builtins::math::math_hypot::<crate::vm::Vm>, 2),
("imul", oxide_builtins::math::math_imul::<crate::vm::Vm>, 2),
("log", oxide_builtins::math::math_log::<crate::vm::Vm>, 1),
("log10", oxide_builtins::math::math_log10::<crate::vm::Vm>, 1),
("log2", oxide_builtins::math::math_log2::<crate::vm::Vm>, 1),
("max", oxide_builtins::math::math_max::<crate::vm::Vm>, 2),
("min", oxide_builtins::math::math_min::<crate::vm::Vm>, 2),
("pow", oxide_builtins::math::math_pow::<crate::vm::Vm>, 2),
("random", oxide_builtins::math::math_random::<crate::vm::Vm>, 0),
("round", oxide_builtins::math::math_round::<crate::vm::Vm>, 1),
("sign", oxide_builtins::math::math_sign::<crate::vm::Vm>, 1),
("sin", oxide_builtins::math::math_sin::<crate::vm::Vm>, 1),
("sinh", oxide_builtins::math::math_sinh::<crate::vm::Vm>, 1),
("sqrt", oxide_builtins::math::math_sqrt::<crate::vm::Vm>, 1),
("tan", oxide_builtins::math::math_tan::<crate::vm::Vm>, 1),
("tanh", oxide_builtins::math::math_tanh::<crate::vm::Vm>, 1),
("trunc", oxide_builtins::math::math_trunc::<crate::vm::Vm>, 1),
("acosh", oxide_builtins::math::math_acosh::<crate::vm::Vm>, 1),
("asinh", oxide_builtins::math::math_asinh::<crate::vm::Vm>, 1),
("atanh", oxide_builtins::math::math_atanh::<crate::vm::Vm>, 1),
("clz32", oxide_builtins::math::math_clz32::<crate::vm::Vm>, 1),
("expm1", oxide_builtins::math::math_expm1::<crate::vm::Vm>, 1),
("fround", oxide_builtins::math::math_fround::<crate::vm::Vm>, 1),
("log1p", oxide_builtins::math::math_log1p::<crate::vm::Vm>, 1),
);
for (name, val) in [
+25 -13
View File
@@ -12,17 +12,21 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
let proto_ptr = session.builtin_world().number_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
configure_native_constructor(ctor, crate::builtins::number::number_constructor as *const (), 1);
configure_native_constructor(ctor, oxide_builtins::number::number_constructor::<crate::vm::Vm> as *const (), 1);
apply_binding_table(
session.builtin_world(),
ctor,
core,
&[
("isNaN", crate::builtins::number::number_is_nan as *const (), 1),
("isFinite", crate::builtins::number::number_is_finite as *const (), 1),
("isInteger", crate::builtins::number::number_is_integer as *const (), 1),
("isSafeInteger", crate::builtins::number::number_is_safe_integer as *const (), 1),
("isNaN", oxide_builtins::number::number_is_nan::<crate::vm::Vm> as *const (), 1),
("isFinite", oxide_builtins::number::number_is_finite::<crate::vm::Vm> as *const (), 1),
("isInteger", oxide_builtins::number::number_is_integer::<crate::vm::Vm> as *const (), 1),
(
"isSafeInteger",
oxide_builtins::number::number_is_safe_integer::<crate::vm::Vm> as *const (),
1,
),
],
);
@@ -31,11 +35,19 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
proto,
core,
&[
("toString", crate::builtins::number::number_to_string as *const (), 0),
("toFixed", crate::builtins::number::number_to_fixed as *const (), 0),
("toExponential", crate::builtins::number::number_to_exponential as *const (), 0),
("toPrecision", crate::builtins::number::number_to_precision as *const (), 0),
("valueOf", crate::builtins::number::number_value_of as *const (), 0),
("toString", oxide_builtins::number::number_to_string::<crate::vm::Vm> as *const (), 0),
("toFixed", oxide_builtins::number::number_to_fixed::<crate::vm::Vm> as *const (), 0),
(
"toExponential",
oxide_builtins::number::number_to_exponential::<crate::vm::Vm> as *const (),
0,
),
(
"toPrecision",
oxide_builtins::number::number_to_precision::<crate::vm::Vm> as *const (),
0,
),
("valueOf", oxide_builtins::number::number_value_of::<crate::vm::Vm> as *const (), 0),
],
);
@@ -55,15 +67,15 @@ pub fn bind_number(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
ctor.set_shape_id(next_shape);
}
bind_constructor!(core, global, "Number", ctor_ptr, crate::builtins::number::number_constructor, 1, hash: true);
bind_constructor!(core, global, "Number", ctor_ptr, oxide_builtins::number::number_constructor::<crate::vm::Vm>, 1, hash: true);
apply_binding_table(
session.builtin_world(),
global,
core,
&[
("parseInt", crate::builtins::number::number_parse_int as *const (), 1),
("parseFloat", crate::builtins::number::number_parse_float as *const (), 1),
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 1),
("parseFloat", oxide_builtins::number::number_parse_float::<crate::vm::Vm> as *const (), 1),
],
);
}
+31 -22
View File
@@ -8,32 +8,41 @@ use crate::bind_constructor;
pub fn bind_object(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let methods = ObjectMethods {
keys: crate::builtins::object::object_keys as *const (),
create: crate::builtins::object::object_create as *const (),
assign: crate::builtins::object::object_assign as *const (),
is: crate::builtins::object::object_is 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 (),
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 (),
is_frozen: crate::builtins::object::object_is_frozen as *const (),
is_sealed: crate::builtins::object::object_is_sealed as *const (),
is_extensible: crate::builtins::object::object_is_extensible as *const (),
get_own_property_names: crate::builtins::object::object_get_own_property_names as *const (),
define_properties: crate::builtins::object::object_define_properties as *const (),
from_entries: crate::builtins::object::object_from_entries as *const (),
get_prototype_of: crate::builtins::object::object_get_prototype_of as *const (),
has_own: crate::builtins::object::object_has_own as *const (),
entries: crate::builtins::object::object_entries as *const (),
values: crate::builtins::object::object_values as *const (),
has_own_property: crate::builtins::object::object_proto_has_own_property as *const (),
property_is_enumerable: crate::builtins::object::object_proto_property_is_enumerable as *const (),
keys: oxide_builtins::object::object_keys::<crate::vm::Vm> as *const (),
create: oxide_builtins::object::object_create::<crate::vm::Vm> as *const (),
assign: oxide_builtins::object::object_assign::<crate::vm::Vm> as *const (),
is: oxide_builtins::object::object_is::<crate::vm::Vm> as *const (),
define_property: oxide_builtins::object::object_define_property::<crate::vm::Vm> as *const (),
get_own_property_descriptor: oxide_builtins::object::object_get_own_property_descriptor::<crate::vm::Vm>
as *const (),
freeze: oxide_builtins::object::object_freeze::<crate::vm::Vm> as *const (),
seal: oxide_builtins::object::object_seal::<crate::vm::Vm> as *const (),
prevent_extensions: oxide_builtins::object::object_prevent_extensions::<crate::vm::Vm> as *const (),
is_frozen: oxide_builtins::object::object_is_frozen::<crate::vm::Vm> as *const (),
is_sealed: oxide_builtins::object::object_is_sealed::<crate::vm::Vm> as *const (),
is_extensible: oxide_builtins::object::object_is_extensible::<crate::vm::Vm> as *const (),
get_own_property_names: oxide_builtins::object::object_get_own_property_names::<crate::vm::Vm> as *const (),
define_properties: oxide_builtins::object::object_define_properties::<crate::vm::Vm> as *const (),
from_entries: oxide_builtins::object::object_from_entries::<crate::vm::Vm> as *const (),
get_prototype_of: oxide_builtins::object::object_get_prototype_of::<crate::vm::Vm> as *const (),
has_own: oxide_builtins::object::object_has_own::<crate::vm::Vm> as *const (),
entries: oxide_builtins::object::object_entries::<crate::vm::Vm> as *const (),
values: oxide_builtins::object::object_values::<crate::vm::Vm> as *const (),
has_own_property: oxide_builtins::object::object_proto_has_own_property::<crate::vm::Vm> as *const (),
property_is_enumerable: oxide_builtins::object::object_proto_property_is_enumerable::<crate::vm::Vm>
as *const (),
};
session
.builtin_world()
.bind_object_methods(&methods, core.perm_interner().as_ref(), core.shape_forge().as_ref());
let ctor_ptr = session.builtin_world().object_constructor.as_ptr() as *mut JsObject;
bind_constructor!(core, global, "Object", ctor_ptr, crate::builtins::object::object_constructor, 1);
bind_constructor!(
core,
global,
"Object",
ctor_ptr,
oxide_builtins::object::object_constructor::<crate::vm::Vm>,
1
);
}
+38 -14
View File
@@ -17,23 +17,47 @@ pub fn bind_reflect(core: &Arc<KernelCore>, session: &KernelSession, global: &mu
&mut reflect,
core,
&[
("apply", crate::builtins::reflect::reflect_apply as *const (), 3),
("construct", crate::builtins::reflect::reflect_construct as *const (), 2),
("defineProperty", crate::builtins::reflect::reflect_define_property as *const (), 3),
("deleteProperty", crate::builtins::reflect::reflect_delete_property as *const (), 2),
("get", crate::builtins::reflect::reflect_get as *const (), 2),
("apply", oxide_builtins::reflect::reflect_apply::<crate::vm::Vm> as *const (), 3),
("construct", oxide_builtins::reflect::reflect_construct::<crate::vm::Vm> as *const (), 2),
(
"getOwnPropertyDescriptor",
crate::builtins::reflect::reflect_get_own_property_descriptor as *const (),
"defineProperty",
oxide_builtins::reflect::reflect_define_property::<crate::vm::Vm> as *const (),
3,
),
(
"deleteProperty",
oxide_builtins::reflect::reflect_delete_property::<crate::vm::Vm> as *const (),
2,
),
("get", oxide_builtins::reflect::reflect_get::<crate::vm::Vm> as *const (), 2),
(
"getOwnPropertyDescriptor",
oxide_builtins::reflect::reflect_get_own_property_descriptor::<crate::vm::Vm> as *const (),
2,
),
(
"getPrototypeOf",
oxide_builtins::reflect::reflect_get_prototype_of::<crate::vm::Vm> as *const (),
1,
),
("has", oxide_builtins::reflect::reflect_has::<crate::vm::Vm> as *const (), 2),
(
"isExtensible",
oxide_builtins::reflect::reflect_is_extensible::<crate::vm::Vm> as *const (),
1,
),
("ownKeys", oxide_builtins::reflect::reflect_own_keys::<crate::vm::Vm> as *const (), 1),
(
"preventExtensions",
oxide_builtins::reflect::reflect_prevent_extensions::<crate::vm::Vm> as *const (),
1,
),
("set", oxide_builtins::reflect::reflect_set::<crate::vm::Vm> as *const (), 3),
(
"setPrototypeOf",
oxide_builtins::reflect::reflect_set_prototype_of::<crate::vm::Vm> as *const (),
2,
),
("getPrototypeOf", crate::builtins::reflect::reflect_get_prototype_of as *const (), 1),
("has", crate::builtins::reflect::reflect_has as *const (), 2),
("isExtensible", crate::builtins::reflect::reflect_is_extensible as *const (), 1),
("ownKeys", crate::builtins::reflect::reflect_own_keys as *const (), 1),
("preventExtensions", crate::builtins::reflect::reflect_prevent_extensions as *const (), 1),
("set", crate::builtins::reflect::reflect_set as *const (), 3),
("setPrototypeOf", crate::builtins::reflect::reflect_set_prototype_of as *const (), 2),
],
);
+5 -5
View File
@@ -12,18 +12,18 @@ pub fn bind_regexp(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
let proto_ptr = session.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, oxide_builtins::regexp::regexp_constructor::<crate::vm::Vm> as *const (), 2);
apply_binding_table(
session.builtin_world(),
proto,
core,
&[
("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),
("exec", oxide_builtins::regexp::regexp_exec::<crate::vm::Vm> as *const (), 1),
("test", oxide_builtins::regexp::regexp_test::<crate::vm::Vm> as *const (), 1),
("toString", oxide_builtins::regexp::regexp_to_string::<crate::vm::Vm> as *const (), 0),
],
);
bind_constructor!(core, global, "RegExp", ctor_ptr, crate::builtins::regexp::regexp_constructor, 2, hash: true);
bind_constructor!(core, global, "RegExp", ctor_ptr, oxide_builtins::regexp::regexp_constructor::<crate::vm::Vm>, 2, hash: true);
}
+7 -7
View File
@@ -10,7 +10,7 @@ pub fn bind_set(core: &Arc<KernelCore>, session: &KernelSession, global: &mut Js
let ctor = unsafe { &mut *ctor_ptr };
let proto_ptr = session.builtin_world().set_proto.as_ptr() as *mut JsObject;
configure_native_constructor(ctor, crate::builtins::set::set_constructor as *const (), 1);
configure_native_constructor(ctor, oxide_builtins::set::set_constructor::<crate::vm::Vm> as *const (), 1);
let proto = unsafe { &mut *proto_ptr };
apply_binding_table(
@@ -18,13 +18,13 @@ pub fn bind_set(core: &Arc<KernelCore>, session: &KernelSession, global: &mut Js
proto,
core,
&[
("add", crate::builtins::set::set_add as *const (), 1),
("has", crate::builtins::set::set_has as *const (), 1),
("delete", crate::builtins::set::set_delete as *const (), 1),
("clear", crate::builtins::set::set_clear as *const (), 0),
("size", crate::builtins::set::set_size as *const (), 0),
("add", oxide_builtins::set::set_add::<crate::vm::Vm> as *const (), 1),
("has", oxide_builtins::set::set_has::<crate::vm::Vm> as *const (), 1),
("delete", oxide_builtins::set::set_delete::<crate::vm::Vm> as *const (), 1),
("clear", oxide_builtins::set::set_clear::<crate::vm::Vm> as *const (), 0),
("size", oxide_builtins::set::set_size::<crate::vm::Vm> as *const (), 0),
],
);
bind_constructor!(core, global, "Set", ctor_ptr, crate::builtins::set::set_constructor, 1, hash: true);
bind_constructor!(core, global, "Set", ctor_ptr, oxide_builtins::set::set_constructor::<crate::vm::Vm>, 1, hash: true);
}
+29 -29
View File
@@ -8,33 +8,33 @@ use oxide_types::value::JsValue;
pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let string_methods = StringMethods {
from_char_code: crate::builtins::string::string_from_char_code as *const (),
index_of: crate::builtins::string::string_index_of as *const (),
includes: crate::builtins::string::string_includes as *const (),
char_at: crate::builtins::string::string_char_at as *const (),
char_code_at: crate::builtins::string::string_char_code_at as *const (),
concat: crate::builtins::string::string_concat as *const (),
slice: crate::builtins::string::string_slice as *const (),
substring: crate::builtins::string::string_substring as *const (),
to_upper_case: crate::builtins::string::string_to_upper_case as *const (),
to_lower_case: crate::builtins::string::string_to_lower_case as *const (),
trim: crate::builtins::string::string_trim as *const (),
repeat: crate::builtins::string::string_repeat as *const (),
pad_start: crate::builtins::string::string_pad_start as *const (),
pad_end: crate::builtins::string::string_pad_end as *const (),
starts_with: crate::builtins::string::string_starts_with as *const (),
ends_with: crate::builtins::string::string_ends_with as *const (),
split: crate::builtins::string::string_split as *const (),
replace: crate::builtins::string::string_replace as *const (),
match_fn: crate::builtins::string::string_match_fn as *const (),
search: crate::builtins::string::string_search as *const (),
trim_start: crate::builtins::string::string_trim_start as *const (),
trim_end: crate::builtins::string::string_trim_end as *const (),
code_point_at: crate::builtins::string::string_code_point_at as *const (),
normalize: crate::builtins::string::string_normalize as *const (),
match_all: crate::builtins::string::string_match_all as *const (),
replace_all: crate::builtins::string::string_replace_all as *const (),
value_of: crate::builtins::string::string_value_of as *const (),
from_char_code: oxide_builtins::string::string_from_char_code::<crate::vm::Vm> as *const (),
index_of: oxide_builtins::string::string_index_of::<crate::vm::Vm> as *const (),
includes: oxide_builtins::string::string_includes::<crate::vm::Vm> as *const (),
char_at: oxide_builtins::string::string_char_at::<crate::vm::Vm> as *const (),
char_code_at: oxide_builtins::string::string_char_code_at::<crate::vm::Vm> as *const (),
concat: oxide_builtins::string::string_concat::<crate::vm::Vm> as *const (),
slice: oxide_builtins::string::string_slice::<crate::vm::Vm> as *const (),
substring: oxide_builtins::string::string_substring::<crate::vm::Vm> as *const (),
to_upper_case: oxide_builtins::string::string_to_upper_case::<crate::vm::Vm> as *const (),
to_lower_case: oxide_builtins::string::string_to_lower_case::<crate::vm::Vm> as *const (),
trim: oxide_builtins::string::string_trim::<crate::vm::Vm> as *const (),
repeat: oxide_builtins::string::string_repeat::<crate::vm::Vm> as *const (),
pad_start: oxide_builtins::string::string_pad_start::<crate::vm::Vm> as *const (),
pad_end: oxide_builtins::string::string_pad_end::<crate::vm::Vm> as *const (),
starts_with: oxide_builtins::string::string_starts_with::<crate::vm::Vm> as *const (),
ends_with: oxide_builtins::string::string_ends_with::<crate::vm::Vm> as *const (),
split: oxide_builtins::string::string_split::<crate::vm::Vm> as *const (),
replace: oxide_builtins::string::string_replace::<crate::vm::Vm> as *const (),
match_fn: oxide_builtins::string::string_match_fn::<crate::vm::Vm> as *const (),
search: oxide_builtins::string::string_search::<crate::vm::Vm> as *const (),
trim_start: oxide_builtins::string::string_trim_start::<crate::vm::Vm> as *const (),
trim_end: oxide_builtins::string::string_trim_end::<crate::vm::Vm> as *const (),
code_point_at: oxide_builtins::string::string_code_point_at::<crate::vm::Vm> as *const (),
normalize: oxide_builtins::string::string_normalize::<crate::vm::Vm> as *const (),
match_all: oxide_builtins::string::string_match_all::<crate::vm::Vm> as *const (),
replace_all: oxide_builtins::string::string_replace_all::<crate::vm::Vm> as *const (),
value_of: oxide_builtins::string::string_value_of::<crate::vm::Vm> as *const (),
};
session.builtin_world().bind_string_methods(
&string_methods,
@@ -44,7 +44,7 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
let ctor_ptr = session.builtin_world().string_constructor.as_ptr() as *mut JsObject;
let ctor = unsafe { &mut *ctor_ptr };
configure_native_constructor(ctor, crate::builtins::string::string_constructor as *const (), 1);
configure_native_constructor(ctor, oxide_builtins::string::string_constructor::<crate::vm::Vm> as *const (), 1);
let proto_ptr = session.builtin_world().string_proto.as_ptr() as *mut JsObject;
let proto = unsafe { &mut *proto_ptr };
@@ -52,7 +52,7 @@ pub fn bind_string(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
session.builtin_world(),
proto,
core,
&[("toString", crate::builtins::string::string_to_string as *const (), 0)],
&[("toString", oxide_builtins::string::string_to_string::<crate::vm::Vm> as *const (), 0)],
);
let si_str = core.perm_interner().intern("String").0;
+16 -8
View File
@@ -9,14 +9,22 @@ use oxide_types::value::JsValue;
use crate::bindings::{bind_global_value, configure_native_constructor};
const STUBS: [(&str, *const (), u8); 8] = [
("Proxy", crate::builtins::stubs::proxy_stub as *const (), 2),
("BigInt", crate::builtins::stubs::bigint_stub as *const (), 1),
("WeakMap", crate::builtins::stubs::weakmap_stub as *const (), 0),
("WeakSet", crate::builtins::stubs::weakset_stub as *const (), 0),
("WeakRef", crate::builtins::stubs::weakref_stub as *const (), 1),
("FinalizationRegistry", crate::builtins::stubs::finalization_registry_stub as *const (), 1),
("SharedArrayBuffer", crate::builtins::stubs::shared_array_buffer_stub as *const (), 1),
("Atomics", crate::builtins::stubs::atomics_stub as *const (), 0),
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
("BigInt", oxide_builtins::stubs::bigint_stub::<crate::vm::Vm> as *const (), 1),
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1),
(
"FinalizationRegistry",
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
1,
),
(
"SharedArrayBuffer",
oxide_builtins::stubs::shared_array_buffer_stub::<crate::vm::Vm> as *const (),
1,
),
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
];
pub fn bind_stubs(core: &Arc<KernelCore>, session: &mut KernelSession, global: &mut JsObject) {
+5 -5
View File
@@ -13,15 +13,15 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
let proto_ptr = session.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, oxide_builtins::symbol::symbol_constructor::<crate::vm::Vm> as *const (), 1);
apply_binding_table(
session.builtin_world(),
ctor,
core,
&[
("for", crate::builtins::symbol::symbol_for as *const (), 1),
("keyFor", crate::builtins::symbol::symbol_key_for as *const (), 1),
("for", oxide_builtins::symbol::symbol_for::<crate::vm::Vm> as *const (), 1),
("keyFor", oxide_builtins::symbol::symbol_key_for::<crate::vm::Vm> as *const (), 1),
],
);
@@ -29,7 +29,7 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
session.builtin_world(),
proto,
core,
&[("toString", crate::builtins::symbol::symbol_to_string as *const (), 0)],
&[("toString", oxide_builtins::symbol::symbol_to_string::<crate::vm::Vm> as *const (), 0)],
);
for (name, val) in [
@@ -57,7 +57,7 @@ pub fn bind_symbol(core: &Arc<KernelCore>, session: &KernelSession, global: &mut
bind_well_known_symbol(core, ctor, name, val);
}
bind_constructor!(core, global, "Symbol", ctor_ptr, crate::builtins::symbol::symbol_constructor, 1, hash: true);
bind_constructor!(core, global, "Symbol", ctor_ptr, oxide_builtins::symbol::symbol_constructor::<crate::vm::Vm>, 1, hash: true);
}
fn bind_well_known_symbol(core: &Arc<KernelCore>, ctor: &mut JsObject, name: &str, val: JsValue) {
@@ -8,7 +8,7 @@ use oxide_types::object::JsObject;
macro_rules! bind_typed_array_constructor {
($core:expr, $global:expr, $name:literal, $ctor_ptr:expr, $ctor_fn:path) => {{
let ctor = unsafe { &mut *$ctor_ptr };
configure_native_constructor(ctor, $ctor_fn as *const (), 1);
configure_native_constructor(ctor, ($ctor_fn as fn(&mut $crate::vm::Vm, &[u8]) -> oxide_runtime_api::NativeResult) as *const (), 1);
bind_constructor!($core, $global, $name, $ctor_ptr, $ctor_fn, 1, hash: true);
}};
}
@@ -21,12 +21,20 @@ pub fn bind_typed_array(core: &Arc<KernelCore>, session: &KernelSession, global:
shared_proto,
core,
&[
("at", crate::builtins::typed_array::typed_array_at as *const (), 1),
("fill", crate::builtins::typed_array::typed_array_fill as *const (), 3),
("set", crate::builtins::typed_array::typed_array_set as *const (), 2),
("slice", crate::builtins::typed_array::typed_array_slice as *const (), 2),
("subarray", crate::builtins::typed_array::typed_array_subarray as *const (), 2),
("toString", crate::builtins::typed_array::typed_array_to_string as *const (), 0),
("at", oxide_builtins::typed_array::typed_array_at::<crate::vm::Vm> as *const (), 1),
("fill", oxide_builtins::typed_array::typed_array_fill::<crate::vm::Vm> as *const (), 3),
("set", oxide_builtins::typed_array::typed_array_set::<crate::vm::Vm> as *const (), 2),
("slice", oxide_builtins::typed_array::typed_array_slice::<crate::vm::Vm> as *const (), 2),
(
"subarray",
oxide_builtins::typed_array::typed_array_subarray::<crate::vm::Vm> as *const (),
2,
),
(
"toString",
oxide_builtins::typed_array::typed_array_to_string::<crate::vm::Vm> as *const (),
0,
),
],
);
@@ -35,76 +43,76 @@ pub fn bind_typed_array(core: &Arc<KernelCore>, session: &KernelSession, global:
global,
"Int8Array",
session.builtin_world().int8array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::int8array_constructor
oxide_builtins::typed_array::int8array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Uint8Array",
session.builtin_world().uint8array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::uint8array_constructor
oxide_builtins::typed_array::uint8array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Uint8ClampedArray",
session.builtin_world().uint8clampedarray_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::uint8clampedarray_constructor
oxide_builtins::typed_array::uint8clampedarray_constructor
);
bind_typed_array_constructor!(
core,
global,
"Int16Array",
session.builtin_world().int16array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::int16array_constructor
oxide_builtins::typed_array::int16array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Uint16Array",
session.builtin_world().uint16array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::uint16array_constructor
oxide_builtins::typed_array::uint16array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Int32Array",
session.builtin_world().int32array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::int32array_constructor
oxide_builtins::typed_array::int32array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Uint32Array",
session.builtin_world().uint32array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::uint32array_constructor
oxide_builtins::typed_array::uint32array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Float32Array",
session.builtin_world().float32array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::float32array_constructor
oxide_builtins::typed_array::float32array_constructor
);
bind_typed_array_constructor!(
core,
global,
"Float64Array",
session.builtin_world().float64array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::float64array_constructor
oxide_builtins::typed_array::float64array_constructor
);
bind_typed_array_constructor!(
core,
global,
"BigInt64Array",
session.builtin_world().bigint64array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::bigint64array_constructor
oxide_builtins::typed_array::bigint64array_constructor
);
bind_typed_array_constructor!(
core,
global,
"BigUint64Array",
session.builtin_world().biguint64array_constructor.as_ptr() as *mut JsObject,
crate::builtins::typed_array::biguint64array_constructor
oxide_builtins::typed_array::biguint64array_constructor
);
}
+147 -62
View File
@@ -46,7 +46,8 @@ macro_rules! bind_constructor {
$global.push_prop(val);
}
let ctor = unsafe { &mut *$ctor_ptr };
ctor.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw($ctor_fn as *const ()) }));
let ptr: *const () = ($ctor_fn as fn(&mut $crate::vm::Vm, &[u8]) -> oxide_runtime_api::NativeResult) as *const ();
ctor.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(ptr) }));
ctor.set_native_arg_count($nargs);
}};
}
@@ -127,14 +128,22 @@ fn bind_global_functions(core: &Arc<KernelCore>, session: &KernelSession, global
global,
core,
&[
("parseInt", crate::builtins::number::number_parse_int as *const (), 1),
("parseFloat", crate::builtins::number::number_parse_float as *const (), 1),
("escape", crate::builtins::global::js_escape as *const (), 1),
("unescape", crate::builtins::global::js_unescape as *const (), 1),
("encodeURI", crate::builtins::global::encode_uri as *const (), 1),
("decodeURI", crate::builtins::global::decode_uri as *const (), 1),
("encodeURIComponent", crate::builtins::global::encode_uri_component as *const (), 1),
("decodeURIComponent", crate::builtins::global::decode_uri_component as *const (), 1),
("parseInt", oxide_builtins::number::number_parse_int::<crate::vm::Vm> as *const (), 1),
("parseFloat", oxide_builtins::number::number_parse_float::<crate::vm::Vm> as *const (), 1),
("escape", oxide_builtins::global::js_escape::<crate::vm::Vm> as *const (), 1),
("unescape", oxide_builtins::global::js_unescape::<crate::vm::Vm> as *const (), 1),
("encodeURI", oxide_builtins::global::encode_uri::<crate::vm::Vm> as *const (), 1),
("decodeURI", oxide_builtins::global::decode_uri::<crate::vm::Vm> as *const (), 1),
(
"encodeURIComponent",
oxide_builtins::global::encode_uri_component::<crate::vm::Vm> as *const (),
1,
),
(
"decodeURIComponent",
oxide_builtins::global::decode_uri_component::<crate::vm::Vm> as *const (),
1,
),
],
);
}
@@ -149,23 +158,47 @@ fn bind_reflect_global(core: &Arc<KernelCore>, session: &KernelSession, global:
&mut reflect,
core,
&[
("apply", crate::builtins::reflect::reflect_apply as *const (), 3),
("construct", crate::builtins::reflect::reflect_construct as *const (), 2),
("defineProperty", crate::builtins::reflect::reflect_define_property as *const (), 3),
("deleteProperty", crate::builtins::reflect::reflect_delete_property as *const (), 2),
("get", crate::builtins::reflect::reflect_get as *const (), 2),
("apply", oxide_builtins::reflect::reflect_apply::<crate::vm::Vm> as *const (), 3),
("construct", oxide_builtins::reflect::reflect_construct::<crate::vm::Vm> as *const (), 2),
(
"getOwnPropertyDescriptor",
crate::builtins::reflect::reflect_get_own_property_descriptor as *const (),
"defineProperty",
oxide_builtins::reflect::reflect_define_property::<crate::vm::Vm> as *const (),
3,
),
(
"deleteProperty",
oxide_builtins::reflect::reflect_delete_property::<crate::vm::Vm> as *const (),
2,
),
("get", oxide_builtins::reflect::reflect_get::<crate::vm::Vm> as *const (), 2),
(
"getOwnPropertyDescriptor",
oxide_builtins::reflect::reflect_get_own_property_descriptor::<crate::vm::Vm> as *const (),
2,
),
(
"getPrototypeOf",
oxide_builtins::reflect::reflect_get_prototype_of::<crate::vm::Vm> as *const (),
1,
),
("has", oxide_builtins::reflect::reflect_has::<crate::vm::Vm> as *const (), 2),
(
"isExtensible",
oxide_builtins::reflect::reflect_is_extensible::<crate::vm::Vm> as *const (),
1,
),
("ownKeys", oxide_builtins::reflect::reflect_own_keys::<crate::vm::Vm> as *const (), 1),
(
"preventExtensions",
oxide_builtins::reflect::reflect_prevent_extensions::<crate::vm::Vm> as *const (),
1,
),
("set", oxide_builtins::reflect::reflect_set::<crate::vm::Vm> as *const (), 3),
(
"setPrototypeOf",
oxide_builtins::reflect::reflect_set_prototype_of::<crate::vm::Vm> as *const (),
2,
),
("getPrototypeOf", crate::builtins::reflect::reflect_get_prototype_of as *const (), 1),
("has", crate::builtins::reflect::reflect_has as *const (), 2),
("isExtensible", crate::builtins::reflect::reflect_is_extensible as *const (), 1),
("ownKeys", crate::builtins::reflect::reflect_own_keys as *const (), 1),
("preventExtensions", crate::builtins::reflect::reflect_prevent_extensions as *const (), 1),
("set", crate::builtins::reflect::reflect_set as *const (), 3),
("setPrototypeOf", crate::builtins::reflect::reflect_set_prototype_of as *const (), 2),
],
);
bind_existing_global(core, global, "Reflect", JsValue::from_js_object(Box::into_raw(reflect)));
@@ -175,26 +208,38 @@ fn bind_iterator_global(core: &Arc<KernelCore>, session: &KernelSession, global:
let function_proto = session.builtin_world().function_proto.as_ptr() as *mut JsObject;
let mut iterator = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto)));
iterator.set_function(true);
configure_native_constructor(&mut iterator, crate::builtins::iterator::iterator_constructor as *const (), 0);
configure_native_constructor(
&mut iterator,
oxide_builtins::iterator::iterator_constructor::<crate::vm::Vm> as *const (),
0,
);
apply_binding_table(
session.builtin_world(),
&mut iterator,
core,
&[("from", crate::builtins::iterator::iterator_from as *const (), 1)],
&[("from", oxide_builtins::iterator::iterator_from::<crate::vm::Vm> as *const (), 1)],
);
bind_existing_global(core, global, "Iterator", JsValue::from_js_object(Box::into_raw(iterator)));
}
fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
for (index, (name, native_fn, arg_count)) in [
("Proxy", crate::builtins::stubs::proxy_stub as *const (), 2),
("BigInt", crate::builtins::stubs::bigint_stub as *const (), 1),
("WeakMap", crate::builtins::stubs::weakmap_stub as *const (), 0),
("WeakSet", crate::builtins::stubs::weakset_stub as *const (), 0),
("WeakRef", crate::builtins::stubs::weakref_stub as *const (), 1),
("FinalizationRegistry", crate::builtins::stubs::finalization_registry_stub as *const (), 1),
("SharedArrayBuffer", crate::builtins::stubs::shared_array_buffer_stub as *const (), 1),
("Atomics", crate::builtins::stubs::atomics_stub as *const (), 0),
("Proxy", oxide_builtins::stubs::proxy_stub::<crate::vm::Vm> as *const (), 2),
("BigInt", oxide_builtins::stubs::bigint_stub::<crate::vm::Vm> as *const (), 1),
("WeakMap", oxide_builtins::stubs::weakmap_stub::<crate::vm::Vm> as *const (), 0),
("WeakSet", oxide_builtins::stubs::weakset_stub::<crate::vm::Vm> as *const (), 0),
("WeakRef", oxide_builtins::stubs::weakref_stub::<crate::vm::Vm> as *const (), 1),
(
"FinalizationRegistry",
oxide_builtins::stubs::finalization_registry_stub::<crate::vm::Vm> as *const (),
1,
),
(
"SharedArrayBuffer",
oxide_builtins::stubs::shared_array_buffer_stub::<crate::vm::Vm> as *const (),
1,
),
("Atomics", oxide_builtins::stubs::atomics_stub::<crate::vm::Vm> as *const (), 0),
]
.into_iter()
.enumerate()
@@ -209,26 +254,66 @@ fn bind_stub_globals(core: &Arc<KernelCore>, session: &KernelSession, global: &m
pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession, global: &mut JsObject) {
let world = session.builtin_world();
configure_existing_ctor(&world.object_constructor, crate::builtins::object::object_constructor as *const (), 1);
configure_existing_ctor(&world.array_constructor, crate::builtins::array::array_constructor as *const (), 1);
configure_existing_ctor(
&world.object_constructor,
oxide_builtins::object::object_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.array_constructor,
oxide_builtins::array::array_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.array_buffer_constructor,
crate::builtins::array_buffer::array_buffer_constructor as *const (),
oxide_builtins::array_buffer::array_buffer_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.data_view_constructor,
crate::builtins::data_view::data_view_constructor as *const (),
oxide_builtins::data_view::data_view_constructor::<crate::vm::Vm> as *const (),
3,
);
configure_existing_ctor(&world.error_constructor, crate::builtins::error::error_constructor as *const (), 1);
configure_existing_ctor(&world.number_constructor, crate::builtins::number::number_constructor as *const (), 1);
configure_existing_ctor(&world.date_constructor, crate::builtins::date::date_constructor as *const (), 7);
configure_existing_ctor(&world.set_constructor, crate::builtins::set::set_constructor as *const (), 1);
configure_existing_ctor(&world.map_constructor, crate::builtins::map::map_constructor as *const (), 1);
configure_existing_ctor(&world.boolean_constructor, crate::builtins::boolean::boolean_constructor as *const (), 1);
configure_existing_ctor(&world.regexp_constructor, crate::builtins::regexp::regexp_constructor as *const (), 2);
configure_existing_ctor(&world.symbol_constructor, crate::builtins::symbol::symbol_constructor as *const (), 1);
configure_existing_ctor(
&world.error_constructor,
oxide_builtins::error::error_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.number_constructor,
oxide_builtins::number::number_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.date_constructor,
oxide_builtins::date::date_constructor::<crate::vm::Vm> as *const (),
7,
);
configure_existing_ctor(
&world.set_constructor,
oxide_builtins::set::set_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.map_constructor,
oxide_builtins::map::map_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.boolean_constructor,
oxide_builtins::boolean::boolean_constructor::<crate::vm::Vm> as *const (),
1,
);
configure_existing_ctor(
&world.regexp_constructor,
oxide_builtins::regexp::regexp_constructor::<crate::vm::Vm> as *const (),
2,
);
configure_existing_ctor(
&world.symbol_constructor,
oxide_builtins::symbol::symbol_constructor::<crate::vm::Vm> as *const (),
1,
);
for (name, value) in [
("Object", JsValue::from_js_object(world.object_constructor.as_ptr() as *mut JsObject)),
@@ -258,57 +343,57 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
(
"Int8Array",
&world.int8array_constructor,
crate::builtins::typed_array::int8array_constructor as *const (),
oxide_builtins::typed_array::int8array_constructor::<crate::vm::Vm> as *const (),
),
(
"Uint8Array",
&world.uint8array_constructor,
crate::builtins::typed_array::uint8array_constructor as *const (),
oxide_builtins::typed_array::uint8array_constructor::<crate::vm::Vm> as *const (),
),
(
"Uint8ClampedArray",
&world.uint8clampedarray_constructor,
crate::builtins::typed_array::uint8clampedarray_constructor as *const (),
oxide_builtins::typed_array::uint8clampedarray_constructor::<crate::vm::Vm> as *const (),
),
(
"Int16Array",
&world.int16array_constructor,
crate::builtins::typed_array::int16array_constructor as *const (),
oxide_builtins::typed_array::int16array_constructor::<crate::vm::Vm> as *const (),
),
(
"Uint16Array",
&world.uint16array_constructor,
crate::builtins::typed_array::uint16array_constructor as *const (),
oxide_builtins::typed_array::uint16array_constructor::<crate::vm::Vm> as *const (),
),
(
"Int32Array",
&world.int32array_constructor,
crate::builtins::typed_array::int32array_constructor as *const (),
oxide_builtins::typed_array::int32array_constructor::<crate::vm::Vm> as *const (),
),
(
"Uint32Array",
&world.uint32array_constructor,
crate::builtins::typed_array::uint32array_constructor as *const (),
oxide_builtins::typed_array::uint32array_constructor::<crate::vm::Vm> as *const (),
),
(
"Float32Array",
&world.float32array_constructor,
crate::builtins::typed_array::float32array_constructor as *const (),
oxide_builtins::typed_array::float32array_constructor::<crate::vm::Vm> as *const (),
),
(
"Float64Array",
&world.float64array_constructor,
crate::builtins::typed_array::float64array_constructor as *const (),
oxide_builtins::typed_array::float64array_constructor::<crate::vm::Vm> as *const (),
),
(
"BigInt64Array",
&world.bigint64array_constructor,
crate::builtins::typed_array::bigint64array_constructor as *const (),
oxide_builtins::typed_array::bigint64array_constructor::<crate::vm::Vm> as *const (),
),
(
"BigUint64Array",
&world.biguint64array_constructor,
crate::builtins::typed_array::biguint64array_constructor as *const (),
oxide_builtins::typed_array::biguint64array_constructor::<crate::vm::Vm> as *const (),
),
] {
configure_existing_ctor(ctor, native_fn, 1);
@@ -321,7 +406,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"TypeError",
&world.type_error_proto,
crate::builtins::error::type_error_constructor as *const (),
oxide_builtins::error::type_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_global(
core,
@@ -329,7 +414,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"ReferenceError",
&world.reference_error_proto,
crate::builtins::error::reference_error_constructor as *const (),
oxide_builtins::error::reference_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_global(
core,
@@ -337,7 +422,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"RangeError",
&world.range_error_proto,
crate::builtins::error::range_error_constructor as *const (),
oxide_builtins::error::range_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_global(
core,
@@ -345,7 +430,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"SyntaxError",
&world.syntax_error_proto,
crate::builtins::error::syntax_error_constructor as *const (),
oxide_builtins::error::syntax_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_global(
core,
@@ -353,7 +438,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"URIError",
&world.uri_error_proto,
crate::builtins::error::uri_error_constructor as *const (),
oxide_builtins::error::uri_error_constructor::<crate::vm::Vm> as *const (),
);
bind_error_subtype_global(
core,
@@ -361,7 +446,7 @@ pub fn bind_global_builtin_slots(core: &Arc<KernelCore>, session: &KernelSession
global,
"EvalError",
&world.eval_error_proto,
crate::builtins::error::eval_error_constructor as *const (),
oxide_builtins::error::eval_error_constructor::<crate::vm::Vm> as *const (),
);
bind_reflect_global(core, session, global);
-952
View File
@@ -1,952 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, MAX_DENSE_PROPS};
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
macro_rules! array_ptr {
($vm:expr, $args:expr) => {{
match get_this_array_ref($vm, $vm.reg($args[0])) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
}
}};
}
macro_rules! array_ptr_len {
($vm:expr, $args:expr) => {{
let arr_ptr = array_ptr!($vm, $args);
let len = unsafe { (*arr_ptr).prop_count() } as usize;
(arr_ptr, len)
}};
}
fn array_type_error(vm: &mut Vm, msg: &str) -> JsValue {
crate::builtins::error::create_type_error(vm, msg)
}
fn get_this_array_ref(vm: &mut Vm, val: JsValue) -> Result<*mut JsObject, JsValue> {
if !val.is_object() {
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
let obj = unsafe { &*ptr };
if !obj.is_array() {
return Err(array_type_error(vm, "Array method called on incompatible receiver"));
}
Ok(ptr)
}
fn require_callback(vm: &mut Vm, callback_val: JsValue) -> Result<JsValue, JsValue> {
if !callback_val.is_object() {
return Err(array_type_error(vm, "callback is not a function"));
}
let ptr = callback_val.as_js_object_ptr();
if ptr.is_null() || !unsafe { &*ptr }.is_function() {
return Err(array_type_error(vm, "callback is not a function"));
}
Ok(callback_val)
}
fn create_new_array(vm: &mut Vm, n: usize) -> *mut JsObject {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(proto),
n.min(MAX_DENSE_PROPS),
vm.epoch().bump(),
))
}
fn array_length_arg(vm: &mut Vm, value: JsValue) -> Result<usize, JsValue> {
let n = coercion::to_number(value);
if !n.is_finite() || n < 0.0 || n.fract() != 0.0 || n > MAX_DENSE_PROPS as f64 {
return Err(crate::builtins::error::create_range_error(vm, "Invalid array length"));
}
Ok(n as usize)
}
fn invoke_native_callback(vm: &mut Vm, callback_val: JsValue, this_val: JsValue, cb_args: &[JsValue]) -> NativeResult {
if !callback_val.is_object() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function"));
}
let cb_ptr = callback_val.as_js_object_ptr();
if cb_ptr.is_null() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function"));
}
let cb = unsafe { &*cb_ptr };
if !cb.is_function() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "callback is not a function"));
}
match vm.call_function_sync(callback_val, this_val, cb_args) {
Ok(value) => NativeResult::Ok(value),
Err(err) => NativeResult::Err(callback_error_from_text(vm, &err)),
}
}
fn callback_error_from_text(vm: &mut Vm, err: &str) -> JsValue {
let err = err.strip_prefix("uncaught ").unwrap_or(err);
if let Some(msg) = err.strip_prefix("TypeError: ") {
return crate::builtins::error::create_type_error(vm, msg);
}
if let Some(msg) = err.strip_prefix("ReferenceError: ") {
return crate::builtins::error::create_reference_error(vm, msg);
}
if let Some(msg) = err.strip_prefix("RangeError: ") {
return crate::builtins::error::create_range_error(vm, msg);
}
if let Some(msg) = err.strip_prefix("SyntaxError: ") {
return crate::builtins::error::create_syntax_error(vm, msg);
}
if let Some(msg) = err.strip_prefix("Error: ") {
return crate::builtins::error::create_error(vm, msg);
}
crate::builtins::error::create_error(vm, err)
}
fn unexpected_tail_call_error(vm: &mut Vm) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_type_error(vm, "unexpected tail call in array callback"))
}
pub fn array_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let proto_val = JsValue::from_js_object(proto);
if args.len() == 2 {
let val = vm.reg(args[1]);
if val.is_int() || val.is_double() {
let n = match array_length_arg(vm, val) {
Ok(n) => n,
Err(err) => return NativeResult::Err(err),
};
let arr = vm.alloc_object(JsObject::new_array(EMPTY_SHAPE_ID, proto_val, n, vm.epoch().bump()));
return NativeResult::Ok(JsValue::from_js_object(arr));
}
}
let n_elems = if args.len() > 1 { args.len() - 1 } else { 0 };
let arr = vm.alloc_object(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]));
}
}
unsafe {
(*arr).set_prop_count(n_elems);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
pub fn array_is_array(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if !val.is_object() {
return NativeResult::Ok(JsValue::bool(false));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(unsafe { &*ptr }.is_array()))
}
pub fn array_push(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let mut len = unsafe { &*arr_ptr }.prop_count();
for &arg_reg in args.iter().skip(1) {
let val = vm.promote_if_needed_for_write_ptr(arr_ptr, vm.reg(arg_reg));
unsafe { &mut *arr_ptr }.set_prop_at(len, val);
len += 1;
}
unsafe { &mut *arr_ptr }.set_prop_count(len);
NativeResult::Ok(JsValue::int(len as i32))
}
pub fn array_pop(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count();
if len == 0 {
return NativeResult::Ok(JsValue::undefined());
}
let last = arr.get_prop_at(len - 1);
arr.set_prop_count(len - 1);
NativeResult::Ok(last)
}
pub fn array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let start = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1])) as i32) as usize
} else {
0
};
let end = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as i32) as usize
} else {
n
};
let start = start.min(n);
let end = end.min(n);
let count = end.saturating_sub(start);
let new_arr = create_new_array(vm, count);
unsafe {
for i in 0..count {
(*new_arr).set_prop_at(i, arr.get_prop_at(start + i));
}
(*new_arr).set_prop_count(count);
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_splice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr };
let n = arr.prop_count() as usize;
let start = if args.len() > 1 {
let v = coercion::to_number(vm.reg(args[1]));
let s = v as i32;
if s < 0 {
(n as i32 + s).max(0) as usize
} else {
(s as usize).min(n)
}
} else {
0
};
let delete_count = if args.len() > 2 {
let v = coercion::to_number(vm.reg(args[2]));
(v as usize).min(n - start)
} else {
n - start
};
let insert_count = if args.len() > 3 { args.len() - 3 } else { 0 };
let mut removed: Vec<JsValue> = Vec::new();
for i in 0..delete_count {
removed.push(arr.get_prop_at(start + i));
}
if insert_count > delete_count {
let shift = insert_count - delete_count;
for i in (start + delete_count..n).rev() {
let val = arr.get_prop_at(i);
arr.set_prop_at(i + shift, val);
}
} else if insert_count < delete_count {
let shift = delete_count - insert_count;
for i in start + delete_count..n {
let val = arr.get_prop_at(i);
arr.set_prop_at(i - shift, val);
}
}
for i in 0..insert_count {
arr.set_prop_at(start + i, vm.reg(args[3 + i]));
}
let new_len = n + insert_count - delete_count;
arr.set_prop_count(new_len);
let removed_arr = create_new_array(vm, removed.len());
unsafe {
for (i, val) in removed.iter().enumerate() {
(*removed_arr).set_prop_at(i, *val);
}
(*removed_arr).set_prop_count(removed.len());
}
NativeResult::Ok(JsValue::from_js_object(removed_arr))
}
pub fn array_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let mut all: Vec<JsValue> = Vec::new();
for i in 0..n {
all.push(arr.get_prop_at(i));
}
for &arg_reg in args.iter().skip(1) {
let val = vm.reg(arg_reg);
if val.is_object() {
let o_ptr = val.as_js_object_ptr();
if !o_ptr.is_null() {
let o = unsafe { &*o_ptr };
if o.is_array() {
let on = o.prop_count() as usize;
for i in 0..on {
all.push(o.get_prop_at(i));
}
continue;
}
}
}
all.push(val);
}
let new_arr = create_new_array(vm, all.len());
unsafe {
for (i, val) in all.iter().enumerate() {
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(all.len());
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_join(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let sep = if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
",".to_string()
};
let parts: Vec<String> = (0..n)
.map(|i| {
let v = arr.get_prop_at(i);
if v.is_undefined() || v.is_null() {
String::new()
} else {
coercion::to_string(v)
}
})
.collect();
let joined = parts.join(&sep);
let result_val = vm.new_string(&joined);
NativeResult::Ok(result_val)
}
pub fn array_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
if args.len() < 2 {
return NativeResult::Ok(JsValue::int(-1));
}
let target = vm.reg(args[1]);
for i in 0..n {
if coercion::strict_equality(arr.get_prop_at(i), target) {
return NativeResult::Ok(JsValue::int(i as i32));
}
}
NativeResult::Ok(JsValue::int(-1))
}
pub fn array_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let target = vm.reg(args[1]);
for i in 0..n {
let elem = arr.get_prop_at(i);
if elem.is_double() && target.is_double() {
let ea = elem.as_double();
let ta = target.as_double();
if ea.is_nan() && ta.is_nan() {
return NativeResult::Ok(JsValue::bool(true));
}
}
if coercion::strict_equality(elem, target) {
return NativeResult::Ok(JsValue::bool(true));
}
}
NativeResult::Ok(JsValue::bool(false))
}
pub fn array_reverse(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr };
let n = arr.prop_count() as usize;
let mut i = 0;
let mut j = n.saturating_sub(1);
while i < j {
let tmp = arr.get_prop_at(i);
arr.set_prop_at(i, arr.get_prop_at(j));
arr.set_prop_at(j, tmp);
i += 1;
j = j.saturating_sub(1);
}
NativeResult::Ok(vm.reg(args[0]))
}
pub fn array_flat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &*arr_ptr };
let n = arr.prop_count() as usize;
let depth = if args.len() > 1 {
let n = coercion::to_number(vm.reg(args[1]));
if !n.is_finite() {
vm.kernel_core.config.max_call_depth
} else {
(n as i32).max(1) as usize
}
} else {
1
}
.min(vm.kernel_core.config.max_call_depth);
fn flatten(items: &[JsValue], remaining_depth: usize, seen: &mut Vec<*mut JsObject>) -> Vec<JsValue> {
let mut out = Vec::new();
for &v in items {
if remaining_depth > 0 && v.is_object() {
let ptr = v.as_js_object_ptr();
if !ptr.is_null() {
if seen.iter().any(|seen_ptr| std::ptr::eq(*seen_ptr, ptr)) {
out.push(v);
continue;
}
let o = unsafe { &*ptr };
if o.is_array() {
seen.push(ptr);
let on = o.prop_count() as usize;
let sub: Vec<JsValue> = (0..on).map(|i| o.get_prop_at(i)).collect();
let flat = flatten(&sub, remaining_depth - 1, seen);
seen.pop();
out.extend(flat);
continue;
}
}
}
out.push(v);
}
out
}
let all: Vec<JsValue> = (0..n).map(|i| arr.get_prop_at(i)).collect();
let mut seen = vec![arr_ptr];
let flat = flatten(&all, depth, &mut seen);
let new_arr = create_new_array(vm, flat.len());
unsafe {
for (i, val) in flat.iter().enumerate() {
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(flat.len());
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_for_each(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(_) => {}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::undefined())
}
pub fn array_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(mapped) => unsafe {
(*new_arr).set_prop_at(i, mapped);
},
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
unsafe {
(*new_arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_filter(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(result_val) => {
if coercion::to_boolean(result_val) {
kept.push(elem);
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
let new_arr = create_new_array(vm, kept.len());
unsafe {
for (i, val) in kept.iter().enumerate() {
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(kept.len());
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_reduce(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if n == 0 && args.len() < 3 {
return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value"));
}
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
let has_initial = args.len() > 2;
let mut accumulator;
let start_idx;
if has_initial {
accumulator = vm.reg(args[2]);
start_idx = 0;
} else {
accumulator = unsafe { (*arr_ptr).get_prop_at(0) };
start_idx = 1;
}
let this_val = JsValue::undefined();
for i in start_idx..n {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
this_val,
&[accumulator, elem, JsValue::int(i as i32), vm.reg(args[0])],
) {
NativeResult::Ok(result) => accumulator = result,
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(accumulator)
}
pub fn array_find(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(result_val) => {
if coercion::to_boolean(result_val) {
return NativeResult::Ok(elem);
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::undefined())
}
pub fn array_some(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(result_val) => {
if coercion::to_boolean(result_val) {
return NativeResult::Ok(JsValue::bool(true));
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::bool(false))
}
pub fn array_every(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(result_val) => {
if !coercion::to_boolean(result_val) {
return NativeResult::Ok(JsValue::bool(false));
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::bool(true))
}
pub fn array_flat_map(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(result) => {
if result.is_object() {
let r_ptr = result.as_js_object_ptr();
if !r_ptr.is_null() {
let r = unsafe { &*r_ptr };
if r.is_array() {
let rn = r.prop_count() as usize;
for j in 0..rn {
flat.push(r.get_prop_at(j));
}
continue;
}
}
}
flat.push(result);
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
let new_arr = create_new_array(vm, flat.len());
unsafe {
for (i, val) in flat.iter().enumerate() {
(*new_arr).set_prop_at(i, *val);
}
(*new_arr).set_prop_count(flat.len());
}
NativeResult::Ok(JsValue::from_js_object(new_arr))
}
pub fn array_shift(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count();
if len == 0 {
return NativeResult::Ok(JsValue::undefined());
}
let first = arr.get_prop_at(0);
for i in 1..len {
let v = arr.get_prop_at(i);
arr.set_prop_at(i - 1, v);
}
arr.set_prop_count(len - 1);
NativeResult::Ok(first)
}
pub fn array_unshift(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let arr = unsafe { &mut *arr_ptr };
let len = arr.prop_count();
let n_items = args.len().saturating_sub(1);
for i in (0..len as usize).rev() {
let v = arr.get_prop_at(i);
arr.set_prop_at(i + n_items, v);
}
for (j, &arg_reg) in args.iter().skip(1).enumerate() {
arr.set_prop_at(j, vm.reg(arg_reg));
}
let new_len = len as usize + n_items;
arr.set_prop_count(new_len);
NativeResult::Ok(JsValue::int(new_len as i32))
}
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 start = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as i32).max(0) as usize
} else {
0
};
let end = if args.len() > 3 {
let e = coercion::to_number(vm.reg(args[3])) as i32;
(e as usize).min(len)
} else {
len
};
for i in start..end {
arr.set_prop_at(i, value);
}
NativeResult::Ok(vm.reg(args[0]))
}
pub fn array_copy_within(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;
if len == 0 {
return NativeResult::Ok(vm.reg(args[0]));
}
let target = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1])) as i32) as usize
} else {
0
};
let start = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as i32) as usize
} else {
0
};
let end = if args.len() > 3 {
(coercion::to_number(vm.reg(args[3])) as i32 as usize).min(len)
} else {
len
};
let mut to = target;
for from in start..end {
if to >= len {
break;
}
let v = arr.get_prop_at(from);
arr.set_prop_at(to, v);
to += 1;
}
NativeResult::Ok(vm.reg(args[0]))
}
pub fn array_at(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 mut index = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as i32 } else { 0 };
if index < 0 {
index += len;
}
if index < 0 || index >= len {
return NativeResult::Ok(JsValue::undefined());
}
NativeResult::Ok(arr.get_prop_at(index))
}
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() };
for i in (0..len).rev() {
if coercion::strict_equality(arr.get_prop_at(i), search) {
return NativeResult::Ok(JsValue::int(i));
}
}
NativeResult::Ok(JsValue::int(-1))
}
pub fn array_find_index(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(r) => {
if coercion::to_boolean(r) {
return NativeResult::Ok(JsValue::int(i as i32));
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::int(-1))
}
pub fn array_find_last(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = {
let (arr_ptr, len) = array_ptr_len!(vm, args);
(arr_ptr, len as i32)
};
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
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])]) {
NativeResult::Ok(r) => {
if coercion::to_boolean(r) {
return NativeResult::Ok(elem);
}
}
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(JsValue::undefined())
}
pub fn array_reduce_right(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (arr_ptr, n) = array_ptr_len!(vm, args);
if args.len() < 2 {
return NativeResult::Err(array_type_error(vm, "callback is not a function"));
}
let callback_val = match require_callback(vm, vm.reg(args[1])) {
Ok(callback) => callback,
Err(err) => return NativeResult::Err(err),
};
let (mut acc, start_idx): (JsValue, i32) = if args.len() > 2 {
(vm.reg(args[2]), n as i32 - 1)
} else {
if n == 0 {
return NativeResult::Err(array_type_error(vm, "Reduce of empty array with no initial value"));
}
(unsafe { (*arr_ptr).get_prop_at(n - 1) }, n as i32 - 2)
};
for i in (0..=start_idx).rev() {
let elem = unsafe { (*arr_ptr).get_prop_at(i) };
match invoke_native_callback(
vm,
callback_val,
JsValue::undefined(),
&[acc, elem, JsValue::int(i), vm.reg(args[0])],
) {
NativeResult::Ok(r) => acc = r,
NativeResult::Err(err) => return NativeResult::Err(err),
NativeResult::TailCall { .. } => return unexpected_tail_call_error(vm),
}
}
NativeResult::Ok(acc)
}
pub fn array_sort(vm: &mut Vm, args: &[u8]) -> NativeResult {
let arr_ptr = array_ptr!(vm, args);
let len = unsafe { (*arr_ptr).prop_count() as usize };
let mut vals: Vec<JsValue> = (0..len).map(|i| unsafe { (*arr_ptr).get_prop_at(i) }).collect();
let comparator = if args.len() > 1 {
let candidate = vm.reg(args[1]);
if candidate.is_undefined() {
None
} else {
match require_callback(vm, candidate) {
Ok(callback) => Some(callback),
Err(err) => return NativeResult::Err(err),
}
}
} else {
None
};
let mut sort_error = None;
vals.sort_by(|a, b| {
if sort_error.is_some() {
return std::cmp::Ordering::Equal;
}
if let Some(callback) = comparator {
match invoke_native_callback(vm, callback, JsValue::undefined(), &[*a, *b]) {
NativeResult::Ok(result) => {
let n = coercion::to_number(result);
if n.is_nan() || n == 0.0 {
std::cmp::Ordering::Equal
} else if n < 0.0 {
std::cmp::Ordering::Less
} else {
std::cmp::Ordering::Greater
}
}
NativeResult::Err(err) => {
sort_error = Some(err);
std::cmp::Ordering::Equal
}
NativeResult::TailCall { .. } => {
sort_error =
Some(crate::builtins::error::create_type_error(vm, "unexpected tail call in array callback"));
std::cmp::Ordering::Equal
}
}
} else {
let sa = coercion::to_string(*a);
let sb = coercion::to_string(*b);
sa.cmp(&sb)
}
});
if let Some(err) = sort_error {
return NativeResult::Err(err);
}
let arr = unsafe { &mut *arr_ptr };
for (i, &v) in vals.iter().enumerate() {
arr.set_prop_at(i, v);
}
NativeResult::Ok(vm.reg(args[0]))
}
pub fn array_values(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
match crate::builtins::iterator::make_iterator_for_value(vm, this_val) {
Ok(iterator) => NativeResult::Ok(iterator),
Err(err) => NativeResult::Err(err),
}
}
@@ -1,181 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub(crate) const MAX_ARRAY_BUFFER_LENGTH: usize = 1 << 30;
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
fn type_error(vm: &mut Vm, msg: &str) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_type_error(vm, msg))
}
fn to_index(vm: &mut Vm, value: JsValue) -> Result<usize, JsValue> {
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
if !n.is_finite() || n < 0.0 || n > MAX_ARRAY_BUFFER_LENGTH as f64 {
return Err(crate::builtins::error::create_range_error(vm, "invalid ArrayBuffer length"));
}
Ok(n.trunc() as usize)
}
fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value);
if n.is_nan() {
return 0;
}
let int = n.trunc() as isize;
if int < 0 {
len.saturating_sub((-int) as usize)
} else {
(int as usize).min(len)
}
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
pub(crate) fn new_array_buffer(vm: &mut Vm, data: Vec<u8>) -> *mut JsObject {
let proto = vm.session().builtin_world().array_buffer_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
obj.type_tag = JsObject::OBJ_TYPE_ARRAY_BUFFER;
let len = data.len();
let data_ptr = Box::into_raw(Box::new(data));
// SAFETY: ArrayBuffer objects are not callable, so their native_fn slot is reused
// as an opaque Box<Vec<u8>> pointer, matching the existing RegExp storage pattern.
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data_ptr as *const ()) }));
set_named_prop(
vm,
&mut obj,
"byteLength",
JsValue::int(len as i32),
PropAttributes::new(false, false, false),
);
vm.alloc_object(obj)
}
fn array_buffer_vec_ptr(obj: &JsObject) -> Option<*mut Vec<u8>> {
if !obj.is_array_buffer_obj() {
return None;
}
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut Vec<u8>)
}
pub(crate) fn clone_array_buffer_native(old_obj: &JsObject, new_obj: &mut JsObject) {
let Some(ptr) = array_buffer_vec_ptr(old_obj) else {
return;
};
if ptr.is_null() {
return;
}
let cloned = unsafe { (&*ptr).clone() };
let cloned_ptr = Box::into_raw(Box::new(cloned));
new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned_ptr as *const ()) }));
}
pub(crate) fn drop_array_buffer_native(obj: &mut JsObject) -> u64 {
let Some(data_ptr) = array_buffer_vec_ptr(obj) else {
return 0;
};
if data_ptr.is_null() {
return 0;
}
// SAFETY: ArrayBuffer stores `Box<Vec<u8>>` in native_fn.
let data = unsafe { Box::from_raw(data_ptr) };
let bytes = std::mem::size_of::<Vec<u8>>() + data.capacity();
obj.set_native_fn(None);
bytes as u64
}
pub(crate) fn array_buffer_data_ptr(vm: &mut Vm, this_val: JsValue) -> Result<*mut Vec<u8>, JsValue> {
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"ArrayBuffer method called on incompatible receiver",
));
}
let obj_ptr = this_val.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
}
let obj = unsafe { &*obj_ptr };
if !obj.is_array_buffer_obj() {
return Err(crate::builtins::error::create_type_error(
vm,
"ArrayBuffer method called on incompatible receiver",
));
}
let Some(data_ptr) = obj.native_fn() else {
return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
};
let data_ptr = data_ptr.as_ptr() as *mut Vec<u8>;
if data_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "ArrayBuffer internal state invalid"));
}
Ok(data_ptr)
}
pub fn array_buffer_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let length = if args.len() > 1 {
match to_index(vm, vm.reg(args[1])) {
Ok(length) => length,
Err(err) => return NativeResult::Err(err),
}
} else {
0
};
NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, vec![0; length])))
}
pub fn array_buffer_byte_length(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val));
NativeResult::Ok(JsValue::int(unsafe { (*data_ptr).len() } as i32))
}
pub fn array_buffer_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let data_ptr = native_try!(array_buffer_data_ptr(vm, this_val));
let data = unsafe { &*data_ptr };
let len = data.len();
let start = if args.len() > 1 { normalize_index(vm, vm.reg(args[1]), len) } else { 0 };
let end = if args.len() > 2 { normalize_index(vm, vm.reg(args[2]), len) } else { len };
let end = end.max(start);
NativeResult::Ok(JsValue::from_js_object(new_array_buffer(vm, data[start..end].to_vec())))
}
pub fn array_buffer_is_view(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if !value.is_object() {
return NativeResult::Ok(JsValue::bool(false));
}
let ptr = value.as_js_object_ptr();
if ptr.is_null() {
return NativeResult::Ok(JsValue::bool(false));
}
let obj = unsafe { &*ptr };
NativeResult::Ok(JsValue::bool(obj.is_data_view_obj() || obj.is_typed_array_obj()))
}
pub fn array_buffer_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if array_buffer_data_ptr(vm, this_val).is_err() {
return type_error(vm, "ArrayBuffer.prototype.toString called on incompatible receiver");
}
NativeResult::Ok(vm.new_string("[object ArrayBuffer]"))
}
-109
View File
@@ -1,109 +0,0 @@
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub fn boolean_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let bool_val = if args.len() > 1 {
let arg = vm.reg(args[1]);
if arg.is_undefined() || arg.is_null() {
false
} else if arg.is_bool() {
arg.as_bool()
} else if arg.is_string() {
// SAFETY: arg is a string value.
!unsafe { (*arg.as_string_ptr()).is_empty() }
} else if arg.is_object() {
true
} else if arg.is_int() {
arg.as_int() != 0
} else if arg.is_double() {
arg.as_double() != 0.0
} else {
false
}
} else {
false
};
let boolean_proto = vm.session().builtin_world().boolean_proto.as_ptr() as *mut JsObject;
let is_ctor = if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, boolean_proto)
}
} else {
false
};
if !is_ctor {
return NativeResult::Ok(JsValue::bool(bool_val));
}
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.type_tag = JsObject::OBJ_TYPE_BOOLEAN_OBJ;
obj.push_prop(JsValue::bool(bool_val));
NativeResult::Ok(this_val)
}
pub fn boolean_prototype_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_bool() {
return NativeResult::Ok(this_val);
}
if !this_val.is_object() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
if !obj.is_boolean_obj() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.valueOf called on non-Boolean object",
));
}
let val = obj.get_prop_at(0);
NativeResult::Ok(val)
}
pub fn boolean_prototype_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_bool() {
return if this_val.as_bool() {
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.new_string("false"))
};
}
if !this_val.is_object() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let obj = unsafe { &*this_val.as_js_object_ptr() };
if !obj.is_boolean_obj() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Boolean.prototype.toString called on non-Boolean object",
));
}
let val = obj.get_prop_at(0);
let is_true = if val.is_bool() {
val.as_bool()
} else {
!val.is_null() && !val.is_undefined()
};
if is_true {
NativeResult::Ok(vm.new_string("true"))
} else {
NativeResult::Ok(vm.new_string("false"))
}
}
-438
View File
@@ -1,438 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
use crate::builtins::array_buffer::array_buffer_data_ptr;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
#[derive(Clone, Copy)]
pub(crate) struct DataViewData {
buffer: JsValue,
byte_offset: usize,
byte_length: usize,
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
if !n.is_finite() || n < 0.0 {
return Err(crate::builtins::error::create_range_error(vm, msg));
}
Ok(n.trunc() as usize)
}
fn is_little_endian(vm: &mut Vm, args: &[u8], idx: usize) -> bool {
args.get(idx).map(|reg| coercion::to_boolean(vm.reg(*reg))).unwrap_or(false)
}
fn numeric_arg(vm: &mut Vm, args: &[u8], idx: usize) -> f64 {
args.get(idx).map(|reg| coercion::to_number(vm.reg(*reg))).unwrap_or(0.0)
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
fn get_data_view_data(vm: &mut Vm, this_val: JsValue) -> Result<DataViewData, JsValue> {
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"DataView method called on incompatible receiver",
));
}
let obj_ptr = this_val.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid"));
}
let obj = unsafe { &*obj_ptr };
if !obj.is_data_view_obj() {
return Err(crate::builtins::error::create_type_error(
vm,
"DataView method called on incompatible receiver",
));
}
let Some(data_ptr) = obj.native_fn() else {
return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid"));
};
let data_ptr = data_ptr.as_ptr() as *const DataViewData;
if data_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "DataView internal state invalid"));
}
Ok(unsafe { *data_ptr })
}
fn checked_absolute_offset(vm: &mut Vm, view: DataViewData, offset: usize, width: usize) -> Result<usize, JsValue> {
let Some(end) = offset.checked_add(width) else {
return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds"));
};
if end > view.byte_length {
return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds"));
}
Ok(view.byte_offset + offset)
}
fn read_bytes<const N: usize>(vm: &mut Vm, args: &[u8]) -> Result<[u8; N], JsValue> {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = get_data_view_data(vm, this_val)?;
let offset = if args.len() > 1 {
to_index(vm, vm.reg(args[1]), "DataView offset out of bounds")?
} else {
0
};
let abs = checked_absolute_offset(vm, view, offset, N)?;
let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?;
let buffer = unsafe { &*buffer_ptr };
if abs + N > buffer.len() {
return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds"));
}
let mut out = [0u8; N];
out.copy_from_slice(&buffer[abs..abs + N]);
Ok(out)
}
fn write_bytes<const N: usize>(vm: &mut Vm, args: &[u8], bytes: [u8; N]) -> Result<(), JsValue> {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = get_data_view_data(vm, this_val)?;
let offset = if args.len() > 1 {
to_index(vm, vm.reg(args[1]), "DataView offset out of bounds")?
} else {
0
};
let abs = checked_absolute_offset(vm, view, offset, N)?;
let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?;
let buffer = unsafe { &mut *buffer_ptr };
if abs + N > buffer.len() {
return Err(crate::builtins::error::create_range_error(vm, "DataView offset out of bounds"));
}
buffer[abs..abs + N].copy_from_slice(&bytes);
Ok(())
}
pub fn data_view_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "DataView requires an ArrayBuffer"));
}
let buffer = vm.reg(args[1]);
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, buffer));
let buffer_len = unsafe { (*buffer_ptr).len() };
let byte_offset = if args.len() > 2 {
native_try!(to_index(vm, vm.reg(args[2]), "DataView byteOffset out of bounds"))
} else {
0
};
if byte_offset > buffer_len {
return NativeResult::Err(crate::builtins::error::create_range_error(vm, "DataView byteOffset out of bounds"));
}
let default_len = buffer_len - byte_offset;
let byte_length = if args.len() > 3 {
native_try!(to_index(vm, vm.reg(args[3]), "DataView byteLength out of bounds"))
} else {
default_len
};
if byte_offset + byte_length > buffer_len {
return NativeResult::Err(crate::builtins::error::create_range_error(vm, "DataView byteLength out of bounds"));
}
let proto = vm.session().builtin_world().data_view_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
obj.type_tag = JsObject::OBJ_TYPE_DATA_VIEW;
let data = Box::into_raw(Box::new(DataViewData {
buffer,
byte_offset,
byte_length,
}));
// SAFETY: DataView objects are not callable, so native_fn is an opaque Box<DataViewData>
// pointer, matching ArrayBuffer and RegExp typed-storage patterns.
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data as *const ()) }));
set_named_prop(vm, &mut obj, "buffer", buffer, PropAttributes::new(false, false, false));
set_named_prop(
vm,
&mut obj,
"byteOffset",
JsValue::int(byte_offset as i32),
PropAttributes::new(false, false, false),
);
set_named_prop(
vm,
&mut obj,
"byteLength",
JsValue::int(byte_length as i32),
PropAttributes::new(false, false, false),
);
NativeResult::Ok(JsValue::from_js_object(vm.alloc_object(obj)))
}
fn data_view_data_ptr(obj: &JsObject) -> Option<*mut DataViewData> {
if !obj.is_data_view_obj() {
return None;
}
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut DataViewData)
}
pub(crate) fn data_view_native_edges(obj: &JsObject) -> Vec<JsValue> {
let Some(ptr) = data_view_data_ptr(obj) else {
return Vec::new();
};
if ptr.is_null() {
return Vec::new();
}
let data = unsafe { *ptr };
if data.buffer.is_object() {
vec![data.buffer]
} else {
Vec::new()
}
}
pub(crate) fn clone_data_view_native_with_rewrite<F>(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
let Some(ptr) = data_view_data_ptr(old_obj) else {
return;
};
if ptr.is_null() {
return;
}
let mut data = unsafe { *ptr };
data.buffer = rewrite(data.buffer);
let cloned = Box::into_raw(Box::new(data));
new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) }));
}
pub(crate) fn rewrite_data_view_native<F>(obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
let Some(ptr) = data_view_data_ptr(obj) else {
return;
};
if ptr.is_null() {
return;
}
unsafe {
(*ptr).buffer = rewrite((*ptr).buffer);
}
}
pub(crate) fn drop_data_view_native(obj: &mut JsObject) -> u64 {
let Some(ptr) = data_view_data_ptr(obj) else {
return 0;
};
if ptr.is_null() {
return 0;
}
unsafe { drop(Box::from_raw(ptr)) };
obj.set_native_fn(None);
std::mem::size_of::<DataViewData>() as u64
}
pub fn data_view_get_int8(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<1>(vm, args));
NativeResult::Ok(JsValue::int(i8::from_ne_bytes(bytes) as i32))
}
pub fn data_view_get_uint8(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<1>(vm, args));
NativeResult::Ok(JsValue::int(bytes[0] as i32))
}
pub fn data_view_get_int16(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<2>(vm, args));
let n = if is_little_endian(vm, args, 2) {
i16::from_le_bytes(bytes)
} else {
i16::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::int(n as i32))
}
pub fn data_view_get_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<2>(vm, args));
let n = if is_little_endian(vm, args, 2) {
u16::from_le_bytes(bytes)
} else {
u16::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::int(n as i32))
}
pub fn data_view_get_int32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<4>(vm, args));
let n = if is_little_endian(vm, args, 2) {
i32::from_le_bytes(bytes)
} else {
i32::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::int(n))
}
pub fn data_view_get_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<4>(vm, args));
let n = if is_little_endian(vm, args, 2) {
u32::from_le_bytes(bytes)
} else {
u32::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::float(n as f64))
}
pub fn data_view_get_float32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<4>(vm, args));
let n = if is_little_endian(vm, args, 2) {
f32::from_le_bytes(bytes)
} else {
f32::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::float(n as f64))
}
pub fn data_view_get_float64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<8>(vm, args));
let n = if is_little_endian(vm, args, 2) {
f64::from_le_bytes(bytes)
} else {
f64::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::float(n))
}
pub fn data_view_get_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<8>(vm, args));
let n = if is_little_endian(vm, args, 2) {
i64::from_le_bytes(bytes)
} else {
i64::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::float(n as f64))
}
pub fn data_view_get_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let bytes = native_try!(read_bytes::<8>(vm, args));
let n = if is_little_endian(vm, args, 2) {
u64::from_le_bytes(bytes)
} else {
u64::from_be_bytes(bytes)
};
NativeResult::Ok(JsValue::float(n as f64))
}
pub fn data_view_set_int8(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i32 as u8 as i8;
native_try!(write_bytes(vm, args, value.to_ne_bytes()));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_uint8(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i32 as u8;
native_try!(write_bytes(vm, args, [value]));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_int16(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i32 as u16 as i16;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_uint16(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i32 as u16;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_int32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i32;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_uint32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as u32;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_float32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as f32;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_float64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2);
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_big_int64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as i64;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_set_big_uint64(vm: &mut Vm, args: &[u8]) -> NativeResult {
let value = numeric_arg(vm, args, 2) as u64;
let bytes = if is_little_endian(vm, args, 3) {
value.to_le_bytes()
} else {
value.to_be_bytes()
};
native_try!(write_bytes(vm, args, bytes));
NativeResult::Ok(JsValue::undefined())
}
pub fn data_view_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_data_view_data(vm, this_val));
NativeResult::Ok(vm.new_string("[object DataView]"))
}
-500
View File
@@ -1,500 +0,0 @@
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, Timelike, Utc};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
fn get_timestamp(obj: &JsObject) -> f64 {
let v = obj.get_prop_at(0);
if v.is_double() {
v.as_double()
} else {
f64::NAN
}
}
fn set_timestamp(obj: &mut JsObject, ms: f64) {
obj.set_prop_at(0, JsValue::float(ms));
}
fn ensure_date(vm: &mut Vm, obj: &JsObject) -> Result<(), JsValue> {
if !obj.is_date_obj() {
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
}
Ok(())
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
fn date_this_mut(vm: &mut Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> {
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"));
}
let obj_ptr = raw.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj = unsafe { &*obj_ptr };
ensure_date(vm, obj)?;
Ok(obj_ptr)
}
fn date_this(vm: &mut Vm, args: &[u8]) -> Result<*const JsObject, JsValue> {
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"));
}
let obj_ptr = raw.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "called on incompatible receiver"));
}
let obj = unsafe { &*obj_ptr };
ensure_date(vm, obj)?;
Ok(obj_ptr)
}
fn dt_from_ms(ms: f64) -> Option<DateTime<Utc>> {
if !ms.is_finite() {
return None;
}
DateTime::from_timestamp_millis(ms as i64)
}
fn naive_from_ms(ms: f64) -> Option<NaiveDateTime> {
if !ms.is_finite() {
return None;
}
dt_from_ms(ms).map(|dt| dt.naive_utc())
}
pub fn date_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let is_ctor_call = this_val.is_object() && {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let date_proto = vm.session().builtin_world().date_proto.as_ptr() as *mut JsObject;
if date_proto.is_null() {
false
} else {
let obj = unsafe { &*ptr };
let proto_ptr = obj.proto().as_js_object_ptr();
if proto_ptr.is_null() {
false
} else {
std::ptr::eq(proto_ptr, date_proto)
}
}
}
};
let timestamp = if args.len() < 2 {
Utc::now().timestamp_millis() as f64
} else if args.len() > 2 {
let y = coercion::to_number(vm.reg(args[1])) as i32;
let m = coercion::to_number(vm.reg(args[2])) as u32;
let d = if args.len() > 3 { coercion::to_number(vm.reg(args[3])) as u32 } else { 1 };
let h = if args.len() > 4 { coercion::to_number(vm.reg(args[4])) as u32 } else { 0 };
let min = if args.len() > 5 { coercion::to_number(vm.reg(args[5])) as u32 } else { 0 };
let sec = if args.len() > 6 { coercion::to_number(vm.reg(args[6])) as u32 } else { 0 };
let ms = if args.len() > 7 { coercion::to_number(vm.reg(args[7])) as u32 } else { 0 };
NaiveDate::from_ymd_opt(y, m + 1, d)
.and_then(|nd| {
nd.and_hms_milli_opt(h, min, sec, ms)
.and_then(|ndt| ndt.and_local_timezone(Utc).earliest())
})
.map(|dt| dt.timestamp_millis() as f64)
.unwrap_or(f64::NAN)
} else {
let val = vm.reg(args[1]);
if val.is_string() {
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
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 {
if let Ok(ndt) = NaiveDateTime::parse_from_str(&s, fmt) {
ts = ndt.and_utc().timestamp_millis() as f64;
break;
}
}
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()) {
ts = ndt.timestamp_millis() as f64;
}
}
}
ts
} else if val.is_int() || val.is_double() {
if val.is_int() {
val.as_int() as f64
} else {
val.as_double()
}
} else if val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
unsafe { &*ptr }.is_date_obj()
}
} {
let obj = unsafe { &*val.as_js_object_ptr() };
get_timestamp(obj)
} else {
f64::NAN
}
};
if !is_ctor_call {
let s = if timestamp.is_finite() {
dt_from_ms(timestamp)
.map(|dt| dt.to_rfc2822())
.unwrap_or_else(|| "Invalid Date".to_string())
} else {
"Invalid Date".to_string()
};
return NativeResult::Ok(vm.new_string(&s));
}
let mut obj = JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(vm.session().builtin_world().date_proto.as_ptr() as *mut JsObject),
);
obj.type_tag = JsObject::OBJ_TYPE_DATE;
obj.set_prop_at(0, JsValue::float(timestamp));
let ptr = vm.alloc_object(obj);
NativeResult::Ok(JsValue::from_js_object(ptr))
}
pub fn date_now(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::float(Utc::now().timestamp_millis() as f64))
}
pub fn date_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let val = vm.reg(args[1]);
if !val.is_string() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
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 {
if let Ok(ndt) = NaiveDateTime::parse_from_str(&s, fmt) {
ts = ndt.and_utc().timestamp_millis() as f64;
break;
}
}
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()) {
ts = ndt.timestamp_millis() as f64;
}
}
}
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_utc(_vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::float(f64::NAN))
}
macro_rules! make_getter {
($name:ident, $f:expr, $df:expr) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float($df));
}
match dt_from_ms(ms) {
Some(dt) => NativeResult::Ok(JsValue::float(($f)(dt))),
None => NativeResult::Ok(JsValue::float($df)),
}
}
};
}
macro_rules! make_utc_getter {
($name:ident, $f:expr, $df:expr) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float($df));
}
match naive_from_ms(ms) {
Some(ndt) => NativeResult::Ok(JsValue::float(($f)(ndt))),
None => NativeResult::Ok(JsValue::float($df)),
}
}
};
}
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_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_milliseconds,
|ndt: NaiveDateTime| ndt.time().nanosecond() as f64 / 1_000_000.0,
f64::NAN
);
pub fn date_get_timezone_offset(vm: &mut Vm, args: &[u8]) -> NativeResult {
let _obj = unsafe { &*native_try!(date_this(vm, args)) };
let offset_min = local_offset_minutes();
NativeResult::Ok(JsValue::float(offset_min as f64))
}
fn local_offset_minutes() -> i32 {
chrono::Local::now().offset().local_minus_utc() / 60
}
fn get_opt_arg(vm: &Vm, args: &[u8], idx: usize, default: u32) -> u32 {
if args.len() > idx {
coercion::to_number(vm.reg(args[idx])) as u32
} else {
default
}
}
pub fn date_set_time(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let val = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { f64::NAN };
set_timestamp(obj, val);
NativeResult::Ok(JsValue::float(val))
}
pub fn date_set_full_year(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let m = get_opt_arg(vm, args, 2, dt.month0());
let d = get_opt_arg(vm, args, 3, dt.day0());
let nd = dt
.with_year(v as i32)
.and_then(|x| x.with_month0(m))
.and_then(|x| x.with_day0(d))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_month(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::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 ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_date(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let nd = dt.with_day0(v as u32).unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_hours(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let min = get_opt_arg(vm, args, 2, dt.minute());
let sec = get_opt_arg(vm, args, 3, dt.second());
let ms_arg = get_opt_arg(vm, args, 4, dt.timestamp_subsec_millis());
let nd = dt
.with_hour(v as u32)
.and_then(|x| x.with_minute(min))
.and_then(|x| x.with_second(sec))
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_minutes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let sec = get_opt_arg(vm, args, 2, dt.second());
let ms_arg = get_opt_arg(vm, args, 3, dt.timestamp_subsec_millis());
let nd = dt
.with_minute(v as u32)
.and_then(|x| x.with_second(sec))
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_seconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let ms_arg = get_opt_arg(vm, args, 2, dt.timestamp_subsec_millis());
let nd = dt
.with_second(v as u32)
.and_then(|x| x.with_nanosecond(ms_arg * 1_000_000))
.unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
pub fn date_set_milliseconds(vm: &mut Vm, args: &[u8]) -> NativeResult {
let val = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let v = coercion::to_number(val);
let obj = unsafe { &mut *native_try!(date_this_mut(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let dt = match dt_from_ms(ms) {
Some(d) => d,
None => return NativeResult::Ok(JsValue::float(f64::NAN)),
};
let nd = dt.with_nanosecond(v as u32 * 1_000_000).unwrap_or(dt);
let ts = nd.timestamp_millis() as f64;
set_timestamp(obj, ts);
NativeResult::Ok(JsValue::float(ts))
}
fn date_to_string_inner(vm: &mut Vm, args: &[u8], format_str: &str, invalid: &str) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(vm.new_string(invalid));
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format(format_str).to_string(),
None => invalid.to_string(),
};
NativeResult::Ok(vm.new_string(&s))
}
pub fn date_to_iso_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%Y-%m-%dT%H:%M:%S%.3fZ", "Invalid Date")
}
pub fn date_to_json(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
if !ms.is_finite() {
return NativeResult::Ok(JsValue::null());
}
let s = match dt_from_ms(ms) {
Some(dt) => dt.format("%Y-%m-%dT%H:%M:%S%.3fZ").to_string(),
None => return NativeResult::Ok(JsValue::null()),
};
NativeResult::Ok(vm.new_string(&s))
}
pub fn date_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y %H:%M:%S %Z %z", "Invalid Date")
}
pub fn date_to_date_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a %b %d %Y", "Invalid Date")
}
pub fn date_to_time_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%H:%M:%S %Z", "Invalid Date")
}
pub fn date_to_utc_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
date_to_string_inner(vm, args, "%a, %d %b %Y %H:%M:%S GMT", "Invalid Date")
}
pub fn date_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj = unsafe { &*native_try!(date_this(vm, args)) };
let ms = get_timestamp(obj);
NativeResult::Ok(JsValue::float(ms))
}
-194
View File
@@ -1,194 +0,0 @@
use std::sync::Arc;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::mem::P;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
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_core().perm_interner());
let sh = Arc::clone(vm.kernel_core().shape_forge());
let si_message = sf.intern("message").0;
let sh_message = sh.make_shape(EMPTY_SHAPE_ID, si_message);
let msg_val = vm.new_string(message);
unsafe {
(*obj).set_shape_id(sh_message);
(*obj).push_prop(msg_val);
}
let si_name = sf.intern("name").0;
let sh_name = sh.make_shape(sh_message, si_name);
let name_val = vm.new_string(name);
unsafe {
(*obj).set_shape_id(sh_name);
(*obj).push_prop(name_val);
}
JsValue::from_js_object(obj)
}
pub fn create_kind_error(vm: &mut Vm, kind: &str, msg: &str) -> JsValue {
let proto_ptr = match kind {
"TypeError" => P::as_ptr(&vm.session().builtin_world().type_error_proto) as *mut JsObject,
"RangeError" => P::as_ptr(&vm.session().builtin_world().range_error_proto) as *mut JsObject,
"ReferenceError" => P::as_ptr(&vm.session().builtin_world().reference_error_proto) as *mut JsObject,
"SyntaxError" => P::as_ptr(&vm.session().builtin_world().syntax_error_proto) as *mut JsObject,
"URIError" => P::as_ptr(&vm.session().builtin_world().uri_error_proto) as *mut JsObject,
"EvalError" => P::as_ptr(&vm.session().builtin_world().eval_error_proto) as *mut JsObject,
_ => P::as_ptr(&vm.session().builtin_world().error_proto) as *mut JsObject,
};
create_error_object(vm, proto_ptr, kind, msg)
}
pub fn create_type_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "TypeError", msg)
}
pub fn create_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "Error", msg)
}
pub fn create_reference_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "ReferenceError", msg)
}
pub fn create_range_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "RangeError", msg)
}
pub fn create_syntax_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "SyntaxError", msg)
}
pub fn create_uri_error(vm: &mut Vm, msg: &str) -> JsValue {
create_kind_error(vm, "URIError", msg)
}
fn get_msg(vm: &mut Vm, args: &[u8]) -> String {
if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
}
}
pub fn error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "Error", &msg))
}
pub fn type_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().type_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "TypeError", &msg))
}
pub fn reference_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().reference_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "ReferenceError", &msg))
}
pub fn range_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().range_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "RangeError", &msg))
}
pub fn syntax_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().syntax_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "SyntaxError", &msg))
}
pub fn uri_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().uri_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "URIError", &msg))
}
pub fn eval_error_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let msg = get_msg(vm, args);
let proto_ptr = P::as_ptr(&vm.session().builtin_world().eval_error_proto) as *mut JsObject;
NativeResult::Ok(create_error_object(vm, proto_ptr, "EvalError", &msg))
}
pub fn error_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
let (name, msg) = if this_val.is_object() {
let obj = unsafe { &*this_val.as_js_object_ptr() };
let name_val = obj
.hash_props_vec()
.and_then(|v| v.first())
.copied()
.unwrap_or(JsValue::undefined());
let msg_val = obj
.hash_props_vec()
.and_then(|v| v.get(1))
.copied()
.unwrap_or(JsValue::undefined());
(
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 = crate::vm::format_error_message(&name, &msg);
NativeResult::Ok(vm.new_string(&result))
}
pub fn error_stack_getter(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let mut lines = vec![];
for frame in vm.frames.iter().rev() {
let func_name = vm
.kernel_core()
.perm_interner()
.lookup(frame.function_name)
.unwrap_or("<anonymous>");
lines.push(format!(" at {} (native)", func_name));
}
lines.push(" at <anonymous> (native)".to_string());
let result = lines.join("\n");
NativeResult::Ok(vm.new_string(&result))
}
#[cfg(test)]
mod tests {
use super::error_stack_getter;
use crate::vm::{CallFrame, FrameContinuation, Vm};
use oxide_types::value::JsValue;
#[test]
fn error_stack_uses_call_frame_function_name() {
let mut vm = Vm::new();
let name_si = vm.kernel_core().perm_interner().intern("foo").0;
vm.frames.push(CallFrame {
return_addr: 0,
function_name: name_si,
caller_reg_limit: 1,
saved_reg_offset: 0,
saved_this: JsValue::undefined(),
saved_new_target: JsValue::undefined(),
callee: JsValue::undefined(),
construct_result_reg: None,
constructed_this: None,
is_derived_constructor: false,
continuation: FrameContinuation::None,
});
let stack = error_stack_getter(&mut vm, &[0]).unwrap();
let stack_str = vm.lookup_str(stack).unwrap_or_default();
assert!(stack_str.contains("foo"), "expected function name in stack: {stack_str}");
}
}
-144
View File
@@ -1,144 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::value::JsValue;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
fn invoke_target(vm: &mut Vm, target_val: JsValue, this_val: JsValue, arg_regs: &[u8]) -> NativeResult {
let args: Vec<JsValue> = arg_regs.iter().map(|&r| vm.reg(r)).collect();
NativeResult::TailCall {
callee: target_val,
this: this_val,
args,
}
}
fn bind_dispatcher(vm: &mut Vm, args: &[u8]) -> NativeResult {
let wrapper_val = vm.reg(254);
let wrapper = unsafe { &*wrapper_val.as_js_object_ptr() };
let bound_target = wrapper
.hash_props_vec()
.and_then(|v| v.first().copied())
.unwrap_or(JsValue::undefined());
let bound_this = wrapper
.hash_props_vec()
.and_then(|v| v.get(1).copied())
.unwrap_or(JsValue::undefined());
// Forward bound call arguments (skip args[0], the bound-wrapper receiver).
let arg_regs: Vec<u8> = args.iter().skip(1).copied().collect();
invoke_target(vm, bound_target, bound_this, &arg_regs)
}
pub fn function_call(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.is_empty() {
return NativeResult::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 arg_regs: Vec<u8> = args.iter().skip(2).copied().collect();
invoke_target(vm, target_val, this_val, &arg_regs)
}
pub fn function_apply(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.is_empty() {
return NativeResult::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 arg_regs: Vec<u8>;
if args.len() > 2 {
let arr_val = vm.reg(args[2]);
if arr_val.is_object() {
let arr_ptr = arr_val.as_js_object_ptr();
if !arr_ptr.is_null() {
let arr = unsafe { &*arr_ptr };
if arr.is_array() {
let max_args = 55usize; // base=200, max safe register is 255.
let n = arr.hash_props_vec().map_or(0, |v| v.len()).min(max_args);
let base = 200u8;
arg_regs = (0..n).map(|i| base + i as u8).collect();
for i in 0..n {
let vec = arr.hash_props_vec();
if let Some(v) = vec.and_then(|v| v.get(i)) {
vm.set_reg(base + i as u8, *v);
}
}
} else {
arg_regs = Vec::new();
}
} else {
arg_regs = Vec::new();
}
} else {
arg_regs = Vec::new();
}
} else {
arg_regs = Vec::new();
}
invoke_target(vm, target_val, this_val, &arg_regs)
}
pub fn function_bind(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.is_empty() {
return NativeResult::Err(JsValue::undefined());
}
let target_val = vm.reg(args[0]);
if !target_val.is_object() {
return NativeResult::Err(JsValue::undefined());
}
let bound_this = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let wrapper = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
unsafe {
(*wrapper).set_function(true);
// SAFETY: bind_dispatcher is a NativeFn fn-item; valid to store as NativeFnPtr.
(*wrapper).set_native_fn(Some(NativeFnPtr::from_raw(bind_dispatcher as *const ())));
(*wrapper).set_native_arg_count(0);
(*wrapper).ensure_hash_props().push(target_val);
(*wrapper).ensure_hash_props().push(bound_this);
}
NativeResult::Ok(JsValue::from_js_object(wrapper))
}
pub fn function_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !this_val.is_object() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Function.prototype.toString called on non-function",
));
}
let func = unsafe { &*this_val.as_js_object_ptr() };
if !func.is_function() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Function.prototype.toString called on non-function",
));
}
let name_si = vm.kernel_core().perm_interner().intern("name").0;
let name = vm
.resolve_property(func, name_si)
.and_then(|v| vm.lookup_str(v))
.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()
} else {
String::new()
}
});
let body = if func.native_fn().is_some() { "[native code]" } else { "[bytecode]" };
let result = if name.is_empty() {
format!("function () {{ {body} }}")
} else {
format!("function {name}() {{ {body} }}")
};
NativeResult::Ok(vm.new_string(&result))
}
-20
View File
@@ -1,20 +0,0 @@
mod annex_b;
mod uri;
use crate::coercion;
use crate::vm::Vm;
pub use annex_b::{js_escape, js_unescape};
pub use uri::{decode_uri, decode_uri_component, encode_uri, encode_uri_component};
fn string_arg(vm: &mut Vm, args: &[u8]) -> String {
if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
"undefined".to_string()
}
}
fn parse_hex_u8(slice: &[u8]) -> Option<u8> {
std::str::from_utf8(slice).ok().and_then(|s| u8::from_str_radix(s, 16).ok())
}
@@ -1,118 +0,0 @@
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
const ESCAPE_SAFE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789@*_+-./";
fn parse_hex_u16(slice: &[u8]) -> Option<u16> {
std::str::from_utf8(slice).ok().and_then(|s| u16::from_str_radix(s, 16).ok())
}
fn escape_string(input: &str) -> String {
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
if ESCAPE_SAFE.contains(ch) {
out.push(ch);
continue;
}
let code = ch as u32;
if code <= 0xFF {
out.push('%');
out.push_str(&format!("{code:02X}"));
continue;
}
let mut buf = [0u16; 2];
for unit in ch.encode_utf16(&mut buf).iter() {
out.push_str(&format!("%u{:04X}", *unit));
}
}
out
}
fn unescape_string(input: &str) -> String {
let bytes = input.as_bytes();
let mut out = String::with_capacity(input.len());
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'%' {
if i + 6 <= bytes.len() && bytes[i + 1] == b'u' {
if let Some(unit) = parse_hex_u16(&bytes[i + 2..i + 6]) {
if (0xD800..=0xDBFF).contains(&unit)
&& i + 12 <= bytes.len()
&& bytes[i + 6] == b'%'
&& bytes[i + 7] == b'u'
{
if let Some(low) = parse_hex_u16(&bytes[i + 8..i + 12]) {
if (0xDC00..=0xDFFF).contains(&low) {
let pair = [unit, low];
let decoded = char::decode_utf16(pair).next();
match decoded {
Some(Ok(ch)) => out.push(ch),
_ => {
out.push_str(&input[i..i + 12]);
}
}
i += 12;
continue;
}
}
}
if let Some(ch) = char::from_u32(unit as u32) {
out.push(ch);
} else {
out.push_str(&input[i..i + 6]);
}
i += 6;
continue;
}
} else if i + 3 <= bytes.len() {
if let Some(value) = super::parse_hex_u8(&bytes[i + 1..i + 3]) {
out.push(value as char);
i += 3;
continue;
}
}
}
let ch = input[i..].chars().next().expect("valid utf-8 char boundary");
out.push(ch);
i += ch.len_utf8();
}
out
}
pub fn js_escape(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&escape_string(&input)))
}
pub fn js_unescape(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&unescape_string(&input)))
}
#[cfg(test)]
mod tests {
use super::{escape_string, unescape_string};
#[test]
fn escape_encodes_spaces_and_unicode() {
assert_eq!(escape_string("hello world"), "hello%20world");
assert_eq!(escape_string("AΩ你"), "A%u03A9%u4F60");
}
#[test]
fn unescape_decodes_percent_sequences() {
assert_eq!(unescape_string("hello%20world"), "hello world");
assert_eq!(unescape_string("%u03A9%u4F60"), "Ω你");
}
#[test]
fn unescape_keeps_invalid_sequences() {
assert_eq!(unescape_string("%uXYZ1%2G"), "%uXYZ1%2G");
}
}
-151
View File
@@ -1,151 +0,0 @@
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
const URI_UNESCAPED: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'()";
const URI_RESERVED: &str = ";/?:@&=+$,#";
const URI_SAFE: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.!~*'();/?:@&=+$,#";
const URI_ERROR_MESSAGE: &str = "malformed URI sequence";
fn encode_uri_string(input: &str, safe: &str) -> String {
let mut out = String::with_capacity(input.len());
for ch in input.chars() {
if ch.is_ascii() && safe.contains(ch) {
out.push(ch);
continue;
}
let mut buf = [0u8; 4];
for byte in ch.encode_utf8(&mut buf).as_bytes() {
out.push_str(&format!("%{byte:02X}"));
}
}
out
}
fn read_percent_byte(bytes: &[u8], pos: usize) -> Result<u8, ()> {
if pos + 3 > bytes.len() || bytes[pos] != b'%' {
return Err(());
}
super::parse_hex_u8(&bytes[pos + 1..pos + 3]).ok_or(())
}
fn utf8_sequence_len(first: u8) -> Result<usize, ()> {
match first {
0x00..=0x7F => Ok(1),
0xC2..=0xDF => Ok(2),
0xE0..=0xEF => Ok(3),
0xF0..=0xF4 => Ok(4),
_ => Err(()),
}
}
fn decode_uri_string(input: &str, preserve_reserved: bool) -> Result<String, ()> {
let bytes = input.as_bytes();
let mut out = String::with_capacity(input.len());
let mut i = 0;
while i < bytes.len() {
if bytes[i] != b'%' {
let ch = input[i..].chars().next().expect("valid utf-8 char boundary");
out.push(ch);
i += ch.len_utf8();
continue;
}
let first = read_percent_byte(bytes, i)?;
let sequence_len = utf8_sequence_len(first)?;
let raw_start = i;
let mut encoded = Vec::with_capacity(sequence_len);
encoded.push(first);
i += 3;
for _ in 1..sequence_len {
let byte = read_percent_byte(bytes, i)?;
if byte & 0b1100_0000 != 0b1000_0000 {
return Err(());
}
encoded.push(byte);
i += 3;
}
let decoded = std::str::from_utf8(&encoded).map_err(|_| ())?;
if preserve_reserved && decoded.len() == 1 {
let ch = decoded.chars().next().expect("single decoded char");
if URI_RESERVED.contains(ch) {
out.push_str(&input[raw_start..i]);
continue;
}
}
out.push_str(decoded);
}
Ok(out)
}
fn uri_error(vm: &mut Vm) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_uri_error(vm, URI_ERROR_MESSAGE))
}
pub fn encode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_SAFE)))
}
pub fn encode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
NativeResult::Ok(vm.new_string(&encode_uri_string(&input, URI_UNESCAPED)))
}
pub fn decode_uri(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
match decode_uri_string(&input, true) {
Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)),
Err(()) => uri_error(vm),
}
}
pub fn decode_uri_component(vm: &mut Vm, args: &[u8]) -> NativeResult {
let input = super::string_arg(vm, args);
match decode_uri_string(&input, false) {
Ok(decoded) => NativeResult::Ok(vm.new_string(&decoded)),
Err(()) => uri_error(vm),
}
}
#[cfg(test)]
mod tests {
use super::{decode_uri_string, encode_uri_string, URI_UNESCAPED};
#[test]
fn encode_uri_keeps_reserved_and_encodes_space() {
assert_eq!(
encode_uri_string("https://example.com/path?q=hello world", super::URI_SAFE),
"https://example.com/path?q=hello%20world"
);
}
#[test]
fn encode_uri_component_encodes_reserved() {
assert_eq!(encode_uri_string("a=1&b=2", URI_UNESCAPED), "a%3D1%26b%3D2");
}
#[test]
fn decode_uri_preserves_reserved_escapes() {
assert_eq!(
decode_uri_string("https://example.com/path%3Fq=hello%20world", true).unwrap(),
"https://example.com/path%3Fq=hello world"
);
}
#[test]
fn decode_uri_component_decodes_reserved() {
assert_eq!(decode_uri_string("a%3D1%26b%3D2", false).unwrap(), "a=1&b=2");
}
#[test]
fn decode_uri_rejects_malformed_sequences() {
assert!(decode_uri_string("%", false).is_err());
assert!(decode_uri_string("%E0%A4", false).is_err());
assert!(decode_uri_string("%ED%A0%80", false).is_err());
}
}
-181
View File
@@ -1,181 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
const INNER_PROP: &str = "__inner__";
const INDEX_PROP: &str = "__index__";
pub fn iterator_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_type_error(vm, "Iterator is not a constructor"))
}
pub fn iterator_from(vm: &mut Vm, args: &[u8]) -> NativeResult {
let iterable = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
match make_iterator_for_value(vm, iterable) {
Ok(iterator) => NativeResult::Ok(iterator),
Err(err) => NativeResult::Err(err),
}
}
pub(crate) fn make_iterator_for_value(vm: &mut Vm, value: JsValue) -> Result<JsValue, JsValue> {
let inner = get_iterator(vm, value)?;
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let wrapper = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let wrapper_obj = unsafe { &mut *wrapper };
vm.set_or_create_prop_value(wrapper_obj, inner_si, inner);
vm.set_or_create_prop_value(wrapper_obj, index_si, JsValue::int(0));
let next_fn = make_native_function(vm, "next", iterator_wrapper_next as *const (), 0);
vm.set_or_create_prop_value(wrapper_obj, next_si, next_fn);
Ok(JsValue::from_js_object(wrapper))
}
pub fn iterator_wrapper_next(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if !this_val.is_object() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Iterator wrapper next called on non-object",
));
}
let wrapper = unsafe { &mut *this_val.as_js_object_ptr() };
let inner_si = vm.kernel_core().perm_interner().intern(INNER_PROP).0;
let index_si = vm.kernel_core().perm_interner().intern(INDEX_PROP).0;
let inner = match vm.ordinary_get(wrapper, inner_si, this_val) {
Ok(inner) if !inner.is_undefined() => inner,
_ => {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Iterator wrapper has no inner iterator",
))
}
};
if let Some(result) = next_array_like(vm, wrapper, inner, index_si) {
return NativeResult::Ok(result);
}
if inner.is_object() {
let inner_obj = unsafe { &*inner.as_js_object_ptr() };
let next_si = vm.kernel_core().perm_interner().intern("next").0;
let next = match vm.ordinary_get(inner_obj, next_si, inner) {
Ok(next) => next,
Err(err) => return NativeResult::Err(crate::builtins::error::create_type_error(vm, &err)),
};
return match vm.call_function_sync(next, inner, &[]) {
Ok(result) => NativeResult::Ok(result),
Err(err) => NativeResult::Err(crate::builtins::error::create_type_error(vm, &err)),
};
}
NativeResult::Err(crate::builtins::error::create_type_error(vm, "value is not iterable"))
}
fn get_iterator(vm: &mut Vm, value: JsValue) -> Result<JsValue, JsValue> {
if value.is_string() || is_array_value(value) {
return Ok(value);
}
if value.is_object() {
let obj = unsafe { &*value.as_js_object_ptr() };
let next_si = vm.kernel_core().perm_interner().intern("next").0;
if let Ok(next) = vm.ordinary_get(obj, next_si, value) {
if is_callable(next) {
return Ok(value);
}
}
}
Err(crate::builtins::error::create_type_error(vm, "value is not iterable"))
}
fn next_array_like(vm: &mut Vm, wrapper: &mut JsObject, inner: JsValue, index_si: u32) -> Option<JsValue> {
if is_array_value(inner) {
let index = current_index(vm, wrapper, index_si);
let arr = unsafe { &*inner.as_js_object_ptr() };
if index < arr.prop_count() as usize {
let value = arr.get_prop_at(index);
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
return Some(make_iter_result(vm, value, false));
}
return Some(make_iter_result(vm, JsValue::undefined(), true));
}
if inner.is_string() {
let index = current_index(vm, wrapper, index_si);
let source = coercion::to_string(inner);
let mut chars = source.chars();
if let Some(ch) = chars.nth(index) {
vm.set_or_create_prop_value(wrapper, index_si, JsValue::int((index + 1) as i32));
let value = vm.new_string(&ch.to_string());
return Some(make_iter_result(vm, value, false));
}
return Some(make_iter_result(vm, JsValue::undefined(), true));
}
None
}
fn current_index(vm: &mut Vm, wrapper: &JsObject, index_si: u32) -> usize {
match vm.ordinary_get(wrapper, index_si, JsValue::undefined()) {
Ok(value) if value.is_int() => value.as_int().max(0) as usize,
Ok(value) if value.is_double() => value.as_double().max(0.0) as usize,
_ => 0,
}
}
fn make_iter_result(vm: &mut Vm, value: JsValue, done: bool) -> JsValue {
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let obj = vm
.epoch()
.alloc(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto)));
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let done_si = vm.kernel_core().perm_interner().intern("done").0;
let obj_ref = unsafe { &mut *obj };
vm.set_or_create_prop_value(obj_ref, value_si, value);
vm.set_or_create_prop_value(obj_ref, done_si, JsValue::bool(done));
JsValue::from_js_object(obj)
}
fn make_native_function(vm: &mut Vm, name: &str, native_fn: *const (), arg_count: u8) -> JsValue {
let function_proto = vm.session().builtin_world().function_proto.as_ptr() as *mut JsObject;
let mut func = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(function_proto));
func.set_function(true);
// SAFETY: native_fn is provided from a NativeFn fn item.
func.set_native_fn(Some(unsafe { oxide_types::object::NativeFnPtr::from_raw(native_fn) }));
func.set_native_arg_count(arg_count);
let func = vm.alloc_object(func);
let name_si = vm.kernel_core().perm_interner().intern("name").0;
let value = vm.new_string(name);
let func_ref = unsafe { &mut *func };
vm.set_or_create_prop_value(func_ref, name_si, value);
JsValue::from_js_object(func)
}
fn is_array_value(value: JsValue) -> bool {
if !value.is_object() {
return false;
}
let ptr = value.as_js_object_ptr();
!ptr.is_null() && unsafe { &*ptr }.is_array()
}
fn is_callable(value: JsValue) -> bool {
if !value.is_object() {
return false;
}
let ptr = value.as_js_object_ptr();
!ptr.is_null() && unsafe { &*ptr }.is_function()
}
-220
View File
@@ -1,220 +0,0 @@
use std::collections::HashSet;
use std::fmt::Write;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::builtins::object::walk_own_keys;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub fn json_parse(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::builtins::error::create_syntax_error(vm, "JSON.parse requires 1 argument"));
}
let val = vm.reg(args[1]);
if !val.is_string() {
return NativeResult::Err(crate::builtins::error::create_syntax_error(
vm,
"JSON.parse: argument is not a string",
));
}
let text = {
// SAFETY: val is a string value.
unsafe { (*val.as_string_ptr()).data.clone() }
};
let parsed: serde_json::Value = match serde_json::from_str(&text) {
Ok(v) => v,
Err(e) => return NativeResult::Err(crate::builtins::error::create_syntax_error(vm, &format!("{}", e))),
};
NativeResult::Ok(value_to_jsvalue(vm, &parsed))
}
fn value_to_jsvalue(vm: &mut Vm, val: &serde_json::Value) -> JsValue {
match val {
serde_json::Value::Null => JsValue::null(),
serde_json::Value::Bool(b) => JsValue::bool(*b),
serde_json::Value::Number(n) => {
if let Some(f) = n.as_f64() {
JsValue::float(f)
} else {
JsValue::float(0.0)
}
}
serde_json::Value::String(s) => vm.new_string(s),
serde_json::Value::Array(arr) => {
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let n = arr.len();
let array_obj = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, v) in arr.iter().enumerate() {
let jsv = value_to_jsvalue(vm, v);
unsafe {
(*array_obj).set_prop_at(i, jsv);
}
}
JsValue::from_js_object(array_obj)
}
serde_json::Value::Object(map) => {
let object_proto = vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(object_proto));
for (key, val) in map {
let si = vm.kernel_core().perm_interner().intern(key).0;
let jsv = value_to_jsvalue(vm, val);
let new_shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(new_shape);
obj.ensure_hash_props().push(jsv);
}
let obj_ptr = vm.alloc_object(obj);
JsValue::from_js_object(obj_ptr)
}
}
}
pub fn json_stringify(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let value = vm.reg(args[1]);
if value.is_undefined() {
return NativeResult::Ok(JsValue::undefined());
}
let mut visited = HashSet::new();
let mut output = String::new();
if jsvalue_to_json(vm, value, &mut visited, &mut output).is_err() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "cyclic object value"));
};
NativeResult::Ok(vm.new_string(&output))
}
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() {
} else if val.is_bool() {
out.push_str(if val.as_bool() { "true" } else { "false" });
} else if val.is_int() {
write!(out, "{}", val.as_int()).unwrap();
} else if val.is_double() {
let n = val.as_double();
if !n.is_finite() {
out.push_str("null");
} else if n.fract() == 0.0 && n.abs() < 1e21 {
write!(out, "{}", n as i64).unwrap();
} else {
let mut buf = ryu::Buffer::new();
out.push_str(buf.format(n));
}
} else if val.is_string() {
// SAFETY: val is a string value.
let s = unsafe { (*val.as_string_ptr()).data.clone() };
stringify_string(&s, out);
} else if val.is_object() {
let obj_ptr = val.as_js_object_ptr();
if obj_ptr.is_null() {
out.push_str("null");
return Ok(());
}
if visited.contains(&(obj_ptr as *const u8)) {
return Err(());
}
visited.insert(obj_ptr as *const u8);
let obj = unsafe { &*obj_ptr };
if obj.is_array() {
stringify_array(vm, obj, visited, out)?;
} else {
stringify_object(vm, obj, visited, out)?;
}
visited.remove(&(obj_ptr as *const u8));
}
Ok(())
}
fn stringify_string(s: &str, out: &mut String) {
out.push('"');
for c in s.chars() {
match c {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if c.is_control() => {
let mut buf = [0u16; 2];
let encoded = c.encode_utf16(&mut buf);
for unit in encoded {
out.push_str(&format!("\\u{:04x}", unit));
}
}
_ => out.push(c),
}
}
out.push('"');
}
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;
for (si, pos) in keys {
let val = obj.get_prop_at(pos);
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
unsafe { (*ptr).is_function() }
}
};
if val.is_undefined() || is_function {
continue;
}
if !first {
out.push(',');
}
first = false;
let name = vm.kernel_core().perm_interner().lookup(si).unwrap_or_default();
stringify_string(name, out);
out.push(':');
jsvalue_to_json(vm, val, visited, out)?;
}
out.push('}');
Ok(())
}
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 {
if i > 0 {
out.push(',');
}
let val = obj.get_prop_at(i);
let is_function = val.is_object() && {
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
unsafe { (*ptr).is_function() }
}
};
if is_function || val.is_undefined() {
out.push_str("null");
} else {
jsvalue_to_json(vm, val, visited, out)?;
}
}
out.push(']');
Ok(())
}
-203
View File
@@ -1,203 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::builtins::set::SetKey;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
type MapInner = indexmap::IndexMap<SetKey, JsValue>;
/// Retrieve the `IndexMap` pointer stored in a Map object's native-data slot.
///
/// # Safety contract maintained by callers
///
/// The pointer is valid as long as the Map `JsObject` itself is alive, which is guaranteed
/// because the `JsObject` lives in the current `Epoch` arena and `Epoch::reset()` is never
/// called while a native builtin is executing. The `Box<IndexMap>` that owns the allocation
/// is created in `new_map_inner()` and is never freed until the process exits (intentional
/// leak — lifetime is tied to the epoch). Only one live `*mut` alias exists at a time
/// per Map object because native calls are single-threaded.
fn get_map_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut MapInner, JsValue> {
if !this_val.is_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"));
}
// SAFETY: map_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the
// current Epoch. It remains valid for the duration of this call (epoch is not reset
// during native execution).
let map_obj = unsafe { &*map_ptr };
if !map_obj.is_map() {
return Err(crate::builtins::error::create_type_error(
vm,
"Map.prototype method called on incompatible receiver",
));
}
// SAFETY: native_data holds the raw pointer written by `alloc_map`.
// The pointer is a valid, heap-allocated `Box<IndexMap<SetKey, JsValue>>`.
// Alignment: IndexMap requires at most 8-byte alignment; the global allocator satisfies this.
let inner_ptr = map_obj.native_data() as *mut MapInner;
if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "Map internal state invalid"));
}
Ok(inner_ptr)
}
fn new_map_inner() -> *mut MapInner {
Box::into_raw(Box::new(MapInner::new()))
}
fn alloc_map(vm: &mut Vm) -> *mut JsObject {
let map_proto = vm.session().builtin_world().map_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(map_proto));
obj.set_map(true);
let inner = new_map_inner();
obj.set_native_data(inner as *mut u8);
vm.alloc_object(obj)
}
pub(crate) fn map_native_edges(obj: &JsObject) -> Vec<JsValue> {
if !obj.is_map() {
return Vec::new();
}
let inner = obj.native_data() as *const MapInner;
if inner.is_null() {
return Vec::new();
}
unsafe {
(*inner)
.iter()
.flat_map(|(key, value)| [key.0, *value])
.filter(|value| value.is_object())
.collect()
}
}
pub(crate) fn clone_map_native_with_rewrite<F>(src: &JsObject, dst: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
if !src.is_map() {
return;
}
let inner = src.native_data() as *const MapInner;
if inner.is_null() {
dst.set_native_data(std::ptr::null_mut());
return;
}
let mut cloned = MapInner::new();
unsafe {
for (key, value) in (*inner).iter() {
let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key };
let new_value = if value.is_object() { rewrite(*value) } else { *value };
cloned.insert(new_key, new_value);
}
}
dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8);
}
pub(crate) fn rewrite_map_native<F>(obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
if !obj.is_map() {
return;
}
let inner = obj.native_data() as *mut MapInner;
if inner.is_null() {
return;
}
unsafe {
let mut rewritten = MapInner::with_capacity((*inner).len());
for (key, value) in (*inner).iter() {
let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key };
let new_value = if value.is_object() { rewrite(*value) } else { *value };
rewritten.insert(new_key, new_value);
}
*inner = rewritten;
}
}
pub(crate) fn drop_map_native(obj: &mut JsObject) -> u64 {
if !obj.is_map() {
return 0;
}
let inner = obj.native_data() as *mut MapInner;
if inner.is_null() {
return 0;
}
unsafe {
let boxed = Box::from_raw(inner);
let bytes = std::mem::size_of::<MapInner>() + boxed.capacity() * std::mem::size_of::<(SetKey, JsValue)>();
drop(boxed);
obj.set_native_data(std::ptr::null_mut());
bytes as u64
}
}
pub fn map_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let map_obj = alloc_map(vm);
NativeResult::Ok(JsValue::from_js_object(map_obj))
}
pub fn map_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let val = vm.reg(if args.len() > 2 { args[2] } else { 0 });
unsafe {
(*inner).insert(SetKey(key), val);
}
NativeResult::Ok(this_val)
}
pub fn map_get(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).get(&SetKey(key)).copied() };
NativeResult::Ok(found.unwrap_or(JsValue::undefined()))
}
pub fn map_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).contains_key(&SetKey(key)) };
NativeResult::Ok(JsValue::bool(found))
}
pub fn map_delete(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
let key = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let removed = unsafe { (*inner).shift_remove(&SetKey(key)) };
NativeResult::Ok(JsValue::bool(removed.is_some()))
}
pub fn map_clear(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
unsafe {
(*inner).clear();
}
NativeResult::Ok(JsValue::undefined())
}
pub fn map_size(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_map_inner(vm, this_val));
NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64))
}
-155
View File
@@ -1,155 +0,0 @@
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
fn num(vm: &Vm, reg: u8) -> f64 {
coercion::to_number(vm.reg(reg))
}
fn arg1(vm: &Vm, args: &[u8]) -> f64 {
if args.len() < 2 {
f64::NAN
} else {
num(vm, args[1])
}
}
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 },
)
}
pub fn math_abs(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let x = vm.reg(args[1]);
if x.is_int() {
NativeResult::Ok(JsValue::int(x.as_int().abs()))
} else {
NativeResult::Ok(JsValue::float(num(vm, args[1]).abs()))
}
}
macro_rules! math_unary {
($name:ident, $op:ident) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::float(arg1(vm, args).$op()))
}
};
}
math_unary!(math_acos, acos);
math_unary!(math_acosh, acosh);
math_unary!(math_asin, asin);
math_unary!(math_asinh, asinh);
math_unary!(math_atan, atan);
math_unary!(math_atanh, atanh);
math_unary!(math_cbrt, cbrt);
math_unary!(math_ceil, ceil);
math_unary!(math_cos, cos);
math_unary!(math_cosh, cosh);
math_unary!(math_exp, exp);
math_unary!(math_expm1, exp_m1);
math_unary!(math_floor, floor);
math_unary!(math_log, ln);
math_unary!(math_log10, log10);
math_unary!(math_log1p, ln_1p);
math_unary!(math_log2, log2);
math_unary!(math_sin, sin);
math_unary!(math_sinh, sinh);
math_unary!(math_sqrt, sqrt);
math_unary!(math_tan, tan);
math_unary!(math_tanh, tanh);
math_unary!(math_trunc, trunc);
pub fn math_atan2(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
NativeResult::Ok(JsValue::float(a.atan2(b)))
}
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() };
NativeResult::Ok(JsValue::float(r))
}
pub fn math_sign(vm: &mut Vm, args: &[u8]) -> NativeResult {
let x = arg1(vm, args);
if x.is_nan() {
NativeResult::Ok(JsValue::float(f64::NAN))
} else if x > 0.0 {
NativeResult::Ok(JsValue::int(1))
} else if x < 0.0 {
NativeResult::Ok(JsValue::int(-1))
} else {
NativeResult::Ok(JsValue::float(x))
}
}
pub fn math_clz32(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = arg1(vm, args) as u32;
NativeResult::Ok(JsValue::int(n.leading_zeros() as i32))
}
pub fn math_fround(vm: &mut Vm, args: &[u8]) -> NativeResult {
NativeResult::Ok(JsValue::float(arg1(vm, args) as f32 as f64))
}
pub fn math_hypot(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
NativeResult::Ok(JsValue::float(a.hypot(b)))
}
pub fn math_imul(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
NativeResult::Ok(JsValue::int((a as i32).wrapping_mul(b as i32)))
}
pub fn math_pow(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (a, b) = arg2(vm, args);
NativeResult::Ok(JsValue::float(a.powf(b)))
}
pub fn math_max(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NEG_INFINITY));
}
let mut m = f64::NEG_INFINITY;
for &r in args.iter().skip(1) {
let x = num(vm, r);
if x.is_nan() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
if x > m {
m = x;
}
}
NativeResult::Ok(JsValue::float(m))
}
pub fn math_min(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::INFINITY));
}
let mut m = f64::INFINITY;
for &r in args.iter().skip(1) {
let x = num(vm, r);
if x.is_nan() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
if x < m {
m = x;
}
}
NativeResult::Ok(JsValue::float(m))
}
pub fn math_random(vm: &mut Vm, _args: &[u8]) -> NativeResult {
vm.step_rng();
NativeResult::Ok(JsValue::float(vm.math_rng_value()))
}
-21
View File
@@ -1,21 +0,0 @@
pub mod array;
pub mod array_buffer;
pub mod boolean;
pub mod data_view;
pub mod date;
pub mod error;
pub mod function;
pub mod global;
pub mod iterator;
pub mod json;
pub mod map;
pub mod math;
pub mod number;
pub mod object;
pub mod reflect;
pub mod regexp;
pub mod set;
pub mod string;
pub mod stubs;
pub mod symbol;
pub mod typed_array;
-263
View File
@@ -1,263 +0,0 @@
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub fn number_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) } else { 0.0 };
let number_proto = vm.session().builtin_world().number_proto.as_ptr() as *mut oxide_types::object::JsObject;
let is_ctor = if let Some(this_reg) = args.first().copied() {
let this_val = vm.reg(this_reg);
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, number_proto)
}
} else {
false
}
} else {
false
};
if is_ctor {
let this_val = vm.reg(args[0]);
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.type_tag = oxide_types::object::JsObject::OBJ_TYPE_NUMBER_OBJ;
let boxed = if n.fract() == 0.0 && n.is_finite() && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
JsValue::int(n as i32)
} else {
JsValue::float(n)
};
obj.set_prop_at(0, boxed);
return NativeResult::Ok(this_val);
}
if n.fract() == 0.0 && n.is_finite() && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
NativeResult::Ok(JsValue::int(n as i32))
} else {
NativeResult::Ok(JsValue::float(n))
}
}
pub fn number_is_nan(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if val.is_double() {
NativeResult::Ok(JsValue::bool(val.as_double().is_nan()))
} else {
NativeResult::Ok(JsValue::bool(false))
}
}
pub fn number_is_finite(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let val = vm.reg(args[1]);
if val.is_int() {
return NativeResult::Ok(JsValue::bool(true));
}
if val.is_double() {
return NativeResult::Ok(JsValue::bool(val.as_double().is_finite()));
}
let n = coercion::to_number(val);
NativeResult::Ok(JsValue::bool(n.is_finite()))
}
pub fn number_parse_int(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let s = coercion::to_string(vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let radix = if args.len() > 2 {
let r = coercion::to_number(vm.reg(args[2])) as i32;
if r == 0 {
10
} else {
r.clamp(2, 36)
}
} else {
10
};
let (rest, hex) = if s.starts_with("0x") || s.starts_with("0X") {
if radix == 16 || radix == 0 || (args.len() <= 2) {
(s[2..].to_string(), true)
} else {
(s.to_string(), false)
}
} else {
(s.to_string(), false)
};
let actual_radix = if hex { 16u32 } else { radix as u32 };
if let Ok(n) = i32::from_str_radix(&rest, actual_radix) {
return NativeResult::Ok(JsValue::int(n));
}
NativeResult::Ok(JsValue::float(f64::NAN))
}
pub fn number_parse_float(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
let s = coercion::to_string(vm.reg(args[1]));
let s = s.trim();
if s.is_empty() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
match fast_float::parse::<f64, _>(&s) {
Ok(v) => NativeResult::Ok(JsValue::float(v)),
Err(_) => NativeResult::Ok(JsValue::float(f64::NAN)),
}
}
pub fn number_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]));
let radix = if args.len() > 1 {
let r = coercion::to_number(vm.reg(args[1])) as u32;
r.clamp(2, 36)
} else {
10u32
};
if radix == 10 {
if n.is_nan() {
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
if n.is_sign_positive() {
return NativeResult::Ok(vm.new_string("Infinity"));
}
return NativeResult::Ok(vm.new_string("-Infinity"));
}
if n.fract() == 0.0 && n >= i32::MIN as f64 && n <= i32::MAX as f64 {
return NativeResult::Ok(vm.new_string(&(n as i64).to_string()));
}
let mut buf = ryu::Buffer::new();
NativeResult::Ok(vm.new_string(buf.format(n)))
} else {
if n.is_nan() {
return NativeResult::Ok(vm.new_string("NaN"));
}
let nn = n as i64;
let mut result = String::new();
let mut value = nn.abs();
if value == 0 {
result.push('0');
} else {
let chars = "0123456789abcdefghijklmnopqrstuvwxyz";
let mut digits = Vec::new();
while value > 0 {
digits.push(chars.as_bytes()[(value % radix as i64) as usize] as char);
value /= radix as i64;
}
for ch in digits.iter().rev() {
result.push(*ch);
}
}
if nn < 0 {
result.insert(0, '-');
}
NativeResult::Ok(vm.new_string(&result))
}
}
pub fn number_to_fixed(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]));
let digits = if args.len() > 1 {
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(20);
let formatted = format!("{:.digits$}", n);
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_is_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);
NativeResult::Ok(JsValue::bool(n.trunc() == n && n.is_finite()))
}
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);
let safe = n.trunc() == n && n.is_finite() && n >= -9007199254740991i64 as f64 && n <= 9007199254740991i64 as f64;
NativeResult::Ok(JsValue::bool(safe))
}
pub fn number_to_exponential(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]));
let digits = if args.len() > 1 {
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(100);
if n.is_nan() {
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
return NativeResult::Ok(vm.new_string(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
}
let formatted = format!("{:.digits$e}", n);
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_to_precision(vm: &mut Vm, args: &[u8]) -> NativeResult {
let n = coercion::to_number(vm.reg(args[0]));
let precision = if args.len() > 1 {
coercion::to_number(vm.reg(args[1])) as usize
} else {
0usize
}
.min(21);
if n.is_nan() {
return NativeResult::Ok(vm.new_string("NaN"));
}
if n.is_infinite() {
return NativeResult::Ok(vm.new_string(if n.is_sign_positive() { "Infinity" } else { "-Infinity" }));
}
let formatted = format!("{:.precision$}", n);
NativeResult::Ok(vm.new_string(&formatted))
}
pub fn number_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
if this_val.is_int() || this_val.is_double() {
return NativeResult::Ok(this_val);
}
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
if obj.is_number_obj() {
return NativeResult::Ok(obj.get_prop_at(0));
}
}
}
NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Number.prototype.valueOf called on incompatible receiver",
))
}
-565
View File
@@ -1,565 +0,0 @@
use oxide_kernel::shape_forge::{ShapeForge, EMPTY_SHAPE_ID};
use oxide_kernel::string_forge::PermInterner;
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::is_private_name_key;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub(crate) fn walk_own_keys(vm: &Vm, obj: &JsObject) -> Vec<(u32, u32)> {
let mut keys: Vec<(u32, u32)> = Vec::new();
let shape_id = obj.shape_id();
let mut pos: u32 = 0;
let mut shape_ids = Vec::new();
let mut cursor = Some(shape_id);
while let Some(id) = cursor {
if id == EMPTY_SHAPE_ID {
break;
}
if let Some(shape) = vm.kernel_core().shape_forge().get_shape(id) {
cursor = shape.parent;
if shape.property_name != u32::MAX && !is_private_name_key(shape.property_name) {
shape_ids.push(id);
}
} else {
break;
}
}
for id in shape_ids.iter().rev() {
if let Some(shape) = vm.kernel_core().shape_forge().get_shape(*id) {
if shape.property_name != 0 && !is_private_name_key(shape.property_name) {
keys.push((shape.property_name, pos));
}
}
pos += 1;
}
keys
}
pub fn object_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()));
NativeResult::Ok(JsValue::from_js_object(obj))
}
pub fn object_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let val = vm.reg(args[1]);
if !val.is_object() {
return NativeResult::Err(JsValue::undefined());
}
let obj_ptr = val.as_js_object_ptr();
if obj_ptr.is_null() {
return NativeResult::Err(JsValue::undefined());
}
let key_names: Vec<String>;
{
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
key_names = keys
.iter()
.map(|(si, _offset)| vm.kernel_core().perm_interner().lookup(*si).unwrap_or("").to_string())
.collect();
}
let n = key_names.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.new_string(k);
unsafe {
(*arr).set_prop_at(i, str_val);
}
}
unsafe {
(*arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
pub fn object_create(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let proto_val = vm.reg(args[1]);
if !proto_val.is_null() && !proto_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
}
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
NativeResult::Ok(JsValue::from_js_object(obj))
}
pub fn object_assign(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Object.assign requires a target"));
}
let target_val = vm.reg(args[1]);
let target_val = match coercion::to_object(target_val, vm) {
Ok(val) => val,
Err(msg) => return NativeResult::Err(crate::builtins::error::create_type_error(vm, &msg)),
};
let target_ptr = target_val.as_js_object_ptr();
if target_ptr.is_null() {
return NativeResult::Ok(target_val);
}
{
let target = unsafe { &mut *target_ptr };
for &arg_reg in args.iter().skip(2) {
let source_val = vm.reg(arg_reg);
if !source_val.is_object() {
continue;
}
let source_ptr = source_val.as_js_object_ptr();
if source_ptr.is_null() {
continue;
}
let source = unsafe { &*source_ptr };
let source_keys = walk_own_keys(vm, source);
for (_si, offset) in source_keys {
let prop_val = source.get_prop_at(offset);
target.push_prop(prop_val);
}
}
}
NativeResult::Ok(target_val)
}
pub fn object_is(vm: &mut Vm, args: &[u8]) -> NativeResult {
let lhs = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let rhs = if args.len() > 2 { vm.reg(args[2]) } else { JsValue::undefined() };
NativeResult::Ok(JsValue::bool(coercion::same_value(lhs, rhs)))
}
pub fn object_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 4 {
return NativeResult::Err(JsValue::undefined());
}
let obj_val = vm.reg(args[1]);
if !obj_val.is_object() {
return NativeResult::Err(JsValue::undefined());
}
let obj_ptr = obj_val.as_js_object_ptr();
if obj_ptr.is_null() {
return NativeResult::Err(JsValue::undefined());
}
let desc_val = vm.reg(args[3]);
if !desc_val.is_object() {
return NativeResult::Err(JsValue::undefined());
}
let desc_ptr = desc_val.as_js_object_ptr();
if desc_ptr.is_null() {
return NativeResult::Err(JsValue::undefined());
}
let prop_name_str = coercion::to_string(vm.reg(args[2]));
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let get_si = vm.kernel_core().perm_interner().intern("get").0;
let set_si = vm.kernel_core().perm_interner().intern("set").0;
let writable_si = vm.kernel_core().perm_interner().intern("writable").0;
let enumerable_si = vm.kernel_core().perm_interner().intern("enumerable").0;
let configurable_si = vm.kernel_core().perm_interner().intern("configurable").0;
let desc = unsafe { &*desc_ptr };
let value_field = own_field(vm, desc, value_si);
let get_field = own_field(vm, desc, get_si);
let set_field = own_field(vm, desc, set_si);
let writable_field = own_field(vm, desc, writable_si);
let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false);
let has_data = value_field.is_some() || writable_field.is_some();
let has_accessor = get_field.is_some() || set_field.is_some();
if has_data && has_accessor {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Invalid property descriptor: cannot mix data and accessor fields",
));
}
let obj = unsafe { &mut *obj_ptr };
if has_accessor {
let get = get_field.unwrap_or(JsValue::undefined());
let set = set_field.unwrap_or(JsValue::undefined());
if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"accessor descriptor get/set must be callable or undefined",
));
}
if vm
.define_accessor_property(obj, si, get, set, PropAttributes::new(false, enumerable, configurable))
.is_err()
{
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Cannot define property"));
}
} else {
let value = value_field.unwrap_or(JsValue::undefined());
let writable = writable_field.map(coercion::to_boolean).unwrap_or(false);
if vm
.define_data_property(obj, si, value, PropAttributes::new(writable, enumerable, configurable))
.is_err()
{
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "Cannot define property"));
}
}
NativeResult::Ok(obj_val)
}
pub fn object_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 3 {
return NativeResult::Err(JsValue::undefined());
}
let obj_val = vm.reg(args[1]);
if !obj_val.is_object() {
return NativeResult::Ok(JsValue::undefined());
}
let obj_ptr = obj_val.as_js_object_ptr();
if obj_ptr.is_null() {
return NativeResult::Ok(JsValue::undefined());
}
let prop_name_str = coercion::to_string(vm.reg(args[2]));
let si = vm.kernel_core().perm_interner().intern(&prop_name_str).0;
let (found_value, found_meta, found) = {
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
let mut found_value = JsValue::undefined();
let mut found_meta = None;
let mut found = false;
for (prop_si, offset) in keys {
if prop_si == si {
found_value = obj.get_prop_at(offset);
found_meta = obj.prop_meta_at(offset);
found = true;
break;
}
}
(found_value, found_meta, found)
};
if !found {
return NativeResult::Ok(JsValue::undefined());
}
let sf_ptr = vm.kernel_core().perm_interner().as_ref() as *const PermInterner;
let sh_ptr = vm.kernel_core().shape_forge().as_ref() as *const ShapeForge;
let desc_proto = vm.session().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 sf = unsafe { &*sf_ptr };
let sh = unsafe { &*sh_ptr };
let d: &mut JsObject = unsafe { &mut *desc };
let meta = found_meta.unwrap_or_else(|| oxide_types::object::PropMetaEntry::data(PropAttributes::DEFAULT_DATA));
if meta.is_accessor {
push_desc_prop(d, sh, sf.intern("get").0, meta.get);
push_desc_prop(d, sh, sf.intern("set").0, meta.set);
push_desc_prop(d, sh, sf.intern("enumerable").0, JsValue::bool(meta.attributes.enumerable()));
push_desc_prop(d, sh, sf.intern("configurable").0, JsValue::bool(meta.attributes.configurable()));
} else {
push_desc_prop(d, sh, sf.intern("value").0, found_value);
push_desc_prop(d, sh, sf.intern("writable").0, JsValue::bool(meta.attributes.writable()));
push_desc_prop(d, sh, sf.intern("enumerable").0, JsValue::bool(meta.attributes.enumerable()));
push_desc_prop(d, sh, sf.intern("configurable").0, JsValue::bool(meta.attributes.configurable()));
}
NativeResult::Ok(JsValue::from_js_object(desc))
}
fn own_field(vm: &Vm, obj: &JsObject, prop_si: u32) -> Option<JsValue> {
vm.kernel_core()
.shape_forge()
.lookup_position(obj.shape_id(), prop_si)
.and_then(|pos| {
if obj.prop_vec_len() > pos as usize {
Some(obj.get_prop_at(pos))
} else {
None
}
})
}
fn is_callable(value: JsValue) -> bool {
value.is_object() && unsafe { &*value.as_js_object_ptr() }.is_function()
}
fn push_desc_prop(obj: &mut JsObject, shape_forge: &ShapeForge, prop_si: u32, val: JsValue) {
let shape_id = shape_forge.make_shape(obj.shape_id(), prop_si);
obj.set_shape_id(shape_id);
obj.push_prop(val);
}
fn require_obj_arg(vm: &mut Vm, args: &[u8], fn_name: &str) -> Result<*mut JsObject, JsValue> {
if args.len() < 2 {
return Err(crate::builtins::error::create_type_error(
vm,
&format!("Object.{fn_name} called on non-object"),
));
}
let val = vm.reg(args[1]);
if !val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
&format!("Object.{fn_name} called on non-object"),
));
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return Err(crate::builtins::error::create_type_error(
vm,
&format!("Object.{fn_name} called on non-object"),
));
}
Ok(ptr)
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
pub fn object_freeze(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let val = vm.reg(args[1]);
if !val.is_object() || val.as_js_object_ptr().is_null() {
return NativeResult::Ok(val);
}
NativeResult::Ok(val)
}
pub fn object_seal(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let val = vm.reg(args[1]);
if !val.is_object() || val.as_js_object_ptr().is_null() {
return NativeResult::Ok(val);
}
NativeResult::Ok(val)
}
pub fn object_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let val = vm.reg(args[1]);
if !val.is_object() || val.as_js_object_ptr().is_null() {
return NativeResult::Ok(val);
}
NativeResult::Ok(val)
}
pub fn object_is_frozen(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(true));
}
let val = vm.reg(args[1]);
if !val.is_object() || val.as_js_object_ptr().is_null() {
return NativeResult::Ok(JsValue::bool(true));
}
NativeResult::Ok(JsValue::bool(false))
}
pub fn object_is_sealed(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(true));
}
let val = vm.reg(args[1]);
if !val.is_object() || val.as_js_object_ptr().is_null() {
return NativeResult::Ok(JsValue::bool(true));
}
NativeResult::Ok(JsValue::bool(false))
}
pub fn object_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult {
native_try!(require_obj_arg(vm, args, "isExtensible"));
NativeResult::Ok(JsValue::bool(true))
}
pub fn object_get_own_property_names(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "getOwnPropertyNames"));
let key_names: Vec<String> = {
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
keys.iter()
.map(|(si, _)| vm.kernel_core().perm_interner().lookup(*si).unwrap_or("").to_string())
.collect()
};
let n = key_names.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, k) in key_names.iter().enumerate() {
let str_val = vm.new_string(k);
unsafe {
(*arr).set_prop_at(i, str_val);
}
}
unsafe {
(*arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
pub fn object_define_properties(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 3 {
return NativeResult::Ok(vm.reg(args[1]));
}
let _obj_ptr = native_try!(require_obj_arg(vm, args, "defineProperties"));
let desc_val = vm.reg(args[2]);
if !desc_val.is_object() {
return NativeResult::Ok(vm.reg(args[1]));
}
NativeResult::Ok(vm.reg(args[1]))
}
pub fn object_from_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::null());
}
let entries = vm.reg(args[1]);
if !entries.is_object() {
return NativeResult::Ok(JsValue::null());
}
let obj_ptr = vm.alloc_object(JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(vm.session().builtin_world().object_proto.as_ptr() as *mut JsObject),
));
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
}
pub fn object_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "getPrototypeOf"));
NativeResult::Ok(unsafe { (*obj_ptr).proto() })
}
pub fn object_has_own(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 3 {
return NativeResult::Ok(JsValue::bool(false));
}
let obj_ptr = native_try!(require_obj_arg(vm, args, "hasOwn"));
let key_si = vm.property_key_si(vm.reg(args[2]));
let obj = unsafe { &*obj_ptr };
NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some()))
}
pub fn object_proto_has_own_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let this_val = vm.reg(args[0]);
if !this_val.is_object() || this_val.as_js_object_ptr().is_null() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Object.prototype.hasOwnProperty called on non-object",
));
}
let key_si = vm.property_key_si(vm.reg(args[1]));
let obj = unsafe { &*this_val.as_js_object_ptr() };
NativeResult::Ok(JsValue::bool(vm.get_own_property_slot(obj, key_si).is_some()))
}
pub fn object_proto_property_is_enumerable(vm: &mut Vm, args: &[u8]) -> NativeResult {
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let this_val = vm.reg(args[0]);
if !this_val.is_object() || this_val.as_js_object_ptr().is_null() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Object.prototype.propertyIsEnumerable called on non-object",
));
}
let key_si = vm.property_key_si(vm.reg(args[1]));
let obj = unsafe { &*this_val.as_js_object_ptr() };
let Some(pos) = vm.get_own_property_slot(obj, key_si) else {
return NativeResult::Ok(JsValue::bool(false));
};
let enumerable = obj
.prop_meta_at(pos)
.map(|meta| meta.attributes.enumerable())
.unwrap_or(PropAttributes::DEFAULT_DATA.enumerable());
NativeResult::Ok(JsValue::bool(enumerable))
}
pub fn object_entries(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "entries"));
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
let n = keys.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, (si, offset)) in keys.iter().enumerate() {
let key_str = vm.kernel_core().perm_interner().lookup(*si).unwrap_or_default();
let key_val = vm.new_string(key_str);
let val = obj.get_prop_at(*offset);
let pair = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
2,
vm.epoch().bump(),
));
unsafe {
(*pair).set_prop_at(0, key_val);
(*pair).set_prop_at(1, val);
(*pair).set_prop_count(2);
(*arr).set_prop_at(i, JsValue::from_js_object(pair));
}
}
unsafe {
(*arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
pub fn object_values(vm: &mut Vm, args: &[u8]) -> NativeResult {
let obj_ptr = native_try!(require_obj_arg(vm, args, "values"));
let obj = unsafe { &*obj_ptr };
let keys = walk_own_keys(vm, obj);
let n = keys.len();
let array_proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(array_proto),
n,
vm.epoch().bump(),
));
for (i, (_si, offset)) in keys.iter().enumerate() {
let val = obj.get_prop_at(*offset);
unsafe {
(*arr).set_prop_at(i, val);
}
}
unsafe {
(*arr).set_prop_count(n);
}
NativeResult::Ok(JsValue::from_js_object(arr))
}
-267
View File
@@ -1,267 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
use oxide_types::value::JsValue;
use crate::builtins::object::walk_own_keys;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub fn reflect_apply(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target = arg(vm, args, 1);
let this_arg = arg(vm, args, 2);
let arg_list = arg(vm, args, 3);
if !is_callable(target) {
return type_error(vm, "Reflect.apply target is not callable");
}
let Some(call_args) = array_like_elements(arg_list) else {
return type_error(vm, "Reflect.apply argumentsList must be an array-like object");
};
match vm.call_function_sync(target, this_arg, &call_args) {
Ok(value) => NativeResult::Ok(value),
Err(err) => type_error(vm, &err),
}
}
pub fn reflect_construct(vm: &mut Vm, _args: &[u8]) -> NativeResult {
type_error(vm, "Reflect.construct not yet supported")
}
pub fn reflect_define_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.defineProperty target is not an object");
};
let desc_val = arg(vm, args, 3);
let Some(desc_ptr) = object_ptr(desc_val) else {
return type_error(vm, "Reflect.defineProperty descriptor is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let desc = unsafe { &*desc_ptr };
let value_si = vm.kernel_core().perm_interner().intern("value").0;
let get_si = vm.kernel_core().perm_interner().intern("get").0;
let set_si = vm.kernel_core().perm_interner().intern("set").0;
let writable_si = vm.kernel_core().perm_interner().intern("writable").0;
let enumerable_si = vm.kernel_core().perm_interner().intern("enumerable").0;
let configurable_si = vm.kernel_core().perm_interner().intern("configurable").0;
let value_field = own_field(vm, desc, value_si);
let get_field = own_field(vm, desc, get_si);
let set_field = own_field(vm, desc, set_si);
let writable_field = own_field(vm, desc, writable_si);
let enumerable = own_field(vm, desc, enumerable_si).map(coercion::to_boolean).unwrap_or(false);
let configurable = own_field(vm, desc, configurable_si).map(coercion::to_boolean).unwrap_or(false);
let has_data = value_field.is_some() || writable_field.is_some();
let has_accessor = get_field.is_some() || set_field.is_some();
if has_data && has_accessor {
return NativeResult::Ok(JsValue::bool(false));
}
let target = unsafe { &mut *target_ptr };
let result = if has_accessor {
let get = get_field.unwrap_or(JsValue::undefined());
let set = set_field.unwrap_or(JsValue::undefined());
if (!get.is_undefined() && !is_callable(get)) || (!set.is_undefined() && !is_callable(set)) {
return NativeResult::Ok(JsValue::bool(false));
}
vm.define_accessor_property(target, key_si, get, set, PropAttributes::new(false, enumerable, configurable))
} else {
let value = value_field.unwrap_or(JsValue::undefined());
let writable = writable_field.map(coercion::to_boolean).unwrap_or(false);
vm.define_data_property(target, key_si, value, PropAttributes::new(writable, enumerable, configurable))
};
NativeResult::Ok(JsValue::bool(result.is_ok()))
}
pub fn reflect_delete_property(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.deleteProperty target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let target = unsafe { &mut *target_ptr };
NativeResult::Ok(JsValue::bool(delete_own_property(vm, target, key_si)))
}
pub fn reflect_get(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.get target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let receiver = if args.len() > 3 { vm.reg(args[3]) } else { target_val };
match vm.ordinary_get(unsafe { &*target_ptr }, key_si, receiver) {
Ok(value) => NativeResult::Ok(value),
Err(err) => type_error(vm, &err),
}
}
pub fn reflect_get_own_property_descriptor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(_) = object_ptr(target_val) else {
return type_error(vm, "Reflect.getOwnPropertyDescriptor target is not an object");
};
crate::builtins::object::object_get_own_property_descriptor(vm, args)
}
pub fn reflect_get_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.getPrototypeOf target is not an object");
};
NativeResult::Ok(unsafe { &*target_ptr }.proto())
}
pub fn reflect_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.has target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
NativeResult::Ok(JsValue::bool(vm.resolve_property(unsafe { &*target_ptr }, key_si).is_some()))
}
pub fn reflect_is_extensible(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.isExtensible target is not an object");
};
NativeResult::Ok(JsValue::bool(unsafe { &*target_ptr }.is_extensible()))
}
pub fn reflect_own_keys(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.ownKeys target is not an object");
};
let target = unsafe { &*target_ptr };
let key_names: Vec<String> = walk_own_keys(vm, target)
.into_iter()
.map(|(si, _)| vm.kernel_core().perm_interner().lookup(si).unwrap_or("").to_string())
.collect();
NativeResult::Ok(make_string_array(vm, &key_names))
}
pub fn reflect_prevent_extensions(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.preventExtensions target is not an object");
};
unsafe { &mut *target_ptr }.set_extensible(false);
NativeResult::Ok(JsValue::bool(true))
}
pub fn reflect_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.set target is not an object");
};
let key_si = vm.property_key_si(arg(vm, args, 2));
let value = arg(vm, args, 3);
let receiver = if args.len() > 4 { vm.reg(args[4]) } else { target_val };
let target = unsafe { &mut *target_ptr };
if !target.is_extensible() && vm.get_own_property_slot(target, key_si).is_none() {
return NativeResult::Ok(JsValue::bool(false));
}
NativeResult::Ok(JsValue::bool(vm.ordinary_set(target, key_si, value, receiver).is_ok()))
}
pub fn reflect_set_prototype_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let target_val = arg(vm, args, 1);
let Some(target_ptr) = object_ptr(target_val) else {
return type_error(vm, "Reflect.setPrototypeOf target is not an object");
};
let proto = arg(vm, args, 2);
if !proto.is_object() && !proto.is_null() {
return type_error(vm, "Reflect.setPrototypeOf prototype must be an object or null");
}
NativeResult::Ok(JsValue::bool(unsafe { &mut *target_ptr }.set_proto(proto).is_ok()))
}
fn arg(vm: &Vm, args: &[u8], idx: usize) -> JsValue {
args.get(idx).map(|reg| vm.reg(*reg)).unwrap_or_else(JsValue::undefined)
}
fn object_ptr(value: JsValue) -> Option<*mut JsObject> {
if !value.is_object() {
return None;
}
let ptr = value.as_js_object_ptr();
(!ptr.is_null()).then_some(ptr)
}
fn is_callable(value: JsValue) -> bool {
object_ptr(value).is_some_and(|ptr| unsafe { &*ptr }.is_function())
}
fn type_error(vm: &mut Vm, message: &str) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_type_error(vm, message))
}
fn array_like_elements(value: JsValue) -> Option<Vec<JsValue>> {
let ptr = object_ptr(value)?;
let obj = unsafe { &*ptr };
Some((0..obj.prop_count() as usize).map(|idx| obj.get_prop_at(idx)).collect())
}
fn own_field(vm: &Vm, obj: &JsObject, prop_si: u32) -> Option<JsValue> {
vm.kernel_core()
.shape_forge()
.lookup_position(obj.shape_id(), prop_si)
.and_then(|pos| (obj.prop_vec_len() > pos as usize).then(|| obj.get_prop_at(pos)))
}
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
let proto = vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject;
let arr = vm.alloc_object(JsObject::new_array(
EMPTY_SHAPE_ID,
JsValue::from_js_object(proto),
parts.len(),
vm.epoch().bump(),
));
for (idx, part) in parts.iter().enumerate() {
let value = vm.new_string(part);
unsafe { &mut *arr }.set_prop_at(idx, value);
}
unsafe { &mut *arr }.set_prop_count(parts.len());
JsValue::from_js_object(arr)
}
fn delete_own_property(vm: &mut Vm, obj: &mut JsObject, key_si: u32) -> bool {
let keys = walk_own_keys(vm, obj);
let Some((_, delete_pos)) = keys.iter().find(|(si, _)| *si == key_si).copied() else {
return true;
};
if obj
.prop_meta_at(delete_pos)
.map(|meta| !meta.attributes.configurable())
.unwrap_or(false)
{
return false;
}
let retained: Vec<(u32, JsValue, Option<PropMetaEntry>)> = keys
.into_iter()
.filter(|(_, pos)| *pos != delete_pos)
.map(|(si, pos)| (si, obj.get_prop_at(pos), obj.prop_meta_at(pos)))
.collect();
obj.set_shape_id(EMPTY_SHAPE_ID);
obj.set_prop_count(0usize);
for (si, value, meta) in retained {
let shape = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(shape);
let pos = obj.push_prop(value);
if let Some(meta) = meta {
if meta.is_accessor {
obj.set_accessor_meta(pos, meta.get, meta.set, meta.attributes);
} else {
obj.set_data_meta(pos, meta.attributes);
}
}
}
obj.bump_generation();
true
}
-260
View File
@@ -1,260 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
fn get_regexp_ptr(vm: &mut Vm, args: &[u8]) -> Result<*mut JsObject, JsValue> {
let this_val = vm.reg(args[0]);
if !this_val.is_object() {
return Err(crate::builtins::error::create_type_error(
vm,
"RegExp.prototype method called on non-object",
));
}
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "null object"));
}
if !unsafe { &*ptr }.is_regexp_obj() {
return Err(crate::builtins::error::create_type_error(
vm,
"RegExp.prototype method called on non-RegExp object",
));
}
Ok(ptr)
}
fn parse_flags(flags: &str) -> (bool, bool, bool) {
let mut global = false;
let mut ignore_case = false;
let mut multi_line = false;
for c in flags.chars() {
match c {
'g' => global = true,
'i' => ignore_case = true,
'm' => multi_line = true,
_ => {}
}
}
(global, ignore_case, multi_line)
}
fn set_prop(obj: &mut JsObject, name: &str, val: JsValue, vm: &Vm) {
let si = vm.kernel_core().perm_interner().intern(name).0;
let shape_id = vm.kernel_core().shape_forge().make_shape(obj.shape_id(), si);
obj.set_shape_id(shape_id);
obj.ensure_hash_props().push(val);
}
fn get_prop(obj: &JsObject, idx: usize) -> JsValue {
obj.hash_props_vec()
.and_then(|v| v.get(idx))
.copied()
.unwrap_or(JsValue::undefined())
}
fn set_prop_at(obj: *mut JsObject, idx: usize, val: JsValue) {
unsafe {
let re = &mut *obj;
if let Some(vec) = re.hash_props_vec() {
let ptr = vec.as_ptr() as *mut JsValue;
let v = &mut *ptr.add(idx);
*v = val;
}
}
}
pub fn regexp_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let (pattern, flags) = if args.len() < 2 {
(String::new(), String::new())
} else if args.len() < 3 {
let pat = coercion::to_string(vm.reg(args[1]));
(pat, String::new())
} else {
let pat = coercion::to_string(vm.reg(args[1]));
let fl = coercion::to_string(vm.reg(args[2]));
(pat, fl)
};
let (global, ignore_case, multi_line) = parse_flags(&flags);
let mut obj = JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject),
);
let compiled = regex::RegexBuilder::new(&pattern)
.case_insensitive(ignore_case)
.multi_line(multi_line)
.build();
match compiled {
Ok(re) => {
let re_ptr = Box::into_raw(Box::new(re));
// SAFETY: re_ptr is a Box<regex::Regex> pointer stored by the constructor via
// NativeFnPtr::from_raw(re_ptr as *const ()). RegExp objects repurpose the native_fn
// field to hold the compiled Regex — not a NativeFn pointer. Valid for the object's
// lifetime; VM reset drops the Box through `drop_regexp_native`.
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(re_ptr as *const ()) }));
}
Err(e) => {
return NativeResult::Err(crate::builtins::error::create_syntax_error(
vm,
&format!("Invalid regular expression: {e}"),
));
}
}
set_prop(&mut obj, "lastIndex", JsValue::int(0), vm);
set_prop(&mut obj, "source", vm.new_string(&pattern), vm);
set_prop(&mut obj, "flags", vm.new_string(&flags), vm);
set_prop(&mut obj, "global", JsValue::bool(global), vm);
set_prop(&mut obj, "ignoreCase", JsValue::bool(ignore_case), vm);
set_prop(&mut obj, "multiline", JsValue::bool(multi_line), vm);
set_prop(&mut obj, "dotAll", JsValue::bool(false), vm);
set_prop(&mut obj, "sticky", JsValue::bool(false), vm);
set_prop(&mut obj, "unicode", JsValue::bool(false), vm);
obj.type_tag = JsObject::OBJ_TYPE_REGEXP;
let obj_ptr = vm.alloc_object(obj);
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
}
pub(crate) fn drop_regexp_native(obj: &mut JsObject) -> u64 {
if !obj.is_regexp_obj() {
return 0;
}
let Some(ptr) = obj.native_fn() else {
return 0;
};
let regex_ptr = ptr.as_ptr() as *mut regex::Regex;
if regex_ptr.is_null() {
return 0;
}
unsafe { drop(Box::from_raw(regex_ptr)) };
obj.set_native_fn(None);
std::mem::size_of::<regex::Regex>() as u64
}
pub fn regexp_test(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re_ptr = match get_regexp_ptr(vm, args) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
None => {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "invalid RegExp"));
}
Some(p) => p,
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
let last_index = coercion::to_number(get_prop(re, 0)) as usize;
let is_global = get_prop(re, 3).as_bool();
if is_global {
let result = regex.find_at(&haystack, last_index).is_some();
NativeResult::Ok(JsValue::bool(result))
} else {
NativeResult::Ok(JsValue::bool(regex.is_match(&haystack)))
}
}
pub fn regexp_exec(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re_ptr = match get_regexp_ptr(vm, args) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
let fn_ptr = {
let re = unsafe { &*re_ptr };
match re.native_fn() {
None => {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "invalid RegExp"));
}
Some(p) => p,
}
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let haystack = coercion::to_string(vm.reg(if args.len() > 1 { args[1] } else { args[0] }));
let (last_index, is_global) = {
let re = unsafe { &*re_ptr };
let li = coercion::to_number(get_prop(re, 0)) as usize;
let g = get_prop(re, 3).as_bool();
(li, g)
};
let match_result = if is_global {
if last_index > haystack.len() {
return NativeResult::Ok(JsValue::null());
}
regex.find_at(&haystack, last_index)
} else {
regex.find(&haystack)
};
if let Some(m) = match_result {
let mut match_obj = JsObject::new_empty(
EMPTY_SHAPE_ID,
JsValue::from_js_object(vm.session().builtin_world().array_proto.as_ptr() as *mut JsObject),
);
let full_match = &haystack[m.start()..m.end()];
let full_val = vm.new_string(full_match);
match_obj.ensure_hash_props().push(full_val);
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 cap_val = vm.new_string(cap_str);
match_obj.ensure_hash_props().push(cap_val);
}
}
set_prop(&mut match_obj, "index", JsValue::int(m.start() as i32), vm);
let haystack_val = vm.new_string(&haystack);
set_prop(&mut match_obj, "input", haystack_val, vm);
set_prop(&mut match_obj, "groups", JsValue::undefined(), vm);
if is_global {
set_prop_at(re_ptr, 0, JsValue::int(m.end() as i32));
}
let obj_ptr = vm.alloc_object(match_obj);
NativeResult::Ok(JsValue::from_js_object(obj_ptr))
} else {
if is_global {
set_prop_at(re_ptr, 0, JsValue::int(0));
}
NativeResult::Ok(JsValue::null())
}
}
pub fn regexp_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let re_ptr = match get_regexp_ptr(vm, args) {
Ok(ptr) => ptr,
Err(err) => return NativeResult::Err(err),
};
let re = unsafe { &*re_ptr };
let source = {
let val = get_prop(re, 1);
vm.lookup_str(val).unwrap_or_default()
};
let flags = {
let val = get_prop(re, 2);
vm.lookup_str(val).unwrap_or_default()
};
let result = format!("/{}/{}", source, flags);
NativeResult::Ok(vm.new_string(&result))
}
-226
View File
@@ -1,226 +0,0 @@
use std::hash::{Hash, Hasher};
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
#[derive(Clone, Copy)]
pub struct SetKey(pub JsValue);
impl PartialEq for SetKey {
fn eq(&self, other: &Self) -> bool {
if self.0.is_double() && other.0.is_double() {
let a = self.0.as_double();
let b = other.0.as_double();
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
return true;
}
return a == b;
}
// SAFETY: JsValue is an 8-byte NaN-boxed Copy value; raw bits define non-double identity here.
unsafe { std::mem::transmute::<JsValue, u64>(self.0) == std::mem::transmute::<JsValue, u64>(other.0) }
}
}
impl Eq for SetKey {}
impl Hash for SetKey {
fn hash<H: Hasher>(&self, state: &mut H) {
if self.0.is_double() {
let d = self.0.as_double();
if d.is_nan() {
0u64.hash(state);
} else if d == 0.0 {
0.0f64.to_bits().hash(state);
} else {
d.to_bits().hash(state);
}
} else {
// SAFETY: JsValue is an 8-byte NaN-boxed Copy value; hashing raw bits matches equality above.
unsafe { std::mem::transmute::<JsValue, u64>(self.0).hash(state) }
}
}
}
type SetInner = indexmap::IndexSet<SetKey>;
/// Retrieve the `IndexSet` pointer stored in a Set object's native-data slot.
///
/// # Safety contract maintained by callers
///
/// The pointer is valid as long as the Set `JsObject` itself is alive. The `JsObject`
/// lives in the current `Epoch` arena and `Epoch::reset()` is never called while a
/// native builtin is executing. The `Box<IndexSet>` is allocated in `new_set_inner()`
/// and is never freed during the Set's lifetime. Only one live `*mut` alias exists per
/// Set object at a time because native calls are single-threaded.
fn get_set_inner(vm: &mut Vm, this_val: JsValue) -> Result<*mut SetInner, JsValue> {
if !this_val.is_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"));
}
// SAFETY: set_ptr is a non-null, aligned pointer to a JsObject bump-allocated in the
// current Epoch. Remains valid for the duration of this call.
let set_obj = unsafe { &*set_ptr };
if !set_obj.is_set() {
return Err(crate::builtins::error::create_type_error(
vm,
"Set.prototype.add called on incompatible receiver",
));
}
// SAFETY: native_data holds the raw pointer written by `alloc_set`.
// The pointer is a valid, heap-allocated `Box<IndexSet<SetKey>>`.
// Alignment: IndexSet requires at most 8-byte alignment; the global allocator satisfies this.
let inner_ptr = set_obj.native_data() as *mut SetInner;
if inner_ptr.is_null() {
return Err(crate::builtins::error::create_type_error(vm, "Set internal state invalid"));
}
Ok(inner_ptr)
}
fn new_set_inner() -> *mut SetInner {
Box::into_raw(Box::new(SetInner::new()))
}
fn alloc_set(vm: &mut Vm) -> *mut JsObject {
let set_proto = vm.session().builtin_world().set_proto.as_ptr() as *mut JsObject;
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(set_proto));
obj.set_set(true);
let inner = new_set_inner();
obj.set_native_data(inner as *mut u8);
vm.alloc_object(obj)
}
pub(crate) fn set_native_edges(obj: &JsObject) -> Vec<JsValue> {
if !obj.is_set() {
return Vec::new();
}
let inner = obj.native_data() as *const SetInner;
if inner.is_null() {
return Vec::new();
}
unsafe { (*inner).iter().map(|key| key.0).filter(|value| value.is_object()).collect() }
}
pub(crate) fn clone_set_native_with_rewrite<F>(src: &JsObject, dst: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
if !src.is_set() {
return;
}
let inner = src.native_data() as *const SetInner;
if inner.is_null() {
dst.set_native_data(std::ptr::null_mut());
return;
}
let mut cloned = SetInner::new();
unsafe {
for key in (*inner).iter() {
let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key };
cloned.insert(new_key);
}
}
dst.set_native_data(Box::into_raw(Box::new(cloned)) as *mut u8);
}
pub(crate) fn rewrite_set_native<F>(obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
if !obj.is_set() {
return;
}
let inner = obj.native_data() as *mut SetInner;
if inner.is_null() {
return;
}
unsafe {
let mut rewritten = SetInner::with_capacity((*inner).len());
for key in (*inner).iter() {
let new_key = if key.0.is_object() { SetKey(rewrite(key.0)) } else { *key };
rewritten.insert(new_key);
}
*inner = rewritten;
}
}
pub(crate) fn drop_set_native(obj: &mut JsObject) -> u64 {
if !obj.is_set() {
return 0;
}
let inner = obj.native_data() as *mut SetInner;
if inner.is_null() {
return 0;
}
unsafe {
let boxed = Box::from_raw(inner);
let bytes = std::mem::size_of::<SetInner>() + boxed.capacity() * std::mem::size_of::<SetKey>();
drop(boxed);
obj.set_native_data(std::ptr::null_mut());
bytes as u64
}
}
pub fn set_constructor(vm: &mut Vm, _args: &[u8]) -> NativeResult {
let set_obj = alloc_set(vm);
NativeResult::Ok(JsValue::from_js_object(set_obj))
}
pub fn set_add(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_set_inner(vm, this_val));
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
unsafe {
(*inner).insert(SetKey(val));
}
NativeResult::Ok(this_val)
}
pub fn set_has(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_set_inner(vm, this_val));
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let found = unsafe { (*inner).contains(&SetKey(val)) };
NativeResult::Ok(JsValue::bool(found))
}
pub fn set_delete(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_set_inner(vm, this_val));
let val = vm.reg(if args.len() > 1 { args[1] } else { 0 });
let removed = unsafe { (*inner).shift_remove(&SetKey(val)) };
NativeResult::Ok(JsValue::bool(removed))
}
pub fn set_clear(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_set_inner(vm, this_val));
unsafe {
(*inner).clear();
}
NativeResult::Ok(JsValue::undefined())
}
pub fn set_size(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let inner = native_try!(get_set_inner(vm, this_val));
NativeResult::Ok(JsValue::float(unsafe { (*inner).len() } as f64))
}
-611
View File
@@ -1,611 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use memchr::memchr;
use oxide_runtime_api::NativeResult;
fn this_string(vm: &Vm, args: &[u8]) -> String {
coercion::to_string(vm.reg(args[0]))
}
pub fn string_from_char_code(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut out = String::new();
for &arg_reg in args.iter().skip(1) {
let code = coercion::to_uint32(vm.reg(arg_reg)) & 0xFFFF;
if let Some(ch) = char::from_u32(code) {
out.push(ch);
} else {
out.push('\u{FFFD}');
}
}
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_value_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
if this_val.is_string() {
return NativeResult::Ok(this_val);
}
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
if obj.is_string_obj() {
return NativeResult::Ok(obj.get_prop_at(0));
}
}
}
NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"String.prototype.valueOf called on non-String object",
))
}
pub fn string_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
};
let str_val = vm.new_string(&s);
let string_proto = vm.session().builtin_world().string_proto.as_ptr() as *mut JsObject;
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let is_ctor = if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if ptr.is_null() {
false
} else {
let proto_ptr = unsafe { (*ptr).proto().as_js_object_ptr() };
!proto_ptr.is_null() && std::ptr::eq(proto_ptr, string_proto)
}
} else {
false
};
if !is_ctor {
return NativeResult::Ok(str_val);
}
let obj = unsafe { &mut *this_val.as_js_object_ptr() };
obj.type_tag = JsObject::OBJ_TYPE_STRING_OBJ;
obj.push_prop(str_val);
NativeResult::Ok(this_val)
}
pub fn string_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
if this_val.is_string() {
return NativeResult::Ok(this_val);
}
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
if obj.is_string_obj() {
return NativeResult::Ok(obj.get_prop_at(0));
}
}
}
NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"String.prototype.toString called on non-String object",
))
}
fn make_string_array(vm: &mut Vm, parts: &[String]) -> JsValue {
let proto = vm.session().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()));
unsafe {
for (i, s) in parts.iter().enumerate() {
let sv = vm.new_string(s);
(*arr).set_prop_at(i, sv);
}
(*arr).set_prop_count(n);
}
JsValue::from_js_object(arr)
}
fn as_string(_vm: &Vm, val: JsValue) -> String {
coercion::to_string(val)
}
fn char_len(s: &str) -> usize {
s.chars().count()
}
fn byte_index_at_char(s: &str, char_pos: usize) -> usize {
if char_pos == 0 {
return 0;
}
s.char_indices().nth(char_pos).map(|(idx, _)| idx).unwrap_or(s.len())
}
fn char_slice(s: &str, start: usize, end: usize) -> &str {
let start_byte = byte_index_at_char(s, start);
let end_byte = byte_index_at_char(s, end);
&s[start_byte..end_byte]
}
fn take_chars(s: &str, count: usize) -> String {
s.chars().take(count).collect()
}
fn is_regexp_obj(val: JsValue, vm: &Vm) -> bool {
if !val.is_object() {
return false;
}
let ptr = val.as_js_object_ptr();
if ptr.is_null() {
return false;
}
let obj = unsafe { &*ptr };
let proto = obj.proto();
if !proto.is_object() {
return false;
}
let proto_ptr = proto.as_js_object_ptr();
if proto_ptr.is_null() {
return false;
}
let rp = vm.session().builtin_world().regexp_proto.as_ptr() as *mut JsObject;
std::ptr::eq(proto_ptr, rp)
}
pub fn string_index_of(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s);
if args.len() < 2 {
return NativeResult::Ok(JsValue::int(-1));
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
if search.is_empty() {
return NativeResult::Ok(JsValue::int(pos as i32));
}
let start_byte = byte_index_at_char(&s, pos);
let haystack = &s[start_byte..];
if search.len() == 1 {
if let Some(idx) = memchr(search.as_bytes()[0], haystack.as_bytes()) {
let matched_byte = start_byte + idx;
return NativeResult::Ok(JsValue::int(char_len(&s[..matched_byte]) as i32));
}
} else if let Some(idx) = haystack.find(&search) {
let matched_byte = start_byte + idx;
return NativeResult::Ok(JsValue::int(char_len(&s[..matched_byte]) as i32));
}
NativeResult::Ok(JsValue::int(-1))
}
pub fn string_includes(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s);
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
if search.is_empty() {
return NativeResult::Ok(JsValue::bool(true));
}
let haystack = &s[byte_index_at_char(&s, pos)..];
if search.len() == 1 {
NativeResult::Ok(JsValue::bool(memchr(search.as_bytes()[0], haystack.as_bytes()).is_some()))
} else {
NativeResult::Ok(JsValue::bool(haystack.contains(&search)))
}
}
pub fn string_char_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
if s.is_empty() {
return NativeResult::Ok(vm.new_string(""));
}
return NativeResult::Ok(vm.new_string(&take_chars(&s, 1)));
}
let idx = coercion::to_number(vm.reg(args[1])) as i32;
if idx < 0 || idx as usize >= char_len(&s) {
return NativeResult::Ok(vm.new_string(""));
}
let ch = s.chars().nth(idx as usize).map(|c| c.to_string()).unwrap_or_default();
NativeResult::Ok(vm.new_string(&ch))
}
pub fn string_char_code_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
if s.is_empty() {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
return NativeResult::Ok(JsValue::int(s.chars().next().unwrap() as i32));
}
let idx = coercion::to_number(vm.reg(args[1])) as i32;
if idx < 0 || idx as usize >= char_len(&s) {
return NativeResult::Ok(JsValue::float(f64::NAN));
}
NativeResult::Ok(JsValue::int(s.chars().nth(idx as usize).unwrap() as i32))
}
pub fn string_concat(vm: &mut Vm, args: &[u8]) -> NativeResult {
let mut result = this_string(vm, args);
for &arg_reg in args.iter().skip(1) {
result.push_str(&coercion::to_string(vm.reg(arg_reg)));
}
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s) as i32;
let start = if args.len() > 1 {
let v = coercion::to_number(vm.reg(args[1])) as i32;
if v < 0 {
(n + v).max(0)
} else {
v.min(n)
}
} else {
0
};
let end = if args.len() > 2 {
let v = coercion::to_number(vm.reg(args[2])) as i32;
if v < 0 {
(n + v).max(0)
} else {
v.min(n)
}
} else {
n
};
let start = start as usize;
let end = end as usize;
let result = if start < end { char_slice(&s, start, end) } else { "" };
NativeResult::Ok(vm.new_string(result))
}
pub fn string_substring(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s) as i32;
let mut start = if args.len() > 1 {
(coercion::to_number(vm.reg(args[1])) as i32).max(0).min(n)
} else {
0
};
let mut end = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as i32).max(0).min(n)
} else {
n
};
if start > end {
std::mem::swap(&mut start, &mut end);
}
let result = char_slice(&s, start as usize, end as usize);
NativeResult::Ok(vm.new_string(result))
}
pub fn string_to_upper_case(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.new_string(&s.to_uppercase()))
}
pub fn string_to_lower_case(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.new_string(&s.to_lowercase()))
}
pub fn string_trim(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.new_string(s.trim()))
}
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])) as usize).min(10000)
} else {
1
};
NativeResult::Ok(vm.new_string(&s.repeat(n)))
}
pub fn string_pad_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let s_len = char_len(&s);
let target = if args.len() > 1 {
coercion::to_number(vm.reg(args[1])) as usize
} else {
s_len
};
if target > 10000 {
return NativeResult::Err(crate::builtins::error::create_range_error(vm, "Invalid string length"));
}
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
if s_len >= target || pad.is_empty() {
return NativeResult::Ok(vm.new_string(&s));
}
let needed = target - s_len;
let pad_len = char_len(&pad).max(1);
let reps = needed.div_ceil(pad_len);
let mut out = take_chars(&pad.repeat(reps), needed);
out.push_str(&s);
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_pad_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let s_len = char_len(&s);
let target = if args.len() > 1 {
coercion::to_number(vm.reg(args[1])) as usize
} else {
s_len
};
if target > 10000 {
return NativeResult::Err(crate::builtins::error::create_range_error(vm, "Invalid string length"));
}
let pad = if args.len() > 2 { as_string(vm, vm.reg(args[2])) } else { " ".to_string() };
if s_len >= target || pad.is_empty() {
return NativeResult::Ok(vm.new_string(&s));
}
let needed = target - s_len;
let pad_len = char_len(&pad).max(1);
let reps = needed.div_ceil(pad_len);
let mut out = s;
out.push_str(&take_chars(&pad.repeat(reps), needed));
NativeResult::Ok(vm.new_string(&out))
}
pub fn string_starts_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s);
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let search = as_string(vm, vm.reg(args[1]));
let pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
0
};
NativeResult::Ok(JsValue::bool(s[byte_index_at_char(&s, pos)..].starts_with(&search)))
}
pub fn string_ends_with(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let n = char_len(&s);
if args.len() < 2 {
return NativeResult::Ok(JsValue::bool(false));
}
let search = as_string(vm, vm.reg(args[1]));
let end_pos = if args.len() > 2 {
(coercion::to_number(vm.reg(args[2])) as usize).min(n)
} else {
n
};
NativeResult::Ok(JsValue::bool(s[..byte_index_at_char(&s, end_pos)].ends_with(&search)))
}
pub fn string_split(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
let parts = vec![s.clone()];
return NativeResult::Ok(make_string_array(vm, &parts));
}
let sep_val = vm.reg(args[1]);
let limit = if args.len() > 2 {
coercion::to_number(vm.reg(args[2])) as usize
} else {
usize::MAX
};
if is_regexp_obj(sep_val, vm) {
let re_ptr = sep_val.as_js_object_ptr();
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => {
let parts = vec![s.clone()];
return NativeResult::Ok(make_string_array(vm, &parts));
}
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let raw: Vec<&str> = regex.split(&s).collect();
let parts: Vec<String> = raw.iter().map(|p| p.to_string()).take(limit).collect();
return NativeResult::Ok(make_string_array(vm, &parts));
}
let sep = as_string(vm, sep_val);
let parts: Vec<String> = if sep.is_empty() {
s.chars().map(|c| c.to_string()).take(limit).collect()
} else {
s.split(&sep).map(|p| p.to_string()).take(limit).collect()
};
NativeResult::Ok(make_string_array(vm, &parts))
}
pub fn string_replace(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(vm.new_string(&s));
}
let pattern_val = vm.reg(args[1]);
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 };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(vm.new_string(&s)),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let result = regex.replace_all(&s, replacement.as_str()).to_string();
return NativeResult::Ok(vm.new_string(&result));
}
let pattern = as_string(vm, pattern_val);
let result = s.replacen(&pattern, &replacement, 1);
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_match_fn(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::undefined());
}
let pattern_val = vm.reg(args[1]);
if is_regexp_obj(pattern_val, vm) {
let re_ptr = pattern_val.as_js_object_ptr();
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(JsValue::undefined()),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
return NativeResult::Ok(make_string_array(vm, &matches));
}
let pattern = as_string(vm, pattern_val);
if let Some(pos) = s.find(&pattern) {
return NativeResult::Ok(make_string_array(vm, &[s[pos..pos + pattern.len()].to_string()]));
}
NativeResult::Ok(JsValue::undefined())
}
pub fn string_search(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(JsValue::int(-1));
}
let pattern_val = vm.reg(args[1]);
if is_regexp_obj(pattern_val, vm) {
let re_ptr = pattern_val.as_js_object_ptr();
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(JsValue::int(-1)),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
if let Some(m) = regex.find(&s) {
return NativeResult::Ok(JsValue::int(m.start() as i32));
}
return NativeResult::Ok(JsValue::int(-1));
}
let pattern = as_string(vm, pattern_val);
if let Some(pos) = s.find(&pattern) {
return NativeResult::Ok(JsValue::int(pos as i32));
}
NativeResult::Ok(JsValue::int(-1))
}
pub fn string_trim_start(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.new_string(s.trim_start()))
}
pub fn string_trim_end(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
NativeResult::Ok(vm.new_string(s.trim_end()))
}
pub fn string_code_point_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
let pos = if args.len() > 1 { coercion::to_number(vm.reg(args[1])) as usize } else { 0 };
let chars: Vec<char> = s.chars().collect();
if pos >= chars.len() {
return NativeResult::Ok(JsValue::undefined());
}
let c = chars[pos] as u32;
if (0xD800..=0xDBFF).contains(&c) && pos + 1 < chars.len() {
let next = chars[pos + 1] as u32;
if (0xDC00..=0xDFFF).contains(&next) {
let cp = 0x10000 + ((c - 0xD800) << 10) + (next - 0xDC00);
return NativeResult::Ok(JsValue::int(cp as i32));
}
}
NativeResult::Ok(JsValue::int(c as i32))
}
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 result: String = match form.as_str() {
"NFD" => s.nfd().collect(),
"NFKC" => s.nfkc().collect(),
"NFKD" => s.nfkd().collect(),
_ => s.nfc().collect(),
};
NativeResult::Ok(vm.new_string(&result))
}
pub fn string_match_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Err(JsValue::undefined());
}
let pattern_val = vm.reg(args[1]);
if is_regexp_obj(pattern_val, vm) {
let re_ptr = pattern_val.as_js_object_ptr();
let re = unsafe { &*re_ptr };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(JsValue::undefined()),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let matches: Vec<String> = regex.find_iter(&s).map(|m| m.as_str().to_string()).collect();
return NativeResult::Ok(make_string_array(vm, &matches));
}
NativeResult::Ok(JsValue::undefined())
}
pub fn string_replace_all(vm: &mut Vm, args: &[u8]) -> NativeResult {
let s = this_string(vm, args);
if args.len() < 2 {
return NativeResult::Ok(vm.new_string(&s));
}
let pattern_val = vm.reg(args[1]);
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 };
let fn_ptr = match re.native_fn() {
Some(p) => p,
None => return NativeResult::Ok(vm.new_string(&s)),
};
// SAFETY: fn_ptr holds a Box<regex::Regex> pointer stored by regexp_constructor.
let regex = unsafe { &*(fn_ptr.as_ptr() as *const regex::Regex) };
let result = regex.replace_all(&s, replacement.as_str()).to_string();
return NativeResult::Ok(vm.new_string(&result));
}
let pattern = as_string(vm, pattern_val);
let result = s.replace(&pattern, &replacement);
NativeResult::Ok(vm.new_string(&result))
}
-32
View File
@@ -1,32 +0,0 @@
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
fn stub_error(vm: &mut Vm, name: &str) -> NativeResult {
NativeResult::Err(crate::builtins::error::create_type_error(vm, &format!("{name} is not implemented")))
}
// Architecturally deferred — these features are excluded from the supported language subset by design.
pub fn proxy_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "Proxy")
}
pub fn bigint_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "BigInt")
}
pub fn weakmap_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "WeakMap")
}
pub fn weakset_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "WeakSet")
}
pub fn weakref_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "WeakRef")
}
pub fn finalization_registry_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "FinalizationRegistry")
}
pub fn shared_array_buffer_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "SharedArrayBuffer")
}
pub fn atomics_stub(vm: &mut Vm, _args: &[u8]) -> NativeResult {
stub_error(vm, "Atomics")
}
-82
View File
@@ -1,82 +0,0 @@
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
pub fn symbol_constructor(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
if this_val.is_object() {
let ptr = this_val.as_js_object_ptr();
if !ptr.is_null() {
let obj = unsafe { &*ptr };
let proto = obj.proto();
if proto.is_object() {
let proto_ptr = proto.as_js_object_ptr();
if !proto_ptr.is_null() {
let sp = vm.session().builtin_world().symbol_proto.as_ptr() as *mut JsObject;
if std::ptr::eq(proto_ptr, sp) {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Symbol is not a constructor",
));
}
}
}
}
}
let description = if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
String::new()
};
let idx = vm.symbols.intern(description);
NativeResult::Ok(JsValue::symbol(idx))
}
pub fn symbol_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(args[0]);
if !this_val.is_symbol() {
return NativeResult::Err(crate::builtins::error::create_type_error(
vm,
"Symbol.prototype.toString requires a Symbol",
));
}
let idx = this_val.as_symbol_index();
let desc = vm.symbols.description(idx).unwrap_or("").to_string();
let result = format!("Symbol({})", desc);
NativeResult::Ok(vm.new_string(&result))
}
pub fn symbol_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
let key = if args.len() > 1 {
coercion::to_string(vm.reg(args[1]))
} else {
"undefined".to_string()
};
if let Some(idx) = vm.symbols.lookup_global(&key) {
return NativeResult::Ok(JsValue::symbol(idx));
}
let idx = vm.symbols.intern(key.clone());
vm.symbols.register_global(key, idx);
NativeResult::Ok(JsValue::symbol(idx))
}
pub fn symbol_key_for(vm: &mut Vm, args: &[u8]) -> NativeResult {
let sym = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
if !sym.is_symbol() {
return NativeResult::Err(crate::builtins::error::create_type_error(vm, "is not a symbol"));
}
let idx = sym.as_symbol_index();
match vm.symbols.key_for_id(idx) {
Some(key) => NativeResult::Ok(vm.new_string(&key)),
None => NativeResult::Ok(JsValue::undefined()),
}
}
-481
View File
@@ -1,481 +0,0 @@
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes, TypedArrayKind};
use oxide_types::value::JsValue;
use crate::builtins::array_buffer::{array_buffer_data_ptr, new_array_buffer, MAX_ARRAY_BUFFER_LENGTH};
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api::NativeResult;
#[derive(Clone, Copy)]
pub(crate) struct TypedArrayData {
pub kind: TypedArrayKind,
pub buffer: JsValue,
pub byte_offset: usize,
pub length: usize,
}
macro_rules! native_try {
($expr:expr) => {
match $expr {
Ok(value) => value,
Err(err) => return NativeResult::Err(err),
}
};
}
fn type_error(vm: &mut Vm, msg: &str) -> JsValue {
crate::builtins::error::create_type_error(vm, msg)
}
fn range_error(vm: &mut Vm, msg: &str) -> JsValue {
crate::builtins::error::create_range_error(vm, msg)
}
fn to_index(vm: &mut Vm, value: JsValue, msg: &str) -> Result<usize, JsValue> {
let n = coercion::to_number(value);
if n.is_nan() {
return Ok(0);
}
if !n.is_finite() || n < 0.0 {
return Err(range_error(vm, msg));
}
Ok(n.trunc() as usize)
}
fn normalize_index(_vm: &mut Vm, value: JsValue, len: usize) -> usize {
let n = coercion::to_number(value);
if n.is_nan() {
return 0;
}
let int = n.trunc() as isize;
if int < 0 {
len.saturating_sub((-int) as usize)
} else {
(int as usize).min(len)
}
}
fn set_named_prop(vm: &mut Vm, obj: &mut JsObject, name: &str, value: JsValue, attributes: PropAttributes) {
let si = vm.kernel_core().perm_interner().intern(name).0;
let _ = vm.define_data_property(obj, si, value, attributes);
}
fn typed_array_proto_ptr(vm: &mut Vm, kind: TypedArrayKind) -> *mut JsObject {
let world = vm.session().builtin_world();
match kind {
TypedArrayKind::Int8 => world.int8array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Uint8 => world.uint8array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Uint8Clamped => world.uint8clampedarray_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Int16 => world.int16array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Uint16 => world.uint16array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Int32 => world.int32array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Uint32 => world.uint32array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Float32 => world.float32array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::Float64 => world.float64array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::BigInt64 => world.bigint64array_proto.as_ptr() as *mut JsObject,
TypedArrayKind::BigUint64 => world.biguint64array_proto.as_ptr() as *mut JsObject,
}
}
fn create_typed_array(
vm: &mut Vm, kind: TypedArrayKind, buffer: JsValue, byte_offset: usize, length: usize,
) -> *mut JsObject {
let proto = typed_array_proto_ptr(vm, kind);
let mut obj = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::from_js_object(proto));
obj.type_tag = JsObject::OBJ_TYPE_TYPED_ARRAY;
let data = Box::into_raw(Box::new(TypedArrayData {
kind,
buffer,
byte_offset,
length,
}));
// SAFETY: TypedArray instances are not callable; native_fn stores opaque Box<TypedArrayData>,
// matching ArrayBuffer/DataView typed-object storage in this VM.
obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(data as *const ()) }));
let byte_length = length.saturating_mul(kind.bytes_per_element());
set_named_prop(vm, &mut obj, "buffer", buffer, PropAttributes::new(false, false, false));
set_named_prop(
vm,
&mut obj,
"byteOffset",
JsValue::int(byte_offset as i32),
PropAttributes::new(false, false, false),
);
set_named_prop(
vm,
&mut obj,
"byteLength",
JsValue::int(byte_length as i32),
PropAttributes::new(false, false, false),
);
set_named_prop(
vm,
&mut obj,
"length",
JsValue::int(length as i32),
PropAttributes::new(false, false, false),
);
vm.alloc_object(obj)
}
fn typed_array_data_ptr(obj: &JsObject) -> Option<*mut TypedArrayData> {
if !obj.is_typed_array_obj() {
return None;
}
obj.native_fn().map(|ptr| ptr.as_ptr() as *mut TypedArrayData)
}
pub(crate) fn typed_array_native_edges(obj: &JsObject) -> Vec<JsValue> {
let Some(ptr) = typed_array_data_ptr(obj) else {
return Vec::new();
};
if ptr.is_null() {
return Vec::new();
}
let data = unsafe { *ptr };
if data.buffer.is_object() {
vec![data.buffer]
} else {
Vec::new()
}
}
pub(crate) fn clone_typed_array_native_with_rewrite<F>(old_obj: &JsObject, new_obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
let Some(ptr) = typed_array_data_ptr(old_obj) else {
return;
};
if ptr.is_null() {
return;
}
let mut data = unsafe { *ptr };
data.buffer = rewrite(data.buffer);
let cloned = Box::into_raw(Box::new(data));
new_obj.set_native_fn(Some(unsafe { NativeFnPtr::from_raw(cloned as *const ()) }));
}
pub(crate) fn rewrite_typed_array_native<F>(obj: &mut JsObject, mut rewrite: F)
where
F: FnMut(JsValue) -> JsValue,
{
let Some(ptr) = typed_array_data_ptr(obj) else {
return;
};
if ptr.is_null() {
return;
}
unsafe {
(*ptr).buffer = rewrite((*ptr).buffer);
}
}
pub(crate) fn drop_typed_array_native(obj: &mut JsObject) -> u64 {
let Some(ptr) = typed_array_data_ptr(obj) else {
return 0;
};
if ptr.is_null() {
return 0;
}
unsafe { drop(Box::from_raw(ptr)) };
obj.set_native_fn(None);
std::mem::size_of::<TypedArrayData>() as u64
}
pub(crate) fn get_typed_array_data(vm: &mut Vm, this_val: JsValue) -> Result<TypedArrayData, JsValue> {
if !this_val.is_object() {
return Err(type_error(vm, "TypedArray method called on incompatible receiver"));
}
let obj_ptr = this_val.as_js_object_ptr();
if obj_ptr.is_null() {
return Err(type_error(vm, "TypedArray internal state invalid"));
}
let obj = unsafe { &*obj_ptr };
if !obj.is_typed_array_obj() {
return Err(type_error(vm, "TypedArray method called on incompatible receiver"));
}
let Some(data_ptr) = obj.native_fn() else {
return Err(type_error(vm, "TypedArray internal state invalid"));
};
let data_ptr = data_ptr.as_ptr() as *const TypedArrayData;
if data_ptr.is_null() {
return Err(type_error(vm, "TypedArray internal state invalid"));
}
Ok(unsafe { *data_ptr })
}
fn absolute_byte_offset(view: TypedArrayData, index: usize) -> usize {
view.byte_offset + index * view.kind.bytes_per_element()
}
fn read_element(kind: TypedArrayKind, bytes: &[u8], offset: usize) -> JsValue {
match kind {
TypedArrayKind::Int8 => JsValue::int(bytes[offset] as i8 as i32),
TypedArrayKind::Uint8 | TypedArrayKind::Uint8Clamped => JsValue::int(bytes[offset] as i32),
TypedArrayKind::Int16 => JsValue::int(i16::from_ne_bytes(bytes[offset..offset + 2].try_into().unwrap()) as i32),
TypedArrayKind::Uint16 => {
JsValue::int(u16::from_ne_bytes(bytes[offset..offset + 2].try_into().unwrap()) as i32)
}
TypedArrayKind::Int32 => JsValue::int(i32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap())),
TypedArrayKind::Uint32 => {
JsValue::float(u32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap()) as f64)
}
TypedArrayKind::Float32 => {
JsValue::float(f32::from_ne_bytes(bytes[offset..offset + 4].try_into().unwrap()) as f64)
}
TypedArrayKind::Float64 => JsValue::float(f64::from_ne_bytes(bytes[offset..offset + 8].try_into().unwrap())),
TypedArrayKind::BigInt64 => {
JsValue::float(i64::from_ne_bytes(bytes[offset..offset + 8].try_into().unwrap()) as f64)
}
TypedArrayKind::BigUint64 => {
JsValue::float(u64::from_ne_bytes(bytes[offset..offset + 8].try_into().unwrap()) as f64)
}
}
}
fn numeric_value(_vm: &mut Vm, value: JsValue) -> f64 {
coercion::to_number(value)
}
fn write_element(kind: TypedArrayKind, bytes: &mut [u8], offset: usize, value: f64) {
match kind {
TypedArrayKind::Int8 => bytes[offset] = value as i32 as u8 as i8 as u8,
TypedArrayKind::Uint8 => bytes[offset] = value as i32 as u8,
TypedArrayKind::Uint8Clamped => bytes[offset] = value.clamp(0.0, 255.0).round() as u8,
TypedArrayKind::Int16 => bytes[offset..offset + 2].copy_from_slice(&(value as i32 as u16 as i16).to_ne_bytes()),
TypedArrayKind::Uint16 => bytes[offset..offset + 2].copy_from_slice(&(value as i32 as u16).to_ne_bytes()),
TypedArrayKind::Int32 => bytes[offset..offset + 4].copy_from_slice(&(value as i32).to_ne_bytes()),
TypedArrayKind::Uint32 => bytes[offset..offset + 4].copy_from_slice(&(value as u32).to_ne_bytes()),
TypedArrayKind::Float32 => bytes[offset..offset + 4].copy_from_slice(&(value as f32).to_ne_bytes()),
TypedArrayKind::Float64 => bytes[offset..offset + 8].copy_from_slice(&value.to_ne_bytes()),
TypedArrayKind::BigInt64 => bytes[offset..offset + 8].copy_from_slice(&(value as i64).to_ne_bytes()),
TypedArrayKind::BigUint64 => bytes[offset..offset + 8].copy_from_slice(&(value as u64).to_ne_bytes()),
}
}
fn collect_array_like(vm: &mut Vm, value: JsValue) -> Result<Vec<JsValue>, JsValue> {
if !value.is_object() {
return Err(type_error(vm, "TypedArray source must be array-like"));
}
let ptr = value.as_js_object_ptr();
if ptr.is_null() {
return Err(type_error(vm, "TypedArray source must be array-like"));
}
let obj = unsafe { &*ptr };
if obj.is_array() {
let len = obj.prop_count() as usize;
return Ok((0..len).map(|i| obj.get_prop_at(i)).collect());
}
if obj.is_typed_array_obj() {
let view = get_typed_array_data(vm, value)?;
let buffer_ptr = array_buffer_data_ptr(vm, view.buffer)?;
let buffer = unsafe { &*buffer_ptr };
return Ok((0..view.length)
.map(|i| read_element(view.kind, buffer, absolute_byte_offset(view, i)))
.collect());
}
Err(type_error(vm, "TypedArray source must be Array, ArrayBuffer, or TypedArray"))
}
fn typed_array_new(vm: &mut Vm, args: &[u8], kind: TypedArrayKind) -> NativeResult {
let first = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::int(0) };
let bpe = kind.bytes_per_element();
let (buffer, byte_offset, length) = if first.is_int() || first.is_double() || first.is_undefined() {
let len = native_try!(to_index(vm, first, "invalid TypedArray length"));
let Some(byte_len) = len.checked_mul(bpe) else {
return NativeResult::Err(range_error(vm, "invalid TypedArray length"));
};
if byte_len > MAX_ARRAY_BUFFER_LENGTH {
return NativeResult::Err(range_error(vm, "invalid TypedArray length"));
}
let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; byte_len]));
(buffer, 0, len)
} else if first.is_object() {
let first_ptr = first.as_js_object_ptr();
let first_obj = unsafe { &*first_ptr };
if first_obj.is_array_buffer_obj() {
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, first));
let buffer_len = unsafe { (*buffer_ptr).len() };
let byte_offset = if args.len() > 2 {
native_try!(to_index(vm, vm.reg(args[2]), "TypedArray byteOffset out of bounds"))
} else {
0
};
if byte_offset > buffer_len || byte_offset % bpe != 0 {
return NativeResult::Err(range_error(vm, "TypedArray byteOffset out of bounds"));
}
let remaining = buffer_len - byte_offset;
let length = if args.len() > 3 {
native_try!(to_index(vm, vm.reg(args[3]), "TypedArray length out of bounds"))
} else {
remaining / bpe
};
let Some(byte_length) = length.checked_mul(bpe) else {
return NativeResult::Err(range_error(vm, "TypedArray length out of bounds"));
};
if byte_length > remaining {
return NativeResult::Err(range_error(vm, "TypedArray length out of bounds"));
}
(first, byte_offset, length)
} else {
let values = native_try!(collect_array_like(vm, first));
let byte_len = values.len().saturating_mul(bpe);
let buffer = JsValue::from_js_object(new_array_buffer(vm, vec![0; byte_len]));
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, buffer));
let buffer_ref = unsafe { &mut *buffer_ptr };
for (idx, value) in values.into_iter().enumerate() {
let n = numeric_value(vm, value);
write_element(kind, buffer_ref, idx * bpe, n);
}
(buffer, 0, byte_len / bpe)
}
} else {
return NativeResult::Err(type_error(vm, "invalid TypedArray constructor argument"));
};
NativeResult::Ok(JsValue::from_js_object(create_typed_array(vm, kind, buffer, byte_offset, length)))
}
macro_rules! typed_array_ctor {
($name:ident, $kind:ident) => {
pub fn $name(vm: &mut Vm, args: &[u8]) -> NativeResult {
typed_array_new(vm, args, TypedArrayKind::$kind)
}
};
}
typed_array_ctor!(int8array_constructor, Int8);
typed_array_ctor!(uint8array_constructor, Uint8);
typed_array_ctor!(uint8clampedarray_constructor, Uint8Clamped);
typed_array_ctor!(int16array_constructor, Int16);
typed_array_ctor!(uint16array_constructor, Uint16);
typed_array_ctor!(int32array_constructor, Int32);
typed_array_ctor!(uint32array_constructor, Uint32);
typed_array_ctor!(float32array_constructor, Float32);
typed_array_ctor!(float64array_constructor, Float64);
typed_array_ctor!(bigint64array_constructor, BigInt64);
typed_array_ctor!(biguint64array_constructor, BigUint64);
pub fn typed_array_at(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = native_try!(get_typed_array_data(vm, this_val));
let raw_index = if args.len() > 1 {
numeric_value(vm, vm.reg(args[1])).trunc() as isize
} else {
0
};
let idx = if raw_index < 0 { view.length as isize + raw_index } else { raw_index };
if idx < 0 || idx as usize >= view.length {
return NativeResult::Ok(JsValue::undefined());
}
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, view.buffer));
let buffer = unsafe { &*buffer_ptr };
NativeResult::Ok(read_element(view.kind, buffer, absolute_byte_offset(view, idx as usize)))
}
pub fn typed_array_fill(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = native_try!(get_typed_array_data(vm, this_val));
let value = if args.len() > 1 { vm.reg(args[1]) } else { JsValue::undefined() };
let start = if args.len() > 2 {
normalize_index(vm, vm.reg(args[2]), view.length)
} else {
0
};
let end = if args.len() > 3 {
normalize_index(vm, vm.reg(args[3]), view.length)
} else {
view.length
};
let n = numeric_value(vm, value);
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, view.buffer));
let buffer = unsafe { &mut *buffer_ptr };
for idx in start..end.max(start) {
write_element(view.kind, buffer, absolute_byte_offset(view, idx), n);
}
NativeResult::Ok(this_val)
}
pub fn typed_array_slice(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = native_try!(get_typed_array_data(vm, this_val));
let start = if args.len() > 1 {
normalize_index(vm, vm.reg(args[1]), view.length)
} else {
0
};
let end = if args.len() > 2 {
normalize_index(vm, vm.reg(args[2]), view.length)
} else {
view.length
};
let count = end.max(start).saturating_sub(start);
let bpe = view.kind.bytes_per_element();
let src_ptr = native_try!(array_buffer_data_ptr(vm, view.buffer));
let src = unsafe { &*src_ptr };
let mut out = vec![0u8; count * bpe];
let src_start = absolute_byte_offset(view, start);
let src_end = src_start + count * bpe;
out.copy_from_slice(&src[src_start..src_end]);
let buffer = JsValue::from_js_object(new_array_buffer(vm, out));
NativeResult::Ok(JsValue::from_js_object(create_typed_array(vm, view.kind, buffer, 0, count)))
}
pub fn typed_array_subarray(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = native_try!(get_typed_array_data(vm, this_val));
let start = if args.len() > 1 {
normalize_index(vm, vm.reg(args[1]), view.length)
} else {
0
};
let end = if args.len() > 2 {
normalize_index(vm, vm.reg(args[2]), view.length)
} else {
view.length
};
let count = end.max(start).saturating_sub(start);
let byte_offset = view.byte_offset + start * view.kind.bytes_per_element();
NativeResult::Ok(JsValue::from_js_object(create_typed_array(
vm,
view.kind,
view.buffer,
byte_offset,
count,
)))
}
pub fn typed_array_set(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
let view = native_try!(get_typed_array_data(vm, this_val));
if args.len() < 2 {
return NativeResult::Err(type_error(vm, "TypedArray.set source required"));
}
let source = vm.reg(args[1]);
let offset = if args.len() > 2 {
native_try!(to_index(vm, vm.reg(args[2]), "TypedArray.set offset out of bounds"))
} else {
0
};
let values = native_try!(collect_array_like(vm, source));
if offset > view.length || values.len() > view.length - offset {
return NativeResult::Err(range_error(vm, "TypedArray.set offset out of bounds"));
}
let numbers: Vec<f64> = values.into_iter().map(|v| numeric_value(vm, v)).collect();
let buffer_ptr = native_try!(array_buffer_data_ptr(vm, view.buffer));
let buffer = unsafe { &mut *buffer_ptr };
for (i, n) in numbers.into_iter().enumerate() {
write_element(view.kind, buffer, absolute_byte_offset(view, offset + i), n);
}
NativeResult::Ok(JsValue::undefined())
}
pub fn typed_array_to_string(vm: &mut Vm, args: &[u8]) -> NativeResult {
let this_val = vm.reg(if args.is_empty() { 0 } else { args[0] });
native_try!(get_typed_array_data(vm, this_val));
NativeResult::Ok(vm.new_string("[object TypedArray]"))
}
-343
View File
@@ -1,343 +0,0 @@
use crate::vm::Vm;
use oxide_types::object::JsObject;
use oxide_types::shape::EMPTY_SHAPE_ID;
use oxide_types::value::JsValue;
/// Borrow a string value's content. Caller must hold a valid string `JsValue`.
///
/// # Safety
/// `val` must be a string `JsValue` whose `JsString` pointer is alive (session or
/// permanent string) — the same contract as object-pointer derefs in the VM.
#[inline]
unsafe fn string_data(val: JsValue) -> &'static str {
(*val.as_string_ptr()).as_str()
}
/// Content equality of two string values: pointer identity → hash+len reject →
/// byte compare. Replaces the old interner-index compare.
#[inline]
fn string_value_eq(a: JsValue, b: JsValue) -> bool {
if a.as_string_ptr() == b.as_string_ptr() {
return true;
}
// SAFETY: both are string JsValues; their JsString pointers are valid.
let sa = unsafe { &*a.as_string_ptr() };
let sb = unsafe { &*b.as_string_ptr() };
sa.hash == sb.hash && sa.data == sb.data
}
pub fn to_number(val: JsValue) -> f64 {
if val.is_int() {
return val.as_int() as f64;
}
if val.is_double() {
return val.as_double();
}
if val.is_bool() {
return if val.as_bool() { 1.0 } else { 0.0 };
}
if val.is_null() {
return 0.0;
}
if val.is_undefined() {
return f64::NAN;
}
if val.is_string() {
// SAFETY: val is a string value.
let s = unsafe { string_data(val) };
return s.parse::<f64>().unwrap_or(f64::NAN);
}
if val.is_object() {
return f64::NAN;
}
f64::NAN
}
pub fn to_uint32(val: JsValue) -> u32 {
let n = to_number(val);
if n == 0.0 || !n.is_finite() {
return 0;
}
n.trunc().rem_euclid(4_294_967_296.0) as u32
}
pub fn to_int32(val: JsValue) -> i32 {
let int = to_uint32(val);
if int > i32::MAX as u32 {
(int as i64 - 4_294_967_296i64) as i32
} else {
int as i32
}
}
pub fn to_string(val: JsValue) -> String {
if val.is_int() {
return val.as_int().to_string();
}
if val.is_double() {
let d = val.as_double();
if d.is_nan() {
return "NaN".to_string();
}
if d.is_infinite() {
return if d.is_sign_positive() {
"Infinity".to_string()
} else {
"-Infinity".to_string()
};
}
if d.is_finite() && d.fract() == 0.0 {
return (d as i64).to_string();
}
let mut buf = ryu::Buffer::new();
return buf.format(d).to_string();
}
if val.is_bool() {
return val.as_bool().to_string();
}
if val.is_null() {
return "null".to_string();
}
if val.is_undefined() {
return "undefined".to_string();
}
if val.is_string() {
// SAFETY: val is a string value.
return unsafe { string_data(val) }.to_string();
}
if val.is_object() {
return "[object]".to_string();
}
String::new()
}
pub fn to_boolean(val: JsValue) -> bool {
if val.is_undefined() || val.is_null() {
return false;
}
if val.is_bool() {
return val.as_bool();
}
if val.is_int() {
return val.as_int() != 0;
}
if val.is_double() {
let d = val.as_double();
return !(d == 0.0 || d == -0.0 || d.is_nan());
}
if val.is_string() {
// SAFETY: val is a string value.
return !unsafe { (*val.as_string_ptr()).is_empty() };
}
if val.is_object() {
return true;
}
if val.is_symbol() {
return true;
}
false
}
pub fn abstract_eq(lhs: JsValue, rhs: JsValue) -> bool {
if lhs.is_null() && rhs.is_undefined() {
return true;
}
if lhs.is_undefined() && rhs.is_null() {
return true;
}
if lhs.is_string() && rhs.is_string() {
return string_value_eq(lhs, rhs);
}
if lhs.is_int() && rhs.is_int() {
return lhs.as_int() == rhs.as_int();
}
if lhs.is_double() && rhs.is_double() {
return strict_double_eq(lhs.as_double(), rhs.as_double());
}
if lhs.is_bool() && rhs.is_bool() {
return lhs.as_bool() == rhs.as_bool();
}
if lhs.is_null() && rhs.is_null() {
return true;
}
if lhs.is_undefined() && rhs.is_undefined() {
return true;
}
if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) {
return strict_double_eq(to_number(lhs), to_number(rhs));
}
if (lhs.is_int() || lhs.is_double()) && rhs.is_bool() {
return to_number(lhs) == to_number(rhs);
}
if lhs.is_bool() && (rhs.is_int() || rhs.is_double()) {
return to_number(lhs) == to_number(rhs);
}
if lhs.is_bool() || rhs.is_bool() {
return to_number(lhs) == to_number(rhs);
}
if lhs.is_null() || rhs.is_null() {
return false;
}
if lhs.is_object() || rhs.is_object() {
return lhs.is_object() && rhs.is_object() && lhs.as_ptr() == rhs.as_ptr();
}
false
}
pub fn strict_eq(lhs: JsValue, rhs: JsValue) -> bool {
if lhs.is_int() && rhs.is_int() {
return lhs.as_int() == rhs.as_int();
}
if lhs.is_double() && rhs.is_double() {
return strict_double_eq(lhs.as_double(), rhs.as_double());
}
if lhs.is_bool() && rhs.is_bool() {
return lhs.as_bool() == rhs.as_bool();
}
if lhs.is_null() && rhs.is_null() {
return true;
}
if lhs.is_undefined() && rhs.is_undefined() {
return true;
}
if lhs.is_string() && rhs.is_string() {
return string_value_eq(lhs, rhs);
}
if lhs.is_object() && rhs.is_object() {
return lhs.as_ptr() == rhs.as_ptr();
}
if lhs.is_symbol() && rhs.is_symbol() {
return lhs.as_symbol_index() == rhs.as_symbol_index();
}
false
}
fn strict_double_eq(a: f64, b: f64) -> bool {
if a.is_nan() || b.is_nan() {
return false;
}
a == b
}
pub fn relational_compare(lhs: JsValue, rhs: JsValue) -> Option<bool> {
if lhs.is_string() && rhs.is_string() {
// SAFETY: both are string values.
let ls = unsafe { string_data(lhs) };
let rs = unsafe { string_data(rhs) };
return Some(ls < rs);
}
let l = to_number(lhs);
let r = to_number(rhs);
if l.is_nan() || r.is_nan() {
return None;
}
l.partial_cmp(&r).map(|o| o.is_lt())
}
pub fn string_concat(lhs: &str, rhs: &str) -> String {
let mut s = String::with_capacity(lhs.len() + rhs.len());
s.push_str(lhs);
s.push_str(rhs);
s
}
fn to_f64(val: JsValue) -> f64 {
if val.is_int() {
val.as_int() as f64
} else if val.is_double() {
val.as_double()
} else {
f64::NAN
}
}
pub fn same_value(lhs: JsValue, rhs: JsValue) -> bool {
if lhs.is_double() && rhs.is_double() {
let a = lhs.as_double();
let b = rhs.as_double();
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
let a_neg = a.is_sign_negative();
let b_neg = b.is_sign_negative();
return a_neg == b_neg;
}
return a == b;
}
if lhs.is_int() && rhs.is_int() {
return lhs.as_int() == rhs.as_int();
}
if (lhs.is_int() || lhs.is_double()) && (rhs.is_int() || rhs.is_double()) {
let a = to_f64(lhs);
let b = to_f64(rhs);
if a.is_nan() && b.is_nan() {
return true;
}
if a == 0.0 && b == 0.0 {
return a.is_sign_negative() == b.is_sign_negative();
}
return a == b;
}
if lhs.is_bool() && rhs.is_bool() {
return lhs.as_bool() == rhs.as_bool();
}
if lhs.is_null() && rhs.is_null() {
return true;
}
if lhs.is_undefined() && rhs.is_undefined() {
return true;
}
if lhs.is_string() && rhs.is_string() {
return string_value_eq(lhs, rhs);
}
if lhs.is_object() && rhs.is_object() {
return lhs.as_ptr() == rhs.as_ptr();
}
if lhs.is_symbol() && rhs.is_symbol() {
return lhs.as_symbol_index() == rhs.as_symbol_index();
}
false
}
pub fn strict_equality(lhs: JsValue, rhs: JsValue) -> bool {
if lhs.is_double() && rhs.is_double() {
return strict_double_eq(lhs.as_double(), rhs.as_double());
}
same_value(lhs, rhs)
}
pub fn to_object(val: JsValue, vm: &mut Vm) -> Result<JsValue, String> {
if val.is_object() {
return Ok(val);
}
if val.is_null() || val.is_undefined() {
return Err(vm.error_message_text("TypeError", "Cannot convert null or undefined to object"));
}
let (proto_ptr, type_tag) = if val.is_string() {
(
vm.session().builtin_world().string_proto.as_ptr() as *mut JsObject,
JsObject::OBJ_TYPE_STRING_OBJ,
)
} else if val.is_int() || val.is_double() {
(
vm.session().builtin_world().number_proto.as_ptr() as *mut JsObject,
JsObject::OBJ_TYPE_NUMBER_OBJ,
)
} else if val.is_bool() {
(
vm.session().builtin_world().boolean_proto.as_ptr() as *mut JsObject,
JsObject::OBJ_TYPE_BOOLEAN_OBJ,
)
} else {
(&*vm.object_prototype as *const JsObject as *mut JsObject, JsObject::OBJ_TYPE_PLAIN)
};
let proto_val = JsValue::from_js_object(proto_ptr);
let obj = vm.alloc_object(JsObject::new_empty(EMPTY_SHAPE_ID, proto_val));
let obj_val = JsValue::from_js_object(obj);
let obj_ref = unsafe { &mut *obj };
obj_ref.type_tag = type_tag;
obj_ref.ensure_hash_props().push(val);
obj_ref.set_prop_count(1);
Ok(obj_val)
}
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
impl Vm {
+1 -1
View File
@@ -54,7 +54,7 @@ impl Vm {
format!("{err_val}")
};
builtins_debug!("native_call err={}", msg);
(crate::builtins::error::create_error(self, &msg), "Error")
(oxide_builtins::error::create_error(self, &msg), "Error")
};
self.exception_value = Some(error);
self.pending_error_kind = Some(kind);
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
impl Vm {
+1 -1
View File
@@ -1,6 +1,6 @@
use crate::coercion;
use crate::vm::Vm;
use oxide_bytecode::opcode::{self, Instr};
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
impl Vm {
+1 -1
View File
@@ -2,8 +2,8 @@ use oxide_bytecode::opcode::OpCode;
use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use crate::coercion;
use crate::vm::Vm;
use oxide_runtime_api as coercion;
impl Vm {
pub(crate) fn dispatch_member_op(&mut self, op: OpCode, rd: usize, a: usize, b: usize) -> Result<(), String> {
-2
View File
@@ -1,8 +1,6 @@
#![doc = "OxideJS - Register-based VM with epoch arena memory"]
pub mod bindings;
pub mod builtins;
pub mod coercion;
mod dispatch;
mod ic_helper;
pub mod native;
+1 -1
View File
@@ -4,8 +4,8 @@ use oxide_types::object::JsObject;
use oxide_types::value::JsValue;
use rustc_hash::FxBuildHasher;
use crate::builtins::{data_view, map, set, typed_array};
use crate::vm::Vm;
use oxide_builtins::{data_view, map, set, typed_array};
impl Vm {
pub(crate) fn is_session_escape_root_ptr(&self, target_ptr: *mut JsObject) -> bool {
+2 -2
View File
@@ -7,8 +7,8 @@ use oxide_types::object::{JsObject, JsString, PropMetaEntry};
use oxide_types::value::JsValue;
use rustc_hash::FxBuildHasher;
use crate::builtins::{array_buffer, data_view, map, regexp, set, typed_array};
use crate::vm::Vm;
use oxide_builtins::{array_buffer, data_view, map, regexp, set, typed_array};
pub struct SessionGc {
pub total_collections: u64,
@@ -502,8 +502,8 @@ mod tests {
use oxide_types::object::JsObject;
use super::*;
use crate::builtins::{array_buffer, data_view, map, set, typed_array};
use crate::vm::{CallFrame, FrameContinuation};
use oxide_builtins::{array_buffer, data_view, map, set, typed_array};
use oxide_runtime_api::NativeResult;
fn plain_object(vm: &mut Vm) -> *mut JsObject {
+121 -3
View File
@@ -7,15 +7,15 @@ use oxide_bytecode::opcode::{self, OpCode};
use smallvec::SmallVec;
pub use crate::bindings::init_kernel_builtins;
use crate::coercion;
use crate::native::NativeFn;
use crate::session_gc::SessionGc;
use crate::vm_state::{GcState, IterState, ProfilingState, SymbolState};
use oxide_kernel::kernel::{KernelCore, KernelSession};
use oxide_runtime_api as coercion;
use oxide_runtime_api::NativeResult;
use oxide_types::error::{JsError, JsErrorKind};
use oxide_types::mem::{Epoch, P};
use oxide_types::object::{JsObject, NativeFnPtr};
use oxide_types::object::{JsObject, NativeFnPtr, PropAttributes};
use oxide_types::value::JsValue;
pub(crate) const MAX_PROTO_CHAIN_DEPTH: usize = 1024;
@@ -408,7 +408,7 @@ impl Vm {
pub(crate) fn raise_js_error(&mut self, err: JsError) -> Result<(), String> {
let kind = js_error_kind_name(err.kind);
let error = crate::builtins::error::create_kind_error(self, kind, &err.message);
let error = oxide_builtins::error::create_kind_error(self, kind, &err.message);
self.exception_value = Some(error);
self.pending_error_kind = Some(kind);
self.unwind()
@@ -1457,3 +1457,121 @@ mod tests {
assert_eq!(vm.reg(255), JsValue::int(13));
}
}
impl oxide_runtime_api::VmHost for Vm {
fn reg(&self, idx: u8) -> JsValue {
self.reg(idx)
}
fn set_reg(&mut self, idx: u8, val: JsValue) {
self.set_reg(idx, val);
}
fn alloc_object(&mut self, obj: JsObject) -> *mut JsObject {
self.alloc_object(obj)
}
fn new_string(&mut self, s: &str) -> JsValue {
self.new_string(s)
}
fn kernel_core(&self) -> &Arc<KernelCore> {
self.kernel_core()
}
fn session(&self) -> &KernelSession {
self.session()
}
fn epoch(&self) -> &Epoch {
self.epoch()
}
fn property_key_si(&mut self, val: JsValue) -> u32 {
self.property_key_si(val)
}
fn resolve_property(&self, obj: &JsObject, prop_name_si: u32) -> Option<JsValue> {
self.resolve_property(obj, prop_name_si)
}
fn get_own_property_slot(&self, obj: &JsObject, prop_name_si: u32) -> Option<u32> {
self.get_own_property_slot(obj, prop_name_si)
}
fn ordinary_get(&mut self, obj: &JsObject, prop_name_si: u32, receiver: JsValue) -> Result<JsValue, String> {
self.ordinary_get(obj, prop_name_si, receiver)
}
fn ordinary_set(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, receiver: JsValue,
) -> Result<(), String> {
self.ordinary_set(obj, prop_name_si, val, receiver)
}
fn define_data_property(
&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue, attributes: PropAttributes,
) -> Result<(), String> {
self.define_data_property(obj, prop_name_si, val, attributes)
}
fn define_accessor_property(
&mut self, obj: &mut JsObject, prop_name_si: u32, get: JsValue, set: JsValue, attributes: PropAttributes,
) -> Result<(), String> {
self.define_accessor_property(obj, prop_name_si, get, set, attributes)
}
fn set_or_create_prop_value(&mut self, obj: &mut JsObject, prop_name_si: u32, val: JsValue) {
self.set_or_create_prop_value(obj, prop_name_si, val)
}
fn lookup_str(&self, val: JsValue) -> Option<String> {
self.lookup_str(val)
}
fn coerce_primitive_bounded(&mut self, value: JsValue, prefer_string: bool) -> Result<JsValue, String> {
self.coerce_primitive_bounded(value, prefer_string)
}
fn coerce_number_bounded(&mut self, value: JsValue) -> Result<f64, String> {
self.coerce_number_bounded(value)
}
fn call_function_sync(&mut self, callee: JsValue, receiver: JsValue, args: &[JsValue]) -> Result<JsValue, String> {
self.call_function_sync(callee, receiver, args)
}
fn checked_object_ptr(&mut self, val: JsValue, error_msg: &str) -> Result<Option<*mut JsObject>, String> {
self.checked_object_ptr(val, error_msg)
}
fn raise_type_error(&mut self, msg: &str) -> Result<(), String> {
self.raise_type_error(msg)
}
fn error_message_text(&self, kind: &str, msg: &str) -> String {
self.error_message_text(kind, msg)
}
fn call_stack_function_names(&self) -> Vec<String> {
self.frames
.iter()
.rev()
.map(|f| {
self.kernel_core
.perm_interner()
.lookup(f.function_name)
.unwrap_or("<anonymous>")
.to_string()
})
.collect()
}
fn promote_if_needed_for_write_ptr(&mut self, target_ptr: *mut JsObject, value: JsValue) -> JsValue {
self.promote_if_needed_for_write_ptr(target_ptr, value)
}
fn step_rng(&mut self) {
self.step_rng()
}
fn math_rng_value(&self) -> f64 {
self.math_rng_value()
}
fn sub_module_function_name(&self, sub_idx: u16) -> String {
self.sub_modules
.get(sub_idx as usize)
.and_then(|m| m.function_name.clone())
.unwrap_or_default()
}
fn symbol_intern(&mut self, desc: String) -> u32 {
self.symbols.intern(desc)
}
fn symbol_description(&self, idx: u32) -> Option<&str> {
self.symbols.description(idx)
}
fn symbol_lookup_global(&self, key: &str) -> Option<u32> {
self.symbols.lookup_global(key)
}
fn symbol_register_global(&mut self, key: String, idx: u32) {
self.symbols.register_global(key, idx)
}
fn symbol_key_for_id(&self, idx: u32) -> Option<String> {
self.symbols.key_for_id(idx)
}
}
+4 -4
View File
@@ -2,7 +2,7 @@ use std::sync::Arc;
use crate::native::NativeFn;
use crate::vm::{native_fn_ptr_to_fn, CallFrame, ForInIter, FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
use oxide_runtime_api::NativeResult;
use oxide_runtime_api::{to_boolean, NativeResult};
use oxide_types::object::{JsObject, PropAttributes};
use oxide_types::private_key::is_private_name_key;
use oxide_types::value::JsValue;
@@ -155,7 +155,7 @@ impl Vm {
Ok(true)
} else {
let error =
crate::builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported");
oxide_builtins::error::create_error(self, "NEW_EXPRESSION: bytecode constructors not yet supported");
self.exception_value = Some(error);
self.pending_error_kind = Some(self.thrown_error_kind(error));
self.unwind().map(|_| true)
@@ -344,7 +344,7 @@ impl Vm {
pub(crate) fn dispatch_for_of_init(&mut self, a: usize) -> Result<(), String> {
let iterable = self.regs[a];
match crate::builtins::iterator::make_iterator_for_value(self, iterable) {
match oxide_builtins::iterator::make_iterator_for_value(self, iterable) {
Ok(iterator) => {
self.iters.push_for_of(iterator);
self.iters.clear_last_for_of_result();
@@ -377,7 +377,7 @@ impl Vm {
let result_obj = unsafe { &*result.as_js_object_ptr() };
let done_si = self.kernel_core.perm_interner().intern("done").0;
let done_val = self.ordinary_get(result_obj, done_si, result)?;
let done = crate::coercion::to_boolean(done_val);
let done = to_boolean(done_val);
self.iters.set_last_for_of_result(result);
self.regs[rd] = JsValue::bool(!done);
Ok(())
+1 -1
View File
@@ -1,5 +1,5 @@
use crate::coercion;
use crate::vm::{FrameContinuation, Vm, MAX_PROTO_CHAIN_DEPTH};
use oxide_runtime_api as coercion;
use oxide_types::object::{JsObject, PropAttributes, PropMetaEntry};
use oxide_types::value::JsValue;
@@ -1,6 +1,6 @@
use oxide_builtins::array::{array_constructor, array_push};
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::builtins::array::{array_constructor, array_push};
use oxide_vm::vm::Vm;
fn eval(source: &str) -> Result<(Vm, JsValue), String> {
@@ -1,7 +1,7 @@
use oxide_builtins::boolean::{boolean_constructor, boolean_prototype_to_string, boolean_prototype_value_of};
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::vm::Vm;
use oxide_kernel::shape_forge::EMPTY_SHAPE_ID;
@@ -1,6 +1,6 @@
use oxide_builtins::error;
use oxide_compiler::compiler::Compiler;
use oxide_types::value::JsValue;
use oxide_vm::builtins::error;
use oxide_vm::vm::Vm;
fn make_vm() -> Vm {
+1 -1
View File
@@ -1,6 +1,6 @@
use oxide_builtins::map::{map_clear, map_constructor as new_map, map_delete, map_get, map_has, map_set, map_size};
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::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
+1 -1
View File
@@ -1,6 +1,6 @@
use oxide_builtins::set::{set_add, set_clear, set_constructor as new_set, set_delete, set_has, set_size};
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::vm::Vm;
fn eval(vm: &mut Vm, source: &str) -> Result<JsValue, String> {
+1 -1
View File
@@ -1,7 +1,7 @@
use oxide_compiler::compiler::Compiler;
use oxide_parser::Allocator;
use oxide_runtime_api as coercion;
use oxide_types::value::JsValue;
use oxide_vm::coercion;
use oxide_vm::vm::Vm;
fn eval(source: &str) -> String {