mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Co-authored-by: Dylan Conway <35280289+dylan-conway@users.noreply.github.com>
This commit is contained in:
@@ -427,15 +427,23 @@ pub const AsyncModule = struct {
|
||||
this.poll_ref.unref(jsc_vm);
|
||||
outer: {
|
||||
errorable = JSC.ErrorableResolvedSource.ok(this.resumeLoadingModule(&log) catch |err| {
|
||||
VirtualMachine.processFetchLog(
|
||||
this.globalThis,
|
||||
bun.String.init(this.specifier),
|
||||
bun.String.init(this.referrer),
|
||||
&log,
|
||||
&errorable,
|
||||
err,
|
||||
);
|
||||
break :outer;
|
||||
switch (err) {
|
||||
error.JSError => {
|
||||
errorable = .err(error.JSError, this.globalThis.takeError(error.JSError).asVoid());
|
||||
break :outer;
|
||||
},
|
||||
else => {
|
||||
VirtualMachine.processFetchLog(
|
||||
this.globalThis,
|
||||
bun.String.init(this.specifier),
|
||||
bun.String.init(this.referrer),
|
||||
&log,
|
||||
&errorable,
|
||||
err,
|
||||
);
|
||||
break :outer;
|
||||
},
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -455,7 +463,7 @@ pub const AsyncModule = struct {
|
||||
pub fn fulfill(
|
||||
globalThis: *JSGlobalObject,
|
||||
promise: JSValue,
|
||||
resolved_source: ResolvedSource,
|
||||
resolved_source: *ResolvedSource,
|
||||
err: ?anyerror,
|
||||
specifier_: bun.String,
|
||||
referrer_: bun.String,
|
||||
@@ -471,16 +479,27 @@ pub const AsyncModule = struct {
|
||||
|
||||
var errorable: JSC.ErrorableResolvedSource = undefined;
|
||||
if (err) |e| {
|
||||
VirtualMachine.processFetchLog(
|
||||
globalThis,
|
||||
specifier,
|
||||
referrer,
|
||||
log,
|
||||
&errorable,
|
||||
e,
|
||||
);
|
||||
defer {
|
||||
if (resolved_source.source_code_needs_deref) {
|
||||
resolved_source.source_code_needs_deref = false;
|
||||
resolved_source.source_code.deref();
|
||||
}
|
||||
}
|
||||
|
||||
if (e == error.JSError) {
|
||||
errorable = JSC.ErrorableResolvedSource.err(error.JSError, globalThis.takeError(error.JSError).asVoid());
|
||||
} else {
|
||||
VirtualMachine.processFetchLog(
|
||||
globalThis,
|
||||
specifier,
|
||||
referrer,
|
||||
log,
|
||||
&errorable,
|
||||
e,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
errorable = JSC.ErrorableResolvedSource.ok(resolved_source);
|
||||
errorable = JSC.ErrorableResolvedSource.ok(resolved_source.*);
|
||||
}
|
||||
log.deinit();
|
||||
|
||||
@@ -1782,17 +1801,21 @@ pub export fn Bun__transpileFile(
|
||||
globalObject,
|
||||
FetchFlags.transpile,
|
||||
) catch |err| {
|
||||
if (err == error.AsyncModule) {
|
||||
bun.assert(promise != null);
|
||||
return promise;
|
||||
switch (err) {
|
||||
error.AsyncModule => {
|
||||
bun.assert(promise != null);
|
||||
return promise;
|
||||
},
|
||||
error.PluginError => return null,
|
||||
error.JSError => {
|
||||
ret.* = JSC.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError).asVoid());
|
||||
return null;
|
||||
},
|
||||
else => {
|
||||
VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err);
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
if (err == error.PluginError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer.*, &log, ret, err);
|
||||
return null;
|
||||
},
|
||||
);
|
||||
return promise;
|
||||
@@ -1816,8 +1839,9 @@ export fn Bun__runVirtualModule(globalObject: *JSGlobalObject, specifier_ptr: *c
|
||||
else
|
||||
specifier[@min(namespace.len + 1, specifier.len)..];
|
||||
|
||||
return globalObject.runOnLoadPlugins(bun.String.init(namespace), bun.String.init(after_namespace), .bun) orelse
|
||||
return globalObject.runOnLoadPlugins(bun.String.init(namespace), bun.String.init(after_namespace), .bun) catch {
|
||||
return JSValue.zero;
|
||||
} orelse return .zero;
|
||||
}
|
||||
|
||||
fn getHardcodedModule(jsc_vm: *VirtualMachine, specifier: bun.String, hardcoded: HardcodedModule) ?ResolvedSource {
|
||||
@@ -1957,11 +1981,17 @@ export fn Bun__transpileVirtualModule(
|
||||
globalObject,
|
||||
FetchFlags.transpile,
|
||||
) catch |err| {
|
||||
if (err == error.PluginError) {
|
||||
return true;
|
||||
switch (err) {
|
||||
error.PluginError => return true,
|
||||
error.JSError => {
|
||||
ret.* = JSC.ErrorableResolvedSource.err(error.JSError, globalObject.takeError(error.JSError).asVoid());
|
||||
return true;
|
||||
},
|
||||
else => {
|
||||
VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err);
|
||||
return true;
|
||||
},
|
||||
}
|
||||
VirtualMachine.processFetchLog(globalObject, specifier_ptr.*, referrer_ptr.*, &log, ret, err);
|
||||
return true;
|
||||
},
|
||||
);
|
||||
Analytics.Features.virtual_modules += 1;
|
||||
@@ -2258,7 +2288,7 @@ pub const RuntimeTranspilerStore = struct {
|
||||
|
||||
_ = vm.transpiler_store.store.put(this);
|
||||
|
||||
ModuleLoader.AsyncModule.fulfill(globalThis, promise, resolved_source, parse_error, specifier, referrer, &log);
|
||||
ModuleLoader.AsyncModule.fulfill(globalThis, promise, &resolved_source, parse_error, specifier, referrer, &log);
|
||||
}
|
||||
|
||||
pub fn schedule(this: *TranspilerJob) void {
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
#include "BunClientData.h"
|
||||
#include "JSCommonJSModule.h"
|
||||
#include "isBuiltinModule.h"
|
||||
|
||||
#include "AsyncContextFrame.h"
|
||||
#include "ImportMetaObject.h"
|
||||
|
||||
namespace Zig {
|
||||
@@ -707,6 +707,8 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunStri
|
||||
|
||||
JSC::MarkedArgumentBuffer arguments;
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
scope.assertNoExceptionExceptTermination();
|
||||
|
||||
JSC::JSObject* paramsObject = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 1);
|
||||
const auto& builtinNames = WebCore::builtinNames(vm);
|
||||
@@ -715,16 +717,8 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunStri
|
||||
jsString(vm, pathString));
|
||||
arguments.append(paramsObject);
|
||||
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
scope.assertNoExceptionExceptTermination();
|
||||
|
||||
JSC::CallData callData = JSC::getCallData(function);
|
||||
|
||||
auto result = call(globalObject, function, callData, JSC::jsUndefined(), arguments);
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
return JSValue::encode(scope.exception());
|
||||
}
|
||||
auto result = AsyncContextFrame::call(globalObject, function, JSC::jsUndefined(), arguments);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (auto* promise = JSC::jsDynamicCast<JSPromise*>(result)) {
|
||||
switch (promise->status(vm)) {
|
||||
@@ -740,11 +734,11 @@ EncodedJSValue BunPlugin::OnLoad::run(JSC::JSGlobalObject* globalObject, BunStri
|
||||
}
|
||||
|
||||
if (!result.isObject()) {
|
||||
JSC::throwTypeError(globalObject, throwScope, "onLoad() expects an object returned"_s);
|
||||
JSC::throwTypeError(globalObject, scope, "onLoad() expects an object returned"_s);
|
||||
return JSValue::encode({});
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(result));
|
||||
}
|
||||
|
||||
std::optional<String> BunPlugin::OnLoad::resolveVirtualModule(const String& path, const String& from)
|
||||
@@ -780,8 +774,10 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS
|
||||
}
|
||||
|
||||
auto& callbacks = group.callbacks;
|
||||
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
auto scope = DECLARE_THROW_SCOPE(vm);
|
||||
WTF::String pathString = path->toWTFString(BunString::ZeroCopy);
|
||||
|
||||
for (size_t i = 0; i < filters.size(); i++) {
|
||||
if (!filters[i].get()->match(globalObject, pathString, 0)) {
|
||||
continue;
|
||||
@@ -792,7 +788,6 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS
|
||||
}
|
||||
|
||||
JSC::MarkedArgumentBuffer arguments;
|
||||
auto& vm = JSC::getVM(globalObject);
|
||||
|
||||
JSC::JSObject* paramsObject = JSC::constructEmptyObject(globalObject, globalObject->objectPrototype(), 2);
|
||||
const auto& builtinNames = WebCore::builtinNames(vm);
|
||||
@@ -804,18 +799,8 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS
|
||||
Bun::toJS(globalObject, *importer));
|
||||
arguments.append(paramsObject);
|
||||
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
auto scope = DECLARE_CATCH_SCOPE(vm);
|
||||
scope.assertNoExceptionExceptTermination();
|
||||
|
||||
JSC::CallData callData = JSC::getCallData(function);
|
||||
|
||||
auto result = call(globalObject, function, callData, JSC::jsUndefined(), arguments);
|
||||
if (UNLIKELY(scope.exception())) {
|
||||
JSC::Exception* exception = scope.exception();
|
||||
scope.clearException();
|
||||
return JSValue::encode(exception);
|
||||
}
|
||||
auto result = AsyncContextFrame::call(globalObject, function, JSC::jsUndefined(), arguments);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (result.isUndefinedOrNull()) {
|
||||
continue;
|
||||
@@ -824,7 +809,7 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS
|
||||
if (auto* promise = JSC::jsDynamicCast<JSPromise*>(result)) {
|
||||
switch (promise->status(vm)) {
|
||||
case JSPromise::Status::Pending: {
|
||||
JSC::throwTypeError(globalObject, throwScope, "onResolve() doesn't support pending promises yet"_s);
|
||||
JSC::throwTypeError(globalObject, scope, "onResolve() doesn't support pending promises yet"_s);
|
||||
return JSValue::encode({});
|
||||
}
|
||||
case JSPromise::Status::Rejected: {
|
||||
@@ -840,11 +825,11 @@ EncodedJSValue BunPlugin::OnResolve::run(JSC::JSGlobalObject* globalObject, BunS
|
||||
}
|
||||
|
||||
if (!result.isObject()) {
|
||||
JSC::throwTypeError(globalObject, throwScope, "onResolve() expects an object returned"_s);
|
||||
JSC::throwTypeError(globalObject, scope, "onResolve() expects an object returned"_s);
|
||||
return JSValue::encode({});
|
||||
}
|
||||
|
||||
RELEASE_AND_RETURN(throwScope, JSValue::encode(result));
|
||||
RELEASE_AND_RETURN(scope, JSValue::encode(result));
|
||||
}
|
||||
|
||||
return JSValue::encode(JSC::jsUndefined());
|
||||
|
||||
@@ -230,9 +230,12 @@ pub const JSGlobalObject = opaque {
|
||||
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_: bun.String, path: bun.String, target: BunPluginTarget) ?JSValue {
|
||||
pub fn runOnLoadPlugins(this: *JSGlobalObject, namespace_: bun.String, path: bun.String, target: BunPluginTarget) bun.JSError!?JSValue {
|
||||
JSC.markBinding(@src());
|
||||
const result = Bun__runOnLoadPlugins(this, if (namespace_.length() > 0) &namespace_ else null, &path, target);
|
||||
if (this.hasException()) {
|
||||
return error.JSError;
|
||||
}
|
||||
if (result.isEmptyOrUndefinedOrNull()) {
|
||||
return null;
|
||||
}
|
||||
@@ -240,10 +243,15 @@ pub const JSGlobalObject = opaque {
|
||||
return result;
|
||||
}
|
||||
|
||||
pub fn runOnResolvePlugins(this: *JSGlobalObject, namespace_: bun.String, path: bun.String, source: bun.String, target: BunPluginTarget) ?JSValue {
|
||||
pub fn runOnResolvePlugins(this: *JSGlobalObject, namespace_: bun.String, path: bun.String, source: bun.String, target: BunPluginTarget) bun.JSError!?JSValue {
|
||||
JSC.markBinding(@src());
|
||||
|
||||
const result = Bun__runOnResolvePlugins(this, if (namespace_.length() > 0) &namespace_ else null, &path, &source, target);
|
||||
|
||||
if (this.hasException()) {
|
||||
return error.JSError;
|
||||
}
|
||||
|
||||
if (result.isEmptyOrUndefinedOrNull()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -101,8 +101,7 @@ static JSC::SyntheticSourceProvider::SyntheticSourceGenerator generateInternalMo
|
||||
JSC::EnsureStillAliveScope stillAlive(object);
|
||||
|
||||
PropertyNameArray properties(vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
|
||||
object->getPropertyNames(globalObject, properties, DontEnumPropertiesMode::Exclude);
|
||||
|
||||
object->getOwnPropertyNames(object, globalObject, properties, DontEnumPropertiesMode::Exclude);
|
||||
RETURN_IF_EXCEPTION(throwScope, void());
|
||||
|
||||
auto len = properties.size() + 1;
|
||||
@@ -116,7 +115,9 @@ static JSC::SyntheticSourceProvider::SyntheticSourceGenerator generateInternalMo
|
||||
hasDefault = true;
|
||||
}
|
||||
exportNames.append(entry);
|
||||
exportValues.append(object->get(globalObject, entry));
|
||||
JSValue value = object->get(globalObject, entry);
|
||||
RETURN_IF_EXCEPTION(throwScope, void());
|
||||
exportValues.append(value);
|
||||
}
|
||||
|
||||
if (!hasDefault) {
|
||||
|
||||
@@ -151,7 +151,7 @@ pub const PluginRunner = struct {
|
||||
bun.String.init(namespace_slice)
|
||||
else
|
||||
bun.String.empty;
|
||||
const on_resolve_plugin = global.runOnResolvePlugins(
|
||||
const on_resolve_plugin = try global.runOnResolvePlugins(
|
||||
namespace,
|
||||
bun.String.init(specifier).substring(if (namespace.length() > 0) namespace.length() + 1 else 0),
|
||||
bun.String.init(importer),
|
||||
@@ -241,7 +241,7 @@ pub const PluginRunner = struct {
|
||||
|
||||
pub fn onResolveJSC(this: *const PluginRunner, namespace: bun.String, specifier: bun.String, importer: bun.String, target: JSC.JSGlobalObject.BunPluginTarget) bun.JSError!?JSC.ErrorableString {
|
||||
var global = this.global_object;
|
||||
const on_resolve_plugin = global.runOnResolvePlugins(
|
||||
const on_resolve_plugin = try global.runOnResolvePlugins(
|
||||
if (namespace.length() > 0 and !namespace.eqlComptime("file"))
|
||||
namespace
|
||||
else
|
||||
|
||||
7
test/js/bun/plugin/plugin-recursive-fixture-run.ts
generated
Normal file
7
test/js/bun/plugin/plugin-recursive-fixture-run.ts
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
try {
|
||||
require("recursive:recursive");
|
||||
} catch (e: any) {
|
||||
console.log(e.message);
|
||||
}
|
||||
|
||||
await import("recursive:recursive");
|
||||
11
test/js/bun/plugin/plugin-recursive-fixture.ts
generated
Normal file
11
test/js/bun/plugin/plugin-recursive-fixture.ts
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
Bun.plugin({
|
||||
name: "recursive",
|
||||
setup(build) {
|
||||
build.onResolve({ filter: /recursive$/, namespace: "recursive" }, args => {
|
||||
return {
|
||||
path: require.resolve("recursive:" + args.path),
|
||||
namespace: "recursive",
|
||||
};
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -35,6 +35,16 @@ plugin({
|
||||
},
|
||||
});
|
||||
|
||||
plugin({
|
||||
name: "recursion",
|
||||
setup(builder) {
|
||||
builder.onResolve({ filter: /.*/, namespace: "recursion" }, ({ path }) => ({
|
||||
path: require.resolve("recursion:" + path),
|
||||
namespace: "recursion",
|
||||
}));
|
||||
},
|
||||
});
|
||||
|
||||
plugin({
|
||||
name: "boop beep beep",
|
||||
setup(builder) {
|
||||
@@ -190,6 +200,7 @@ plugin({
|
||||
import "../../third_party/svelte";
|
||||
import "./module-plugins";
|
||||
import { render as svelteRender } from "svelte/server";
|
||||
import { bunEnv, bunExe } from "harness";
|
||||
|
||||
describe("require", () => {
|
||||
it("SSRs `<h1>Hello world!</h1>` with Svelte", () => {
|
||||
@@ -510,3 +521,31 @@ it("import(...) without __esModule", async () => {
|
||||
const { default: mod } = await import("my-virtual-module-with-default");
|
||||
expect(mod).toBe("world");
|
||||
});
|
||||
|
||||
it("recursion throws stack overflow", () => {
|
||||
expect(() => {
|
||||
require("recursion:recursion");
|
||||
}).toThrow("Maximum call stack size exceeded");
|
||||
|
||||
try {
|
||||
require("recursion:recursion");
|
||||
throw -1;
|
||||
} catch (e: any) {
|
||||
if (e === -1) {
|
||||
throw new Error("Expected error");
|
||||
}
|
||||
expect(e.message).toMatchInlineSnapshot(`"Maximum call stack size exceeded."`);
|
||||
}
|
||||
});
|
||||
|
||||
it("recursion throws stack overflow at entry point", () => {
|
||||
const result = Bun.spawnSync({
|
||||
cmd: [bunExe(), "--preload=./plugin-recursive-fixture.ts", "plugin-recursive-fixture-run.ts"],
|
||||
stdout: "pipe",
|
||||
stderr: "pipe",
|
||||
env: bunEnv,
|
||||
cwd: import.meta.dir,
|
||||
});
|
||||
|
||||
expect(result.stderr.toString()).toContain("RangeError: Maximum call stack size exceeded.");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user