Skip to content

Commit 4a21013

Browse files
authored
implement --no-addons flag and "node-addons" export condition (#19530)
1 parent a4bc1c8 commit 4a21013

File tree

12 files changed

+153
-14
lines changed

12 files changed

+153
-14
lines changed

src/api/schema.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,6 +1707,9 @@ pub const Api = struct {
17071707
serve_hmr: ?bool = null,
17081708
serve_define: ?StringMap = null,
17091709

1710+
// from --no-addons. null == true
1711+
allow_addons: ?bool = null,
1712+
17101713
bunfig_path: []const u8,
17111714

17121715
pub fn decode(reader: anytype) anyerror!TransformOptions {

src/bun.js/VirtualMachine.zig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ comptime {
1717
@export(&setEntryPointEvalResultCJS, .{ .name = "Bun__VM__setEntryPointEvalResultCJS" });
1818
@export(&specifierIsEvalEntryPoint, .{ .name = "Bun__VM__specifierIsEvalEntryPoint" });
1919
@export(&string_allocation_limit, .{ .name = "Bun__stringSyntheticAllocationLimit" });
20+
@export(&allowAddons, .{ .name = "Bun__VM__allowAddons" });
2021
}
2122

2223
global: *JSGlobalObject,
@@ -192,6 +193,10 @@ pub const OnUnhandledRejection = fn (*VirtualMachine, globalObject: *JSGlobalObj
192193

193194
pub const OnException = fn (*ZigException) void;
194195

196+
pub fn allowAddons(this: *VirtualMachine) callconv(.c) bool {
197+
return if (this.transpiler.options.transform_options.allow_addons) |allow_addons| allow_addons else true;
198+
}
199+
195200
pub fn initRequestBodyValue(this: *VirtualMachine, body: JSC.WebCore.Body.Value) !*Body.Value.HiveRef {
196201
return .init(body, &this.body_value_hive_allocator);
197202
}

src/bun.js/bindings/BunProcess.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ static char* toFileURI(std::span<const char> span)
361361
extern "C" size_t Bun__process_dlopen_count;
362362

363363
extern "C" void CrashHandler__setDlOpenAction(const char* action);
364+
extern "C" bool Bun__VM__allowAddons(void* vm);
364365

365366
JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalObject_, JSC::CallFrame* callFrame))
366367
{
@@ -369,6 +370,10 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionDlopen, (JSC::JSGlobalObject * globalOb
369370
auto scope = DECLARE_THROW_SCOPE(JSC::getVM(globalObject));
370371
auto& vm = JSC::getVM(globalObject);
371372

373+
if (!Bun__VM__allowAddons(globalObject->bunVM())) {
374+
return ERR::DLOPEN_DISABLED(scope, globalObject, "Cannot load native addon because loading addons is disabled."_s);
375+
}
376+
372377
auto argCount = callFrame->argumentCount();
373378
if (argCount < 2) {
374379
JSC::throwTypeError(globalObject, scope, "dlopen requires 2 arguments"_s);

src/bun.js/bindings/ErrorCode.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,12 @@ JSC::EncodedJSValue INVALID_THIS(JSC::ThrowScope& scope, JSC::JSGlobalObject* gl
14111411
return {};
14121412
}
14131413

1414+
JSC::EncodedJSValue DLOPEN_DISABLED(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, ASCIILiteral message)
1415+
{
1416+
scope.throwException(globalObject, createError(globalObject, ErrorCode::ERR_DLOPEN_DISABLED, message));
1417+
return {};
1418+
}
1419+
14141420
} // namespace ERR
14151421

14161422
static JSC::JSValue ERR_INVALID_ARG_TYPE(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, JSValue arg0, JSValue arg1, JSValue arg2)

src/bun.js/bindings/ErrorCode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ JSC::EncodedJSValue MISSING_OPTION(JSC::ThrowScope&, JSC::JSGlobalObject*, ASCII
134134
JSC::EncodedJSValue INVALID_MIME_SYNTAX(JSC::ThrowScope&, JSC::JSGlobalObject*, const String& part, const String& input, int position);
135135
JSC::EncodedJSValue CLOSED_MESSAGE_PORT(JSC::ThrowScope&, JSC::JSGlobalObject*);
136136
JSC::EncodedJSValue INVALID_THIS(JSC::ThrowScope& scope, JSC::JSGlobalObject* globalObject, ASCIILiteral expectedType);
137+
JSC::EncodedJSValue DLOPEN_DISABLED(JSC::ThrowScope&, JSC::JSGlobalObject*, ASCIILiteral message);
137138

138139
// URL
139140

src/bun.js/web_worker.zig

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -278,10 +278,47 @@ pub const WebWorker = struct {
278278
assert(this.status.load(.acquire) == .start);
279279
assert(this.vm == null);
280280

281+
var transform_options = this.parent.transpiler.options.transform_options;
282+
283+
if (this.execArgv) |exec_argv| parse_new_args: {
284+
var new_args: std.ArrayList([]const u8) = try .initCapacity(bun.default_allocator, exec_argv.len);
285+
defer {
286+
for (new_args.items) |arg| {
287+
bun.default_allocator.free(arg);
288+
}
289+
new_args.deinit();
290+
}
291+
292+
for (exec_argv) |arg| {
293+
try new_args.append(arg.toOwnedSliceZ(bun.default_allocator));
294+
}
295+
296+
var diag: bun.clap.Diagnostic = .{};
297+
var iter: bun.clap.args.SliceIterator = .init(new_args.items);
298+
299+
var args = bun.clap.parseEx(bun.clap.Help, bun.CLI.Command.Tag.RunCommand.params(), &iter, .{
300+
.diagnostic = &diag,
301+
.allocator = bun.default_allocator,
302+
303+
// just one for executable
304+
.stop_after_positional_at = 1,
305+
}) catch {
306+
// ignore param parsing errors
307+
break :parse_new_args;
308+
};
309+
defer args.deinit();
310+
311+
// override the existing even if it was set
312+
transform_options.allow_addons = !args.flag("--no-addons");
313+
314+
// TODO: currently this only checks for --no-addons. I think
315+
// this should go through most flags and update the options.
316+
}
317+
281318
this.arena = try bun.MimallocArena.init();
282319
var vm = try JSC.VirtualMachine.initWorker(this, .{
283320
.allocator = this.arena.?.allocator(),
284-
.args = this.parent.transpiler.options.transform_options,
321+
.args = transform_options,
285322
.store_fd = this.store_fd,
286323
.graph = this.parent.standalone_module_graph,
287324
});

src/cli.zig

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,7 @@ pub const Arguments = struct {
246246
clap.parseParam("--title <STR> Set the process title") catch unreachable,
247247
clap.parseParam("--zero-fill-buffers Boolean to force Buffer.allocUnsafe(size) to be zero-filled.") catch unreachable,
248248
clap.parseParam("--redis-preconnect Preconnect to $REDIS_URL at startup") catch unreachable,
249+
clap.parseParam("--no-addons Throw an error if process.dlopen is called, and disable export condition \"node-addons\"") catch unreachable,
249250
};
250251

251252
const auto_or_run_params = [_]ParamType{
@@ -715,6 +716,12 @@ pub const Arguments = struct {
715716
ctx.runtime_options.redis_preconnect = true;
716717
}
717718

719+
if (args.flag("--no-addons")) {
720+
// used for disabling process.dlopen and
721+
// for disabling export condition "node-addons"
722+
opts.allow_addons = false;
723+
}
724+
718725
if (args.option("--port")) |port_str| {
719726
if (comptime cmd == .RunAsNodeCommand) {
720727
// TODO: prevent `node --port <script>` from working

src/deps/zig-clap/clap/args.zig

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,21 @@ pub const ExampleArgIterator = struct {
2121
pub const SliceIterator = struct {
2222
const Error = error{};
2323

24-
args: []const []const u8,
25-
index: usize = 0,
24+
remain: []const []const u8,
2625

27-
pub fn next(iter: *SliceIterator) Error!?[]const u8 {
28-
if (iter.args.len <= iter.index)
29-
return null;
26+
pub fn init(args: []const []const u8) SliceIterator {
27+
return .{
28+
.remain = args,
29+
};
30+
}
3031

31-
defer iter.index += 1;
32-
return iter.args[iter.index];
32+
pub fn next(iter: *SliceIterator) ?[]const u8 {
33+
if (iter.remain.len > 0) {
34+
const res = iter.remain[0];
35+
iter.remain = iter.remain[1..];
36+
return res;
37+
}
38+
return null;
3339
}
3440
};
3541

src/options.zig

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,7 +1122,7 @@ pub const ESMConditions = struct {
11221122
require: ConditionsMap,
11231123
style: ConditionsMap,
11241124

1125-
pub fn init(allocator: std.mem.Allocator, defaults: []const string) !ESMConditions {
1125+
pub fn init(allocator: std.mem.Allocator, defaults: []const string) bun.OOM!ESMConditions {
11261126
var default_condition_amp = ConditionsMap.init(allocator);
11271127

11281128
var import_condition_map = ConditionsMap.init(allocator);
@@ -1176,7 +1176,7 @@ pub const ESMConditions = struct {
11761176
};
11771177
}
11781178

1179-
pub fn appendSlice(self: *ESMConditions, conditions: []const string) !void {
1179+
pub fn appendSlice(self: *ESMConditions, conditions: []const string) bun.OOM!void {
11801180
try self.default.ensureUnusedCapacity(conditions.len);
11811181
try self.import.ensureUnusedCapacity(conditions.len);
11821182
try self.require.ensureUnusedCapacity(conditions.len);
@@ -1189,6 +1189,13 @@ pub const ESMConditions = struct {
11891189
self.style.putAssumeCapacity(condition, {});
11901190
}
11911191
}
1192+
1193+
pub fn append(self: *ESMConditions, condition: string) bun.OOM!void {
1194+
try self.default.put(condition, {});
1195+
try self.import.put(condition, {});
1196+
try self.require.put(condition, {});
1197+
try self.style.put(condition, {});
1198+
}
11921199
};
11931200

11941201
pub const JSX = struct {
@@ -1993,10 +2000,26 @@ pub const BundleOptions = struct {
19932000
opts.main_fields = Target.DefaultMainFields.get(opts.target);
19942001
}
19952002

1996-
opts.conditions = try ESMConditions.init(allocator, opts.target.defaultConditions());
2003+
{
2004+
// conditions:
2005+
// 1. defaults
2006+
// 2. node-addons
2007+
// 3. user conditions
2008+
opts.conditions = try ESMConditions.init(allocator, opts.target.defaultConditions());
2009+
2010+
dont_append_node_addons: {
2011+
if (transform.allow_addons) |allow_addons| {
2012+
if (!allow_addons) {
2013+
break :dont_append_node_addons;
2014+
}
2015+
}
2016+
2017+
try opts.conditions.append("node-addons");
2018+
}
19972019

1998-
if (transform.conditions.len > 0) {
1999-
opts.conditions.appendSlice(transform.conditions) catch bun.outOfMemory();
2020+
if (transform.conditions.len > 0) {
2021+
try opts.conditions.appendSlice(transform.conditions);
2022+
}
20002023
}
20012024

20022025
switch (opts.target) {

test/internal/ban-words.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ const words: Record<string, { reason: string; limit?: number; regex?: boolean }>
3434

3535
[String.raw`: [a-zA-Z0-9_\.\*\?\[\]\(\)]+ = undefined,`]: { reason: "Do not default a struct field to undefined", limit: 240, regex: true },
3636
"usingnamespace": { reason: "Zig 0.15 will remove `usingnamespace`" },
37-
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1850 },
37+
"catch unreachable": { reason: "For out-of-memory, prefer 'catch bun.outOfMemory()'", limit: 1851 },
3838

3939
"std.fs.Dir": { reason: "Prefer bun.sys + bun.FD instead of std.fs", limit: 180 },
4040
"std.fs.cwd": { reason: "Prefer bun.FD.cwd()", limit: 103 },

0 commit comments

Comments
 (0)