mirror of
https://github.com/oven-sh/bun
synced 2026-02-07 01:18:51 +00:00
Compare commits
9 Commits
ciro/fix-a
...
kai/vm-com
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96acbdbc2d | ||
|
|
1980ba9124 | ||
|
|
3f75a5dbdc | ||
|
|
68ce0dd3f6 | ||
|
|
ab870c5bd8 | ||
|
|
76af83a2eb | ||
|
|
a5a055af94 | ||
|
|
fccc8b8902 | ||
|
|
0d171c42fa |
@@ -637,6 +637,7 @@ file(GLOB BUN_CXX_SOURCES ${CONFIGURE_DEPENDS}
|
||||
${CWD}/src/bun.js/bindings/v8/shim/*.cpp
|
||||
${CWD}/src/bake/*.cpp
|
||||
${CWD}/src/deps/*.cpp
|
||||
${CWD}/src/vm/*.cpp
|
||||
${BUN_USOCKETS_SOURCE}/src/crypto/*.cpp
|
||||
)
|
||||
|
||||
@@ -797,7 +798,7 @@ target_include_directories(${bun} PRIVATE
|
||||
${NODEJS_HEADERS_PATH}/include
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
if(NOT WIN32)
|
||||
target_include_directories(${bun} PRIVATE ${CWD}/src/bun.js/bindings/libuv)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -804,8 +804,8 @@ JSC_DEFINE_HOST_FUNCTION(Process_functionChdir, (JSC::JSGlobalObject * globalObj
|
||||
RELEASE_AND_RETURN(scope, JSC::JSValue::encode(result));
|
||||
}
|
||||
|
||||
static HashMap<String, int>* signalNameToNumberMap = nullptr;
|
||||
static HashMap<int, String>* signalNumberToNameMap = nullptr;
|
||||
static std::optional<HashMap<String, int>> signalNameToNumberMap;
|
||||
static std::optional<HashMap<int, String>> signalNumberToNameMap;
|
||||
|
||||
// On windows, signals need to have a handle to the uv_signal_t. When sigaction is used, this is kept track globally for you.
|
||||
struct SignalHandleValue {
|
||||
@@ -860,7 +860,7 @@ static void loadSignalNumberMap()
|
||||
static std::once_flag signalNameToNumberMapOnceFlag;
|
||||
std::call_once(signalNameToNumberMapOnceFlag, [] {
|
||||
auto signalNames = getSignalNames();
|
||||
signalNameToNumberMap = new HashMap<String, int>();
|
||||
signalNameToNumberMap.emplace();
|
||||
signalNameToNumberMap->reserveInitialCapacity(31);
|
||||
#if OS(WINDOWS)
|
||||
// libuv supported signals
|
||||
@@ -1072,7 +1072,7 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e
|
||||
{
|
||||
if (Bun__isMainThreadVM()) {
|
||||
// IPC handlers
|
||||
if (eventName.string() == "message"_s || eventName.string() == "disconnect"_s) {
|
||||
if (eventName == "message" || eventName == "disconnect") {
|
||||
auto* global = jsCast<GlobalObject*>(eventEmitter.scriptExecutionContext()->jsGlobalObject());
|
||||
auto& vm = JSC::getVM(global);
|
||||
auto messageListenerCount = eventEmitter.listenerCount(vm.propertyNames->message);
|
||||
@@ -1101,7 +1101,7 @@ static void onDidChangeListeners(EventEmitter& eventEmitter, const Identifier& e
|
||||
static std::once_flag signalNumberToNameMapOnceFlag;
|
||||
std::call_once(signalNumberToNameMapOnceFlag, [] {
|
||||
auto signalNames = getSignalNames();
|
||||
signalNumberToNameMap = new HashMap<int, String>();
|
||||
signalNumberToNameMap.emplace();
|
||||
signalNumberToNameMap->reserveInitialCapacity(31);
|
||||
signalNumberToNameMap->add(SIGHUP, signalNames[0]);
|
||||
signalNumberToNameMap->add(SIGINT, signalNames[1]);
|
||||
|
||||
@@ -76,20 +76,26 @@ bool JSNextTickQueue::isEmpty()
|
||||
|
||||
void JSNextTickQueue::drain(JSC::VM& vm, JSC::JSGlobalObject* globalObject)
|
||||
{
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
bool mustResetContext = false;
|
||||
if (isEmpty()) {
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
vm.drainMicrotasks();
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
mustResetContext = true;
|
||||
}
|
||||
|
||||
if (!isEmpty()) {
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
if (mustResetContext) {
|
||||
globalObject->m_asyncContextData.get()->putInternalField(vm, 0, jsUndefined());
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
}
|
||||
auto* drainFn = internalField(2).get().getObject();
|
||||
auto throwScope = DECLARE_THROW_SCOPE(vm);
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
MarkedArgumentBuffer drainArgs;
|
||||
JSC::call(globalObject, drainFn, drainArgs, "Failed to drain next tick queue"_s);
|
||||
RETURN_IF_EXCEPTION(throwScope, );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
|
||||
#include "JavaScriptCore/JSCInlines.h"
|
||||
|
||||
#include "../vm/SigintWatcher.h"
|
||||
|
||||
namespace Bun {
|
||||
using namespace WebCore;
|
||||
|
||||
@@ -51,8 +53,7 @@ using namespace WebCore;
|
||||
/// This code is adapted/inspired from JSC::constructFunction, which is used for function declarations.
|
||||
static JSC::JSFunction* constructAnonymousFunction(JSC::JSGlobalObject* globalObject, const ArgList& args, const SourceOrigin& sourceOrigin, const String& fileName = String(), JSC::SourceTaintedOrigin sourceTaintOrigin = JSC::SourceTaintedOrigin::Untainted, TextPosition position = TextPosition(), JSC::JSScope* scope = nullptr);
|
||||
static String stringifyAnonymousFunction(JSGlobalObject* globalObject, const ArgList& args, ThrowScope& scope, int* outOffset);
|
||||
|
||||
NodeVMGlobalObject* createContextImpl(JSC::VM& vm, JSGlobalObject* globalObject, JSObject* sandbox);
|
||||
static std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey);
|
||||
|
||||
/// For some reason Node has this error message with a grammar error and we have to match it so the tests pass:
|
||||
/// `The "<name>" argument must be an vm.Context`
|
||||
@@ -149,10 +150,10 @@ template<typename, JSC::SubspaceAccess mode> JSC::GCClient::IsoSubspace* NodeVMG
|
||||
[](auto& server) -> JSC::HeapCellType& { return server.m_heapCellTypeForNodeVMGlobalObject; });
|
||||
}
|
||||
|
||||
NodeVMGlobalObject* NodeVMGlobalObject::create(JSC::VM& vm, JSC::Structure* structure)
|
||||
NodeVMGlobalObject* NodeVMGlobalObject::create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options)
|
||||
{
|
||||
auto* cell = new (NotNull, JSC::allocateCell<NodeVMGlobalObject>(vm)) NodeVMGlobalObject(vm, structure);
|
||||
cell->finishCreation(vm);
|
||||
cell->finishCreation(vm, options);
|
||||
return cell;
|
||||
}
|
||||
|
||||
@@ -162,9 +163,12 @@ Structure* NodeVMGlobalObject::createStructure(JSC::VM& vm, JSC::JSValue prototy
|
||||
return JSC::Structure::create(vm, nullptr, prototype, JSC::TypeInfo(JSC::GlobalObjectType, StructureFlags & ~IsImmutablePrototypeExoticObject), info());
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::finishCreation(JSC::VM&)
|
||||
void NodeVMGlobalObject::finishCreation(JSC::VM&, NodeVMContextOptions options)
|
||||
{
|
||||
Base::finishCreation(vm());
|
||||
setEvalEnabled(options.allowStrings, "Code generation from strings disallowed for this context"_s);
|
||||
setWebAssemblyEnabled(options.allowWasm, "Wasm code generation disallowed by embedder"_s);
|
||||
vm().ensureTerminationException();
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::destroy(JSCell* cell)
|
||||
@@ -174,6 +178,7 @@ void NodeVMGlobalObject::destroy(JSCell* cell)
|
||||
|
||||
NodeVMGlobalObject::~NodeVMGlobalObject()
|
||||
{
|
||||
SigintWatcher::get().unregisterGlobalObject(this);
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::setContextifiedObject(JSC::JSObject* contextifiedObject)
|
||||
@@ -186,6 +191,11 @@ void NodeVMGlobalObject::clearContextifiedObject()
|
||||
m_sandbox.clear();
|
||||
}
|
||||
|
||||
void NodeVMGlobalObject::sigintReceived()
|
||||
{
|
||||
vm().notifyNeedTermination();
|
||||
}
|
||||
|
||||
bool NodeVMGlobalObject::put(JSCell* cell, JSGlobalObject* globalObject, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
|
||||
{
|
||||
// if (!propertyName.isSymbol())
|
||||
@@ -779,6 +789,34 @@ static bool handleException(JSGlobalObject* globalObject, VM& vm, NakedPtr<Excep
|
||||
return false;
|
||||
}
|
||||
|
||||
extern "C" void Bun__ensureSignalHandler();
|
||||
|
||||
static void ensureSigintHandler()
|
||||
{
|
||||
#if !OS(WINDOWS)
|
||||
Bun__ensureSignalHandler();
|
||||
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(struct sigaction));
|
||||
|
||||
// Set the handler in the action struct
|
||||
action.sa_handler = [](int signalNumber) {
|
||||
SigintWatcher::get().signalReceived();
|
||||
};
|
||||
|
||||
// Clear the sa_mask
|
||||
sigemptyset(&action.sa_mask);
|
||||
sigaddset(&action.sa_mask, SIGINT);
|
||||
action.sa_flags = SA_RESTART;
|
||||
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
|
||||
SigintWatcher::get().install();
|
||||
#else
|
||||
static_assert(false, "TODO(@heimskr): implement sigint handler on Windows");
|
||||
#endif
|
||||
}
|
||||
|
||||
static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVMScript* script, JSObject* contextifiedObject, JSValue optionsArg, bool allowStringInPlaceOfOptions = false)
|
||||
{
|
||||
|
||||
@@ -797,6 +835,11 @@ static JSC::EncodedJSValue runInContext(NodeVMGlobalObject* globalObject, NodeVM
|
||||
// Set the contextified object before evaluating
|
||||
globalObject->setContextifiedObject(contextifiedObject);
|
||||
|
||||
if (options.breakOnSigint) {
|
||||
ensureSigintHandler();
|
||||
SigintWatcher::get().registerGlobalObject(globalObject);
|
||||
}
|
||||
|
||||
NakedPtr<Exception> exception;
|
||||
JSValue result = JSC::evaluate(globalObject, script->source(), globalObject, exception);
|
||||
|
||||
@@ -894,6 +937,10 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInThisContext, (JSGlobalObject * globalObject,
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options.breakOnSigint) {
|
||||
ensureSigintHandler();
|
||||
}
|
||||
|
||||
NakedPtr<Exception> exception;
|
||||
JSValue result = JSC::evaluate(globalObject, script->source(), globalObject, exception);
|
||||
|
||||
@@ -942,9 +989,18 @@ JSC_DEFINE_HOST_FUNCTION(vmModuleRunInNewContext, (JSGlobalObject * globalObject
|
||||
|
||||
JSObject* sandbox = asObject(contextArg);
|
||||
|
||||
JSValue contextOptionsArg = callFrame->argument(2);
|
||||
|
||||
NodeVMContextOptions contextOptions {};
|
||||
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, contextOptionsArg, contextOptions, "contextCodeGeneration")) {
|
||||
return *encodedException;
|
||||
}
|
||||
|
||||
// Create context and run code
|
||||
auto* context = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure());
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure(),
|
||||
contextOptions);
|
||||
|
||||
context->setContextifiedObject(sandbox);
|
||||
|
||||
@@ -1148,7 +1204,7 @@ JSC_DEFINE_HOST_FUNCTION(scriptRunInNewContext, (JSGlobalObject * globalObject,
|
||||
auto* zigGlobal = defaultGlobalObject(globalObject);
|
||||
JSObject* context = asObject(contextObjectValue);
|
||||
auto* targetContext = NodeVMGlobalObject::create(
|
||||
vm, zigGlobal->NodeVMGlobalObjectStructure());
|
||||
vm, zigGlobal->NodeVMGlobalObjectStructure(), {});
|
||||
|
||||
return runInContext(targetContext, script, context, callFrame->argument(1));
|
||||
}
|
||||
@@ -1158,21 +1214,6 @@ Structure* createNodeVMGlobalObjectStructure(JSC::VM& vm)
|
||||
return NodeVMGlobalObject::createStructure(vm, jsNull());
|
||||
}
|
||||
|
||||
NodeVMGlobalObject* createContextImpl(JSC::VM& vm, JSGlobalObject* globalObject, JSObject* sandbox)
|
||||
{
|
||||
auto* targetContext = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure());
|
||||
|
||||
// Set sandbox as contextified object
|
||||
targetContext->setContextifiedObject(sandbox);
|
||||
|
||||
// Store context in WeakMap for isContext checks
|
||||
auto* zigGlobalObject = defaultGlobalObject(globalObject);
|
||||
zigGlobalObject->vmModuleContextMap()->set(vm, sandbox, targetContext);
|
||||
|
||||
return targetContext;
|
||||
}
|
||||
|
||||
JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject, CallFrame* callFrame))
|
||||
{
|
||||
VM& vm = globalObject->vm();
|
||||
@@ -1194,31 +1235,17 @@ JSC_DEFINE_HOST_FUNCTION(vmModule_createContext, (JSGlobalObject * globalObject,
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options"_s, "object"_s, optionsArg);
|
||||
}
|
||||
|
||||
// If options is provided, validate name and origin properties
|
||||
if (optionsArg.isObject()) {
|
||||
JSObject* options = asObject(optionsArg);
|
||||
NodeVMContextOptions contextOptions {};
|
||||
|
||||
// Check name property
|
||||
if (JSValue nameValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "name"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!nameValue.isUndefined() && !nameValue.isString()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.name"_s, "string"_s, nameValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Check origin property
|
||||
if (JSValue originValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "origin"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!originValue.isUndefined() && !originValue.isString()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.origin"_s, "string"_s, originValue);
|
||||
}
|
||||
}
|
||||
if (auto encodedException = getNodeVMContextOptions(globalObject, vm, scope, optionsArg, contextOptions, "codeGeneration")) {
|
||||
return *encodedException;
|
||||
}
|
||||
|
||||
JSObject* sandbox = asObject(contextArg);
|
||||
|
||||
auto* targetContext = NodeVMGlobalObject::create(vm,
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure());
|
||||
defaultGlobalObject(globalObject)->NodeVMGlobalObjectStructure(),
|
||||
contextOptions);
|
||||
|
||||
// Set sandbox as contextified object
|
||||
targetContext->setContextifiedObject(sandbox);
|
||||
@@ -1571,8 +1598,8 @@ static String stringifyAnonymousFunction(JSGlobalObject* globalObject, const Arg
|
||||
auto body = args.at(0).toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
program = tryMakeString("(function () {"_s, body, "})"_s);
|
||||
*outOffset = "(function () {"_s.length();
|
||||
program = tryMakeString("(function () {\n"_s, body, "\n})"_s);
|
||||
*outOffset = "(function () {\n"_s.length();
|
||||
|
||||
if (UNLIKELY(!program)) {
|
||||
throwOutOfMemoryError(globalObject, scope);
|
||||
@@ -1596,8 +1623,8 @@ static String stringifyAnonymousFunction(JSGlobalObject* globalObject, const Arg
|
||||
auto body = args.at(parameterCount).toWTFString(globalObject);
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
program = tryMakeString("(function ("_s, paramString.toString(), ") {"_s, body, "})"_s);
|
||||
*outOffset = "(function ("_s.length() + paramString.length() + ") {"_s.length();
|
||||
program = tryMakeString("(function ("_s, paramString.toString(), ") {\n"_s, body, "\n})"_s);
|
||||
*outOffset = "(function ("_s.length() + paramString.length() + ") {\n"_s.length();
|
||||
|
||||
if (UNLIKELY(!program)) {
|
||||
throwOutOfMemoryError(globalObject, scope);
|
||||
@@ -1608,4 +1635,68 @@ static String stringifyAnonymousFunction(JSGlobalObject* globalObject, const Arg
|
||||
return program;
|
||||
}
|
||||
|
||||
// Returns an encoded exception if the options are invalid.
|
||||
// Otherwise, returns an empty optional.
|
||||
static std::optional<JSC::EncodedJSValue> getNodeVMContextOptions(JSGlobalObject* globalObject, JSC::VM& vm, JSC::ThrowScope& scope, JSValue optionsArg, NodeVMContextOptions& outOptions, ASCIILiteral codeGenerationKey)
|
||||
{
|
||||
outOptions = {};
|
||||
|
||||
// If options is provided, validate name and origin properties
|
||||
if (!optionsArg.isObject()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
JSObject* options = asObject(optionsArg);
|
||||
|
||||
// Check name property
|
||||
if (JSValue nameValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "name"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!nameValue.isUndefined() && !nameValue.isString()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.name"_s, "string"_s, nameValue);
|
||||
}
|
||||
}
|
||||
|
||||
// Check origin property
|
||||
if (JSValue originValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, "origin"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!originValue.isUndefined() && !originValue.isString()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, "options.origin"_s, "string"_s, originValue);
|
||||
}
|
||||
}
|
||||
|
||||
if (JSValue codeGenerationValue = options->getIfPropertyExists(globalObject, Identifier::fromString(vm, codeGenerationKey))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
|
||||
if (codeGenerationValue.isUndefined()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!codeGenerationValue.isObject()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, WTF::makeString("options."_s, codeGenerationKey), "object"_s, codeGenerationValue);
|
||||
}
|
||||
|
||||
JSObject* codeGenerationObject = asObject(codeGenerationValue);
|
||||
|
||||
if (JSValue allowStringsValue = codeGenerationObject->getIfPropertyExists(globalObject, Identifier::fromString(vm, "strings"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!allowStringsValue.isBoolean()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, WTF::makeString("options."_s, codeGenerationKey, ".strings"_s), "boolean"_s, allowStringsValue);
|
||||
}
|
||||
|
||||
outOptions.allowStrings = allowStringsValue.toBoolean(globalObject);
|
||||
}
|
||||
|
||||
if (JSValue allowWasmValue = codeGenerationObject->getIfPropertyExists(globalObject, Identifier::fromString(vm, "wasm"_s))) {
|
||||
RETURN_IF_EXCEPTION(scope, {});
|
||||
if (!allowWasmValue.isBoolean()) {
|
||||
return ERR::INVALID_ARG_TYPE(scope, globalObject, WTF::makeString("options."_s, codeGenerationKey, ".wasm"_s), "boolean"_s, allowWasmValue);
|
||||
}
|
||||
|
||||
outOptions.allowWasm = allowWasmValue.toBoolean(globalObject);
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
#include <JavaScriptCore/Nodes.h>
|
||||
|
||||
namespace Bun {
|
||||
class NodeVMContextOptions final {
|
||||
public:
|
||||
bool allowStrings = true;
|
||||
bool allowWasm = true;
|
||||
};
|
||||
|
||||
// This class represents a sandboxed global object for vm contexts
|
||||
class NodeVMGlobalObject final : public Bun::GlobalScope {
|
||||
@@ -23,16 +28,17 @@ public:
|
||||
static constexpr JSC::DestructionMode needsDestruction = NeedsDestruction;
|
||||
|
||||
template<typename, JSC::SubspaceAccess mode> static JSC::GCClient::IsoSubspace* subspaceFor(JSC::VM& vm);
|
||||
static NodeVMGlobalObject* create(JSC::VM& vm, JSC::Structure* structure);
|
||||
static NodeVMGlobalObject* create(JSC::VM& vm, JSC::Structure* structure, NodeVMContextOptions options);
|
||||
static Structure* createStructure(JSC::VM& vm, JSC::JSValue prototype);
|
||||
|
||||
DECLARE_INFO;
|
||||
DECLARE_VISIT_CHILDREN;
|
||||
|
||||
void finishCreation(JSC::VM&);
|
||||
void finishCreation(JSC::VM&, NodeVMContextOptions options);
|
||||
static void destroy(JSCell* cell);
|
||||
void setContextifiedObject(JSC::JSObject* contextifiedObject);
|
||||
void clearContextifiedObject();
|
||||
void sigintReceived();
|
||||
|
||||
// Override property access to delegate to contextified object
|
||||
static bool getOwnPropertySlot(JSObject*, JSGlobalObject*, JSC::PropertyName, JSC::PropertySlot&);
|
||||
|
||||
@@ -8,7 +8,7 @@ const ObjectFreeze = Object.freeze;
|
||||
const { createContext, isContext, Script, runInNewContext, runInThisContext, compileFunction } = vm;
|
||||
|
||||
function runInContext(code, context, options) {
|
||||
return new Script(code, options).runInContext(context);
|
||||
return new Script(code, options).runInContext(context, options);
|
||||
}
|
||||
|
||||
function createScript(code, options) {
|
||||
|
||||
50
src/vm/Semaphore.cpp
Normal file
50
src/vm/Semaphore.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "Semaphore.h"
|
||||
|
||||
namespace Bun {
|
||||
|
||||
Semaphore::Semaphore(unsigned int value)
|
||||
{
|
||||
#if OS(WINDOWS)
|
||||
uv_sem_init(&m_semaphore, value);
|
||||
#elif OS(DARWIN)
|
||||
semaphore_create(mach_task_self(), &m_semaphore, SYNC_POLICY_FIFO, value);
|
||||
#else
|
||||
sem_init(&m_semaphore, 0, value);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::~Semaphore()
|
||||
{
|
||||
#if OS(WINDOWS)
|
||||
uv_sem_destroy(&m_semaphore);
|
||||
#elif OS(DARWIN)
|
||||
semaphore_destroy(mach_task_self(), m_semaphore);
|
||||
#else
|
||||
sem_destroy(&m_semaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::signal()
|
||||
{
|
||||
#if OS(WINDOWS)
|
||||
uv_sem_post(&m_semaphore);
|
||||
return true;
|
||||
#elif OS(DARWIN)
|
||||
return semaphore_signal(m_semaphore) == KERN_SUCCESS;
|
||||
#else
|
||||
return sem_post(&m_semaphore) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::wait()
|
||||
{
|
||||
#if OS(WINDOWS)
|
||||
uv_sem_wait(&m_semaphore);
|
||||
return true;
|
||||
#elif OS(DARWIN)
|
||||
return semaphore_wait(m_semaphore) == KERN_SUCCESS;
|
||||
#else
|
||||
return sem_wait(&m_semaphore) == 0;
|
||||
#endif
|
||||
}
|
||||
} // namespace Bun
|
||||
32
src/vm/Semaphore.h
Normal file
32
src/vm/Semaphore.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#if OS(WINDOWS)
|
||||
#include <uv.h>
|
||||
#elif OS(DARWIN)
|
||||
#include <mach/task.h>
|
||||
#include <mach/semaphore.h>
|
||||
#else
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class Semaphore {
|
||||
public:
|
||||
Semaphore(unsigned int value);
|
||||
~Semaphore();
|
||||
|
||||
bool signal();
|
||||
bool wait();
|
||||
|
||||
private:
|
||||
#if OS(WINDOWS)
|
||||
uv_sem_t m_semaphore;
|
||||
#elif OS(DARWIN)
|
||||
semaphore_t m_semaphore;
|
||||
#else
|
||||
sem_t m_semaphore;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
99
src/vm/SigintWatcher.cpp
Normal file
99
src/vm/SigintWatcher.cpp
Normal file
@@ -0,0 +1,99 @@
|
||||
#include "NodeVM.h"
|
||||
#include "SigintWatcher.h"
|
||||
|
||||
extern "C" void Bun__onPosixSignal(int signalNumber);
|
||||
|
||||
namespace Bun {
|
||||
|
||||
SigintWatcher SigintWatcher::s_instance;
|
||||
|
||||
SigintWatcher::SigintWatcher()
|
||||
: m_semaphore(1)
|
||||
{
|
||||
m_globalObjects.reserve(16);
|
||||
}
|
||||
|
||||
SigintWatcher::~SigintWatcher()
|
||||
{
|
||||
uninstall();
|
||||
}
|
||||
|
||||
void SigintWatcher::install()
|
||||
{
|
||||
if (m_installed.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_thread = std::thread([this] {
|
||||
while (m_installed.load()) {
|
||||
bool success = m_semaphore.wait();
|
||||
ASSERT(success);
|
||||
if (m_waiting.test_and_set()) {
|
||||
m_waiting.clear();
|
||||
if (!signalAll()) {
|
||||
Bun__onPosixSignal(SIGINT);
|
||||
}
|
||||
} else {
|
||||
m_waiting.clear();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void SigintWatcher::uninstall()
|
||||
{
|
||||
if (m_installed.exchange(false)) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void SigintWatcher::signalReceived()
|
||||
{
|
||||
if (!m_waiting.test_and_set()) {
|
||||
bool success = m_semaphore.signal();
|
||||
ASSERT(success);
|
||||
}
|
||||
}
|
||||
|
||||
void SigintWatcher::registerGlobalObject(NodeVMGlobalObject* globalObject)
|
||||
{
|
||||
std::unique_lock lock(m_globalObjectsMutex);
|
||||
|
||||
m_globalObjects.push_back(globalObject);
|
||||
}
|
||||
|
||||
void SigintWatcher::unregisterGlobalObject(NodeVMGlobalObject* globalObject)
|
||||
{
|
||||
std::unique_lock lock(m_globalObjectsMutex);
|
||||
|
||||
auto iter = std::find(m_globalObjects.begin(), m_globalObjects.end(), globalObject);
|
||||
|
||||
if (iter == m_globalObjects.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::swap(*iter, m_globalObjects.back());
|
||||
m_globalObjects.pop_back();
|
||||
}
|
||||
|
||||
SigintWatcher& SigintWatcher::get()
|
||||
{
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
bool SigintWatcher::signalAll()
|
||||
{
|
||||
std::unique_lock lock(m_globalObjectsMutex);
|
||||
|
||||
if (m_globalObjects.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (NodeVMGlobalObject* globalObject : m_globalObjects) {
|
||||
globalObject->sigintReceived();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Bun
|
||||
41
src/vm/SigintWatcher.h
Normal file
41
src/vm/SigintWatcher.h
Normal file
@@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "root.h"
|
||||
#include "Semaphore.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
namespace Bun {
|
||||
|
||||
class NodeVMGlobalObject;
|
||||
|
||||
class SigintWatcher {
|
||||
public:
|
||||
SigintWatcher();
|
||||
~SigintWatcher();
|
||||
|
||||
void install();
|
||||
void uninstall();
|
||||
void signalReceived();
|
||||
void registerGlobalObject(NodeVMGlobalObject* globalObject);
|
||||
void unregisterGlobalObject(NodeVMGlobalObject* globalObject);
|
||||
|
||||
static SigintWatcher& get();
|
||||
|
||||
private:
|
||||
std::thread m_thread;
|
||||
std::atomic_bool m_installed = false;
|
||||
std::atomic_flag m_waiting = false;
|
||||
Semaphore m_semaphore;
|
||||
std::mutex m_globalObjectsMutex;
|
||||
std::vector<NodeVMGlobalObject*> m_globalObjects;
|
||||
|
||||
bool signalAll();
|
||||
|
||||
static SigintWatcher s_instance;
|
||||
};
|
||||
|
||||
} // namespace Bun
|
||||
101
test/js/node/test/parallel/test-vm-codegen.js
Normal file
101
test/js/node/test/parallel/test-vm-codegen.js
Normal file
@@ -0,0 +1,101 @@
|
||||
'use strict';
|
||||
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const { createContext, runInContext, runInNewContext } = require('vm');
|
||||
|
||||
const WASM_BYTES = Buffer.from(
|
||||
[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
|
||||
|
||||
{
|
||||
const ctx = createContext({ WASM_BYTES });
|
||||
const test = 'eval(""); new WebAssembly.Module(WASM_BYTES);';
|
||||
runInContext(test, ctx);
|
||||
|
||||
runInNewContext(test, { WASM_BYTES }, {
|
||||
contextCodeGeneration: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const ctx = createContext({}, {
|
||||
codeGeneration: {
|
||||
strings: false,
|
||||
},
|
||||
});
|
||||
|
||||
const EvalError = runInContext('EvalError', ctx);
|
||||
assert.throws(() => {
|
||||
runInContext('eval("x")', ctx);
|
||||
}, EvalError);
|
||||
}
|
||||
|
||||
{
|
||||
const ctx = createContext({ WASM_BYTES }, {
|
||||
codeGeneration: {
|
||||
wasm: false,
|
||||
},
|
||||
});
|
||||
|
||||
const CompileError = runInContext('WebAssembly.CompileError', ctx);
|
||||
assert.rejects(() => {
|
||||
return runInContext('try { WebAssembly.instantiate(new WebAssembly.Module(WASM_BYTES)); } catch (e) { Promise.reject(e); }', ctx);
|
||||
}, CompileError);
|
||||
}
|
||||
|
||||
assert.throws(() => {
|
||||
runInNewContext('eval("x")', {}, {
|
||||
contextCodeGeneration: {
|
||||
strings: false,
|
||||
},
|
||||
});
|
||||
}, {
|
||||
name: 'EvalError'
|
||||
});
|
||||
|
||||
assert.rejects(() => {
|
||||
return runInNewContext('try { WebAssembly.instantiate(new WebAssembly.Module(WASM_BYTES)); } catch (e) { Promise.reject(e); }', { WASM_BYTES }, {
|
||||
contextCodeGeneration: {
|
||||
wasm: false,
|
||||
},
|
||||
});
|
||||
}, {
|
||||
name: 'CompileError'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
createContext({}, {
|
||||
codeGeneration: {
|
||||
strings: 0,
|
||||
},
|
||||
});
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
runInNewContext('eval("x")', {}, {
|
||||
contextCodeGeneration: {
|
||||
wasm: 1,
|
||||
},
|
||||
});
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE'
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
createContext({}, {
|
||||
codeGeneration: 1,
|
||||
});
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
|
||||
assert.throws(() => {
|
||||
createContext({}, {
|
||||
codeGeneration: null,
|
||||
});
|
||||
}, {
|
||||
code: 'ERR_INVALID_ARG_TYPE',
|
||||
});
|
||||
Reference in New Issue
Block a user