diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index c3978fa71135..378b76ba3ad8 100644 --- a/scripts/build/deps/webkit.ts +++ b/scripts/build/deps/webkit.ts @@ -3,7 +3,7 @@ * for local mode. Override via `--webkit-version=` to test a branch. * From https://github.com/oven-sh/WebKit releases. */ -export const WEBKIT_VERSION = "5488984d20e0dbfe4be2c3ba8fb18eb81a5e0e8b"; +export const WEBKIT_VERSION = "3167a44fb92c268c83f09b232b38a9f3e7f9655a"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/src/codegen/create_hash_table b/src/codegen/create_hash_table index 3c226975dd1e..8962ccfe71a3 100755 --- a/src/codegen/create_hash_table +++ b/src/codegen/create_hash_table @@ -58,14 +58,14 @@ while () { chomp; s/^\s+//; next if /^\#|^$/; # Comment or blank line. Do nothing. - if (/^\@begin/ && !$inside) { + if (!$inside && /^\@begin/) { if (/^\@begin\s*([:_\w]+)\s*\d*\s*$/) { $inside = 1; $name = $1; } else { print STDERR "WARNING: \@begin without table name, skipping $_\n"; } - } elsif (/^\@end\s*$/ && $inside) { + } elsif ($inside && /^\@end\s*$/) { output(); @keys = (); @@ -74,7 +74,7 @@ while () { $includeBuiltin = 0; $inside = 0; - } elsif (/^(\S+)\s*(\S+)\s*([\w\|]*)\s*(\w*)\s*(\w*)\s*$/ && $inside) { + } elsif ($inside && /^(\S+)\s*(\S+)\s*([\w\|]*)\s*(\w*)\s*(\w*)\s*$/) { my $key = $1; my $val = $2; my $att = $3; @@ -106,15 +106,11 @@ while () { } elsif (length($att)) { my $get = $val; my $put = "0"; - my $type = "PropertyAttribute::Property"; - if ($att =~ m/Builtin/) { - $type = "PropertyAttribute::BuiltinAccessor"; - } if (!($att =~ m/ReadOnly/)) { $put = "set" . jsc_ucfirst($val); } $hasSetter = "true"; - push(@values, { "type" => $type, "get" => $get, "put" => $put }); + push(@values, { "type" => "PropertyAttribute::Property", "get" => $get, "put" => $put }); } else { push(@values, { "type" => "Lexer", "value" => $val }); } @@ -230,7 +226,7 @@ sub uint64_multi($$) { return $product & $mask64; } -sub wymum($$) { +sub rapid_mul128($$) { my ($A, $B) = @_; my $ha = $A >> 32; @@ -253,100 +249,90 @@ sub wymum($$) { return ($lo, $hi); }; -sub wymix($$) { +sub rapid_mix($$) { my ($A, $B) = @_; - ($A, $B) = wymum($A, $B); + ($A, $B) = rapid_mul128($A, $B); return $A ^ $B; } -sub convert32BitTo64Bit($) { - my ($v) = @_; - my ($mask1) = 281470681808895; # 0x0000_ffff_0000_ffff - $v = ($v | ($v << 16)) & $mask1; - my ($mask2) = 71777214294589695; # 0x00ff_00ff_00ff_00ff - return ($v | ($v << 8)) & $mask2; -} - -sub convert16BitTo32Bit($) { - my ($v) = @_; - return ($v | ($v << 8)) & 0x00ff_00ff; -} - -sub wyhash { - # https://github.com/wangyi-fudan/wyhash +sub rapidhash { + # https://github.com/Nicoshev/rapidhash + # Hashes raw ASCII bytes (1 byte per character). my @chars = @_; - my $charCount = scalar @chars; - my $byteCount = $charCount << 1; - my $charIndex = 0; - my $seed = 0; - my @secret = ( 11562461410679940143, 16646288086500911323, 10285213230658275043, 6384245875588680899 ); - my $move1 = (($byteCount >> 3) << 2) >> 1; - - $seed ^= wymix($seed ^ $secret[0], $secret[1]); + my $len = scalar @chars; + my @secret = ( 3257665815644502181, 10067880064238660809, 5418857496715711651 ); + + my $seed = rapid_mix(0 ^ $secret[0], $secret[1]) ^ $len; my $a = 0; my $b = 0; - local *c2i = sub { + local *read64 = sub { my ($i) = @_; - return ord($chars[$i]); + return ord($chars[$i]) + | (ord($chars[$i + 1]) << 8) + | (ord($chars[$i + 2]) << 16) + | (ord($chars[$i + 3]) << 24) + | (ord($chars[$i + 4]) << 32) + | (ord($chars[$i + 5]) << 40) + | (ord($chars[$i + 6]) << 48) + | (ord($chars[$i + 7]) << 56); }; - local *wyr8 = sub { + local *read32 = sub { my ($i) = @_; - my $v = c2i($i) | (c2i($i + 1) << 8) | (c2i($i + 2) << 16) | (c2i($i + 3) << 24); - return convert32BitTo64Bit($v); + return ord($chars[$i]) + | (ord($chars[$i + 1]) << 8) + | (ord($chars[$i + 2]) << 16) + | (ord($chars[$i + 3]) << 24); }; - local *wyr4 = sub { - my ($i) = @_; - my $v = c2i($i) | (c2i($i + 1) << 8); - return convert16BitTo32Bit($v); + local *readSmall = sub { + my ($i, $k) = @_; + return (ord($chars[$i]) << 56) + | (ord($chars[$i + ($k >> 1)]) << 32) + | ord($chars[$i + $k - 1]); }; - local *wyr2 = sub { - my ($i) = @_; - return c2i($i) << 16; - }; - - if ($byteCount <= 16) { - if ($byteCount >= 4) { - $a = (wyr4($charIndex) << 32) | wyr4($charIndex + $move1); - $charIndex = $charIndex + $charCount - 2; - $b = (wyr4($charIndex) << 32) | wyr4($charIndex - $move1); - } elsif ($byteCount > 0) { - $a = wyr2($charIndex); + if ($len <= 16) { + if ($len >= 4) { + my $delta = ($len >= 8) ? 4 : 0; + $a = (read32(0) << 32) | read32($len - 4); + $b = (read32($delta) << 32) | read32($len - 4 - $delta); + } elsif ($len > 0) { + $a = readSmall(0, $len); $b = 0; } else { $a = $b = 0; } } else { - my $i = $byteCount; + my $i = $len; + my $off = 0; if ($i > 48) { my $see1 = $seed; my $see2 = $seed; do { - $seed = wymix(wyr8($charIndex) ^ $secret[1], wyr8($charIndex + 4) ^ $seed); - $see1 = wymix(wyr8($charIndex + 8) ^ $secret[2], wyr8($charIndex + 12) ^ $see1); - $see2 = wymix(wyr8($charIndex + 16) ^ $secret[3], wyr8($charIndex + 20) ^ $see2); - $charIndex += 24; + $seed = rapid_mix(read64($off) ^ $secret[0], read64($off + 8) ^ $seed); + $see1 = rapid_mix(read64($off + 16) ^ $secret[1], read64($off + 24) ^ $see1); + $see2 = rapid_mix(read64($off + 32) ^ $secret[2], read64($off + 40) ^ $see2); + $off += 48; $i -= 48; - } while ($i > 48); + } while ($i >= 48); $seed ^= $see1 ^ $see2; } - while ($i > 16) { - $seed = wymix(wyr8($charIndex) ^ $secret[1], wyr8($charIndex + 4) ^ $seed); - $i -= 16; - $charIndex += 8; + if ($i > 16) { + $seed = rapid_mix(read64($off) ^ $secret[2], read64($off + 8) ^ $seed ^ $secret[1]); + if ($i > 32) { + $seed = rapid_mix(read64($off + 16) ^ $secret[2], read64($off + 24) ^ $seed); + } } - my $move2 = $i >> 1; - $a = wyr8($charIndex + $move2 - 8); - $b = wyr8($charIndex + $move2 - 4); + $a = read64($off + $i - 16); + $b = read64($off + $i - 8); } $a ^= $secret[1]; $b ^= $seed; - ($a, $b) = wymum($a, $b); - my $hash = wymix($a ^ $secret[0] ^ $byteCount, $b ^ $secret[1]) & $mask32; + ($a, $b) = rapid_mul128($a, $b); + my $hash = rapid_mix($a ^ $secret[0] ^ $len, $b ^ $secret[1]) & $mask32; return maskTop8BitsAndAvoidZero($hash); } @@ -354,13 +340,15 @@ sub wyhash { sub hashValue($) { my ($string) = @_; my @chars = split(/ */, $string); - return wyhash(@chars); + return rapidhash(@chars); } sub output() { if (!$banner) { $banner = 1; - print "// Automatically generated from $file using $0. DO NOT EDIT!\n"; + my ($srcName) = $file =~ m|([^/]+)$|; + my ($selfName) = $0 =~ m|([^/]+)$|; + print "// Automatically generated from $srcName using $selfName. DO NOT EDIT!\n"; } my $nameEntries = "${name}Values"; @@ -376,36 +364,24 @@ sub output() { print "\n"; local *generateHashTableHelper = sub { - calcPerfectHashSize(); calcCompactHashSize(); my $hashTableString = ""; - if ($compactSize != 0) { - $hashTableString .= "static constinit const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n"; - for (my $i = 0; $i < $compactSize; $i++) { - my $T = -1; - if (defined($table[$i])) { $T = $table[$i]; } - my $L = -1; - if (defined($links[$i])) { $L = $links[$i]; } - $hashTableString .= " { $T, $L },\n"; - } - } else { - # MSVC dislikes empty arrays. - $hashTableString .= "static constinit const struct CompactHashIndex ${nameIndex}\[1\] = {\n"; - $hashTableString .= " { 0, 0 }\n"; + $hashTableString .= "static constinit const struct CompactHashIndex ${nameIndex}\[$compactSize\] = {\n"; + for (my $i = 0; $i < $compactSize; $i++) { + my $T = -1; + if (defined($table[$i])) { $T = $table[$i]; } + my $L = -1; + if (defined($links[$i])) { $L = $links[$i]; } + $hashTableString .= " { $T, $L },\n"; } + $hashTableString .= "};\n"; $hashTableString .= "\n"; my $packedSize = scalar @keys; - if ($packedSize != 0) { - $hashTableString .= "static constinit const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n"; - } else { - # MSVC dislikes empty arrays. - $hashTableString .= "static constinit const struct HashTableValue ${nameEntries}\[1\] = {\n"; - $hashTableString .= " { { }, 0, NoIntrinsic, { HashTableValue::End } }\n"; - } + $hashTableString .= "static constinit const struct HashTableValue ${nameEntries}\[$packedSize\] = {\n"; my $i = 0; foreach my $key (@keys) { @@ -420,14 +396,6 @@ sub output() { $firstValue = $values[$i]{"function"}; $secondValue = $values[$i]{"params"}; $intrinsic = $values[$i]{"intrinsic"}; - } elsif ($values[$i]{"type"} eq "PropertyAttribute::BuiltinAccessor") { - $typeTag = "BuiltinAccessor"; - $firstValue = $values[$i]{"get"}; - $secondValue = $values[$i]{"put"}; - } elsif ($values[$i]{"type"} eq "PropertyAttribute::ConstantInteger") { - $typeTag = "Constant"; - $firstValue = $values[$i]{"value"}; - $hasSecondValue = 0; } elsif ($values[$i]{"type"} eq "PropertyAttribute::Property") { $typeTag = "GetterSetter"; $firstValue = $values[$i]{"get"}; @@ -445,6 +413,10 @@ sub output() { $typeTag = "LazyProperty"; $firstValue = $values[$i]{"cback"}; $hasSecondValue = 0; + } elsif ($values[$i]{"type"} eq "PropertyAttribute::ConstantInteger") { + $typeTag = "Constant"; + $firstValue = $values[$i]{"value"}; + $hasSecondValue = 0; } my $attributes = "PropertyAttribute::" . $attrs[$i]; @@ -472,7 +444,8 @@ sub output() { return $hashTableString; }; - print generateHashTableHelper(); + my $hashTableToWrite = generateHashTableHelper(); + print $hashTableToWrite; print "} // namespace JSC\n"; } diff --git a/src/codegen/replacements.ts b/src/codegen/replacements.ts index 2e72207444f9..10dc18c4fbef 100644 --- a/src/codegen/replacements.ts +++ b/src/codegen/replacements.ts @@ -256,34 +256,16 @@ export function applyReplacements(src: string, length: number) { const id = registerNativeCall(kind, args[0], args[1], is_create_fn ? args[2] : null); return [slice.slice(0, match.index) + "__intrinsic__lazy(" + id + ")", inner.rest, true]; - } else if (name === "isPromiseFulfilled") { + } else if (name === "isPromiseFulfilled" || name === "isPromiseRejected" || name === "isPromisePending") { const inner = sliceSourceCode(rest, true); + // JSC::JSPromise::Status: Pending = 0, Fulfilled = 1, Rejected = 2. + const status = name === "isPromisePending" ? 0 : name === "isPromiseFulfilled" ? 1 : 2; let args; if (debug) { // use a property on @lazy as a temporary holder for the expression. only in debug! - args = `($assert(__intrinsic__isPromise(__intrinsic__lazy.temp=${inner.result.slice(0, -1)}))),(__intrinsic__getPromiseInternalField(__intrinsic__lazy.temp, __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === (__intrinsic__lazy.temp = undefined, __intrinsic__promiseStateFulfilled))`; + args = `($assert(__intrinsic__isPromise(__intrinsic__lazy.temp=${inner.result.slice(0, -1)}))),__intrinsic__peekPromiseStatus(__intrinsic__lazy.temp) === (__intrinsic__lazy.temp = undefined, ${status}))`; } else { - args = `((__intrinsic__getPromiseInternalField(${inner.result.slice(0, -1)}), __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === __intrinsic__promiseStateFulfilled)`; - } - return [slice.slice(0, match.index) + args, inner.rest, true]; - } else if (name === "isPromiseRejected") { - const inner = sliceSourceCode(rest, true); - let args; - if (debug) { - // use a property on @lazy as a temporary holder for the expression. only in debug! - args = `($assert(__intrinsic__isPromise(__intrinsic__lazy.temp=${inner.result.slice(0, -1)}))),(__intrinsic__getPromiseInternalField(__intrinsic__lazy.temp, __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === (__intrinsic__lazy.temp = undefined, __intrinsic__promiseStateRejected))`; - } else { - args = `((__intrinsic__getPromiseInternalField(${inner.result.slice(0, -1)}), __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === __intrinsic__promiseStateRejected)`; - } - return [slice.slice(0, match.index) + args, inner.rest, true]; - } else if (name === "isPromisePending") { - const inner = sliceSourceCode(rest, true); - let args; - if (debug) { - // use a property on @lazy as a temporary holder for the expression. only in debug! - args = `($assert(__intrinsic__isPromise(__intrinsic__lazy.temp=${inner.result.slice(0, -1)}))),(__intrinsic__getPromiseInternalField(__intrinsic__lazy.temp, __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === (__intrinsic__lazy.temp = undefined, __intrinsic__promiseStatePending))`; - } else { - args = `((__intrinsic__getPromiseInternalField(${inner.result.slice(0, -1)}), __intrinsic__promiseFieldFlags) & __intrinsic__promiseStateMask) === __intrinsic__promiseStatePending)`; + args = `(__intrinsic__peekPromiseStatus${inner.result} === ${status})`; } return [slice.slice(0, match.index) + args, inner.rest, true]; } else if (name === "bindgenFn") { diff --git a/src/js/builtins.d.ts b/src/js/builtins.d.ts index 709b040fc550..328356a9ee70 100644 --- a/src/js/builtins.d.ts +++ b/src/js/builtins.d.ts @@ -129,19 +129,20 @@ declare function $getByValWithThis(target: any, receiver: any, propertyKey: stri /** gets the prototype of an object */ declare function $getPrototypeOf(value: any): any; /** - * Gets an internal property on a promise - * - * You can pass - * - {@link $promiseFieldFlags} - get a number with flags - * - {@link $promiseFieldReactionsOrResult} - get the result (like {@link Bun.peek}) - * - * @param promise the promise to get the field from - * @param key an internal field id. + * Returns the internal Promise state as a small integer: + * `0` = pending, `1` = fulfilled, `2` = rejected. */ -declare function $getPromiseInternalField( - promise: Promise, - key: K, -): PromiseFieldToValue; +declare function $peekPromiseStatus(promise: Promise): number; +/** + * Returns the settlement value of a settled Promise (the fulfillment value + * or the rejection reason). Returns `undefined` for a pending promise. + */ +declare function $peekPromiseSettledValue(promise: Promise): V | undefined; +/** + * Marks a promise as handled so it doesn't fire the unhandled-rejection + * tracker. Equivalent to JSC's `JSPromise::markAsHandled()`. + */ +declare function $pokePromiseAsHandled(promise: Promise): void; declare function $getInternalField( base: InternalFieldObject, number: N, @@ -259,11 +260,6 @@ declare function $putInternalField number: N, value: Fields[N], ): void; -declare function $putPromiseInternalField>( - promise: P, - key: T, - value: PromiseFieldToValue, -): void; declare function $putGeneratorInternalField(): TODO; declare function $putAsyncGeneratorInternalField(): TODO; declare function $putArrayIteratorInternalField(): TODO; @@ -316,14 +312,6 @@ declare const $ModuleLink: number; declare const $ModuleReady: number; declare const $promiseRejectionReject: TODO; declare const $promiseRejectionHandle: TODO; -declare const $promiseStatePending: number; -declare const $promiseStateFulfilled: number; -declare const $promiseStateRejected: number; -declare const $promiseStateMask: number; -declare const $promiseFlagsIsHandled: number; -declare const $promiseFlagsIsFirstResolvingFunctionCalled: number; -declare const $promiseFieldFlags: 0; -declare const $promiseFieldReactionsOrResult: 1; declare const $proxyFieldTarget: TODO; declare const $proxyFieldHandler: TODO; declare const $generatorFieldState: TODO; @@ -552,12 +540,6 @@ interface InternalFieldObject { } // Types used in the above functions -type PromiseFieldType = typeof $promiseFieldFlags | typeof $promiseFieldReactionsOrResult; -type PromiseFieldToValue = X extends typeof $promiseFieldFlags - ? number - : X extends typeof $promiseFieldReactionsOrResult - ? V | any - : any; type WellKnownSymbol = keyof { [K in keyof SymbolConstructor as SymbolConstructor[K] extends symbol ? K : never]: K }; // You can also `@` on any method on a classes to avoid prototype pollution and secret internals diff --git a/src/js/builtins/BunBuiltinNames.h b/src/js/builtins/BunBuiltinNames.h index 457ecedd07d5..c66f4c6231ee 100644 --- a/src/js/builtins/BunBuiltinNames.h +++ b/src/js/builtins/BunBuiltinNames.h @@ -151,8 +151,11 @@ using namespace JSC; macro(partitioned) \ macro(path) \ macro(paths) \ + macro(peekPromiseSettledValue) \ + macro(peekPromiseStatus) \ macro(pendingAbortRequest) \ macro(pendingPullIntos) \ + macro(pokePromiseAsHandled) \ macro(port) \ macro(post) \ macro(processBindingConstants) \ diff --git a/src/js/builtins/BundlerPlugin.ts b/src/js/builtins/BundlerPlugin.ts index 2e262c785482..083a7c2949ba 100644 --- a/src/js/builtins/BundlerPlugin.ts +++ b/src/js/builtins/BundlerPlugin.ts @@ -255,7 +255,12 @@ export function runSetupFunction( const ret = callback(); if ($isPromise(ret)) { - if (($getPromiseInternalField(ret, $promiseFieldFlags) & $promiseStateMask) != $promiseStateFulfilled) { + if ($peekPromiseStatus(ret) != 1) { + // Stash the deferred promise; the aggregate handler is attached later + // (in loadAndResolvePluginsForServe via Promise.all). Mark it as + // handled now so a rejection that lands while it's only sitting in + // the array doesn't fire the unhandled-rejection tracker. + $pokePromiseAsHandled(ret); self.promises ??= []; self.promises.push(ret); } @@ -369,8 +374,8 @@ export function runSetupFunction( } as PluginBuilderExt); if (setupResult && $isPromise(setupResult)) { - if ($getPromiseInternalField(setupResult, $promiseFieldFlags) & $promiseStateFulfilled) { - setupResult = $getPromiseInternalField(setupResult, $promiseFieldReactionsOrResult); + if ($peekPromiseStatus(setupResult) === 1) { + setupResult = $peekPromiseSettledValue(setupResult); } else { return setupResult.$then(() => { if (is_last && self.promises !== undefined && self.promises.length > 0) { @@ -413,12 +418,8 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa // pluginData }); - while ( - result && - $isPromise(result) && - ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult); + while (result && $isPromise(result) && $peekPromiseStatus(result) === 1) { + result = $peekPromiseSettledValue(result); } if (result && $isPromise(result)) { @@ -480,12 +481,8 @@ export function runOnResolvePlugins(this: BundlerPlugin, specifier, inputNamespa return null; })(specifier, inputNamespace, importer, kind); - while ( - promiseResult && - $isPromise(promiseResult) && - ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult); + while (promiseResult && $isPromise(promiseResult) && $peekPromiseStatus(promiseResult) === 1) { + promiseResult = $peekPromiseSettledValue(promiseResult); } if (promiseResult && $isPromise(promiseResult)) { @@ -529,12 +526,8 @@ export function runOnLoadPlugins( side: isServerSide ? "server" : "client", }); - while ( - result && - $isPromise(result) && - ($getPromiseInternalField(result, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - result = $getPromiseInternalField(result, $promiseFieldReactionsOrResult); + while (result && $isPromise(result) && $peekPromiseStatus(result) === 1) { + result = $peekPromiseSettledValue(result); } if (result && $isPromise(result)) { @@ -580,12 +573,8 @@ export function runOnLoadPlugins( return null; })(internalID, path, namespace, isServerSide, loaderName, generateDefer); - while ( - promiseResult && - $isPromise(promiseResult) && - ($getPromiseInternalField(promiseResult, $promiseFieldFlags) & $promiseStateMask) === $promiseStateFulfilled - ) { - promiseResult = $getPromiseInternalField(promiseResult, $promiseFieldReactionsOrResult); + while (promiseResult && $isPromise(promiseResult) && $peekPromiseStatus(promiseResult) === 1) { + promiseResult = $peekPromiseSettledValue(promiseResult); } if (promiseResult && $isPromise(promiseResult)) { diff --git a/src/js/builtins/CommonJS.ts b/src/js/builtins/CommonJS.ts index c038fdaf0c8e..b2e756e16b79 100644 --- a/src/js/builtins/CommonJS.ts +++ b/src/js/builtins/CommonJS.ts @@ -209,7 +209,7 @@ function loadEsmIntoCjs__dead(resolvedSpecifier: string) { // - we've never fetched it // - a fetch is in progress (!$isPromise(fetch) || - ($getPromiseInternalField(fetch, $promiseFieldFlags) & $promiseStateMask) === $promiseStatePending)) + ($peekPromiseStatus(fetch)) === 0)) ) { // force it to be no longer pending $fulfillModuleSync(key); @@ -225,7 +225,7 @@ function loadEsmIntoCjs__dead(resolvedSpecifier: string) { if (state < $ModuleLink && $isPromise(fetch)) { // This will probably never happen, but just in case - if (($getPromiseInternalField(fetch, $promiseFieldFlags) & $promiseStateMask) === $promiseStatePending) { + if (($peekPromiseStatus(fetch)) === 0) { registry.$delete(resolvedSpecifier); throw new TypeError(`require() async module "${key}" is unsupported. use "await import()" instead.`); @@ -233,21 +233,20 @@ function loadEsmIntoCjs__dead(resolvedSpecifier: string) { // this pulls it out of the promise without delaying by a tick // the promise is already fulfilled by $fulfillModuleSync - const sourceCodeObject = $getPromiseInternalField(fetch, $promiseFieldReactionsOrResult); + const sourceCodeObject = $peekPromiseSettledValue(fetch); moduleRecordPromise = loader.parseModule(key, sourceCodeObject); } let mod = entry?.module; if (moduleRecordPromise && $isPromise(moduleRecordPromise)) { - let reactionsOrResult = $getPromiseInternalField(moduleRecordPromise, $promiseFieldReactionsOrResult); - let flags = $getPromiseInternalField(moduleRecordPromise, $promiseFieldFlags); - let state = flags & $promiseStateMask; + let reactionsOrResult = $peekPromiseSettledValue(moduleRecordPromise); + let state = $peekPromiseStatus(moduleRecordPromise); // this branch should never happen, but just to be safe - if (state === $promiseStatePending || (reactionsOrResult && $isPromise(reactionsOrResult))) { + if (state === 0 || (reactionsOrResult && $isPromise(reactionsOrResult))) { registry.$delete(resolvedSpecifier); throw new TypeError(`require() async module "${key}" is unsupported. use "await import()" instead.`); - } else if (state === $promiseStateRejected) { + } else if (state === 2) { if (!reactionsOrResult?.message) { throw new TypeError( `${ diff --git a/src/js/builtins/Peek.ts b/src/js/builtins/Peek.ts index 578936feed3e..5d916ba7cc6f 100644 --- a/src/js/builtins/Peek.ts +++ b/src/js/builtins/Peek.ts @@ -1,19 +1,11 @@ export function peek(promise: unknown): unknown { - $assert($promiseStatePending == 0); - - return $isPromise(promise) && $getPromiseInternalField(promise, $promiseFieldFlags) & $promiseStateMask - ? $getPromiseInternalField(promise, $promiseFieldReactionsOrResult) - : promise; + return $isPromise(promise) && $peekPromiseStatus(promise) ? $peekPromiseSettledValue(promise) : promise; } export function peekStatus(promise: unknown): string { - $assert($promiseStatePending == 0); - $assert($promiseStateFulfilled == 1); - $assert($promiseStateRejected == 2); - return ["pending", "fulfilled", "rejected"][ $isPromise(promise) // - ? $getPromiseInternalField(promise, $promiseFieldFlags) & $promiseStateMask + ? $peekPromiseStatus(promise) : 1 ]; } diff --git a/src/js/builtins/ReadableStreamInternals.ts b/src/js/builtins/ReadableStreamInternals.ts index 86bd3b3ebc7d..27f5af998048 100644 --- a/src/js/builtins/ReadableStreamInternals.ts +++ b/src/js/builtins/ReadableStreamInternals.ts @@ -1823,7 +1823,7 @@ export function readableStreamFromAsyncIterator(target, fn) { if ($isPromise(promise) && $isPromiseFulfilled(promise)) { clearImmediate(immediateTask); - ({ value, done } = $getPromiseInternalField(promise, $promiseFieldReactionsOrResult)); + ({ value, done } = $peekPromiseSettledValue(promise)); $assert(!$isPromise(value), "Expected a value, not a promise"); } else { immediateTask = setImmediate(() => immediateTask && controller?.flush?.(true)); diff --git a/src/js/builtins/StreamInternals.ts b/src/js/builtins/StreamInternals.ts index 3f1d7d0f30e8..daf9b96569b9 100644 --- a/src/js/builtins/StreamInternals.ts +++ b/src/js/builtins/StreamInternals.ts @@ -28,11 +28,7 @@ export function markPromiseAsHandled(promise: Promise) { $assert($isPromise(promise)); - $putPromiseInternalField( - promise, - $promiseFieldFlags, - $getPromiseInternalField(promise, $promiseFieldFlags) | $promiseFlagsIsHandled, - ); + $pokePromiseAsHandled(promise); } export function shieldingPromiseResolve(result) { diff --git a/src/js/builtins/WritableStreamInternals.ts b/src/js/builtins/WritableStreamInternals.ts index e14c5dee3590..9fe4583c6407 100644 --- a/src/js/builtins/WritableStreamInternals.ts +++ b/src/js/builtins/WritableStreamInternals.ts @@ -467,7 +467,7 @@ export function writableStreamDefaultWriterEnsureClosedPromiseRejected(writer, e let closedPromiseCapability = $getByIdDirectPrivate(writer, "closedPromise"); let closedPromise = closedPromiseCapability.promise; - if (($getPromiseInternalField(closedPromise, $promiseFieldFlags) & $promiseStateMask) !== $promiseStatePending) { + if ($peekPromiseStatus(closedPromise) !== 0) { closedPromiseCapability = $newPromiseCapability(Promise); closedPromise = closedPromiseCapability.promise; $putByIdDirectPrivate(writer, "closedPromise", closedPromiseCapability); @@ -481,7 +481,7 @@ export function writableStreamDefaultWriterEnsureReadyPromiseRejected(writer, er let readyPromiseCapability = $getByIdDirectPrivate(writer, "readyPromise"); let readyPromise = readyPromiseCapability.promise; - if (($getPromiseInternalField(readyPromise, $promiseFieldFlags) & $promiseStateMask) !== $promiseStatePending) { + if ($peekPromiseStatus(readyPromise) !== 0) { readyPromiseCapability = $newPromiseCapability(Promise); readyPromise = readyPromiseCapability.promise; $putByIdDirectPrivate(writer, "readyPromise", readyPromiseCapability); diff --git a/src/js/internal/util/inspect.js b/src/js/internal/util/inspect.js index 52ad43eae782..143bb96f0841 100644 --- a/src/js/internal/util/inspect.js +++ b/src/js/internal/util/inspect.js @@ -2678,12 +2678,9 @@ function getOwnNonIndexProperties(a, filter = ONLY_ENUMERABLE) { return ret; } function getPromiseDetails(promise) { - const state = $getPromiseInternalField(promise, $promiseFieldFlags) & $promiseStateMask; - if (state !== $promiseStatePending) { - return [ - state === $promiseStateRejected ? kRejected : kFulfilled, - $getPromiseInternalField(promise, $promiseFieldReactionsOrResult), - ]; + const state = $peekPromiseStatus(promise); + if (state !== 0) { + return [state === 2 ? kRejected : kFulfilled, $peekPromiseSettledValue(promise)]; } return [kPending, undefined]; } diff --git a/src/jsc/JSType.rs b/src/jsc/JSType.rs index a080064bce6e..0a23dd68231f 100644 --- a/src/jsc/JSType.rs +++ b/src/jsc/JSType.rs @@ -215,38 +215,41 @@ impl JSType { /// Global context for Promise.all() (new in recent WebKit). pub const PromiseAllGlobalContext: JSType = JSType(26); + /// Streaming WebAssembly compile/instantiate context (new in WebKit). + pub const WebAssemblyStreamingContext: JSType = JSType(27); + /// Microtask dispatcher for promise/microtask queue management. - pub const JSMicrotaskDispatcher: JSType = JSType(27); + pub const JSMicrotaskDispatcher: JSType = JSType(28); /// Module loader registry entry (new C++ module loader). - pub const ModuleRegistryEntry: JSType = JSType(28); + pub const ModuleRegistryEntry: JSType = JSType(29); /// Module loading context (new C++ module loader). - pub const ModuleLoadingContext: JSType = JSType(29); + pub const ModuleLoadingContext: JSType = JSType(30); /// Module loader payload (new C++ module loader). - pub const ModuleLoaderPayload: JSType = JSType(30); + pub const ModuleLoaderPayload: JSType = JSType(31); /// Module graph loading state (new C++ module loader). - pub const ModuleGraphLoadingState: JSType = JSType(31); + pub const ModuleGraphLoadingState: JSType = JSType(32); /// JSModuleLoader cell type (new C++ module loader). - pub const JSModuleLoader: JSType = JSType(32); + pub const JSModuleLoader: JSType = JSType(33); /// Base JavaScript object type. /// ```js /// {} /// new Object() /// ``` - pub const Object: JSType = JSType(33); + pub const Object: JSType = JSType(34); /// Optimized object type for object literals with fixed properties. /// ```js /// { a: 1, b: 2 } /// ``` - pub const FinalObject: JSType = JSType(34); + pub const FinalObject: JSType = JSType(35); - pub const JSCallee: JSType = JSType(35); + pub const JSCallee: JSType = JSType(36); /// JavaScript function object created from JavaScript source code. /// ```js @@ -256,7 +259,7 @@ impl JSType { /// method() {} /// } /// ``` - pub const JSFunction: JSType = JSType(36); + pub const JSFunction: JSType = JSType(37); /// Built-in function implemented in native code. /// ```js @@ -265,23 +268,23 @@ impl JSType { /// parseInt /// console.log /// ``` - pub const InternalFunction: JSType = JSType(37); + pub const InternalFunction: JSType = JSType(38); - pub const NullSetterFunction: JSType = JSType(38); + pub const NullSetterFunction: JSType = JSType(39); /// Boxed Boolean object. /// ```js /// new Boolean(true) /// new Boolean(false) /// ``` - pub const BooleanObject: JSType = JSType(39); + pub const BooleanObject: JSType = JSType(40); /// Boxed Number object. /// ```js /// new Number(42) /// new Number(3.14) /// ``` - pub const NumberObject: JSType = JSType(40); + pub const NumberObject: JSType = JSType(41); /// JavaScript Error object and its subclasses. /// ```js @@ -289,9 +292,9 @@ impl JSType { /// new TypeError() /// throw new RangeError() /// ``` - pub const ErrorInstance: JSType = JSType(41); + pub const ErrorInstance: JSType = JSType(42); - pub const GlobalProxy: JSType = JSType(42); + pub const GlobalProxy: JSType = JSType(43); /// Arguments object for function parameters. /// ```js @@ -300,10 +303,10 @@ impl JSType { /// console.log(arguments.length); /// } /// ``` - pub const DirectArguments: JSType = JSType(43); + pub const DirectArguments: JSType = JSType(44); - pub const ScopedArguments: JSType = JSType(44); - pub const ClonedArguments: JSType = JSType(45); + pub const ScopedArguments: JSType = JSType(45); + pub const ClonedArguments: JSType = JSType(46); /// JavaScript Array object. /// ```js @@ -312,94 +315,94 @@ impl JSType { /// new Array(10) /// Array.from(iterable) /// ``` - pub const Array: JSType = JSType(46); + pub const Array: JSType = JSType(47); /// Array subclass created through class extension. /// ```js /// class MyArray extends Array {} /// const arr = new MyArray(); /// ``` - pub const DerivedArray: JSType = JSType(47); + pub const DerivedArray: JSType = JSType(48); /// ArrayBuffer for binary data storage. /// ```js /// new ArrayBuffer(1024) /// ``` - pub const ArrayBuffer: JSType = JSType(48); + pub const ArrayBuffer: JSType = JSType(49); /// Typed array for 8-bit signed integers. /// ```js /// new Int8Array(buffer) /// new Int8Array([1, -1, 127]) /// ``` - pub const Int8Array: JSType = JSType(49); + pub const Int8Array: JSType = JSType(50); /// Typed array for 8-bit unsigned integers. /// ```js /// new Uint8Array(buffer) /// new Uint8Array([0, 255]) /// ``` - pub const Uint8Array: JSType = JSType(50); + pub const Uint8Array: JSType = JSType(51); /// Typed array for 8-bit unsigned integers with clamping. /// ```js /// new Uint8ClampedArray([0, 300]) // 300 becomes 255 /// ``` - pub const Uint8ClampedArray: JSType = JSType(51); + pub const Uint8ClampedArray: JSType = JSType(52); /// Typed array for 16-bit signed integers. /// ```js /// new Int16Array(buffer) /// ``` - pub const Int16Array: JSType = JSType(52); + pub const Int16Array: JSType = JSType(53); /// Typed array for 16-bit unsigned integers. /// ```js /// new Uint16Array(buffer) /// ``` - pub const Uint16Array: JSType = JSType(53); + pub const Uint16Array: JSType = JSType(54); /// Typed array for 32-bit signed integers. /// ```js /// new Int32Array(buffer) /// ``` - pub const Int32Array: JSType = JSType(54); + pub const Int32Array: JSType = JSType(55); /// Typed array for 32-bit unsigned integers. /// ```js /// new Uint32Array(buffer) /// ``` - pub const Uint32Array: JSType = JSType(55); + pub const Uint32Array: JSType = JSType(56); /// Typed array for 16-bit floating point numbers. /// ```js /// new Float16Array(buffer) /// ``` - pub const Float16Array: JSType = JSType(56); + pub const Float16Array: JSType = JSType(57); /// Typed array for 32-bit floating point numbers. /// ```js /// new Float32Array(buffer) /// ``` - pub const Float32Array: JSType = JSType(57); + pub const Float32Array: JSType = JSType(58); /// Typed array for 64-bit floating point numbers. /// ```js /// new Float64Array(buffer) /// ``` - pub const Float64Array: JSType = JSType(58); + pub const Float64Array: JSType = JSType(59); /// Typed array for 64-bit signed BigInt values. /// ```js /// new BigInt64Array([123n, -456n]) /// ``` - pub const BigInt64Array: JSType = JSType(59); + pub const BigInt64Array: JSType = JSType(60); /// Typed array for 64-bit unsigned BigInt values. /// ```js /// new BigUint64Array([123n, 456n]) /// ``` - pub const BigUint64Array: JSType = JSType(60); + pub const BigUint64Array: JSType = JSType(61); /// DataView for flexible binary data access. /// ```js @@ -407,7 +410,7 @@ impl JSType { /// view.getInt32(0) /// view.setFloat64(8, 3.14) /// ``` - pub const DataView: JSType = JSType(61); + pub const DataView: JSType = JSType(62); /// Global object containing all global variables and functions. /// ```js @@ -415,12 +418,12 @@ impl JSType { /// window // in browsers /// global // in Node.js /// ``` - pub const GlobalObject: JSType = JSType(62); + pub const GlobalObject: JSType = JSType(63); - pub const GlobalLexicalEnvironment: JSType = JSType(63); - pub const LexicalEnvironment: JSType = JSType(64); - pub const ModuleEnvironment: JSType = JSType(65); - pub const StrictEvalActivation: JSType = JSType(66); + pub const GlobalLexicalEnvironment: JSType = JSType(64); + pub const LexicalEnvironment: JSType = JSType(65); + pub const ModuleEnvironment: JSType = JSType(66); + pub const StrictEvalActivation: JSType = JSType(67); /// Scope object for with statements. /// ```js @@ -428,19 +431,19 @@ impl JSType { /// prop; // looks up prop in obj first /// } /// ``` - pub const WithScope: JSType = JSType(67); + pub const WithScope: JSType = JSType(68); - pub const AsyncDisposableStack: JSType = JSType(68); - pub const DisposableStack: JSType = JSType(69); + pub const AsyncDisposableStack: JSType = JSType(69); + pub const DisposableStack: JSType = JSType(70); /// Namespace object for ES6 modules. /// ```js /// import * as ns from 'module'; /// ns.exportedFunction() /// ``` - pub const ModuleNamespaceObject: JSType = JSType(70); + pub const ModuleNamespaceObject: JSType = JSType(71); - pub const ShadowRealm: JSType = JSType(71); + pub const ShadowRealm: JSType = JSType(72); /// Regular expression object. /// ```js @@ -448,7 +451,7 @@ impl JSType { /// new RegExp('pattern', 'flags') /// /abc/gi /// ``` - pub const RegExpObject: JSType = JSType(72); + pub const RegExpObject: JSType = JSType(73); /// JavaScript Date object for date/time operations. /// ```js @@ -456,7 +459,7 @@ impl JSType { /// new Date('2023-01-01') /// Date.now() /// ``` - pub const JSDate: JSType = JSType(73); + pub const JSDate: JSType = JSType(74); /// Proxy object that intercepts operations on another object. /// ```js @@ -464,7 +467,7 @@ impl JSType { /// get(obj, prop) { return obj[prop]; } /// }) /// ``` - pub const ProxyObject: JSType = JSType(74); + pub const ProxyObject: JSType = JSType(75); /// Generator object created by generator functions. /// ```js @@ -472,7 +475,7 @@ impl JSType { /// const g = gen(); /// g.next() /// ``` - pub const Generator: JSType = JSType(75); + pub const Generator: JSType = JSType(76); /// Async generator object for asynchronous iteration. /// ```js @@ -480,17 +483,17 @@ impl JSType { /// yield await promise; /// } /// ``` - pub const AsyncGenerator: JSType = JSType(76); + pub const AsyncGenerator: JSType = JSType(77); /// Iterator for Array objects. /// ```js /// [1,2,3][Symbol.iterator]() /// for (const x of array) {} /// ``` - pub const JSArrayIterator: JSType = JSType(77); + pub const JSArrayIterator: JSType = JSType(78); - pub const Iterator: JSType = JSType(78); - pub const IteratorHelper: JSType = JSType(79); + pub const Iterator: JSType = JSType(79); + pub const IteratorHelper: JSType = JSType(80); /// Iterator for Map objects. /// ```js @@ -499,32 +502,32 @@ impl JSType { /// map.entries() /// for (const [k,v] of map) {} /// ``` - pub const MapIterator: JSType = JSType(80); + pub const MapIterator: JSType = JSType(81); /// Iterator for Set objects. /// ```js /// set.values() /// for (const value of set) {} /// ``` - pub const SetIterator: JSType = JSType(81); + pub const SetIterator: JSType = JSType(82); /// Iterator for String objects. /// ```js /// 'hello'[Symbol.iterator]() /// for (const char of string) {} /// ``` - pub const StringIterator: JSType = JSType(82); + pub const StringIterator: JSType = JSType(83); - pub const WrapForValidIterator: JSType = JSType(83); + pub const WrapForValidIterator: JSType = JSType(84); /// Iterator for RegExp string matching. /// ```js /// 'abc'.matchAll(/./g) /// for (const match of string.matchAll(regex)) {} /// ``` - pub const RegExpStringIterator: JSType = JSType(84); + pub const RegExpStringIterator: JSType = JSType(85); - pub const AsyncFromSyncIterator: JSType = JSType(85); + pub const AsyncFromSyncIterator: JSType = JSType(86); /// JavaScript Promise object for asynchronous operations. /// ```js @@ -532,7 +535,7 @@ impl JSType { /// Promise.resolve(42) /// async function foo() { await promise; } /// ``` - pub const JSPromise: JSType = JSType(86); + pub const JSPromise: JSType = JSType(87); /// JavaScript Map object for key-value storage. /// ```js @@ -540,7 +543,7 @@ impl JSType { /// map.set(key, value) /// map.get(key) /// ``` - pub const Map: JSType = JSType(87); + pub const Map: JSType = JSType(88); /// JavaScript Set object for unique value storage. /// ```js @@ -548,34 +551,34 @@ impl JSType { /// set.add(value) /// set.has(value) /// ``` - pub const Set: JSType = JSType(88); + pub const Set: JSType = JSType(89); /// WeakMap for weak key-value references. /// ```js /// new WeakMap() /// weakMap.set(object, value) /// ``` - pub const WeakMap: JSType = JSType(89); + pub const WeakMap: JSType = JSType(90); /// WeakSet for weak value references. /// ```js /// new WeakSet() /// weakSet.add(object) /// ``` - pub const WeakSet: JSType = JSType(90); + pub const WeakSet: JSType = JSType(91); - pub const WebAssemblyModule: JSType = JSType(91); - pub const WebAssemblyInstance: JSType = JSType(92); - pub const WebAssemblyGCObject: JSType = JSType(93); + pub const WebAssemblyModule: JSType = JSType(92); + pub const WebAssemblyInstance: JSType = JSType(93); + pub const WebAssemblyGCObject: JSType = JSType(94); /// Boxed String object. /// ```js /// new String("hello") /// ``` - pub const StringObject: JSType = JSType(94); + pub const StringObject: JSType = JSType(95); - pub const DerivedStringObject: JSType = JSType(95); - pub const InternalFieldTuple: JSType = JSType(96); + pub const DerivedStringObject: JSType = JSType(96); + pub const InternalFieldTuple: JSType = JSType(97); pub const MaxJS: JSType = JSType(0b11111111); pub const Event: JSType = JSType(0b11101111); diff --git a/src/jsc/JSType.zig b/src/jsc/JSType.zig index b111f9c6a944..d5b316415abb 100644 --- a/src/jsc/JSType.zig +++ b/src/jsc/JSType.zig @@ -203,38 +203,41 @@ pub const JSType = enum(u8) { /// Global context for Promise.all() (new in recent WebKit). PromiseAllGlobalContext = 26, + /// Streaming WebAssembly compile/instantiate context (new in WebKit). + WebAssemblyStreamingContext = 27, + /// Microtask dispatcher for promise/microtask queue management. - JSMicrotaskDispatcher = 27, + JSMicrotaskDispatcher = 28, /// Module loader registry entry (new C++ module loader). - ModuleRegistryEntry = 28, + ModuleRegistryEntry = 29, /// Module loading context (new C++ module loader). - ModuleLoadingContext = 29, + ModuleLoadingContext = 30, /// Module loader payload (new C++ module loader). - ModuleLoaderPayload = 30, + ModuleLoaderPayload = 31, /// Module graph loading state (new C++ module loader). - ModuleGraphLoadingState = 31, + ModuleGraphLoadingState = 32, /// JSModuleLoader cell type (new C++ module loader). - JSModuleLoader = 32, + JSModuleLoader = 33, /// Base JavaScript object type. /// ```js /// {} /// new Object() /// ``` - Object = 33, + Object = 34, /// Optimized object type for object literals with fixed properties. /// ```js /// { a: 1, b: 2 } /// ``` - FinalObject = 34, + FinalObject = 35, - JSCallee = 35, + JSCallee = 36, /// JavaScript function object created from JavaScript source code. /// ```js @@ -244,7 +247,7 @@ pub const JSType = enum(u8) { /// method() {} /// } /// ``` - JSFunction = 36, + JSFunction = 37, /// Built-in function implemented in native code. /// ```js @@ -253,23 +256,23 @@ pub const JSType = enum(u8) { /// parseInt /// console.log /// ``` - InternalFunction = 37, + InternalFunction = 38, - NullSetterFunction = 38, + NullSetterFunction = 39, /// Boxed Boolean object. /// ```js /// new Boolean(true) /// new Boolean(false) /// ``` - BooleanObject = 39, + BooleanObject = 40, /// Boxed Number object. /// ```js /// new Number(42) /// new Number(3.14) /// ``` - NumberObject = 40, + NumberObject = 41, /// JavaScript Error object and its subclasses. /// ```js @@ -277,9 +280,9 @@ pub const JSType = enum(u8) { /// new TypeError() /// throw new RangeError() /// ``` - ErrorInstance = 41, + ErrorInstance = 42, - GlobalProxy = 42, + GlobalProxy = 43, /// Arguments object for function parameters. /// ```js @@ -288,10 +291,10 @@ pub const JSType = enum(u8) { /// console.log(arguments.length); /// } /// ``` - DirectArguments = 43, + DirectArguments = 44, - ScopedArguments = 44, - ClonedArguments = 45, + ScopedArguments = 45, + ClonedArguments = 46, /// JavaScript Array object. /// ```js @@ -300,94 +303,94 @@ pub const JSType = enum(u8) { /// new Array(10) /// Array.from(iterable) /// ``` - Array = 46, + Array = 47, /// Array subclass created through class extension. /// ```js /// class MyArray extends Array {} /// const arr = new MyArray(); /// ``` - DerivedArray = 47, + DerivedArray = 48, /// ArrayBuffer for binary data storage. /// ```js /// new ArrayBuffer(1024) /// ``` - ArrayBuffer = 48, + ArrayBuffer = 49, /// Typed array for 8-bit signed integers. /// ```js /// new Int8Array(buffer) /// new Int8Array([1, -1, 127]) /// ``` - Int8Array = 49, + Int8Array = 50, /// Typed array for 8-bit unsigned integers. /// ```js /// new Uint8Array(buffer) /// new Uint8Array([0, 255]) /// ``` - Uint8Array = 50, + Uint8Array = 51, /// Typed array for 8-bit unsigned integers with clamping. /// ```js /// new Uint8ClampedArray([0, 300]) // 300 becomes 255 /// ``` - Uint8ClampedArray = 51, + Uint8ClampedArray = 52, /// Typed array for 16-bit signed integers. /// ```js /// new Int16Array(buffer) /// ``` - Int16Array = 52, + Int16Array = 53, /// Typed array for 16-bit unsigned integers. /// ```js /// new Uint16Array(buffer) /// ``` - Uint16Array = 53, + Uint16Array = 54, /// Typed array for 32-bit signed integers. /// ```js /// new Int32Array(buffer) /// ``` - Int32Array = 54, + Int32Array = 55, /// Typed array for 32-bit unsigned integers. /// ```js /// new Uint32Array(buffer) /// ``` - Uint32Array = 55, + Uint32Array = 56, /// Typed array for 16-bit floating point numbers. /// ```js /// new Float16Array(buffer) /// ``` - Float16Array = 56, + Float16Array = 57, /// Typed array for 32-bit floating point numbers. /// ```js /// new Float32Array(buffer) /// ``` - Float32Array = 57, + Float32Array = 58, /// Typed array for 64-bit floating point numbers. /// ```js /// new Float64Array(buffer) /// ``` - Float64Array = 58, + Float64Array = 59, /// Typed array for 64-bit signed BigInt values. /// ```js /// new BigInt64Array([123n, -456n]) /// ``` - BigInt64Array = 59, + BigInt64Array = 60, /// Typed array for 64-bit unsigned BigInt values. /// ```js /// new BigUint64Array([123n, 456n]) /// ``` - BigUint64Array = 60, + BigUint64Array = 61, /// DataView for flexible binary data access. /// ```js @@ -395,7 +398,7 @@ pub const JSType = enum(u8) { /// view.getInt32(0) /// view.setFloat64(8, 3.14) /// ``` - DataView = 61, + DataView = 62, /// Global object containing all global variables and functions. /// ```js @@ -403,12 +406,12 @@ pub const JSType = enum(u8) { /// window // in browsers /// global // in Node.js /// ``` - GlobalObject = 62, + GlobalObject = 63, - GlobalLexicalEnvironment = 63, - LexicalEnvironment = 64, - ModuleEnvironment = 65, - StrictEvalActivation = 66, + GlobalLexicalEnvironment = 64, + LexicalEnvironment = 65, + ModuleEnvironment = 66, + StrictEvalActivation = 67, /// Scope object for with statements. /// ```js @@ -416,19 +419,19 @@ pub const JSType = enum(u8) { /// prop; // looks up prop in obj first /// } /// ``` - WithScope = 67, + WithScope = 68, - AsyncDisposableStack = 68, - DisposableStack = 69, + AsyncDisposableStack = 69, + DisposableStack = 70, /// Namespace object for ES6 modules. /// ```js /// import * as ns from 'module'; /// ns.exportedFunction() /// ``` - ModuleNamespaceObject = 70, + ModuleNamespaceObject = 71, - ShadowRealm = 71, + ShadowRealm = 72, /// Regular expression object. /// ```js @@ -436,7 +439,7 @@ pub const JSType = enum(u8) { /// new RegExp('pattern', 'flags') /// /abc/gi /// ``` - RegExpObject = 72, + RegExpObject = 73, /// JavaScript Date object for date/time operations. /// ```js @@ -444,7 +447,7 @@ pub const JSType = enum(u8) { /// new Date('2023-01-01') /// Date.now() /// ``` - JSDate = 73, + JSDate = 74, /// Proxy object that intercepts operations on another object. /// ```js @@ -452,7 +455,7 @@ pub const JSType = enum(u8) { /// get(obj, prop) { return obj[prop]; } /// }) /// ``` - ProxyObject = 74, + ProxyObject = 75, /// Generator object created by generator functions. /// ```js @@ -460,7 +463,7 @@ pub const JSType = enum(u8) { /// const g = gen(); /// g.next() /// ``` - Generator = 75, + Generator = 76, /// Async generator object for asynchronous iteration. /// ```js @@ -468,17 +471,17 @@ pub const JSType = enum(u8) { /// yield await promise; /// } /// ``` - AsyncGenerator = 76, + AsyncGenerator = 77, /// Iterator for Array objects. /// ```js /// [1,2,3][Symbol.iterator]() /// for (const x of array) {} /// ``` - JSArrayIterator = 77, + JSArrayIterator = 78, - Iterator = 78, - IteratorHelper = 79, + Iterator = 79, + IteratorHelper = 80, /// Iterator for Map objects. /// ```js @@ -487,32 +490,32 @@ pub const JSType = enum(u8) { /// map.entries() /// for (const [k,v] of map) {} /// ``` - MapIterator = 80, + MapIterator = 81, /// Iterator for Set objects. /// ```js /// set.values() /// for (const value of set) {} /// ``` - SetIterator = 81, + SetIterator = 82, /// Iterator for String objects. /// ```js /// 'hello'[Symbol.iterator]() /// for (const char of string) {} /// ``` - StringIterator = 82, + StringIterator = 83, - WrapForValidIterator = 83, + WrapForValidIterator = 84, /// Iterator for RegExp string matching. /// ```js /// 'abc'.matchAll(/./g) /// for (const match of string.matchAll(regex)) {} /// ``` - RegExpStringIterator = 84, + RegExpStringIterator = 85, - AsyncFromSyncIterator = 85, + AsyncFromSyncIterator = 86, /// JavaScript Promise object for asynchronous operations. /// ```js @@ -520,7 +523,7 @@ pub const JSType = enum(u8) { /// Promise.resolve(42) /// async function foo() { await promise; } /// ``` - JSPromise = 86, + JSPromise = 87, /// JavaScript Map object for key-value storage. /// ```js @@ -528,7 +531,7 @@ pub const JSType = enum(u8) { /// map.set(key, value) /// map.get(key) /// ``` - Map = 87, + Map = 88, /// JavaScript Set object for unique value storage. /// ```js @@ -536,34 +539,34 @@ pub const JSType = enum(u8) { /// set.add(value) /// set.has(value) /// ``` - Set = 88, + Set = 89, /// WeakMap for weak key-value references. /// ```js /// new WeakMap() /// weakMap.set(object, value) /// ``` - WeakMap = 89, + WeakMap = 90, /// WeakSet for weak value references. /// ```js /// new WeakSet() /// weakSet.add(object) /// ``` - WeakSet = 90, + WeakSet = 91, - WebAssemblyModule = 91, - WebAssemblyInstance = 92, - WebAssemblyGCObject = 93, + WebAssemblyModule = 92, + WebAssemblyInstance = 93, + WebAssemblyGCObject = 94, /// Boxed String object. /// ```js /// new String("hello") /// ``` - StringObject = 94, + StringObject = 95, - DerivedStringObject = 95, - InternalFieldTuple = 96, + DerivedStringObject = 96, + InternalFieldTuple = 97, MaxJS = 0b11111111, Event = 0b11101111, diff --git a/src/jsc/bindings/BunPlugin.cpp b/src/jsc/bindings/BunPlugin.cpp index b2a6bc6af2b6..5773394aed30 100644 --- a/src/jsc/bindings/BunPlugin.cpp +++ b/src/jsc/bindings/BunPlugin.cpp @@ -848,7 +848,7 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS return {}; } case JSPromise::Status::Rejected: { - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast(JSC::JSPromise::Status::Fulfilled))); + promise->setFlags(static_cast(JSC::JSPromise::Status::Fulfilled)); result = promise->result(); return JSValue::encode(result); } diff --git a/src/jsc/bindings/ZigGlobalObject.cpp b/src/jsc/bindings/ZigGlobalObject.cpp index a7969dc80971..d90901468d31 100644 --- a/src/jsc/bindings/ZigGlobalObject.cpp +++ b/src/jsc/bindings/ZigGlobalObject.cpp @@ -1617,6 +1617,9 @@ JSC_DECLARE_HOST_FUNCTION(makeDOMExceptionForBuiltins); JSC_DECLARE_HOST_FUNCTION(createWritableStreamFromInternal); JSC_DECLARE_HOST_FUNCTION(getInternalWritableStream); JSC_DECLARE_HOST_FUNCTION(isAbortSignal); +JSC_DECLARE_HOST_FUNCTION(jsBunPeekPromiseStatus); +JSC_DECLARE_HOST_FUNCTION(jsBunPeekPromiseSettledValue); +JSC_DECLARE_HOST_FUNCTION(jsBunPokePromiseAsHandled); JSC_DEFINE_HOST_FUNCTION(makeGetterTypeErrorForBuiltins, (JSGlobalObject * globalObject, CallFrame* callFrame)) { @@ -1718,6 +1721,43 @@ JSC_DEFINE_HOST_FUNCTION(isAbortSignal, (JSGlobalObject*, CallFrame* callFrame)) return JSValue::encode(jsBoolean(callFrame->uncheckedArgument(0).inherits())); } +// JSPromise lost its JSInternalFieldObjectImpl<2> layout in WebKit, so the +// @getPromiseInternalField/@putPromiseInternalField bytecode intrinsics that +// our builtins relied on no longer exist. These helpers expose the equivalent +// reads/writes through the new CompactPointerTuple/m_slot representation. + +static inline JSC::JSPromise* peekPromiseArgument(CallFrame* callFrame) +{ + ASSERT(callFrame->argumentCount() == 1); + JSValue arg = callFrame->uncheckedArgument(0); + if (!arg.inherits()) [[unlikely]] + return nullptr; + return static_cast(arg.asCell()); +} + +JSC_DEFINE_HOST_FUNCTION(jsBunPeekPromiseStatus, (JSGlobalObject*, CallFrame* callFrame)) +{ + auto* promise = peekPromiseArgument(callFrame); + if (!promise) [[unlikely]] + return JSValue::encode(jsNumber(0)); + return JSValue::encode(jsNumber(static_cast(promise->status()))); +} + +JSC_DEFINE_HOST_FUNCTION(jsBunPeekPromiseSettledValue, (JSGlobalObject*, CallFrame* callFrame)) +{ + auto* promise = peekPromiseArgument(callFrame); + if (!promise || promise->status() == JSC::JSPromise::Status::Pending) [[unlikely]] + return JSValue::encode(jsUndefined()); + return JSValue::encode(promise->result()); +} + +JSC_DEFINE_HOST_FUNCTION(jsBunPokePromiseAsHandled, (JSGlobalObject*, CallFrame* callFrame)) +{ + if (auto* promise = peekPromiseArgument(callFrame)) + promise->markAsHandled(); + return JSValue::encode(jsUndefined()); +} + extern "C" JSC::EncodedJSValue Bun__Jest__createTestModuleObject(JSC::JSGlobalObject*); extern "C" JSC::EncodedJSValue Bun__Jest__testModuleObject(Zig::GlobalObject* globalObject) { @@ -2860,6 +2900,9 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm) GlobalPropertyInfo(builtinNames.cloneArrayBufferPrivateName(), JSFunction::create(vm, this, 3, String(), cloneArrayBuffer, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.structuredCloneForStreamPrivateName(), JSFunction::create(vm, this, 1, String(), structuredCloneForStream, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.isAbortSignalPrivateName(), JSFunction::create(vm, this, 1, String(), isAbortSignal, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), + GlobalPropertyInfo(builtinNames.peekPromiseStatusPrivateName(), JSFunction::create(vm, this, 1, String(), jsBunPeekPromiseStatus, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), + GlobalPropertyInfo(builtinNames.peekPromiseSettledValuePrivateName(), JSFunction::create(vm, this, 1, String(), jsBunPeekPromiseSettledValue, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), + GlobalPropertyInfo(builtinNames.pokePromiseAsHandledPrivateName(), JSFunction::create(vm, this, 1, String(), jsBunPokePromiseAsHandled, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.getInternalWritableStreamPrivateName(), JSFunction::create(vm, this, 1, String(), getInternalWritableStream, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.createWritableStreamFromInternalPrivateName(), JSFunction::create(vm, this, 1, String(), createWritableStreamFromInternal, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), GlobalPropertyInfo(builtinNames.fulfillModuleSyncPrivateName(), JSFunction::create(vm, this, 1, String(), functionFulfillModuleSync, ImplementationVisibility::Public), PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly), @@ -3655,32 +3698,39 @@ extern "C" void JSC__Wasm__StreamingCompiler__addBytes(JSC::Wasm::StreamingCompi compiler->addBytes(std::span(spanPtr, spanSize)); } -static JSC::JSPromise* handleResponseOnStreamingAction(JSGlobalObject* lexicalGlobalObject, JSC::JSValue source, JSC::Wasm::CompilerMode mode, JSC::JSObject* importObject, std::optional&& compileOptions) +static void handleResponseOnStreamingAction(JSGlobalObject* lexicalGlobalObject, JSC::JSPromise* promise, JSC::JSValue source, JSC::Wasm::CompilerMode mode, JSC::JSObject* importObject, std::optional&& compileOptions) { auto globalObject = defaultGlobalObject(lexicalGlobalObject); auto& vm = JSC::getVM(globalObject); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSLockHolder locker(vm); - auto promise = JSC::JSPromise::create(vm, globalObject->promiseStructure()); auto sourceCode = makeSource("[wasm code]"_s, SourceOrigin(), SourceTaintedOrigin::Untainted); auto compiler = JSC::Wasm::StreamingCompiler::create(vm, mode, globalObject, promise, importObject, WTF::move(compileOptions), sourceCode); - // getBodyStreamOrBytesForWasmStreaming throws the proper exception. Since this is being - // executed in a .then(...) callback, throwing is perfectly fine. + // The streaming hook used to return a freshly created promise; the caller + // (webAssemblyCompileStreamingFunc) was a host function that propagated + // any pending exception into a rejected promise. Now the caller passes the + // already-allocated outer promise in and is itself an internal microtask + // (webAssemblyCompileStreaming in JSMicrotask.cpp) that does NOT catch the + // exception. If this callback throws, the outer promise is never settled + // and the awaiting test hangs. Convert any thrown exception into a + // rejection here. auto readableStreamMaybe = JSC::JSValue::decode(Zig__GlobalObject__getBodyStreamOrBytesForWasmStreaming( globalObject, JSC::JSValue::encode(source), compiler.ptr())); - RETURN_IF_EXCEPTION(scope, nullptr); + if (scope.exception()) [[unlikely]] { + promise->rejectWithCaughtException(globalObject, scope); + return; + } // We were able to get the slice synchronously. if (readableStreamMaybe.isNull()) { compiler->finalize(globalObject); - - // Apparently rejecting a Promise (done in JSC::Wasm::StreamingCompiler#fail) can throw - RETURN_IF_EXCEPTION(scope, nullptr); - return promise; + if (scope.exception()) [[unlikely]] + promise->rejectWithCaughtException(globalObject, scope); + return; } auto wrapper = WebCore::toJSNewlyCreated(globalObject, globalObject, WTF::move(compiler)); @@ -3690,18 +3740,18 @@ static JSC::JSPromise* handleResponseOnStreamingAction(JSGlobalObject* lexicalGl arguments.append(readableStreamMaybe); JSC::call(globalObject, builtin, callData, wrapper, arguments); - scope.assertNoException(); - return promise; + if (scope.exception()) [[unlikely]] + promise->rejectWithCaughtException(globalObject, scope); } -JSC::JSPromise* GlobalObject::compileStreaming(JSGlobalObject* globalObject, JSC::JSValue source, std::optional&& compileOptions) +void GlobalObject::compileStreaming(JSGlobalObject* globalObject, JSC::JSPromise* promise, JSC::JSValue source, std::optional&& compileOptions) { - return handleResponseOnStreamingAction(globalObject, source, JSC::Wasm::CompilerMode::Validation, nullptr, WTF::move(compileOptions)); + handleResponseOnStreamingAction(globalObject, promise, source, JSC::Wasm::CompilerMode::Validation, nullptr, WTF::move(compileOptions)); } -JSC::JSPromise* GlobalObject::instantiateStreaming(JSGlobalObject* globalObject, JSC::JSValue source, JSC::JSObject* importObject, std::optional&& compileOptions) +void GlobalObject::instantiateStreaming(JSGlobalObject* globalObject, JSC::JSPromise* promise, JSC::JSValue source, JSC::JSObject* importObject, std::optional&& compileOptions) { - return handleResponseOnStreamingAction(globalObject, source, JSC::Wasm::CompilerMode::FullCompile, importObject, WTF::move(compileOptions)); + handleResponseOnStreamingAction(globalObject, promise, source, JSC::Wasm::CompilerMode::FullCompile, importObject, WTF::move(compileOptions)); } GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(Zig::FFIFunction handler) diff --git a/src/jsc/bindings/ZigGlobalObject.h b/src/jsc/bindings/ZigGlobalObject.h index ec74c45e6c49..29bf8703bee2 100644 --- a/src/jsc/bindings/ZigGlobalObject.h +++ b/src/jsc/bindings/ZigGlobalObject.h @@ -201,8 +201,8 @@ class GlobalObject : public Bun::GlobalScope { static JSC::JSPromise* moduleLoaderFetch(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue key, RefPtr, RefPtr); static JSC::JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue key, JSC::JSModuleRecord*, RefPtr); static JSC::JSValue moduleLoaderEvaluate(JSGlobalObject*, JSC::JSModuleLoader*, JSValue key, JSValue moduleRecordValue, RefPtr, JSValue sentValue, JSValue resumeMode); - static JSC::JSPromise* compileStreaming(JSGlobalObject*, JSC::JSValue source, std::optional&&); - static JSC::JSPromise* instantiateStreaming(JSGlobalObject*, JSC::JSValue source, JSC::JSObject* importObject, std::optional&&); + static void compileStreaming(JSGlobalObject*, JSC::JSPromise*, JSC::JSValue source, std::optional&&); + static void instantiateStreaming(JSGlobalObject*, JSC::JSPromise*, JSC::JSValue source, JSC::JSObject* importObject, std::optional&&); static ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*); static void promiseRejectionTracker(JSGlobalObject*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation); diff --git a/src/jsc/bindings/bindings.cpp b/src/jsc/bindings/bindings.cpp index 22ac38063376..94fe73eb68f1 100644 --- a/src/jsc/bindings/bindings.cpp +++ b/src/jsc/bindings/bindings.cpp @@ -2264,24 +2264,49 @@ static void collectAsyncStackFramesFromPromise(JSC::VM& vm, JSC::JSCell* owner, return *out != nullptr; }; + auto unwrapGeneratorFromContext = [&](JSC::JSValue context) -> JSC::JSGenerator* { + JSC::InternalFieldTuple* tuple = nullptr; + if (dynamicCastValue(context, &tuple)) + context = tuple->getInternalField(0); + JSC::JSGenerator* generator = nullptr; + dynamicCastValue(context, &generator); + return generator; + }; + // Walk reaction->context → generator. If context is not a generator (e.g. // thenable-chain from `return promise` without await inside an async // function), follow reaction->promise() to the next promise in the chain. // Cap hops to avoid pathological chains. + // + // The pending reaction can be stored two ways: + // - Inline in the JSPromise itself (the common single-await / single-then + // fast path). InternalMicrotask carries the await generator context in + // m_slot; FulfillHandler/RejectHandler carry the result promise in + // payloadCell() and the handler in m_slot. + // - As a heap-allocated JSPromiseReaction list once a second handler is + // attached, headed at payloadCell(). auto getAwaitingGenerator = [&](JSC::JSPromise* p) -> JSC::JSGenerator* { for (unsigned hops = 0; p && hops < 32; hops++) { if (p->status() != JSC::JSPromise::Status::Pending) return nullptr; - JSC::JSValue reactionsValue = p->reactionsOrResult(); - JSC::JSPromiseReaction* reaction = nullptr; - if (!dynamicCastValue(reactionsValue, &reaction)) + switch (p->inlineReactionKind()) { + case JSC::JSPromise::InlineReactionKind::InternalMicrotask: { + if (auto* generator = unwrapGeneratorFromContext(p->inlineReactionContext())) + return generator; + return nullptr; + } + case JSC::JSPromise::InlineReactionKind::FulfillHandler: + case JSC::JSPromise::InlineReactionKind::RejectHandler: { + p = p->inlineHandlerResultPromise(); + continue; + } + case JSC::JSPromise::InlineReactionKind::None: + break; + } + auto* reaction = dynamicDowncast(p->payloadCell()); + if (!reaction) return nullptr; - JSC::JSValue context = JSC::JSPromiseReaction::tryGetContext(reactionsValue); - JSC::InternalFieldTuple* tuple = nullptr; - if (dynamicCastValue(context, &tuple)) - context = tuple->getInternalField(0); - JSC::JSGenerator* generator = nullptr; - if (dynamicCastValue(context, &generator)) + if (auto* generator = unwrapGeneratorFromContext(JSC::JSPromiseReaction::tryGetContext(reaction))) return generator; // No generator in context — follow the thenable chain to the // promise this reaction resolves/rejects. @@ -3748,13 +3773,13 @@ void JSC__JSPromise__rejectOnNextTickWithHandled(JSC::JSPromise* promise, JSC::J auto& vm = JSC::getVM(lexicalGlobalObject); auto scope = DECLARE_THROW_SCOPE(vm); - uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); + uint16_t flags = promise->flags(); if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) { if (handled) { flags |= JSC::JSPromise::isHandledFlag; } - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag)); + promise->setFlags(static_cast(flags | JSC::JSPromise::isFirstResolvingFunctionCalledFlag)); auto* globalObject = uncheckedDowncast(promise->globalObject()); auto rejectPromiseFunction = globalObject->rejectPromiseFunction(); @@ -3784,23 +3809,21 @@ JSC::JSPromise* JSC__JSPromise__resolvedPromise(JSC::JSGlobalObject* globalObjec { auto& vm = JSC::getVM(globalObject); JSC::JSPromise* promise = JSC::JSPromise::create(vm, globalObject->promiseStructure()); - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast(JSC::JSPromise::Status::Fulfilled))); - promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, JSC::JSValue::decode(JSValue1)); + promise->setFlags(static_cast(JSC::JSPromise::Status::Fulfilled)); + promise->setSlot(vm, JSC::JSValue::decode(JSValue1)); return promise; } [[ZIG_EXPORT(nothrow)]] JSC::EncodedJSValue JSC__JSPromise__result(JSC::JSPromise* promise, JSC::VM* arg1) { - auto& vm = *arg1; + UNUSED_PARAM(arg1); // if the promise is rejected we automatically mark it as handled so it // doesn't end up in the promise rejection tracker switch (promise->status()) { case JSC::JSPromise::Status::Rejected: { - uint32_t flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); - if (!(flags & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) { - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isHandledFlag)); - } + if (!(promise->flags() & JSC::JSPromise::isFirstResolvingFunctionCalledFlag)) + promise->markAsHandled(); } // fallthrough intended case JSC::JSPromise::Status::Fulfilled: { @@ -3910,9 +3933,8 @@ bool JSC__JSInternalPromise__isHandled(const JSC::JSPromise* arg0) } void JSC__JSInternalPromise__setHandled(JSC::JSPromise* promise, JSC::VM* arg1) { - auto& vm = *arg1; - auto flags = promise->internalField(JSC::JSPromise::Field::Flags).get().asUInt32(); - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(flags | JSC::JSPromise::isHandledFlag)); + UNUSED_PARAM(arg1); + promise->markAsHandled(); } #pragma mark - JSC::JSGlobalObject @@ -5032,8 +5054,8 @@ JSC::EncodedJSValue JSC__JSPromise__rejectedPromiseValue(JSC::JSGlobalObject* gl { auto& vm = JSC::getVM(globalObject); JSC::JSPromise* promise = JSC::JSPromise::create(vm, globalObject->promiseStructure()); - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast(JSC::JSPromise::Status::Rejected))); - promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, JSC::JSValue::decode(JSValue1)); + promise->setFlags(static_cast(JSC::JSPromise::Status::Rejected)); + promise->setSlot(vm, JSC::JSValue::decode(JSValue1)); JSC::ensureStillAliveHere(promise); JSC::ensureStillAliveHere(JSC::JSValue::decode(JSValue1)); return JSC::JSValue::encode(promise); @@ -5044,8 +5066,8 @@ JSC::EncodedJSValue JSC__JSPromise__resolvedPromiseValue(JSC::JSGlobalObject* gl { auto& vm = JSC::getVM(globalObject); JSC::JSPromise* promise = JSC::JSPromise::create(vm, globalObject->promiseStructure()); - promise->internalField(JSC::JSPromise::Field::Flags).set(vm, promise, jsNumber(static_cast(JSC::JSPromise::Status::Fulfilled))); - promise->internalField(JSC::JSPromise::Field::ReactionsOrResult).set(vm, promise, JSC::JSValue::decode(JSValue1)); + promise->setFlags(static_cast(JSC::JSPromise::Status::Fulfilled)); + promise->setSlot(vm, JSC::JSValue::decode(JSValue1)); JSC::ensureStillAliveHere(promise); JSC::ensureStillAliveHere(JSC::JSValue::decode(JSValue1)); return JSC::JSValue::encode(promise); diff --git a/src/jsc/bindings/webcore/JSCookieMap.cpp b/src/jsc/bindings/webcore/JSCookieMap.cpp index 557bc166c0ff..e6b8ad3b53e9 100644 --- a/src/jsc/bindings/webcore/JSCookieMap.cpp +++ b/src/jsc/bindings/webcore/JSCookieMap.cpp @@ -151,8 +151,11 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSCookieMapDOMConstructo } init = WTF::move(seqSeq); } else { - // Handle as record - HashMap record; + // Handle as record. Build a sequence so the + // CookieMap preserves insertion order — going through HashMap would + // scramble it (and the hash order itself shifted when WTF moved + // its string hash to RapidHash). + Vector> seqSeq; PropertyNameArrayBuilder propertyNames(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude); JSObject::getOwnPropertyNames(object, lexicalGlobalObject, propertyNames, DontEnumPropertiesMode::Include); @@ -165,9 +168,12 @@ template<> JSC::EncodedJSValue JSC_HOST_CALL_ATTRIBUTES JSCookieMapDOMConstructo auto valueStr = value.toString(lexicalGlobalObject)->value(lexicalGlobalObject); RETURN_IF_EXCEPTION(throwScope, {}); - record.set(propertyName.string(), valueStr); + Vector pair; + pair.append(propertyName.string()); + pair.append(WTF::move(valueStr)); + seqSeq.append(WTF::move(pair)); } - init = WTF::move(record); + init = WTF::move(seqSeq); } } else { throwTypeError(lexicalGlobalObject, throwScope, "Invalid initializer type"_s); diff --git a/test/js/bun/cookie/cookie-map.test.ts b/test/js/bun/cookie/cookie-map.test.ts index 3675057b212b..71ebbd845953 100644 --- a/test/js/bun/cookie/cookie-map.test.ts +++ b/test/js/bun/cookie/cookie-map.test.ts @@ -344,8 +344,8 @@ describe("iterator", () => { expect([...cookies.entries()].map(([key, value]) => `${key}=${value}`).join("\n")).toMatchInlineSnapshot(` "e=f g=h - c=d - a=b" + a=b + c=d" `); }); });