diff --git a/crates/e2e/scenarios/real-web/bing.com.we b/crates/e2e/scenarios/real-web/bing.com.we index 94521f7..0012c71 100644 --- a/crates/e2e/scenarios/real-web/bing.com.we +++ b/crates/e2e/scenarios/real-web/bing.com.we @@ -38,6 +38,7 @@ cache_put bing https://assets.msn.com/bundles/v1/bingHomepage/latest/widget-init # Preact fetches the carousel JSON after hydration and then lazily resolves # the PopNow thumbnail URLs relative to https://www.bing.com/. cache_put bing https://www.bing.com/hp/api/v1/carousel?&format=json&ecount=20&efirst=0& ../../real-web/snapshots/bing.com/www.bing.com/hp/api/v1/carousel-format-json-ecount-20-efirst-0.json application/json +cache_put bing https://www.bing.com/hp/api/v1/carousel?&format=json&ecount=20&efirst=0&& ../../real-web/snapshots/bing.com/www.bing.com/hp/api/v1/carousel-format-json-ecount-20-efirst-0.json application/json cache_put bing https://www.bing.com/th?id=ONUT.154UxLdP7Uf64MeWOs1BUA&w=186&h=88&c=7&rs=2&qlt=80&pid=PopNow ../../real-web/snapshots/bing.com/www.bing.com/thumbnails/carousel-01.jpg image/jpeg cache_put bing https://www.bing.com/th?id=ONUT.t_uH-BTS6q1cglR3uHzk9Q&w=186&h=88&c=7&rs=2&qlt=80&pid=PopNow ../../real-web/snapshots/bing.com/www.bing.com/thumbnails/carousel-02.jpg image/jpeg cache_put bing https://www.bing.com/th?id=ONUT.Q4wvi6NH_-OSmq1WTCwxTQ&w=186&h=88&c=7&rs=2&qlt=80&pid=PopNow ../../real-web/snapshots/bing.com/www.bing.com/thumbnails/carousel-03.jpg image/jpeg diff --git a/crates/js/src/builtins.rs b/crates/js/src/builtins.rs index 27bb901..2799f42 100644 --- a/crates/js/src/builtins.rs +++ b/crates/js/src/builtins.rs @@ -248,7 +248,7 @@ pub fn init_builtins(vm: &mut Vm) { let mut str_proto_data = ObjectData::new(); str_proto_data.prototype = Some(obj_proto); let str_proto = vm.gc.alloc(HeapObject::Object(str_proto_data)); - init_string_prototype(&mut vm.gc, &mut vm.shapes, str_proto); + init_string_prototype(&mut vm.gc, &mut vm.shapes, str_proto, arr_proto); // Create Number.prototype (inherits from Object.prototype). let mut num_proto_data = ObjectData::new(); @@ -2288,7 +2288,12 @@ fn set_iter_idx(gc: &mut Gc, shapes: &mut ShapeTable, iter_ref: GcRe // ── String built-in ────────────────────────────────────────── -fn init_string_prototype(gc: &mut Gc, shapes: &mut ShapeTable, proto: GcRef) { +fn init_string_prototype( + gc: &mut Gc, + shapes: &mut ShapeTable, + proto: GcRef, + arr_proto: GcRef, +) { let methods: &[NativeMethod] = &[ ("charAt", string_proto_char_at), ("charCodeAt", string_proto_char_code_at), @@ -2322,6 +2327,9 @@ fn init_string_prototype(gc: &mut Gc, shapes: &mut ShapeTable, proto ]; for &(name, callback) in methods { let f = make_native(gc, name, callback); + if name == "split" { + set_func_prop(gc, f, "__array_prototype__", Value::Object(arr_proto)); + } set_builtin_prop(gc, shapes, proto, name, Value::Function(f)); } @@ -2716,8 +2724,14 @@ fn string_proto_repeat(args: &[Value], ctx: &mut NativeContext) -> Result Result { let s = this_string(ctx); + let prototype = array_prototype_from_context(ctx); if args.is_empty() || matches!(args.first(), Some(Value::Undefined)) { - return Ok(make_value_array(ctx.gc, ctx.shapes, &[Value::String(s)])); + return Ok(make_value_array_with_proto( + ctx.gc, + ctx.shapes, + &[Value::String(s)], + prototype, + )); } let limit = args .get(1) @@ -2727,7 +2741,7 @@ fn string_proto_split(args: &[Value], ctx: &mut NativeContext) -> Result Result Result, ) -> Result { use crate::regex::{exec, CompiledRegex}; @@ -2812,7 +2831,7 @@ fn string_split_regexp( let rest: String = chars[last_end..].iter().collect(); items.push(Value::String(rest)); } - Ok(make_value_array(gc, shapes, &items)) + Ok(make_value_array_with_proto(gc, shapes, &items, prototype)) } fn string_proto_replace(args: &[Value], ctx: &mut NativeContext) -> Result { @@ -5743,6 +5762,53 @@ pub fn promise_state_pub(gc: &Gc, shapes: &ShapeTable, promise: GcRe promise_state(gc, shapes, promise) } +pub fn settle_promise_all_reaction_pub( + gc: &mut Gc, + shapes: &mut ShapeTable, + chained: GcRef, + value: Value, + fulfilled: bool, +) -> bool { + let target = match promise_get_prop(gc, shapes, chained, "__all_target__") { + Value::Object(r) => r, + _ => return false, + }; + let index = match promise_get_prop(gc, shapes, chained, "__all_index__") { + Value::Number(n) if n >= 0.0 => n as usize, + _ => return false, + }; + let results_ref = match promise_get_prop(gc, shapes, target, "__all_results__") { + Value::Object(r) => r, + _ => return false, + }; + let all_settled = matches!( + promise_get_prop(gc, shapes, chained, "__all_settled__"), + Value::Boolean(true) + ); + + if all_settled { + let entry = make_settled_entry( + gc, + shapes, + if fulfilled { "fulfilled" } else { "rejected" }, + &value, + fulfilled, + ); + array_set(gc, shapes, results_ref, index, entry.clone()); + promise_all_decrement(gc, shapes, target); + resolve_promise_internal(gc, shapes, chained, entry); + } else if fulfilled { + array_set(gc, shapes, results_ref, index, value.clone()); + promise_all_decrement(gc, shapes, target); + resolve_promise_internal(gc, shapes, chained, value); + } else { + reject_promise_internal(gc, shapes, target, value.clone()); + reject_promise_internal(gc, shapes, chained, value); + } + + true +} + pub fn is_promise_pub(gc: &Gc, shapes: &ShapeTable, value: &Value) -> bool { is_promise(gc, shapes, value) } @@ -5862,13 +5928,30 @@ fn init_promise_builtins(vm: &mut Vm) { let static_reject = make_native(&mut vm.gc, "reject", promise_static_reject); vm.set_global("__Promise_static_reject", Value::Function(static_reject)); + let arr_proto = vm.array_prototype; let static_all = make_native(&mut vm.gc, "all", promise_static_all); + if let Some(proto) = arr_proto { + set_func_prop( + &mut vm.gc, + static_all, + "__array_prototype__", + Value::Object(proto), + ); + } vm.set_global("__Promise_static_all", Value::Function(static_all)); let static_race = make_native(&mut vm.gc, "race", promise_static_race); vm.set_global("__Promise_static_race", Value::Function(static_race)); let static_all_settled = make_native(&mut vm.gc, "allSettled", promise_static_all_settled); + if let Some(proto) = arr_proto { + set_func_prop( + &mut vm.gc, + static_all_settled, + "__array_prototype__", + Value::Object(proto), + ); + } vm.set_global( "__Promise_static_allSettled", Value::Function(static_all_settled), @@ -6365,15 +6448,17 @@ fn promise_static_all(args: &[Value], ctx: &mut NativeContext) -> Result