Skip to content

Commit

Permalink
feat: bun --print (#9358)
Browse files Browse the repository at this point in the history
* --print cli flag

* less code elimination

* handle cjs module eval results

* make node -p work

* better test

* more tests

* if

* delete commented code

* delete commented code

* EvalGlobalObject

* remove one constructor

---------

Co-authored-by: Jarred Sumner <[email protected]>
  • Loading branch information
dylan-conway and Jarred-Sumner committed Mar 13, 2024
1 parent 4b0eb47 commit 3765032
Show file tree
Hide file tree
Showing 20 changed files with 431 additions and 161 deletions.
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
70 changes: 63 additions & 7 deletions src/bun.js/bindings/ZigGlobalObject.cpp
Expand Up @@ -742,7 +742,7 @@ static void resetOnEachMicrotaskTick(JSC::VM& vm, Zig::GlobalObject* globalObjec
}
}

extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, void* worker_ptr)
extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client, int32_t executionContextId, bool miniMode, bool evalMode, void* worker_ptr)
{

auto heapSize = miniMode ? JSC::HeapType::Small : JSC::HeapType::Large;
Expand Down Expand Up @@ -779,6 +779,13 @@ extern "C" JSC__JSGlobalObject* Zig__GlobalObject__create(void* console_client,
globalObject->m_processEnvObject.set(vm, globalObject, env);
}
}
} else if (evalMode) {
globalObject = Zig::EvalGlobalObject::create(
vm,
Zig::EvalGlobalObject::createStructure(vm, JSC::JSGlobalObject::create(vm, JSC::JSGlobalObject::createStructure(vm, JSC::jsNull())),
JSC::jsNull()),
&Zig::EvalGlobalObject::s_globalObjectMethodTable);

} else {
globalObject = Zig::GlobalObject::create(
vm,
Expand Down Expand Up @@ -993,15 +1000,38 @@ const JSC::GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = {
&reportUncaughtExceptionAtEventLoop,
&currentScriptExecutionOwner,
&scriptExecutionStatus,
nullptr, // reportViolationForUnsafeEval
nullptr, // defaultLanguage
nullptr, // compileStreaming
nullptr, // instantiateStreaming
&Zig::deriveShadowRealmGlobalObject
};

const JSC::GlobalObjectMethodTable EvalGlobalObject::s_globalObjectMethodTable = {
&supportsRichSourceInfo,
&shouldInterruptScript,
&javaScriptRuntimeFlags,
// &queueMicrotaskToEventLoop, // queueTaskToEventLoop
nullptr,
nullptr, // &shouldInterruptScriptBeforeTimeout,
&moduleLoaderImportModule, // moduleLoaderImportModule
&moduleLoaderResolve, // moduleLoaderResolve
&moduleLoaderFetch, // moduleLoaderFetch
&moduleLoaderCreateImportMetaProperties, // moduleLoaderCreateImportMetaProperties
&moduleLoaderEvaluate, // moduleLoaderEvaluate
&promiseRejectionTracker, // promiseRejectionTracker
&reportUncaughtExceptionAtEventLoop,
&currentScriptExecutionOwner,
&scriptExecutionStatus,
nullptr, // reportViolationForUnsafeEval
nullptr, // defaultLanguage
nullptr, // compileStreaming
nullptr, // instantiateStreaming
&Zig::deriveShadowRealmGlobalObject
};

GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure)
: JSC::JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable)
: JSC::JSGlobalObject(vm, structure, methodTable)
, m_bunVM(Bun__getVM())
, m_constructors(makeUnique<WebCore::DOMConstructors>())
, m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal))
Expand All @@ -1017,8 +1047,8 @@ GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure)
globalEventScope.relaxAdoptionRequirement();
}

GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::ScriptExecutionContextIdentifier contextId)
: JSC::JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
GlobalObject::GlobalObject(JSC::VM& vm, JSC::Structure* structure, WebCore::ScriptExecutionContextIdentifier contextId, const JSC::GlobalObjectMethodTable* methodTable)
: JSC::JSGlobalObject(vm, structure, methodTable)
, m_bunVM(Bun__getVM())
, m_constructors(makeUnique<WebCore::DOMConstructors>())
, m_world(WebCore::DOMWrapperWorld::create(vm, WebCore::DOMWrapperWorld::Type::Normal))
Expand Down Expand Up @@ -4478,21 +4508,47 @@ JSC::JSObject* GlobalObject::moduleLoaderCreateImportMetaProperties(JSGlobalObje
return Zig::ImportMetaObject::create(globalObject, keyString);
}

JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* globalObject,
extern "C" void Bun__VM__setEvalResultIfEntryPoint(void*, EncodedJSValue, EncodedJSValue);

JSC::JSValue GlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGlobalObject,
JSModuleLoader* moduleLoader, JSValue key,
JSValue moduleRecordValue, JSValue scriptFetcher,
JSValue sentValue, JSValue resumeMode)
{
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);

if (UNLIKELY(scriptFetcher && scriptFetcher.isObject())) {
return scriptFetcher;
}

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

return result;
}

JSC::JSValue EvalGlobalObject::moduleLoaderEvaluate(JSGlobalObject* lexicalGlobalObject,
JSModuleLoader* moduleLoader, JSValue key,
JSValue moduleRecordValue, JSValue scriptFetcher,
JSValue sentValue, JSValue resumeMode)
{
Zig::GlobalObject* globalObject = jsCast<Zig::GlobalObject*>(lexicalGlobalObject);

if (UNLIKELY(scriptFetcher && scriptFetcher.isObject())) {
Bun__VM__setEvalResultIfEntryPoint(globalObject->bunVM(), JSValue::encode(key), JSValue::encode(scriptFetcher));
return scriptFetcher;
}

JSC::JSValue result = moduleLoader->evaluateNonVirtual(lexicalGlobalObject, 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(globalObject->bunVM(), JSValue::encode(key), JSValue::encode(result));

return result;
}

GlobalObject::PromiseFunctions GlobalObject::promiseHandlerID(EncodedJSValue (*handler)(JSC__JSGlobalObject* arg0, JSC__CallFrame* arg1))
{
if (handler == Bun__HTTPRequestContext__onReject) {
Expand Down
35 changes: 31 additions & 4 deletions src/bun.js/bindings/ZigGlobalObject.h
Expand Up @@ -92,6 +92,9 @@ class GlobalObject : public JSC::JSGlobalObject {
// Make binding code generation easier.
GlobalObject* globalObject() { return this; }

GlobalObject(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable*);
GlobalObject(JSC::VM& vm, JSC::Structure* structure, uint32_t, const JSC::GlobalObjectMethodTable*);

DOMGuardedObjectSet& guardedObjects() WTF_REQUIRES_LOCK(m_gcLock) { return m_guardedObjects; }

const DOMGuardedObjectSet& guardedObjects() const WTF_IGNORES_THREAD_SAFETY_ANALYSIS
Expand All @@ -107,14 +110,28 @@ class GlobalObject : public JSC::JSGlobalObject {

static GlobalObject* create(JSC::VM& vm, JSC::Structure* structure)
{
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure);
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure, &s_globalObjectMethodTable);
ptr->finishCreation(vm);
return ptr;
}

static GlobalObject* create(JSC::VM& vm, JSC::Structure* structure, uint32_t scriptExecutionContextId)
{
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure, scriptExecutionContextId);
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure, scriptExecutionContextId, &s_globalObjectMethodTable);
ptr->finishCreation(vm);
return ptr;
}

static GlobalObject* create(JSC::VM& vm, JSC::Structure* structure, const JSC::GlobalObjectMethodTable* methodTable)
{
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure, methodTable);
ptr->finishCreation(vm);
return ptr;
}

static GlobalObject* create(JSC::VM& vm, JSC::Structure* structure, uint32_t scriptExecutionContextId, const JSC::GlobalObjectMethodTable* methodTable)
{
GlobalObject* ptr = new (NotNull, JSC::allocateCell<GlobalObject>(vm)) GlobalObject(vm, structure, scriptExecutionContextId, methodTable);
ptr->finishCreation(vm);
return ptr;
}
Expand Down Expand Up @@ -169,6 +186,7 @@ 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 ScriptExecutionStatus scriptExecutionStatus(JSGlobalObject*, JSObject*);
static void promiseRejectionTracker(JSGlobalObject*, JSC::JSPromise*, JSC::JSPromiseRejectionOperation);
void setConsole(void* console);
Expand Down Expand Up @@ -452,8 +470,6 @@ class GlobalObject : public JSC::JSGlobalObject {
void finishCreation(JSC::VM&);
friend void WebCore::JSBuiltinInternalFunctions::initialize(Zig::GlobalObject&);
WebCore::JSBuiltinInternalFunctions m_builtinInternalFunctions;
GlobalObject(JSC::VM& vm, JSC::Structure* structure);
GlobalObject(JSC::VM& vm, JSC::Structure* structure, uint32_t);
std::unique_ptr<WebCore::DOMConstructors> m_constructors;
uint8_t m_worldIsNormal;
JSDOMStructureMap m_structures WTF_GUARDED_BY_LOCK(m_gcLock);
Expand Down Expand Up @@ -556,6 +572,17 @@ class GlobalObject : public JSC::JSGlobalObject {
WTF::Vector<JSC::Strong<JSC::JSFunction>> m_ffiFunctions;
};

class EvalGlobalObject : public GlobalObject {
public:
static const JSC::GlobalObjectMethodTable s_globalObjectMethodTable;
static JSC::JSValue moduleLoaderEvaluate(JSGlobalObject*, JSC::JSModuleLoader*, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue, JSC::JSValue);

EvalGlobalObject(JSC::VM& vm, JSC::Structure* structure)
: GlobalObject(vm, structure, &s_globalObjectMethodTable)
{
}
};

} // namespace Zig

// TODO: move this
Expand Down
17 changes: 17 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 @@ -4074,6 +4075,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
3 changes: 2 additions & 1 deletion src/bun.js/bindings/exports.zig
Expand Up @@ -45,9 +45,10 @@ pub const ZigGlobalObject = extern struct {
console: *anyopaque,
context_id: i32,
mini_mode: bool,
eval_mode: bool,
worker_ptr: ?*anyopaque,
) *JSGlobalObject {
const global = shim.cppFn("create", .{ console, context_id, mini_mode, worker_ptr });
const global = shim.cppFn("create", .{ console, context_id, mini_mode, eval_mode, worker_ptr });
Backtrace.reloadHandlers() catch unreachable;
return global;
}
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/bindings/headers.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/bun.js/bindings/headers.zig

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3765032

Please sign in to comment.