diff --git a/crates/js/src/builtins.rs b/crates/js/src/builtins.rs index 8fd7348..729abb9 100644 --- a/crates/js/src/builtins.rs +++ b/crates/js/src/builtins.rs @@ -118,6 +118,14 @@ fn set_array_length(gc: &mut Gc, obj: GcRef, len: usize) { } } +/// Check whether an object has a "length" property (i.e. is array-like). +fn array_length_exists(gc: &Gc, obj: GcRef) -> bool { + match gc.get(obj) { + Some(HeapObject::Object(data)) => data.properties.contains_key("length"), + _ => false, + } +} + /// Get an element by index from an array-like object. fn array_get(gc: &Gc, obj: GcRef, idx: usize) -> Value { match gc.get(obj) { @@ -180,7 +188,7 @@ pub fn init_builtins(vm: &mut Vm) { vm.set_global("Array", Value::Function(arr_ctor)); // Create and register Error constructors. - init_error_constructors(vm, obj_proto, err_proto); + init_error_constructors(vm, err_proto); // Register global utility functions. init_global_functions(vm); @@ -1057,7 +1065,8 @@ fn array_concat(args: &[Value], ctx: &mut NativeContext) -> Result { let arg_len = array_length(ctx.gc, *r); - if arg_len > 0 { + // Only spread array-like objects (those with a length property). + if array_length_exists(ctx.gc, *r) { for i in 0..arg_len { items.push(array_get(ctx.gc, *r, i)); } @@ -1307,48 +1316,43 @@ fn init_error_prototype(gc: &mut Gc, proto: GcRef) { set_builtin_prop(gc, proto, "toString", Value::Function(to_string)); } -fn init_error_constructors(vm: &mut Vm, obj_proto: GcRef, err_proto: GcRef) { +fn init_error_constructors(vm: &mut Vm, err_proto: GcRef) { // Base Error. let error_ctor = make_error_constructor(&mut vm.gc, "Error", err_proto); vm.set_global("Error", Value::Function(error_ctor)); // TypeError. - let te_proto = make_error_subclass_proto(&mut vm.gc, "TypeError", err_proto, obj_proto); + let te_proto = make_error_subclass_proto(&mut vm.gc, "TypeError", err_proto); let te_ctor = make_error_constructor(&mut vm.gc, "TypeError", te_proto); vm.set_global("TypeError", Value::Function(te_ctor)); // ReferenceError. - let re_proto = make_error_subclass_proto(&mut vm.gc, "ReferenceError", err_proto, obj_proto); + let re_proto = make_error_subclass_proto(&mut vm.gc, "ReferenceError", err_proto); let re_ctor = make_error_constructor(&mut vm.gc, "ReferenceError", re_proto); vm.set_global("ReferenceError", Value::Function(re_ctor)); // SyntaxError. - let se_proto = make_error_subclass_proto(&mut vm.gc, "SyntaxError", err_proto, obj_proto); + let se_proto = make_error_subclass_proto(&mut vm.gc, "SyntaxError", err_proto); let se_ctor = make_error_constructor(&mut vm.gc, "SyntaxError", se_proto); vm.set_global("SyntaxError", Value::Function(se_ctor)); // RangeError. - let rae_proto = make_error_subclass_proto(&mut vm.gc, "RangeError", err_proto, obj_proto); + let rae_proto = make_error_subclass_proto(&mut vm.gc, "RangeError", err_proto); let rae_ctor = make_error_constructor(&mut vm.gc, "RangeError", rae_proto); vm.set_global("RangeError", Value::Function(rae_ctor)); // URIError. - let ue_proto = make_error_subclass_proto(&mut vm.gc, "URIError", err_proto, obj_proto); + let ue_proto = make_error_subclass_proto(&mut vm.gc, "URIError", err_proto); let ue_ctor = make_error_constructor(&mut vm.gc, "URIError", ue_proto); vm.set_global("URIError", Value::Function(ue_ctor)); // EvalError. - let ee_proto = make_error_subclass_proto(&mut vm.gc, "EvalError", err_proto, obj_proto); + let ee_proto = make_error_subclass_proto(&mut vm.gc, "EvalError", err_proto); let ee_ctor = make_error_constructor(&mut vm.gc, "EvalError", ee_proto); vm.set_global("EvalError", Value::Function(ee_ctor)); } -fn make_error_subclass_proto( - gc: &mut Gc, - name: &str, - parent_proto: GcRef, - _obj_proto: GcRef, -) -> GcRef { +fn make_error_subclass_proto(gc: &mut Gc, name: &str, parent_proto: GcRef) -> GcRef { let mut data = ObjectData::new(); data.prototype = Some(parent_proto); data.properties.insert( @@ -1430,7 +1434,7 @@ fn parse_int(args: &[Value], ctx: &mut NativeContext) -> Result