use oxide_types::mem::P; use oxide_types::object::JsObject; use oxide_types::value::JsValue; use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; use crate::string_forge::StringForge; #[macro_export] macro_rules! bind_method { ($world:expr, $target:expr, $sf:expr, $sh:expr, $name:literal, $func:path, $nargs:literal) => { let _ = $world.bind_method($target, $sh, $sf, $name, $func as *const (), $nargs); }; } #[macro_export] macro_rules! bind_methods { ($world:expr, $target:expr, $sf:expr, $sh:expr, $(($name:literal, $func:path, $nargs:literal)),* $(,)?) => { $( bind_method!($world, $target, $sf, $sh, $name, $func, $nargs); )* }; } pub struct ObjectMethods { pub keys: *const (), pub create: *const (), pub assign: *const (), pub define_property: *const (), pub get_own_property_descriptor: *const (), pub freeze: *const (), pub seal: *const (), pub prevent_extensions: *const (), pub is_frozen: *const (), pub is_sealed: *const (), pub is_extensible: *const (), pub get_own_property_names: *const (), pub define_properties: *const (), pub from_entries: *const (), pub get_prototype_of: *const (), pub has_own: *const (), pub entries: *const (), pub values: *const (), } pub struct ArrayMethods { pub push: *const (), pub pop: *const (), pub slice: *const (), pub splice: *const (), pub concat: *const (), pub join: *const (), pub index_of: *const (), pub includes: *const (), pub reverse: *const (), pub for_each: *const (), pub map: *const (), pub filter: *const (), pub reduce: *const (), pub find: *const (), pub some: *const (), pub every: *const (), pub flat: *const (), pub flat_map: *const (), pub shift: *const (), pub unshift: *const (), pub fill: *const (), pub copy_within: *const (), pub at: *const (), pub last_index_of: *const (), pub find_index: *const (), pub find_last: *const (), pub reduce_right: *const (), pub sort: *const (), } pub struct ErrorMethods { pub error: *const (), pub type_error: *const (), pub reference_error: *const (), pub range_error: *const (), pub syntax_error: *const (), pub uri_error: *const (), pub eval_error: *const (), pub to_string: *const (), pub stack: *const (), } pub struct StringMethods { pub index_of: *const (), pub includes: *const (), pub char_at: *const (), pub char_code_at: *const (), pub concat: *const (), pub slice: *const (), pub substring: *const (), pub to_upper_case: *const (), pub to_lower_case: *const (), pub trim: *const (), pub repeat: *const (), pub pad_start: *const (), pub pad_end: *const (), pub starts_with: *const (), pub ends_with: *const (), pub split: *const (), pub replace: *const (), pub match_fn: *const (), pub search: *const (), pub trim_start: *const (), pub trim_end: *const (), pub code_point_at: *const (), pub normalize: *const (), pub match_all: *const (), pub replace_all: *const (), } pub struct RegExpMethods { pub exec: *const (), pub test: *const (), pub to_string: *const (), } pub struct NumberMethods { pub is_nan: *const (), pub is_finite: *const (), pub parse_int: *const (), pub parse_float: *const (), pub to_string: *const (), pub to_fixed: *const (), pub is_integer: *const (), pub is_safe_integer: *const (), pub to_exponential: *const (), pub to_precision: *const (), pub value_of: *const (), } pub struct FunctionMethods { pub call: *const (), pub apply: *const (), pub bind: *const (), pub to_string: *const (), } pub struct BuiltinWorld { pub object_proto: P, pub array_proto: P, pub function_proto: P, pub string_proto: P, pub number_proto: P, pub boolean_proto: P, pub error_proto: P, pub symbol_proto: P, pub object_constructor: P, pub array_constructor: P, pub function_constructor: P, pub string_constructor: P, pub number_constructor: P, pub boolean_constructor: P, pub error_constructor: P, pub symbol_constructor: P, pub type_error_proto: P, pub reference_error_proto: P, pub range_error_proto: P, pub syntax_error_proto: P, pub uri_error_proto: P, pub eval_error_proto: P, pub math_object: P, pub json_object: P, pub date_constructor: P, pub date_proto: P, pub set_constructor: P, pub set_proto: P, pub map_constructor: P, pub map_proto: P, pub regexp_constructor: P, pub regexp_proto: P, pub sym_match: P, pub sym_replace: P, pub sym_search: P, pub sym_split: P, pub sym_iterator: P, } fn intern_label(string_forge: &StringForge, label: &str) -> u32 { string_forge.intern(label).0 } fn make_pair( string_forge: &StringForge, shape_forge: &ShapeForge, name: &str, si_prototype: u32, si_constructor: u32, si_name: u32, ) -> (P, P) { intern_label(string_forge, name); let mut proto = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()); let mut ctor = JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null()); let proto_shape = shape_forge.make_shape(EMPTY_SHAPE_ID, si_constructor); proto.set_shape_id(proto_shape); // proto shape: EMPTY -> "constructor" (slot 0) let ctor_shape1 = shape_forge.make_shape(EMPTY_SHAPE_ID, si_prototype); let ctor_shape2 = shape_forge.make_shape(ctor_shape1, si_name); ctor.set_shape_id(ctor_shape2); // ctor shape: EMPTY -> "prototype" (slot 0) -> "name" (slot 1) ctor.set_function(true); // Pre-allocate Vec slots so wire_ctor_proto can overwrite vec[0] later. // Proto: 1 property ("constructor"), Ctor: 2 properties ("prototype", "name"). proto .ensure_hash_props() .push(Box::new(JsValue::undefined())); // slot 0: "constructor" placeholder ctor.ensure_hash_props() .push(Box::new(JsValue::undefined())); // slot 0: "prototype" placeholder ctor.ensure_hash_props() .push(Box::new(JsValue::undefined())); // slot 1: "name" placeholder (P::new(proto), P::new(ctor)) } impl BuiltinWorld { fn fn_proto_val(&self) -> JsValue { JsValue::from_js_object(self.function_proto.as_ptr() as *mut JsObject) } pub fn new(string_forge: &StringForge, shape_forge: &ShapeForge) -> Self { let si_prototype = intern_label(string_forge, "prototype"); let si_constructor = intern_label(string_forge, "constructor"); let si_name = intern_label(string_forge, "name"); intern_label(string_forge, "length"); intern_label(string_forge, "toString"); intern_label(string_forge, "valueOf"); let (object_proto, object_constructor) = make_pair( string_forge, shape_forge, "Object", si_prototype, si_constructor, si_name, ); let (array_proto, array_constructor) = make_pair( string_forge, shape_forge, "Array", si_prototype, si_constructor, si_name, ); let (function_proto, function_constructor) = make_pair( string_forge, shape_forge, "Function", si_prototype, si_constructor, si_name, ); let (string_proto, string_constructor) = make_pair( string_forge, shape_forge, "String", si_prototype, si_constructor, si_name, ); let (number_proto, number_constructor) = make_pair( string_forge, shape_forge, "Number", si_prototype, si_constructor, si_name, ); let (boolean_proto, boolean_constructor) = make_pair( string_forge, shape_forge, "Boolean", si_prototype, si_constructor, si_name, ); let (error_proto, error_constructor) = make_pair( string_forge, shape_forge, "Error", si_prototype, si_constructor, si_name, ); let (symbol_proto, symbol_constructor) = make_pair( string_forge, shape_forge, "Symbol", si_prototype, si_constructor, si_name, ); let error_proto_val = JsValue::from_js_object(error_proto.as_ptr() as *mut JsObject); let type_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let reference_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let range_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let syntax_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let uri_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let eval_error_proto = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, error_proto_val)); let math_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let json_object = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let (date_proto, date_constructor) = make_pair( string_forge, shape_forge, "Date", si_prototype, si_constructor, si_name, ); let (set_proto, set_constructor) = make_pair( string_forge, shape_forge, "Set", si_prototype, si_constructor, si_name, ); let (map_proto, map_constructor) = make_pair( string_forge, shape_forge, "Map", si_prototype, si_constructor, si_name, ); let (regexp_proto, regexp_constructor) = make_pair( string_forge, shape_forge, "RegExp", si_prototype, si_constructor, si_name, ); let sym_match = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_replace = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_search = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_split = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); let sym_iterator = P::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); /// Overwrite placeholder slots (set up by make_pair) with real values. /// ctor.vec[0] = constructor.prototype -> proto /// proto.vec[0] = proto.constructor -> ctor fn wire_ctor_proto(ctor: &P, proto: &P) { let ctor_ptr = ctor.as_ptr() as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; let vec = ctor.ensure_hash_props(); if !vec.is_empty() { *vec[0] = JsValue::from_js_object(proto.as_ptr() as *mut JsObject); } let proto_ptr = proto.as_ptr() as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let pvec = proto.ensure_hash_props(); if !pvec.is_empty() { *pvec[0] = JsValue::from_js_object(ctor_ptr); } } wire_ctor_proto(&object_constructor, &object_proto); wire_ctor_proto(&array_constructor, &array_proto); wire_ctor_proto(&function_constructor, &function_proto); wire_ctor_proto(&string_constructor, &string_proto); wire_ctor_proto(&number_constructor, &number_proto); wire_ctor_proto(&boolean_constructor, &boolean_proto); wire_ctor_proto(&error_constructor, &error_proto); wire_ctor_proto(&symbol_constructor, &symbol_proto); wire_ctor_proto(&date_constructor, &date_proto); wire_ctor_proto(&set_constructor, &set_proto); wire_ctor_proto(&map_constructor, &map_proto); // Wire prototype chains: all constructor prototypes inherit from Object.prototype. let obj_proto_val = JsValue::from_js_object(object_proto.as_ptr() as *mut JsObject); let non_object_protos: [&P; 11] = [ &array_proto, &function_proto, &string_proto, &number_proto, &boolean_proto, &error_proto, &symbol_proto, &date_proto, &set_proto, &map_proto, ®exp_proto, ]; for proto in &non_object_protos { let p = proto.as_ptr() as *mut JsObject; unsafe { &mut *p }.set_proto(obj_proto_val).ok(); } wire_ctor_proto(®exp_constructor, ®exp_proto); Self { object_proto, array_proto, function_proto, string_proto, number_proto, boolean_proto, error_proto, symbol_proto, object_constructor, array_constructor, function_constructor, string_constructor, number_constructor, boolean_constructor, error_constructor, symbol_constructor, type_error_proto, reference_error_proto, range_error_proto, syntax_error_proto, uri_error_proto, eval_error_proto, math_object, json_object, date_constructor, date_proto, set_constructor, set_proto, map_constructor, map_proto, regexp_constructor, regexp_proto, sym_match, sym_replace, sym_search, sym_split, sym_iterator, } } pub fn bind_object_methods( &self, methods: &ObjectMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let ctor_ptr = P::as_ptr(&self.object_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; let _ = self.bind_method(ctor, shape_forge, string_forge, "keys", methods.keys, 1); let _ = self.bind_method(ctor, shape_forge, string_forge, "create", methods.create, 2); let _ = self.bind_method(ctor, shape_forge, string_forge, "assign", methods.assign, 2); let _ = self.bind_method( ctor, shape_forge, string_forge, "defineProperty", methods.define_property, 3, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "getOwnPropertyDescriptor", methods.get_own_property_descriptor, 2, ); let _ = self.bind_method(ctor, shape_forge, string_forge, "freeze", methods.freeze, 1); let _ = self.bind_method(ctor, shape_forge, string_forge, "seal", methods.seal, 1); let _ = self.bind_method( ctor, shape_forge, string_forge, "preventExtensions", methods.prevent_extensions, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "isFrozen", methods.is_frozen, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "isSealed", methods.is_sealed, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "isExtensible", methods.is_extensible, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "getOwnPropertyNames", methods.get_own_property_names, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "defineProperties", methods.define_properties, 2, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "fromEntries", methods.from_entries, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "getPrototypeOf", methods.get_prototype_of, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "hasOwn", methods.has_own, 2, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "entries", methods.entries, 1, ); let _ = self.bind_method(ctor, shape_forge, string_forge, "values", methods.values, 1); } pub fn bind_array_methods( &self, methods: &ArrayMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let proto_ptr = P::as_ptr(&self.array_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let _ = self.bind_method(proto, shape_forge, string_forge, "push", methods.push, 1); let _ = self.bind_method(proto, shape_forge, string_forge, "pop", methods.pop, 0); let _ = self.bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); let _ = self.bind_method( proto, shape_forge, string_forge, "splice", methods.splice, 2, ); let _ = self.bind_method( proto, shape_forge, string_forge, "concat", methods.concat, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "join", methods.join, 1); let _ = self.bind_method( proto, shape_forge, string_forge, "indexOf", methods.index_of, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "includes", methods.includes, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "reverse", methods.reverse, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "forEach", methods.for_each, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "map", methods.map, 1); let _ = self.bind_method( proto, shape_forge, string_forge, "filter", methods.filter, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "reduce", methods.reduce, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "find", methods.find, 1); let _ = self.bind_method(proto, shape_forge, string_forge, "some", methods.some, 1); let _ = self.bind_method(proto, shape_forge, string_forge, "every", methods.every, 1); let _ = self.bind_method(proto, shape_forge, string_forge, "flat", methods.flat, 0); let _ = self.bind_method( proto, shape_forge, string_forge, "flatMap", methods.flat_map, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "shift", methods.shift, 0); let _ = self.bind_method( proto, shape_forge, string_forge, "unshift", methods.unshift, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "fill", methods.fill, 1); let _ = self.bind_method( proto, shape_forge, string_forge, "copyWithin", methods.copy_within, 2, ); let _ = self.bind_method(proto, shape_forge, string_forge, "at", methods.at, 1); let _ = self.bind_method( proto, shape_forge, string_forge, "lastIndexOf", methods.last_index_of, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "findIndex", methods.find_index, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "findLast", methods.find_last, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "reduceRight", methods.reduce_right, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "sort", methods.sort, 0); } pub fn bind_error_methods( &self, methods: &ErrorMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let ctor_ptr = P::as_ptr(&self.error_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; let _ = self.bind_method(ctor, shape_forge, string_forge, "Error", methods.error, 1); let _ = self.bind_method( ctor, shape_forge, string_forge, "TypeError", methods.type_error, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "ReferenceError", methods.reference_error, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "RangeError", methods.range_error, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "SyntaxError", methods.syntax_error, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "URIError", methods.uri_error, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "EvalError", methods.eval_error, 1, ); let proto_ptr = P::as_ptr(&self.error_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let _ = self.bind_method( proto, shape_forge, string_forge, "toString", methods.to_string, 0, ); let _ = self.bind_method(proto, shape_forge, string_forge, "stack", methods.stack, 0); } pub fn bind_string_methods( &self, methods: &StringMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let proto_ptr = P::as_ptr(&self.string_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let _ = self.bind_method( proto, shape_forge, string_forge, "indexOf", methods.index_of, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "includes", methods.includes, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "charAt", methods.char_at, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "charCodeAt", methods.char_code_at, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "concat", methods.concat, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "slice", methods.slice, 2); let _ = self.bind_method( proto, shape_forge, string_forge, "substring", methods.substring, 2, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toUpperCase", methods.to_upper_case, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toLowerCase", methods.to_lower_case, 0, ); let _ = self.bind_method(proto, shape_forge, string_forge, "trim", methods.trim, 0); let _ = self.bind_method( proto, shape_forge, string_forge, "repeat", methods.repeat, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "padStart", methods.pad_start, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "padEnd", methods.pad_end, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "startsWith", methods.starts_with, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "endsWith", methods.ends_with, 1, ); let _ = self.bind_method(proto, shape_forge, string_forge, "split", methods.split, 1); let _ = self.bind_method( proto, shape_forge, string_forge, "replace", methods.replace, 2, ); let _ = self.bind_method( proto, shape_forge, string_forge, "match", methods.match_fn, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "search", methods.search, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "trimStart", methods.trim_start, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "trimEnd", methods.trim_end, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "codePointAt", methods.code_point_at, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "normalize", methods.normalize, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "matchAll", methods.match_all, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "replaceAll", methods.replace_all, 2, ); } pub fn bind_number_methods( &self, methods: &NumberMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let ctor_ptr = P::as_ptr(&self.number_constructor) as *mut JsObject; let ctor = unsafe { &mut *ctor_ptr }; let proto_ptr = P::as_ptr(&self.number_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let _ = self.bind_method(ctor, shape_forge, string_forge, "isNaN", methods.is_nan, 1); let _ = self.bind_method( ctor, shape_forge, string_forge, "isFinite", methods.is_finite, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "parseInt", methods.parse_int, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "parseFloat", methods.parse_float, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toString", methods.to_string, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toFixed", methods.to_fixed, 0, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "isInteger", methods.is_integer, 1, ); let _ = self.bind_method( ctor, shape_forge, string_forge, "isSafeInteger", methods.is_safe_integer, 1, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toExponential", methods.to_exponential, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "toPrecision", methods.to_precision, 0, ); let _ = self.bind_method( proto, shape_forge, string_forge, "valueOf", methods.value_of, 0, ); } pub fn bind_function_methods( &self, methods: &FunctionMethods, string_forge: &StringForge, shape_forge: &ShapeForge, ) { let proto_ptr = P::as_ptr(&self.function_proto) as *mut JsObject; let proto = unsafe { &mut *proto_ptr }; let fp = self.fn_proto_val(); let _ = Self::bind_method_static( proto, shape_forge, string_forge, "call", methods.call, 1, fp, ); let _ = Self::bind_method_static( proto, shape_forge, string_forge, "apply", methods.apply, 2, fp, ); let _ = Self::bind_method_static( proto, shape_forge, string_forge, "bind", methods.bind, 1, fp, ); let _ = Self::bind_method_static( proto, shape_forge, string_forge, "toString", methods.to_string, 0, fp, ); } pub fn bind_method( &self, proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str, native_fn_ptr: *const (), arg_count: u8, ) -> Result<(), String> { Self::bind_method_static( proto, shape_forge, string_forge, method_name, native_fn_ptr, arg_count, self.fn_proto_val(), ) } pub fn bind_method_static( proto: &mut JsObject, shape_forge: &ShapeForge, string_forge: &StringForge, method_name: &str, native_fn_ptr: *const (), arg_count: u8, wrapper_proto: JsValue, ) -> Result<(), String> { let si = string_forge.intern(method_name).0; let wrapper_proto_ptr = if wrapper_proto.is_object() { wrapper_proto.as_js_object_ptr() } else { std::ptr::null_mut() }; let mut wrapper = Box::new(JsObject::new_empty(EMPTY_SHAPE_ID, JsValue::null())); if !wrapper_proto_ptr.is_null() { wrapper.set_proto(wrapper_proto).ok(); } wrapper.set_function(true); wrapper.set_native_fn(Some(native_fn_ptr)); wrapper.set_native_arg_count(arg_count); let wrapper_val = JsValue::from_js_object(Box::into_raw(wrapper)); let new_shape = shape_forge.make_shape(proto.shape_id(), si); proto.set_shape_id(new_shape); proto.ensure_hash_props().push(Box::new(wrapper_val)); proto.bump_generation(); Ok(()) } } #[cfg(test)] mod tests { use super::*; use crate::shape_forge::{ShapeForge, EMPTY_SHAPE_ID}; use crate::string_forge::StringForge; fn make_world() -> BuiltinWorld { let sf = StringForge::new(); let sh = ShapeForge::new(); BuiltinWorld::new(&sf, &sh) } #[test] fn test_all_protos_valid() { let w = make_world(); let protos = [ &w.object_proto, &w.array_proto, &w.function_proto, &w.string_proto, &w.number_proto, &w.boolean_proto, &w.error_proto, &w.symbol_proto, ]; for p in protos { assert!( p.shape_id() > EMPTY_SHAPE_ID, "proto should have a non-empty shape" ); } } #[test] fn test_all_constructors_valid() { let w = make_world(); assert!(w.object_constructor.is_function()); assert!(w.array_constructor.is_function()); assert!(w.function_constructor.is_function()); assert!(w.string_constructor.is_function()); assert!(w.number_constructor.is_function()); assert!(w.boolean_constructor.is_function()); assert!(w.error_constructor.is_function()); assert!(w.symbol_constructor.is_function()); } #[test] fn test_prototypes_are_not_functions() { let w = make_world(); assert!(!w.object_proto.is_function()); assert!(!w.array_proto.is_function()); assert!(!w.function_proto.is_function()); assert!(!w.string_proto.is_function()); assert!(!w.number_proto.is_function()); assert!(!w.boolean_proto.is_function()); assert!(!w.error_proto.is_function()); assert!(!w.symbol_proto.is_function()); } #[test] fn test_protos_have_null_proto() { let w = make_world(); // Object.prototype is the root - its __proto__ is null. assert!(w.object_proto.proto().is_null()); // All other constructor prototypes inherit from Object.prototype. assert!(w.array_proto.proto().is_object()); assert!(w.function_proto.proto().is_object()); assert!(w.string_proto.proto().is_object()); assert!(w.number_proto.proto().is_object()); assert!(w.boolean_proto.proto().is_object()); assert!(w.error_proto.proto().is_object()); assert!(w.symbol_proto.proto().is_object()); } #[test] fn test_shapes_populated() { let w = make_world(); assert!( w.object_constructor.shape_id() > EMPTY_SHAPE_ID, "constructor should have prototype + name shape" ); assert!( w.object_proto.shape_id() > EMPTY_SHAPE_ID, "prototype should have constructor shape" ); } }