diff --git a/packages/bun-inspector-protocol/scripts/generate-protocol.ts b/packages/bun-inspector-protocol/scripts/generate-protocol.ts index 99b97af9577c..129a58daec6d 100644 --- a/packages/bun-inspector-protocol/scripts/generate-protocol.ts +++ b/packages/bun-inspector-protocol/scripts/generate-protocol.ts @@ -8,7 +8,7 @@ // tarball ships it at its top level, so after `bun bd` it can be found in the // build cache for the WEBKIT_VERSION pinned in scripts/build/deps/webkit.ts; // that is what is used when no path is given. A local WebKit build writes it -// to /JavaScriptCore/DerivedSources/CombinedDomains.json. +// to /JavaScriptCore/Scripts/CombinedDomains.json. // // Pass --v8 to also refresh src/protocol/v8 from the Chrome DevTools protocol // repository (requires network access). diff --git a/scripts/build/deps/webkit.ts b/scripts/build/deps/webkit.ts index 2ee17190b4eb..783ec7f9913d 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 = "4556ec7c1c4770937284ee93179fff0a39c437e2"; +export const WEBKIT_VERSION = "b1e30d08cb1428688c5f24109fe4f534096ee2ad"; /** * WebKit (JavaScriptCore) — the JS engine. diff --git a/src/jsc/bindings/BunProcess.cpp b/src/jsc/bindings/BunProcess.cpp index adaa1a5f1831..c0e19f8782bb 100644 --- a/src/jsc/bindings/BunProcess.cpp +++ b/src/jsc/bindings/BunProcess.cpp @@ -837,7 +837,7 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionUmask, (JSGlobalObject * globalObject, auto str = value.getString(globalObject); RETURN_IF_EXCEPTION(throwScope, {}); auto policy = WTF::TrailingJunkPolicy::Disallow; - auto opt = str.is8Bit() ? WTF::parseInteger(str.span8(), 8, policy) : WTF::parseInteger(str.span16(), 8, policy); + auto opt = str.is8Bit() ? WTF::parseInteger(str.span8(), 8, policy) : WTF::parseInteger(str.span16(), 8, policy); if (!opt.has_value()) return Bun::ERR::INVALID_ARG_VALUE(throwScope, globalObject, "mask"_s, value, "must be a 32-bit unsigned integer or an octal string"_s); newUmask = opt.value(); } else { diff --git a/src/jsc/bindings/BunString.cpp b/src/jsc/bindings/BunString.cpp index 824e2c103fef..98dd90b34c36 100644 --- a/src/jsc/bindings/BunString.cpp +++ b/src/jsc/bindings/BunString.cpp @@ -107,7 +107,7 @@ static void assertStaticStringIsNotCommon(EncodedSlice slice) { if (Zig::isTaggedUTF16Ptr(slice.ptr)) return; - std::span literal { Zig::untag(slice.ptr), slice.len }; + std::span literal { byteCast(Zig::untag(slice.ptr)), slice.len }; ASSERT_WITH_MESSAGE(!CommonStrings::isCommonStringLiteral(literal), "\"%.*s\" is in BunCommonStrings.h: use global.common_strings() instead of String::static_(..).to_js()", static_cast(literal.size()), reinterpret_cast(literal.data())); diff --git a/src/jsc/bindings/JSBuffer.cpp b/src/jsc/bindings/JSBuffer.cpp index 29fb49bb4002..c0325e63b350 100644 --- a/src/jsc/bindings/JSBuffer.cpp +++ b/src/jsc/bindings/JSBuffer.cpp @@ -605,7 +605,7 @@ JSC::EncodedJSValue constructFromEncoding(JSGlobalObject* lexicalGlobalObject, W } case WebCore::BufferEncodingType::ascii: // ascii is a noop for latin1 case WebCore::BufferEncodingType::latin1: { // The native encoding is latin1, so we don't need to do any conversion. - result = JSValue::encode(createBuffer(lexicalGlobalObject, span.data(), span.size())); + result = JSValue::encode(createBuffer(lexicalGlobalObject, byteCast(span.data()), span.size())); break; } default: { @@ -2092,7 +2092,7 @@ JSC::EncodedJSValue jsBufferToStringFromBytes(JSGlobalObject* lexicalGlobalObjec if (str.isNull()) [[unlikely]] { return Bun::ERR::MEMORY_ALLOCATION_FAILED(scope, lexicalGlobalObject); } - Bun__encoding__writeLatin1(bytes.data(), bytes.size(), data.data(), data.size(), static_cast(encoding)); + Bun__encoding__writeLatin1(byteCast(bytes.data()), bytes.size(), byteCast(data.data()), data.size(), static_cast(encoding)); return JSValue::encode(jsString(vm, WTF::move(str))); } diff --git a/src/jsc/bindings/JSCommonJSModule.cpp b/src/jsc/bindings/JSCommonJSModule.cpp index e9df7c93de2e..bec3e3802776 100644 --- a/src/jsc/bindings/JSCommonJSModule.cpp +++ b/src/jsc/bindings/JSCommonJSModule.cpp @@ -158,7 +158,7 @@ class GraphCommonJSSourceProvider final : public JSC::SourceProvider { private: GraphCommonJSSourceProvider(Ref&& source, unsigned overlayShape) - : JSC::SourceProvider(source->sourceOrigin(), String(source->sourceURL()), String(source->preRedirectURL()), source->sourceTaintedOrigin(), source->startPosition(), source->sourceType(), source->provenance()) + : JSC::SourceProvider(source->sourceOrigin(), String(source->sourceURL()), String(source->preRedirectURL()), source->taintedness(), source->startPosition(), source->sourceType(), source->provenance()) , m_source(WTF::move(source)) , m_overlayShape(overlayShape) { diff --git a/src/jsc/bindings/LintReport.cpp b/src/jsc/bindings/LintReport.cpp index a540a77d202f..4034bb7f431e 100644 --- a/src/jsc/bindings/LintReport.cpp +++ b/src/jsc/bindings/LintReport.cpp @@ -88,7 +88,7 @@ class Writer { { if (text.is8Bit() && charactersAreAllASCII(text.span8())) [[likely]] { word(text.length()); - bytes(text.span8()); + bytes(byteCast(text.span8())); return; } // No character takes more than three bytes for each UTF-16 code unit. diff --git a/src/jsc/bindings/NodeVM.cpp b/src/jsc/bindings/NodeVM.cpp index 7262f44d09f5..4c05340489ca 100644 --- a/src/jsc/bindings/NodeVM.cpp +++ b/src/jsc/bindings/NodeVM.cpp @@ -100,6 +100,10 @@ static JSValue scriptFetchParametersToImportAttributes(JSGlobalObject* globalObj // Unreachable with BUN_JSC_ADDITIONS (`type: "text"` parses as HostDefined); kept so the switch stays exhaustive. obj->putDirect(vm, vm.propertyNames->type, jsNontrivialString(vm, "text"_s)); break; + case JSC::ScriptFetchParameters::Type::CSS: + // Unreachable for the same reason. + obj->putDirect(vm, vm.propertyNames->type, jsNontrivialString(vm, "css"_s)); + break; case JSC::ScriptFetchParameters::Type::HostDefined: obj->putDirect(vm, vm.propertyNames->type, jsString(vm, params->hostDefinedImportType())); break; @@ -541,6 +545,9 @@ static void writeArrowHeaderStack(VM& vm, ErrorInstance* errorInstance, const St bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr exception, ThrowScope& throwScope) { if (auto* errorInstance = dynamicDowncast(exception->value())) { + // No stack is captured for a throw that a `catch` clause further up is certain to handle. The error's own is + // taken then, and that one is gone once the `stack` property is materialized. + auto& e_stack = exception->stackForReporting(vm); errorInstance->materializeErrorInfoIfNeeded(vm, vm.propertyNames->stack); RETURN_IF_EXCEPTION(throwScope, {}); JSValue stack_jsval = errorInstance->get(globalObject, vm.propertyNames->stack); @@ -551,7 +558,6 @@ bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtrstack(); size_t stack_size = e_stack.size(); if (stack_size == 0) { return false; diff --git a/src/jsc/bindings/StringBuilderBinding.cpp b/src/jsc/bindings/StringBuilderBinding.cpp index 1d419eedba9c..69a4ded43eb5 100644 --- a/src/jsc/bindings/StringBuilderBinding.cpp +++ b/src/jsc/bindings/StringBuilderBinding.cpp @@ -45,9 +45,9 @@ extern "C" void StringBuilder__appendString(WTF::StringBuilder* builder, const B str->appendToBuilder(*builder); } -extern "C" void StringBuilder__appendLChar(WTF::StringBuilder* builder, Latin1Character c) +extern "C" void StringBuilder__appendLChar(WTF::StringBuilder* builder, uint8_t c) { - builder->append(c); + builder->append(Latin1Character { c }); } extern "C" void StringBuilder__appendUChar(WTF::StringBuilder* builder, UChar c) diff --git a/src/jsc/bindings/ZigException.cpp b/src/jsc/bindings/ZigException.cpp index cfd1059c2e78..30c6ffa62300 100644 --- a/src/jsc/bindings/ZigException.cpp +++ b/src/jsc/bindings/ZigException.cpp @@ -162,7 +162,7 @@ static void populateStackFramePosition(const JSC::StackFrame& stackFrame, BunStr lineEnd++; } - const unsigned char* bytes = sourceString.span8().data(); + const unsigned char* bytes = byteCast(sourceString.span8().data()); // Most of the time, when you look at a stack trace, you want a couple lines above. @@ -823,7 +823,7 @@ void exceptionFromString(ZigException& except, JSC::JSValue value, JSC::JSGlobal extern "C" void JSC__Exception__getStackTrace(JSC::Exception* arg0, JSC::JSGlobalObject* global, ZigStackTrace* trace) { - populateStackTrace(arg0->vm(), arg0->stack(), *trace, PopulateStackTraceFlags::OnlyPosition); + populateStackTrace(arg0->vm(), arg0->stackForReporting(arg0->vm()), *trace, PopulateStackTraceFlags::OnlyPosition); } extern "C" [[ZIG_EXPORT(check_slow)]] void JSC__JSValue__toZigException(JSC::EncodedJSValue jsException, JSC::JSGlobalObject* global, ZigException* exception) @@ -872,8 +872,9 @@ extern "C" void ZigException__collectSourceLines(JSC::EncodedJSValue jsException auto* jscException = uncheckedDowncast(value); JSValue unwrapped = jscException->value(); - if (jscException->stack().size() > 0) { - populateStackTrace(global->vm(), jscException->stack(), exception->stack, PopulateStackTraceFlags::OnlySourceLines); + auto& stack = jscException->stackForReporting(global->vm()); + if (stack.size() > 0) { + populateStackTrace(global->vm(), stack, exception->stack, PopulateStackTraceFlags::OnlySourceLines); } exceptionFromString(*exception, unwrapped, global); diff --git a/src/jsc/bindings/ZigGlobalObject.cpp b/src/jsc/bindings/ZigGlobalObject.cpp index 467b626d8604..2daaea3a024e 100644 --- a/src/jsc/bindings/ZigGlobalObject.cpp +++ b/src/jsc/bindings/ZigGlobalObject.cpp @@ -1492,7 +1492,7 @@ JSC_DEFINE_HOST_FUNCTION(functionBTOA, RELEASE_AND_RETURN( throwScope, Bun__encoding__toString( - encodedString.span8().data(), + byteCast(encodedString.span8().data()), length, globalObject, static_cast(WebCore::BufferEncodingType::base64))); diff --git a/src/jsc/bindings/bindings.cpp b/src/jsc/bindings/bindings.cpp index 555ec7972294..9f6a0f2e4747 100644 --- a/src/jsc/bindings/bindings.cpp +++ b/src/jsc/bindings/bindings.cpp @@ -34,6 +34,7 @@ #include "JavaScriptCore/AggregateError.h" #include "JavaScriptCore/ArrayBufferView.h" #include "JavaScriptCore/ArrayStorage.h" +#include "JavaScriptCore/SourceProviderCache.h" #include "JavaScriptCore/SparseArrayValueMap.h" #include "JavaScriptCore/BytecodeIndex.h" #include "JavaScriptCore/CodeBlock.h" @@ -2829,7 +2830,7 @@ static JSC::EncodedJSValue systemErrorToErrorInstance(const SystemError* arg0, J // bun_sys::Error tags are static literals; "write" and "close" are common strings. JSC::JSValue syscall; auto staticSyscall = err.syscall.tag == BunStringTag::StaticEncodedSlice && !Zig::isTaggedUTF16Ptr(err.syscall.impl.encoded.ptr) - ? std::span { Zig::untag(err.syscall.impl.encoded.ptr), err.syscall.impl.encoded.len } + ? std::span { byteCast(Zig::untag(err.syscall.impl.encoded.ptr)), err.syscall.impl.encoded.len } : std::span {}; if (equalSpans(staticSyscall, "write"_span8)) syscall = Bun::commonStrings(vm).writeString(); @@ -3763,7 +3764,7 @@ JSC::EncodedJSValue JSC__JSGlobalObject__createAggregateErrorWithArray(JSC::JSGl auto ref = String(ExternalStringImpl::create({ reinterpret_cast(Zig::untag(str.ptr)), str.len }, Zig::untagVoid(str.ptr), free_global_string)); return JSC::JSValue::encode(JSC::jsString(arg1->vm(), WTF::move(ref))); } else { - auto ref = String(ExternalStringImpl::create({ Zig::untag(str.ptr), str.len }, Zig::untagVoid(str.ptr), free_global_string)); + auto ref = String(ExternalStringImpl::create({ byteCast(Zig::untag(str.ptr)), str.len }, Zig::untagVoid(str.ptr), free_global_string)); return JSC::JSValue::encode(JSC::jsString(arg1->vm(), WTF::move(ref))); } } @@ -4518,7 +4519,7 @@ JSC::EncodedJSValue JSC__JSValue__createObject2(JSC::JSGlobalObject* globalObjec } // Since Identifier might not ref the string, we need to ensure it doesn't get deref'd until this function returns - const auto propertyString = String(StringImpl::createWithoutCopying({ arg1, arg2 })); + const auto propertyString = String(StringImpl::createWithoutCopying({ byteCast(arg1), arg2 })); const auto identifier = JSC::Identifier::fromString(vm, propertyString); const auto property = JSC::PropertyName(identifier); @@ -4544,7 +4545,7 @@ JSC::EncodedJSValue JSC__JSValue__getPropertyValue(JSC::EncodedJSValue encodedVa } // Since Identifier might not ref the string, we need to ensure it doesn't get deref'd until this function returns - const auto propertyString = String(StringImpl::createWithoutCopying({ propertyName, propertyNameLength })); + const auto propertyString = String(StringImpl::createWithoutCopying({ byteCast(propertyName), propertyNameLength })); const auto identifier = JSC::Identifier::fromString(vm, propertyString); const auto property = JSC::PropertyName(identifier); @@ -4884,7 +4885,7 @@ BunString JSC__JSValue__getClassName(JSC::EncodedJSValue JSValue0, JSC::JSGlobal bool JSC__JSValue__getClassInfoName(JSValue value, const uint8_t** outPtr, size_t* outLen) { if (auto info = value.classInfoOrNull()) { - *outPtr = info->className.span8().data(); + *outPtr = byteCast(info->className.span8().data()); *outLen = info->className.span8().size(); return true; } @@ -4997,8 +4998,10 @@ size_t JSC__VM__runGC(JSC::VM* vm, bool sync) vm->finalizeSynchronousJSExecution(); + // A full collection only drops the caches that nothing has used since the one before it. + vm->sourceProviderCacheMap.clear(); + if (sync) { - vm->clearSourceProviderCaches(); vm->heap.deleteAllUnlinkedCodeBlocks(JSC::PreventCollectionAndDeleteAllCode); vm->heap.collectNow(JSC::Sync, JSC::CollectionScope::Full); #if IS_MALLOC_DEBUGGING_ENABLED && OS(DARWIN) diff --git a/src/jsc/bindings/decodeURIComponentSIMD.cpp b/src/jsc/bindings/decodeURIComponentSIMD.cpp index ccf50a8eab21..f13fc2e13013 100644 --- a/src/jsc/bindings/decodeURIComponentSIMD.cpp +++ b/src/jsc/bindings/decodeURIComponentSIMD.cpp @@ -29,7 +29,7 @@ ALWAYS_INLINE static void appendLiteralRun(StringBuilder& result, std::span input) @@ -64,7 +64,7 @@ WTF::String decodeURIComponentSIMD(std::span input) if (inputIsASCII) return String(lchar); - return Zig::convertUTF8ToString(lchar); + return Zig::convertUTF8ToString(input); slow_path: while (cursor < end && *cursor != '%') { @@ -96,7 +96,7 @@ WTF::String decodeURIComponentSIMD(std::span input) // Start of UTF-8 sequence if ((byte & 0x80) == 0) { // ASCII - result.append(byte); + result.append(static_cast(byte)); cursor += 3; } else if ((byte & 0xE0) == 0xC0) { // 2-byte sequence diff --git a/src/jsc/bindings/headers-handwritten.h b/src/jsc/bindings/headers-handwritten.h index f4c22e820680..8ef784f8f1f1 100644 --- a/src/jsc/bindings/headers-handwritten.h +++ b/src/jsc/bindings/headers-handwritten.h @@ -421,13 +421,13 @@ extern "C" const char* Bun__version_sha; extern "C" void EncodedSlice__freeGlobal(const unsigned char* ptr, size_t len); -extern "C" size_t Bun__encoding__writeLatin1(const unsigned char* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); +extern "C" size_t Bun__encoding__writeLatin1(const Latin1Character* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); extern "C" size_t Bun__encoding__writeUTF16(const char16_t* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); -extern "C" size_t Bun__encoding__byteLengthLatin1AsUTF8(const unsigned char* ptr, size_t len); +extern "C" size_t Bun__encoding__byteLengthLatin1AsUTF8(const Latin1Character* ptr, size_t len); extern "C" size_t Bun__encoding__byteLengthUTF16AsUTF8(const char16_t* ptr, size_t len); -extern "C" JSC::EncodedJSValue Bun__encoding__constructFromLatin1(void*, const unsigned char* ptr, size_t len, Encoding encoding); +extern "C" JSC::EncodedJSValue Bun__encoding__constructFromLatin1(void*, const Latin1Character* ptr, size_t len, Encoding encoding); extern "C" JSC::EncodedJSValue Bun__encoding__constructFromUTF16(void*, const char16_t* ptr, size_t len, Encoding encoding); extern "C" void Bun__EventLoop__runCallback2(JSC::JSGlobalObject* global, JSC::EncodedJSValue callback, JSC::EncodedJSValue thisValue, JSC::EncodedJSValue arg1, JSC::EncodedJSValue arg2); diff --git a/src/jsc/bindings/helpers.h b/src/jsc/bindings/helpers.h index e4f4b8730c4d..c12eedab5bae 100644 --- a/src/jsc/bindings/helpers.h +++ b/src/jsc/bindings/helpers.h @@ -133,7 +133,7 @@ static const WTF::String toString(EncodedSlice str) } return !isTaggedUTF16Ptr(str.ptr) - ? WTF::String(WTF::ExternalStringImpl::create({ untag(str.ptr), str.len }, untagVoid(str.ptr), free_global_string)) + ? WTF::String(WTF::ExternalStringImpl::create({ byteCast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)) : WTF::String(WTF::ExternalStringImpl::create({ reinterpret_cast(untag(str.ptr)), str.len }, untagVoid(str.ptr), free_global_string)); } @@ -143,7 +143,7 @@ static const WTF::String toString(EncodedSlice str) } return !isTaggedUTF16Ptr(str.ptr) - ? WTF::String(WTF::StringImpl::createWithoutCopying({ untag(str.ptr), str.len })) + ? WTF::String(WTF::StringImpl::createWithoutCopying({ byteCast(untag(str.ptr)), str.len })) : WTF::String(WTF::StringImpl::createWithoutCopying( { reinterpret_cast(untag(str.ptr)), str.len })); } @@ -171,7 +171,7 @@ static const WTF::String toStringCopy(EncodedSlice str, StringPointer ptr) } return !isTaggedUTF16Ptr(str.ptr) - ? WTF::String(WTF::StringImpl::create(std::span { &untag(str.ptr)[ptr.off], ptr.len })) + ? WTF::String(WTF::StringImpl::create(std::span { byteCast(&untag(str.ptr)[ptr.off]), ptr.len })) : WTF::String(WTF::StringImpl::create( std::span { &reinterpret_cast(untag(str.ptr))[ptr.off], ptr.len })); } @@ -234,7 +234,7 @@ static void appendToBuilder(EncodedSlice str, WTF::StringBuilder& builder) return; } - builder.append({ untag(str.ptr), str.len }); + builder.append({ byteCast(untag(str.ptr)), str.len }); } static JSC::JSString* toJSStringGC(EncodedSlice str, JSC::JSGlobalObject* global) @@ -257,7 +257,7 @@ static EncodedSlice toEncodedSlice(const WTF::StringImpl* str) { return (!str || str->isEmpty()) ? EncodedSliceEmpty - : EncodedSlice { str->is8Bit() ? str->span8().data() : taggedUTF16Ptr(str->span16().data()), + : EncodedSlice { str->is8Bit() ? byteCast(str->span8().data()) : taggedUTF16Ptr(str->span16().data()), str->length() }; } @@ -265,7 +265,7 @@ static EncodedSlice toEncodedSlice(const WTF::StringView& str) { return str.isEmpty() ? EncodedSliceEmpty - : EncodedSlice { str.is8Bit() ? str.span8().data() : taggedUTF16Ptr(str.span16().data()), + : EncodedSlice { str.is8Bit() ? byteCast(str.span8().data()) : taggedUTF16Ptr(str.span16().data()), str.length() }; } @@ -301,7 +301,7 @@ static const WTF::String toStringStatic(EncodedSlice str) // and asserts a trailing NUL) over-reads. Intern via a length-bounded span // instead — same atom-table caching, no NUL dependency. auto* untagged = untag(str.ptr); - return WTF::String(AtomStringImpl::add(std::span { untagged, str.len })); + return WTF::String(AtomStringImpl::add(std::span { byteCast(untagged), str.len })); } static const JSC::Identifier toIdentifier(EncodedSlice str, JSC::JSGlobalObject* global) diff --git a/src/jsc/bindings/node/crypto/CryptoPrimes.cpp b/src/jsc/bindings/node/crypto/CryptoPrimes.cpp index 034f0a2f4ee7..fcf2b4d1992f 100644 --- a/src/jsc/bindings/node/crypto/CryptoPrimes.cpp +++ b/src/jsc/bindings/node/crypto/CryptoPrimes.cpp @@ -242,7 +242,7 @@ JSValue GeneratePrimeJob::result(JSGlobalObject* globalObject, JSC::ThrowScope& return {}; } - JSValue result = JSBigInt::parseInt(globalObject, vm, primeHex.span(), 16, JSBigInt::ErrorParseMode::IgnoreExceptions, JSBigInt::ParseIntSign::Unsigned); + JSValue result = JSBigInt::parseInt(globalObject, vm, byteCast(primeHex.span()), 16, JSBigInt::ErrorParseMode::IgnoreExceptions, JSBigInt::ParseIntSign::Unsigned); RETURN_IF_EXCEPTION(scope, {}); if (result.isEmpty()) { ERR::CRYPTO_OPERATION_FAILED(scope, globalObject, "could not generate prime"_s); diff --git a/src/jsc/bindings/node/crypto/KeyObject.cpp b/src/jsc/bindings/node/crypto/KeyObject.cpp index d3c513910d90..c0abf2bf9c7d 100644 --- a/src/jsc/bindings/node/crypto/KeyObject.cpp +++ b/src/jsc/bindings/node/crypto/KeyObject.cpp @@ -799,7 +799,7 @@ void KeyObject::getRsaKeyDetails(JSGlobalObject* globalObject, ThrowScope& scope return; } - JSValue publicExponent = JSBigInt::parseInt(globalObject, vm, publicExponentHex.span(), 16, JSBigInt::ErrorParseMode::IgnoreExceptions, JSBigInt::ParseIntSign::Unsigned); + JSValue publicExponent = JSBigInt::parseInt(globalObject, vm, byteCast(publicExponentHex.span()), 16, JSBigInt::ErrorParseMode::IgnoreExceptions, JSBigInt::ParseIntSign::Unsigned); RETURN_IF_EXCEPTION(scope, ); if (!publicExponent) { ERR::CRYPTO_OPERATION_FAILED(scope, globalObject, "Failed to create public exponent"_s); diff --git a/src/jsc/bindings/sqlite/JSSQLStatement.cpp b/src/jsc/bindings/sqlite/JSSQLStatement.cpp index 95e8561b0660..e15476bb2222 100644 --- a/src/jsc/bindings/sqlite/JSSQLStatement.cpp +++ b/src/jsc/bindings/sqlite/JSSQLStatement.cpp @@ -464,7 +464,7 @@ class SQLiteBindingsMap { name += prefixOffset; size_t namelen = strlen(reinterpret_cast(name)); if (prefixOffset == 1 && name[0] >= '0' && name[0] <= '9') { - auto integer = WTF::parseInteger(StringView({ name, namelen }), 10); + auto integer = WTF::parseInteger(StringView({ byteCast(name), namelen }), 10); if (integer.has_value()) { hasOutOfOrderNames = true; bindingNames.clear(); diff --git a/src/jsc/bindings/v8/V8String.cpp b/src/jsc/bindings/v8/V8String.cpp index 7a4ee8a9b37d..4c2fe9735446 100644 --- a/src/jsc/bindings/v8/V8String.cpp +++ b/src/jsc/bindings/v8/V8String.cpp @@ -81,7 +81,7 @@ MaybeLocal String::NewFromOneByte(Isolate* isolate, const uint8_t* data, } auto& vm = isolate->vm(); - std::span span(data, length); + std::span span(byteCast(data), length); JSString* jsString = nullptr; switch (type) { case NewStringType::kNormal: { @@ -197,7 +197,7 @@ void String::WriteV2(Isolate* isolate, uint32_t offset, uint32_t length, uint16_ if (length > 0) { auto str = jsString->view(isolate->globalObject()); if (str->is8Bit()) { - WTF::copyElements(std::span(buffer, length), str->span8().subspan(offset, length)); + WTF::copyElements(std::span(buffer, length), byteCast(str->span8().subspan(offset, length))); } else { memcpy(buffer, str->span16().subspan(offset, length).data(), static_cast(length) * sizeof(uint16_t)); } @@ -217,7 +217,7 @@ void String::WriteOneByteV2(Isolate* isolate, uint32_t offset, uint32_t length, memcpy(buffer, str->span8().subspan(offset, length).data(), length); } else { // like V8, only the least significant byte of each code unit is written - WTF::copyElements(std::span(buffer, length), str->span16().subspan(offset, length)); + WTF::copyElements(std::span(byteCast(buffer), length), str->span16().subspan(offset, length)); } } if (flags & WriteFlags::kNullTerminate) { diff --git a/src/jsc/bindings/webcore/HTTPHeaderMap.cpp b/src/jsc/bindings/webcore/HTTPHeaderMap.cpp index 9e3a48b7082a..3bc4366ddf3b 100644 --- a/src/jsc/bindings/webcore/HTTPHeaderMap.cpp +++ b/src/jsc/bindings/webcore/HTTPHeaderMap.cpp @@ -55,12 +55,12 @@ String lowercaseHeaderName(const String& name) if (name.is8Bit()) { auto span = name.span8(); size_t length = span.size(); - if (highway_index_of_first_ascii_upper(span.data(), length) == length) + if (highway_index_of_first_ascii_upper(byteCast(span.data()), length) == length) return name; std::span data; String result = String::createUninitialized(static_cast(length), data); - highway_lower_ascii(span.data(), length, data.data()); + highway_lower_ascii(byteCast(span.data()), length, byteCast(data.data())); return result; } diff --git a/src/jsc/bindings/webcore/SerializedScriptValue.cpp b/src/jsc/bindings/webcore/SerializedScriptValue.cpp index cea213dc9ef8..81bd5650dca1 100644 --- a/src/jsc/bindings/webcore/SerializedScriptValue.cpp +++ b/src/jsc/bindings/webcore/SerializedScriptValue.cpp @@ -2533,7 +2533,7 @@ class CloneDeserializer : public CloneBase { if (is8Bit) { if ((end - ptr) < static_cast(length)) return false; - str = String { std::span { ptr, length } }; + str = String { std::span { byteCast(ptr), length } }; ptr += length; return true; } diff --git a/src/jsc/bindings/webcore/WebSocket.cpp b/src/jsc/bindings/webcore/WebSocket.cpp index d0e51df0b286..0adb48453c5d 100644 --- a/src/jsc/bindings/webcore/WebSocket.cpp +++ b/src/jsc/bindings/webcore/WebSocket.cpp @@ -1379,7 +1379,7 @@ void WebSocket::didReceiveHandshakeResponse(uint16_t statusCode, std::spanputDirect(vm, builtinNames.statusCodePublicName(), JSC::jsNumber(statusCode)); obj->putDirect(vm, builtinNames.statusMessagePublicName(), - JSC::jsString(vm, WTF::String({ statusMessage.data(), statusMessage.size() }))); + JSC::jsString(vm, WTF::String(byteCast(statusMessage)))); auto* rawHeaders = JSC::constructEmptyArray(globalObject, nullptr, headers.size() * 2); if (!rawHeaders || scope.exception()) [[unlikely]] { @@ -1388,10 +1388,10 @@ void WebSocket::didReceiveHandshakeResponse(uint16_t statusCode, std::spanputDirectIndex(globalObject, i * 2, - JSC::jsString(vm, WTF::String(headers[i].name.span()))); + JSC::jsString(vm, WTF::String(byteCast(headers[i].name.span())))); RETURN_IF_EXCEPTION(scope, ); rawHeaders->putDirectIndex(globalObject, i * 2 + 1, - JSC::jsString(vm, WTF::String(headers[i].value.span()))); + JSC::jsString(vm, WTF::String(byteCast(headers[i].value.span())))); RETURN_IF_EXCEPTION(scope, ); } obj->putDirect(vm, builtinNames.rawHeadersPublicName(), rawHeaders); diff --git a/src/jsc/bindings/webcrypto/SubtleCrypto.cpp b/src/jsc/bindings/webcrypto/SubtleCrypto.cpp index 6345449e9a02..74ee546a358b 100644 --- a/src/jsc/bindings/webcrypto/SubtleCrypto.cpp +++ b/src/jsc/bindings/webcrypto/SubtleCrypto.cpp @@ -1557,7 +1557,7 @@ void SubtleCrypto::unwrapKey(JSC::JSGlobalObject& state, KeyFormat format, Buffe keyData = bytes; break; case SubtleCrypto::KeyFormat::Jwk: { - String jwkString(bytes.span()); + String jwkString(byteCast(bytes.span())); auto jwkObject = JSONParse(&state, jwkString); if (scope.exception()) [[unlikely]] { weakThis->m_pendingPromises.remove(index); diff --git a/src/jsc/bindings/wtf-bindings.cpp b/src/jsc/bindings/wtf-bindings.cpp index 4d6b42454708..26c27c2e8cc4 100644 --- a/src/jsc/bindings/wtf-bindings.cpp +++ b/src/jsc/bindings/wtf-bindings.cpp @@ -242,7 +242,7 @@ extern "C" size_t WTF__base64URLEncode(const char* __restrict inputDataBuffer, s extern "C" double Bun__parseDateTimeString(const Latin1Character* string, size_t length) { bool isLocalTime = false; - return v8::TimeClip(v8::ParseDateTimeString(string, length, isLocalTime)); + return v8::TimeClip(v8::ParseDateTimeString(byteCast(string), length, isLocalTime)); } namespace Bun { diff --git a/src/runtime/bake/BakeSourceProvider.cpp b/src/runtime/bake/BakeSourceProvider.cpp index 1aeef1356865..f8257fba3747 100644 --- a/src/runtime/bake/BakeSourceProvider.cpp +++ b/src/runtime/bake/BakeSourceProvider.cpp @@ -145,7 +145,7 @@ extern "C" JSC::EncodedJSValue BakeGetOnModuleNamespace( size_t keyLength ) { auto& vm = JSC::getVM(global); - const auto propertyString = String(StringImpl::createWithoutCopying({ key, keyLength })); + const auto propertyString = String(StringImpl::createWithoutCopying({ byteCast(key), keyLength })); const auto identifier = JSC::Identifier::fromString(vm, propertyString); const auto property = JSC::PropertyName(identifier); return JSC::JSValue::encode(moduleNamespace->get(global, property)); diff --git a/src/runtime/webview/ChromeBackend.h b/src/runtime/webview/ChromeBackend.h index 2cbc972c9d96..329df94fe83f 100644 --- a/src/runtime/webview/ChromeBackend.h +++ b/src/runtime/webview/ChromeBackend.h @@ -195,12 +195,7 @@ class Command { m_sb.append(m_paramsRaw ? "}"_s : "}}"_s); if (m_sb.is8Bit()) [[likely]] { auto s = m_sb.span8(); - // OR-accumulate: all bytes < 0x80 → no high bit → valid ASCII. - // One pass, no branching. Commands are typically <512B. - Latin1Character acc = 0; - for (auto c : s) - acc |= c; - if (!(acc & 0x80)) [[likely]] { + if (charactersAreAllASCII(s)) [[likely]] { sink(reinterpret_cast(s.data()), s.size()); sink("\0", 1); return; diff --git a/test/bundler/bundler_bytecode_portable.test.ts b/test/bundler/bundler_bytecode_portable.test.ts index a41e23c52b5d..3c7a8d5c760c 100644 --- a/test/bundler/bundler_bytecode_portable.test.ts +++ b/test/bundler/bundler_bytecode_portable.test.ts @@ -443,7 +443,7 @@ describe("bytecode cache portability", () => { { "builtin corpus": { "bytes": 5120, - "sha256": "c6c9a9cb623d2ba1bd6389d6aee7d860077bbd840a8083961e7e03b41f768448", + "sha256": "f2397781dfd71ad09e4b60b8aca2a24208d2e8a5662b6ea8c95d29900d5b57ab", }, "builtin corpus strings": { "bytes": 1044, @@ -452,36 +452,36 @@ describe("bytecode cache portability", () => { "bun build --bytecode --minify all.js": { "js": "50b3e5192dd86a205c73583d585884c1b414467b14db54d03c77d3026bf236ff", "jsc": { - "bytes": 1730896, - "sha256": "d0fdc8d6b0c5433d168d633c53398e5bda3a491f1661912fd7527a569a88384d", + "bytes": 1730976, + "sha256": "71f4322680d3a5db2b48f4a832037b675c9f5e9e88eac0d2d14b7a631f66a9c3", }, }, "bun build --bytecode --minify features.js": { "js": "d30a5febed53e316cc2dd2b076502079e809bb0c201ef1671e9a190ecdcf093d", "jsc": { "bytes": 41624, - "sha256": "7635c882a17363a996de2a04ab0e3f01fe1f25b1567c722f9acfccb2a14e5f58", + "sha256": "88567b82c47299accdf727e952fb2c8a7db6ed0a38ee80022f6a005b18e311e7", }, }, "bun build --bytecode --minify records.js": { "js": "889cbb2c9525ff69a2676a6e81d97bb87760bdee65178b836c9c1d6808ac7c6e", "jsc": { - "bytes": 79944, - "sha256": "445cdf340504fe89e37ae4e31c15d1520372a965bac969394bfc19bf766b8be0", + "bytes": 80112, + "sha256": "e407b5abb8fb9e378fd80b008294de014b443353477d17ace4fdd4246827d8e5", }, }, "bun build --bytecode acorn/dist/acorn.mjs": { "js": "aa22cb20382fa5d66ff2ddd90817c0899f82b346bdd1da87dc9e193d203a0ce9", "jsc": { "bytes": 236680, - "sha256": "53f1fe77c52e658ed56140e6aa2179a3f9094e5460a3e893a8ca021f069f8ac8", + "sha256": "cb893ad8f00cd985065a0ae390870c46c730474f3d425b1850bb25c73adb4f67", }, }, "bun build --bytecode all.js": { "js": "ce4cf9db35e0aa3257f982fb756363a5686fb52a3b7cc63ac0c66bdcaf13a849", "jsc": { - "bytes": 1875200, - "sha256": "607d1cf75a299900a465c6d1e73128259112984da6f59f6dbcfefb48a4005b32", + "bytes": 1875264, + "sha256": "fe6c22c1203ac805ae8c6b407df50fb8116490b28a9de0eae959228d43fd5e0f", }, }, "bun build --bytecode big.js": { @@ -495,70 +495,70 @@ describe("bytecode cache portability", () => { "js": "2ee211924620db96d6e99e9490bfe0ee60a3bc6b003f37940c5631e6eabc2c73", "jsc": { "bytes": 43392, - "sha256": "9aeaaf8a34d216f515082895549b05ed2fbe8c5139deadcfa8747a6a5ae6aaf2", + "sha256": "03706d57a89ea83d16b645e817bfe339ada2e398b694dcd89d4a75c14577ab21", }, }, "bun build --bytecode happy-dom/lib/index.js": { "js": "1415a67d0da987f3bb7fc68ca431e222b3898fa14be8af88e2845813c4120f43", "jsc": { - "bytes": 2218280, - "sha256": "a955535bb5547c3d6107b8cd85de98e12acc256222aceb0c9bc86afc89af1297", + "bytes": 2218448, + "sha256": "c0fb3f9c19c152784ebc711c8a151c8b797a206f1d83f332a2febf989c638efc", }, }, "bun build --bytecode immutable/dist/immutable.es.js": { "js": "d011b6c5105dad96f17aaf541c848b8d2be1b2e65a1de050112380352979bb6b", "jsc": { - "bytes": 239400, - "sha256": "280d1800c117eb89b217d050b63d55d8831db4dc7a2f74bf8b6fe0235e5bcc9d", + "bytes": 239072, + "sha256": "75e78effee8c372c488c9b9e0e5163fbf2807e555e9fd7b428865268488d8518", }, }, "bun build --bytecode libraries.js": { "js": "7b8ce5d1861461086be174e60797d52794328828458a2086f4d49b39a3f8b28a", "jsc": { - "bytes": 21179744, - "sha256": "584ee53aa8e96724f11a2c1d441bb9e5e7f529d868148d3c7cdc2427942c7670", + "bytes": 21178032, + "sha256": "a9961b69e0f50de29ac8b10a7c9d54cdd266d296d65bb55e70707feee71f7b1d", }, }, "bun build --bytecode lodash/lodash.js": { "js": "9951d06da1bf94c69dece1b23f304c1cbf4018b894e398ac5ea10f35b5600187", "jsc": { - "bytes": 303616, - "sha256": "4b10e11aecfe92ad601a526855548658c0bb0a1a7af9668140d3d8c4e49375fd", + "bytes": 303584, + "sha256": "66198157a74a4728868574763a57d14453038ae406ef854b84df7124741672fa", }, }, "bun build --bytecode react-dom/cjs/react-dom.development.js": { "js": "3392a38ccef2f1bb7b1c8c8cbfc8111b45f6cf6f8dec3c72a99f13a6568fd5a1", "jsc": { - "bytes": 829944, - "sha256": "2ef931c269adbcb55ddcb3a548c5bb2e036ccc1b3b5103fad90a04ad2ef45ff7", + "bytes": 829880, + "sha256": "2503665481f634ba6b69817195f32e6e403072972b375cf57091272e61c5df54", }, }, "bun build --bytecode records.js": { "js": "c87ea35df4ad6b2063402c9901ef4775a82f12594f280db76f50695f4b0eba13", "jsc": { - "bytes": 82664, - "sha256": "34573c43813caa443cc5b3aef814f65769c2c291d355a5b58ad7a8241eb50962", + "bytes": 82832, + "sha256": "e159cf9bdbd844297bea0eac7a6b0051ef7f573ba5cfcdff69afecc00f4b2e0c", }, }, "bun build --bytecode shapes.js": { "js": "dfcf0136de2c98f6a29d2c41477637879ccae98385a1bf30c666b85002bcae07", "jsc": { "bytes": 214224, - "sha256": "5b3cb32b71313ab5d36baa8eef461b52be2e1c792b76a0dd552387678d02df82", + "sha256": "d746d61acb1772b9840e2332db3c44d9a6498f9356d87ce7797557f8e48dda33", }, }, "bun build --bytecode svelte/compiler/index.js": { "js": "17e7431a6f28a4b6b5d356fc815b0876ebd9613ae6c25a98dc4c5560004341ce", "jsc": { "bytes": 1820320, - "sha256": "90f10bf35bb735f362240835c4fd9d281462497127d835c0ab5dc3ead5b5fa9f", + "sha256": "047126f5a2fee01d4d2638c941df7ba85c04af702a507e6f3a47715a21954153", }, }, "bun build --bytecode undici/index.js": { "js": "e1c4f1494711ecaae57a6d63dfb8ac6096629582f55cc42530ff5a156b70c9de", "jsc": { - "bytes": 852600, - "sha256": "5ee6a96a6645573a6567c190cc8f68106c9c3fed4ad7970a766974816a28fb7c", + "bytes": 852464, + "sha256": "eb962b6462b67f21630e62e93be61772ff1d0d40555bb71259b6fdd1b391434b", }, }, "vm.Script big.js": { @@ -567,19 +567,19 @@ describe("bytecode cache portability", () => { }, "vm.Script features.js": { "bytes": 45240, - "sha256": "554cf423e60ad55981b5025dec7c23440f3c0fe87d9d5100ca01c4860709eb3e", + "sha256": "95fb7b91fc1113cbc3e2b5b137bb5096dc70495aecebbd0a4b776fa141c693b4", }, "vm.Script lodash.js": { - "bytes": 342992, - "sha256": "5066d916e3a303739b503d5ad1a0a92d379f09fd306deb75002ad9b5ff36ef98", + "bytes": 342960, + "sha256": "642ce0567cc591bd522d7102c76034f76985fd46c241d0df633a742cf0d78d13", }, "vm.Script records.js": { - "bytes": 86656, - "sha256": "3d63c310491ae836741c03d881b840f5e55e3a9d74cb35c832080f6f056964f6", + "bytes": 86624, + "sha256": "46323dc86ba13f798366cce48166284dfca6c70b402f53fae906acc81b496e9f", }, "vm.Script shapes.js": { "bytes": 257488, - "sha256": "200a618a258aaa621e17950c479afdac20d1a6e49416c77744282ad751f16034", + "sha256": "335a4e20d4347088be322d54d1400c40a3c1e811a53af9ad37b34a78ee572ad7", }, "vm.Script source-forms.js": { "bytes": 4712, @@ -587,15 +587,15 @@ describe("bytecode cache portability", () => { }, "vm.Script typescript.js": { "bytes": 11326400, - "sha256": "7cbdd3a281991f76b431a0aa11824870941ff09983bed76efa59cb9722790190", + "sha256": "0348719bf1d0408a92c883bda77569ec55f266d5df0a1fae4265c8f0aefcbbac", }, "vm.SourceTextModule acorn.mjs": { "bytes": 255472, - "sha256": "1d42c1c696bc08ca000343ebb1a387ccb0b6119b504a4946f75781d1f40110f7", + "sha256": "e21a36d1a6c30aecc3b524d7516834c0d2ca8996eb57a21a8f64d10307ce0429", }, "vm.SourceTextModule module.js": { "bytes": 9104, - "sha256": "67c94acc5a5d3ba8d9067948080070fefc95611297d3e8033b292291d992c0ea", + "sha256": "66cf5399c2c312e91db2066c9ef00186dbbdba80cb8871a5b7f31404753f803a", }, } `); diff --git a/test/js/bun/jsc/webkit-upgrade-23b0720b40.test.ts b/test/js/bun/jsc/webkit-upgrade-23b0720b40.test.ts new file mode 100644 index 000000000000..f0eb6ff5fa35 --- /dev/null +++ b/test/js/bun/jsc/webkit-upgrade-23b0720b40.test.ts @@ -0,0 +1,147 @@ +import { numberOfDFGCompiles } from "bun:jsc"; +import { describe, expect, test } from "bun:test"; +import vm from "node:vm"; + +// Coverage for the WebKit 23b0720b40 sync. Each case pins an observable difference between +// the old and the new JavaScriptCore, but for the last two, which pin what had to stay the same. +// The sources that depend on how JavaScriptCore parses +// are compiled with `new Function` or an indirect eval, so that Bun's transpiler leaves them alone. +// A source that has to be a SyntaxError goes through the indirect eval: see the TODO in +// webkit-upgrade-dbdca7545d.test.ts about the Function constructor. + +describe.concurrent("WebKit 23b0720b40 upgrade", () => { + test("a unary plus on an operand of `*` is not dropped (b985a89bbd)", () => { + expect(() => new Function("return +1n * 2n;")()).toThrow(TypeError); + expect(() => new Function("return 2n * +1n;")()).toThrow(TypeError); + + const log: string[] = []; + const a = { + valueOf() { + log.push("a"); + return 3; + }, + }; + const b = () => { + log.push("b"); + return 2; + }; + expect(new Function("a", "b", "return +a * b();")(a, b)).toBe(6); + expect(log).toEqual(["a", "b"]); + }); + + test("`using` is allowed in a function nested in a switch clause (e694d1c0c3)", () => { + expect((0, eval)("switch (0) { case 0: (() => { using x = null; })(); } 1")).toBe(1); + // Directly in the clause it is still an error. + expect(() => (0, eval)("switch (0) { case 0: using x = null; }")).toThrow(SyntaxError); + }); + + test("`await a ** b` is a SyntaxError (e33864764e)", () => { + expect(() => (0, eval)("(async function () { await 2 ** 2; })")).toThrow(SyntaxError); + expect(typeof (0, eval)("(async function () { (await 2) ** 2; })")).toBe("function"); + }); + + test("a labelled function declaration is declared in the enclosing scope (29e6343215)", () => { + expect(new Function("return typeof f; label: function f() {}")()).toBe("function"); + expect(() => (0, eval)("(function () { let f; label: function f() {} })")).toThrow(SyntaxError); + }); + + test("a 'use strict' directive rejects reserved words in the names before it (9f543645c4)", () => { + expect(() => (0, eval)("(function foo(implements) { 'use strict'; })")).toThrow(SyntaxError); + expect(() => (0, eval)("(function static() { 'use strict'; })")).toThrow(SyntaxError); + expect(typeof (0, eval)("(function static(implements) {})")).toBe("function"); + }); + + test("for-in visits the indexed properties of a function (21b684a378)", () => { + const f: any = function () {}; + f[0] = "a"; + f[1] = "b"; + f.named = "c"; + const keys: string[] = []; + for (const key in f) keys.push(key); + expect(keys).toEqual(["0", "1", "named"]); + }); + + test("a built-in constructor reads newTarget.prototype before it looks for newTarget's realm (6fba1aeeb7)", () => { + const log: string[] = []; + const { proxy, revoke } = Proxy.revocable(function () {}, { + get(_, key) { + log.push(String(key)); + revoke(); + return undefined; + }, + }); + // The prototype is not an object, so the realm's default is wanted, and a revoked proxy has no realm. + expect(() => Reflect.construct(Map, [], proxy)).toThrow(TypeError); + expect(log).toEqual(["prototype"]); + }); + + test("Array.fromAsync reads the iterator's `next` before it constructs `this` (d59de52c72)", async () => { + const log: string[] = []; + function Collection() { + log.push("construct"); + } + const iterable = { + [Symbol.asyncIterator]() { + return { + get next() { + log.push("next"); + return async () => ({ done: true, value: undefined }); + }, + }; + }, + }; + await Array.fromAsync.call(Collection, iterable); + expect(log).toEqual(["next", "construct"]); + }); + + test("optimized code does not take `x + y + 0.5` for an integer sum (13714a5887)", () => { + const f = new Function("x", "y", "return (x + y + 0.5) | 0;"); + let result = f(2147483647, 2147483647); + // Until the DFG's code is what runs. The bound is for a run with the JIT off. + for (let i = 0; i < 200_000 && result === -2 && numberOfDFGCompiles(f) === 0; i++) + result = f(2147483647, 2147483647); + for (let i = 0; i < 100 && result === -2; i++) result = f(2147483647, 2147483647); + expect(result).toBe(-2); + }); + + // JavaScriptCore no longer captures a stack in its Exception for a throw that a `catch` clause is certain to handle + // (70896f26ff). node:vm takes the position for its header from that stack, between the throw and the `catch`. + test("node:vm gives an error its header under a `catch` clause too", async () => { + const cases: [() => unknown, string[]][] = [ + [() => vm.runInNewContext("\n var 1a;"), ["evalmachine.:2", " var 1a;", " ^", ""]], + [() => new vm.Script("\n var 1a;", { filename: "f.js" }), ["f.js:2", " var 1a;", " ^", ""]], + [ + () => vm.runInNewContext("\n throw new Error('boom')"), + ["evalmachine.:2", " throw new Error('boom')", " ^", ""], + ], + ]; + const header = (error: Error) => error.stack!.split("\n").slice(0, 4); + for (const [run, expected] of cases) { + let caught: Error | undefined; + try { + run(); + } catch (error) { + caught = error as Error; + } + expect(header(caught!)).toEqual(expected); + // Here native code is what catches: the Promise constructor calls its executor. + expect(header(await new Promise(run).catch(error => error))).toEqual(expected); + } + }); + + // Upstream removed ShadowRealm in d5f13fa851 and Bun's fork keeps it. + test("an error that leaves a ShadowRealm is caught as the outer realm's TypeError", () => { + const realm = new ShadowRealm(); + const thrower = realm.evaluate("() => { throw new RangeError('inner'); }") as () => never; + expect(String(thrower)).toBe("function () { [native code] }"); + let caught: unknown; + try { + thrower(); + } catch (error) { + caught = error; + } + expect(caught).toBeInstanceOf(TypeError); + expect((caught as TypeError).message).toBe("inner"); + expect((caught as TypeError).stack).toBeString(); + }); +});