Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: bun --print #9358

Merged
merged 13 commits into from Mar 13, 2024
2 changes: 1 addition & 1 deletion src/bun.js/ConsoleObject.zig
Expand Up @@ -85,7 +85,7 @@ pub fn messageWithTypeAndLevel(
//message_level: u32,
level: MessageLevel,
global: *JSGlobalObject,
vals: [*]JSValue,
vals: [*]const JSValue,
len: usize,
) callconv(.C) void {
if (comptime is_bindgen) {
Expand Down
27 changes: 27 additions & 0 deletions src/bun.js/bindings/CommonJSModuleRecord.cpp
Expand Up @@ -92,6 +92,9 @@ static bool canPerformFastEnumeration(Structure* s)
return true;
}

extern "C" bool Bun__VM__specifierIsEvalEntryPoint(void*, EncodedJSValue);
extern "C" void Bun__VM__setEntryPointEvalResult(void*, EncodedJSValue);

static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObject, JSCommonJSModule* moduleObject, JSString* dirname, JSValue filename, WTF::NakedPtr<Exception>& exception)
{
JSSourceCode* code = moduleObject->sourceCode.get();
Expand Down Expand Up @@ -119,6 +122,30 @@ static bool evaluateCommonJSModuleOnce(JSC::VM& vm, Zig::GlobalObject* globalObj

moduleObject->hasEvaluated = true;

if (Bun__VM__specifierIsEvalEntryPoint(globalObject->bunVM(), JSValue::encode(filename))) {

// Using same approach as node, `arguments` in the entry point isn't defined
// https://github.com/nodejs/node/blob/592c6907bfe1922f36240e9df076be1864c3d1bd/lib/internal/process/execution.js#L92
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "exports"_s), moduleObject->exportsObject(), 0);
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "require"_s), requireFunction, 0);
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "module"_s), moduleObject, 0);
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "__filename"_s), filename, 0);
globalObject->putDirect(vm, Identifier::fromLatin1(vm, "__dirname"_s), dirname, 0);

JSValue result = JSC::evaluate(globalObject, code->sourceCode(), jsUndefined(), exception);

if (UNLIKELY(exception.get() || result.isEmpty())) {
moduleObject->sourceCode.clear();
return false;
}

Bun__VM__setEntryPointEvalResult(globalObject->bunVM(), JSValue::encode(result));

moduleObject->sourceCode.clear();

return true;
}

// This will return 0 if there was a syntax error or an allocation failure
JSValue fnValue = JSC::evaluate(globalObject, code->sourceCode(), jsUndefined(), exception);

Expand Down
31 changes: 29 additions & 2 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Expand Up @@ -977,7 +977,7 @@ JSC::ScriptExecutionStatus Zig::GlobalObject::scriptExecutionStatus(JSC::JSGloba
}
}

const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
&supportsRichSourceInfo,
&shouldInterruptScript,
&javaScriptRuntimeFlags,
Expand All @@ -993,10 +993,10 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
&reportUncaughtExceptionAtEventLoop,
&currentScriptExecutionOwner,
&scriptExecutionStatus,
nullptr, // reportViolationForUnsafeEval
nullptr, // defaultLanguage
nullptr, // compileStreaming
nullptr, // instantiateStreaming
nullptr,
&Zig::deriveShadowRealmGlobalObject
};

Expand Down Expand Up @@ -4493,6 +4493,33 @@ JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* globalObject,
return result;
}

extern "C" void Bun__VM__setEvalResultIfEntryPoint(void*, EncodedJSValue, EncodedJSValue);

JSC::JSValue GlobalObject::moduleLoaderEvaluateForEval(JSGlobalObject* globalObject,
JSModuleLoader* moduleLoader, JSValue key,
JSValue moduleRecordValue, JSValue scriptFetcher,
JSValue sentValue, JSValue resumeMode)
{
if (UNLIKELY(scriptFetcher && scriptFetcher.isObject())) {
Bun__VM__setEvalResultIfEntryPoint(jsCast<Zig::GlobalObject*>(globalObject)->bunVM(), JSValue::encode(key), JSValue::encode(scriptFetcher));
return scriptFetcher;
}

JSC::JSValue result = moduleLoader->evaluateNonVirtual(globalObject, key, moduleRecordValue,
scriptFetcher, sentValue, resumeMode);

// need to check each module evaluated to cover cases like these (23 should be the result):
// `import "./foo"; 23; import "./bar"`
Bun__VM__setEvalResultIfEntryPoint(jsCast<Zig::GlobalObject*>(globalObject)->bunVM(), JSValue::encode(key), JSValue::encode(result));

return result;
}

void GlobalObject::setupModuleLoaderEvaluateForEval()
{
s_globalObjectMethodTable.moduleLoaderEvaluate = &moduleLoaderEvaluateForEval;
}

GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1))
{
if (handler == Bun__HTTPRequestContext__onReject) {
Expand Down
5 changes: 4 additions & 1 deletion src/bun.js/bindings/ZigGlobalObject.h
Expand Up @@ -69,7 +69,7 @@ class GlobalObject : public JSC::JSGlobalObject {

public:
static const JSC::ClassInfo s_info;
static const JSC::GlobalObjectMethodTable s_globalObjectMethodTable;
static JSC::GlobalObjectMethodTable s_globalObjectMethodTable;

template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm)
{
Expand Down Expand Up @@ -169,6 +169,9 @@ class GlobalObject : public JSC::JSGlobalObject {
static JSC::JSInternalPromise* moduleLoaderFetch(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue);
static JSC::JSObject* moduleLoaderCreateImportMetaProperties(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSModuleRecord*, JSC::JSValue);
static JSC::JSValue moduleLoaderEvaluate(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue);
static JSC::JSValue moduleLoaderEvaluateForEval(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue);
void setupModuleLoaderEvaluateForEval();

static ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*);
static void promiseRejectionTracker(JSGlobalObject*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation);
void setConsole(void* console);
Expand Down
5 changes: 5 additions & 0 deletions src/bun.js/bindings/bindings.cpp
Expand Up @@ -5475,6 +5475,11 @@ extern "C" bool JSGlobalObject__hasException(JSC::JSGlobalObject* globalObject)
return DECLARE_CATCH_SCOPE(globalObject->vm()).exception() != 0;
}

extern "C" void JSGlobalObject__setupModuleLoaderEvaluateForEval(JSC::JSGlobalObject* globalObject)
{
reinterpret_cast<Zig::GlobalObject*>(globalObject)->setupModuleLoaderEvaluateForEval();
}

CPP_DECL bool JSC__GetterSetter__isGetterNull(JSC__GetterSetter* gettersetter)
{
return gettersetter->isGetterNull();
Expand Down
22 changes: 22 additions & 0 deletions src/bun.js/bindings/bindings.zig
Expand Up @@ -14,6 +14,7 @@ const is_bindgen: bool = std.meta.globalOption("bindgen", bool) orelse false;
const ArrayBuffer = @import("../base.zig").ArrayBuffer;
const JSC = @import("root").bun.JSC;
const Shimmer = JSC.Shimmer;
const ConsoleObject = JSC.ConsoleObject;
const FFI = @import("./FFI.zig");
const NullableAllocator = @import("../../nullable_allocator.zig").NullableAllocator;
const MutableString = bun.MutableString;
Expand Down Expand Up @@ -2641,6 +2642,11 @@ pub const JSGlobalObject = extern struct {
return @enumFromInt(@as(JSValue.Type, @bitCast(@intFromPtr(globalThis))));
}

extern fn JSGlobalObject__setupModuleLoaderEvaluateForEval(*JSGlobalObject) void;
pub fn setupModuleLoaderEvaluateForEval(this: *JSGlobalObject) void {
JSGlobalObject__setupModuleLoaderEvaluateForEval(this);
}

pub fn throwInvalidArguments(
this: *JSGlobalObject,
comptime fmt: string,
Expand Down Expand Up @@ -4074,6 +4080,22 @@ pub const JSValue = enum(JSValueReprInt) {
});
}

pub fn print(
this: JSValue,
globalObject: *JSGlobalObject,
message_type: ConsoleObject.MessageType,
message_level: ConsoleObject.MessageLevel,
) void {
JSC.ConsoleObject.messageWithTypeAndLevel(
undefined,
message_type,
message_level,
globalObject,
&[_]JSC.JSValue{this},
1,
);
}

/// Create a JSValue string from a zig format-print (fmt + args)
pub fn printString(globalThis: *JSGlobalObject, comptime stack_buffer_size: usize, comptime fmt: []const u8, args: anytype) !JSValue {
var stack_fallback = std.heap.stackFallback(stack_buffer_size, globalThis.allocator());
Expand Down
35 changes: 31 additions & 4 deletions src/bun.js/javascript.zig
Expand Up @@ -576,6 +576,8 @@ pub const VirtualMachine = struct {
rare_data: ?*JSC.RareData = null,
is_us_loop_entered: bool = false,
pending_internal_promise: *JSC.JSInternalPromise = undefined,
entry_point_result: JSC.Strong = .{},

auto_install_dependencies: bool = false,

onUnhandledRejection: *const OnUnhandledRejection = defaultOnUnhandledRejection,
Expand Down Expand Up @@ -901,8 +903,33 @@ pub const VirtualMachine = struct {
return .running;
}

pub fn specifierIsEvalEntryPoint(this: *VirtualMachine, specifier: JSValue) callconv(.C) bool {
if (this.module_loader.eval_source) |eval_source| {
var specifier_str = specifier.toBunString(this.global);
defer specifier_str.deref();
return specifier_str.eqlUTF8(eval_source.path.text);
}

return false;
}

pub fn setEvalResultIfEntryPoint(this: *VirtualMachine, specifier: JSValue, result: JSValue) callconv(.C) void {
if (!this.entry_point_result.has() and this.specifierIsEvalEntryPoint(specifier)) {
this.entry_point_result.set(this.global, result);
}
}

pub fn setEntryPointEvalResult(this: *VirtualMachine, value: JSValue) callconv(.C) void {
if (!this.entry_point_result.has()) {
this.entry_point_result.set(this.global, value);
}
}

comptime {
@export(scriptExecutionStatus, .{ .name = "Bun__VM__scriptExecutionStatus" });
@export(setEvalResultIfEntryPoint, .{ .name = "Bun__VM__setEvalResultIfEntryPoint" });
@export(setEntryPointEvalResult, .{ .name = "Bun__VM__setEntryPointEvalResult" });
@export(specifierIsEvalEntryPoint, .{ .name = "Bun__VM__specifierIsEvalEntryPoint" });
}

pub fn onExit(this: *VirtualMachine) void {
Expand Down Expand Up @@ -1602,13 +1629,13 @@ pub const VirtualMachine = struct {
break :brk options.Loader.file;
};

if (jsc_vm.module_loader.eval_script) |eval_script| {
if (jsc_vm.module_loader.eval_source) |eval_source| {
if (strings.endsWithComptime(specifier, bun.pathLiteral("/[eval]"))) {
virtual_source = eval_script;
virtual_source = eval_source;
loader = .tsx;
}
if (strings.endsWithComptime(specifier, bun.pathLiteral("/[stdin]"))) {
virtual_source = eval_script;
virtual_source = eval_source;
loader = .tsx;
}
}
Expand Down Expand Up @@ -1686,7 +1713,7 @@ pub const VirtualMachine = struct {
ret.result = null;
ret.path = result.path;
return;
} else if (jsc_vm.module_loader.eval_script != null and
} else if (jsc_vm.module_loader.eval_source != null and
(strings.endsWithComptime(specifier, bun.pathLiteral("/[eval]")) or
strings.endsWithComptime(specifier, bun.pathLiteral("/[stdin]"))))
{
Expand Down
31 changes: 20 additions & 11 deletions src/bun.js/module_loader.zig
Expand Up @@ -432,6 +432,11 @@ pub const RuntimeTranspilerStore = struct {
var should_close_input_file_fd = fd == null;

var input_file_fd: StoredFileDescriptorType = .zero;

const is_main = vm.main.len == path.text.len and
vm.main_hash == hash and
strings.eqlLong(vm.main, path.text, false);

var parse_options = Bundler.ParseOptions{
.allocator = allocator,
.path = path,
Expand All @@ -446,12 +451,13 @@ pub const RuntimeTranspilerStore = struct {
.virtual_source = null,
.dont_bundle_twice = true,
.allow_commonjs = true,
.inject_jest_globals = bundler.options.rewrite_jest_for_tests and
vm.main.len == path.text.len and
vm.main_hash == hash and
strings.eqlLong(vm.main, path.text, false),
.set_breakpoint_on_first_line = vm.debugger != null and vm.debugger.?.set_breakpoint_on_first_line and strings.eqlLong(vm.main, path.text, true) and setBreakPointOnFirstLine(),
.inject_jest_globals = bundler.options.rewrite_jest_for_tests and is_main,
.set_breakpoint_on_first_line = vm.debugger != null and
vm.debugger.?.set_breakpoint_on_first_line and
is_main and
setBreakPointOnFirstLine(),
.runtime_transpiler_cache = if (!JSC.RuntimeTranspilerCache.is_disabled) &cache else null,
.remove_cjs_module_wrapper = is_main and vm.module_loader.eval_source != null,
};

defer {
Expand Down Expand Up @@ -654,7 +660,7 @@ pub const RuntimeTranspilerStore = struct {

pub const ModuleLoader = struct {
transpile_source_code_arena: ?*bun.ArenaAllocator = null,
eval_script: ?*logger.Source = null,
eval_source: ?*logger.Source = null,

const debug = Output.scoped(.ModuleLoader, true);

Expand Down Expand Up @@ -1558,9 +1564,12 @@ pub const ModuleLoader = struct {
.dont_bundle_twice = true,
.allow_commonjs = true,
.inject_jest_globals = jsc_vm.bundler.options.rewrite_jest_for_tests and is_main,
.set_breakpoint_on_first_line = is_main and jsc_vm.debugger != null and jsc_vm.debugger.?.set_breakpoint_on_first_line and setBreakPointOnFirstLine(),

.set_breakpoint_on_first_line = is_main and
jsc_vm.debugger != null and
jsc_vm.debugger.?.set_breakpoint_on_first_line and
setBreakPointOnFirstLine(),
.runtime_transpiler_cache = if (!disable_transpilying and !JSC.RuntimeTranspilerCache.is_disabled) &cache else null,
.remove_cjs_module_wrapper = is_main and jsc_vm.module_loader.eval_source != null,
};
defer {
if (should_close_input_file_fd and input_file_fd != bun.invalid_fd) {
Expand Down Expand Up @@ -2140,13 +2149,13 @@ pub const ModuleLoader = struct {
// The concurrent one only handles javascript-like loaders right now.
var loader: ?options.Loader = jsc_vm.bundler.options.loaders.get(path.name.ext);

if (jsc_vm.module_loader.eval_script) |eval_script| {
if (jsc_vm.module_loader.eval_source) |eval_source| {
if (strings.endsWithComptime(specifier, bun.pathLiteral("/[eval]"))) {
virtual_source = eval_script;
virtual_source = eval_source;
loader = .tsx;
}
if (strings.endsWithComptime(specifier, bun.pathLiteral("/[stdin]"))) {
virtual_source = eval_script;
virtual_source = eval_source;
loader = .tsx;
}
}
Expand Down
23 changes: 16 additions & 7 deletions src/bun_js.zig
Expand Up @@ -182,12 +182,15 @@ pub const Run = struct {
vm.arena = &run.arena;
vm.allocator = arena.allocator();

if (ctx.runtime_options.eval_script.len > 0) {
vm.module_loader.eval_script = ptr: {
const v = try bun.default_allocator.create(logger.Source);
v.* = logger.Source.initPathString(entry_path, ctx.runtime_options.eval_script);
break :ptr v;
};
if (ctx.runtime_options.eval.script.len > 0) {
const script_source = try bun.default_allocator.create(logger.Source);
script_source.* = logger.Source.initPathString(entry_path, ctx.runtime_options.eval.script);
vm.module_loader.eval_source = script_source;

if (ctx.runtime_options.eval.eval_and_print) {
b.options.dead_code_elimination = false;
vm.global.setupModuleLoaderEvaluateForEval();
}
}

b.options.install = ctx.install;
Expand Down Expand Up @@ -266,7 +269,7 @@ pub const Run = struct {
vm.hot_reload = this.ctx.debug.hot_reload;
vm.onUnhandledRejection = &onUnhandledRejectionBeforeClose;

if (this.ctx.runtime_options.eval_script.len > 0) {
if (this.ctx.runtime_options.eval.script.len > 0) {
Bun__ExposeNodeModuleGlobals(vm.global);
}

Expand Down Expand Up @@ -389,6 +392,12 @@ pub const Run = struct {

vm.onExit();

if (this.ctx.runtime_options.eval.eval_and_print) {
if (vm.entry_point_result.trySwap()) |result| {
result.print(vm.global, .Log, .Log);
}
}

if (!JSC.is_bindgen) JSC.napi.fixDeadCodeElimination();
Global.exit(exit_code);
}
Expand Down
2 changes: 2 additions & 0 deletions src/bundler.zig
Expand Up @@ -1278,6 +1278,7 @@ pub const Bundler = struct {
inject_jest_globals: bool = false,
set_breakpoint_on_first_line: bool = false,
emit_decorator_metadata: bool = false,
remove_cjs_module_wrapper: bool = false,

dont_bundle_twice: bool = false,
allow_commonjs: bool = false,
Expand Down Expand Up @@ -1429,6 +1430,7 @@ pub const Bundler = struct {
opts.features.minify_syntax = bundler.options.minify_syntax;
opts.features.minify_identifiers = bundler.options.minify_identifiers;
opts.features.dead_code_elimination = bundler.options.dead_code_elimination;
opts.features.remove_cjs_module_wrapper = this_parse.remove_cjs_module_wrapper;

if (bundler.macro_context == null) {
bundler.macro_context = js_ast.Macro.MacroContext.init(bundler);
Expand Down