aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bench/snippets/module-exports-putter.cjs65
-rw-r--r--bench/websocket-server/chat-server.bun.js1
-rw-r--r--docs/cli/run.md2
-rw-r--r--packages/bun-types/bun-test.d.ts52
-rw-r--r--packages/bun-types/bun.d.ts4
-rw-r--r--packages/bun-types/scripts/bundle.ts2
-rw-r--r--packages/bun-types/tests/mocks.test-d.ts16
-rw-r--r--src/build-id2
-rw-r--r--src/bun.js/api/bun.zig17
-rw-r--r--src/bun.js/api/bun/socket.zig16
-rw-r--r--src/bun.js/api/server.zig10
-rw-r--r--src/bun.js/bindings/BunPlugin.cpp4
-rw-r--r--src/bun.js/bindings/CallSitePrototype.cpp2
-rw-r--r--src/bun.js/bindings/CommonJSModuleRecord.cpp497
-rw-r--r--src/bun.js/bindings/ErrorStackTrace.h1
-rw-r--r--src/bun.js/bindings/ExceptionOr.h6
-rw-r--r--src/bun.js/bindings/JSBundlerPlugin.cpp2
-rw-r--r--src/bun.js/bindings/Process.cpp14
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.cpp14
-rw-r--r--src/bun.js/bindings/ZigGlobalObject.h1
-rw-r--r--src/bun.js/bindings/webcore/EventEmitter.cpp1
-rw-r--r--src/bun.js/bindings/webcore/EventTarget.cpp1
-rw-r--r--src/bun.js/bindings/webcore/Node.h31
-rw-r--r--src/bun.js/bindings/webcore/WebSocket.cpp36
-rw-r--r--src/cli/init_command.zig8
-rw-r--r--src/js_parser.zig78
-rw-r--r--src/string.zig3
-rw-r--r--test/bundler/esbuild/default.test.ts42
-rw-r--r--test/js/bun/http/bun-server.test.ts48
-rw-r--r--test/js/bun/net/tcp-server.test.ts38
-rw-r--r--test/js/node/process/process.test.js10
31 files changed, 617 insertions, 407 deletions
diff --git a/bench/snippets/module-exports-putter.cjs b/bench/snippets/module-exports-putter.cjs
new file mode 100644
index 000000000..9bef17b90
--- /dev/null
+++ b/bench/snippets/module-exports-putter.cjs
@@ -0,0 +1,65 @@
+// This is a stress test of some internals in How Bun does the module.exports assignment.
+// If it crashes or throws then this fails
+import("./runner.mjs").then(({ bench, run }) => {
+ bench("Object.defineProperty(module, 'exports', { get() { return 42; } })", () => {
+ Object.defineProperty(module, "exports", {
+ get() {
+ return 42;
+ },
+ set() {
+ throw new Error("bad");
+ },
+ configurable: true,
+ });
+ if (module.exports !== 42) throw new Error("bad");
+ if (!Object.getOwnPropertyDescriptor(module, "exports").get) throw new Error("bad");
+ });
+
+ bench("Object.defineProperty(module.exports = {})", () => {
+ Object.defineProperty(module, "exports", {
+ value: { abc: 123 },
+ });
+
+ if (!module.exports.abc) throw new Error("bad");
+ if (Object.getOwnPropertyDescriptor(module, "exports").value !== module.exports) throw new Error("bad");
+ });
+
+ bench("module.exports = {}", () => {
+ module.exports = { abc: 123 };
+
+ if (!module.exports.abc) throw new Error("bad");
+ if (Object.getOwnPropertyDescriptor(module, "exports").value !== module.exports) throw new Error("bad");
+ });
+
+ run().then(() => {
+ module.exports = {
+ a: 1,
+ };
+
+ console.log(
+ module?.exports,
+ require.cache[module.id].exports,
+ module?.exports === require.cache[module.id],
+ __dirname,
+ Object.keys(require(module.id)),
+ require(module.id),
+ );
+
+ module.exports = function lol() {
+ return 42;
+ };
+
+ console.log(module.exports, module.exports());
+
+ queueMicrotask(() => {
+ console.log(
+ module?.exports,
+ require.cache[module.id].exports,
+ module?.exports === require.cache[module.id]?.exports,
+ __dirname,
+ Object.keys(require(module.id)),
+ require(module.id),
+ );
+ });
+ });
+});
diff --git a/bench/websocket-server/chat-server.bun.js b/bench/websocket-server/chat-server.bun.js
index b4c71a6dc..9d45b4622 100644
--- a/bench/websocket-server/chat-server.bun.js
+++ b/bench/websocket-server/chat-server.bun.js
@@ -32,6 +32,7 @@ const server = Bun.serve({
},
perMessageDeflate: false,
+ publishToSelf: true
},
fetch(req, server) {
diff --git a/docs/cli/run.md b/docs/cli/run.md
index 7a5f8e9d2..1398e10ca 100644
--- a/docs/cli/run.md
+++ b/docs/cli/run.md
@@ -102,7 +102,7 @@ To debug environment variables, run `bun run env` to view a list of resolved env
Bun is designed to start fast and run fast.
-Under the hood Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. In most cases, the startup and running performance is faster than V8, the engine used by Node.js and Chromium-based browsers. It's transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js.
+Under the hood Bun uses the [JavaScriptCore engine](https://developer.apple.com/documentation/javascriptcore), which is developed by Apple for Safari. In most cases, the startup and running performance is faster than V8, the engine used by Node.js and Chromium-based browsers. Its transpiler and runtime are written in Zig, a modern, high-performance language. On Linux, this translates into startup times [4x faster](https://twitter.com/jarredsumner/status/1499225725492076544) than Node.js.
{% image src="/images/bun-run-speed.jpeg" caption="Bun vs Node.js vs Deno running Hello World" /%}
diff --git a/packages/bun-types/bun-test.d.ts b/packages/bun-types/bun-test.d.ts
index bf89b808d..05fd0eac0 100644
--- a/packages/bun-types/bun-test.d.ts
+++ b/packages/bun-types/bun-test.d.ts
@@ -27,43 +27,11 @@ declare module "bun:test" {
export const mock: {
<T extends AnyFunction>(Function: T): Mock<T>;
-
- mockClear(): typeof mock;
- mockReset(): typeof mock;
- mockRestore(): void;
- mockReturnValue<T extends JestMock.FunctionLike = JestMock.UnknownFunction>(
- value: ReturnType<T>,
- ): JestMock.MockInstance<T>;
- mockReturnValueOnce<
- T extends JestMock.FunctionLike = JestMock.UnknownFunction,
- >(
- value: ReturnType<T>,
- ): JestMock.MockInstance<T>;
- mockResolvedValue<
- T extends JestMock.FunctionLike = JestMock.UnknownFunction,
- >(
- value: JestMock.ResolveType<T>,
- ): JestMock.MockInstance<T>;
- mockResolvedValueOnce<
- T extends JestMock.FunctionLike = JestMock.UnknownFunction,
- >(
- value: JestMock.ResolveType<T>,
- ): JestMock.MockInstance<T>;
- mockRejectedValue<
- T extends JestMock.FunctionLike = JestMock.UnknownFunction,
- >(
- value: JestMock.RejectType<T>,
- ): JestMock.MockInstance<T>;
- mockRejectedValueOnce<
- T extends JestMock.FunctionLike = JestMock.UnknownFunction,
- >(
- value: JestMock.RejectType<T>,
- ): JestMock.MockInstance<T>;
};
interface Jest {
restoreAllMocks(): void;
- fn<T extends AnyFunction>(func: T): Mock<T>;
+ fn<T extends AnyFunction>(func?: T): Mock<T>;
}
export const jest: Jest;
export namespace jest {
@@ -473,6 +441,24 @@ declare module "bun:test" {
*/
toBe(expected: T): void;
/**
+ * Asserts that a number is odd.
+ *
+ * @link https://jest-extended.jestcommunity.dev/docs/matchers/number/#tobeodd
+ * @example
+ * expect(1).toBeOdd();
+ * expect(2).not.toBeOdd();
+ */
+ toBeOdd(): void;
+ /**
+ * Asserts that a number is even.
+ *
+ * @link https://jest-extended.jestcommunity.dev/docs/matchers/number/#tobeeven
+ * @example
+ * expect(2).toBeEven();
+ * expect(1).not.toBeEven();
+ */
+ toBeEven(): void;
+ /**
* Asserts that value is close to the expected by floating point precision.
*
* For example, the following fails because arithmetic on decimal (base 10)
diff --git a/packages/bun-types/bun.d.ts b/packages/bun-types/bun.d.ts
index 6987a9226..306971d4c 100644
--- a/packages/bun-types/bun.d.ts
+++ b/packages/bun-types/bun.d.ts
@@ -51,6 +51,10 @@ declare module "bun" {
*
*/
export const env: Env;
+ /**
+ * The raw arguments passed to the process, including flags passed to Bun. If you want to easily read flags passed to your script, consider using `process.argv` instead.
+ */
+ export const argv: string[];
export const origin: string;
/**
diff --git a/packages/bun-types/scripts/bundle.ts b/packages/bun-types/scripts/bundle.ts
index aa91a3dfa..ce1c5ff4d 100644
--- a/packages/bun-types/scripts/bundle.ts
+++ b/packages/bun-types/scripts/bundle.ts
@@ -75,6 +75,8 @@ const tsConfig = {
skipLibCheck: true,
jsx: "react-jsx",
allowImportingTsExtensions: true,
+ emitDeclarationOnly: true,
+ composite: true,
allowSyntheticDefaultImports: true,
forceConsistentCasingInFileNames: true,
allowJs: true,
diff --git a/packages/bun-types/tests/mocks.test-d.ts b/packages/bun-types/tests/mocks.test-d.ts
index f4fe47d4f..9bb8a9070 100644
--- a/packages/bun-types/tests/mocks.test-d.ts
+++ b/packages/bun-types/tests/mocks.test-d.ts
@@ -14,4 +14,18 @@ declare var arg2: arg2;
arg2.mock.calls[0];
mock;
-type _arg3 = jest.Mock<() => number>;
+// @ts-expect-error
+jest.fn<() => Promise<string>>().mockReturnValue("asdf");
+// @ts-expect-error
+jest.fn<() => string>().mockReturnValue(24);
+jest.fn<() => string>().mockReturnValue("24");
+
+jest.fn<() => Promise<string>>().mockResolvedValue("asdf");
+// @ts-expect-error
+jest.fn<() => string>().mockResolvedValue(24);
+// @ts-expect-error
+jest.fn<() => string>().mockResolvedValue("24");
+
+jest.fn().mockClear();
+jest.fn().mockReset();
+jest.fn().mockRejectedValueOnce(new Error());
diff --git a/src/build-id b/src/build-id
index 45a4fb75d..f599e28b8 100644
--- a/src/build-id
+++ b/src/build-id
@@ -1 +1 @@
-8
+10
diff --git a/src/bun.js/api/bun.zig b/src/bun.js/api/bun.zig
index d3ef6919a..5580e8840 100644
--- a/src/bun.js/api/bun.zig
+++ b/src/bun.js/api/bun.zig
@@ -440,7 +440,7 @@ pub fn getAssetPrefix(
_: js.JSStringRef,
_: js.ExceptionRef,
) js.JSValueRef {
- return ZigString.init(VirtualMachine.get().bundler.options.routes.asset_prefix_path).toValue(ctx.ptr()).asRef();
+ return ZigString.init(VirtualMachine.get().bundler.options.routes.asset_prefix_path).toValueGC(ctx.ptr()).asRef();
}
pub fn getArgv(
@@ -450,19 +450,8 @@ pub fn getArgv(
_: js.JSStringRef,
_: js.ExceptionRef,
) js.JSValueRef {
- if (comptime Environment.isWindows) {
- @compileError("argv not supported on windows");
- }
-
- var argv_list = std.heap.stackFallback(128, getAllocator(ctx));
- var allocator = argv_list.get();
- var argv = allocator.alloc(ZigString, std.os.argv.len) catch unreachable;
- defer if (argv.len > 128) allocator.free(argv);
- for (std.os.argv, 0..) |arg, i| {
- argv[i] = ZigString.init(std.mem.span(arg));
- }
-
- return JSValue.createStringArray(ctx.ptr(), argv.ptr, argv.len, true).asObjectRef();
+ // TODO: cache this
+ return JSC.Node.Process.getArgv(ctx).asObjectRef();
}
pub fn getRoutesDir(
diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig
index 780bfcb14..48bfe4218 100644
--- a/src/bun.js/api/bun/socket.zig
+++ b/src/bun.js/api/bun/socket.zig
@@ -254,19 +254,17 @@ pub const SocketConfig = struct {
var ssl: ?JSC.API.ServerConfig.SSLConfig = null;
var default_data = JSValue.zero;
- if (opts.getTruthy(globalObject, "tls")) |tls| outer: {
+ if (opts.getTruthy(globalObject, "tls")) |tls| {
if (tls.isBoolean()) {
if (tls.toBoolean()) {
ssl = JSC.API.ServerConfig.SSLConfig.zero;
}
-
- break :outer;
- }
-
- if (JSC.API.ServerConfig.SSLConfig.inJS(globalObject, tls, exception)) |ssl_config| {
- ssl = ssl_config;
- } else if (exception.* != null) {
- return null;
+ } else {
+ if (JSC.API.ServerConfig.SSLConfig.inJS(globalObject, tls, exception)) |ssl_config| {
+ ssl = ssl_config;
+ } else if (exception.* != null) {
+ return null;
+ }
}
}
diff --git a/src/bun.js/api/server.zig b/src/bun.js/api/server.zig
index 8fd9acde7..37bc601a5 100644
--- a/src/bun.js/api/server.zig
+++ b/src/bun.js/api/server.zig
@@ -270,6 +270,11 @@ pub const ServerConfig = struct {
pub fn inJS(global: *JSC.JSGlobalObject, obj: JSC.JSValue, exception: JSC.C.ExceptionRef) ?SSLConfig {
var result = zero;
+ if (!obj.isObject()) {
+ JSC.throwInvalidArguments("tls option expects an object", .{}, global, exception);
+ return null;
+ }
+
var any = false;
// Required
@@ -688,7 +693,7 @@ pub const ServerConfig = struct {
}
if (arguments.next()) |arg| {
- if (arg.isUndefinedOrNull() or !arg.isObject()) {
+ if (!arg.isObject()) {
JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global, exception);
return args;
}
@@ -812,6 +817,9 @@ pub const ServerConfig = struct {
}
return args;
}
+ } else {
+ JSC.throwInvalidArguments("Bun.serve expects an object", .{}, global, exception);
+ return args;
}
if (args.base_uri.len > 0) {
diff --git a/src/bun.js/bindings/BunPlugin.cpp b/src/bun.js/bindings/BunPlugin.cpp
index ad4039336..066cf82fd 100644
--- a/src/bun.js/bindings/BunPlugin.cpp
+++ b/src/bun.js/bindings/BunPlugin.cpp
@@ -49,7 +49,7 @@ static EncodedJSValue jsFunctionAppendOnLoadPluginBody(JSC::JSGlobalObject* glob
auto clientData = WebCore::clientData(vm);
auto& builtinNames = clientData->builtinNames();
JSC::RegExpObject* filter = nullptr;
- if (JSValue filterValue = filterObject->getIfPropertyExists(globalObject, builtinNames.filterPublicName())) {
+ if (JSValue filterValue = filterObject->getIfPropertyExists(globalObject, Identifier::fromString(vm, "filter"_s))) {
if (filterValue.isCell() && filterValue.asCell()->inherits<JSC::RegExpObject>())
filter = jsCast<JSC::RegExpObject*>(filterValue);
}
@@ -101,7 +101,7 @@ static EncodedJSValue jsFunctionAppendOnResolvePluginBody(JSC::JSGlobalObject* g
auto clientData = WebCore::clientData(vm);
auto& builtinNames = clientData->builtinNames();
JSC::RegExpObject* filter = nullptr;
- if (JSValue filterValue = filterObject->getIfPropertyExists(globalObject, builtinNames.filterPublicName())) {
+ if (JSValue filterValue = filterObject->getIfPropertyExists(globalObject, Identifier::fromString(vm, "filter"_s))) {
if (filterValue.isCell() && filterValue.asCell()->inherits<JSC::RegExpObject>())
filter = jsCast<JSC::RegExpObject*>(filterValue);
}
diff --git a/src/bun.js/bindings/CallSitePrototype.cpp b/src/bun.js/bindings/CallSitePrototype.cpp
index faaf571e2..f3e365cb0 100644
--- a/src/bun.js/bindings/CallSitePrototype.cpp
+++ b/src/bun.js/bindings/CallSitePrototype.cpp
@@ -89,7 +89,7 @@ const JSC::ClassInfo CallSitePrototype::s_info = { "CallSite"_s, &Base::s_info,
void CallSitePrototype::finishCreation(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
{
Base::finishCreation(vm);
- ASSERT(inherits(vm, info()));
+ ASSERT(inherits(info()));
reifyStaticProperties(vm, CallSite::info(), CallSitePrototypeTableValues, *this);
JSC_TO_STRING_TAG_WITHOUT_TRANSITION();
diff --git a/src/bun.js/bindings/CommonJSModuleRecord.cpp b/src/bun.js/bindings/CommonJSModuleRecord.cpp
index 31c24bb66..1cee1091b 100644
--- a/src/bun.js/bindings/CommonJSModuleRecord.cpp
+++ b/src/bun.js/bindings/CommonJSModuleRecord.cpp
@@ -68,9 +68,6 @@
namespace Bun {
using namespace JSC;
-static Structure* internalCreateCommonJSModuleStructure(
- Zig::GlobalObject* globalObject);
-
class JSCommonJSModule final : public JSC::JSNonFinalObject {
public:
using Base = JSC::JSNonFinalObject;
@@ -78,15 +75,13 @@ public:
mutable JSC::WriteBarrier<JSC::Unknown> m_exportsObject;
mutable JSC::WriteBarrier<JSC::JSString> m_id;
- mutable JSC::WriteBarrier<JSC::EvalExecutable> m_executable;
- void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, JSC::JSValue requireFunction, JSC::EvalExecutable* executable)
+ void finishCreation(JSC::VM& vm, JSC::JSValue exportsObject, JSC::JSString* id, JSC::JSString* filename, JSC::JSString* dirname, JSC::JSValue requireFunction)
{
Base::finishCreation(vm);
ASSERT(inherits(vm, info()));
m_exportsObject.set(vm, this, exportsObject);
m_id.set(vm, this, id);
- m_executable.set(vm, this, executable);
this->putDirectOffset(
vm,
@@ -101,15 +96,83 @@ public:
this->putDirectOffset(
vm,
2,
- id);
+ filename);
+
this->putDirectOffset(
vm,
3,
- filename);
+ jsBoolean(false));
+
this->putDirectOffset(
vm,
4,
- requireFunction);
+ dirname);
+
+ this->putDirectOffset(
+ vm,
+ 5,
+ jsUndefined());
+ }
+
+ static JSC::Structure* createStructure(
+ JSC::JSGlobalObject* globalObject)
+ {
+ auto& vm = globalObject->vm();
+ JSC::Structure* structure = JSC::Structure::create(
+ vm,
+ globalObject,
+ globalObject->objectPrototype(),
+ JSC::TypeInfo(JSC::ObjectType, JSCommonJSModule::StructureFlags),
+ JSCommonJSModule::info(),
+ JSC::NonArray,
+ 6);
+
+ JSC::PropertyOffset offset;
+ auto clientData = WebCore::clientData(vm);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "exports"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "id"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "filename"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "loaded"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "path"_s),
+ 0,
+ offset);
+
+ structure = structure->addPropertyTransition(
+ vm,
+ structure,
+ JSC::Identifier::fromString(vm, "require"_s),
+ 0,
+ offset);
+
+ return structure;
}
static JSCommonJSModule* create(
@@ -118,11 +181,11 @@ public:
JSC::JSValue exportsObject,
JSC::JSString* id,
JSC::JSString* filename,
- JSC::JSValue requireFunction,
- JSC::EvalExecutable* executable)
+ JSC::JSString* dirname,
+ JSC::JSValue requireFunction)
{
JSCommonJSModule* cell = new (NotNull, JSC::allocateCell<JSCommonJSModule>(vm)) JSCommonJSModule(vm, structure);
- cell->finishCreation(vm, exportsObject, id, filename, requireFunction, executable);
+ cell->finishCreation(vm, exportsObject, id, filename, dirname, requireFunction);
return cell;
}
@@ -145,34 +208,34 @@ public:
JSC::JSValue value,
JSC::PutPropertySlot& slot)
{
- JSCommonJSModule* thisObject = jsCast<JSCommonJSModule*>(cell);
- ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+
auto& vm = globalObject->vm();
+ auto* clientData = WebCore::clientData(vm);
auto throwScope = DECLARE_THROW_SCOPE(vm);
- auto* clientData = WebCore::clientData(vm);
- bool result = Base::put(thisObject, globalObject, propertyName, value, slot);
- if (result) {
- // Whenever you call module.exports = ... in a module, we need to:
- //
- // - Update the internal exports object
- // - Update the require map
- //
- if (propertyName == clientData->builtinNames().exportsPublicName()) {
- thisObject->m_exportsObject.set(vm, thisObject, value);
- Zig::GlobalObject* zigGlobalObject = jsCast<Zig::GlobalObject*>(globalObject);
- zigGlobalObject->requireMap()->set(globalObject, thisObject->id(), value);
- RETURN_IF_EXCEPTION(throwScope, false);
+ if (propertyName == clientData->builtinNames().exportsPublicName()) {
+ JSCommonJSModule* thisObject = jsCast<JSCommonJSModule*>(cell);
+ ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+
+ // It will crash if we attempt to assign Object.defineProperty() result to a JSMap*.
+ if (UNLIKELY(slot.thisValue() != thisObject))
+ RELEASE_AND_RETURN(throwScope, JSObject::definePropertyOnReceiver(globalObject, propertyName, value, slot));
+
+ JSValue prevValue = thisObject->m_exportsObject.get();
+
+ // TODO: refactor this to not go through ESM path and we don't need to do this check.
+ // IF we do this on every call, it causes GC to happen in a place that it may not be able to.
+ // This breaks loading Bluebird in some cases, for example.
+ // We need to update the require map "live" because otherwise the code in Discord.js will break
+ // The bug is something to do with exception handling which causes GC to happen in the error path and then boom.
+ if (prevValue != value && (!prevValue.isCell() || !value.isCell() || prevValue.asCell()->type() != value.asCell()->type())) {
+ jsCast<Zig::GlobalObject*>(globalObject)->requireMap()->set(globalObject, thisObject->id(), value);
}
+
+ thisObject->m_exportsObject.set(vm, thisObject, value);
}
- RELEASE_AND_RETURN(throwScope, result);
- }
-
- static JSC::Structure* createStructure(
- JSC::JSGlobalObject* globalObject)
- {
- return internalCreateCommonJSModuleStructure(reinterpret_cast<Zig::GlobalObject*>(globalObject));
+ RELEASE_AND_RETURN(throwScope, Base::put(cell, globalObject, propertyName, value, slot));
}
DECLARE_INFO;
@@ -200,53 +263,6 @@ Structure* createCommonJSModuleStructure(
return JSCommonJSModule::createStructure(globalObject);
}
-static Structure* internalCreateCommonJSModuleStructure(
- Zig::GlobalObject* globalObject)
-{
- auto& vm = globalObject->vm();
- JSC::Structure* structure = JSC::Structure::create(
- vm,
- globalObject,
- globalObject->objectPrototype(),
- JSC::TypeInfo(JSC::ObjectType, JSCommonJSModule::StructureFlags),
- JSCommonJSModule::info(),
- JSC::NonArray,
- 4);
-
- JSC::PropertyOffset offset;
- auto clientData = WebCore::clientData(vm);
-
- structure = structure->addPropertyTransition(
- vm,
- structure,
- JSC::Identifier::fromString(vm, "exports"_s),
- 0,
- offset);
-
- structure = structure->addPropertyTransition(
- vm,
- structure,
- JSC::Identifier::fromString(vm, "id"_s),
- 0,
- offset);
-
- structure = structure->addPropertyTransition(
- vm,
- structure,
- JSC::Identifier::fromString(vm, "filename"_s),
- 0,
- offset);
-
- structure = structure->addPropertyTransition(
- vm,
- structure,
- JSC::Identifier::fromString(vm, "require"_s),
- JSC::PropertyAttribute::Builtin | JSC::PropertyAttribute::Function | 0,
- offset);
-
- return structure;
-}
-
template<typename Visitor>
void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
{
@@ -255,34 +271,11 @@ void JSCommonJSModule::visitChildrenImpl(JSCell* cell, Visitor& visitor)
Base::visitChildren(thisObject, visitor);
visitor.append(thisObject->m_exportsObject);
visitor.append(thisObject->m_id);
- visitor.append(thisObject->m_executable);
}
DEFINE_VISIT_CHILDREN(JSCommonJSModule);
const JSC::ClassInfo JSCommonJSModule::s_info = { "Module"_s, &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSCommonJSModule) };
-JSCommonJSModule* createCommonJSModuleObject(
- Zig::GlobalObject* globalObject,
- const ResolvedSource& source,
- const WTF::String& sourceURL,
- JSC::JSValue exportsObjectValue,
- JSC::JSValue requireFunctionValue,
- JSC::EvalExecutable* executable,
- JSC::JSString* filename)
-{
- auto& vm = globalObject->vm();
- auto scope = DECLARE_THROW_SCOPE(vm);
- auto* jsSourceURL = JSC::jsString(vm, sourceURL);
-
- JSCommonJSModule* moduleObject = JSCommonJSModule::create(
- vm,
- globalObject->CommonJSModuleObjectStructure(),
- exportsObjectValue,
- jsSourceURL, filename, requireFunctionValue, executable);
-
- return moduleObject;
-}
-
static bool canPerformFastEnumeration(Structure* s)
{
if (s->typeInfo().overridesGetOwnPropertySlot())
@@ -300,172 +293,155 @@ static bool canPerformFastEnumeration(Structure* s)
return true;
}
-JSC::SourceCode createCommonJSModule(
+JSValue evaluateCommonJSModule(
Zig::GlobalObject* globalObject,
+ Ref<Zig::SourceProvider> sourceProvider,
+ const WTF::String& sourceURL,
ResolvedSource source)
{
- auto sourceURL = Zig::toStringCopy(source.source_url);
- auto sourceProvider = Zig::SourceProvider::create(globalObject, source, JSC::SourceProviderSourceType::Program);
+ auto& vm = globalObject->vm();
- return JSC::SourceCode(
- JSC::SyntheticSourceProvider::create(
- [source, sourceProvider = WTFMove(sourceProvider), sourceURL](JSC::JSGlobalObject* lexicalGlobalObject,
- JSC::Identifier moduleKey,
- Vector<JSC::Identifier, 4>& exportNames,
- JSC::MarkedArgumentBuffer& exportValues) -> void {
- auto* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);
- auto& vm = globalObject->vm();
+ auto throwScope = DECLARE_THROW_SCOPE(vm);
+ auto* requireMapKey = jsString(vm, sourceURL);
+
+ JSC::JSObject* exportsObject = source.commonJSExportsLen < 64
+ ? JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), source.commonJSExportsLen)
+ : JSC::constructEmptyObject(globalObject, globalObject->objectPrototype());
+ auto index = sourceURL.reverseFind('/', sourceURL.length());
+ JSString* dirname = jsEmptyString(vm);
+ JSString* filename = requireMapKey;
+ if (index != WTF::notFound) {
+ dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index);
+ }
- auto throwScope = DECLARE_THROW_SCOPE(vm);
- auto* requireMapKey = jsString(vm, sourceURL);
-
- JSC::JSObject* exportsObject = source.commonJSExportsLen < 64
- ? JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), source.commonJSExportsLen)
- : JSC::constructEmptyObject(globalObject, globalObject->objectPrototype());
- auto index = sourceURL.reverseFind('/', sourceURL.length());
- JSString* dirname = jsEmptyString(vm);
- JSString* filename = requireMapKey;
- if (index != WTF::notFound) {
- dirname = JSC::jsSubstring(globalObject, requireMapKey, 0, index);
- }
+ globalObject->requireMap()->set(globalObject, requireMapKey, exportsObject);
+ auto* requireFunction = Zig::ImportMetaObject::createRequireFunction(vm, globalObject, sourceURL);
- globalObject->requireMap()->set(globalObject, requireMapKey, exportsObject);
- JSC::SourceCode inputSource(
- WTFMove(sourceProvider));
+ JSC::SourceCode inputSource(
+ WTFMove(sourceProvider));
- JSC::Structure* scopeExtensionObjectStructure = globalObject->commonJSFunctionArgumentsStructure();
- JSC::JSObject* scopeExtensionObject = JSC::constructEmptyObject(
- vm,
- scopeExtensionObjectStructure);
+ auto* moduleObject = JSCommonJSModule::create(
+ vm,
+ globalObject->CommonJSModuleObjectStructure(),
+ exportsObject,
+ requireMapKey, filename, dirname, requireFunction);
- auto* requireFunction = Zig::ImportMetaObject::createRequireFunction(vm, globalObject, sourceURL);
- auto* executable = JSC::DirectEvalExecutable::create(
- globalObject, inputSource, DerivedContextType::None, NeedsClassFieldInitializer::No, PrivateBrandRequirement::None,
- false, false, EvalContextType::None, nullptr, nullptr, ECMAMode::sloppy());
+ if (UNLIKELY(throwScope.exception())) {
+ globalObject->requireMap()->remove(globalObject, requireMapKey);
+ RELEASE_AND_RETURN(throwScope, JSValue());
+ }
- if (UNLIKELY(!executable && !throwScope.exception())) {
- // I'm not sure if this case happens, but it's better to be safe than sorry.
- throwSyntaxError(globalObject, throwScope, "Failed to compile CommonJS module."_s);
- }
+ JSC::Structure* thisObjectStructure = globalObject->commonJSFunctionArgumentsStructure();
+ JSC::JSObject* thisObject = JSC::constructEmptyObject(
+ vm,
+ thisObjectStructure);
+ thisObject->putDirectOffset(
+ vm,
+ 0,
+ moduleObject);
- if (UNLIKELY(throwScope.exception())) {
- globalObject->requireMap()->remove(globalObject, requireMapKey);
- throwScope.release();
- return;
- }
+ thisObject->putDirectOffset(
+ vm,
+ 1,
+ exportsObject);
- auto* moduleObject = createCommonJSModuleObject(globalObject,
- source,
- sourceURL,
- exportsObject,
- requireFunction, executable, filename);
-
- scopeExtensionObject->putDirectOffset(
- vm,
- 0,
- moduleObject);
-
- scopeExtensionObject->putDirectOffset(
- vm,
- 1,
- exportsObject);
-
- scopeExtensionObject->putDirectOffset(
- vm,
- 2,
- dirname);
-
- scopeExtensionObject->putDirectOffset(
- vm,
- 3,
- filename);
-
- scopeExtensionObject->putDirectOffset(
- vm,
- 4,
- requireFunction);
-
- if (UNLIKELY(throwScope.exception())) {
- globalObject->requireMap()->remove(globalObject, requireMapKey);
- throwScope.release();
- return;
- }
+ thisObject->putDirectOffset(
+ vm,
+ 2,
+ dirname);
- auto catchScope = DECLARE_CATCH_SCOPE(vm);
-
- // Where the magic happens.
- //
- // A `with` scope is created containing { module, exports, require }.
- // We eval() the CommonJS module code
- // with that scope.
- //
- // Doing it that way saves us a roundtrip through C++ <> JS.
- //
- // Sidenote: another implementation could use
- // FunctionExecutable. It looks like there are lots of arguments
- // to pass to that and it isn't used directly much, so that
- // seems harder to do correctly.
- {
- // We must use a global scope extension or else the JSWithScope will be collected unexpectedly.
- // https://github.com/oven-sh/bun/issues/3161
- globalObject->clearGlobalScopeExtension();
-
- JSWithScope* withScope = JSWithScope::create(vm, globalObject, globalObject->globalScope(), scopeExtensionObject);
- globalObject->setGlobalScopeExtension(withScope);
- vm.interpreter.executeEval(executable, globalObject, globalObject->globalScope());
- globalObject->clearGlobalScopeExtension();
-
- if (UNLIKELY(catchScope.exception())) {
- auto returnedException = catchScope.exception();
- catchScope.clearException();
- JSC::throwException(globalObject, throwScope, returnedException);
- }
- }
+ thisObject->putDirectOffset(
+ vm,
+ 3,
+ filename);
- if (throwScope.exception()) {
- globalObject->requireMap()->remove(globalObject, requireMapKey);
- throwScope.release();
- return;
- }
+ thisObject->putDirectOffset(
+ vm,
+ 4,
+ requireFunction);
- JSValue result = moduleObject->exportsObject();
-
- // The developer can do something like:
- //
- // Object.defineProperty(module, 'exports', {get: getter})
- //
- // In which case, the exports object is now a GetterSetter object.
- //
- // We can't return a GetterSetter object to ESM code, so we need to call it.
- if (!result.isEmpty() && (result.isGetterSetter() || result.isCustomGetterSetter())) {
- auto* clientData = WebCore::clientData(vm);
-
- // TODO: is there a faster way to call these getters? We shouldn't need to do a full property lookup.
- //
- // we use getIfPropertyExists just incase a pathological devleoper did:
- //
- // - Object.defineProperty(module, 'exports', {get: getter})
- // - delete module.exports
- //
- if (result.isGetterSetter()) {
- JSC::GetterSetter* getter = jsCast<JSC::GetterSetter*>(result);
- result = getter->callGetter(globalObject, moduleObject);
- } else {
- result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName());
- }
+ {
+ WTF::NakedPtr<Exception> exception;
+ globalObject->m_BunCommonJSModuleValue.set(vm, globalObject, thisObject);
+ JSC::evaluate(globalObject, inputSource, globalObject->globalThis(), exception);
+
+ if (exception.get()) {
+ throwScope.throwException(globalObject, exception->value());
+ exception.clear();
+ RELEASE_AND_RETURN(throwScope, JSValue());
+ }
+ }
- if (UNLIKELY(throwScope.exception())) {
- // Unlike getters on properties of the exports object
- // When the exports object itself is a getter and it throws
- // There's not a lot we can do
- // so we surface that error
- globalObject->requireMap()->remove(globalObject, requireMapKey);
- throwScope.release();
- return;
- }
+ if (UNLIKELY(throwScope.exception())) {
+ globalObject->requireMap()->remove(globalObject, requireMapKey);
+ RELEASE_AND_RETURN(throwScope, JSValue());
+ }
+
+ JSValue result = moduleObject->exportsObject();
+
+ // The developer can do something like:
+ //
+ // Object.defineProperty(module, 'exports', {get: getter})
+ //
+ // In which case, the exports object is now a GetterSetter object.
+ //
+ // We can't return a GetterSetter object to ESM code, so we need to call it.
+ if (!result.isEmpty() && (result.isGetterSetter() || result.isCustomGetterSetter())) {
+ auto* clientData = WebCore::clientData(vm);
+
+ // TODO: is there a faster way to call these getters? We shouldn't need to do a full property lookup.
+ //
+ // we use getIfPropertyExists just incase a pathological devleoper did:
+ //
+ // - Object.defineProperty(module, 'exports', {get: getter})
+ // - delete module.exports
+ //
+ if (result.isGetterSetter()) {
+ JSC::GetterSetter* getter = jsCast<JSC::GetterSetter*>(result);
+ result = getter->callGetter(globalObject, moduleObject);
+ } else {
+ result = moduleObject->getIfPropertyExists(globalObject, clientData->builtinNames().exportsPublicName());
+ }
+
+ if (UNLIKELY(throwScope.exception())) {
+ // Unlike getters on properties of the exports object
+ // When the exports object itself is a getter and it throws
+ // There's not a lot we can do
+ // so we surface that error
+ globalObject->requireMap()->remove(globalObject, requireMapKey);
+ RELEASE_AND_RETURN(throwScope, JSValue());
+ }
+ }
+
+ globalObject->requireMap()->set(globalObject, requireMapKey, result);
+
+ return result;
+}
+
+JSC::SourceCode createCommonJSModule(
+ Zig::GlobalObject* globalObject,
+ ResolvedSource source)
+{
+ auto sourceURL = Zig::toStringCopy(source.source_url);
+ auto sourceProvider = Zig::SourceProvider::create(globalObject, source, JSC::SourceProviderSourceType::Program);
+
+ return JSC::SourceCode(
+ JSC::SyntheticSourceProvider::create(
+ [source, sourceProvider = WTFMove(sourceProvider), sourceURL](JSC::JSGlobalObject* globalObject,
+ JSC::Identifier moduleKey,
+ Vector<JSC::Identifier, 4>& exportNames,
+ JSC::MarkedArgumentBuffer& exportValues) -> void {
+ JSValue result = evaluateCommonJSModule(
+ jsCast<Zig::GlobalObject*>(globalObject),
+ WTFMove(sourceProvider),
+ sourceURL,
+ source);
+
+ if (!result) {
+ return;
}
- globalObject->requireMap()->set(globalObject, requireMapKey, result);
+ auto& vm = globalObject->vm();
exportNames.append(vm.propertyNames->defaultKeyword);
exportValues.append(result);
@@ -474,9 +450,8 @@ JSC::SourceCode createCommonJSModule(
exportNames.append(Identifier::fromUid(vm.symbolRegistry().symbolForKey("CommonJS"_s)));
exportValues.append(jsNumber(0));
- moduleObject->m_executable.clear();
-
if (result.isObject()) {
+ DeferGCForAWhile deferGC(vm);
auto* exports = asObject(result);
auto* structure = exports->structure();
@@ -498,22 +473,20 @@ JSC::SourceCode createCommonJSModule(
return true;
});
} else {
+ auto catchScope = DECLARE_CATCH_SCOPE(vm);
JSC::PropertyNameArray properties(vm, JSC::PropertyNameMode::Strings, JSC::PrivateSymbolMode::Exclude);
exports->methodTable()->getOwnPropertyNames(exports, globalObject, properties, DontEnumPropertiesMode::Exclude);
- if (throwScope.exception()) {
- throwScope.release();
+ if (catchScope.exception()) {
+ catchScope.clearExceptionExceptTermination();
return;
}
for (auto property : properties) {
- if (UNLIKELY(property.isEmpty() || property.isNull()))
+ if (UNLIKELY(property.isEmpty() || property.isNull() || property.isPrivateName() || property.isSymbol()))
continue;
// ignore constructor
- if (property == vm.propertyNames->constructor)
- continue;
-
- if (property.isSymbol() || property.isPrivateName() || property == vm.propertyNames->defaultKeyword)
+ if (property == vm.propertyNames->constructor || property == vm.propertyNames->defaultKeyword)
continue;
JSC::PropertySlot slot(exports, PropertySlot::InternalMethodType::Get);
diff --git a/src/bun.js/bindings/ErrorStackTrace.h b/src/bun.js/bindings/ErrorStackTrace.h
index a8a6a192f..1284376a4 100644
--- a/src/bun.js/bindings/ErrorStackTrace.h
+++ b/src/bun.js/bindings/ErrorStackTrace.h
@@ -94,7 +94,6 @@ public:
bool hasBytecodeIndex() const { return (m_bytecodeIndex.offset() != UINT_MAX) && !m_isWasmFrame; }
JSC::BytecodeIndex bytecodeIndex() const
{
- ASSERT(hasBytecodeOffset());
return m_bytecodeIndex;
}
diff --git a/src/bun.js/bindings/ExceptionOr.h b/src/bun.js/bindings/ExceptionOr.h
index f3378466e..3bbc641a1 100644
--- a/src/bun.js/bindings/ExceptionOr.h
+++ b/src/bun.js/bindings/ExceptionOr.h
@@ -116,25 +116,21 @@ template<typename ReturnType> inline bool ExceptionOr<ReturnType>::hasException(
template<typename ReturnType> inline const Exception& ExceptionOr<ReturnType>::exception() const
{
- ASSERT(!m_wasReleased);
return m_value.error();
}
template<typename ReturnType> inline Exception ExceptionOr<ReturnType>::releaseException()
{
- ASSERT(!std::exchange(m_wasReleased, true));
return WTFMove(m_value.error());
}
template<typename ReturnType> inline const ReturnType& ExceptionOr<ReturnType>::returnValue() const
{
- ASSERT(!m_wasReleased);
return m_value.value();
}
template<typename ReturnType> inline ReturnType ExceptionOr<ReturnType>::releaseReturnValue()
{
- ASSERT(!std::exchange(m_wasReleased, true));
return WTFMove(m_value.value());
}
@@ -185,13 +181,11 @@ inline bool ExceptionOr<void>::hasException() const
inline const Exception& ExceptionOr<void>::exception() const
{
- ASSERT(!m_wasReleased);
return m_value.error();
}
inline Exception ExceptionOr<void>::releaseException()
{
- ASSERT(!std::exchange(m_wasReleased, true));
return WTFMove(m_value.error());
}
diff --git a/src/bun.js/bindings/JSBundlerPlugin.cpp b/src/bun.js/bindings/JSBundlerPlugin.cpp
index e93556963..cae6a4b22 100644
--- a/src/bun.js/bindings/JSBundlerPlugin.cpp
+++ b/src/bun.js/bindings/JSBundlerPlugin.cpp
@@ -239,7 +239,7 @@ JSC_DEFINE_HOST_FUNCTION(jsBundlerPluginFunction_onResolveAsync, (JSC::JSGlobalO
void JSBundlerPlugin::finishCreation(JSC::VM& vm)
{
Base::finishCreation(vm);
- ASSERT(inherits(vm, info()));
+ ASSERT(inherits(info()));
this->onLoadFunction.initLater(
[](const JSC::LazyProperty<JSBundlerPlugin, JSC::JSFunction>::Initializer& init) {
auto& vm = init.vm;
diff --git a/src/bun.js/bindings/Process.cpp b/src/bun.js/bindings/Process.cpp
index ad8e5d073..69ee11e60 100644
--- a/src/bun.js/bindings/Process.cpp
+++ b/src/bun.js/bindings/Process.cpp
@@ -825,16 +825,12 @@ JSC_DEFINE_CUSTOM_GETTER(Process_getArgv, (JSC::JSGlobalObject * globalObject, J
if (!thisObject) {
return JSValue::encode(JSC::jsUndefined());
}
- auto clientData = WebCore::clientData(vm);
-
- if (JSC::JSValue argv = thisObject->getIfPropertyExists(
- globalObject, clientData->builtinNames().argvPrivateName())) {
- return JSValue::encode(argv);
- }
JSC::EncodedJSValue argv_ = Bun__Process__getArgv(globalObject);
- thisObject->putDirect(vm, clientData->builtinNames().argvPrivateName(),
- JSC::JSValue::decode(argv_));
+ auto clientData = WebCore::clientData(vm);
+
+ thisObject->putDirect(vm, clientData->builtinNames().argvPublicName(),
+ JSC::JSValue::decode(argv_), 0);
return argv_;
}
@@ -852,7 +848,7 @@ JSC_DEFINE_CUSTOM_SETTER(Process_setArgv,
auto clientData = WebCore::clientData(vm);
- return thisObject->putDirect(vm, clientData->builtinNames().argvPrivateName(),
+ return thisObject->putDirect(vm, clientData->builtinNames().argvPublicName(),
JSC::JSValue::decode(value));
}
diff --git a/src/bun.js/bindings/ZigGlobalObject.cpp b/src/bun.js/bindings/ZigGlobalObject.cpp
index f2f5a372b..299ad7a8c 100644
--- a/src/bun.js/bindings/ZigGlobalObject.cpp
+++ b/src/bun.js/bindings/ZigGlobalObject.cpp
@@ -3168,6 +3168,15 @@ extern "C" void Bun__setOnEachMicrotaskTick(JSC::VM* vm, void* ptr, void (*callb
});
}
+JSC_DEFINE_CUSTOM_GETTER(BunCommonJSModule_getter, (JSGlobalObject * globalObject, EncodedJSValue thisValue, PropertyName))
+{
+ Zig::GlobalObject* bunGlobalObject = jsCast<Zig::GlobalObject*>(globalObject);
+ JSValue returnValue = bunGlobalObject->m_BunCommonJSModuleValue.get();
+ if (!returnValue) {
+ returnValue = jsUndefined();
+ }
+ return JSValue::encode(returnValue);
+}
// This implementation works the same as setTimeout(myFunction, 0)
// TODO: make it more efficient
// https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
@@ -3526,6 +3535,9 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "CloseEvent"_s), JSC::CustomGetterSetter::create(vm, JSCloseEvent_getter, nullptr),
JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
+ putDirectCustomAccessor(vm, JSC::Identifier::fromString(vm, "$_BunCommonJSModule_$"_s), JSC::CustomGetterSetter::create(vm, BunCommonJSModule_getter, nullptr),
+ JSC::PropertyAttribute::DontEnum | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
+
auto bufferAccessor = JSC::CustomGetterSetter::create(vm, JSBuffer_getter, JSBuffer_setter);
auto realBufferAccessor = JSC::CustomGetterSetter::create(vm, JSBuffer_privateGetter, nullptr);
@@ -3556,7 +3568,6 @@ void GlobalObject::addBuiltinGlobals(JSC::VM& vm)
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().WritableStreamDefaultControllerPrivateName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_WritableStreamDefaultControllerConstructor, nullptr), attributesForStructure(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly));
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().WritableStreamDefaultWriterPrivateName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_WritableStreamDefaultWriterConstructor, nullptr), attributesForStructure(JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly));
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().AbortSignalPrivateName(), CustomGetterSetter::create(vm, JSDOMAbortSignal_getter, nullptr), JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
- putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().TransformStreamDefaultControllerPublicName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_TransformStreamDefaultControllerConstructor, nullptr), static_cast<unsigned>(JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontEnum));
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().ReadableByteStreamControllerPublicName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_ReadableByteStreamControllerConstructor, nullptr), JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().ReadableStreamPublicName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_ReadableStreamConstructor, nullptr), JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
putDirectCustomAccessor(vm, static_cast<JSVMClientData*>(vm.clientData)->builtinNames().ReadableStreamBYOBReaderPublicName(), CustomGetterSetter::create(vm, jsServiceWorkerGlobalScope_ReadableStreamBYOBReaderConstructor, nullptr), JSC::PropertyAttribute::CustomAccessor | JSC::PropertyAttribute::DontDelete | JSC::PropertyAttribute::ReadOnly);
@@ -3936,6 +3947,7 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
}
thisObject->visitGeneratedLazyClasses<Visitor>(thisObject, visitor);
+ visitor.append(thisObject->m_BunCommonJSModuleValue);
ScriptExecutionContext* context = thisObject->scriptExecutionContext();
visitor.addOpaqueRoot(context);
diff --git a/src/bun.js/bindings/ZigGlobalObject.h b/src/bun.js/bindings/ZigGlobalObject.h
index d5f933540..dda1c8330 100644
--- a/src/bun.js/bindings/ZigGlobalObject.h
+++ b/src/bun.js/bindings/ZigGlobalObject.h
@@ -363,6 +363,7 @@ public:
mutable WriteBarrier<Unknown> m_JSURLSearchParamsSetterValue;
mutable WriteBarrier<Unknown> m_JSWebSocketSetterValue;
mutable WriteBarrier<Unknown> m_JSDOMFormDataSetterValue;
+ mutable WriteBarrier<Unknown> m_BunCommonJSModuleValue;
mutable WriteBarrier<JSFunction> m_thenables[promiseFunctionsSize + 1];
diff --git a/src/bun.js/bindings/webcore/EventEmitter.cpp b/src/bun.js/bindings/webcore/EventEmitter.cpp
index 4ea10587e..0650d624c 100644
--- a/src/bun.js/bindings/webcore/EventEmitter.cpp
+++ b/src/bun.js/bindings/webcore/EventEmitter.cpp
@@ -171,7 +171,6 @@ Vector<JSObject*> EventEmitter::getListeners(const Identifier& eventType)
// https://dom.spec.whatwg.org/#concept-event-listener-invoke
void EventEmitter::fireEventListeners(const Identifier& eventType, const MarkedArgumentBuffer& arguments)
{
- ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
auto* data = eventTargetData();
if (!data)
diff --git a/src/bun.js/bindings/webcore/EventTarget.cpp b/src/bun.js/bindings/webcore/EventTarget.cpp
index cc2113ec9..9fb875595 100644
--- a/src/bun.js/bindings/webcore/EventTarget.cpp
+++ b/src/bun.js/bindings/webcore/EventTarget.cpp
@@ -261,7 +261,6 @@ static const AtomString& legacyType(const Event& event)
// https://dom.spec.whatwg.org/#concept-event-listener-invoke
void EventTarget::fireEventListeners(Event& event, EventInvokePhase phase)
{
- ASSERT_WITH_SECURITY_IMPLICATION(ScriptDisallowedScope::isEventAllowedInMainThread());
ASSERT(event.isInitialized());
auto* data = eventTargetData();
diff --git a/src/bun.js/bindings/webcore/Node.h b/src/bun.js/bindings/webcore/Node.h
index df9917a25..509e04192 100644
--- a/src/bun.js/bindings/webcore/Node.h
+++ b/src/bun.js/bindings/webcore/Node.h
@@ -75,42 +75,20 @@ public:
// mutable OptionSet<NodeFlag> m_nodeFlags;
};
-#if ASSERT_ENABLED
-
-inline void adopted(Node* node)
-{
- if (!node)
- return;
- ASSERT(!node->m_deletionHasBegun);
- ASSERT(!node->m_inRemovedLastRefFunction);
- node->m_adoptionIsRequired = false;
-}
-
-#endif // ASSERT_ENABLED
-
ALWAYS_INLINE void Node::ref() const
{
- ASSERT(isMainThread());
- ASSERT(!m_deletionHasBegun);
- ASSERT(!m_inRemovedLastRefFunction);
- ASSERT(!m_adoptionIsRequired);
+
m_refCountAndParentBit += s_refCountIncrement;
}
ALWAYS_INLINE void Node::deref() const
{
- ASSERT(isMainThread());
- ASSERT(refCount());
- ASSERT(!m_deletionHasBegun);
- ASSERT(!m_inRemovedLastRefFunction);
- ASSERT(!m_adoptionIsRequired);
+
auto updatedRefCount = m_refCountAndParentBit - s_refCountIncrement;
if (!updatedRefCount) {
// Don't update m_refCountAndParentBit to avoid double destruction through use of Ref<T>/RefPtr<T>.
// (This is a security mitigation in case of programmer error. It will ASSERT in debug builds.)
-#if ASSERT_ENABLED
- m_inRemovedLastRefFunction = true;
-#endif
+
const_cast<Node&>(*this).removedLastRef();
return;
}
@@ -119,8 +97,7 @@ ALWAYS_INLINE void Node::deref() const
ALWAYS_INLINE bool Node::hasOneRef() const
{
- ASSERT(!m_deletionHasBegun);
- ASSERT(!m_inRemovedLastRefFunction);
+
return refCount() == 1;
}
diff --git a/src/bun.js/bindings/webcore/WebSocket.cpp b/src/bun.js/bindings/webcore/WebSocket.cpp
index e19145fc7..a346175df 100644
--- a/src/bun.js/bindings/webcore/WebSocket.cpp
+++ b/src/bun.js/bindings/webcore/WebSocket.cpp
@@ -267,7 +267,7 @@ static String resourceName(const URL& url)
static String hostName(const URL& url, bool secure)
{
- ASSERT(url.protocolIs("wss") == secure);
+ // ASSERT(url.protocolIs("wss"_s) == secure);
if (url.port() && ((!secure && url.port().value() != 80) || (secure && url.port().value() != 443)))
return makeString(asASCIILowercase(url.host()), ':', url.port().value());
return url.host().convertToASCIILowercase();
@@ -280,7 +280,7 @@ ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& pr
ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& protocols, std::optional<FetchHeaders::Init>&& headersInit)
{
- LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
+ // LOG(Network, "WebSocket %p connect() url='%s'", this, url.utf8().data());
m_url = URL { url };
ASSERT(scriptExecutionContext());
@@ -446,7 +446,7 @@ ExceptionOr<void> WebSocket::connect(const String& url, const Vector<String>& pr
ExceptionOr<void> WebSocket::send(const String& message)
{
- LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
+ // LOG(Network, "WebSocket %p send() Sending String '%s'", this, message.utf8().data());
if (m_state == CONNECTING)
return Exception { InvalidStateError };
// No exception is raised if the connection was once established but has subsequently been closed.
@@ -466,7 +466,7 @@ ExceptionOr<void> WebSocket::send(const String& message)
ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData)
{
- LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, &binaryData);
+ // LOG(Network, "WebSocket %p send() Sending ArrayBuffer %p", this, &binaryData);
if (m_state == CONNECTING)
return Exception { InvalidStateError };
if (m_state == CLOSING || m_state == CLOSED) {
@@ -484,7 +484,7 @@ ExceptionOr<void> WebSocket::send(ArrayBuffer& binaryData)
ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
{
- LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, &arrayBufferView);
+ // LOG(Network, "WebSocket %p send() Sending ArrayBufferView %p", this, &arrayBufferView);
if (m_state == CONNECTING)
return Exception { InvalidStateError };
@@ -506,7 +506,7 @@ ExceptionOr<void> WebSocket::send(ArrayBufferView& arrayBufferView)
// ExceptionOr<void> WebSocket::send(Blob& binaryData)
// {
-// LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data());
+// LOG(Network, "WebSocket %p send() Sending Blob '%s'", this, binaryData.url().stringCenterEllipsizedToLength().utf8().data());
// if (m_state == CONNECTING)
// return Exception { InvalidStateError };
// if (m_state == CLOSING || m_state == CLOSED) {
@@ -587,10 +587,10 @@ void WebSocket::sendWebSocketString(const String& message)
ExceptionOr<void> WebSocket::close(std::optional<unsigned short> optionalCode, const String& reason)
{
int code = optionalCode ? optionalCode.value() : static_cast<int>(0);
- if (code == 0)
- LOG(Network, "WebSocket %p close() without code and reason", this);
- else {
- LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
+ if (code == 0) {
+ // LOG(Network, "WebSocket %p close() without code and reason", this);
+ } else {
+ // LOG(Network, "WebSocket %p close() code=%d reason='%s'", this, code, reason.utf8().data());
// if (!(code == WebSocketChannel::CloseEventCodeNormalClosure || (WebSocketChannel::CloseEventCodeMinimumUserDefined <= code && code <= WebSocketChannel::CloseEventCodeMaximumUserDefined)))
// return Exception { InvalidAccessError };
if (reason.length() > maxReasonSizeInBytes) {
@@ -718,7 +718,7 @@ ScriptExecutionContext* WebSocket::scriptExecutionContext() const
// void WebSocket::contextDestroyed()
// {
-// LOG(Network, "WebSocket %p contextDestroyed()", this);
+// LOG(Network, "WebSocket %p contextDestroyed()", this);
// ASSERT(!m_channel);
// ASSERT(m_state == CLOSED);
// // ActiveDOMObject::contextDestroyed();
@@ -763,7 +763,7 @@ void WebSocket::didConnect()
{
// from new WebSocket() -> connect()
- LOG(Network, "WebSocket %p didConnect()", this);
+ // LOG(Network, "WebSocket %p didConnect()", this);
// queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this] {
if (m_state == CLOSED)
return;
@@ -797,7 +797,7 @@ void WebSocket::didConnect()
void WebSocket::didReceiveMessage(String&& message)
{
- LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, message.utf8().data());
+ // LOG(Network, "WebSocket %p didReceiveMessage() Text message '%s'", this, message.utf8().data());
// queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, message = WTFMove(message)]() mutable {
if (m_state != OPEN)
return;
@@ -831,7 +831,7 @@ void WebSocket::didReceiveMessage(String&& message)
void WebSocket::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
{
- LOG(Network, "WebSocket %p didReceiveBinaryData() %u byte binary message", this, static_cast<unsigned>(binaryData.size()));
+ // LOG(Network, "WebSocket %p didReceiveBinaryData() %u byte binary message", this, static_cast<unsigned>(binaryData.size()));
// queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, binaryData = WTFMove(binaryData)]() mutable {
if (m_state != OPEN)
return;
@@ -916,7 +916,7 @@ void WebSocket::didReceiveBinaryData(Vector<uint8_t>&& binaryData)
void WebSocket::didReceiveMessageError(unsigned short code, WTF::String reason)
{
- LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
+ // LOG(Network, "WebSocket %p didReceiveErrorMessage()", this);
// queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this, reason = WTFMove(reason)] {
if (m_state == CLOSED)
return;
@@ -931,7 +931,7 @@ void WebSocket::didReceiveMessageError(unsigned short code, WTF::String reason)
void WebSocket::didUpdateBufferedAmount(unsigned bufferedAmount)
{
- LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %u", this, bufferedAmount);
+ // LOG(Network, "WebSocket %p didUpdateBufferedAmount() New bufferedAmount is %u", this, bufferedAmount);
if (m_state == CLOSED)
return;
m_bufferedAmount = bufferedAmount;
@@ -939,7 +939,7 @@ void WebSocket::didUpdateBufferedAmount(unsigned bufferedAmount)
void WebSocket::didStartClosingHandshake()
{
- LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
+ // LOG(Network, "WebSocket %p didStartClosingHandshake()", this);
// queueTaskKeepingObjectAlive(*this, TaskSource::WebSocket, [this] {
if (m_state == CLOSED)
return;
@@ -950,7 +950,7 @@ void WebSocket::didStartClosingHandshake()
void WebSocket::didClose(unsigned unhandledBufferedAmount, unsigned short code, const String& reason)
{
- LOG(Network, "WebSocket %p didClose()", this);
+ // LOG(Network, "WebSocket %p didClose()", this);
if (this->m_connectedWebSocketKind == ConnectedWebSocketKind::None)
return;
diff --git a/src/cli/init_command.zig b/src/cli/init_command.zig
index bc8867368..c7f566836 100644
--- a/src/cli/init_command.zig
+++ b/src/cli/init_command.zig
@@ -300,13 +300,7 @@ pub const InitCommand = struct {
if (needs_dev_dependencies) {
var dev_dependencies = fields.object.get("devDependencies") orelse js_ast.Expr.init(js_ast.E.Object, js_ast.E.Object{}, logger.Loc.Empty);
- const version = comptime brk: {
- var base = Global.version;
- base.patch = 0;
- break :brk base;
- };
-
- try dev_dependencies.data.e_object.putString(alloc, "bun-types", comptime std.fmt.comptimePrint("^{any}", .{version.fmt("")}));
+ try dev_dependencies.data.e_object.putString(alloc, "bun-types", "latest");
try fields.object.put(alloc, "devDependencies", dev_dependencies);
}
diff --git a/src/js_parser.zig b/src/js_parser.zig
index 7c227ce75..5a9bca91a 100644
--- a/src/js_parser.zig
+++ b/src/js_parser.zig
@@ -5798,8 +5798,7 @@ fn NewParser_(
if (p.options.features.inlining) {
if (p.const_values.get(ref)) |replacement| {
- // TODO:
- // p.ignoreUsage(ref);
+ p.ignoreUsage(ref);
return replacement;
}
}
@@ -20427,7 +20426,8 @@ fn NewParser_(
var end: usize = 0;
for (decls) |decl| {
if (decl.binding.data == .b_identifier) {
- if (p.const_values.contains(decl.binding.data.b_identifier.ref)) {
+ const symbol = p.symbols.items[decl.binding.data.b_identifier.ref.innerIndex()];
+ if (p.const_values.contains(decl.binding.data.b_identifier.ref) and symbol.use_count_estimate == 0) {
continue;
}
}
@@ -21180,17 +21180,75 @@ fn NewParser_(
},
logger.Loc.Empty,
);
+ const cjsGlobal = p.newSymbol(.unbound, "$_BunCommonJSModule_$") catch unreachable;
var call_args = allocator.alloc(Expr, 6) catch unreachable;
+ const this_module = p.newExpr(
+ E.Dot{
+ .name = "module",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ );
//
- // (function(module, exports, require, __dirname, __filename) {}).call(exports, module, exports, require, __dirname, __filename)
+ // (function(module, exports, require, __dirname, __filename) {}).call(this.exports, this.module, this.exports, this.require, __dirname, __filename)
call_args[0..6].* = .{
- p.newExpr(E.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty),
- p.newExpr(E.Identifier{ .ref = p.module_ref }, logger.Loc.Empty),
- p.newExpr(E.Identifier{ .ref = p.exports_ref }, logger.Loc.Empty),
- p.newExpr(E.Identifier{ .ref = p.require_ref }, logger.Loc.Empty),
- p.newExpr(E.Identifier{ .ref = p.dirname_ref }, logger.Loc.Empty),
- p.newExpr(E.Identifier{ .ref = p.filename_ref }, logger.Loc.Empty),
+ p.newExpr(
+ E.Dot{
+ .name = "exports",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
+ this_module,
+ p.newExpr(
+ E.Dot{
+ .name = "exports",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
+ p.newExpr(
+ E.Binary{
+ .left = p.newExpr(
+ E.Dot{
+ .name = "require",
+ .target = this_module,
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
+ .op = .bin_assign,
+ .right = p.newExpr(
+ E.Dot{
+ .name = "require",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
+ },
+ logger.Loc.Empty,
+ ),
+ p.newExpr(
+ E.Dot{
+ .name = "__dirname",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
+ p.newExpr(
+ E.Dot{
+ .name = "__filename",
+ .target = p.newExpr(E.Identifier{ .ref = cjsGlobal }, logger.Loc.Empty),
+ .name_loc = logger.Loc.Empty,
+ },
+ logger.Loc.Empty,
+ ),
};
const call = p.newExpr(
diff --git a/src/string.zig b/src/string.zig
index 62cdc5462..f09428f69 100644
--- a/src/string.zig
+++ b/src/string.zig
@@ -293,6 +293,7 @@ pub const String = extern struct {
) String;
pub fn createExternal(bytes: []const u8, isLatin1: bool, ctx: ?*anyopaque, callback: ?*const fn (*anyopaque, *anyopaque, u32) callconv(.C) void) String {
+ JSC.markBinding(@src());
return BunString__createExternal(bytes.ptr, bytes.len, isLatin1, ctx, callback);
}
@@ -348,6 +349,8 @@ pub const String = extern struct {
}
pub fn toWTF(this: *String) void {
+ JSC.markBinding(@src());
+
BunString__toWTFString(this);
}
diff --git a/test/bundler/esbuild/default.test.ts b/test/bundler/esbuild/default.test.ts
index 215276139..0d1775606 100644
--- a/test/bundler/esbuild/default.test.ts
+++ b/test/bundler/esbuild/default.test.ts
@@ -6520,4 +6520,46 @@ describe("bundler", () => {
`,
},
});
+ itBundled("default/ConstDeclNotRemovedIfReferencedBeforeDecl", {
+ files: {
+ "/entry.js": `
+ {
+ const foo = () => {
+ return data;
+ }
+ const data = 123;
+
+ console.log(foo());
+ }
+ `,
+ },
+ minifySyntax: true,
+ run: {
+ stdout: "123",
+ },
+ onAfterBundle(api) {
+ api.expectFile("/out.js").toContain("data = 123");
+ },
+ });
+ itBundled("default/ConstDeclRemovedIfReferencedBeforeAllUses", {
+ files: {
+ "/entry.js": `
+ {
+ const data = 123;
+ const foo = () => {
+ return data;
+ }
+
+ console.log(foo());
+ }
+ `,
+ },
+ minifySyntax: true,
+ run: {
+ stdout: "123",
+ },
+ onAfterBundle(api) {
+ api.expectFile("/out.js").not.toContain("data = 123");
+ },
+ });
});
diff --git a/test/js/bun/http/bun-server.test.ts b/test/js/bun/http/bun-server.test.ts
index 7d0915b89..37675c25d 100644
--- a/test/js/bun/http/bun-server.test.ts
+++ b/test/js/bun/http/bun-server.test.ts
@@ -1,6 +1,54 @@
import { describe, expect, test } from "bun:test";
describe("Server", () => {
+ test("should not allow Bun.serve without first argument being a object", () => {
+ expect(() => {
+ //@ts-ignore
+ const server = Bun.serve();
+ server.stop(true);
+ }).toThrow("Bun.serve expects an object");
+
+ [undefined, null, 1, "string", true, false, Symbol("symbol")].forEach(value => {
+ expect(() => {
+ //@ts-ignore
+ const server = Bun.serve(value);
+ server.stop(true);
+ }).toThrow("Bun.serve expects an object");
+ });
+ });
+
+ test("should not allow Bun.serve with invalid tls option", () => {
+ [1, "string", true, Symbol("symbol"), false].forEach(value => {
+ expect(() => {
+ const server = Bun.serve({
+ //@ts-ignore
+ tls: value,
+ fetch() {
+ return new Response("Hello");
+ },
+ port: 0,
+ });
+ server.stop(true);
+ }).toThrow("tls option expects an object");
+ });
+ });
+
+ test("should allow Bun.serve using null or undefined tls option", () => {
+ [null, undefined].forEach(value => {
+ expect(() => {
+ const server = Bun.serve({
+ //@ts-ignore
+ tls: value,
+ fetch() {
+ return new Response("Hello");
+ },
+ port: 0,
+ });
+ server.stop(true);
+ }).not.toThrow("tls option expects an object");
+ });
+ });
+
test("returns active port when initializing server with 0 port", () => {
const server = Bun.serve({
fetch() {
diff --git a/test/js/bun/net/tcp-server.test.ts b/test/js/bun/net/tcp-server.test.ts
index d029d9273..18fa29231 100644
--- a/test/js/bun/net/tcp-server.test.ts
+++ b/test/js/bun/net/tcp-server.test.ts
@@ -58,6 +58,44 @@ it("remoteAddress works", async () => {
await prom;
});
+it("should not allow invalid tls option", () => {
+ [1, "string", Symbol("symbol")].forEach(value => {
+ expect(() => {
+ // @ts-ignore
+ const server = Bun.listen({
+ socket: {
+ open(ws) {},
+ close() {},
+ data() {},
+ },
+ port: 0,
+ hostname: "localhost",
+ tls: value,
+ });
+ server.stop(true);
+ }).toThrow("tls option expects an object");
+ });
+});
+
+it("should allow using false, null or undefined tls option", () => {
+ [false, null, undefined].forEach(value => {
+ expect(() => {
+ // @ts-ignore
+ const server = Bun.listen({
+ socket: {
+ open(ws) {},
+ close() {},
+ data() {},
+ },
+ port: 0,
+ hostname: "localhost",
+ tls: value,
+ });
+ server.stop(true);
+ }).not.toThrow("tls option expects an object");
+ });
+});
+
it("echo server 1 on 1", async () => {
// wrap it in a separate closure so the GC knows to clean it up
// the sockets & listener don't escape the closure
diff --git a/test/js/node/process/process.test.js b/test/js/node/process/process.test.js
index f701be1b3..ee181e70c 100644
--- a/test/js/node/process/process.test.js
+++ b/test/js/node/process/process.test.js
@@ -1,6 +1,7 @@
import { resolveSync, which } from "bun";
import { describe, expect, it } from "bun:test";
import { existsSync, readFileSync, realpathSync } from "fs";
+import { bunExe } from "harness";
import { basename, resolve } from "path";
it("process", () => {
@@ -224,3 +225,12 @@ it("process.execArgv", () => {
it("process.binding", () => {
expect(() => process.binding("buffer")).toThrow();
});
+
+it("process.argv", () => {
+ expect(process.argv).toBeInstanceOf(Array);
+ expect(process.argv[0]).toBe(bunExe());
+ expect(process.argv).toEqual(Bun.argv);
+
+ // assert we aren't creating a new process.argv each call
+ expect(process.argv).toBe(process.argv);
+});