diff options
Diffstat (limited to 'src')
33 files changed, 1344 insertions, 416 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 72815d9a2..380ca9cbf 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -77,7 +77,7 @@ const VirtualMachine = JSC.VirtualMachine; const IOTask = JSC.IOTask; const zlib = @import("../../zlib.zig"); const Which = @import("../../which.zig"); - +const ErrorableString = JSC.ErrorableString; const is_bindgen = JSC.is_bindgen; const max_addressible_memory = std.math.maxInt(u56); @@ -982,18 +982,18 @@ fn doResolve( } } - return doResolveWithArgs(ctx, specifier.getZigString(ctx.ptr()), from.getZigString(ctx.ptr()), exception, is_esm, false); + return doResolveWithArgs(ctx, specifier.toBunString(ctx.ptr()), from.toBunString(ctx.ptr()), exception, is_esm, false); } fn doResolveWithArgs( ctx: js.JSContextRef, - specifier: ZigString, - from: ZigString, + specifier: bun.String, + from: bun.String, exception: js.ExceptionRef, is_esm: bool, comptime is_file_path: bool, ) ?JSC.JSValue { - var errorable: ErrorableZigString = undefined; + var errorable: ErrorableString = undefined; var query_string = ZigString.Empty; if (comptime is_file_path) { @@ -1037,7 +1037,7 @@ fn doResolveWithArgs( return ZigString.initUTF8(arraylist.items).toValueGC(ctx); } - return errorable.result.value.toValue(ctx); + return errorable.result.value.toJS(ctx); } pub fn resolveSync( @@ -1076,7 +1076,7 @@ export fn Bun__resolve( ) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; var exception = &exception_; - const value = doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, is_esm, true) orelse { + const value = doResolveWithArgs(global, specifier.toBunString(global), source.toBunString(global), exception, is_esm, true) orelse { return JSC.JSPromise.rejectedPromiseValue(global, JSC.JSValue.fromRef(exception[0])); }; return JSC.JSPromise.resolvedPromiseValue(global, value); @@ -1090,7 +1090,7 @@ export fn Bun__resolveSync( ) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; var exception = &exception_; - return doResolveWithArgs(global, specifier.getZigString(global), source.getZigString(global), exception, is_esm, true) orelse { + return doResolveWithArgs(global, specifier.toBunString(global), source.toBunString(global), exception, is_esm, true) orelse { return JSC.JSValue.fromRef(exception[0]); }; } @@ -1098,12 +1098,12 @@ export fn Bun__resolveSync( export fn Bun__resolveSyncWithSource( global: *JSGlobalObject, specifier: JSValue, - source: *ZigString, + source: *bun.String, is_esm: bool, ) JSC.JSValue { var exception_ = [1]JSC.JSValueRef{null}; var exception = &exception_; - return doResolveWithArgs(global, specifier.getZigString(global), source.*, exception, is_esm, true) orelse { + return doResolveWithArgs(global, specifier.toBunString(global), source.*, exception, is_esm, true) orelse { return JSC.JSValue.fromRef(exception[0]); }; } diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index be18cc672..780bfcb14 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -1589,8 +1589,8 @@ fn NewSocket(comptime ssl: bool) type { globalObject.throw("sendfile() not implemented yet", .{}); return .{ .fail = {} }; - } else if (args.ptr[0].toStringOrNull(globalObject)) |jsstring| { - var zig_str = jsstring.toSlice(globalObject, globalObject.bunVM().allocator); + } else if (bun.String.tryFromJS(args.ptr[0], globalObject)) |bun_str| { + var zig_str = bun_str.toUTF8(bun.default_allocator); defer zig_str.deinit(); var slice = zig_str.slice(); diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig index acd870c08..0f967785e 100644 --- a/src/bun.js/api/server.zig +++ b/src/bun.js/api/server.zig @@ -2372,6 +2372,8 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp } pub fn doRenderWithBody(this: *RequestContext, value: *JSC.WebCore.Body.Value) void { + // If a ReadableStream can trivially be converted to a Blob, do so. + // If it's a WTFStringImpl and it cannot be used as a UTF-8 string, convert it to a Blob. value.toBlobIfPossible(); switch (value.*) { @@ -2386,10 +2388,12 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp return; }, // .InlineBlob, + .WTFStringImpl, .InternalBlob, .Blob, => { - this.blob = value.useAsAnyBlob(); + // toBlobIfPossible checks for WTFString needing a conversion. + this.blob = value.useAsAnyBlobAllowNonUTF8String(); this.renderWithBlobFromBodyValue(); return; }, @@ -2528,7 +2532,7 @@ fn NewRequestContext(comptime ssl_enabled: bool, comptime debug_mode: bool, comp // Faster to do the memcpy than to do the two network calls // We are not streaming // This is an important performance optimization - if (this.has_abort_handler and this.blob.size() < 16384 - 1024) { + if (this.has_abort_handler and this.blob.fastSize() < 16384 - 1024) { if (this.resp) |resp| { resp.runCorkedWithType(*RequestContext, doRenderBlobCorked, this); } diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp index ea1963c08..ad4039336 100644 --- a/src/bun.js/bindings/BunPlugin.cpp +++ b/src/bun.js/bindings/BunPlugin.cpp @@ -366,15 +366,15 @@ JSFunction* BunPlugin::Group::find(JSC::JSGlobalObject* globalObject, String& pa return nullptr; } -EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, ZigString* namespaceString, ZigString* path) +EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path) { - Group* groupPtr = this->group(namespaceString ? Zig::toString(*namespaceString) : String()); + Group* groupPtr = this->group(namespaceString ? Bun::toWTFString(*namespaceString) : String()); if (groupPtr == nullptr) { return JSValue::encode(jsUndefined()); } Group& group = *groupPtr; - auto pathString = Zig::toString(*path); + auto pathString = Bun::toWTFString(*path); JSC::JSFunction* function = group.find(globalObject, pathString); if (!function) { @@ -428,9 +428,9 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, ZigStri RELEASE_AND_RETURN(throwScope, JSValue::encode(result)); } -EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, ZigString* namespaceString, ZigString* path, ZigString* importer) +EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path, BunString* importer) { - Group* groupPtr = this->group(namespaceString ? Zig::toString(*namespaceString) : String()); + Group* groupPtr = this->group(namespaceString ? Bun::toWTFString(*namespaceString) : String()); if (groupPtr == nullptr) { return JSValue::encode(jsUndefined()); } @@ -443,7 +443,7 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, ZigS auto& callbacks = group.callbacks; - WTF::String pathString = Zig::toString(*path); + WTF::String pathString = Bun::toWTFString(*path); for (size_t i = 0; i < filters.size(); i++) { if (!filters[i].get()->match(globalObject, pathString, 0)) { continue; @@ -461,10 +461,10 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, ZigS auto& builtinNames = clientData->builtinNames(); paramsObject->putDirect( vm, clientData->builtinNames().pathPublicName(), - Zig::toJSStringValue(*path, globalObject)); + Bun::toJS(globalObject, *path)); paramsObject->putDirect( vm, clientData->builtinNames().importerPublicName(), - Zig::toJSStringValue(*importer, globalObject)); + Bun::toJS(globalObject, *importer)); arguments.append(paramsObject); auto throwScope = DECLARE_THROW_SCOPE(vm); @@ -515,12 +515,12 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, ZigS } // namespace Zig -extern "C" JSC::EncodedJSValue Bun__runOnResolvePlugins(Zig::GlobalObject* globalObject, ZigString* namespaceString, ZigString* path, ZigString* from, BunPluginTarget target) +extern "C" JSC::EncodedJSValue Bun__runOnResolvePlugins(Zig::GlobalObject* globalObject, BunString* namespaceString, BunString* path, BunString* from, BunPluginTarget target) { return globalObject->onResolvePlugins[target].run(globalObject, namespaceString, path, from); } -extern "C" JSC::EncodedJSValue Bun__runOnLoadPlugins(Zig::GlobalObject* globalObject, ZigString* namespaceString, ZigString* path, BunPluginTarget target) +extern "C" JSC::EncodedJSValue Bun__runOnLoadPlugins(Zig::GlobalObject* globalObject, BunString* namespaceString, BunString* path, BunPluginTarget target) { return globalObject->onLoadPlugins[target].run(globalObject, namespaceString, path); } diff --git a/src/bun.js/bindings/BunPlugin.h b/src/bun.js/bindings/BunPlugin.h index 600705500..cf37b739b 100644 --- a/src/bun.js/bindings/BunPlugin.h +++ b/src/bun.js/bindings/BunPlugin.h @@ -67,7 +67,7 @@ public: { } - EncodedJSValue run(JSC::JSGlobalObject* globalObject, ZigString* namespaceString, ZigString* path); + EncodedJSValue run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path); }; class OnResolve final : public Base { @@ -78,7 +78,7 @@ public: { } - EncodedJSValue run(JSC::JSGlobalObject* globalObject, ZigString* namespaceString, ZigString* path, ZigString* importer); + EncodedJSValue run(JSC::JSGlobalObject* globalObject, BunString* namespaceString, BunString* path, BunString* importer); }; }; diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp new file mode 100644 index 000000000..797f66545 --- /dev/null +++ b/src/bun.js/bindings/BunString.cpp @@ -0,0 +1,141 @@ +#include "root.h" +#include "headers-handwritten.h" +#include "JavaScriptCore/JSCJSValueInlines.h" +#include "helpers.h" + +using namespace JSC; + +extern "C" void Bun__WTFStringImpl__deref(WTF::StringImpl* impl) +{ + impl->deref(); +} +extern "C" void Bun__WTFStringImpl__ref(WTF::StringImpl* impl) +{ + impl->ref(); +} + +extern "C" bool BunString__fromJS(JSC::JSGlobalObject* globalObject, JSC::EncodedJSValue encodedValue, BunString* bunString) +{ + JSC::JSValue value = JSC::JSValue::decode(encodedValue); + *bunString = Bun::toString(globalObject, value); + return bunString->tag != BunStringTag::Dead; +} + +namespace Bun { +JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, BunString bunString) +{ + if (bunString.tag == BunStringTag::Empty || bunString.tag == BunStringTag::Dead) { + return JSValue(JSC::jsEmptyString(globalObject->vm())); + } + if (bunString.tag == BunStringTag::WTFStringImpl) { + return JSValue(jsString(globalObject->vm(), String(bunString.impl.wtf))); + } + + if (bunString.tag == BunStringTag::StaticZigString) { + return JSValue(jsString(globalObject->vm(), Zig::toStringStatic(bunString.impl.zig))); + } + + return JSValue(Zig::toJSStringGC(bunString.impl.zig, globalObject)); +} + +WTF::String toWTFString(const BunString& bunString) +{ + if (bunString.tag == BunStringTag::ZigString) { + if (Zig::isTaggedUTF8Ptr(bunString.impl.zig.ptr)) { + return Zig::toStringCopy(bunString.impl.zig); + } else { + return Zig::toString(bunString.impl.zig); + } + + } else if (bunString.tag == BunStringTag::StaticZigString) { + return Zig::toStringStatic(bunString.impl.zig); + } + + if (bunString.tag == BunStringTag::WTFStringImpl) { + return WTF::String(bunString.impl.wtf); + } + + return WTF::String(); +} + +BunString fromJS(JSC::JSGlobalObject* globalObject, JSValue value) +{ + JSC::JSString* str = value.toStringOrNull(globalObject); + if (UNLIKELY(!str)) { + return { BunStringTag::Dead }; + } + + if (str->length() == 0) { + return { BunStringTag::Empty }; + } + + auto wtfString = str->value(globalObject); + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} + +BunString toString(JSC::JSGlobalObject* globalObject, JSValue value) +{ + return fromJS(globalObject, value); +} + +BunString toString(WTF::String& wtfString) +{ + if (wtfString.length() == 0) + return { BunStringTag::Empty }; + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} +BunString toString(const WTF::String& wtfString) +{ + if (wtfString.length() == 0) + return { BunStringTag::Empty }; + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} +BunString toString(WTF::StringImpl* wtfString) +{ + if (wtfString->length() == 0) + return { BunStringTag::Empty }; + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; +} + +BunString fromString(WTF::String& wtfString) +{ + if (wtfString.length() == 0) + return { BunStringTag::Empty }; + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString.impl() } }; +} + +BunString fromString(WTF::StringImpl* wtfString) +{ + if (wtfString->length() == 0) + return { BunStringTag::Empty }; + + return { BunStringTag::WTFStringImpl, { .wtf = wtfString } }; +} + +} + +extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject, BunString* bunString) +{ + return JSValue::encode(Bun::toJS(globalObject, *bunString)); +} + +extern "C" void BunString__toWTFString(BunString* bunString) +{ + if (bunString->tag == BunStringTag::ZigString) { + if (Zig::isTaggedUTF8Ptr(bunString->impl.zig.ptr)) { + bunString->impl.wtf = Zig::toStringCopy(bunString->impl.zig).impl(); + } else { + bunString->impl.wtf = Zig::toString(bunString->impl.zig).impl(); + } + + bunString->tag = BunStringTag::WTFStringImpl; + } else if (bunString->tag == BunStringTag::StaticZigString) { + bunString->impl.wtf = Zig::toStringStatic(bunString->impl.zig).impl(); + bunString->tag = BunStringTag::WTFStringImpl; + } +}
\ No newline at end of file diff --git a/src/bun.js/bindings/ImportMetaObject.cpp b/src/bun.js/bindings/ImportMetaObject.cpp index 38cbeba47..a53712823 100644 --- a/src/bun.js/bindings/ImportMetaObject.cpp +++ b/src/bun.js/bindings/ImportMetaObject.cpp @@ -59,7 +59,7 @@ static EncodedJSValue functionRequireResolve(JSC::JSGlobalObject* globalObject, JSC::JSValue moduleName = callFrame->argument(0); auto doIt = [&](const WTF::String& fromStr) -> JSC::EncodedJSValue { - ZigString from = Zig::toZigString(fromStr); + BunString from = Bun::toString(fromStr); auto result = Bun__resolveSyncWithSource(globalObject, JSC::JSValue::encode(moduleName), &from, false); if (!JSC::JSValue::decode(result).isString()) { diff --git a/src/bun.js/bindings/ImportMetaObject.h b/src/bun.js/bindings/ImportMetaObject.h index f43d01899..d0f8f0963 100644 --- a/src/bun.js/bindings/ImportMetaObject.h +++ b/src/bun.js/bindings/ImportMetaObject.h @@ -11,7 +11,7 @@ extern "C" JSC_DECLARE_HOST_FUNCTION(functionImportMeta__resolveSync); extern "C" JSC::EncodedJSValue Bun__resolve(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from, bool is_esm); extern "C" JSC::EncodedJSValue Bun__resolveSync(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, JSC::EncodedJSValue from, bool is_esm); -extern "C" JSC::EncodedJSValue Bun__resolveSyncWithSource(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, ZigString* from, bool is_esm); +extern "C" JSC::EncodedJSValue Bun__resolveSyncWithSource(JSC::JSGlobalObject* global, JSC::EncodedJSValue specifier, BunString* from, bool is_esm); namespace Zig { diff --git a/src/bun.js/bindings/JSMockFunction.cpp b/src/bun.js/bindings/JSMockFunction.cpp index 6aeb44ded..eda0837c9 100644 --- a/src/bun.js/bindings/JSMockFunction.cpp +++ b/src/bun.js/bindings/JSMockFunction.cpp @@ -17,6 +17,7 @@ #include <JavaScriptCore/Weak.h> #include <JavaScriptCore/GetterSetter.h> #include <JavaScriptCore/WeakMapImpl.h> +#include <JavaScriptCore/WeakMapImplInlines.h> namespace Bun { diff --git a/src/bun.js/bindings/ModuleLoader.cpp b/src/bun.js/bindings/ModuleLoader.cpp index f4e96130b..035cd15c8 100644 --- a/src/bun.js/bindings/ModuleLoader.cpp +++ b/src/bun.js/bindings/ModuleLoader.cpp @@ -41,7 +41,7 @@ namespace Bun { using namespace Zig; using namespace WebCore; -extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, ZigString* specifier); +extern "C" BunLoaderType Bun__getDefaultLoader(JSC::JSGlobalObject*, BunString* specifier); static JSC::JSInternalPromise* rejectedInternalPromise(JSC::JSGlobalObject* globalObject, JSC::JSValue value) { @@ -131,7 +131,7 @@ PendingVirtualModuleResult* PendingVirtualModuleResult::create(JSC::JSGlobalObje return virtualModule; } -OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, ZigString* specifier) +OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier) { OnLoadResult result = {}; result.type = OnLoadResultTypeError; @@ -212,7 +212,7 @@ OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC:: return result; } -static OnLoadResult handleOnLoadResult(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, ZigString* specifier) +static OnLoadResult handleOnLoadResult(Zig::GlobalObject* globalObject, JSC::JSValue objectValue, BunString* specifier) { if (JSC::JSPromise* promise = JSC::jsDynamicCast<JSC::JSPromise*>(objectValue)) { OnLoadResult result = {}; @@ -229,8 +229,8 @@ static JSValue handleVirtualModuleResult( Zig::GlobalObject* globalObject, JSValue virtualModuleResult, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer) + BunString* specifier, + BunString* referrer) { auto onLoadResult = handleOnLoadResult(globalObject, virtualModuleResult, specifier); JSC::VM& vm = globalObject->vm(); @@ -297,7 +297,7 @@ static JSValue handleVirtualModuleResult( object); auto source = JSC::SourceCode( JSC::SyntheticSourceProvider::create(WTFMove(function), - JSC::SourceOrigin(), Zig::toString(*specifier))); + JSC::SourceOrigin(), Bun::toWTFString(*specifier))); JSC::ensureStillAliveHere(object); return rejectOrResolve(JSSourceCode::create(globalObject->vm(), WTFMove(source))); } @@ -307,8 +307,8 @@ static JSValue handleVirtualModuleResult( JSFunction* performPromiseThenFunction = globalObject->performPromiseThenFunction(); auto callData = JSC::getCallData(performPromiseThenFunction); ASSERT(callData.type != CallData::Type::None); - auto specifierString = Zig::toString(*specifier); - auto referrerString = Zig::toString(*referrer); + auto specifierString = Bun::toWTFString(*specifier); + auto referrerString = Bun::toWTFString(*referrer); PendingVirtualModuleResult* pendingModule = PendingVirtualModuleResult::create(globalObject, specifierString, referrerString); JSC::JSInternalPromise* internalPromise = pendingModule->internalPromise(); MarkedArgumentBuffer arguments; @@ -330,8 +330,8 @@ static JSValue handleVirtualModuleResult( extern "C" void Bun__onFulfillAsyncModule( EncodedJSValue promiseValue, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer) + BunString* specifier, + BunString* referrer) { JSC::JSValue value = JSValue::decode(promiseValue); JSC::JSInternalPromise* promise = jsCast<JSC::JSInternalPromise*>(value); @@ -354,8 +354,8 @@ template<bool allowPromise> static JSValue fetchSourceCode( Zig::GlobalObject* globalObject, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer) + BunString* specifier, + BunString* referrer) { void* bunVM = globalObject->bunVM(); auto& vm = globalObject->vm(); @@ -403,7 +403,7 @@ static JSValue fetchSourceCode( return reject(exception); } - auto moduleKey = Zig::toString(*specifier); + auto moduleKey = Bun::toWTFString(*specifier); switch (res->result.value.tag) { case SyntheticModuleType::Module: { @@ -505,8 +505,8 @@ extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultResolve(JSC::JSGlobal pendingModule->internalField(1).set(vm, pendingModule, JSC::jsUndefined()); JSC::JSInternalPromise* promise = pendingModule->internalPromise(); - ZigString specifier = Zig::toZigString(specifierString, globalObject); - ZigString referrer = Zig::toZigString(referrerString, globalObject); + BunString specifier = Bun::toString(globalObject, specifierString); + BunString referrer = Bun::toString(globalObject, referrerString); auto scope = DECLARE_THROW_SCOPE(vm); JSC::JSValue result = handleVirtualModuleResult<false>(reinterpret_cast<Zig::GlobalObject*>(globalObject), objectResult, &res, &specifier, &referrer); if (res.success) { @@ -550,8 +550,8 @@ extern "C" JSC::EncodedJSValue jsFunctionOnLoadObjectResultReject(JSC::JSGlobalO JSValue fetchSourceCodeSync( Zig::GlobalObject* globalObject, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer) + BunString* specifier, + BunString* referrer) { return fetchSourceCode<false>(globalObject, res, specifier, referrer); } @@ -559,8 +559,8 @@ JSValue fetchSourceCodeSync( JSValue fetchSourceCodeAsync( Zig::GlobalObject* globalObject, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer) + BunString* specifier, + BunString* referrer) { return fetchSourceCode<true>(globalObject, res, specifier, referrer); } diff --git a/src/bun.js/bindings/ModuleLoader.h b/src/bun.js/bindings/ModuleLoader.h index 9090abcdc..0deaeff08 100644 --- a/src/bun.js/bindings/ModuleLoader.h +++ b/src/bun.js/bindings/ModuleLoader.h @@ -82,13 +82,13 @@ OnLoadResult handleOnLoadResultNotPromise(Zig::GlobalObject* globalObject, JSC:: JSValue fetchSourceCodeSync( Zig::GlobalObject* globalObject, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer); + BunString* specifier, + BunString* referrer); JSValue fetchSourceCodeAsync( Zig::GlobalObject* globalObject, ErrorableResolvedSource* res, - ZigString* specifier, - ZigString* referrer); + BunString* specifier, + BunString* referrer); } // namespace Bun
\ No newline at end of file diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index f31a3c1cc..8926be6bb 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -118,6 +118,8 @@ #include "JavaScriptCore/RemoteInspectorServer.h" #endif +using namespace Bun; + extern "C" JSC::EncodedJSValue Bun__fetch(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame); using JSGlobalObject @@ -282,7 +284,7 @@ JSC_DEFINE_HOST_FUNCTION(functionFulfillModuleSync, return JSValue::encode(JSC::jsUndefined()); } - auto specifier = Zig::toZigString(moduleKey); + auto specifier = Bun::toString(moduleKey); ErrorableResolvedSource res; res.success = false; res.result.err.code = 0; @@ -4055,19 +4057,19 @@ JSC::Identifier GlobalObject::moduleLoaderResolve(JSGlobalObject* globalObject, JSModuleLoader* loader, JSValue key, JSValue referrer, JSValue origin) { - ErrorableZigString res; + ErrorableString res; res.success = false; - ZigString keyZ = toZigString(key, globalObject); - ZigString referrerZ = referrer && !referrer.isUndefinedOrNull() && referrer.isString() ? toZigString(referrer, globalObject) : ZigStringEmpty; + BunString keyZ = Bun::toString(globalObject, key); + BunString referrerZ = referrer && !referrer.isUndefinedOrNull() && referrer.isString() ? Bun::toString(globalObject, referrer) : BunStringEmpty; ZigString queryString = { 0, 0 }; Zig__GlobalObject__resolve(&res, globalObject, &keyZ, &referrerZ, &queryString); if (res.success) { if (queryString.len > 0) { - return JSC::Identifier::fromString(globalObject->vm(), makeString(Zig::toString(res.result.value), Zig::toString(queryString))); + return JSC::Identifier::fromString(globalObject->vm(), makeString(Bun::toWTFString(res.result.value), Zig::toString(queryString))); } - return toIdentifier(res.result.value, globalObject); + return Identifier::fromString(globalObject->vm(), toWTFString(res.result.value)); } else { auto scope = DECLARE_THROW_SCOPE(globalObject->vm()); throwException(scope, res.result.err, globalObject); @@ -4088,9 +4090,9 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* g RETURN_IF_EXCEPTION(scope, promise->rejectWithCaughtException(globalObject, scope)); auto sourceURL = sourceOrigin.url(); - ErrorableZigString resolved; - auto moduleNameZ = toZigString(moduleNameValue, globalObject); - auto sourceOriginZ = sourceURL.isEmpty() ? ZigStringCwd : toZigString(sourceURL.fileSystemPath()); + ErrorableString resolved; + auto moduleNameZ = Bun::toString(globalObject, moduleNameValue); + auto sourceOriginZ = sourceURL.isEmpty() ? BunStringCwd : Bun::toString(sourceURL.fileSystemPath()); ZigString queryString = { 0, 0 }; resolved.success = false; Zig__GlobalObject__resolve(&resolved, globalObject, &moduleNameZ, &sourceOriginZ, &queryString); @@ -4101,9 +4103,9 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderImportModule(JSGlobalObject* g JSC::Identifier resolvedIdentifier; if (queryString.len == 0) { - resolvedIdentifier = toIdentifier(resolved.result.value, globalObject); + resolvedIdentifier = JSC::Identifier::fromString(vm, Bun::toWTFString(resolved.result.value)); } else { - resolvedIdentifier = JSC::Identifier::fromString(vm, makeString(Zig::toString(resolved.result.value), Zig::toString(queryString))); + resolvedIdentifier = JSC::Identifier::fromString(vm, makeString(Bun::toWTFString(resolved.result.value), Zig::toString(queryString))); } auto result = JSC::importModule(globalObject, resolvedIdentifier, @@ -4138,8 +4140,8 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb return rejectedInternalPromise(globalObject, createTypeError(globalObject, "To load Node-API modules, use require() or process.dlopen instead of import."_s)); } - auto moduleKeyZig = toZigString(moduleKey); - auto source = Zig::toZigString(value1, globalObject); + auto moduleKeyBun = Bun::toString(moduleKey); + auto source = Bun::toString(globalObject, value1); ErrorableResolvedSource res; res.success = false; res.result.err.code = 0; @@ -4148,7 +4150,7 @@ JSC::JSInternalPromise* GlobalObject::moduleLoaderFetch(JSGlobalObject* globalOb JSValue result = Bun::fetchSourceCodeAsync( reinterpret_cast<Zig::GlobalObject*>(globalObject), &res, - &moduleKeyZig, + &moduleKeyBun, &source); if (auto* internalPromise = JSC::jsDynamicCast<JSC::JSInternalPromise*>(result)) { diff --git a/src/bun.js/bindings/bindings.cpp b/src/bun.js/bindings/bindings.cpp index 8aa01abb3..e64162b34 100644 --- a/src/bun.js/bindings/bindings.cpp +++ b/src/bun.js/bindings/bindings.cpp @@ -2326,10 +2326,10 @@ static JSC::EncodedJSValue resolverFunctionCallback(JSC::JSGlobalObject* globalO JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* globalObject, - const ZigString* arg1) + const BunString* arg1) { globalObject->vm().drainMicrotasks(); - auto name = Zig::toString(*arg1); + auto name = Bun::toWTFString(*arg1); name.impl()->ref(); auto* promise = JSC::loadAndEvaluateModule(globalObject, name, JSC::jsUndefined(), JSC::jsUndefined()); diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 09902adb9..bd6c11fc4 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -19,7 +19,8 @@ const FFI = @import("./FFI.zig"); const NullableAllocator = @import("../../nullable_allocator.zig").NullableAllocator; const MutableString = bun.MutableString; const JestPrettyFormat = @import("../test/pretty_format.zig").JestPrettyFormat; - +const String = bun.String; +const ErrorableString = JSC.ErrorableString; pub const JSObject = extern struct { pub const shim = Shimmer("JSC", "JSObject", @This()); bytes: shim.Bytes, @@ -87,6 +88,7 @@ pub const JSObject = extern struct { }; }; +/// Prefer using bun.String instead of ZigString in new code. pub const ZigString = extern struct { /// This can be a UTF-16, Latin1, or UTF-8 string. /// The pointer itself is tagged, so it cannot be used without untagging it first @@ -99,6 +101,13 @@ pub const ZigString = extern struct { utf16: []const u16, }; + pub fn fromBytes(slice_: []const u8) ZigString { + if (!strings.isAllASCII(slice_)) + return fromUTF8(slice_); + + return init(slice_); + } + pub inline fn as(this: ZigString) ByteString { return if (this.is16Bit()) .{ .utf16 = this.utf16SliceAligned() } else .{ .latin1 = this.slice() }; } @@ -348,11 +357,51 @@ pub const ZigString = extern struct { pub const shim = Shimmer("", "ZigString", @This()); + pub inline fn length(this: ZigString) usize { + return this.len; + } + + pub fn byteSlice(this: ZigString) []const u8 { + if (this.is16Bit()) { + return std.mem.sliceAsBytes(this.utf16SliceAligned()); + } + + return this.slice(); + } + + pub fn markStatic(this: *ZigString) void { + this.ptr = @intToPtr([*]const u8, @ptrToInt(this.ptr) | (1 << 60)); + } + + pub fn isStatic(this: *const ZigString) bool { + return @ptrToInt(this.ptr) & (1 << 60) != 0; + } + pub const Slice = struct { allocator: NullableAllocator = .{}, ptr: [*]const u8 = undefined, len: u32 = 0, + pub fn init(allocator: std.mem.Allocator, input: []const u8) Slice { + return .{ + .ptr = input.ptr, + .len = @truncate(u32, input.len), + .allocator = NullableAllocator.init(allocator), + }; + } + + pub fn toZigString(this: Slice) ZigString { + if (this.isAllocated()) + return ZigString.initUTF8(this.ptr[0..this.len]); + return ZigString.init(this.slice()); + } + + pub inline fn length(this: Slice) usize { + return this.len; + } + + pub const byteSlice = Slice.slice; + pub fn from(input: []u8, allocator: std.mem.Allocator) Slice { return .{ .ptr = input.ptr, @@ -440,6 +489,12 @@ pub const ZigString = extern struct { /// Does nothing if the slice is not allocated pub fn deinit(this: *const Slice) void { if (this.allocator.get()) |allocator| { + if (bun.String.isWTFAllocator(allocator)) { + // workaround for https://github.com/ziglang/zig/issues/4298 + bun.String.StringImplAllocator.free(allocator.ptr, bun.constStrToU8(this.slice()), 0, 0); + return; + } + allocator.free(this.slice()); } } @@ -1685,31 +1740,31 @@ pub fn NewGlobalObject(comptime Type: type) type { const importNotImpl = "Import not implemented"; const resolveNotImpl = "resolve not implemented"; const moduleNotImpl = "Module fetch not implemented"; - pub fn import(global: *JSGlobalObject, specifier: *ZigString, source: *ZigString) callconv(.C) ErrorableZigString { + pub fn import(global: *JSGlobalObject, specifier: *String, source: *String) callconv(.C) ErrorableString { if (comptime @hasDecl(Type, "import")) { return @call(.always_inline, Type.import, .{ global, specifier.*, source.* }); } - return ErrorableZigString.err(error.ImportFailed, ZigString.init(importNotImpl).toErrorInstance(global).asVoid()); + return ErrorableString.err(error.ImportFailed, String.init(importNotImpl).toErrorInstance(global).asVoid()); } pub fn resolve( - res: *ErrorableZigString, + res: *ErrorableString, global: *JSGlobalObject, - specifier: *ZigString, - source: *ZigString, + specifier: *String, + source: *String, query_string: *ZigString, ) callconv(.C) void { if (comptime @hasDecl(Type, "resolve")) { @call(.always_inline, Type.resolve, .{ res, global, specifier.*, source.*, query_string, true }); return; } - res.* = ErrorableZigString.err(error.ResolveFailed, ZigString.init(resolveNotImpl).toErrorInstance(global).asVoid()); + res.* = ErrorableString.err(error.ResolveFailed, String.init(resolveNotImpl).toErrorInstance(global).asVoid()); } - pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: *ZigString, source: *ZigString) callconv(.C) void { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: *String, source: *String) callconv(.C) void { if (comptime @hasDecl(Type, "fetch")) { @call(.always_inline, Type.fetch, .{ ret, global, specifier.*, source.* }); return; } - ret.* = ErrorableResolvedSource.err(error.FetchFailed, ZigString.init(moduleNotImpl).toErrorInstance(global).asVoid()); + ret.* = ErrorableResolvedSource.err(error.FetchFailed, String.init(moduleNotImpl).toErrorInstance(global).asVoid()); } pub fn promiseRejectionTracker(global: *JSGlobalObject, promise: *JSPromise, rejection: JSPromiseRejectionOperation) callconv(.C) JSValue { if (comptime @hasDecl(Type, "promiseRejectionTracker")) { @@ -1769,7 +1824,7 @@ pub const JSModuleLoader = extern struct { }); } - pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const ZigString) *JSInternalPromise { + pub fn loadAndEvaluateModule(globalObject: *JSGlobalObject, module_name: *const bun.String) *JSInternalPromise { return shim.cppFn("loadAndEvaluateModule", .{ globalObject, module_name, @@ -2483,12 +2538,12 @@ pub const JSGlobalObject = extern struct { node = 1, browser = 2, }; - extern fn Bun__runOnLoadPlugins(*JSC.JSGlobalObject, ?*const ZigString, *const ZigString, BunPluginTarget) JSValue; - extern fn Bun__runOnResolvePlugins(*JSC.JSGlobalObject, ?*const ZigString, *const ZigString, *const ZigString, BunPluginTarget) JSValue; + extern fn Bun__runOnLoadPlugins(*JSC.JSGlobalObject, ?*const bun.String, *const bun.String, BunPluginTarget) JSValue; + extern fn Bun__runOnResolvePlugins(*JSC.JSGlobalObject, ?*const bun.String, *const bun.String, *const String, BunPluginTarget) JSValue; - pub fn runOnLoadPlugins(this: *JSGlobalObject, namespace_: ZigString, path: ZigString, target: BunPluginTarget) ?JSValue { + pub fn runOnLoadPlugins(this: *JSGlobalObject, namespace_: bun.String, path: bun.String, target: BunPluginTarget) ?JSValue { JSC.markBinding(@src()); - const result = Bun__runOnLoadPlugins(this, if (namespace_.len > 0) &namespace_ else null, &path, target); + const result = Bun__runOnLoadPlugins(this, if (namespace_.length() > 0) &namespace_ else null, &path, target); if (result.isEmptyOrUndefinedOrNull()) { return null; } @@ -2496,10 +2551,10 @@ pub const JSGlobalObject = extern struct { return result; } - pub fn runOnResolvePlugins(this: *JSGlobalObject, namespace_: ZigString, path: ZigString, source: ZigString, target: BunPluginTarget) ?JSValue { + pub fn runOnResolvePlugins(this: *JSGlobalObject, namespace_: bun.String, path: bun.String, source: bun.String, target: BunPluginTarget) ?JSValue { JSC.markBinding(@src()); - const result = Bun__runOnResolvePlugins(this, if (namespace_.len > 0) &namespace_ else null, &path, &source, target); + const result = Bun__runOnResolvePlugins(this, if (namespace_.length() > 0) &namespace_ else null, &path, &source, target); if (result.isEmptyOrUndefinedOrNull()) { return null; } @@ -3970,6 +4025,10 @@ pub const JSValue = enum(JSValueReprInt) { return cppFn("toZigString", .{ this, out, global }); } + pub fn toBunString(this: JSValue, globalObject: *JSC.JSGlobalObject) bun.String { + return bun.String.fromJS(this, globalObject); + } + /// this: RegExp value /// other: string value pub fn toMatch(this: JSValue, global: *JSGlobalObject, other: JSValue) bool { diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index e6bc953da..e48acb8c4 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -53,16 +53,16 @@ pub const ZigGlobalObject = extern struct { return shim.cppFn("resetModuleRegistryMap", .{ global, map }); } - pub fn import(global: *JSGlobalObject, specifier: *ZigString, source: *ZigString) callconv(.C) ErrorableZigString { + pub fn import(global: *JSGlobalObject, specifier: *bun.String, source: *bun.String) callconv(.C) ErrorableString { JSC.markBinding(@src()); return @call(.always_inline, Interface.import, .{ global, specifier, source }); } - pub fn resolve(res: *ErrorableZigString, global: *JSGlobalObject, specifier: *ZigString, source: *ZigString, query: *ZigString) callconv(.C) void { + pub fn resolve(res: *ErrorableString, global: *JSGlobalObject, specifier: *bun.String, source: *bun.String, query: *ZigString) callconv(.C) void { JSC.markBinding(@src()); @call(.always_inline, Interface.resolve, .{ res, global, specifier, source, query }); } - pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: *ZigString, source: *ZigString) callconv(.C) void { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: *bun.String, source: *bun.String) callconv(.C) void { JSC.markBinding(@src()); @call(.always_inline, Interface.fetch, .{ ret, global, specifier, source }); } @@ -202,7 +202,7 @@ pub const ResolvedSource = extern struct { pub const name = "ResolvedSource"; pub const namespace = shim.namespace; - specifier: ZigString, + specifier: bun.String, source_code: ZigString, source_url: ZigString, commonjs_exports: ?[*]ZigString = null, @@ -866,6 +866,7 @@ pub const ZigException = extern struct { pub const ErrorableResolvedSource = Errorable(ResolvedSource); pub const ErrorableZigString = Errorable(ZigString); pub const ErrorableJSValue = Errorable(JSValue); +pub const ErrorableString = Errorable(bun.String); pub const ZigConsoleClient = struct { pub const shim = Shimmer("Zig", "ConsoleClient", @This()); diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 650203653..9a8cb1ef3 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -9,6 +9,39 @@ typedef struct ZigString { const unsigned char* ptr; size_t len; } ZigString; + +#ifndef __cplusplus +typedef uint8_t BunStringTag; +typedef union BunStringImpl { + ZigString zig; + void* wtf; +} BunStringImpl; + +typedef struct BunString { + BunStringTag tag; + BunStringImpl impl; +} BunString; +#else +typedef union BunStringImpl { + ZigString zig; + WTF::StringImpl* wtf; +} BunStringImpl; + +enum class BunStringTag : uint8_t { + Dead = 0, + WTFStringImpl = 1, + ZigString = 2, + StaticZigString = 3, + Empty = 4, +}; + +typedef struct BunString { + BunStringTag tag; + BunStringImpl impl; +} BunString; + +#endif + typedef struct ZigErrorType { ZigErrorCode code; void* ptr; @@ -21,8 +54,16 @@ typedef struct ErrorableZigString { ErrorableZigStringResult result; bool success; } ErrorableZigString; +typedef union ErrorableStringResult { + BunString value; + ZigErrorType err; +} ErrorableStringResult; +typedef struct ErrorableString { + ErrorableStringResult result; + bool success; +} ErrorableString; typedef struct ResolvedSource { - ZigString specifier; + BunString specifier; ZigString source_code; ZigString source_url; ZigString* commonJSExports; @@ -190,6 +231,22 @@ typedef struct Uint8Array_alias Uint8Array_alias; #ifdef __cplusplus +extern "C" void Bun__WTFStringImpl__deref(WTF::StringImpl* impl); +extern "C" void Bun__WTFStringImpl__ref(WTF::StringImpl* impl); +extern "C" bool BunString__fromJS(JSC::JSGlobalObject*, JSC::EncodedJSValue, BunString*); +extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject*, BunString*); +extern "C" void BunString__toWTFString(BunString*); + +namespace Bun { +JSC::JSValue toJS(JSC::JSGlobalObject*, BunString); +BunString toString(JSC::JSGlobalObject* globalObject, JSC::JSValue value); +WTF::String toWTFString(const BunString& bunString); +BunString toString(WTF::String& wtfString); +BunString toString(const WTF::String& wtfString); +BunString toString(WTF::StringImpl* wtfString); + +} + using Uint8Array_alias = JSC::JSUint8Array; typedef struct { @@ -224,21 +281,21 @@ extern "C" void Microtask__run_default(void* ptr, void* global); extern "C" bool Bun__transpileVirtualModule( JSC::JSGlobalObject* global, - const ZigString* specifier, - const ZigString* referrer, + const BunString* specifier, + const BunString* referrer, ZigString* sourceCode, BunLoaderType loader, ErrorableResolvedSource* result); extern "C" JSC::EncodedJSValue Bun__runVirtualModule( JSC::JSGlobalObject* global, - const ZigString* specifier); + const BunString* specifier); extern "C" void* Bun__transpileFile( void* bunVM, JSC::JSGlobalObject* global, - const ZigString* specifier, - const ZigString* referrer, + const BunString* specifier, + const BunString* referrer, ErrorableResolvedSource* result, bool allowPromise); extern "C" JSC::EncodedJSValue CallbackJob__onResolve(JSC::JSGlobalObject*, JSC::CallFrame*); @@ -247,8 +304,8 @@ extern "C" JSC::EncodedJSValue CallbackJob__onReject(JSC::JSGlobalObject*, JSC:: extern "C" bool Bun__fetchBuiltinModule( void* bunVM, JSC::JSGlobalObject* global, - const ZigString* specifier, - const ZigString* referrer, + const BunString* specifier, + const BunString* referrer, ErrorableResolvedSource* result); // Used in process.version diff --git a/src/bun.js/bindings/headers-replacements.zig b/src/bun.js/bindings/headers-replacements.zig index b84a7b65a..a11f04564 100644 --- a/src/bun.js/bindings/headers-replacements.zig +++ b/src/bun.js/bindings/headers-replacements.zig @@ -69,3 +69,4 @@ pub const WebSocketHTTPClient = bindings.WebSocketHTTPClient; pub const WebSocketHTTPSClient = bindings.WebSocketHTTPSClient; pub const WebSocketClient = bindings.WebSocketClient; pub const WebSocketClientTLS = bindings.WebSocketClientTLS; +pub const BunString = @import("root").bun.String; diff --git a/src/bun.js/bindings/headers.h b/src/bun.js/bindings/headers.h index 9e9254bb2..216a4f6cc 100644 --- a/src/bun.js/bindings/headers.h +++ b/src/bun.js/bindings/headers.h @@ -56,8 +56,9 @@ typedef void* JSClassRef; #ifndef __cplusplus typedef bJSC__CatchScope JSC__CatchScope; // JSC::CatchScope typedef ErrorableResolvedSource ErrorableResolvedSource; + typedef BunString BunString; + typedef ErrorableString ErrorableString; typedef bJSC__ThrowScope JSC__ThrowScope; // JSC::ThrowScope - typedef ErrorableZigString ErrorableZigString; typedef bJSC__JSObject JSC__JSObject; // JSC::JSObject typedef WebSocketClient WebSocketClient; typedef struct WebCore__AbortSignal WebCore__AbortSignal; // WebCore::AbortSignal @@ -109,7 +110,8 @@ typedef void* JSClassRef; } typedef ErrorableResolvedSource ErrorableResolvedSource; - typedef ErrorableZigString ErrorableZigString; + typedef BunString BunString; + typedef ErrorableString ErrorableString; typedef WebSocketClient WebSocketClient; typedef WebSocketHTTPSClient WebSocketHTTPSClient; typedef JSClassRef JSClassRef; @@ -216,7 +218,7 @@ CPP_DECL void JSC__JSString__toZigString(JSC__JSString* arg0, JSC__JSGlobalObjec #pragma mark - JSC::JSModuleLoader CPP_DECL JSC__JSValue JSC__JSModuleLoader__evaluate(JSC__JSGlobalObject* arg0, const unsigned char* arg1, size_t arg2, const unsigned char* arg3, size_t arg4, const unsigned char* arg5, size_t arg6, JSC__JSValue JSValue7, JSC__JSValue* arg8); -CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const ZigString* arg1); +CPP_DECL JSC__JSInternalPromise* JSC__JSModuleLoader__loadAndEvaluateModule(JSC__JSGlobalObject* arg0, const BunString* arg1); #pragma mark - WebCore::AbortSignal @@ -574,12 +576,12 @@ CPP_DECL bool Zig__GlobalObject__resetModuleRegistryMap(JSC__JSGlobalObject* arg #ifdef __cplusplus -ZIG_DECL void Zig__GlobalObject__fetch(ErrorableResolvedSource* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3); -ZIG_DECL ErrorableZigString Zig__GlobalObject__import(JSC__JSGlobalObject* arg0, ZigString* arg1, ZigString* arg2); +ZIG_DECL void Zig__GlobalObject__fetch(ErrorableResolvedSource* arg0, JSC__JSGlobalObject* arg1, BunString* arg2, BunString* arg3); +ZIG_DECL ErrorableString Zig__GlobalObject__import(JSC__JSGlobalObject* arg0, BunString* arg1, BunString* arg2); ZIG_DECL void Zig__GlobalObject__onCrash(); ZIG_DECL JSC__JSValue Zig__GlobalObject__promiseRejectionTracker(JSC__JSGlobalObject* arg0, JSC__JSPromise* arg1, uint32_t JSPromiseRejectionOperation2); ZIG_DECL JSC__JSValue Zig__GlobalObject__reportUncaughtException(JSC__JSGlobalObject* arg0, JSC__Exception* arg1); -ZIG_DECL void Zig__GlobalObject__resolve(ErrorableZigString* arg0, JSC__JSGlobalObject* arg1, ZigString* arg2, ZigString* arg3, ZigString* arg4); +ZIG_DECL void Zig__GlobalObject__resolve(ErrorableString* arg0, JSC__JSGlobalObject* arg1, BunString* arg2, BunString* arg3, ZigString* arg4); #endif diff --git a/src/bun.js/bindings/headers.zig b/src/bun.js/bindings/headers.zig index 7d075e9e6..082f21418 100644 --- a/src/bun.js/bindings/headers.zig +++ b/src/bun.js/bindings/headers.zig @@ -71,6 +71,7 @@ pub const WebSocketHTTPClient = bindings.WebSocketHTTPClient; pub const WebSocketHTTPSClient = bindings.WebSocketHTTPSClient; pub const WebSocketClient = bindings.WebSocketClient; pub const WebSocketClientTLS = bindings.WebSocketClientTLS; +pub const BunString = @import("root").bun.String; pub const JSC__ThrowScope = bJSC__ThrowScope; pub const JSC__JSObject = bJSC__JSObject; pub const JSC__VM = bJSC__VM; @@ -138,7 +139,7 @@ pub extern fn JSC__JSString__length(arg0: [*c]const JSC__JSString) usize; pub extern fn JSC__JSString__toObject(arg0: [*c]bindings.JSString, arg1: *bindings.JSGlobalObject) [*c]bindings.JSObject; pub extern fn JSC__JSString__toZigString(arg0: [*c]bindings.JSString, arg1: *bindings.JSGlobalObject, arg2: [*c]ZigString) void; pub extern fn JSC__JSModuleLoader__evaluate(arg0: *bindings.JSGlobalObject, arg1: [*c]const u8, arg2: usize, arg3: [*c]const u8, arg4: usize, arg5: [*c]const u8, arg6: usize, JSValue7: JSC__JSValue, arg8: [*c]bindings.JSValue) JSC__JSValue; -pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: *bindings.JSGlobalObject, arg1: [*c]const ZigString) [*c]bindings.JSInternalPromise; +pub extern fn JSC__JSModuleLoader__loadAndEvaluateModule(arg0: *bindings.JSGlobalObject, arg1: [*c]const BunString) [*c]bindings.JSInternalPromise; pub extern fn WebCore__AbortSignal__aborted(arg0: ?*bindings.AbortSignal) bool; pub extern fn WebCore__AbortSignal__abortReason(arg0: ?*bindings.AbortSignal) JSC__JSValue; pub extern fn WebCore__AbortSignal__addListener(arg0: ?*bindings.AbortSignal, arg1: ?*anyopaque, ArgFn2: ?*const fn (?*anyopaque, JSC__JSValue) callconv(.C) void) ?*bindings.AbortSignal; diff --git a/src/bun.js/bindings/helpers.h b/src/bun.js/bindings/helpers.h index 8a96a94af..402807f3d 100644 --- a/src/bun.js/bindings/helpers.h +++ b/src/bun.js/bindings/helpers.h @@ -84,7 +84,7 @@ namespace Zig { static const unsigned char* untag(const unsigned char* ptr) { return reinterpret_cast<const unsigned char*>( - ((reinterpret_cast<uintptr_t>(ptr) & ~(static_cast<uint64_t>(1) << 63) & ~(static_cast<uint64_t>(1) << 62)) & ~(static_cast<uint64_t>(1) << 61))); + (((reinterpret_cast<uintptr_t>(ptr) & ~(static_cast<uint64_t>(1) << 63) & ~(static_cast<uint64_t>(1) << 62)) & ~(static_cast<uint64_t>(1) << 61)) & ~(static_cast<uint64_t>(1) << 60))); } static void* untagVoid(const unsigned char* ptr) @@ -245,6 +245,8 @@ static const JSC::JSValue toJSStringValueGC(ZigString str, JSC::JSGlobalObject* static const ZigString ZigStringEmpty = ZigString { nullptr, 0 }; static const unsigned char __dot_char = '.'; static const ZigString ZigStringCwd = ZigString { &__dot_char, 1 }; +static const BunString BunStringCwd = BunString { BunStringTag::StaticZigString, ZigStringCwd }; +static const BunString BunStringEmpty = BunString { BunStringTag::Empty, nullptr }; static const unsigned char* taggedUTF16Ptr(const UChar* ptr) { @@ -330,6 +332,23 @@ static ZigString toZigString(JSC::JSValue val, JSC::JSGlobalObject* global) return toZigString(str); } +static const WTF::String toStringStatic(ZigString str) +{ + if (str.len == 0 || str.ptr == nullptr) { + return WTF::String(); + } + if (UNLIKELY(isTaggedUTF8Ptr(str.ptr))) { + abort(); + } + + if (isTaggedUTF16Ptr(str.ptr)) { + return WTF::String(WTF::ExternalStringImpl::createStatic(reinterpret_cast<const UChar*>(untag(str.ptr)), str.len)); + } + + return WTF::String(WTF::ExternalStringImpl::createStatic( + reinterpret_cast<const LChar*>(untag(str.ptr)), str.len)); +} + static JSC::JSValue getErrorInstance(const ZigString* str, JSC__JSGlobalObject* globalObject) { JSC::VM& vm = globalObject->vm(); diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index c54d84388..5bd066a9a 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -11,6 +11,7 @@ const MutableString = bun.MutableString; const stringZ = bun.stringZ; const default_allocator = bun.default_allocator; const StoredFileDescriptorType = bun.StoredFileDescriptorType; +const ErrorableString = bun.JSC.ErrorableString; const Arena = @import("../mimalloc_arena.zig").Arena; const C = bun.C; const NetworkThread = @import("root").bun.HTTP.NetworkThread; @@ -47,6 +48,7 @@ const WebCore = @import("root").bun.JSC.WebCore; const Request = WebCore.Request; const Response = WebCore.Response; const Headers = WebCore.Headers; +const String = bun.String; const Fetch = WebCore.Fetch; const FetchEvent = WebCore.FetchEvent; const js = @import("root").bun.JSC.C; @@ -934,12 +936,12 @@ pub const VirtualMachine = struct { _ = VirtualMachine.get().ref_strings.remove(ref_string.hash); } - pub fn refCountedResolvedSource(this: *VirtualMachine, code: []const u8, specifier: []const u8, source_url: []const u8, hash_: ?u32) ResolvedSource { + pub fn refCountedResolvedSource(this: *VirtualMachine, code: []const u8, specifier: bun.String, source_url: []const u8, hash_: ?u32) ResolvedSource { var source = this.refCountedString(code, hash_, true); return ResolvedSource{ .source_code = ZigString.init(source.slice()), - .specifier = ZigString.init(specifier), + .specifier = specifier, .source_url = ZigString.init(source_url), .hash = source.hash, .allocator = source, @@ -994,20 +996,26 @@ pub const VirtualMachine = struct { pub fn fetchWithoutOnLoadPlugins( jsc_vm: *VirtualMachine, globalObject: *JSC.JSGlobalObject, - _specifier: string, - referrer: string, + _specifier: String, + referrer: String, log: *logger.Log, ret: *ErrorableResolvedSource, comptime flags: FetchFlags, - ) !ResolvedSource { + ) anyerror!ResolvedSource { std.debug.assert(VirtualMachine.isLoaded()); if (try ModuleLoader.fetchBuiltinModule(jsc_vm, _specifier, log, comptime flags.disableTranspiling())) |builtin| { return builtin; } - var display_specifier = _specifier; - var specifier = ModuleLoader.normalizeSpecifier(jsc_vm, _specifier, &display_specifier); - var path = Fs.Path.init(specifier); + var display_specifier = _specifier.toUTF8(bun.default_allocator); + defer display_specifier.deinit(); + var specifier_clone = _specifier.toUTF8(bun.default_allocator); + defer specifier_clone.deinit(); + var display_slice = display_specifier.slice(); + var specifier = ModuleLoader.normalizeSpecifier(jsc_vm, specifier_clone.slice(), &display_slice); + const referrer_clone = referrer.toUTF8(bun.default_allocator); + defer referrer_clone.deinit(); + var path = Fs.Path.init(specifier_clone.slice()); const loader = jsc_vm.bundler.options.loaders.get(path.name.ext) orelse brk: { if (strings.eqlLong(specifier, jsc_vm.main, true)) { break :brk options.Loader.js; @@ -1018,9 +1026,10 @@ pub const VirtualMachine = struct { return try ModuleLoader.transpileSourceCode( jsc_vm, - specifier, - display_specifier, - referrer, + specifier_clone.slice(), + display_slice, + referrer_clone.slice(), + _specifier, path, loader, log, @@ -1214,10 +1223,10 @@ pub const VirtualMachine = struct { } pub fn resolveForAPI( - res: *ErrorableZigString, + res: *ErrorableString, global: *JSGlobalObject, - specifier: ZigString, - source: ZigString, + specifier: bun.String, + source: bun.String, query_string: *ZigString, is_esm: bool, ) void { @@ -1225,10 +1234,10 @@ pub const VirtualMachine = struct { } pub fn resolveFilePathForAPI( - res: *ErrorableZigString, + res: *ErrorableString, global: *JSGlobalObject, - specifier: ZigString, - source: ZigString, + specifier: bun.String, + source: bun.String, query_string: *ZigString, is_esm: bool, ) void { @@ -1236,10 +1245,10 @@ pub const VirtualMachine = struct { } pub fn resolve( - res: *ErrorableZigString, + res: *ErrorableString, global: *JSGlobalObject, - specifier: ZigString, - source: ZigString, + specifier: bun.String, + source: bun.String, query_string: *ZigString, is_esm: bool, ) void { @@ -1255,10 +1264,10 @@ pub const VirtualMachine = struct { } fn resolveMaybeNeedsTrailingSlash( - res: *ErrorableZigString, + res: *ErrorableString, global: *JSGlobalObject, - specifier: ZigString, - source: ZigString, + specifier: bun.String, + source: bun.String, query_string: ?*ZigString, is_esm: bool, comptime is_a_file_path: bool, @@ -1266,27 +1275,32 @@ pub const VirtualMachine = struct { ) void { var result = ResolveFunctionResult{ .path = "", .result = null }; var jsc_vm = VirtualMachine.get(); + const specifier_utf8 = specifier.toUTF8(bun.default_allocator); + defer specifier_utf8.deinit(); + + const source_utf8 = source.toUTF8(bun.default_allocator); + defer source_utf8.deinit(); if (jsc_vm.plugin_runner) |plugin_runner| { - if (PluginRunner.couldBePlugin(specifier.slice())) { - const namespace = PluginRunner.extractNamespace(specifier.slice()); + if (PluginRunner.couldBePlugin(specifier_utf8.slice())) { + const namespace = PluginRunner.extractNamespace(specifier_utf8.slice()); const after_namespace = if (namespace.len == 0) - specifier + specifier_utf8.slice() else - specifier.substring(namespace.len + 1, specifier.len); + specifier_utf8.slice()[namespace.len + 1 .. specifier_utf8.len]; - if (plugin_runner.onResolveJSC(ZigString.init(namespace), after_namespace, source, .bun)) |resolved_path| { + if (plugin_runner.onResolveJSC(bun.String.init(namespace), bun.String.fromUTF8(after_namespace), source, .bun)) |resolved_path| { res.* = resolved_path; return; } } } - if (JSC.HardcodedModule.Aliases.getWithEql(specifier, ZigString.eqlComptime)) |hardcoded| { + if (JSC.HardcodedModule.Aliases.getWithEql(specifier, bun.String.eqlComptime)) |hardcoded| { if (hardcoded.tag == .none) { resolveMaybeNeedsTrailingSlash( res, global, - ZigString.init(hardcoded.path), + bun.String.init(hardcoded.path), source, query_string, is_esm, @@ -1296,7 +1310,7 @@ pub const VirtualMachine = struct { return; } - res.* = ErrorableZigString.ok(ZigString.init(hardcoded.path)); + res.* = ErrorableString.ok(bun.String.init(hardcoded.path)); return; } @@ -1311,7 +1325,7 @@ pub const VirtualMachine = struct { jsc_vm.bundler.linker.log = old_log; jsc_vm.bundler.resolver.log = old_log; } - _resolve(&result, global, specifier.slice(), normalizeSource(source.slice()), is_esm, is_a_file_path, realpath) catch |err_| { + _resolve(&result, global, specifier_utf8.slice(), normalizeSource(source_utf8.slice()), is_esm, is_a_file_path, realpath) catch |err_| { var err = err_; const msg: logger.Msg = brk: { var msgs: []logger.Msg = log.msgs.items; @@ -1325,8 +1339,8 @@ pub const VirtualMachine = struct { const printed = ResolveMessage.fmt( jsc_vm.allocator, - specifier.slice(), - source.slice(), + specifier_utf8.slice(), + source_utf8.slice(), err, ) catch unreachable; break :brk logger.Msg{ @@ -1337,13 +1351,13 @@ pub const VirtualMachine = struct { ), .metadata = .{ // import_kind is wrong probably - .resolve = .{ .specifier = logger.BabyString.in(printed, specifier.slice()), .import_kind = .stmt }, + .resolve = .{ .specifier = logger.BabyString.in(printed, specifier_utf8.slice()), .import_kind = .stmt }, }, }; }; { - res.* = ErrorableZigString.err(err, ResolveMessage.create(global, VirtualMachine.get().allocator, msg, source.slice()).asVoid()); + res.* = ErrorableString.err(err, ResolveMessage.create(global, VirtualMachine.get().allocator, msg, source_utf8.slice()).asVoid()); } return; @@ -1353,7 +1367,7 @@ pub const VirtualMachine = struct { query.* = ZigString.init(result.query_string); } - res.* = ErrorableZigString.ok(ZigString.init(result.path)); + res.* = ErrorableString.ok(bun.String.init(result.path)); } // // This double prints @@ -1368,28 +1382,20 @@ pub const VirtualMachine = struct { pub const main_file_name: string = "bun:main"; - pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: ZigString, source: ZigString) callconv(.C) void { + pub fn fetch(ret: *ErrorableResolvedSource, global: *JSGlobalObject, specifier: bun.String, source: bun.String) callconv(.C) void { var jsc_vm: *VirtualMachine = if (comptime Environment.isLinux) VirtualMachine.get() else global.bunVM(); var log = logger.Log.init(jsc_vm.bundler.allocator); - var spec = specifier.toSlice(jsc_vm.allocator); - defer spec.deinit(); - var refer = source.toSlice(jsc_vm.allocator); - defer refer.deinit(); - const result = if (!jsc_vm.bundler.options.disable_transpilation) - @call(.always_inline, fetchWithoutOnLoadPlugins, .{ jsc_vm, global, spec.slice(), refer.slice(), &log, ret, .transpile }) catch |err| { - processFetchLog(global, specifier, source, &log, ret, err); - return; - } - else - fetchWithoutOnLoadPlugins(jsc_vm, global, spec.slice(), refer.slice(), &log, ret, .print_source_and_clone) catch |err| { + const result = switch (!jsc_vm.bundler.options.disable_transpilation) { + inline else => |is_disabled| fetchWithoutOnLoadPlugins(jsc_vm, global, specifier, source, &log, ret, if (comptime is_disabled) .print_source_and_clone else .transpile) catch |err| { processFetchLog(global, specifier, source, &log, ret, err); return; - }; + }, + }; if (log.errors > 0) { processFetchLog(global, specifier, source, &log, ret, error.LinkError); @@ -1417,6 +1423,7 @@ pub const VirtualMachine = struct { var vm = get(); if (vm.blobs) |blobs| { + const spec = specifier.toUTF8(bun.default_allocator); const specifier_blob = brk: { if (strings.hasPrefix(spec.slice(), VirtualMachine.get().bundler.fs.top_level_dir)) { break :brk spec.slice()[VirtualMachine.get().bundler.fs.top_level_dir.len..]; @@ -1434,7 +1441,7 @@ pub const VirtualMachine = struct { ret.success = true; } - pub fn processFetchLog(globalThis: *JSGlobalObject, specifier: ZigString, referrer: ZigString, log: *logger.Log, ret: *ErrorableResolvedSource, err: anyerror) void { + pub fn processFetchLog(globalThis: *JSGlobalObject, specifier: bun.String, referrer: bun.String, log: *logger.Log, ret: *ErrorableResolvedSource, err: anyerror) void { switch (log.msgs.items.len) { 0 => { const msg: logger.Msg = brk: { @@ -1443,13 +1450,13 @@ pub const VirtualMachine = struct { .data = logger.rangeData( null, logger.Range.None, - std.fmt.allocPrint(globalThis.allocator(), "Unexpected pending import in \"{s}\". To automatically install npm packages with Bun, please use an import statement instead of require() or dynamic import().\nThis error can also happen if dependencies import packages which are not referenced anywhere. Worst case, run `bun install` and opt-out of the node_modules folder until we come up with a better way to handle this error.", .{specifier.slice()}) catch unreachable, + std.fmt.allocPrint(globalThis.allocator(), "Unexpected pending import in \"{}\". To automatically install npm packages with Bun, please use an import statement instead of require() or dynamic import().\nThis error can also happen if dependencies import packages which are not referenced anywhere. Worst case, run `bun install` and opt-out of the node_modules folder until we come up with a better way to handle this error.", .{specifier}) catch unreachable, ), }; } break :brk logger.Msg{ - .data = logger.rangeData(null, logger.Range.None, std.fmt.allocPrint(globalThis.allocator(), "{s} while building {s}", .{ @errorName(err), specifier.slice() }) catch unreachable), + .data = logger.rangeData(null, logger.Range.None, std.fmt.allocPrint(globalThis.allocator(), "{s} while building {}", .{ @errorName(err), specifier }) catch unreachable), }; }; { @@ -1466,7 +1473,7 @@ pub const VirtualMachine = struct { globalThis, globalThis.allocator(), msg, - referrer.slice(), + referrer.toUTF8(bun.default_allocator).slice(), ).asVoid(), }); return; @@ -1476,14 +1483,14 @@ pub const VirtualMachine = struct { var errors = errors_stack[0..@min(log.msgs.items.len, errors_stack.len)]; - for (log.msgs.items, 0..) |msg, i| { - errors[i] = switch (msg.metadata) { + for (log.msgs.items, errors) |msg, *current| { + current.* = switch (msg.metadata) { .build => BuildMessage.create(globalThis, globalThis.allocator(), msg).asVoid(), .resolve => ResolveMessage.create( globalThis, globalThis.allocator(), msg, - referrer.slice(), + referrer.toUTF8(bun.default_allocator).slice(), ).asVoid(), }; } @@ -1494,9 +1501,9 @@ pub const VirtualMachine = struct { errors.ptr, @intCast(u16, errors.len), &ZigString.init( - std.fmt.allocPrint(globalThis.allocator(), "{d} errors building \"{s}\"", .{ + std.fmt.allocPrint(globalThis.allocator(), "{d} errors building \"{}\"", .{ errors.len, - specifier.slice(), + referrer, }) catch unreachable, ), ).asVoid(), @@ -1582,7 +1589,7 @@ pub const VirtualMachine = struct { // The contents of the node_modules bundle are lazy, so hopefully this should be pretty quick. if (this.node_modules != null and !this.has_loaded_node_modules) { this.has_loaded_node_modules = true; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.static(bun_file_import_path)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.static(bun_file_import_path)); this.waitForPromise(JSC.AnyPromise{ .Internal = promise, }); @@ -1627,7 +1634,7 @@ pub const VirtualMachine = struct { return error.ModuleNotFound; }, }; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(result.path().?.text)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.fromBytes(result.path().?.text)); this.pending_internal_promise = promise; JSValue.fromCell(promise).protect(); @@ -1663,11 +1670,11 @@ pub const VirtualMachine = struct { // only load preloads once this.preload.len = 0; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, ZigString.static(main_file_name)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(main_file_name)); this.pending_internal_promise = promise; JSC.JSValue.fromCell(promise).ensureStillAlive(); } else { - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(this.main)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(this.main)); this.pending_internal_promise = promise; JSC.JSValue.fromCell(promise).ensureStillAlive(); } @@ -1743,7 +1750,7 @@ pub const VirtualMachine = struct { pub inline fn _loadMacroEntryPoint(this: *VirtualMachine, entry_path: string) *JSInternalPromise { var promise: *JSInternalPromise = undefined; - promise = JSModuleLoader.loadAndEvaluateModule(this.global, &ZigString.init(entry_path)); + promise = JSModuleLoader.loadAndEvaluateModule(this.global, &String.init(entry_path)); this.waitForPromise(JSC.AnyPromise{ .Internal = promise, }); @@ -2025,7 +2032,7 @@ pub const VirtualMachine = struct { )) |mapping| { var log = logger.Log.init(default_allocator); var errorable: ErrorableResolvedSource = undefined; - var original_source = fetchWithoutOnLoadPlugins(this, this.global, top.source_url.slice(), "", &log, &errorable, .print_source) catch return; + var original_source = fetchWithoutOnLoadPlugins(this, this.global, bun.String.init(top.source_url), bun.String.empty, &log, &errorable, .print_source) catch return; const code = original_source.source_code.slice(); top.position.line = mapping.original.lines; top.position.line_start = mapping.original.lines; diff --git a/src/bun.js/module_loader.zig b/src/bun.js/module_loader.zig index f4cfe28e4..4611adea4 100644 --- a/src/bun.js/module_loader.zig +++ b/src/bun.js/module_loader.zig @@ -87,6 +87,8 @@ const Install = @import("../install/install.zig"); const VirtualMachine = JSC.VirtualMachine; const Dependency = @import("../install/dependency.zig"); +const String = bun.String; + // Setting BUN_OVERRIDE_MODULE_PATH to the path to the bun repo will make it so modules are loaded // from there instead of the ones embedded into the binary. // In debug mode, this is set automatically for you, using the path relative to this file. @@ -137,11 +139,11 @@ fn jsModuleFromFile(from_path: string, comptime input: string) string { return contents; } -inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag) ResolvedSource { +inline fn jsSyntheticModule(comptime name: ResolvedSource.Tag, specifier: String) ResolvedSource { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(""), - .specifier = ZigString.init(@tagName(name)), + .specifier = specifier, .source_url = ZigString.init(@tagName(name)), .hash = 0, .tag = name, @@ -539,8 +541,8 @@ pub const ModuleLoader = struct { errorable = ErrorableResolvedSource.ok(this.resumeLoadingModule(&log) catch |err| { JSC.VirtualMachine.processFetchLog( this.globalThis, - ZigString.init(this.specifier), - ZigString.init(this.referrer), + bun.String.init(this.specifier), + bun.String.init(this.referrer), &log, &errorable, err, @@ -549,8 +551,8 @@ pub const ModuleLoader = struct { }); } - var spec = ZigString.init(this.specifier).withEncoding(); - var ref = ZigString.init(this.referrer).withEncoding(); + var spec = bun.String.init(ZigString.init(this.specifier).withEncoding()); + var ref = bun.String.init(ZigString.init(this.referrer).withEncoding()); Bun__onFulfillAsyncModule( this.promise.get().?, &errorable, @@ -807,7 +809,7 @@ pub const ModuleLoader = struct { } if (jsc_vm.isWatcherEnabled()) { - var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, specifier, path.text, null); + var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, bun.String.init(specifier), path.text, null); if (parse_result.input_fd) |fd_| { if (jsc_vm.bun_watcher != null and std.fs.path.isAbsolute(path.text) and !strings.contains(path.text, "node_modules")) { @@ -840,7 +842,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), - .specifier = ZigString.init(specifier), + .specifier = String.init(specifier), .source_url = ZigString.init(path.text), .commonjs_exports = if (commonjs_exports.len > 0) commonjs_exports.ptr @@ -871,14 +873,14 @@ pub const ModuleLoader = struct { extern "C" fn Bun__onFulfillAsyncModule( promiseValue: JSC.JSValue, res: *JSC.ErrorableResolvedSource, - specifier: *ZigString, - referrer: *ZigString, + specifier: *bun.String, + referrer: *bun.String, ) void; }; - pub export fn Bun__getDefaultLoader(global: *JSC.JSGlobalObject, str: *const ZigString) Api.Loader { + pub export fn Bun__getDefaultLoader(global: *JSC.JSGlobalObject, str: *const bun.String) Api.Loader { var jsc_vm = global.bunVM(); - const filename = str.toSlice(jsc_vm.allocator); + const filename = str.toUTF8(jsc_vm.allocator); defer filename.deinit(); const loader = jsc_vm.bundler.options.loader(Fs.PathName.init(filename.slice()).ext).toAPI(); if (loader == .file) { @@ -893,6 +895,7 @@ pub const ModuleLoader = struct { specifier: string, display_specifier: string, referrer: string, + input_specifier: String, path: Fs.Path, loader: options.Loader, log: *logger.Log, @@ -1016,6 +1019,7 @@ pub const ModuleLoader = struct { specifier, display_specifier, referrer, + input_specifier, path, .wasm, log, @@ -1059,7 +1063,7 @@ pub const ModuleLoader = struct { .print_source => ZigString.init(parse_result.source.contents), else => unreachable, }, - .specifier = ZigString.init(display_specifier), + .specifier = input_specifier, .source_url = ZigString.init(path.text), .hash = 0, }; @@ -1069,7 +1073,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(try default_allocator.dupe(u8, parse_result.source.contents)), - .specifier = ZigString.init(specifier), + .specifier = input_specifier, .source_url = ZigString.init(path.text), // // TODO: change hash to a bitfield // .hash = 1, @@ -1152,7 +1156,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = ZigString.init("// auto-generated plugin stub\nexport default undefined\n"), - .specifier = ZigString.init(specifier), + .specifier = input_specifier, .source_url = ZigString.init(path.text), // // TODO: change hash to a bitfield // .hash = 1, @@ -1174,7 +1178,7 @@ pub const ModuleLoader = struct { } if (jsc_vm.isWatcherEnabled()) { - var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, display_specifier, path.text, null); + var resolved_source = jsc_vm.refCountedResolvedSource(printer.ctx.written, input_specifier, path.text, null); resolved_source.commonjs_exports = if (commonjs_exports.len > 0) commonjs_exports.ptr @@ -1192,7 +1196,7 @@ pub const ModuleLoader = struct { return .{ .allocator = null, .source_code = ZigString.init(try default_allocator.dupe(u8, printer.ctx.getWritten())), - .specifier = ZigString.init(display_specifier), + .specifier = input_specifier, .source_url = ZigString.init(path.text), .commonjs_exports = if (commonjs_exports.len > 0) commonjs_exports.ptr @@ -1280,7 +1284,7 @@ pub const ModuleLoader = struct { @as(string, jsModuleFromFile(jsc_vm.load_builtins_from_path, "bun/wasi-runner.js")), ) catch unreachable, ), - .specifier = ZigString.init(display_specifier), + .specifier = input_specifier, .source_url = ZigString.init(path.text), .hash = 0, }; @@ -1291,6 +1295,7 @@ pub const ModuleLoader = struct { specifier, display_specifier, referrer, + input_specifier, path, .file, log, @@ -1325,7 +1330,7 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = &jsc_vm.allocator, .source_code = public_url, - .specifier = ZigString.init(path.text), + .specifier = input_specifier, .source_url = ZigString.init(path.text), .hash = 0, }; @@ -1496,14 +1501,14 @@ pub const ModuleLoader = struct { pub export fn Bun__fetchBuiltinModule( jsc_vm: *VirtualMachine, globalObject: *JSC.JSGlobalObject, - specifier: *ZigString, - referrer: *ZigString, + specifier: *bun.String, + referrer: *bun.String, ret: *ErrorableResolvedSource, ) bool { JSC.markBinding(@src()); var log = logger.Log.init(jsc_vm.bundler.allocator); defer log.deinit(); - if (ModuleLoader.fetchBuiltinModule(jsc_vm, specifier.slice(), &log, false) catch |err| { + if (ModuleLoader.fetchBuiltinModule(jsc_vm, specifier.*, &log, false) catch |err| { if (err == error.AsyncModule) { unreachable; } @@ -1521,8 +1526,8 @@ pub const ModuleLoader = struct { pub export fn Bun__transpileFile( jsc_vm: *VirtualMachine, globalObject: *JSC.JSGlobalObject, - specifier_ptr: *const ZigString, - referrer: *const ZigString, + specifier_ptr: *const bun.String, + referrer: *const bun.String, ret: *ErrorableResolvedSource, allow_promise: bool, ) ?*anyopaque { @@ -1531,8 +1536,8 @@ pub const ModuleLoader = struct { defer log.deinit(); debug("transpileFile: {any}", .{specifier_ptr.*}); - var _specifier = specifier_ptr.toSlice(jsc_vm.allocator); - var referrer_slice = referrer.toSlice(jsc_vm.allocator); + var _specifier = specifier_ptr.toUTF8(jsc_vm.allocator); + var referrer_slice = referrer.toUTF8(jsc_vm.allocator); defer _specifier.deinit(); defer referrer_slice.deinit(); var display_specifier: []const u8 = ""; @@ -1550,6 +1555,7 @@ pub const ModuleLoader = struct { specifier, display_specifier, referrer_slice.slice(), + specifier_ptr.*, path, loader, &log, @@ -1575,11 +1581,13 @@ pub const ModuleLoader = struct { return promise; } - export fn Bun__runVirtualModule(globalObject: *JSC.JSGlobalObject, specifier_ptr: *const ZigString) JSValue { + export fn Bun__runVirtualModule(globalObject: *JSC.JSGlobalObject, specifier_ptr: *const bun.String) JSValue { JSC.markBinding(@src()); if (globalObject.bunVM().plugin_runner == null) return JSValue.zero; - const specifier = specifier_ptr.slice(); + const specifier_slice = specifier_ptr.toUTF8(bun.default_allocator); + defer specifier_slice.deinit(); + const specifier = specifier_slice.slice(); if (!PluginRunner.couldBePlugin(specifier)) { return JSValue.zero; @@ -1591,11 +1599,11 @@ pub const ModuleLoader = struct { else specifier[@min(namespace.len + 1, specifier.len)..]; - return globalObject.runOnLoadPlugins(ZigString.init(namespace), ZigString.init(after_namespace), .bun) orelse return JSValue.zero; + return globalObject.runOnLoadPlugins(bun.String.init(namespace), bun.String.init(after_namespace), .bun) orelse return JSValue.zero; } - pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: string, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource { - if (jsc_vm.node_modules != null and strings.eqlComptime(specifier, JSC.bun_file_import_path)) { + pub fn fetchBuiltinModule(jsc_vm: *VirtualMachine, specifier: bun.String, log: *logger.Log, comptime disable_transpilying: bool) !?ResolvedSource { + if (jsc_vm.node_modules != null and specifier.eqlComptime(JSC.bun_file_import_path)) { // We kind of need an abstraction around this. // Basically we should subclass JSC::SourceCode with: // - hash @@ -1607,19 +1615,19 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(code), - .specifier = ZigString.init(JSC.bun_file_import_path), + .specifier = bun.String.init(JSC.bun_file_import_path), .source_url = ZigString.init(JSC.bun_file_import_path[1..]), .hash = 0, // TODO }; - } else if (jsc_vm.node_modules == null and strings.eqlComptime(specifier, Runtime.Runtime.Imports.Name)) { + } else if (jsc_vm.node_modules == null and specifier.eqlComptime(Runtime.Runtime.Imports.Name)) { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(Runtime.Runtime.sourceContentBun()), - .specifier = ZigString.init(Runtime.Runtime.Imports.Name), + .specifier = bun.String.init(Runtime.Runtime.Imports.Name), .source_url = ZigString.init(Runtime.Runtime.Imports.Name), .hash = Runtime.Runtime.versionHash(), }; - } else if (HardcodedModule.Map.get(specifier)) |hardcoded| { + } else if (HardcodedModule.Map.getWithEql(specifier, bun.String.eqlComptime)) |hardcoded| { switch (hardcoded) { // This is all complicated because the imports have to be linked and we want to run the printer on it // so it consistently handles bundled imports @@ -1628,8 +1636,8 @@ pub const ModuleLoader = struct { if (comptime disable_transpilying) { return ResolvedSource{ .allocator = null, - .source_code = ZigString.init(jsc_vm.entry_point.source.contents), - .specifier = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), + .source_code = ZigString.fromBytes(jsc_vm.entry_point.source.contents), + .specifier = bun.String.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .hash = 0, }; @@ -1701,24 +1709,24 @@ pub const ModuleLoader = struct { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(jsc_vm.allocator.dupe(u8, printer.ctx.written) catch unreachable), - .specifier = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), + .specifier = specifier, .source_url = ZigString.init(bun.asByteSlice(JSC.VirtualMachine.main_file_name)), .hash = 0, }; }, - .@"node:buffer" => return jsSyntheticModule(.@"node:buffer"), - .@"node:string_decoder" => return jsSyntheticModule(.@"node:string_decoder"), - .@"node:module" => return jsSyntheticModule(.@"node:module"), - .@"node:process" => return jsSyntheticModule(.@"node:process"), - .@"node:tty" => return jsSyntheticModule(.@"node:tty"), - .@"node:util/types" => return jsSyntheticModule(.@"node:util/types"), - .@"bun:events_native" => return jsSyntheticModule(.@"bun:events_native"), + .@"node:buffer" => return jsSyntheticModule(.@"node:buffer", specifier), + .@"node:string_decoder" => return jsSyntheticModule(.@"node:string_decoder", specifier), + .@"node:module" => return jsSyntheticModule(.@"node:module", specifier), + .@"node:process" => return jsSyntheticModule(.@"node:process", specifier), + .@"node:tty" => return jsSyntheticModule(.@"node:tty", specifier), + .@"node:util/types" => return jsSyntheticModule(.@"node:util/types", specifier), + .@"bun:events_native" => return jsSyntheticModule(.@"bun:events_native", specifier), .@"node:fs/promises" => { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(JSC.Node.fs.constants_string ++ @embedFile("../js/out/modules/node/fs.promises.js")), - .specifier = ZigString.init("node:fs/promises"), + .specifier = specifier, .source_url = ZigString.init("node:fs/promises"), .hash = 0, }; @@ -1733,72 +1741,72 @@ pub const ModuleLoader = struct { ";export const suffix='" ++ shared_library_suffix ++ "';" ++ @embedFile("../js/out/modules/bun/ffi.js"), ), - .specifier = ZigString.init("bun:ffi"), + .specifier = specifier, .source_url = ZigString.init("bun:ffi"), .hash = 0, }; }, - .@"bun:jsc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:jsc", "bun/jsc.js"), - .@"bun:sqlite" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:sqlite", "bun/sqlite.js"), - - .@"node:assert" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert", "node/assert.js"), - .@"node:assert/strict" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert/strict", "node/assert.strict.js"), - .@"node:async_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:async_hooks", "node/async_hooks.js"), - .@"node:child_process" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/child_process.js"), - .@"node:crypto" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:crypto", "node/crypto.js"), - .@"node:dns" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns", "node/dns.js"), - .@"node:dns/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns/promises", "node/dns.promises.js"), - .@"node:events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/events.js"), - .@"node:fs" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:fs", "node/fs.js"), - .@"node:http" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http", "node/http.js"), - .@"node:https" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:https", "node/https.js"), - .@"node:net" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:net", "node/net.js"), - .@"node:os" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:os", "node/os.js"), - .@"node:path" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path", "node/path.js"), - .@"node:path/posix" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/posix", "node/path.posix.js"), - .@"node:path/win32" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/win32", "node/path.win32.js"), - .@"node:perf_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:perf_hooks", "node/perf_hooks.js"), - .@"node:readline" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline", "node/readline.js"), - .@"node:readline/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline/promises", "node/readline.promises.js"), - .@"node:stream" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream", "node/stream.js"), - .@"node:stream/consumers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/consumers", "node/stream.consumers.js"), - .@"node:stream/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/promises", "node/stream.promises.js"), - .@"node:stream/web" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/web", "node/stream.web.js"), - .@"node:timers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers", "node/timers.js"), - .@"node:timers/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers/promises", "node/timers.promises.js"), - .@"node:tls" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:tls", "node/tls.js"), - .@"node:url" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:url", "node/url.js"), - .@"node:util" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:util", "node/util.js"), - .@"node:vm" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:vm", "node/vm.js"), - .@"node:wasi" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:wasi", "node/wasi.js"), - .@"node:zlib" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:zlib", "node/zlib.js"), - - .@"detect-libc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, if (Environment.isLinux) "thirdparty/detect-libc.linux.js" else "thirdparty/detect-libc.js"), - .depd => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, "thirdparty/depd.js"), - .undici => return jsResolvedSource(jsc_vm.load_builtins_from_path, .undici, "thirdparty/undici.js"), - .ws => return jsResolvedSource(jsc_vm.load_builtins_from_path, .ws, "thirdparty/ws.js"), - - .@"node:cluster" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:cluster", "node/cluster.js"), - .@"node:dgram" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dgram", "node/dgram.js"), - .@"node:diagnostics_channel" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:diagnostics_channel", "node/diagnostics_channel.js"), - .@"node:http2" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http2", "node/http2.js"), - .@"node:inspector" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:inspector", "node/inspector.js"), - .@"node:repl" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:repl", "node/repl.js"), - .@"node:trace_events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:trace_events", "node/trace_events.js"), - .@"node:v8" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:v8", "node/v8.js"), + .@"bun:jsc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:jsc", "bun/jsc.js", specifier), + .@"bun:sqlite" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"bun:sqlite", "bun/sqlite.js", specifier), + + .@"node:assert" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert", "node/assert.js", specifier), + .@"node:assert/strict" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:assert/strict", "node/assert.strict.js", specifier), + .@"node:async_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:async_hooks", "node/async_hooks.js", specifier), + .@"node:child_process" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/child_process.js", specifier), + .@"node:crypto" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:crypto", "node/crypto.js", specifier), + .@"node:dns" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns", "node/dns.js", specifier), + .@"node:dns/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dns/promises", "node/dns.promises.js", specifier), + .@"node:events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:child_process", "node/events.js", specifier), + .@"node:fs" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:fs", "node/fs.js", specifier), + .@"node:http" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http", "node/http.js", specifier), + .@"node:https" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:https", "node/https.js", specifier), + .@"node:net" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:net", "node/net.js", specifier), + .@"node:os" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:os", "node/os.js", specifier), + .@"node:path" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path", "node/path.js", specifier), + .@"node:path/posix" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/posix", "node/path.posix.js", specifier), + .@"node:path/win32" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:path/win32", "node/path.win32.js", specifier), + .@"node:perf_hooks" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:perf_hooks", "node/perf_hooks.js", specifier), + .@"node:readline" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline", "node/readline.js", specifier), + .@"node:readline/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:readline/promises", "node/readline.promises.js", specifier), + .@"node:stream" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream", "node/stream.js", specifier), + .@"node:stream/consumers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/consumers", "node/stream.consumers.js", specifier), + .@"node:stream/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/promises", "node/stream.promises.js", specifier), + .@"node:stream/web" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:stream/web", "node/stream.web.js", specifier), + .@"node:timers" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers", "node/timers.js", specifier), + .@"node:timers/promises" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:timers/promises", "node/timers.promises.js", specifier), + .@"node:tls" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:tls", "node/tls.js", specifier), + .@"node:url" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:url", "node/url.js", specifier), + .@"node:util" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:util", "node/util.js", specifier), + .@"node:vm" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:vm", "node/vm.js", specifier), + .@"node:wasi" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:wasi", "node/wasi.js", specifier), + .@"node:zlib" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:zlib", "node/zlib.js", specifier), + + .@"detect-libc" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, if (Environment.isLinux) "thirdparty/detect-libc.linux.js" else "thirdparty/detect-libc.js", specifier), + .depd => return jsResolvedSource(jsc_vm.load_builtins_from_path, .depd, "thirdparty/depd.js", specifier), + .undici => return jsResolvedSource(jsc_vm.load_builtins_from_path, .undici, "thirdparty/undici.js", specifier), + .ws => return jsResolvedSource(jsc_vm.load_builtins_from_path, .ws, "thirdparty/ws.js", specifier), + + .@"node:cluster" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:cluster", "node/cluster.js", specifier), + .@"node:dgram" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:dgram", "node/dgram.js", specifier), + .@"node:diagnostics_channel" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:diagnostics_channel", "node/diagnostics_channel.js", specifier), + .@"node:http2" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:http2", "node/http2.js", specifier), + .@"node:inspector" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:inspector", "node/inspector.js", specifier), + .@"node:repl" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:repl", "node/repl.js", specifier), + .@"node:trace_events" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:trace_events", "node/trace_events.js", specifier), + .@"node:v8" => return jsResolvedSource(jsc_vm.load_builtins_from_path, .@"node:v8", "node/v8.js", specifier), } - } else if (strings.hasPrefixComptime(specifier, js_ast.Macro.namespaceWithColon)) { - if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier))) |entry| { + } else if (specifier.hasPrefixComptime(js_ast.Macro.namespaceWithColon)) { + if (jsc_vm.macro_entry_points.get(MacroEntryPoint.generateIDFromSpecifier(specifier.byteSlice()))) |entry| { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(entry.source.contents), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(specifier), + .specifier = specifier, + .source_url = specifier.toZigString(), .hash = 0, }; } - } else if (DisabledModule.has(specifier)) { + } else if (DisabledModule.getWithEql(specifier, bun.String.eqlComptime) != null) { return ResolvedSource{ .allocator = null, .source_code = ZigString.init( @@ -1809,17 +1817,19 @@ pub const ModuleLoader = struct { \\export default masqueradesAsUndefined; \\ ), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(specifier), + .specifier = specifier, + .source_url = specifier.toZigString(), .hash = 0, }; } else if (jsc_vm.standalone_module_graph) |graph| { - if (graph.files.get(specifier)) |file| { + const specifier_utf8 = specifier.toUTF8(bun.default_allocator); + defer specifier_utf8.deinit(); + if (graph.files.get(specifier_utf8.slice())) |file| { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(file.contents), - .specifier = ZigString.init(specifier), - .source_url = ZigString.init(specifier), + .specifier = specifier, + .source_url = specifier.toZigString(), .hash = 0, }; } @@ -1830,8 +1840,8 @@ pub const ModuleLoader = struct { export fn Bun__transpileVirtualModule( globalObject: *JSC.JSGlobalObject, - specifier_ptr: *const ZigString, - referrer_ptr: *const ZigString, + specifier_ptr: *const bun.String, + referrer_ptr: *const bun.String, source_code: *ZigString, loader_: Api.Loader, ret: *ErrorableResolvedSource, @@ -1840,12 +1850,12 @@ pub const ModuleLoader = struct { const jsc_vm = globalObject.bunVM(); std.debug.assert(jsc_vm.plugin_runner != null); - var specifier_slice = specifier_ptr.toSlice(jsc_vm.allocator); + var specifier_slice = specifier_ptr.toUTF8(jsc_vm.allocator); const specifier = specifier_slice.slice(); defer specifier_slice.deinit(); var source_code_slice = source_code.toSlice(jsc_vm.allocator); defer source_code_slice.deinit(); - var referrer_slice = referrer_ptr.toSlice(jsc_vm.allocator); + var referrer_slice = referrer_ptr.toUTF8(jsc_vm.allocator); defer referrer_slice.deinit(); var virtual_source = logger.Source.initPathString(specifier, source_code_slice.slice()); @@ -1867,9 +1877,10 @@ pub const ModuleLoader = struct { ret.* = ErrorableResolvedSource.ok( ModuleLoader.transpileSourceCode( jsc_vm, - specifier, - specifier, + specifier_slice.slice(), + specifier_slice.slice(), referrer_slice.slice(), + specifier_ptr.*, path, options.Loader.fromString(@tagName(loader)).?, &log, @@ -2188,11 +2199,11 @@ pub const DisabledModule = bun.ComptimeStringMap( }, ); -inline fn jsResolvedSource(builtins: []const u8, comptime module: HardcodedModule, comptime input: []const u8) ResolvedSource { +inline fn jsResolvedSource(builtins: []const u8, comptime module: HardcodedModule, comptime input: []const u8, specifier: bun.String) ResolvedSource { return ResolvedSource{ .allocator = null, .source_code = ZigString.init(jsModuleFromFile(builtins, input)), - .specifier = ZigString.init(@tagName(module)), + .specifier = specifier, .source_url = ZigString.init(@tagName(module)), .hash = 0, }; diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index 93edb8abd..62be3fbae 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -3782,6 +3782,8 @@ pub const Expect = struct { } pub fn toMatch(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(@src()); + defer this.postMatch(globalObject); const thisValue = callFrame.this(); @@ -3853,6 +3855,7 @@ pub const Expect = struct { } pub fn toHaveBeenCalled(this: *Expect, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + JSC.markBinding(@src()); const thisValue = callframe.this(); defer this.postMatch(globalObject); @@ -3900,6 +3903,8 @@ pub const Expect = struct { unreachable; } pub fn toHaveBeenCalledTimes(this: *Expect, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) callconv(.C) JSC.JSValue { + JSC.markBinding(@src()); + const thisValue = callframe.this(); const arguments_ = callframe.arguments(1); const arguments: []const JSValue = arguments_.ptr[0..arguments_.len]; @@ -3957,6 +3962,8 @@ pub const Expect = struct { } pub fn toMatchObject(this: *Expect, globalObject: *JSC.JSGlobalObject, callFrame: *JSC.CallFrame) callconv(.C) JSValue { + JSC.markBinding(@src()); + defer this.postMatch(globalObject); const thisValue = callFrame.this(); const args = callFrame.arguments(1).slice(); diff --git a/src/bun.js/webcore/blob.zig b/src/bun.js/webcore/blob.zig index a5d3c968d..13b086541 100644 --- a/src/bun.js/webcore/blob.zig +++ b/src/bun.js/webcore/blob.zig @@ -447,7 +447,7 @@ pub const Blob = struct { bun.default_allocator.destroy(this); promise.reject(globalThis, ZigString.init("Body was used after it was consumed").toErrorInstance(globalThis)); }, - // .InlineBlob, + .WTFStringImpl, .InternalBlob, .Null, .Empty, @@ -684,7 +684,7 @@ pub const Blob = struct { var source_blob: Blob = brk: { if (data.as(Response)) |response| { switch (response.body.value) { - // .InlineBlob, + .WTFStringImpl, .InternalBlob, .Used, .Empty, @@ -719,7 +719,7 @@ pub const Blob = struct { if (data.as(Request)) |request| { switch (request.body.value) { - // .InlineBlob, + .WTFStringImpl, .InternalBlob, .Used, .Empty, @@ -3512,10 +3512,20 @@ pub const AnyBlob = union(enum) { Blob: Blob, // InlineBlob: InlineBlob, InternalBlob: InternalBlob, + WTFStringImpl: bun.WTF.StringImpl, + + pub inline fn fastSize(this: *const AnyBlob) Blob.SizeType { + return switch (this.*) { + .Blob => this.Blob.size, + .WTFStringImpl => @truncate(Blob.SizeType, this.WTFStringImpl.byteLength()), + else => @truncate(Blob.SizeType, this.slice().len), + }; + } pub fn hasContentTypeFromUser(this: AnyBlob) bool { return switch (this) { .Blob => this.Blob.hasContentTypeFromUser(), + .WTFStringImpl => false, .InternalBlob => false, }; } @@ -3544,6 +3554,19 @@ pub const AnyBlob = union(enum) { return str; }, + .WTFStringImpl => { + var str = bun.String.init(this.WTFStringImpl); + defer str.deref(); + this.* = .{ + .Blob = .{}, + }; + + if (str.length() == 0) { + return JSValue.jsNull(); + } + + return str.toJS(global).parseJSON(global); + }, } } @@ -3567,6 +3590,13 @@ pub const AnyBlob = union(enum) { this.* = .{ .Blob = .{} }; return owned; }, + .WTFStringImpl => { + var str = bun.String.init(this.WTFStringImpl); + defer str.deref(); + this.* = .{ .Blob = .{} }; + + return str.toJS(global); + }, } } @@ -3599,12 +3629,29 @@ pub const AnyBlob = union(enum) { ); return value.toJS(global, null); }, + .WTFStringImpl => { + const str = bun.String.init(this.WTFStringImpl); + this.* = .{ .Blob = .{} }; + defer str.deref(); + + const out_bytes = str.toUTF8(bun.default_allocator); + if (out_bytes.isAllocated()) { + const value = JSC.ArrayBuffer.fromBytes( + @constCast(out_bytes.slice()), + .ArrayBuffer, + ); + return value.toJS(global, null); + } + + return JSC.ArrayBuffer.create(global, out_bytes.slice(), .ArrayBuffer); + }, } } pub inline fn size(this: *const AnyBlob) Blob.SizeType { return switch (this.*) { .Blob => this.Blob.size, + .WTFStringImpl => @truncate(Blob.SizeType, this.WTFStringImpl.utf8ByteLength()), else => @truncate(Blob.SizeType, this.slice().len), }; } @@ -3635,6 +3682,7 @@ pub const AnyBlob = union(enum) { pub fn contentType(self: *const @This()) []const u8 { return switch (self.*) { .Blob => self.Blob.content_type, + .WTFStringImpl => MimeType.text.value, // .InlineBlob => self.InlineBlob.contentType(), .InternalBlob => self.InternalBlob.contentType(), }; @@ -3643,6 +3691,7 @@ pub const AnyBlob = union(enum) { pub fn wasString(self: *const @This()) bool { return switch (self.*) { .Blob => self.Blob.is_all_ascii orelse false, + .WTFStringImpl => true, // .InlineBlob => self.InlineBlob.was_string, .InternalBlob => self.InternalBlob.was_string, }; @@ -3651,6 +3700,7 @@ pub const AnyBlob = union(enum) { pub inline fn slice(self: *const @This()) []const u8 { return switch (self.*) { .Blob => self.Blob.sharedView(), + .WTFStringImpl => self.WTFStringImpl.utf8Slice(), // .InlineBlob => self.InlineBlob.sliceConst(), .InternalBlob => self.InternalBlob.sliceConst(), }; @@ -3659,8 +3709,7 @@ pub const AnyBlob = union(enum) { pub fn needsToReadFile(self: *const @This()) bool { return switch (self.*) { .Blob => self.Blob.needsToReadFile(), - // .InlineBlob => false, - .InternalBlob => false, + .WTFStringImpl, .InternalBlob => false, }; } @@ -3681,6 +3730,12 @@ pub const AnyBlob = union(enum) { .Blob = .{}, }; }, + .WTFStringImpl => { + self.WTFStringImpl.deref(); + self.* = .{ + .Blob = .{}, + }; + }, }; } }; diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 9cfa1f9ce..df3ba3ce1 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -99,8 +99,7 @@ pub const Body = struct { try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); try this.value.Blob.writeFormat(Formatter, formatter, writer, enable_ansi_colors); - } else if (this.value == .InternalBlob) { - // } else if (this.value == .InternalBlob or this.value == .InlineBlob) { + } else if (this.value == .InternalBlob or this.value == .WTFStringImpl) { try formatter.printComma(Writer, writer, enable_ansi_colors); try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); @@ -293,6 +292,38 @@ pub const Body = struct { pub const Value = union(Tag) { const log = Output.scoped(.BodyValue, false); Blob: Blob, + + /// This is the String type from WebKit + /// It is reference counted, so we must always deref it (which this does automatically) + /// Be careful where it can directly be used. + /// + /// If it is a latin1 string with only ascii, we can use it directly. + /// Otherwise, we must convert it to utf8. + /// + /// Unless we are sending it directly to JavaScript, for example: + /// + /// var str = "hello world ðŸ¤" + /// var response = new Response(str); + /// /* Body.Value stays WTFStringImpl */ + /// var body = await response.text(); + /// + /// In this case, even though there's an emoji, we can use the StringImpl directly. + /// BUT, if we were instead using it in the HTTP server, this cannot be used directly. + /// + /// When the server calls .toBlobIfPossible(), we will automatically + /// convert this Value to an InternalBlob + /// + /// Example code: + /// + /// Bun.serve({ + /// fetch(req) { + /// /* Body.Value becomes InternalBlob */ + /// return new Response("hello world ðŸ¤"); + /// } + /// }) + /// + /// This works for .json(), too. + WTFStringImpl: bun.WTF.StringImpl, /// Single-use Blob /// Avoids a heap allocation. InternalBlob: InternalBlob, @@ -305,6 +336,19 @@ pub const Body = struct { Null: void, pub fn toBlobIfPossible(this: *Value) void { + if (this.* == .WTFStringImpl) { + if (this.WTFStringImpl.toUTF8IfNeeded(bun.default_allocator)) |bytes| { + var str = this.WTFStringImpl; + defer str.deref(); + this.* = .{ + .InternalBlob = InternalBlob{ + .bytes = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, @constCast(bytes.slice())), + .was_string = true, + }, + }; + } + } + if (this.* != .Locked) return; @@ -312,6 +356,7 @@ pub const Body = struct { this.* = switch (blob) { .Blob => .{ .Blob = blob.Blob }, .InternalBlob => .{ .InternalBlob = blob.InternalBlob }, + .WTFStringImpl => .{ .WTFStringImpl = blob.WTFStringImpl }, // .InlineBlob => .{ .InlineBlob = blob.InlineBlob }, }; } @@ -321,6 +366,17 @@ pub const Body = struct { return switch (this.*) { .Blob => this.Blob.size, .InternalBlob => @truncate(Blob.SizeType, this.InternalBlob.sliceConst().len), + .WTFStringImpl => @truncate(Blob.SizeType, this.WTFStringImpl.utf8ByteLength()), + // .InlineBlob => @truncate(Blob.SizeType, this.InlineBlob.sliceConst().len), + else => 0, + }; + } + + pub fn fastSize(this: *const Value) Blob.SizeType { + return switch (this.*) { + .Blob => this.Blob.size, + .InternalBlob => @truncate(Blob.SizeType, this.InternalBlob.sliceConst().len), + .WTFStringImpl => @truncate(Blob.SizeType, this.WTFStringImpl.byteSlice().len), // .InlineBlob => @truncate(Blob.SizeType, this.InlineBlob.sliceConst().len), else => 0, }; @@ -329,6 +385,7 @@ pub const Body = struct { pub fn estimatedSize(this: *const Value) usize { return switch (this.*) { .InternalBlob => this.InternalBlob.sliceConst().len, + .WTFStringImpl => this.WTFStringImpl.byteSlice().len, // .InlineBlob => this.InlineBlob.sliceConst().len, else => 0, }; @@ -359,6 +416,7 @@ pub const Body = struct { pub const Tag = enum { Blob, InternalBlob, + WTFStringImpl, // InlineBlob, Locked, Used, @@ -379,10 +437,7 @@ pub const Body = struct { .Null => { return JSValue.null; }, - .InternalBlob, - .Blob, - // .InlineBlob, - => { + .InternalBlob, .Blob, .WTFStringImpl => { var blob = this.use(); defer blob.detach(); blob.resolveSize(); @@ -461,76 +516,18 @@ pub const Body = struct { const js_type = value.jsType(); if (js_type.isStringLike()) { - var str = value.getZigString(globalThis); - if (str.len == 0) { + var str = value.toBunString(globalThis); + if (str.length() == 0) { return Body.Value{ .Empty = {}, }; } - // if (str.is16Bit()) { - // if (str.maxUTF8ByteLength() < InlineBlob.available_bytes or - // (str.len <= InlineBlob.available_bytes and str.utf8ByteLength() <= InlineBlob.available_bytes)) - // { - // var blob = InlineBlob{ - // .was_string = true, - // .bytes = undefined, - // .len = 0, - // }; - // if (comptime Environment.allow_assert) { - // std.debug.assert(str.utf8ByteLength() <= InlineBlob.available_bytes); - // } - - // const result = strings.copyUTF16IntoUTF8( - // blob.bytes[0..blob.bytes.len], - // []const u16, - // str.utf16SliceAligned(), - // true, - // ); - // blob.len = @intCast(InlineBlob.IntSize, result.written); - // std.debug.assert(@as(usize, result.read) == str.len); - // std.debug.assert(@as(usize, result.written) <= InlineBlob.available_bytes); - - // return Body.Value{ - // .InlineBlob = blob, - // }; - // } - // } else { - // if (str.maxUTF8ByteLength() <= InlineBlob.available_bytes or - // (str.len <= InlineBlob.available_bytes and str.utf8ByteLength() <= InlineBlob.available_bytes)) - // { - // var blob = InlineBlob{ - // .was_string = true, - // .bytes = undefined, - // .len = 0, - // }; - // if (comptime Environment.allow_assert) { - // std.debug.assert(str.utf8ByteLength() <= InlineBlob.available_bytes); - // } - // const result = strings.copyLatin1IntoUTF8( - // blob.bytes[0..blob.bytes.len], - // []const u8, - // str.slice(), - // ); - // blob.len = @intCast(InlineBlob.IntSize, result.written); - // std.debug.assert(@as(usize, result.read) == str.len); - // std.debug.assert(@as(usize, result.written) <= InlineBlob.available_bytes); - // return Body.Value{ - // .InlineBlob = blob, - // }; - // } - // } - - var buffer = str.toOwnedSlice(bun.default_allocator) catch { - globalThis.vm().throwError(globalThis, ZigString.static("Failed to clone string").toErrorInstance(globalThis)); - return null; - }; + str.ref(); + std.debug.assert(str.tag == .WTFStringImpl); return Body.Value{ - .InternalBlob = .{ - .bytes = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, buffer), - .was_string = true, - }, + .WTFStringImpl = str.value.WTFStringImpl, }; } @@ -659,10 +656,11 @@ pub const Body = struct { switch (locked.action) { .getText => { switch (new.*) { + .WTFStringImpl, .InternalBlob, // .InlineBlob, => { - var blob = new.useAsAnyBlob(); + var blob = new.useAsAnyBlobAllowNonUTF8String(); promise.resolve(global, blob.toString(global, .transfer)); }, else => { @@ -672,7 +670,7 @@ pub const Body = struct { } }, .getJSON => { - var blob = new.useAsAnyBlob(); + var blob = new.useAsAnyBlobAllowNonUTF8String(); const json_value = blob.toJSON(global, .share); blob.detach(); @@ -683,7 +681,8 @@ pub const Body = struct { } }, .getArrayBuffer => { - var blob = new.useAsAnyBlob(); + var blob = new.useAsAnyBlobAllowNonUTF8String(); + // toArrayBuffer checks for non-UTF8 strings promise.resolve(global, blob.toArrayBuffer(global, .transfer)); }, .getFormData => inner: { @@ -711,6 +710,7 @@ pub const Body = struct { return switch (this.*) { .Blob => this.Blob.sharedView(), .InternalBlob => this.InternalBlob.sliceConst(), + .WTFStringImpl => if (this.WTFStringImpl.canUseAsUTF8()) this.WTFStringImpl.latin1Slice() else "", // .InlineBlob => this.InlineBlob.sliceConst(), else => "", }; @@ -739,6 +739,27 @@ pub const Body = struct { this.* = .{ .Used = {} }; return new_blob; }, + .WTFStringImpl => { + var new_blob: Blob = undefined; + var wtf = this.WTFStringImpl; + defer wtf.deref(); + if (wtf.toUTF8IfNeeded(bun.default_allocator)) |allocated_slice| { + new_blob = Blob.init( + @constCast(allocated_slice.slice()), + bun.default_allocator, + JSC.VirtualMachine.get().global, + ); + } else { + new_blob = Blob.init( + bun.default_allocator.dupe(u8, wtf.latin1Slice()) catch @panic("Out of memory"), + bun.default_allocator, + JSC.VirtualMachine.get().global, + ); + } + + this.* = .{ .Used = {} }; + return new_blob; + }, // .InlineBlob => { // const cloned = this.InlineBlob.bytes; // // keep same behavior as InternalBlob but clone the data @@ -759,6 +780,12 @@ pub const Body = struct { } pub fn tryUseAsAnyBlob(this: *Value) ?AnyBlob { + if (this.* == .WTFStringImpl) { + if (this.WTFStringImpl.canUseAsUTF8()) { + return AnyBlob{ .WTFStringImpl = this.WTFStringImpl }; + } + } + const any_blob: AnyBlob = switch (this.*) { .Blob => AnyBlob{ .Blob = this.Blob }, .InternalBlob => AnyBlob{ .InternalBlob = this.InternalBlob }, @@ -775,6 +802,38 @@ pub const Body = struct { const any_blob: AnyBlob = switch (this.*) { .Blob => .{ .Blob = this.Blob }, .InternalBlob => .{ .InternalBlob = this.InternalBlob }, + .WTFStringImpl => |str| brk: { + if (str.toUTF8IfNeeded(bun.default_allocator)) |utf8| { + defer str.deref(); + break :brk .{ + .InternalBlob = InternalBlob{ + .bytes = std.ArrayList(u8).fromOwnedSlice(bun.default_allocator, @constCast(utf8.slice())), + .was_string = true, + }, + }; + } else { + break :brk .{ + .WTFStringImpl = str, + }; + } + }, + // .InlineBlob => .{ .InlineBlob = this.InlineBlob }, + .Locked => this.Locked.toAnyBlobAllowPromise() orelse AnyBlob{ .Blob = .{} }, + else => .{ .Blob = Blob.initEmpty(undefined) }, + }; + + this.* = if (this.* == .Null) + .{ .Null = {} } + else + .{ .Used = {} }; + return any_blob; + } + + pub fn useAsAnyBlobAllowNonUTF8String(this: *Value) AnyBlob { + const any_blob: AnyBlob = switch (this.*) { + .Blob => .{ .Blob = this.Blob }, + .InternalBlob => .{ .InternalBlob = this.InternalBlob }, + .WTFStringImpl => .{ .WTFStringImpl = this.WTFStringImpl }, // .InlineBlob => .{ .InlineBlob = this.InlineBlob }, .Locked => this.Locked.toAnyBlobAllowPromise() orelse AnyBlob{ .Blob = .{} }, else => .{ .Blob = Blob.initEmpty(undefined) }, @@ -856,6 +915,11 @@ pub const Body = struct { this.* = Value{ .Null = {} }; } + if (tag == .WTFStringImpl) { + this.WTFStringImpl.deref(); + this.* = Value{ .Null = {} }; + } + if (tag == .Error) { JSC.C.JSValueUnprotect(VirtualMachine.get().global, this.Error.asObjectRef()); } @@ -880,6 +944,11 @@ pub const Body = struct { return Value{ .Blob = this.Blob.dupe() }; } + if (this.* == .WTFStringImpl) { + this.WTFStringImpl.ref(); + return Value{ .WTFStringImpl = this.WTFStringImpl }; + } + if (this.* == .Null) { return Value{ .Null = {} }; } @@ -983,7 +1052,7 @@ pub fn BodyMixin(comptime Type: type) type { return value.Locked.setPromise(globalObject, .{ .getText = {} }); } - var blob = value.useAsAnyBlob(); + var blob = value.useAsAnyBlobAllowNonUTF8String(); return JSC.JSPromise.wrap(globalObject, blob.toString(globalObject, .transfer)); } @@ -1025,7 +1094,7 @@ pub fn BodyMixin(comptime Type: type) type { return value.Locked.setPromise(globalObject, .{ .getJSON = {} }); } - var blob = value.useAsAnyBlob(); + var blob = value.useAsAnyBlobAllowNonUTF8String(); return JSC.JSPromise.wrap(globalObject, blob.toJSON(globalObject, .share)); } @@ -1054,7 +1123,8 @@ pub fn BodyMixin(comptime Type: type) type { return value.Locked.setPromise(globalObject, .{ .getArrayBuffer = {} }); } - var blob: AnyBlob = value.useAsAnyBlob(); + // toArrayBuffer in AnyBlob checks for non-UTF8 strings + var blob: AnyBlob = value.useAsAnyBlobAllowNonUTF8String(); return JSC.JSPromise.wrap(globalObject, blob.toArrayBuffer(globalObject, .transfer)); } diff --git a/src/bun.js/webcore/request.zig b/src/bun.js/webcore/request.zig index 643fbb38b..a8c648212 100644 --- a/src/bun.js/webcore/request.zig +++ b/src/bun.js/webcore/request.zig @@ -127,7 +127,7 @@ pub const Request = struct { pub fn writeFormat(this: *Request, comptime Formatter: type, formatter: *Formatter, writer: anytype, comptime enable_ansi_colors: bool) !void { const Writer = @TypeOf(writer); - try writer.print("Request ({}) {{\n", .{bun.fmt.size(this.body.value.slice().len)}); + try writer.print("Request ({}) {{\n", .{bun.fmt.size(this.body.value.size())}); { formatter.indent += 1; defer formatter.indent -|= 1; @@ -155,13 +155,14 @@ pub const Request = struct { try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); try this.body.value.Blob.writeFormat(Formatter, formatter, writer, enable_ansi_colors); - } else if (this.body.value == .InternalBlob) { + } else if (this.body.value == .InternalBlob or this.body.value == .WTFStringImpl) { try writer.writeAll("\n"); try formatter.writeIndent(Writer, writer); - if (this.body.value.size() == 0) { + const size = this.body.value.size(); + if (size == 0) { try Blob.initEmpty(undefined).writeFormat(Formatter, formatter, writer, enable_ansi_colors); } else { - try Blob.writeFormatForSize(this.body.value.size(), writer, enable_ansi_colors); + try Blob.writeFormatForSize(size, writer, enable_ansi_colors); } } else if (this.body.value == .Locked) { if (this.body.value.Locked.readable) |stream| { @@ -203,6 +204,7 @@ pub const Request = struct { return MimeType.other.value; }, .InternalBlob => return this.body.value.InternalBlob.contentType(), + .WTFStringImpl => return MimeType.text.value, // .InlineBlob => return this.body.value.InlineBlob.contentType(), .Null, .Error, .Used, .Locked, .Empty => return MimeType.other.value, } diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index bfbc83384..beae4d182 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -328,14 +328,13 @@ pub const Response = struct { return default.value; }, - // .InlineBlob => { - // // auto-detect HTML if unspecified - // if (strings.hasPrefixComptime(response.body.value.slice(), "<!DOCTYPE html>")) { - // return MimeType.html.value; - // } - - // return response.body.value.InlineBlob.contentType(); - // }, + .WTFStringImpl => |str| { + if (bun.String.init(str).hasPrefixComptime("<!DOCTYPE html>")) { + return MimeType.html.value; + } + + return default.value; + }, .InternalBlob => { // auto-detect HTML if unspecified if (strings.hasPrefixComptime(response.body.value.slice(), "<!DOCTYPE html>")) { diff --git a/src/bun.zig b/src/bun.zig index 72a2ab8b7..9ca01f2b8 100644 --- a/src/bun.zig +++ b/src/bun.zig @@ -1523,3 +1523,10 @@ pub const Generation = u16; pub const zstd = @import("./deps/zstd.zig"); pub const StringPointer = Schema.Api.StringPointer; pub const StandaloneModuleGraph = @import("./standalone_bun.zig").StandaloneModuleGraph; + +pub const String = @import("./string.zig").String; + +pub const WTF = struct { + /// The String type from WebKit's WTF library. + pub const StringImpl = @import("./string.zig").WTFStringImpl; +}; diff --git a/src/bundler.zig b/src/bundler.zig index c2cec56c1..9a2ae2355 100644 --- a/src/bundler.zig +++ b/src/bundler.zig @@ -176,13 +176,13 @@ pub const PluginRunner = struct { var global = this.global_object; const namespace_slice = extractNamespace(specifier); const namespace = if (namespace_slice.len > 0 and !strings.eqlComptime(namespace_slice, "file")) - JSC.ZigString.init(namespace_slice) + bun.String.init(namespace_slice) else - JSC.ZigString.init(""); + bun.String.empty; const on_resolve_plugin = global.runOnResolvePlugins( namespace, - JSC.ZigString.init(specifier).substring(if (namespace.len > 0) namespace.len + 1 else 0, 0), - JSC.ZigString.init(importer), + bun.String.init(specifier).substring(if (namespace.length() > 0) namespace.length() + 1 else 0), + bun.String.init(importer), target, ) orelse return null; const path_value = on_resolve_plugin.get(global, "path") orelse return null; @@ -216,28 +216,28 @@ pub const PluginRunner = struct { return null; } var static_namespace = true; - const user_namespace: JSC.ZigString = brk: { + const user_namespace: bun.String = brk: { if (on_resolve_plugin.get(global, "namespace")) |namespace_value| { if (!namespace_value.isString()) { log.addError(null, loc, "Expected \"namespace\" to be a string") catch unreachable; return null; } - const namespace_str = namespace_value.getZigString(global); - if (namespace_str.len == 0) { - break :brk JSC.ZigString.init("file"); + const namespace_str = namespace_value.toBunString(global); + if (namespace_str.length() == 0) { + break :brk bun.String.init("file"); } if (namespace_str.eqlComptime("file")) { - break :brk JSC.ZigString.init("file"); + break :brk bun.String.init("file"); } if (namespace_str.eqlComptime("bun")) { - break :brk JSC.ZigString.init("bun"); + break :brk bun.String.init("bun"); } if (namespace_str.eqlComptime("node")) { - break :brk JSC.ZigString.init("node"); + break :brk bun.String.init("node"); } static_namespace = false; @@ -245,13 +245,13 @@ pub const PluginRunner = struct { break :brk namespace_str; } - break :brk JSC.ZigString.init("file"); + break :brk bun.String.init("file"); }; if (static_namespace) { return Fs.Path.initWithNamespace( std.fmt.allocPrint(this.allocator, "{any}", .{file_path}) catch unreachable, - user_namespace.slice(), + user_namespace.byteSlice(), ); } else { return Fs.Path.initWithNamespace( @@ -263,17 +263,17 @@ pub const PluginRunner = struct { pub fn onResolveJSC( this: *const PluginRunner, - namespace: JSC.ZigString, - specifier: JSC.ZigString, - importer: JSC.ZigString, + namespace: bun.String, + specifier: bun.String, + importer: bun.String, target: JSC.JSGlobalObject.BunPluginTarget, - ) ?JSC.ErrorableZigString { + ) ?JSC.ErrorableString { var global = this.global_object; const on_resolve_plugin = global.runOnResolvePlugins( - if (namespace.len > 0 and !namespace.eqlComptime("file")) + if (namespace.length() > 0 and !namespace.eqlComptime("file")) namespace else - JSC.ZigString.init(""), + bun.String.static(""), specifier, importer, target, @@ -281,18 +281,18 @@ pub const PluginRunner = struct { const path_value = on_resolve_plugin.get(global, "path") orelse return null; if (path_value.isEmptyOrUndefinedOrNull()) return null; if (!path_value.isString()) { - return JSC.ErrorableZigString.err( + return JSC.ErrorableString.err( error.JSErrorObject, - JSC.ZigString.init("Expected \"path\" to be a string in onResolve plugin").toErrorInstance(this.global_object).asVoid(), + bun.String.static("Expected \"path\" to be a string in onResolve plugin").toErrorInstance(this.global_object).asVoid(), ); } - const file_path = path_value.getZigString(global); + const file_path = path_value.toBunString(global); - if (file_path.len == 0) { - return JSC.ErrorableZigString.err( + if (file_path.length() == 0) { + return JSC.ErrorableString.err( error.JSErrorObject, - JSC.ZigString.init("Expected \"path\" to be a non-empty string in onResolve plugin").toErrorInstance(this.global_object).asVoid(), + bun.String.static("Expected \"path\" to be a non-empty string in onResolve plugin").toErrorInstance(this.global_object).asVoid(), ); } else if // TODO: validate this better @@ -301,36 +301,36 @@ pub const PluginRunner = struct { file_path.eqlComptime("...") or file_path.eqlComptime(" ")) { - return JSC.ErrorableZigString.err( + return JSC.ErrorableString.err( error.JSErrorObject, - JSC.ZigString.init("\"path\" is invalid in onResolve plugin").toErrorInstance(this.global_object).asVoid(), + bun.String.static("\"path\" is invalid in onResolve plugin").toErrorInstance(this.global_object).asVoid(), ); } var static_namespace = true; - const user_namespace: JSC.ZigString = brk: { + const user_namespace: bun.String = brk: { if (on_resolve_plugin.get(global, "namespace")) |namespace_value| { if (!namespace_value.isString()) { - return JSC.ErrorableZigString.err( + return JSC.ErrorableString.err( error.JSErrorObject, - JSC.ZigString.init("Expected \"namespace\" to be a string").toErrorInstance(this.global_object).asVoid(), + bun.String.static("Expected \"namespace\" to be a string").toErrorInstance(this.global_object).asVoid(), ); } - const namespace_str = namespace_value.getZigString(global); - if (namespace_str.len == 0) { - break :brk JSC.ZigString.init("file"); + const namespace_str = namespace_value.toBunString(global); + if (namespace_str.length() == 0) { + break :brk bun.String.static("file"); } if (namespace_str.eqlComptime("file")) { - break :brk JSC.ZigString.init("file"); + break :brk bun.String.static("file"); } if (namespace_str.eqlComptime("bun")) { - break :brk JSC.ZigString.init("bun"); + break :brk bun.String.static("bun"); } if (namespace_str.eqlComptime("node")) { - break :brk JSC.ZigString.init("node"); + break :brk bun.String.static("node"); } static_namespace = false; @@ -338,7 +338,7 @@ pub const PluginRunner = struct { break :brk namespace_str; } - break :brk JSC.ZigString.init("file"); + break :brk bun.String.static("file"); }; // Our super slow way of cloning the string into memory owned by JSC @@ -347,9 +347,10 @@ pub const PluginRunner = struct { "{any}:{any}", .{ user_namespace, file_path }, ) catch unreachable; - const out = JSC.ZigString.init(combined_string).toValueGC(this.global_object).getZigString(this.global_object); + var out_ = bun.String.init(combined_string); + const out = out_.toJS(this.global_object).toBunString(this.global_object); this.allocator.free(combined_string); - return JSC.ErrorableZigString.ok(out); + return JSC.ErrorableString.ok(out); } }; diff --git a/src/comptime_string_map.zig b/src/comptime_string_map.zig index 1963fc8de..01b5de5b0 100644 --- a/src/comptime_string_map.zig +++ b/src/comptime_string_map.zig @@ -146,12 +146,14 @@ pub fn ComptimeStringMapWithKeyType(comptime KeyType: type, comptime V: type, co } pub fn getWithEql(input: anytype, comptime eql: anytype) ?V { - if (input.len < precomputed.min_len or input.len > precomputed.max_len) + const Input = @TypeOf(input); + const length = if (comptime std.meta.trait.isSlice(Input) or std.meta.trait.isZigString(Input)) input.len else input.length(); + if (length < precomputed.min_len or length > precomputed.max_len) return null; comptime var i: usize = precomputed.min_len; inline while (i <= precomputed.max_len) : (i += 1) { - if (input.len == i) { + if (length == i) { return getWithLengthAndEql(input, i, eql); } } diff --git a/src/js_printer.zig b/src/js_printer.zig index 47d31f3ed..1613500a1 100644 --- a/src/js_printer.zig +++ b/src/js_printer.zig @@ -4757,11 +4757,11 @@ fn NewPrinter( p.print(quote); p.print(import_record.path.namespace); p.print(":"); - p.print(import_record.path.text); + p.printIdentifier(import_record.path.text); p.print(quote); } else { p.print(quote); - p.print(import_record.path.text); + p.printIdentifier(import_record.path.text); p.print(quote); } } diff --git a/src/string.zig b/src/string.zig new file mode 100644 index 000000000..cfa50f792 --- /dev/null +++ b/src/string.zig @@ -0,0 +1,468 @@ +const std = @import("std"); +const bun = @import("root").bun; +const JSC = bun.JSC; +const JSValue = bun.JSC.JSValue; +const Parent = @This(); + +pub const BufferOwnership = enum { + BufferInternal, + BufferOwned, + BufferSubstring, + BufferExternal, +}; + +pub const WTFStringImpl = *WTFStringImplStruct; + +pub const WTFStringImplStruct = extern struct { + m_refCount: u32 = 0, + m_length: u32 = 0, + m_ptr: extern union { latin1: [*]const u8, utf16: [*]const u16 }, + m_hashAndFlags: u32 = 0, + + // --------------------------------------------------------------------- + // These details must stay in sync with WTFStringImpl.h in WebKit! + // --------------------------------------------------------------------- + const s_flagCount: u32 = 8; + + const s_flagMask: u32 = (1 << s_flagCount) - 1; + const s_flagStringKindCount: u32 = 4; + const s_hashZeroValue: u32 = 0; + const s_hashFlagStringKindIsAtom: u32 = @as(1, u32) << (s_flagStringKindCount); + const s_hashFlagStringKindIsSymbol: u32 = @as(1, u32) << (s_flagStringKindCount + 1); + const s_hashMaskStringKind: u32 = s_hashFlagStringKindIsAtom | s_hashFlagStringKindIsSymbol; + const s_hashFlagDidReportCost: u32 = @as(1, u32) << 3; + const s_hashFlag8BitBuffer: u32 = 1 << 2; + const s_hashMaskBufferOwnership: u32 = (1 << 0) | (1 << 1); + + /// The bottom bit in the ref count indicates a static (immortal) string. + const s_refCountFlagIsStaticString = 0x1; + + /// This allows us to ref / deref without disturbing the static string flag. + const s_refCountIncrement = 0x2; + + // --------------------------------------------------------------------- + + pub fn refCount(this: WTFStringImpl) u32 { + return this.m_refCount / s_refCountIncrement; + } + + pub fn isStatic(this: WTFStringImpl) bool { + return this.m_refCount & s_refCountIncrement != 0; + } + + pub fn byteLength(this: WTFStringImpl) usize { + return if (this.is8Bit()) this.m_length else this.m_length * 2; + } + + pub fn byteSlice(this: WTFStringImpl) []const u8 { + return this.m_ptr.latin1[0..this.byteLength()]; + } + + pub inline fn is8Bit(self: WTFStringImpl) bool { + return (self.m_hashAndFlags & s_hashFlag8BitBuffer) != 0; + } + + pub inline fn length(self: WTFStringImpl) u32 { + return self.m_length; + } + + pub inline fn utf16Slice(self: WTFStringImpl) []const u16 { + std.debug.assert(!is8Bit(self)); + return self.m_ptr.utf16[0..length(self)]; + } + + pub inline fn latin1Slice(self: WTFStringImpl) []const u8 { + std.debug.assert(is8Bit(self)); + return self.m_ptr.latin1[0..length(self)]; + } + + /// Caller must ensure that the string is 8-bit and ASCII. + pub inline fn utf8Slice(self: WTFStringImpl) []const u8 { + if (comptime bun.Environment.allow_assert) + std.debug.assert(canUseAsUTF8(self)); + return self.m_ptr.latin1[0..length(self)]; + } + + pub fn toZigString(this: WTFStringImpl) ZigString { + if (this.is8Bit()) { + return ZigString.init(this.latin1Slice()); + } else { + return ZigString.init16(this.utf16Slice()); + } + } + + pub inline fn deref(self: WTFStringImpl) void { + JSC.markBinding(@src()); + const current_count = self.refCount(); + std.debug.assert(current_count > 0); + Bun__WTFStringImpl__deref(self); + if (comptime bun.Environment.allow_assert) { + if (current_count > 1) { + std.debug.assert(self.refCount() < current_count or self.isStatic()); + } + } + } + + pub inline fn ref(self: WTFStringImpl) void { + JSC.markBinding(@src()); + const current_count = self.refCount(); + std.debug.assert(current_count > 0); + Bun__WTFStringImpl__ref(self); + std.debug.assert(self.refCount() > current_count or self.isStatic()); + } + + pub fn toUTF8(this: WTFStringImpl, allocator: std.mem.Allocator) ZigString.Slice { + if (this.is8Bit()) { + if (bun.strings.toUTF8FromLatin1(allocator, this.latin1Slice()) catch null) |utf8| { + return ZigString.Slice.init(allocator, utf8.items); + } + + this.ref(); + return ZigString.Slice.init(this.refCountAllocator(), this.latin1Slice()); + } + + if (bun.strings.toUTF8Alloc(allocator, this.utf16Slice()) catch null) |utf8| { + return ZigString.Slice.init(allocator, utf8); + } + + return .{}; + } + + pub fn toUTF8IfNeeded(this: WTFStringImpl, allocator: std.mem.Allocator) ?ZigString.Slice { + if (this.is8Bit()) { + if (bun.strings.toUTF8FromLatin1(allocator, this.latin1Slice()) catch null) |utf8| { + return ZigString.Slice.init(allocator, utf8.items); + } + + return null; + } + + if (bun.strings.toUTF8Alloc(allocator, this.utf16Slice()) catch null) |utf8| { + return ZigString.Slice.init(allocator, utf8); + } + + return null; + } + + /// Avoid using this in code paths that are about to get the string as a UTF-8 + /// In that case, use toUTF8IfNeeded instead. + pub fn canUseAsUTF8(this: WTFStringImpl) bool { + return this.is8Bit() and bun.strings.isAllASCII(this.latin1Slice()); + } + + pub fn utf8ByteLength(this: WTFStringImpl) usize { + if (this.is8Bit()) { + const input = this.latin1Slice(); + return if (input.len > 0) JSC.WebCore.Encoder.byteLengthU8(input.ptr, input.len, .utf8) else 0; + } else { + const input = this.utf16Slice(); + return if (input.len > 0) JSC.WebCore.Encoder.byteLengthU16(input.ptr, input.len, .utf8) else 0; + } + } + + pub fn refCountAllocator(self: WTFStringImpl) std.mem.Allocator { + return std.mem.Allocator{ .ptr = self, .vtable = StringImplAllocator.VTablePtr }; + } + + extern fn Bun__WTFStringImpl__deref(self: WTFStringImpl) void; + extern fn Bun__WTFStringImpl__ref(self: WTFStringImpl) void; +}; + +pub const StringImplAllocator = struct { + fn alloc(ptr: *anyopaque, len: usize, _: u8, _: usize) ?[*]u8 { + var this = bun.cast(WTFStringImpl, ptr); + const len_ = this.byteLength(); + + if (len_ != len) { + // we don't actually allocate, we just reference count + return null; + } + + this.ref(); + + // we should never actually allocate + return @constCast(this.m_ptr.latin1); + } + + fn resize(_: *anyopaque, _: []u8, _: u8, _: usize, _: usize) bool { + return false; + } + + pub fn free( + ptr: *anyopaque, + buf: []u8, + _: u8, + _: usize, + ) void { + var this = bun.cast(WTFStringImpl, ptr); + std.debug.assert(this.byteSlice().ptr == buf.ptr); + std.debug.assert(this.byteSlice().len == buf.len); + this.deref(); + } + + pub const VTable = std.mem.Allocator.VTable{ + .alloc = &alloc, + .resize = &resize, + .free = &free, + }; + + pub const VTablePtr = &VTable; +}; + +pub const Tag = enum(u8) { + Dead = 0, + WTFStringImpl = 1, + ZigString = 2, + StaticZigString = 3, + Empty = 4, +}; + +const ZigString = bun.JSC.ZigString; + +pub const StringImpl = extern union { + ZigString: ZigString, + WTFStringImpl: WTFStringImpl, + StaticZigString: ZigString, + Dead: void, + Empty: void, +}; + +/// Prefer using String instead of ZigString in new code. +pub const String = extern struct { + pub const name = "BunString"; + + tag: Tag, + value: StringImpl, + + pub const empty = String{ .tag = .Empty, .value = .{ .Empty = {} } }; + + pub const dead = String{ .tag = .Dead, .value = .{ .Dead = {} } }; + pub const StringImplAllocator = Parent.StringImplAllocator; + + pub fn initWithType(comptime Type: type, value: Type) String { + switch (comptime Type) { + ZigString => return String{ .tag = .ZigString, .value = .{ .ZigString = value } }, + [:0]u8, []u8, [:0]const u8, []const u8 => return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.fromBytes(value) } }, + [:0]u16, []u16, [:0]const u16, []const u16 => return String{ .tag = .ZigString, .value = .{ .ZigString = ZigString.from16Slice(value) } }, + WTFStringImpl => return String{ .tag = .WTFStringImpl, .value = .{ .WTFStringImpl = value } }, + *const ZigString, *ZigString => return String{ .tag = .ZigString, .value = .{ .ZigString = value.* } }, + *const [0:0]u8 => return String{ .tag = .Empty, .value = .{ .Empty = {} } }, + String => return value, + else => { + if (comptime std.meta.trait.isZigString(Type)) { + return static(value); + } + + @compileError("Unsupported type for String " ++ @typeName(Type)); + }, + } + } + + pub fn toErrorInstance(this: String, globalObject: *JSC.JSGlobalObject) JSC.JSValue { + return this.toZigString().toErrorInstance(globalObject); + } + + pub fn static(input: []const u8) String { + return .{ + .tag = .StaticZigString, + .value = .{ .StaticZigString = ZigString.init(input) }, + }; + } + + pub fn init(value: anytype) String { + return initWithType(@TypeOf(value), value); + } + + pub fn fromUTF8(value: []const u8) String { + return String.initWithType(ZigString, ZigString.initUTF8(value)); + } + + pub fn fromBytes(value: []const u8) String { + return String.initWithType(ZigString, ZigString.fromBytes(value)); + } + + pub fn format(self: String, comptime fmt: []const u8, opts: std.fmt.FormatOptions, writer: anytype) !void { + try self.toZigString().format(fmt, opts, writer); + } + + pub fn fromJS(value: bun.JSC.JSValue, globalObject: *JSC.JSGlobalObject) String { + JSC.markBinding(@src()); + + var out: String = String.dead; + if (BunString__fromJS(globalObject, value, &out)) { + return out; + } else { + return String.dead; + } + } + + pub fn tryFromJS(value: bun.JSC.JSValue, globalObject: *JSC.JSGlobalObject) ?String { + JSC.markBinding(@src()); + + var out: String = String.dead; + if (BunString__fromJS(globalObject, value, &out)) { + return out; + } else { + return null; + } + } + + pub fn toJS(this: *String, globalObject: *bun.JSC.JSGlobalObject) JSC.JSValue { + JSC.markBinding(@src()); + + return BunString__toJS(globalObject, this); + } + + pub fn toZigString(this: String) ZigString { + if (this.tag == .StaticZigString or this.tag == .ZigString) { + return this.value.ZigString; + } + + if (this.tag == .WTFStringImpl) + return this.value.WTFStringImpl.toZigString(); + + return ZigString.Empty; + } + + pub fn toWTF(this: *String) void { + BunString__toWTFString(this); + } + + pub inline fn length(this: String) usize { + return if (this.tag == .WTFStringImpl) + this.value.WTFStringImpl.length() + else + this.toZigString().length(); + } + + pub inline fn utf16(self: String) []const u16 { + if (self.tag == .Empty) + return &[_]u16{}; + std.debug.assert(self.tag == .WTFStringImpl); + return self.value.WTFStringImpl.utf16(); + } + + pub inline fn latin1(self: String) []const u8 { + if (self.tag == .Empty) + return &[_]u8{}; + + std.debug.assert(self.tag == .WTFStringImpl); + return self.value.WTFStringImpl.latin1(); + } + + pub fn isUTF8(self: String) bool { + if (!self.tag == .ZigString or self.tag == .StaticZigString) + return false; + + return self.value.ZigString.isUTF8(); + } + + pub fn encoding(self: String) bun.strings.Encoding { + if (self.isUTF16()) { + return .utf16; + } + + if (self.isUTF8()) { + return .utf8; + } + + return .latin1; + } + + pub fn byteSlice(this: String) []const u8 { + return switch (this.tag) { + .ZigString, .StaticZigString => this.value.ZigString.byteSlice(), + .WTFStringImpl => this.value.WTFStringImpl.byteSlice(), + else => &[_]u8{}, + }; + } + + pub fn isUTF16(self: String) bool { + if (self.tag == .WTFStringImpl) + return !self.value.WTFStringImpl.is8Bit(); + + if (self.tag == .ZigString or self.tag == .StaticZigString) + return self.value.ZigString.isUTF16(); + + return false; + } + + pub inline fn utf8(self: String) []const u8 { + if (comptime bun.Environment.allow_assert) + std.debug.assert(self.canBeUTF8()); + return self.value.ZigString.slice(); + } + + pub fn canBeUTF8(self: String) bool { + if (self.tag == .WTFStringImpl) + return self.value.WTFStringImpl.is8Bit() and bun.strings.isAllASCII(self.value.WTFStringImpl.latin1()); + + if (self.tag == .ZigString or self.tag == .StaticZigString) + return self.value.ZigString.isUTF8(); + + return self.tag == .Empty; + } + + pub fn substring(self: String, offset: usize) String { + return String.init(self.toZigString().substring(offset, 0)); + } + + pub fn toUTF8(this: String, allocator: std.mem.Allocator) ZigString.Slice { + if (this.tag == .WTFStringImpl) { + return this.value.WTFStringImpl.toUTF8(allocator); + } + + if (this.tag == .ZigString) { + return this.value.ZigString.toSlice(allocator); + } + + if (this.tag == .StaticZigString) { + return ZigString.Slice.fromUTF8NeverFree(this.value.StaticZigString.slice()); + } + + return ZigString.Slice.empty; + } + + extern fn BunString__fromJS(globalObject: *JSC.JSGlobalObject, value: bun.JSC.JSValue, out: *String) bool; + extern fn BunString__toJS(globalObject: *JSC.JSGlobalObject, in: *String) JSC.JSValue; + extern fn BunString__toWTFString(this: *String) void; + + pub fn ref(this: String) void { + switch (this.tag) { + .WTFStringImpl => this.value.WTFStringImpl.ref(), + else => {}, + } + } + + pub fn deref(this: String) void { + switch (this.tag) { + .WTFStringImpl => this.value.WTFStringImpl.deref(), + else => {}, + } + } + + pub const unref = deref; + + pub fn eqlComptime(this: String, comptime value: []const u8) bool { + return this.toZigString().eqlComptime(value); + } + + pub fn hasPrefixComptime(this: String, comptime value: []const u8) bool { + _ = value; + _ = this; + return false; + // return this.toZigString().substring(0, value.len).eqlComptime(value); + } + + pub fn isWTFAllocator(this: std.mem.Allocator) bool { + return this.vtable == @This().StringImplAllocator.VTablePtr; + } + + pub fn eqlBytes(this: String, value: []const u8) bool { + return bun.strings.eqlLong(this.byteSlice(), value, true); + } + + pub fn eql(this: String, other: String) bool { + return this.toZigString().eql(other.toZigString()); + } +}; diff --git a/src/string_immutable.zig b/src/string_immutable.zig index 3d1724d4e..8aaa6f8e8 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -1467,6 +1467,17 @@ pub fn toUTF8ListWithType(list_: std.ArrayList(u8), comptime Type: type, utf16: return toUTF8ListWithTypeBun(list_, Type, utf16); } +pub fn toUTF8FromLatin1(allocator: std.mem.Allocator, latin1: []const u8) !?std.ArrayList(u8) { + if (bun.JSC.is_bindgen) + unreachable; + + if (isAllASCII(latin1)) + return null; + + var list = try std.ArrayList(u8).initCapacity(allocator, latin1.len); + return try allocateLatin1IntoUTF8WithList(list, 0, []const u8, latin1); +} + pub fn toUTF8ListWithTypeBun(list_: std.ArrayList(u8), comptime Type: type, utf16: Type) !std.ArrayList(u8) { var list = list_; var utf16_remaining = utf16; |