diff options
author | 2022-11-28 15:55:02 -0800 | |
---|---|---|
committer | 2022-11-28 15:55:02 -0800 | |
commit | a6cadce6f6292b685cc4160052304b5dfc8cd3ad (patch) | |
tree | a1d4d676cfc04ddcf084e12b26c6b4c59093ebd1 | |
parent | 51d0c3b79f63e5ca1965cb5280c58fc0a9bc4f73 (diff) | |
download | bun-a6cadce6f6292b685cc4160052304b5dfc8cd3ad.tar.gz bun-a6cadce6f6292b685cc4160052304b5dfc8cd3ad.tar.zst bun-a6cadce6f6292b685cc4160052304b5dfc8cd3ad.zip |
Fix process.env and Bun.env object spread
Fixes https://github.com/oven-sh/bun/issues/1512
-rw-r--r-- | src/bun.js/api/bun.zig | 197 | ||||
-rw-r--r-- | src/bun.js/bindings/JSEnvironmentVariableMap.cpp | 71 | ||||
-rw-r--r-- | src/bun.js/bindings/JSEnvironmentVariableMap.h | 15 | ||||
-rw-r--r-- | src/bun.js/bindings/Process.cpp | 16 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.cpp | 18 | ||||
-rw-r--r-- | src/bun.js/bindings/ZigGlobalObject.h | 1 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 10 | ||||
-rw-r--r-- | src/bun.js/javascript.zig | 3 |
8 files changed, 127 insertions, 204 deletions
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig index 1c60a7faf..f993bc86e 100644 --- a/src/bun.js/api/bun.zig +++ b/src/bun.js/api/bun.zig @@ -1241,10 +1241,6 @@ pub const Class = NewClass( .argv = .{ .get = getArgv, }, - .env = .{ - .get = EnvironmentVariables.getter, - }, - .enableANSIColors = .{ .get = enableANSIColors, }, @@ -3285,189 +3281,34 @@ pub const UnsafeCString = struct { /// Also, you can't iterate over process.env normally since it only exists at build-time otherwise // This is aliased to Bun.env pub const EnvironmentVariables = struct { - pub const Class = NewClass( - void, - .{ - .name = "DotEnv", - }, - .{ - .getProperty = .{ - .rfn = getProperty, - }, - .setProperty = .{ - .rfn = setProperty, - }, - .deleteProperty = .{ - .rfn = deleteProperty, - }, - .getPropertyNames = .{ - .rfn = getPropertyNames, - }, - }, - .{}, - ); - - pub fn getter( - _: void, - ctx: js.JSContextRef, - _: js.JSValueRef, - _: js.JSStringRef, - _: js.ExceptionRef, - ) js.JSValueRef { - var existing = ctx.ptr().getCachedObject(ZigString.static("Bun.env")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - ZigString.static("Bun.env"), - JSValue.fromRef(js.JSObjectMake(ctx, EnvironmentVariables.Class.get().*, null)), - ).asObjectRef(); - } - - return existing.asObjectRef(); + pub export fn Bun__getEnvNames(globalObject: *JSC.JSGlobalObject, names: [*]ZigString, max: usize) usize { + return getEnvNames(globalObject, names[0..max]); } - pub const BooleanString = struct { - pub const @"true": string = "true"; - pub const @"false": string = "false"; - }; - - pub fn getProperty( - ctx: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) js.JSValueRef { - var name_slice = propertyName.toZigString().toSlice(ctx.allocator()); - defer name_slice.deinit(); - var name = name_slice.slice(); - if (strings.eqlComptime(name, "toJSON")) { - var existing = ctx.ptr().getCachedObject(ZigString.static("Bun.env.toJSON")); - if (existing.isEmpty()) { - return ctx.ptr().putCachedObject( - ZigString.static("Bun.env.toJSON"), - // TODO: stage2 change this to a ptr - JSC.NewFunction(ctx, ZigString.static("toJSON"), 0, toJSON, false), - ).asObjectRef(); - } - - return existing.asObjectRef(); - } - - if (VirtualMachine.vm.bundler.env.map.get(name)) |value| { - return ZigString.initUTF8(value).toValueGC(ctx).asObjectRef(); - } - - if (Output.enable_ansi_colors) { - // https://github.com/chalk/supports-color/blob/main/index.js - if (strings.eqlComptime(name, "FORCE_COLOR")) { - return ZigString.static("\"true\"").toValue(ctx).asObjectRef(); - } - } - - return js.JSValueMakeUndefined(ctx); - } - - pub fn toJSON( - globalThis: *JSC.JSGlobalObject, - _: *JSC.CallFrame, - ) callconv(.C) JSC.JSValue { - var map = globalThis.bunVM().bundler.env.map.map; - var keys = map.keys(); - var values = map.values(); - const StackFallback = std.heap.StackFallbackAllocator(32 * 2 * @sizeOf(ZigString)); - var stack = StackFallback{ - .buffer = undefined, - .fallback_allocator = bun.default_allocator, - .fixed_buffer_allocator = undefined, - }; - var allocator = stack.get(); - var key_strings_ = allocator.alloc(ZigString, keys.len * 2) catch unreachable; - var key_strings = key_strings_[0..keys.len]; - var value_strings = key_strings_[keys.len..]; - - for (keys) |key, i| { - key_strings[i] = ZigString.initUTF8(key); - value_strings[i] = ZigString.initUTF8(values[i]); - } - - var result = JSValue.fromEntries(globalThis, key_strings.ptr, value_strings.ptr, keys.len, false); - allocator.free(key_strings_); - return result; - // } - // ZigConsoleClient.Formatter.format(this: *Formatter, result: Tag.Result, comptime Writer: type, writer: Writer, value: JSValue, globalThis: *JSGlobalObject, comptime enable_ansi_colors: bool) - } - - pub fn deleteProperty( - globalThis: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - _: js.ExceptionRef, - ) callconv(.C) bool { - var jsc_vm = globalThis.bunVM(); - const allocator = jsc_vm.allocator; - - const zig_str = propertyName.toZigString(); - - var str = zig_str.toSlice(allocator); - defer str.deinit(); - const name = str.slice(); - - if (jsc_vm.bundler.env.map.map.fetchSwapRemove(name)) |entry| { - // this can be a statically allocated string - if (bun.isHeapMemory(entry.value)) - allocator.free(bun.constStrToU8(entry.value)); - - allocator.free(bun.constStrToU8(entry.key)); + pub export fn Bun__getEnvValue(globalObject: *JSC.JSGlobalObject, name: *ZigString, value: *ZigString) bool { + if (getEnvValue(globalObject, name.*)) |val| { + value.* = val; return true; } return false; } - pub fn setProperty( - globalThis: js.JSContextRef, - _: js.JSObjectRef, - propertyName: js.JSStringRef, - value: js.JSValueRef, - _: js.ExceptionRef, - ) callconv(.C) bool { - var jsc_vm = globalThis.bunVM(); - const allocator = jsc_vm.allocator; - - const zig_str = propertyName.toZigString(); - - var str = zig_str.toSlice(allocator); - const name = str.slice(); - var entry = jsc_vm.bundler.env.map.map.getOrPut(name) catch return false; - if (!entry.found_existing) { - const value_str = value.?.value().toSlice(globalThis, allocator).cloneIfNeeded(allocator) catch return false; - entry.key_ptr.* = (str.cloneIfNeeded(allocator) catch return false).slice(); - - entry.value_ptr.* = value_str.slice(); - } else { - defer str.deinit(); - // this can be a statically allocated string - if (bun.isHeapMemory(entry.value_ptr.*)) - allocator.free(bun.constStrToU8(entry.value_ptr.*)); - const cloned_value = value.?.value().toSlice(globalThis, allocator).cloneIfNeeded(allocator) catch return false; - entry.value_ptr.* = cloned_value.slice(); + pub fn getEnvNames(globalObject: *JSC.JSGlobalObject, names: []ZigString) usize { + var vm = globalObject.bunVM(); + const keys = vm.bundler.env.map.map.keys(); + const max = @minimum(names.len, keys.len); + for (keys[0..max]) |key, i| { + names[i] = ZigString.initUTF8(key); } - - return true; + return keys.len; } - - pub fn getPropertyNames( - _: js.JSContextRef, - _: js.JSObjectRef, - props: js.JSPropertyNameAccumulatorRef, - ) callconv(.C) void { - var iter = VirtualMachine.vm.bundler.env.map.iter(); - - while (iter.next()) |item| { - const str = item.key_ptr.*; - var init_str = ZigString.init(str); - init_str.markUTF8(); - js.JSPropertyNameAccumulatorAddName(props, JSC.C.OpaqueJSString.fromZigString(init_str, VirtualMachine.vm.allocator)); - } + pub fn getEnvValue(globalObject: *JSC.JSGlobalObject, name: ZigString) ?ZigString { + var vm = globalObject.bunVM(); + var sliced = name.toSlice(vm.allocator); + defer sliced.deinit(); + const value = vm.bundler.env.map.map.get(sliced.slice()) orelse return null; + return ZigString.initUTF8(value); } }; @@ -3478,6 +3319,8 @@ export fn Bun__reportError(_: *JSGlobalObject, err: JSC.JSValue) void { comptime { if (!is_bindgen) { _ = Bun__reportError; + _ = EnvironmentVariables.Bun__getEnvNames; + _ = EnvironmentVariables.Bun__getEnvValue; } } diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.cpp b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp new file mode 100644 index 000000000..75114d791 --- /dev/null +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.cpp @@ -0,0 +1,71 @@ +#include "root.h" +#include "ZigGlobalObject.h" + +#include "helpers.h" + +#include "JavaScriptCore/JSObject.h" +#include "JavaScriptCore/ObjectConstructor.h" + +extern "C" size_t Bun__getEnvNames(JSGlobalObject*, ZigString* names, size_t max); +extern "C" bool Bun__getEnvValue(JSGlobalObject* globalObject, ZigString* name, ZigString* value); + +namespace Bun { + +JSC_DEFINE_CUSTOM_GETTER(jsGetterEnvironmentVariable, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName propertyName)) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + auto* thisObject = jsDynamicCast<JSObject*>(JSValue::decode(thisValue)); + if (UNLIKELY(!thisObject)) + return JSValue::encode(jsUndefined()); + + ZigString name = gettoZigString(propertyName.publicName()); + ZigString value = { nullptr, 0 }; + + if (UNLIKELY(name.len == 0)) + return JSValue::encode(jsUndefined()); + + if (!Bun__getEnvValue(globalObject, &name, &value) || value.len == 0) { + return JSValue::encode(jsUndefined()); + } + + JSValue result = jsString(vm, Zig::toStringCopy(value)); + thisObject->putDirect(vm, propertyName, result, 0); + return JSValue::encode(result); +} + +JSC_DEFINE_CUSTOM_SETTER(jsSetterEnvironmentVariable, (JSGlobalObject * globalObject, EncodedJSValue thisValue, EncodedJSValue value, PropertyName propertyName)) +{ + VM& vm = globalObject->vm(); + JSC::JSObject* object = JSValue::decode(thisValue).getObject(); + if (!object) + return false; + + object->putDirect(vm, propertyName, JSValue::decode(value), 0); + return true; +} + +JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject) +{ + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + size_t max = 768; + ZigString names[max]; + size_t count = Bun__getEnvNames(globalObject, names, max); + JSC::JSObject* object = nullptr; + if (count < 63) { + object = constructEmptyObject(globalObject, globalObject->objectPrototype(), count); + } else { + object = constructEmptyObject(globalObject, globalObject->objectPrototype()); + } + + for (size_t i = 0; i < count; i++) { + auto name = Zig::toStringCopy(names[i]); + object->putDirectCustomAccessor(vm, Identifier::fromString(vm, name), JSC::CustomGetterSetter::create(vm, jsGetterEnvironmentVariable, jsSetterEnvironmentVariable), JSC::PropertyAttribute::CustomAccessor | 0); + } + + return object; +} +}
\ No newline at end of file diff --git a/src/bun.js/bindings/JSEnvironmentVariableMap.h b/src/bun.js/bindings/JSEnvironmentVariableMap.h new file mode 100644 index 000000000..7cc605303 --- /dev/null +++ b/src/bun.js/bindings/JSEnvironmentVariableMap.h @@ -0,0 +1,15 @@ +#include "root.h" + +namespace Zig { +class GlobalObject; +} + +namespace JSC { +class JSValue; +} + +namespace Bun { + +JSC::JSValue createEnvironmentVariablesMap(Zig::GlobalObject* globalObject); + +}
\ No newline at end of file diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp index 2071cfa05..07aabf343 100644 --- a/src/bun.js/bindings/Process.cpp +++ b/src/bun.js/bindings/Process.cpp @@ -5,6 +5,7 @@ #include <dlfcn.h> #include "ZigGlobalObject.h" #include "headers.h" +#include "JSEnvironmentVariableMap.h" #pragma mark - Node.js Process @@ -292,6 +293,7 @@ void Process::finishCreation(JSC::VM& vm) { Base::finishCreation(vm); auto clientData = WebCore::clientData(vm); + auto* globalObject = reinterpret_cast<Zig::GlobalObject*>(this->globalObject()); putDirectCustomAccessor(vm, clientData->builtinNames().pidPublicName(), JSC::CustomGetterSetter::create(vm, Process_getPID, nullptr), @@ -313,27 +315,27 @@ void Process::finishCreation(JSC::VM& vm) JSC::jsString(vm, makeAtomString(Bun__version_sha)), 0); this->putDirect(vm, clientData->builtinNames().nextTickPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 1, + JSC::JSFunction::create(vm, globalObject, 1, MAKE_STATIC_STRING_IMPL("nextTick"), Process_functionNextTick, ImplementationVisibility::Public), PropertyAttribute::Function | 0); this->putDirect(vm, JSC::Identifier::fromString(vm, "dlopen"_s), - JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 1, + JSC::JSFunction::create(vm, globalObject, 1, MAKE_STATIC_STRING_IMPL("dlopen"), Process_functionDlopen, ImplementationVisibility::Public), PropertyAttribute::Function | 0); this->putDirect(vm, clientData->builtinNames().cwdPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 0, + JSC::JSFunction::create(vm, globalObject, 0, MAKE_STATIC_STRING_IMPL("cwd"), Process_functionCwd, ImplementationVisibility::Public), PropertyAttribute::Function | 0); this->putDirect(vm, clientData->builtinNames().chdirPublicName(), - JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 0, + JSC::JSFunction::create(vm, globalObject, 0, MAKE_STATIC_STRING_IMPL("chdir"), Process_functionChdir, ImplementationVisibility::Public), PropertyAttribute::Function | 0); this->putDirect(vm, JSC::Identifier::fromString(vm, "exit"_s), - JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 0, + JSC::JSFunction::create(vm, globalObject, 0, MAKE_STATIC_STRING_IMPL("exit"), Process_functionExit, ImplementationVisibility::Public), PropertyAttribute::Function | 0); @@ -376,10 +378,10 @@ void Process::finishCreation(JSC::VM& vm) JSC::jsString(this->vm(), makeAtomString("arm64"))); #endif - JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 0, + JSC::JSFunction* hrtime = JSC::JSFunction::create(vm, globalObject, 0, MAKE_STATIC_STRING_IMPL("hrtime"), Process_functionHRTime, ImplementationVisibility::Public); - JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, JSC::jsCast<JSC::JSGlobalObject*>(globalObject()), 0, + JSC::JSFunction* hrtimeBigInt = JSC::JSFunction::create(vm, globalObject, 0, MAKE_STATIC_STRING_IMPL("bigint"), Process_functionHRTimeBigInt, ImplementationVisibility::Public); hrtime->putDirect(vm, JSC::Identifier::fromString(vm, "bigint"_s), hrtimeBigInt); diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp index d79048ea3..6d20a63d2 100644 --- a/src/bun.js/bindings/ZigGlobalObject.cpp +++ b/src/bun.js/bindings/ZigGlobalObject.cpp @@ -2462,13 +2462,7 @@ void GlobalObject::finishCreation(VM& vm) m_processEnvObject.initLater( [](const JSC::LazyProperty<JSC::JSGlobalObject, JSC::JSObject>::Initializer& init) { - auto jsClass = reinterpret_cast<Zig::GlobalObject*>(init.owner)->m_dotEnvClassRef; - - JSC::JSCallbackObject<JSNonFinalObject>* object = JSC::JSCallbackObject<JSNonFinalObject>::create( - init.owner, init.owner->callbackObjectStructure(), jsClass, nullptr); - if (JSObject* prototype = jsClass->prototype(init.owner)) - object->setPrototypeDirect(init.vm, prototype); - init.set(object); + init.set(Bun::createEnvironmentVariablesMap(reinterpret_cast<Zig::GlobalObject*>(init.owner)).getObject()); }); m_processObject.initLater( @@ -3177,6 +3171,16 @@ void GlobalObject::installAPIGlobals(JSClassRef* globals, int count, JSC::VM& vm { + JSC::Identifier identifier = JSC::Identifier::fromString(vm, "env"_s); + object->putDirectCustomAccessor(vm, identifier, + JSC::CustomGetterSetter::create(vm, lazyProcessEnvGetter, lazyProcessEnvSetter), + JSC::PropertyAttribute::DontDelete + | JSC::PropertyAttribute::CustomValue + | 0); + } + + { + JSC::Identifier identifier = JSC::Identifier::fromString(vm, pathToFileURLString); object->putDirectNativeFunction(vm, this, identifier, 1, functionPathToFileURL, ImplementationVisibility::Public, NoIntrinsic, JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly); diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h index 1aee587cf..30749b4ce 100644 --- a/src/bun.js/bindings/ZigGlobalObject.h +++ b/src/bun.js/bindings/ZigGlobalObject.h @@ -26,6 +26,7 @@ class EventLoopTask; #include "JavaScriptCore/JSTypeInfo.h" #include "JavaScriptCore/Structure.h" #include "WebCoreJSBuiltinInternals.h" +#include "JSEnvironmentVariableMap.h" #include "ZigConsoleClient.h" diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index dac30b344..92f269752 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -1352,16 +1352,6 @@ pub const ZigConsoleClient = struct { const callable = js_type != .Object and value.isCallable(globalThis.vm()); if (value.isClass(globalThis) and !callable) { - // Temporary workaround - // console.log(process.env) shows up as [class JSCallbackObject] - // We want to print it like an object - if (CAPI.JSValueIsObjectOfClass(globalThis, value.asObjectRef(), JSC.API.Bun.EnvironmentVariables.Class.get().?[0])) { - return .{ - .tag = .Object, - .cell = js_type, - }; - } - return .{ .tag = .Object, .cell = js_type, diff --git a/src/bun.js/javascript.zig b/src/bun.js/javascript.zig index 7e4ae9ed5..900f42ea1 100644 --- a/src/bun.js/javascript.zig +++ b/src/bun.js/javascript.zig @@ -109,9 +109,6 @@ pub const GlobalClasses = [_]type{ WebCore.Alert.Class, WebCore.Confirm.Class, WebCore.Prompt.Class, - - // The last item in this array becomes "process.env" - Bun.EnvironmentVariables.Class, }; const TaggedPointerUnion = @import("../tagged_pointer.zig").TaggedPointerUnion; const Task = JSC.Task; |