SERVER-115422 Implement the WASM MozJS wrapper and WIT exported interface (#48033)
Co-authored-by: Andrew Bradshaw <andrew.bradshaw@mongodb.com> GitOrigin-RevId: ba0a73f930f7120fa0fecef4611f43e00d2f0b91
This commit is contained in:
parent
8bb52ae9bf
commit
8ceb6ed988
33
MODULE.bazel
33
MODULE.bazel
@ -57,6 +57,28 @@ filegroup(
|
||||
] * 5,
|
||||
)
|
||||
|
||||
http_file(
|
||||
name = "mozjs_wasm",
|
||||
downloaded_file_path = "mozjs_wasm_api.wasm",
|
||||
sha256 = "3d5a780b657fdfe66cc2f45f141e9e89e9bcb5ab7a1c90f0fb4778b5c0080b15",
|
||||
urls = [
|
||||
# Implements retry by relisting each url multiple times to be used as a failover.
|
||||
# TODO(SERVER-86719): Re-implement http_archive to allow sleeping between retries
|
||||
"https://mdb-build-public.s3.us-east-1.amazonaws.com/wasm_scratch/mozjs_wasm_api.wasm",
|
||||
] * 5,
|
||||
)
|
||||
|
||||
http_file(
|
||||
name = "spidermonkey_wasip2_dist",
|
||||
downloaded_file_path = "spidermonkey-wasip2-release.tar.gz",
|
||||
sha256 = "f528e5730721559781aef47d355079da63185588bb14ebb6bf5266d4aa5e3eb0",
|
||||
urls = [
|
||||
# Implements retry by relisting each url multiple times to be used as a failover.
|
||||
# TODO(SERVER-86719): Re-implement http_archive to allow sleeping between retries
|
||||
"https://mdb-build-public.s3.us-east-1.amazonaws.com/wasm_scratch/spidermonkey-wasip2-release.tar.gz",
|
||||
] * 5,
|
||||
)
|
||||
|
||||
# SourceGraph indexer
|
||||
http_file(
|
||||
name = "scip-clang",
|
||||
@ -362,3 +384,14 @@ wasi_deps = use_repo_rule(
|
||||
)
|
||||
|
||||
wasi_deps(name = "wasi_sdk")
|
||||
|
||||
spidermonkey_repo = use_repo_rule(
|
||||
"//src/mongo/scripting/mozjs/wasm:spidermonkey_repo.bzl",
|
||||
"spidermonkey_repository",
|
||||
)
|
||||
|
||||
spidermonkey_repo(
|
||||
name = "spidermonkey",
|
||||
repository_file = "//src/mongo/scripting/mozjs/wasm/spider-monkey:spider-monkey-repository",
|
||||
version_file = "//src/mongo/scripting/mozjs/wasm/spider-monkey:spider-monkey-version",
|
||||
)
|
||||
|
||||
2
MODULE.bazel.lock
generated
2
MODULE.bazel.lock
generated
@ -1306,7 +1306,7 @@
|
||||
"usagesDigest": "9e6XGcxkYrSsu0+gftPsVGZrX02VFK68P4HmulrwVAE=",
|
||||
"recordedFileInputs": {
|
||||
"@@//src/third_party/wasmtime/BUILD.bazel.wasmtime": "f8f59cc6f0f55ead667c8b438e3d056250807cbb565b0cfe1c4b7bce196ca22f",
|
||||
"@@//MODULE.bazel": "8fc6eef7f959c870cba70c101a56f6e7148dfde24ce476d7f02f0e4002c929ea",
|
||||
"@@//MODULE.bazel": "dc17238883ec055ae6309e1489ddfec05de2c74dd5fd3671ce4153b9316d4a46",
|
||||
"@@rules_rust~~rust_host_tools~rust_host_tools//rust_host_tools": "ENOENT"
|
||||
},
|
||||
"recordedDirentsInputs": {},
|
||||
|
||||
@ -18,7 +18,6 @@ BOOST_DEFINES = [
|
||||
# our current Xcode 12 doesn't offer std::atomic_ref, so we cannot.
|
||||
"BOOST_FILESYSTEM_NO_CXX20_ATOMIC_REF",
|
||||
"BOOST_LOG_NO_SHORTHAND_NAMES",
|
||||
"BOOST_LOG_USE_NATIVE_SYSLOG",
|
||||
"BOOST_LOG_WITHOUT_THREAD_ATTR",
|
||||
"BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS",
|
||||
"BOOST_SYSTEM_NO_DEPRECATED",
|
||||
@ -27,6 +26,10 @@ BOOST_DEFINES = [
|
||||
] + select({
|
||||
"@//bazel/config:linkdynamic_not_shared_archive": ["BOOST_LOG_DYN_LINK"],
|
||||
"@//conditions:default": [],
|
||||
}) + select({
|
||||
"@platforms//os:windows": ["BOOST_LOG_WITHOUT_SYSLOG"],
|
||||
"@platforms//os:wasi": ["BOOST_LOG_WITHOUT_SYSLOG"],
|
||||
"@//conditions:default": ["BOOST_LOG_USE_NATIVE_SYSLOG"],
|
||||
})
|
||||
|
||||
ENTERPRISE_DEFINES = select({
|
||||
|
||||
@ -83,13 +83,21 @@ def _wasi_cc_toolchain_config_wasip2_impl(ctx):
|
||||
# can be used to find it if we use a different repository rule later.
|
||||
"--sysroot={}".format("external/_main~_repo_rules~wasi_sdk/share/wasi-sysroot"),
|
||||
"-fno-common",
|
||||
"-fno-omit-frame-pointer",
|
||||
"-Oz",
|
||||
"-ffunction-sections",
|
||||
"-fdata-sections",
|
||||
"-fvisibility=hidden",
|
||||
"-U_FORTIFY_SOURCE",
|
||||
"-D_FORTIFY_SOURCE=0",
|
||||
|
||||
# WASI emulation shims - enable syscall emulation for POSIX APIs
|
||||
"-D_WASI_EMULATED_SIGNAL",
|
||||
"-D_WASI_EMULATED_MMAN",
|
||||
"-D_WASI_EMULATED_PROCESS_CLOCKS",
|
||||
"-D_WASI_EMULATED_GETPID",
|
||||
|
||||
# WASI platform lacks syslog support
|
||||
"-DBOOST_LOG_WITHOUT_SYSLOG",
|
||||
])],
|
||||
)],
|
||||
)
|
||||
@ -103,6 +111,12 @@ def _wasi_cc_toolchain_config_wasip2_impl(ctx):
|
||||
flag_groups = [flag_group(flags = [
|
||||
"-std=c++20",
|
||||
"-fexceptions",
|
||||
|
||||
# Include headers for declarations needed by third-party code
|
||||
"-include",
|
||||
"wasm32-wasip2/assert.h", # assert() for Abseil
|
||||
"-include",
|
||||
"stdlib.h", # malloc/free for fmt
|
||||
])],
|
||||
)],
|
||||
)
|
||||
@ -115,16 +129,77 @@ def _wasi_cc_toolchain_config_wasip2_impl(ctx):
|
||||
actions = all_link_actions,
|
||||
flag_groups = [flag_group(flags = [
|
||||
"-Wl,--gc-sections",
|
||||
"-Wl,--strip-all",
|
||||
"-lc++",
|
||||
"-lc++abi",
|
||||
"-lwasi-emulated-signal",
|
||||
"-lwasi-emulated-mman",
|
||||
"-lwasi-emulated-process-clocks",
|
||||
"-lwasi-emulated-getpid",
|
||||
])],
|
||||
)],
|
||||
)
|
||||
|
||||
# Include path features (mirrors the Linux toolchain).
|
||||
# Without these, external-repo headers (like Abseil) are only added via
|
||||
# -iquote (quoted includes) and <angled> includes fail.
|
||||
all_compile = [
|
||||
ACTION_NAMES.preprocess_assemble,
|
||||
ACTION_NAMES.linkstamp_compile,
|
||||
ACTION_NAMES.c_compile,
|
||||
ACTION_NAMES.cpp_compile,
|
||||
ACTION_NAMES.cpp_header_parsing,
|
||||
ACTION_NAMES.cpp_module_compile,
|
||||
ACTION_NAMES.clif_match,
|
||||
ACTION_NAMES.objc_compile,
|
||||
ACTION_NAMES.objcpp_compile,
|
||||
]
|
||||
|
||||
include_paths_feature = feature(
|
||||
name = "include_paths",
|
||||
enabled = True,
|
||||
flag_sets = [flag_set(
|
||||
actions = all_compile,
|
||||
flag_groups = [
|
||||
flag_group(
|
||||
flags = ["-iquote", "%{quote_include_paths}"],
|
||||
iterate_over = "quote_include_paths",
|
||||
),
|
||||
flag_group(
|
||||
flags = ["-I%{include_paths}"],
|
||||
iterate_over = "include_paths",
|
||||
),
|
||||
flag_group(
|
||||
flags = ["-isystem", "%{system_include_paths}"],
|
||||
iterate_over = "system_include_paths",
|
||||
),
|
||||
],
|
||||
)],
|
||||
)
|
||||
|
||||
external_include_paths_feature = feature(
|
||||
name = "external_include_paths",
|
||||
enabled = True,
|
||||
flag_sets = [flag_set(
|
||||
actions = all_compile,
|
||||
flag_groups = [
|
||||
flag_group(
|
||||
flags = ["-isystem", "%{external_include_paths}"],
|
||||
iterate_over = "external_include_paths",
|
||||
expand_if_available = "external_include_paths",
|
||||
),
|
||||
],
|
||||
)],
|
||||
)
|
||||
|
||||
# WASI SDK sysroot path (relative to execroot)
|
||||
wasi_sysroot = "external/_main~_repo_rules~wasi_sdk/share/wasi-sysroot"
|
||||
|
||||
# Construct the toolchain.
|
||||
return [cc_common.create_cc_toolchain_config_info(
|
||||
ctx = ctx,
|
||||
action_configs = action_configs,
|
||||
tool_paths = tool_paths,
|
||||
toolchain_identifier = "wasi_sdk_cc_wasip2",
|
||||
host_system_name = "local",
|
||||
target_system_name = "wasi",
|
||||
@ -133,11 +208,18 @@ def _wasi_cc_toolchain_config_wasip2_impl(ctx):
|
||||
compiler = "clang",
|
||||
abi_version = "none",
|
||||
abi_libc_version = "wasi",
|
||||
builtin_sysroot = wasi_sysroot,
|
||||
cxx_builtin_include_directories = [
|
||||
wasi_sysroot + "/include",
|
||||
wasi_sysroot + "/include/wasm32-wasip2",
|
||||
],
|
||||
features = [
|
||||
compile_flags,
|
||||
cxx20_flags,
|
||||
link_flags,
|
||||
feature(name = "supports_dynamic_linker", enabled = True),
|
||||
include_paths_feature,
|
||||
external_include_paths_feature,
|
||||
feature(name = "supports_dynamic_linker", enabled = False),
|
||||
],
|
||||
)]
|
||||
|
||||
|
||||
@ -232,6 +232,7 @@ class Linter:
|
||||
files_to_ignore = set(
|
||||
[
|
||||
"src/mongo/scripting/mozjs/shell/PosixNSPR.cpp",
|
||||
"src/mongo/scripting/mozjs/wasm/wit_gen/generated/",
|
||||
"src/mongo/shell/linenoise.cpp",
|
||||
"src/mongo/shell/linenoise.h",
|
||||
"src/mongo/shell/mk_wcwidth.cpp",
|
||||
|
||||
@ -65,3 +65,14 @@ deadlock:mongo::(anonymous namespace)::SSLManagerOpenSSL::SSLManagerOpenSSL
|
||||
race:S2Loop::FindVertex
|
||||
race:S2EdgeIndex::IncrementQueryCount
|
||||
race:S2RegionCoverer::NewCandidate
|
||||
|
||||
# SERVER-115422: wasmtime's parallel compilation (via rayon) triggers TSan false positives.
|
||||
# TSan cannot unwind Rust stacks fully, so reported frames vary across runs: sometimes
|
||||
# wasmtime/rayon/cranelift-specific, sometimes bare Rust stdlib functions (core::ptr,
|
||||
# core::mem, alloc::, __rustc allocator). These patterns cover all observed variants.
|
||||
race:rayon_core
|
||||
race:wasmtime
|
||||
race:cranelift
|
||||
race:__rustc
|
||||
race:core::ptr
|
||||
race:core::mem
|
||||
|
||||
@ -27,7 +27,6 @@
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
|
||||
#include "mongo/logv2/log_domain_global.h"
|
||||
|
||||
#include <cstdint>
|
||||
@ -92,7 +91,14 @@ struct LogDomainGlobal::Impl {
|
||||
RamLogSink,
|
||||
UserAssertSink>
|
||||
ConsoleBackend;
|
||||
#ifndef _WIN32
|
||||
// Syslog support is conditionally compiled using Boost's BOOST_LOG_USE_NATIVE_SYSLOG macro.
|
||||
// This macro is defined by Boost.Log only when the platform provides a native syslog
|
||||
// implementation (via <syslog.h>). Platforms that lack syslog, notably WASI and Windows
|
||||
// will not have this macro defined, causing all syslog-related
|
||||
// types, members, and logic to be excluded from compilation. This avoids link errors and
|
||||
// unavailable-API references on those platforms without requiring us to maintain our own
|
||||
// platform-detection logic.
|
||||
#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
|
||||
typedef CompositeBackend<boost::log::sinks::syslog_backend,
|
||||
RamLogSink,
|
||||
RamLogSink,
|
||||
@ -118,7 +124,7 @@ struct LogDomainGlobal::Impl {
|
||||
boost::shared_ptr<boost::log::sinks::unlocked_sink<ConsoleBackend>> _consoleSink;
|
||||
boost::shared_ptr<boost::log::sinks::unlocked_sink<RotatableFileBackend>> _rotatableFileSink;
|
||||
boost::shared_ptr<boost::log::sinks::unlocked_sink<BacktraceBackend>> _backtraceSink;
|
||||
#ifndef _WIN32
|
||||
#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
|
||||
boost::shared_ptr<boost::log::sinks::unlocked_sink<SyslogBackend>> _syslogSink;
|
||||
#endif
|
||||
AtomicWord<int32_t> activeSourceThreadLocals{0};
|
||||
@ -152,7 +158,7 @@ LogDomainGlobal::Impl::Impl(LogDomainGlobal& parent) : _parent(parent) {
|
||||
}
|
||||
|
||||
Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions const& options) {
|
||||
#ifndef _WIN32
|
||||
#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
|
||||
if (options.syslogEnabled) {
|
||||
// Create a backend
|
||||
auto backend = boost::make_shared<SyslogBackend>(
|
||||
@ -244,7 +250,7 @@ Status LogDomainGlobal::Impl::configure(LogDomainGlobal::ConfigurationOptions co
|
||||
_consoleSink->set_formatter(mkFmt());
|
||||
if (_rotatableFileSink)
|
||||
_rotatableFileSink->set_formatter(mkFmt());
|
||||
#ifndef _WIN32
|
||||
#ifdef BOOST_LOG_USE_NATIVE_SYSLOG
|
||||
if (_syslogSink)
|
||||
_syslogSink->set_formatter(mkFmt());
|
||||
#endif
|
||||
|
||||
@ -80,6 +80,13 @@ inline NativeProcessId getCurrentNativeThreadId() {
|
||||
inline NativeProcessId getCurrentNativeThreadId() {
|
||||
return pthread_getthreadid_np();
|
||||
}
|
||||
#elif defined(__wasi__)
|
||||
inline NativeProcessId getCurrentNativeThreadId() {
|
||||
// WASI doesn't support threads or thread IDs, so return a constant value
|
||||
// Using the SERVER-115422 ticket number as a return number for better debugging
|
||||
// and searchability in logs.
|
||||
return 115422;
|
||||
}
|
||||
#else
|
||||
inline NativeProcessId getCurrentNativeThreadId() {
|
||||
return ::syscall(SYS_gettid);
|
||||
|
||||
@ -57,7 +57,8 @@
|
||||
|
||||
#ifdef _WIN32
|
||||
#define SECURE_RANDOM_BCRYPT
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__)
|
||||
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \
|
||||
defined(__EMSCRIPTEN__) || defined(__wasi__)
|
||||
#define SECURE_RANDOM_URANDOM
|
||||
#elif defined(__OpenBSD__)
|
||||
#define SECURE_RANDOM_ARCFOUR
|
||||
|
||||
@ -235,6 +235,39 @@ bool waitUntil(const void* uaddr,
|
||||
return timeoutOverflow || errno != ETIMEDOUT;
|
||||
}
|
||||
|
||||
#elif defined(__wasi__)
|
||||
|
||||
// WASI is single-threaded and has no futex or atomic-wait primitives.
|
||||
// notify* are no-ops; waitUntil returns false (timeout) when the value
|
||||
// hasn't changed because a single-threaded environment can never receive
|
||||
// an external wake-up. Returning true would cause an infinite busy-spin.
|
||||
|
||||
void notifyOne(const void* uaddr) {
|
||||
(void)uaddr;
|
||||
}
|
||||
|
||||
void notifyMany(const void* uaddr, int nToWake) {
|
||||
(void)uaddr;
|
||||
(void)nToWake;
|
||||
}
|
||||
|
||||
void notifyAll(const void* uaddr) {
|
||||
(void)uaddr;
|
||||
}
|
||||
|
||||
bool waitUntil(const void* uaddr,
|
||||
uint32_t old,
|
||||
boost::optional<system_clock::time_point> deadline) {
|
||||
// Value already changed before we started waiting.
|
||||
if (*static_cast<const uint32_t*>(uaddr) != old) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Single-threaded: the value cannot change during this call.
|
||||
// Return false (timeout) to avoid infinite busy-spin.
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
#error "Need an implementation of waitUntil(), notifyOne(), notifyMany(), notifyAll() for this OS"
|
||||
#endif
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
load("@bazel_skylib//lib:selects.bzl", "selects")
|
||||
load("//bazel:mongo_src_rules.bzl", "idl_generator", "mongo_cc_binary", "mongo_cc_library", "mongo_cc_test", "mongo_cc_unit_test")
|
||||
load("//bazel:mongo_src_rules.bzl", "idl_generator", "mongo_cc_library", "mongo_cc_unit_test")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
|
||||
@ -261,14 +261,6 @@ void Scope::storedFuncMod(OperationContext* opCtx) {
|
||||
[](OperationContext*, boost::optional<Timestamp>) { _lastVersion.fetchAndAdd(1); });
|
||||
}
|
||||
|
||||
void Scope::validateObjectIdString(const string& str) {
|
||||
uassert(10448, "invalid object id: length", str.size() == 24);
|
||||
auto isAllHex = [](StringData s) {
|
||||
return std::all_of(s.begin(), s.end(), [](char c) { return ctype::isXdigit(c); });
|
||||
};
|
||||
uassert(10430, "invalid object id: not hex", isAllHex(str));
|
||||
}
|
||||
|
||||
void Scope::loadStored(OperationContext* opCtx, bool ignoreNotConnected) {
|
||||
if (_localDBName.isEmpty()) {
|
||||
if (ignoreNotConnected)
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
#include "mongo/db/service_context.h"
|
||||
#include "mongo/platform/atomic_word.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/js_regex.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/modules.h"
|
||||
#include "mongo/util/time_support.h"
|
||||
@ -67,15 +68,6 @@ struct MONGO_MOD_NEEDS_REPLACEMENT JSFile {
|
||||
const StringData source;
|
||||
};
|
||||
|
||||
struct MONGO_MOD_PUBLIC JSRegEx {
|
||||
std::string pattern;
|
||||
std::string flags;
|
||||
|
||||
JSRegEx() = default;
|
||||
JSRegEx(std::string pattern, std::string flags)
|
||||
: pattern(std::move(pattern)), flags(std::move(flags)) {}
|
||||
};
|
||||
|
||||
class MONGO_MOD_OPEN Scope {
|
||||
Scope(const Scope&) = delete;
|
||||
Scope& operator=(const Scope&) = delete;
|
||||
@ -209,8 +201,6 @@ public:
|
||||
*/
|
||||
static void storedFuncMod(OperationContext* opCtx);
|
||||
|
||||
static void validateObjectIdString(const std::string& str);
|
||||
|
||||
/** gets the time at which the scope was created */
|
||||
Date_t getCreateTime() const {
|
||||
return _createTime;
|
||||
|
||||
@ -27,14 +27,20 @@
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#pragma once
|
||||
|
||||
#include <jsapi.h>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace mongo::mozjs {
|
||||
namespace mongo {
|
||||
|
||||
MozJSScopeBase* getMozJSScope(JSContext* cx) {
|
||||
return static_cast<MozJSScopeBase*>(JS_GetContextPrivate(cx));
|
||||
}
|
||||
struct JSRegEx {
|
||||
std::string pattern;
|
||||
std::string flags;
|
||||
|
||||
} // namespace mongo::mozjs
|
||||
JSRegEx() = default;
|
||||
JSRegEx(std::string pattern, std::string flags)
|
||||
: pattern(std::move(pattern)), flags(std::move(flags)) {}
|
||||
};
|
||||
|
||||
} // namespace mongo
|
||||
@ -10,26 +10,40 @@ exports_files(
|
||||
]),
|
||||
)
|
||||
|
||||
# Type sources shared between WASI and non-WASI builds.
|
||||
# status.cpp is excluded because WASI and non-WASI builds use different
|
||||
# implementations (wasm stub vs common/types).
|
||||
TYPES_SRCS = [
|
||||
"//src/mongo/scripting/mozjs/common/types:bindata.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:bson.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:code.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:dbpointer.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:dbref.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:maxkey.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:minkey.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:nativefunction.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberdecimal.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberint.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberlong.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:object.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:oid.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:regexp.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:timestamp.cpp",
|
||||
]
|
||||
|
||||
# Filegroup for WASI builds - exports source files for compilation in genrules.
|
||||
# WASI builds use a stub status.cpp from the wasm package.
|
||||
filegroup(
|
||||
name = "wasi_sources",
|
||||
srcs = glob(["*.cpp"]) + TYPES_SRCS + [
|
||||
"//src/mongo/scripting/mozjs/wasm:status.cpp",
|
||||
],
|
||||
visibility = ["//src/mongo/scripting/mozjs/wasm:__pkg__"],
|
||||
)
|
||||
|
||||
mongo_cc_library(
|
||||
name = "mozjs_common",
|
||||
srcs = glob(["*.cpp"]) + [
|
||||
"//src/mongo/scripting/mozjs/common/types:bindata.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:bson.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:code.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:dbpointer.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:dbref.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:maxkey.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:minkey.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:nativefunction.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberdecimal.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberint.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:numberlong.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:object.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:oid.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:regexp.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:status.cpp",
|
||||
"//src/mongo/scripting/mozjs/common/types:timestamp.cpp",
|
||||
],
|
||||
srcs = glob(["*.cpp"]) + TYPES_SRCS,
|
||||
copts = select({
|
||||
"@platforms//os:windows": [
|
||||
# The default MSVC preprocessor elides commas in some cases as a
|
||||
@ -41,10 +55,23 @@ mongo_cc_library(
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
textual_hdrs = glob(["*.defs"]),
|
||||
deps = [
|
||||
"//src/mongo/scripting:scripting_common",
|
||||
"//src/mongo/util:buildinfo",
|
||||
"//src/third_party/mozjs",
|
||||
srcs_select = [
|
||||
{
|
||||
"//bazel/config:wasi": ["//src/mongo/scripting/mozjs/wasm:status.cpp"],
|
||||
"//conditions:default": ["//src/mongo/scripting/mozjs/common/types:status.cpp"],
|
||||
},
|
||||
],
|
||||
textual_hdrs = glob(["*.defs"]),
|
||||
deps = select({
|
||||
"//bazel/config:wasi": [
|
||||
# WASI builds don't need scripting_common (avoids networking/SSL dependencies)
|
||||
# and use SpiderMonkey headers from @spidermonkey, not //src/third_party/mozjs
|
||||
"//src/mongo/util:buildinfo",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"//src/mongo/scripting:scripting_common",
|
||||
"//src/mongo/util:buildinfo",
|
||||
"//src/third_party/mozjs",
|
||||
],
|
||||
}),
|
||||
)
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
#include "mongo/scripting/mozjs/common/error.h"
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/status.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
@ -45,7 +45,11 @@
|
||||
#include <js/RootingAPI.h>
|
||||
#include <js/TypeDecls.h>
|
||||
#include <js/friend/ErrorMessages.h>
|
||||
#ifdef MONGO_MOZJS_WASI_BUILD
|
||||
#include "mongo/scripting/mozjs/wasm/engine/error.h"
|
||||
#else
|
||||
#include <mongo/scripting/mozjs/mongoErrorReportToString.h>
|
||||
#endif
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
@ -64,16 +68,16 @@ void mongoToJSException(JSContext* cx) {
|
||||
JS::ReportUncatchableException(cx);
|
||||
// If a JSAPI callback returns false without setting a pending exception, SpiderMonkey will
|
||||
// treat it as an uncatchable error.
|
||||
auto* scope = getMozJSScope(cx);
|
||||
scope->setStatus(std::move(status));
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
runtime->setStatus(std::move(status));
|
||||
}
|
||||
}
|
||||
|
||||
std::string currentJSStackToString(JSContext* cx) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedValue error(cx);
|
||||
getProto<ErrorInfo>(scope).newInstance(&error);
|
||||
getProto<ErrorInfo>(runtime).newInstance(&error);
|
||||
|
||||
return ObjectWrapper(cx, error).getString("stack");
|
||||
}
|
||||
@ -127,21 +131,26 @@ Status jsExceptionToStatus(JSContext* cx,
|
||||
JS::HandleValue excn,
|
||||
ErrorCodes::Error altCode,
|
||||
StringData altReason) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
// It's possible that we have an uncaught exception for OOM, which is reported on the
|
||||
// exception status of the JSContext. We must check for this OOM exception first to ensure
|
||||
// we return the correct error code and message (i.e JSInterpreterFailure). This is consistent
|
||||
// with MozJSImplScope::_checkForPendingException().
|
||||
// JS_IsThrowingOutOfMemoryException is a MongoDB-specific modification to
|
||||
// SpiderMonkey (see src/third_party/mozjs). The WASI build uses upstream
|
||||
// Firefox SpiderMonkey which doesn't have this.
|
||||
#ifndef MONGO_MOZJS_WASI_BUILD
|
||||
if (JS_IsThrowingOutOfMemoryException(cx, excn)) {
|
||||
return Status(ErrorCodes::JSInterpreterFailure, "Out of memory");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!excn.isObject()) {
|
||||
return Status(altCode, ValueWriter(cx, excn).toString());
|
||||
}
|
||||
|
||||
if (getProto<MongoStatusInfo>(scope).instanceOf(excn)) {
|
||||
if (getProto<MongoStatusInfo>(runtime).instanceOf(excn)) {
|
||||
return MongoStatusInfo::toStatus(cx, excn);
|
||||
}
|
||||
|
||||
|
||||
@ -32,13 +32,19 @@
|
||||
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#ifndef MONGO_MOZJS_WASI_BUILD
|
||||
#include "mongo/scripting/engine.h"
|
||||
#else
|
||||
#include "mongo/scripting/js_regex.h"
|
||||
#endif
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#ifndef MONGO_MOZJS_WASI_BUILD
|
||||
#include "mongo/util/buildinfo.h"
|
||||
#endif
|
||||
#include "mongo/util/version.h"
|
||||
|
||||
#include <cstddef>
|
||||
@ -55,6 +61,12 @@
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
|
||||
#ifdef MONGO_MOZJS_WASI_BUILD
|
||||
namespace wasm {
|
||||
extern uint32_t g_wasmJsHeapLimitMB;
|
||||
} // namespace wasm
|
||||
#endif
|
||||
|
||||
const JSFunctionSpec GlobalInfo::freeFunctions[7] = {
|
||||
MONGO_ATTACH_JS_FUNCTION(sleep),
|
||||
MONGO_ATTACH_JS_FUNCTION(gc),
|
||||
@ -102,18 +114,27 @@ void GlobalInfo::Functions::version::call(JSContext* cx, JS::CallArgs args) {
|
||||
}
|
||||
|
||||
void GlobalInfo::Functions::buildInfo::call(JSContext* cx, JS::CallArgs args) {
|
||||
#ifdef MONGO_MOZJS_WASI_BUILD
|
||||
// WASI builds don't have getBuildInfo() - return empty object
|
||||
BSONObjBuilder b;
|
||||
ValueReader(cx, args.rval()).fromBSON(b.obj(), nullptr, false);
|
||||
#else
|
||||
BSONObjBuilder b;
|
||||
getBuildInfo().serialize(&b);
|
||||
ValueReader(cx, args.rval()).fromBSON(b.obj(), nullptr, false);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GlobalInfo::Functions::getJSHeapLimitMB::call(JSContext* cx, JS::CallArgs args) {
|
||||
ValueReader(cx, args.rval()).fromDouble(mongo::getGlobalScriptEngine()->getJSHeapLimitMB());
|
||||
#ifdef MONGO_MOZJS_WASI_BUILD
|
||||
ValueReader(cx, args.rval()).fromDouble(static_cast<double>(wasm::g_wasmJsHeapLimitMB));
|
||||
#else
|
||||
ValueReader(cx, args.rval()).fromDouble(getGlobalScriptEngine()->getJSHeapLimitMB());
|
||||
#endif
|
||||
}
|
||||
|
||||
void GlobalInfo::Functions::gc::call(JSContext* cx, JS::CallArgs args) {
|
||||
getMozJSScope(cx)->gc();
|
||||
|
||||
getCommonRuntime(cx)->gc();
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
|
||||
@ -123,7 +144,7 @@ void GlobalInfo::Functions::sleep::call(JSContext* cx, JS::CallArgs args) {
|
||||
args.length() == 1 && args.get(0).isNumber());
|
||||
|
||||
int64_t duration = ValueWriter(cx, args.get(0)).toInt64();
|
||||
getMozJSScope(cx)->sleep(Milliseconds(duration));
|
||||
getCommonRuntime(cx)->sleep(Milliseconds(duration));
|
||||
|
||||
args.rval().setUndefined();
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
#include <js/Id.h>
|
||||
@ -63,7 +63,7 @@ InternedStringTable::~InternedStringTable() {
|
||||
}
|
||||
|
||||
InternedStringId::InternedStringId(JSContext* cx, InternedString id)
|
||||
: _id(cx, getMozJSScope(cx)->getInternedStringId(id)) {}
|
||||
: _id(cx, getCommonRuntime(cx)->getInternedStringId(id)) {}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
|
||||
@ -33,8 +33,9 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/bson/util/builder.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/js_regex.h"
|
||||
#include "mongo/scripting/mozjs/common/idwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbref.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
@ -599,9 +600,9 @@ void ObjectWrapper::callMethod(JS::HandleValue fun, JS::MutableHandleValue out)
|
||||
}
|
||||
|
||||
BSONObj ObjectWrapper::toBSON() {
|
||||
auto* scope = getMozJSScope(_context);
|
||||
if (getProto<BSONInfo>(scope).instanceOf(_object) ||
|
||||
getProto<DBRefInfo>(scope).instanceOf(_object)) {
|
||||
auto* runtime = getCommonRuntime(_context);
|
||||
if (getProto<BSONInfo>(runtime).instanceOf(_object) ||
|
||||
getProto<DBRefInfo>(runtime).instanceOf(_object)) {
|
||||
BSONObj* originalBSON = nullptr;
|
||||
bool altered;
|
||||
|
||||
@ -734,9 +735,9 @@ ObjectWrapper::WriteFieldRecursionFrame::WriteFieldRecursionFrame(JSContext* cx,
|
||||
}
|
||||
}
|
||||
|
||||
auto* scope = getMozJSScope(cx);
|
||||
if (getProto<BSONInfo>(scope).instanceOf(thisv) ||
|
||||
getProto<DBRefInfo>(scope).instanceOf(thisv)) {
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
if (getProto<BSONInfo>(runtime).instanceOf(thisv) ||
|
||||
getProto<DBRefInfo>(runtime).instanceOf(thisv)) {
|
||||
std::tie(originalBSON, altered) = BSONInfo::originalBSON(cx, thisv);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,13 +38,16 @@
|
||||
#include "mongo/bson/oid.h"
|
||||
#include "mongo/bson/timestamp.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/engine.h"
|
||||
#include "mongo/scripting/mozjs/common/exception.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/lifetimestack.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
namespace mongo {
|
||||
struct JSRegEx;
|
||||
}
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
53
src/mongo/scripting/mozjs/common/oid_validation.h
Normal file
53
src/mongo/scripting/mozjs/common/oid_validation.h
Normal file
@ -0,0 +1,53 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
|
||||
/**
|
||||
* Validates that a string is a valid ObjectId string (24 hex characters).
|
||||
* Extracted from Scope::validateObjectIdString for WASI builds.
|
||||
*/
|
||||
inline void validateObjectIdString(StringData str) {
|
||||
uassert(11542200, "invalid object id: length", str.size() == 24);
|
||||
auto isAllHex = [](StringData s) {
|
||||
return std::all_of(s.begin(), s.end(), [](char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
|
||||
});
|
||||
};
|
||||
uassert(11542201, "invalid object id: not hex", isAllHex(str));
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
@ -29,8 +29,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/scripting/engine.h"
|
||||
#include "mongo/util/duration.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
@ -38,9 +36,14 @@
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
#include <jsapi.h>
|
||||
|
||||
#include <js/Id.h>
|
||||
|
||||
struct JSContext;
|
||||
namespace mongo {
|
||||
class Status;
|
||||
class StringData;
|
||||
} // namespace mongo
|
||||
|
||||
namespace mongo::mozjs {
|
||||
|
||||
@ -52,14 +55,11 @@ enum class InternedString;
|
||||
struct BinDataInfo;
|
||||
struct BSONInfo;
|
||||
struct CodeInfo;
|
||||
struct CursorHandleInfo;
|
||||
struct CursorInfo;
|
||||
struct DBPointerInfo;
|
||||
struct DBRefInfo;
|
||||
struct ErrorInfo;
|
||||
struct MaxKeyInfo;
|
||||
struct MinKeyInfo;
|
||||
struct MongoExternalInfo;
|
||||
struct MongoStatusInfo;
|
||||
struct NativeFunctionInfo;
|
||||
struct NumberDecimalInfo;
|
||||
@ -67,194 +67,171 @@ struct NumberIntInfo;
|
||||
struct NumberLongInfo;
|
||||
struct OIDInfo;
|
||||
struct RegExpInfo;
|
||||
struct SessionInfo;
|
||||
struct TimestampInfo;
|
||||
struct URIInfo;
|
||||
|
||||
/**
|
||||
* MozJS-specific scope base class.
|
||||
* Abstract interface for the JS runtime services shared by both the shell
|
||||
* (MozJSImplScope) and the WASM sandbox (wasm::MozJSScriptEngine).
|
||||
*
|
||||
* This class extends the engine-agnostic Scope class with MozJS-specific
|
||||
* functionality needed by code in `scripting/mozjs/common/`.
|
||||
* Provides access to common BSON type prototypes, interned strings, GC,
|
||||
* status propagation, and ASAN pointer tracking.
|
||||
*
|
||||
* Intended inheritance hierarchy:
|
||||
* Scope (scripting/engine.h)
|
||||
* └── MozJSScopeBase
|
||||
* └── MozJSImplScope (shell/implscope.h)
|
||||
* └── MozJSWasmScope (wasm/implscope.h)
|
||||
* The concrete shell scope composes
|
||||
* the hierarchy as:
|
||||
*
|
||||
* MozJSImplScope : public Scope,
|
||||
* public MozJSShellRuntimeInterface,
|
||||
* private MozJSCommonRuntimeInterface
|
||||
*
|
||||
* A pointer to this interface is stored in JSContext private data and
|
||||
* retrieved via getCommonRuntime(cx).
|
||||
*/
|
||||
class MONGO_MOD_PUB MozJSScopeBase : public Scope {
|
||||
public:
|
||||
~MozJSScopeBase() override = default;
|
||||
struct MONGO_MOD_PUB MozJSCommonRuntimeInterface {
|
||||
virtual ~MozJSCommonRuntimeInterface() = default;
|
||||
|
||||
virtual void gc() = 0;
|
||||
|
||||
virtual void sleep(Milliseconds ms) = 0;
|
||||
|
||||
virtual std::size_t getGeneration() const = 0;
|
||||
|
||||
virtual JS::HandleId getInternedStringId(InternedString name) = 0;
|
||||
|
||||
// Helpers used by `common/valuereader.cpp`.
|
||||
virtual bool isJavaScriptProtectionEnabled() const = 0;
|
||||
virtual void newFunction(StringData code, JS::MutableHandleValue out) = 0;
|
||||
virtual std::int64_t* trackedNewInt64(std::int64_t value) = 0;
|
||||
|
||||
// Scope generation tracking (used by `common/types/bson.cpp`).
|
||||
virtual std::size_t getGeneration() const = 0;
|
||||
|
||||
// Whether this scope requires all bound BSON objects to be owned.
|
||||
virtual bool requiresOwnedObjects() const = 0;
|
||||
|
||||
// Store a "current status" on the scope (used by `common/exception.cpp`).
|
||||
virtual void setStatus(Status status) = 0;
|
||||
|
||||
// Sleep for the given duration. Used by the JS `sleep()` function.
|
||||
virtual void sleep(Milliseconds ms) = 0;
|
||||
|
||||
// Pointer tracking hooks for ASANHandles. Implementations may no-op these when not needed.
|
||||
virtual void trackNewPointer(void* ptr) = 0;
|
||||
virtual void trackDeletePointer(void* ptr) = 0;
|
||||
|
||||
// --- Prototype accessors ---
|
||||
virtual WrapType<NumberLongInfo>& numberLongProto() = 0;
|
||||
virtual WrapType<NumberIntInfo>& numberIntProto() = 0;
|
||||
virtual WrapType<NumberDecimalInfo>& numberDecimalProto() = 0;
|
||||
virtual WrapType<OIDInfo>& oidProto() = 0;
|
||||
virtual WrapType<BinDataInfo>& binDataProto() = 0;
|
||||
virtual WrapType<BSONInfo>& bsonProto() = 0;
|
||||
virtual WrapType<CodeInfo>& codeProto() = 0;
|
||||
virtual WrapType<CursorHandleInfo>& cursorHandleProto() = 0;
|
||||
virtual WrapType<CursorInfo>& cursorProto() = 0;
|
||||
virtual WrapType<DBPointerInfo>& dbPointerProto() = 0;
|
||||
virtual WrapType<DBRefInfo>& dbRefProto() = 0;
|
||||
virtual WrapType<ErrorInfo>& errorProto() = 0;
|
||||
virtual WrapType<TimestampInfo>& timestampProto() = 0;
|
||||
virtual WrapType<MaxKeyInfo>& maxKeyProto() = 0;
|
||||
virtual WrapType<MinKeyInfo>& minKeyProto() = 0;
|
||||
virtual WrapType<MongoExternalInfo>& mongoExternalProto() = 0;
|
||||
virtual WrapType<MongoStatusInfo>& mongoStatusProto() = 0;
|
||||
virtual WrapType<CodeInfo>& codeProto() = 0;
|
||||
virtual WrapType<DBPointerInfo>& dbPointerProto() = 0;
|
||||
virtual WrapType<NativeFunctionInfo>& nativeFunctionProto() = 0;
|
||||
virtual WrapType<NumberDecimalInfo>& numberDecimalProto() = 0;
|
||||
virtual WrapType<NumberIntInfo>& numberIntProto() = 0;
|
||||
virtual WrapType<NumberLongInfo>& numberLongProto() = 0;
|
||||
virtual WrapType<OIDInfo>& oidProto() = 0;
|
||||
virtual WrapType<ErrorInfo>& errorProto() = 0;
|
||||
virtual WrapType<MongoStatusInfo>& mongoStatusProto() = 0;
|
||||
virtual WrapType<BSONInfo>& bsonProto() = 0;
|
||||
virtual WrapType<DBRefInfo>& dbRefProto() = 0;
|
||||
virtual WrapType<RegExpInfo>& regExpProto() = 0;
|
||||
virtual WrapType<SessionInfo>& sessionProto() = 0;
|
||||
virtual WrapType<TimestampInfo>& timestampProto() = 0;
|
||||
virtual WrapType<URIInfo>& uriProto() = 0;
|
||||
|
||||
virtual void setStatus(Status status) = 0;
|
||||
|
||||
virtual bool isJavaScriptProtectionEnabled() const = 0;
|
||||
|
||||
virtual bool requiresOwnedObjects() const = 0;
|
||||
|
||||
virtual void newFunction(StringData code, JS::MutableHandleValue out) = 0;
|
||||
|
||||
// Pointer tracking for ASAN
|
||||
virtual void trackNewPointer(void* ptr) = 0;
|
||||
virtual void trackDeletePointer(void* ptr) = 0;
|
||||
};
|
||||
|
||||
MONGO_MOD_PUB MozJSScopeBase* getMozJSScope(JSContext* cx);
|
||||
|
||||
template <typename T>
|
||||
WrapType<T>& getProto(MozJSScopeBase* scope);
|
||||
WrapType<T>& getProto(MozJSCommonRuntimeInterface* runtime);
|
||||
|
||||
template <>
|
||||
inline WrapType<BinDataInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->binDataProto();
|
||||
inline WrapType<NumberLongInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->numberLongProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<BSONInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->bsonProto();
|
||||
inline WrapType<NumberIntInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->numberIntProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<CodeInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->codeProto();
|
||||
inline WrapType<NumberDecimalInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->numberDecimalProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<CursorHandleInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->cursorHandleProto();
|
||||
inline WrapType<OIDInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->oidProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<CursorInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->cursorProto();
|
||||
inline WrapType<BinDataInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->binDataProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<DBPointerInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->dbPointerProto();
|
||||
inline WrapType<TimestampInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->timestampProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<DBRefInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->dbRefProto();
|
||||
inline WrapType<MaxKeyInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->maxKeyProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<ErrorInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->errorProto();
|
||||
inline WrapType<MinKeyInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->minKeyProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<MaxKeyInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->maxKeyProto();
|
||||
inline WrapType<CodeInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->codeProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<MinKeyInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->minKeyProto();
|
||||
inline WrapType<DBPointerInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->dbPointerProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<MongoExternalInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->mongoExternalProto();
|
||||
inline WrapType<NativeFunctionInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->nativeFunctionProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<MongoStatusInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->mongoStatusProto();
|
||||
inline WrapType<ErrorInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->errorProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<NativeFunctionInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->nativeFunctionProto();
|
||||
inline WrapType<MongoStatusInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->mongoStatusProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<NumberDecimalInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->numberDecimalProto();
|
||||
inline WrapType<BSONInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->bsonProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<NumberIntInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->numberIntProto();
|
||||
inline WrapType<DBRefInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->dbRefProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<NumberLongInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->numberLongProto();
|
||||
inline WrapType<RegExpInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return runtime->regExpProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<OIDInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->oidProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<RegExpInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->regExpProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<SessionInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->sessionProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<TimestampInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->timestampProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<URIInfo>& getProto(MozJSScopeBase* scope) {
|
||||
return scope->uriProto();
|
||||
}
|
||||
//
|
||||
// Tracked memory allocation helpers
|
||||
// These ensure proper ASAN tracking of dynamically allocated objects.
|
||||
//
|
||||
|
||||
template <typename T, typename... Args>
|
||||
T* trackedNew(MozJSScopeBase* scope, Args&&... args) {
|
||||
T* trackedNew(MozJSCommonRuntimeInterface* runtime, Args&&... args) {
|
||||
T* t = new T(std::forward<Args>(args)...);
|
||||
scope->trackNewPointer(t);
|
||||
runtime->trackNewPointer(t);
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void trackedDelete(MozJSScopeBase* scope, T* t) {
|
||||
scope->trackDeletePointer(t);
|
||||
delete (t);
|
||||
void trackedDelete(MozJSCommonRuntimeInterface* runtime, T* t) {
|
||||
runtime->trackDeletePointer(t);
|
||||
delete t;
|
||||
}
|
||||
|
||||
MONGO_MOD_PUB inline MozJSCommonRuntimeInterface* getCommonRuntime(JSContext* cx) {
|
||||
return static_cast<MozJSCommonRuntimeInterface*>(JS_GetContextPrivate(cx));
|
||||
}
|
||||
|
||||
} // namespace mongo::mozjs
|
||||
@ -38,7 +38,7 @@
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
@ -97,7 +97,7 @@ void hexToBinData(JSContext* cx,
|
||||
int type,
|
||||
const JS::Handle<JS::Value> hexdata,
|
||||
JS::MutableHandleValue out) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
uassert(ErrorCodes::BadValue, "BinData data must be a String", hexdata.isString());
|
||||
|
||||
auto hexstr = ValueWriter(cx, hexdata).toString();
|
||||
@ -110,7 +110,7 @@ void hexToBinData(JSContext* cx,
|
||||
|
||||
args[0].setInt32(type);
|
||||
ValueReader(cx, args[1]).fromStringData(encoded);
|
||||
return getProto<BinDataInfo>(scope).newInstance(args, out);
|
||||
return getProto<BinDataInfo>(runtime).newInstance(args, out);
|
||||
}
|
||||
|
||||
std::string* getEncoded(JS::HandleValue thisv) {
|
||||
@ -128,7 +128,7 @@ void BinDataInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto str = getEncoded(obj);
|
||||
|
||||
if (str) {
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), str);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), str);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,7 +156,7 @@ void BinDataInfo::Functions::UUID::call(JSContext* cx, JS::CallArgs args) {
|
||||
JS::RootedValueArray<2> newArgs(cx);
|
||||
newArgs[0].setInt32(newUUID);
|
||||
ValueReader(cx, newArgs[1]).fromStringData(encoded);
|
||||
getProto<BinDataInfo>(getMozJSScope(cx)).newInstance(newArgs, args.rval());
|
||||
getCommonRuntime(cx)->binDataProto().newInstance(newArgs, args.rval());
|
||||
}
|
||||
|
||||
void BinDataInfo::Functions::MD5::call(JSContext* cx, JS::CallArgs args) {
|
||||
@ -247,7 +247,7 @@ void BinDataInfo::Functions::hex::call(JSContext* cx, JS::CallArgs args) {
|
||||
}
|
||||
|
||||
void BinDataInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
if (args.length() != 2) {
|
||||
uasserted(ErrorCodes::BadValue, "BinData takes 2 arguments -- BinData(subtype,data)");
|
||||
@ -271,7 +271,7 @@ void BinDataInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
auto tmpBase64 = mongo::base64::decode(str);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<BinDataInfo>(scope).newObject(&thisv);
|
||||
getProto<BinDataInfo>(runtime).newObject(&thisv);
|
||||
ObjectWrapper o(cx, thisv);
|
||||
|
||||
JS::RootedValue len(cx);
|
||||
@ -280,8 +280,9 @@ void BinDataInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
o.defineProperty(InternedString::len, len, JSPROP_READONLY);
|
||||
o.defineProperty(InternedString::type, type, JSPROP_READONLY);
|
||||
|
||||
JS::SetReservedSlot(
|
||||
thisv, BinDataStringSlot, JS::PrivateValue(trackedNew<std::string>(scope, std::move(str))));
|
||||
JS::SetReservedSlot(thisv,
|
||||
BinDataStringSlot,
|
||||
JS::PrivateValue(trackedNew<std::string>(runtime, std::move(str))));
|
||||
|
||||
args.rval().setObjectOrNull(thisv);
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
@ -34,13 +35,15 @@
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsonobj_comparator_interface.h"
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#ifndef MONGO_MOZJS_WASI_BUILD
|
||||
#include "mongo/db/pipeline/resume_token.h"
|
||||
#endif
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/idwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -49,7 +52,9 @@
|
||||
#include <cstddef>
|
||||
|
||||
#include <jsapi.h>
|
||||
#if !defined(MONGO_MOZJS_WASI_BUILD)
|
||||
#include <jscustomallocator.h>
|
||||
#endif
|
||||
|
||||
#include <absl/container/node_hash_map.h>
|
||||
#include <boost/move/utility_core.hpp>
|
||||
@ -96,17 +101,21 @@ BSONObj getBSONFromArg(JSContext* cx, JS::HandleValue arg, bool isBSON) {
|
||||
* the appearance of mutable state on the read/write versions.
|
||||
*/
|
||||
struct BSONHolder {
|
||||
BSONHolder(const BSONObj& obj, const BSONObj* parent, const MozJSScopeBase* scope, bool ro)
|
||||
BSONHolder(const BSONObj& obj,
|
||||
const BSONObj* parent,
|
||||
const MozJSCommonRuntimeInterface* runtime,
|
||||
bool ro)
|
||||
: _obj(obj),
|
||||
_generation(scope->getGeneration()),
|
||||
_generation(runtime->getGeneration()),
|
||||
_isOwned(obj.isOwned() || (parent && parent->isOwned())),
|
||||
_resolved(false),
|
||||
_readOnly(ro),
|
||||
_altered(false) {
|
||||
bool requiresOwned = runtime->requiresOwnedObjects();
|
||||
uassert(
|
||||
ErrorCodes::BadValue,
|
||||
"Attempt to bind an unowned BSON Object to a JS scope marked as requiring ownership",
|
||||
_isOwned || (!scope->requiresOwnedObjects()));
|
||||
_isOwned || (!requiresOwned));
|
||||
if (parent) {
|
||||
_parent.emplace(*parent);
|
||||
}
|
||||
@ -117,7 +126,7 @@ struct BSONHolder {
|
||||
}
|
||||
|
||||
void uassertValid(JSContext* cx) const {
|
||||
if (!_isOwned && getMozJSScope(cx)->getGeneration() != _generation)
|
||||
if (!_isOwned && getCommonRuntime(cx)->getGeneration() != _generation)
|
||||
uasserted(ErrorCodes::BadValue,
|
||||
"Attempt to access an invalidated BSON Object in JS scope");
|
||||
}
|
||||
@ -162,12 +171,13 @@ void definePropertyFromBSONElement(JSContext* cx,
|
||||
|
||||
void BSONInfo::make(
|
||||
JSContext* cx, JS::MutableHandleObject obj, BSONObj bson, const BSONObj* parent, bool ro) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
getProto<BSONInfo>(scope).newObject(obj);
|
||||
JS::SetReservedSlot(obj,
|
||||
BSONHolderSlot,
|
||||
JS::PrivateValue(trackedNew<BSONHolder>(scope, bson, parent, scope, ro)));
|
||||
getProto<BSONInfo>(runtime).newObject(obj);
|
||||
JS::SetReservedSlot(
|
||||
obj,
|
||||
BSONHolderSlot,
|
||||
JS::PrivateValue(trackedNew<BSONHolder>(runtime, bson, parent, runtime, ro)));
|
||||
}
|
||||
|
||||
void BSONInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
@ -176,7 +186,7 @@ void BSONInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
if (!holder)
|
||||
return;
|
||||
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), holder);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), holder);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -342,9 +352,9 @@ void bsonCompareCommon(JSContext* cx,
|
||||
uasserted(ErrorCodes::BadValue, fmt::format("{} needs 2 arguments", funcName));
|
||||
|
||||
// If either argument is not proper BSON, then we wrap both objects.
|
||||
auto* scope = getMozJSScope(cx);
|
||||
bool isBSON = getProto<BSONInfo>(scope).instanceOf(args.get(0)) &&
|
||||
getProto<BSONInfo>(scope).instanceOf(args.get(1));
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
bool isBSON = getProto<BSONInfo>(runtime).instanceOf(args.get(0)) &&
|
||||
getProto<BSONInfo>(runtime).instanceOf(args.get(1));
|
||||
|
||||
BSONObj bsonObject1 = getBSONFromArg(cx, args.get(0), isBSON);
|
||||
BSONObj bsonObject2 = getBSONFromArg(cx, args.get(1), isBSON);
|
||||
@ -370,9 +380,9 @@ void BSONInfo::Functions::bsonBinaryEqual::call(JSContext* cx, JS::CallArgs args
|
||||
uasserted(ErrorCodes::BadValue, "bsonBinaryEqual needs 2 arguments");
|
||||
|
||||
// If either argument is not a proper BSON, then we wrap both objects.
|
||||
auto* scope = getMozJSScope(cx);
|
||||
bool isBSON = getProto<BSONInfo>(scope).instanceOf(args.get(0)) &&
|
||||
getProto<BSONInfo>(scope).instanceOf(args.get(1));
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
bool isBSON = getProto<BSONInfo>(runtime).instanceOf(args.get(0)) &&
|
||||
getProto<BSONInfo>(runtime).instanceOf(args.get(1));
|
||||
|
||||
BSONObj bsonObject1 = getBSONFromArg(cx, args.get(0), isBSON);
|
||||
BSONObj bsonObject2 = getBSONFromArg(cx, args.get(1), isBSON);
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -70,11 +70,11 @@ void CodeInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
uassert(ErrorCodes::BadValue,
|
||||
"Code needs 0, 1 or 2 arguments",
|
||||
args.length() == 0 || args.length() == 1 || args.length() == 2);
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
|
||||
getProto<CodeInfo>(scope).newObject(&thisv);
|
||||
getProto<CodeInfo>(runtime).newObject(&thisv);
|
||||
ObjectWrapper o(cx, thisv);
|
||||
|
||||
if (args.length() == 0) {
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
#include "mongo/base/error_codes.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/oid.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -47,7 +47,7 @@ namespace mozjs {
|
||||
const char* const DBPointerInfo::className = "DBPointer";
|
||||
|
||||
void DBPointerInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
if (args.length() != 2)
|
||||
uasserted(ErrorCodes::BadValue, "DBPointer needs 2 arguments");
|
||||
@ -55,11 +55,11 @@ void DBPointerInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
if (!args.get(0).isString())
|
||||
uasserted(ErrorCodes::BadValue, "DBPointer 1st parameter must be a string");
|
||||
|
||||
if (!getProto<OIDInfo>(scope).instanceOf(args.get(1)))
|
||||
if (!getProto<OIDInfo>(runtime).instanceOf(args.get(1)))
|
||||
uasserted(ErrorCodes::BadValue, "DBPointer 2nd parameter must be an ObjectId");
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<DBPointerInfo>(scope).newObject(&thisv);
|
||||
getProto<DBPointerInfo>(runtime).newObject(&thisv);
|
||||
ObjectWrapper o(cx, thisv);
|
||||
|
||||
o.setValue(InternedString::ns, args.get(0));
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
#include "mongo/base/error_codes.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -115,9 +115,9 @@ void DBRefInfo::make(
|
||||
JS::RootedObject local(cx);
|
||||
BSONInfo::make(cx, &local, std::move(bson), parent, ro);
|
||||
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
getProto<DBRefInfo>(scope).newObject(obj);
|
||||
getProto<DBRefInfo>(runtime).newObject(obj);
|
||||
|
||||
JS::SetReservedSlot(
|
||||
obj,
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -68,22 +68,22 @@ void MaxKeyInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
* == and === to MinKey even if created by "new MinKey()" in JS.
|
||||
*/
|
||||
void MaxKeyInfo::call(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
ObjectWrapper o(cx, getProto<MaxKeyInfo>(scope).getProto());
|
||||
ObjectWrapper o(cx, getProto<MaxKeyInfo>(runtime).getProto());
|
||||
|
||||
JS::RootedValue val(cx);
|
||||
|
||||
if (!o.hasField(InternedString::singleton)) {
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<MaxKeyInfo>(scope).newObject(&thisv);
|
||||
getProto<MaxKeyInfo>(runtime).newObject(&thisv);
|
||||
|
||||
val.setObjectOrNull(thisv);
|
||||
o.setValue(InternedString::singleton, val);
|
||||
} else {
|
||||
o.getValue(InternedString::singleton, &val);
|
||||
|
||||
if (!getProto<MaxKeyInfo>(scope).instanceOf(val))
|
||||
if (!getProto<MaxKeyInfo>(runtime).instanceOf(val))
|
||||
uasserted(ErrorCodes::BadValue, "MaxKey singleton not of type MaxKey");
|
||||
}
|
||||
|
||||
@ -102,15 +102,15 @@ void MaxKeyInfo::Functions::hasInstance::call(JSContext* cx, JS::CallArgs args)
|
||||
uassert(ErrorCodes::BadValue, "hasInstance needs 1 argument", args.length() == 1);
|
||||
uassert(ErrorCodes::BadValue, "argument must be an object", args.get(0).isObject());
|
||||
|
||||
auto* scope = getMozJSScope(cx);
|
||||
args.rval().setBoolean(getProto<MaxKeyInfo>(scope).instanceOf(args.get(0)));
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
args.rval().setBoolean(getProto<MaxKeyInfo>(runtime).instanceOf(args.get(0)));
|
||||
}
|
||||
|
||||
void MaxKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
|
||||
ObjectWrapper protoWrapper(cx, proto);
|
||||
|
||||
JS::RootedValue value(cx);
|
||||
getProto<MaxKeyInfo>(getMozJSScope(cx)).newObject(&value);
|
||||
getCommonRuntime(cx)->maxKeyProto().newObject(&value);
|
||||
|
||||
ObjectWrapper(cx, global).setValue(InternedString::MaxKey, value);
|
||||
protoWrapper.setValue(InternedString::singleton, value);
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -68,22 +68,22 @@ void MinKeyInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
* == and === to MinKey even if created by "new MinKey()" in JS.
|
||||
*/
|
||||
void MinKeyInfo::call(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
ObjectWrapper o(cx, getProto<MinKeyInfo>(scope).getProto());
|
||||
ObjectWrapper o(cx, getProto<MinKeyInfo>(runtime).getProto());
|
||||
|
||||
JS::RootedValue val(cx);
|
||||
|
||||
if (!o.hasField(InternedString::singleton)) {
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<MinKeyInfo>(scope).newObject(&thisv);
|
||||
getProto<MinKeyInfo>(runtime).newObject(&thisv);
|
||||
|
||||
val.setObjectOrNull(thisv);
|
||||
o.setValue(InternedString::singleton, val);
|
||||
} else {
|
||||
o.getValue(InternedString::singleton, &val);
|
||||
|
||||
if (!getProto<MinKeyInfo>(scope).instanceOf(val))
|
||||
if (!getProto<MinKeyInfo>(runtime).instanceOf(val))
|
||||
uasserted(ErrorCodes::BadValue, "MinKey singleton not of type MinKey");
|
||||
}
|
||||
|
||||
@ -103,15 +103,15 @@ void MinKeyInfo::Functions::hasInstance::call(JSContext* cx, JS::CallArgs args)
|
||||
uassert(ErrorCodes::BadValue, "hasInstance needs 1 argument", args.length() == 1);
|
||||
uassert(ErrorCodes::BadValue, "argument must be an object", args.get(0).isObject());
|
||||
|
||||
auto* scope = getMozJSScope(cx);
|
||||
args.rval().setBoolean(getProto<MinKeyInfo>(scope).instanceOf(args.get(0)));
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
args.rval().setBoolean(getProto<MinKeyInfo>(runtime).instanceOf(args.get(0)));
|
||||
}
|
||||
|
||||
void MinKeyInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
|
||||
ObjectWrapper protoWrapper(cx, proto);
|
||||
|
||||
JS::RootedValue value(cx);
|
||||
getProto<MinKeyInfo>(getMozJSScope(cx)).newObject(&value);
|
||||
getCommonRuntime(cx)->minKeyProto().newObject(&value);
|
||||
|
||||
ObjectWrapper(cx, global).setValue(InternedString::MinKey, value);
|
||||
protoWrapper.setValue(InternedString::singleton, value);
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -103,7 +103,7 @@ void NativeFunctionInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto holder = JS::GetMaybePtrFromReservedSlot<NativeHolder>(obj, NativeHolderSlot);
|
||||
|
||||
if (holder)
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), holder);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), holder);
|
||||
}
|
||||
|
||||
void NativeFunctionInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) {
|
||||
@ -120,10 +120,10 @@ void NativeFunctionInfo::make(JSContext* cx,
|
||||
JS::MutableHandleObject obj,
|
||||
NativeFunction function,
|
||||
void* data) {
|
||||
auto scope = getMozJSScope(cx);
|
||||
getProto<NativeFunctionInfo>(scope).newObject(obj);
|
||||
auto runtime = getCommonRuntime(cx);
|
||||
getProto<NativeFunctionInfo>(runtime).newObject(obj);
|
||||
JS::SetReservedSlot(
|
||||
obj, NativeHolderSlot, JS::PrivateValue(trackedNew<NativeHolder>(scope, function, data)));
|
||||
obj, NativeHolderSlot, JS::PrivateValue(trackedNew<NativeHolder>(runtime, function, data)));
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
@ -66,7 +66,7 @@ void NumberDecimalInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto x = JS::GetMaybePtrFromReservedSlot<Decimal128>(obj, Decimal128Slot);
|
||||
|
||||
if (x)
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), x);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), x);
|
||||
}
|
||||
|
||||
Decimal128 NumberDecimalInfo::ToNumberDecimal(JSContext* cx, JS::HandleValue thisv) {
|
||||
@ -97,11 +97,11 @@ void NumberDecimalInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args
|
||||
}
|
||||
|
||||
void NumberDecimalInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
|
||||
getProto<NumberDecimalInfo>(scope).newObject(&thisv);
|
||||
getProto<NumberDecimalInfo>(runtime).newObject(&thisv);
|
||||
|
||||
Decimal128 x(0);
|
||||
|
||||
@ -112,18 +112,19 @@ void NumberDecimalInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
} else {
|
||||
uasserted(ErrorCodes::BadValue, "NumberDecimal takes 0 or 1 arguments");
|
||||
}
|
||||
JS::SetReservedSlot(thisv, Decimal128Slot, JS::PrivateValue(trackedNew<Decimal128>(scope, x)));
|
||||
JS::SetReservedSlot(
|
||||
thisv, Decimal128Slot, JS::PrivateValue(trackedNew<Decimal128>(runtime, x)));
|
||||
|
||||
args.rval().setObjectOrNull(thisv);
|
||||
}
|
||||
|
||||
void NumberDecimalInfo::make(JSContext* cx, JS::MutableHandleValue thisv, Decimal128 decimal) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
getProto<NumberDecimalInfo>(scope).newObject(thisv);
|
||||
getProto<NumberDecimalInfo>(runtime).newObject(thisv);
|
||||
JS::SetReservedSlot(thisv.toObjectOrNull(),
|
||||
Decimal128Slot,
|
||||
JS::PrivateValue(trackedNew<Decimal128>(scope, decimal)));
|
||||
JS::PrivateValue(trackedNew<Decimal128>(runtime, decimal)));
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
@ -64,7 +64,7 @@ void NumberIntInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto x = JS::GetMaybePtrFromReservedSlot<int>(obj, IntSlot);
|
||||
|
||||
if (x)
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), x);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), x);
|
||||
}
|
||||
|
||||
int NumberIntInfo::ToNumberInt(JSContext* cx, JS::HandleValue thisv) {
|
||||
@ -107,7 +107,7 @@ void NumberIntInfo::Functions::toJSON::call(JSContext* cx, JS::CallArgs args) {
|
||||
void NumberIntInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
JS::RootedObject thisv(cx);
|
||||
|
||||
getProto<NumberIntInfo>(getMozJSScope(cx)).newObject(&thisv);
|
||||
getCommonRuntime(cx)->numberIntProto().newObject(&thisv);
|
||||
|
||||
int32_t x = 0;
|
||||
|
||||
@ -118,7 +118,7 @@ void NumberIntInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
} else {
|
||||
uasserted(ErrorCodes::BadValue, "NumberInt takes 0 or 1 arguments");
|
||||
}
|
||||
JS::SetReservedSlot(thisv, IntSlot, JS::PrivateValue(trackedNew<int>(getMozJSScope(cx), x)));
|
||||
JS::SetReservedSlot(thisv, IntSlot, JS::PrivateValue(trackedNew<int>(getCommonRuntime(cx), x)));
|
||||
|
||||
args.rval().setObjectOrNull(thisv);
|
||||
}
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
@ -80,7 +80,7 @@ void NumberLongInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto numLong = JS::GetMaybePtrFromReservedSlot<int64_t>(obj, Int64Slot);
|
||||
|
||||
if (numLong)
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), numLong);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), numLong);
|
||||
}
|
||||
|
||||
int64_t NumberLongInfo::ToNumberLong(JSContext* cx, JS::HandleValue thisv) {
|
||||
@ -129,7 +129,7 @@ void NumberLongInfo::Functions::compare::call(JSContext* cx, JS::CallArgs args)
|
||||
uassert(ErrorCodes::BadValue, "NumberLong.compare() needs 1 argument", args.length() == 1);
|
||||
uassert(ErrorCodes::BadValue,
|
||||
"NumberLong.compare() argument must be a NumberLong",
|
||||
getProto<NumberLongInfo>(getMozJSScope(cx)).instanceOf(args.get(0)));
|
||||
getCommonRuntime(cx)->numberLongProto().instanceOf(args.get(0)));
|
||||
|
||||
int64_t thisVal = NumberLongInfo::ToNumberLong(cx, args.thisv());
|
||||
int64_t otherVal = NumberLongInfo::ToNumberLong(cx, args.get(0));
|
||||
@ -171,11 +171,11 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
"NumberLong needs 0, 1 or 3 arguments",
|
||||
args.length() == 0 || args.length() == 1 || args.length() == 3);
|
||||
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
|
||||
getProto<NumberLongInfo>(scope).newObject(&thisv);
|
||||
getProto<NumberLongInfo>(runtime).newObject(&thisv);
|
||||
|
||||
int64_t numLong;
|
||||
|
||||
@ -221,7 +221,7 @@ void NumberLongInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
numLong = (top << 32) + bot;
|
||||
}
|
||||
JS::SetReservedSlot(
|
||||
thisv, Int64Slot, JS::PrivateValue(getMozJSScope(cx)->trackedNewInt64(numLong)));
|
||||
thisv, Int64Slot, JS::PrivateValue(getCommonRuntime(cx)->trackedNewInt64(numLong)));
|
||||
|
||||
args.rval().setObjectOrNull(thisv);
|
||||
}
|
||||
@ -234,7 +234,7 @@ void NumberLongInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::Han
|
||||
if (!JS_DefinePropertyById(
|
||||
cx,
|
||||
proto,
|
||||
getMozJSScope(cx)->getInternedStringId(InternedString::floatApprox),
|
||||
getCommonRuntime(cx)->getInternedStringId(InternedString::floatApprox),
|
||||
smUtils::wrapConstrainedMethod<Functions::floatApprox, false, NumberLongInfo>,
|
||||
nullptr,
|
||||
JSPROP_ENUMERATE)) {
|
||||
@ -245,7 +245,7 @@ void NumberLongInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::Han
|
||||
if (!JS_DefinePropertyById(
|
||||
cx,
|
||||
proto,
|
||||
getMozJSScope(cx)->getInternedStringId(InternedString::top),
|
||||
getCommonRuntime(cx)->getInternedStringId(InternedString::top),
|
||||
smUtils::wrapConstrainedMethod<Functions::top, false, NumberLongInfo>,
|
||||
nullptr,
|
||||
JSPROP_ENUMERATE)) {
|
||||
@ -256,7 +256,7 @@ void NumberLongInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::Han
|
||||
if (!JS_DefinePropertyById(
|
||||
cx,
|
||||
proto,
|
||||
getMozJSScope(cx)->getInternedStringId(InternedString::bottom),
|
||||
getCommonRuntime(cx)->getInternedStringId(InternedString::bottom),
|
||||
smUtils::wrapConstrainedMethod<Functions::bottom, false, NumberLongInfo>,
|
||||
nullptr,
|
||||
JSPROP_ENUMERATE)) {
|
||||
@ -267,7 +267,7 @@ void NumberLongInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::Han
|
||||
if (!JS_DefinePropertyById(
|
||||
cx,
|
||||
proto,
|
||||
getMozJSScope(cx)->getInternedStringId(InternedString::exactValueString),
|
||||
getCommonRuntime(cx)->getInternedStringId(InternedString::exactValueString),
|
||||
smUtils::wrapConstrainedMethod<Functions::exactValueString, false, NumberLongInfo>,
|
||||
nullptr,
|
||||
JSPROP_ENUMERATE)) {
|
||||
|
||||
@ -33,13 +33,13 @@
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/bson/bsonmisc.h"
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/scripting/engine.h"
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/scripting/oid_validation.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/str.h"
|
||||
|
||||
@ -70,7 +70,7 @@ void OIDInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto oid = JS::GetMaybePtrFromReservedSlot<OID>(obj, OIDSlot);
|
||||
|
||||
if (oid) {
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), oid);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), oid);
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,7 +101,7 @@ void OIDInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
} else {
|
||||
auto str = ValueWriter(cx, args.get(0)).toString();
|
||||
|
||||
Scope::validateObjectIdString(str);
|
||||
validateObjectIdString(str);
|
||||
oid.init(str);
|
||||
}
|
||||
|
||||
@ -109,11 +109,11 @@ void OIDInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
}
|
||||
|
||||
void OIDInfo::make(JSContext* cx, const OID& oid, JS::MutableHandleValue out) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<OIDInfo>(scope).newObject(&thisv);
|
||||
JS::SetReservedSlot(thisv, OIDSlot, JS::PrivateValue(trackedNew<OID>(scope, oid)));
|
||||
getProto<OIDInfo>(runtime).newObject(&thisv);
|
||||
JS::SetReservedSlot(thisv, OIDSlot, JS::PrivateValue(trackedNew<OID>(runtime, oid)));
|
||||
|
||||
out.setObjectOrNull(thisv);
|
||||
}
|
||||
@ -139,7 +139,7 @@ void OIDInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObje
|
||||
|
||||
if (!JS_DefinePropertyById(cx,
|
||||
proto,
|
||||
getMozJSScope(cx)->getInternedStringId(InternedString::str),
|
||||
getCommonRuntime(cx)->getInternedStringId(InternedString::str),
|
||||
smUtils::wrapConstrainedMethod<Functions::getter, true, OIDInfo>,
|
||||
nullptr,
|
||||
JSPROP_ENUMERATE)) {
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -70,7 +70,7 @@ Status MongoStatusInfo::toStatus(JSContext* cx, JS::HandleValue value) {
|
||||
|
||||
void MongoStatusInfo::fromStatus(JSContext* cx, Status status, JS::MutableHandleValue value) {
|
||||
invariant(status != Status::OK());
|
||||
auto scope = getMozJSScope(cx);
|
||||
auto runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedValue undef(cx);
|
||||
undef.setUndefined();
|
||||
@ -78,10 +78,10 @@ void MongoStatusInfo::fromStatus(JSContext* cx, Status status, JS::MutableHandle
|
||||
JS::RootedValueArray<1> args(cx);
|
||||
ValueReader(cx, args[0]).fromStringData(status.reason());
|
||||
JS::RootedObject error(cx);
|
||||
getProto<ErrorInfo>(scope).newInstance(args, &error);
|
||||
getProto<ErrorInfo>(runtime).newInstance(args, &error);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<MongoStatusInfo>(scope).newObjectWithProto(&thisv, error);
|
||||
getProto<MongoStatusInfo>(runtime).newObjectWithProto(&thisv, error);
|
||||
ObjectWrapper thisvObj(cx, thisv);
|
||||
thisvObj.defineProperty(InternedString::code,
|
||||
JSPROP_ENUMERATE,
|
||||
@ -103,7 +103,7 @@ void MongoStatusInfo::fromStatus(JSContext* cx, Status status, JS::MutableHandle
|
||||
nullptr);
|
||||
|
||||
JS::SetReservedSlot(
|
||||
thisv, StatusSlot, JS::PrivateValue(trackedNew<Status>(scope, std::move(status))));
|
||||
thisv, StatusSlot, JS::PrivateValue(trackedNew<Status>(runtime, std::move(status))));
|
||||
|
||||
value.setObjectOrNull(thisv);
|
||||
}
|
||||
@ -112,7 +112,7 @@ void MongoStatusInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto status = JS::GetMaybePtrFromReservedSlot<Status>(obj, StatusSlot);
|
||||
|
||||
if (status)
|
||||
trackedDelete(getMozJSScope(freeOpToJSContext(gcCtx)), status);
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), status);
|
||||
}
|
||||
|
||||
void MongoStatusInfo::Functions::code::call(JSContext* cx, JS::CallArgs args) {
|
||||
@ -160,11 +160,11 @@ void MongoStatusInfo::Functions::stack::call(JSContext* cx, JS::CallArgs args) {
|
||||
}
|
||||
|
||||
void MongoStatusInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
|
||||
auto scope = getMozJSScope(cx);
|
||||
auto runtime = getCommonRuntime(cx);
|
||||
JS::SetReservedSlot(proto,
|
||||
StatusSlot,
|
||||
JS::PrivateValue(trackedNew<Status>(
|
||||
scope, Status(ErrorCodes::UnknownError, "Mongo Status Prototype"))));
|
||||
runtime, Status(ErrorCodes::UnknownError, "Mongo Status Prototype"))));
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
@ -79,10 +79,10 @@ double getTimestampComponent(JSContext* cx, JS::HandleValue component, std::stri
|
||||
} // namespace
|
||||
|
||||
void TimestampInfo::construct(JSContext* cx, JS::CallArgs args) {
|
||||
auto* scope = getMozJSScope(cx);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<TimestampInfo>(scope).newObject(&thisv);
|
||||
getProto<TimestampInfo>(runtime).newObject(&thisv);
|
||||
ObjectWrapper o(cx, thisv);
|
||||
|
||||
if (args.length() == 0) {
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "mongo/bson/oid.h"
|
||||
#include "mongo/logv2/log.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bindata.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/types/code.h"
|
||||
@ -58,7 +58,9 @@
|
||||
#include <cstdio>
|
||||
#include <iosfwd>
|
||||
|
||||
#if !defined(MONGO_MOZJS_WASI_BUILD)
|
||||
#include <jscustomallocator.h>
|
||||
#endif
|
||||
|
||||
#include <js/Array.h>
|
||||
#include <js/CharacterEncoding.h>
|
||||
@ -83,36 +85,34 @@ ValueReader::ValueReader(JSContext* cx, JS::MutableHandleValue value)
|
||||
: _context(cx), _value(value) {}
|
||||
|
||||
void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent, bool readOnly) {
|
||||
auto scope = getMozJSScope(_context);
|
||||
auto runtime = getCommonRuntime(_context);
|
||||
|
||||
switch (elem.type()) {
|
||||
case BSONType::code:
|
||||
// javascriptProtection prevents Code and CodeWScope BSON types from
|
||||
// being automatically marshalled into executable functions.
|
||||
if (scope->isJavaScriptProtectionEnabled()) {
|
||||
if (runtime->isJavaScriptProtectionEnabled()) {
|
||||
JS::RootedValueArray<1> args(_context);
|
||||
ValueReader(_context, args[0]).fromStringData(elem.valueStringData());
|
||||
|
||||
JS::RootedObject obj(_context);
|
||||
getProto<CodeInfo>(scope).newInstance(args, _value);
|
||||
getProto<CodeInfo>(runtime).newInstance(args, _value);
|
||||
} else {
|
||||
scope->newFunction(elem.valueStringData(), _value);
|
||||
runtime->newFunction(elem.valueStringData(), _value);
|
||||
}
|
||||
return;
|
||||
case BSONType::codeWScope:
|
||||
if (scope->isJavaScriptProtectionEnabled()) {
|
||||
if (runtime->isJavaScriptProtectionEnabled()) {
|
||||
JS::RootedValueArray<2> args(_context);
|
||||
|
||||
ValueReader(_context, args[0]).fromStringData(elem.codeWScopeCode());
|
||||
ValueReader(_context, args[1])
|
||||
.fromBSON(elem.codeWScopeObject().getOwned(), nullptr, readOnly);
|
||||
|
||||
getProto<CodeInfo>(scope).newInstance(args, _value);
|
||||
getProto<CodeInfo>(runtime).newInstance(args, _value);
|
||||
} else {
|
||||
#ifndef MONGO_MOZJS_WASI_BUILD
|
||||
if (!elem.codeWScopeObject().isEmpty())
|
||||
LOGV2_WARNING(23826, "CodeWScope doesn't transfer to db.eval");
|
||||
scope->newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1),
|
||||
_value);
|
||||
#endif
|
||||
runtime->newFunction(
|
||||
StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1), _value);
|
||||
}
|
||||
return;
|
||||
case BSONType::symbol:
|
||||
@ -157,7 +157,7 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
|
||||
ValueReader(_context, args[1]).fromStringData(elem.regexFlags());
|
||||
|
||||
JS::RootedObject obj(_context);
|
||||
getProto<RegExpInfo>(scope).newInstance(args, &obj);
|
||||
getProto<RegExpInfo>(runtime).newInstance(args, &obj);
|
||||
|
||||
_value.setObjectOrNull(obj);
|
||||
|
||||
@ -175,7 +175,7 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
|
||||
|
||||
ValueReader(_context, args[1]).fromStringData(ss.str());
|
||||
|
||||
getProto<BinDataInfo>(scope).newInstance(args, _value);
|
||||
getProto<BinDataInfo>(runtime).newInstance(args, _value);
|
||||
return;
|
||||
}
|
||||
case BSONType::timestamp: {
|
||||
@ -185,16 +185,16 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
|
||||
.fromDouble(static_cast<double>(elem.timestampTime().toMillisSinceEpoch()) / 1000);
|
||||
ValueReader(_context, args[1]).fromDouble(elem.timestampInc());
|
||||
|
||||
getProto<TimestampInfo>(scope).newInstance(args, _value);
|
||||
getProto<TimestampInfo>(runtime).newInstance(args, _value);
|
||||
|
||||
return;
|
||||
}
|
||||
case BSONType::numberLong: {
|
||||
JS::RootedObject thisv(_context);
|
||||
getProto<NumberLongInfo>(scope).newObject(&thisv);
|
||||
getProto<NumberLongInfo>(runtime).newObject(&thisv);
|
||||
JS::SetReservedSlot(thisv,
|
||||
NumberLongInfo::Int64Slot,
|
||||
JS::PrivateValue(scope->trackedNewInt64(elem.numberLong())));
|
||||
JS::PrivateValue(runtime->trackedNewInt64(elem.numberLong())));
|
||||
_value.setObjectOrNull(thisv);
|
||||
return;
|
||||
}
|
||||
@ -204,16 +204,16 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
|
||||
ValueReader(_context, args[0]).fromDecimal128(decimal);
|
||||
JS::RootedObject obj(_context);
|
||||
|
||||
getProto<NumberDecimalInfo>(scope).newInstance(args, &obj);
|
||||
getProto<NumberDecimalInfo>(runtime).newInstance(args, &obj);
|
||||
_value.setObjectOrNull(obj);
|
||||
|
||||
return;
|
||||
}
|
||||
case BSONType::minKey:
|
||||
getProto<MinKeyInfo>(scope).newInstance(_value);
|
||||
getProto<MinKeyInfo>(runtime).newInstance(_value);
|
||||
return;
|
||||
case BSONType::maxKey:
|
||||
getProto<MaxKeyInfo>(scope).newInstance(_value);
|
||||
getProto<MaxKeyInfo>(runtime).newInstance(_value);
|
||||
return;
|
||||
case BSONType::dbRef: {
|
||||
JS::RootedValueArray<1> oidArgs(_context);
|
||||
@ -221,9 +221,9 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
|
||||
|
||||
JS::RootedValueArray<2> dbPointerArgs(_context);
|
||||
ValueReader(_context, dbPointerArgs[0]).fromStringData(elem.dbrefNS());
|
||||
getProto<OIDInfo>(scope).newInstance(oidArgs, dbPointerArgs[1]);
|
||||
getProto<OIDInfo>(runtime).newInstance(oidArgs, dbPointerArgs[1]);
|
||||
|
||||
getProto<DBPointerInfo>(scope).newInstance(dbPointerArgs, _value);
|
||||
getProto<DBPointerInfo>(runtime).newInstance(dbPointerArgs, _value);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
@ -337,11 +337,11 @@ void ValueReader::fromDouble(double d) {
|
||||
}
|
||||
|
||||
void ValueReader::fromInt64(int64_t i) {
|
||||
auto scope = getMozJSScope(_context);
|
||||
auto runtime = getCommonRuntime(_context);
|
||||
JS::RootedObject num(_context);
|
||||
getProto<NumberLongInfo>(scope).newObject(&num);
|
||||
getProto<NumberLongInfo>(runtime).newObject(&num);
|
||||
JS::SetReservedSlot(
|
||||
num, NumberLongInfo::Int64Slot, JS::PrivateValue(scope->trackedNewInt64(i)));
|
||||
num, NumberLongInfo::Int64Slot, JS::PrivateValue(runtime->trackedNewInt64(i)));
|
||||
_value.setObjectOrNull(num);
|
||||
}
|
||||
|
||||
|
||||
@ -33,11 +33,12 @@
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsontypes.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/js_regex.h"
|
||||
#include "mongo/scripting/mozjs/common/exception.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bindata.h"
|
||||
#include "mongo/scripting/mozjs/common/types/code.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbpointer.h"
|
||||
@ -137,22 +138,22 @@ int ValueWriter::type() {
|
||||
}
|
||||
|
||||
if (auto jsClass = JS::GetClass(obj)) {
|
||||
auto scope = getMozJSScope(_context);
|
||||
if (scope->numberIntProto().getJSClass() == jsClass) {
|
||||
auto runtime = getCommonRuntime(_context);
|
||||
if (runtime->numberIntProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::numberInt);
|
||||
} else if (scope->numberLongProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->numberLongProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::numberLong);
|
||||
} else if (scope->numberDecimalProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->numberDecimalProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::numberDecimal);
|
||||
} else if (scope->oidProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->oidProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::oid);
|
||||
} else if (scope->binDataProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->binDataProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::binData);
|
||||
} else if (scope->timestampProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->timestampProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::timestamp);
|
||||
} else if (scope->minKeyProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->minKeyProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::minKey);
|
||||
} else if (scope->maxKeyProto().getJSClass() == jsClass) {
|
||||
} else if (runtime->maxKeyProto().getJSClass() == jsClass) {
|
||||
return stdx::to_underlying(BSONType::maxKey);
|
||||
}
|
||||
}
|
||||
@ -251,7 +252,7 @@ int32_t ValueWriter::toInt32() {
|
||||
|
||||
int64_t ValueWriter::toInt64() {
|
||||
int64_t out;
|
||||
if (getMozJSScope(_context)->numberLongProto().instanceOf(_value))
|
||||
if (getCommonRuntime(_context)->numberLongProto().instanceOf(_value))
|
||||
return NumberLongInfo::ToNumberLong(_context, _value);
|
||||
|
||||
if (JS::ToInt64(_context, _value, &out))
|
||||
@ -265,13 +266,13 @@ Decimal128 ValueWriter::toDecimal128() {
|
||||
if (_value.isNumber()) {
|
||||
return Decimal128(toNumber(), Decimal128::kRoundTo15Digits);
|
||||
}
|
||||
if (getMozJSScope(_context)->numberIntProto().instanceOf(_value))
|
||||
if (getCommonRuntime(_context)->numberIntProto().instanceOf(_value))
|
||||
return Decimal128(NumberIntInfo::ToNumberInt(_context, _value));
|
||||
|
||||
if (getMozJSScope(_context)->numberLongProto().instanceOf(_value))
|
||||
if (getCommonRuntime(_context)->numberLongProto().instanceOf(_value))
|
||||
return Decimal128(static_cast<int64_t>(NumberLongInfo::ToNumberLong(_context, _value)));
|
||||
|
||||
if (getMozJSScope(_context)->numberDecimalProto().instanceOf(_value))
|
||||
if (getCommonRuntime(_context)->numberDecimalProto().instanceOf(_value))
|
||||
return NumberDecimalInfo::ToNumberDecimal(_context, _value);
|
||||
|
||||
if (_value.isString()) {
|
||||
@ -296,7 +297,7 @@ Decimal128 ValueWriter::toDecimal128() {
|
||||
}
|
||||
|
||||
OID ValueWriter::toOID() {
|
||||
if (getMozJSScope(_context)->oidProto().instanceOf(_value)) {
|
||||
if (getCommonRuntime(_context)->oidProto().instanceOf(_value)) {
|
||||
return OIDInfo::getOID(_context, _value);
|
||||
}
|
||||
|
||||
@ -304,7 +305,7 @@ OID ValueWriter::toOID() {
|
||||
}
|
||||
|
||||
void ValueWriter::toBinData(std::function<void(const BSONBinData&)> withBinData) {
|
||||
if (!getMozJSScope(_context)->binDataProto().instanceOf(_value)) {
|
||||
if (!getCommonRuntime(_context)->binDataProto().instanceOf(_value)) {
|
||||
throwCurrentJSException(_context, ErrorCodes::BadValue, "Unable to write BinData value.");
|
||||
}
|
||||
|
||||
@ -318,9 +319,8 @@ void ValueWriter::toBinData(std::function<void(const BSONBinData&)> withBinData)
|
||||
uassert(ErrorCodes::BadValue, "Cannot call getter on BinData prototype", binDataStr);
|
||||
|
||||
auto binData = base64::decode(*binDataStr);
|
||||
withBinData(BSONBinData(binData.c_str(),
|
||||
binData.size(),
|
||||
static_cast<mongo::BinDataType>(static_cast<int>(subType))));
|
||||
withBinData(BSONBinData(
|
||||
binData.c_str(), binData.size(), static_cast<BinDataType>(static_cast<int>(subType))));
|
||||
}
|
||||
|
||||
Timestamp ValueWriter::toTimestamp() {
|
||||
@ -328,7 +328,7 @@ Timestamp ValueWriter::toTimestamp() {
|
||||
|
||||
uassert(ErrorCodes::BadValue,
|
||||
"Unable to write Timestamp value.",
|
||||
getMozJSScope(_context)->timestampProto().getJSClass() == JS::GetClass(obj));
|
||||
getCommonRuntime(_context)->timestampProto().getJSClass() == JS::GetClass(obj));
|
||||
|
||||
return TimestampInfo::getValidatedValue(_context, obj);
|
||||
}
|
||||
@ -393,7 +393,7 @@ void ValueWriter::writeThis(BSONObjBuilder* b,
|
||||
void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
StringData sd,
|
||||
ObjectWrapper::WriteFieldRecursionFrames* frames) {
|
||||
auto scope = getMozJSScope(_context);
|
||||
auto runtime = getCommonRuntime(_context);
|
||||
|
||||
// We open a block here because it's important that the two rooting types
|
||||
// we need (obj and o) go out of scope before we actually open a
|
||||
@ -407,26 +407,26 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
auto jsclass = JS::GetClass(obj);
|
||||
|
||||
if (jsclass) {
|
||||
if (scope->oidProto().getJSClass() == jsclass) {
|
||||
if (runtime->oidProto().getJSClass() == jsclass) {
|
||||
b->append(sd, OIDInfo::getOID(_context, obj));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->numberLongProto().getJSClass() == jsclass) {
|
||||
if (runtime->numberLongProto().getJSClass() == jsclass) {
|
||||
long long out = NumberLongInfo::ToNumberLong(_context, obj);
|
||||
b->append(sd, out);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->numberIntProto().getJSClass() == jsclass) {
|
||||
if (runtime->numberIntProto().getJSClass() == jsclass) {
|
||||
b->append(sd, NumberIntInfo::ToNumberInt(_context, obj));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->codeProto().getJSClass() == jsclass) {
|
||||
if (runtime->codeProto().getJSClass() == jsclass) {
|
||||
if (o.hasOwnField(InternedString::scope) // CodeWScope
|
||||
&& o.type(InternedString::scope) == stdx::to_underlying(BSONType::object)) {
|
||||
if (o.type(InternedString::code) != stdx::to_underlying(BSONType::string)) {
|
||||
@ -445,16 +445,16 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->numberDecimalProto().getJSClass() == jsclass) {
|
||||
if (runtime->numberDecimalProto().getJSClass() == jsclass) {
|
||||
b->append(sd, NumberDecimalInfo::ToNumberDecimal(_context, obj));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->dbPointerProto().getJSClass() == jsclass) {
|
||||
if (runtime->dbPointerProto().getJSClass() == jsclass) {
|
||||
uassert(ErrorCodes::BadValue,
|
||||
"can't serialize DBPointer prototype",
|
||||
scope->dbPointerProto().getProto() != obj);
|
||||
runtime->dbPointerProto().getProto() != obj);
|
||||
|
||||
JS::RootedValue id(_context);
|
||||
o.getValue("id", &id);
|
||||
@ -464,7 +464,7 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->binDataProto().getJSClass() == jsclass) {
|
||||
if (runtime->binDataProto().getJSClass() == jsclass) {
|
||||
auto str = JS::GetMaybePtrFromReservedSlot<std::string>(
|
||||
obj, BinDataInfo::BinDataStringSlot);
|
||||
|
||||
@ -479,26 +479,26 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
|
||||
b->appendBinData(sd,
|
||||
binData.size(),
|
||||
static_cast<mongo::BinDataType>(static_cast<int>(subType)),
|
||||
static_cast<BinDataType>(static_cast<int>(subType)),
|
||||
binData.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->timestampProto().getJSClass() == jsclass) {
|
||||
if (runtime->timestampProto().getJSClass() == jsclass) {
|
||||
Timestamp ot = TimestampInfo::getValidatedValue(_context, obj);
|
||||
b->append(sd, ot);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->minKeyProto().getJSClass() == jsclass) {
|
||||
if (runtime->minKeyProto().getJSClass() == jsclass) {
|
||||
b->appendMinKey(sd);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (scope->maxKeyProto().getJSClass() == jsclass) {
|
||||
if (runtime->maxKeyProto().getJSClass() == jsclass) {
|
||||
b->appendMaxKey(sd);
|
||||
|
||||
return;
|
||||
@ -511,7 +511,7 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
|
||||
case JSProto_Function: {
|
||||
uassert(16716,
|
||||
"cannot convert native function to BSON",
|
||||
!scope->nativeFunctionProto().instanceOf(obj));
|
||||
!runtime->nativeFunctionProto().instanceOf(obj));
|
||||
JSStringWrapper jsstr;
|
||||
b->appendCode(sd, ValueWriter(_context, _value).toStringData(&jsstr));
|
||||
return;
|
||||
|
||||
@ -36,11 +36,14 @@
|
||||
#include "mongo/bson/oid.h"
|
||||
#include "mongo/bson/timestamp.h"
|
||||
#include "mongo/platform/decimal128.h"
|
||||
#include "mongo/scripting/engine.h"
|
||||
#include "mongo/scripting/mozjs/common/jsstringwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
namespace mongo {
|
||||
struct JSRegEx;
|
||||
}
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
@ -52,8 +52,8 @@ namespace smUtils {
|
||||
* void as the last type in wrapConstrainedMethod.
|
||||
*/
|
||||
template <typename T, typename... Args>
|
||||
bool instanceOf(MozJSScopeBase* scope, bool* isProto, JS::HandleValue value) {
|
||||
auto& proto = getProto<T>(scope);
|
||||
bool instanceOf(MozJSCommonRuntimeInterface* runtime, bool* isProto, JS::HandleValue value) {
|
||||
auto& proto = getProto<T>(runtime);
|
||||
|
||||
if (proto.instanceOf(value)) {
|
||||
if (value.toObjectOrNull() == proto.getProto()) {
|
||||
@ -63,7 +63,7 @@ bool instanceOf(MozJSScopeBase* scope, bool* isProto, JS::HandleValue value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return instanceOf<Args...>(scope, isProto, value);
|
||||
return instanceOf<Args...>(runtime, isProto, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +72,9 @@ bool instanceOf(MozJSScopeBase* scope, bool* isProto, JS::HandleValue value) {
|
||||
* We use this to identify the end of the template list in the general case.
|
||||
*/
|
||||
template <>
|
||||
inline bool instanceOf<void>(MozJSScopeBase* scope, bool* isProto, JS::HandleValue value) {
|
||||
inline bool instanceOf<void>(MozJSCommonRuntimeInterface* runtime,
|
||||
bool* isProto,
|
||||
JS::HandleValue value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -85,7 +87,7 @@ inline bool instanceOf<void>(MozJSScopeBase* scope, bool* isProto, JS::HandleVal
|
||||
* ::call - a static function of type void (JSContext* cx, JS::CallArgs args)
|
||||
* ::name - a static function which returns a const char* with the type name
|
||||
* noProto - whether the method can be invoked on the prototype
|
||||
* Args - The list of types to check against scope->getProto<T>().instanceOf
|
||||
* Args - The list of types to check against runtime->getProto<T>().instanceOf
|
||||
* for the thisv the method has been invoked against
|
||||
*/
|
||||
template <typename T, bool noProto, typename... Args>
|
||||
@ -101,7 +103,7 @@ bool wrapConstrainedMethod(JSContext* cx, unsigned argc, JS::Value* vp) {
|
||||
<< ValueWriter(cx, args.thisv()).typeAsString() << "\"");
|
||||
}
|
||||
|
||||
if (!instanceOf<Args..., void>(getMozJSScope(cx), &isProto, args.thisv())) {
|
||||
if (!instanceOf<Args..., void>(getCommonRuntime(cx), &isProto, args.thisv())) {
|
||||
uasserted(ErrorCodes::BadValue,
|
||||
str::stream() << "Cannot call \"" << T::name() << "\" on object of type \""
|
||||
<< ObjectWrapper(cx, args.thisv()).getClassName() << "\"");
|
||||
|
||||
19
src/mongo/scripting/mozjs/shared/BUILD.bazel
Normal file
19
src/mongo/scripting/mozjs/shared/BUILD.bazel
Normal file
@ -0,0 +1,19 @@
|
||||
load("//bazel:mongo_src_rules.bzl", "mongo_cc_library")
|
||||
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"mozjs_error_types.h",
|
||||
"mozjs_wasm_startup_options.h",
|
||||
])
|
||||
|
||||
# Shared types and definitions for MozJS that can be used by both
|
||||
# the WASM engine and the mongod host side.
|
||||
mongo_cc_library(
|
||||
name = "mozjs_shared_types",
|
||||
hdrs = [
|
||||
"mozjs_error_types.h",
|
||||
"mozjs_wasm_startup_options.h",
|
||||
],
|
||||
deps = [],
|
||||
)
|
||||
91
src/mongo/scripting/mozjs/shared/mozjs_error_types.h
Normal file
91
src/mongo/scripting/mozjs/shared/mozjs_error_types.h
Normal file
@ -0,0 +1,91 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
/**
|
||||
* Shared error codes for MozJS operations.
|
||||
* Used by both the WASM engine and the mongo host side.
|
||||
*/
|
||||
typedef enum {
|
||||
SM_OK = 0,
|
||||
|
||||
// Wrapper / host-side errors
|
||||
SM_E_INVALID_ARG,
|
||||
SM_E_BAD_STATE,
|
||||
SM_E_NOMEM,
|
||||
SM_E_IO,
|
||||
SM_E_TIMEOUT,
|
||||
SM_E_NOT_SUPPORTED,
|
||||
SM_E_INTERNAL,
|
||||
|
||||
// MozJS API usage
|
||||
SM_E_JSAPI_FAIL,
|
||||
SM_E_PENDING_EXCEPTION,
|
||||
SM_E_NO_EXCEPTION,
|
||||
SM_E_TERMINATED,
|
||||
SM_E_OOM,
|
||||
|
||||
// Script failures
|
||||
SM_E_COMPILE,
|
||||
SM_E_RUNTIME,
|
||||
SM_E_MODULE,
|
||||
SM_E_PROMISE_REJECTION,
|
||||
SM_E_STACK_OVERFLOW,
|
||||
|
||||
// Data marshalling / boundary errors
|
||||
SM_E_TYPE,
|
||||
SM_E_ENCODING,
|
||||
} err_code_t;
|
||||
|
||||
typedef struct {
|
||||
char* msg; // Error message
|
||||
size_t msg_len; // Length of msg (excluding null terminator)
|
||||
|
||||
char* filename; // Source filename
|
||||
size_t filename_len; // Length of filename (excluding null terminator)
|
||||
|
||||
char* stack; // Stack trace
|
||||
size_t stack_len; // Length of stack (excluding null terminator)
|
||||
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
err_code_t code;
|
||||
} wasm_mozjs_error_t;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
|
||||
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
typedef struct {
|
||||
size_t opTimeout; // microseconds (0 = no timeout)
|
||||
size_t heapSize; // in MB
|
||||
} wasm_mozjs_startup_options_t;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
|
||||
@ -68,7 +68,7 @@ bool isExternalScriptingEnabled() {
|
||||
}
|
||||
|
||||
namespace {
|
||||
auto operationMozJSScopeBaseDecoration =
|
||||
auto operationMozJSShellRuntimeInterfaceDecoration =
|
||||
OperationContext::declareDecoration<mozjs::MozJSImplScope*>();
|
||||
}
|
||||
|
||||
@ -117,8 +117,8 @@ mongo::Scope* MozJSScriptEngine::createScopeForCurrentThread(boost::optional<int
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::interrupt(ClientLock&, OperationContext* opCtx) {
|
||||
if (opCtx && (*opCtx)[operationMozJSScopeBaseDecoration]) {
|
||||
(*opCtx)[operationMozJSScopeBaseDecoration]->kill();
|
||||
if (opCtx && (*opCtx)[operationMozJSShellRuntimeInterfaceDecoration]) {
|
||||
(*opCtx)[operationMozJSShellRuntimeInterfaceDecoration]->kill();
|
||||
LOGV2_DEBUG(22808, 2, "Interrupting op", "opId"_attr = opCtx->getOpID());
|
||||
} else if (opCtx) {
|
||||
LOGV2_DEBUG(
|
||||
@ -133,8 +133,8 @@ void MozJSScriptEngine::interruptAll(ServiceContextLock& svcCtxLock) {
|
||||
while (auto client = cursor.next()) {
|
||||
stdx::lock_guard lk(*client);
|
||||
if (auto opCtx = client->getOperationContext();
|
||||
opCtx && (*opCtx)[operationMozJSScopeBaseDecoration]) {
|
||||
(*opCtx)[operationMozJSScopeBaseDecoration]->kill();
|
||||
opCtx && (*opCtx)[operationMozJSShellRuntimeInterfaceDecoration]) {
|
||||
(*opCtx)[operationMozJSShellRuntimeInterfaceDecoration]->kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -183,7 +183,7 @@ void MozJSScriptEngine::registerOperation(OperationContext* opCtx, MozJSImplScop
|
||||
"opId"_attr = opCtx->getOpID());
|
||||
|
||||
stdx::lock_guard lk(*opCtx->getClient());
|
||||
(*opCtx)[operationMozJSScopeBaseDecoration] = scope;
|
||||
(*opCtx)[operationMozJSShellRuntimeInterfaceDecoration] = scope;
|
||||
|
||||
if (auto status = opCtx->checkForInterruptNoAssert(); !status.isOK()) {
|
||||
scope->kill();
|
||||
@ -198,7 +198,7 @@ void MozJSScriptEngine::unregisterOperation(OperationContext* opCtx) {
|
||||
"opId"_attr = opCtx->getOpID());
|
||||
|
||||
stdx::lock_guard lk(*opCtx->getClient());
|
||||
(*opCtx)[operationMozJSScopeBaseDecoration] = nullptr;
|
||||
(*opCtx)[operationMozJSShellRuntimeInterfaceDecoration] = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
|
||||
@ -582,7 +582,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine, boost::optional<int> j
|
||||
{
|
||||
JS_AddInterruptCallback(_context, _interruptCallback);
|
||||
JS_SetGCCallback(_context, _gcCallback, this);
|
||||
JS_SetContextPrivate(_context, this);
|
||||
JS_SetContextPrivate(_context, static_cast<MozJSCommonRuntimeInterface*>(this));
|
||||
|
||||
JSAutoRealm ac(_context, _global);
|
||||
_environmentPreparer = std::make_unique<EnvironmentPreparer>(_context);
|
||||
@ -631,6 +631,10 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine, boost::optional<int> j
|
||||
currentJSScope = this;
|
||||
}
|
||||
|
||||
MozJSShellRuntimeInterface* getShellRuntime(JSContext* cx) {
|
||||
return static_cast<MozJSImplScope*>(getCommonRuntime(cx));
|
||||
}
|
||||
|
||||
MozJSImplScope::~MozJSImplScope() {
|
||||
invariant(!_promiseResult.initialized());
|
||||
currentJSScope = nullptr;
|
||||
|
||||
@ -44,7 +44,6 @@
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/global.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bindata.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/types/code.h"
|
||||
@ -74,6 +73,7 @@
|
||||
#include "mongo/scripting/mozjs/shell/mongo.h"
|
||||
#include "mongo/scripting/mozjs/shell/mongohelpers.h"
|
||||
#include "mongo/scripting/mozjs/shell/resumetoken.h"
|
||||
#include "mongo/scripting/mozjs/shell/runtime.h"
|
||||
#include "mongo/scripting/mozjs/shell/session.h"
|
||||
#include "mongo/scripting/mozjs/shell/uri.h"
|
||||
#include "mongo/stdx/condition_variable.h"
|
||||
@ -131,15 +131,20 @@ const StringData kUnknownError = "Unknown Failure from JSInterpreter";
|
||||
*
|
||||
* For more information about overriden fields, see mongo::Scope
|
||||
*/
|
||||
class MONGO_MOD_PUB MozJSImplScope final : public MozJSScopeBase {
|
||||
class MONGO_MOD_PUB MozJSImplScope final : public Scope,
|
||||
public MozJSShellRuntimeInterface,
|
||||
public MozJSCommonRuntimeInterface {
|
||||
MozJSImplScope(const MozJSImplScope&) = delete;
|
||||
MozJSImplScope& operator=(const MozJSImplScope&) = delete;
|
||||
|
||||
template <typename T>
|
||||
friend WrapType<T>& getProto(MozJSCommonRuntimeInterface*);
|
||||
|
||||
public:
|
||||
explicit MozJSImplScope(MozJSScriptEngine* engine, boost::optional<int> jsHeapLimitMB);
|
||||
~MozJSImplScope() override;
|
||||
|
||||
// ---- MozJSScopeBase (common) surface ----
|
||||
// ---- Proto accessors ----
|
||||
WrapType<BinDataInfo>& binDataProto() override {
|
||||
return _binDataProto;
|
||||
}
|
||||
@ -668,13 +673,37 @@ private:
|
||||
};
|
||||
|
||||
MONGO_MOD_PUB inline MozJSImplScope* getScope(JSContext* cx) {
|
||||
return static_cast<MozJSImplScope*>(JS_GetContextPrivate(cx));
|
||||
return static_cast<MozJSImplScope*>(getCommonRuntime(cx));
|
||||
}
|
||||
|
||||
inline MozJSImplScope* getScope(class JS::GCContext* gcCtx) {
|
||||
return getScope(freeOpToJSContext(gcCtx));
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<CursorHandleInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return static_cast<MozJSImplScope*>(runtime)->cursorHandleProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<CursorInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return static_cast<MozJSImplScope*>(runtime)->cursorProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<MongoExternalInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return static_cast<MozJSImplScope*>(runtime)->mongoExternalProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<SessionInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return static_cast<MozJSImplScope*>(runtime)->sessionProto();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline WrapType<URIInfo>& getProto(MozJSCommonRuntimeInterface* runtime) {
|
||||
return static_cast<MozJSImplScope*>(runtime)->uriProto();
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
|
||||
@ -30,9 +30,9 @@
|
||||
#include "mongo/scripting/mozjs/shell/resumetoken.h"
|
||||
|
||||
#include "mongo/db/pipeline/resume_token.h"
|
||||
#include "mongo/scripting/mozjs/common/scope_base.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h" // IWYU pragma: keep
|
||||
#include "mongo/scripting/mozjs/shell/runtime.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
@ -60,8 +60,8 @@ void ResumeTokenDataUtility::Functions::decodeResumeToken::call(JSContext* cx, J
|
||||
const auto resumeTokenDataBson = ResumeToken::parse(encodedResumeTokenBson).getData().toBSON();
|
||||
|
||||
JS::RootedObject thisValue(cx);
|
||||
auto* scope = getMozJSScope(cx);
|
||||
getProto<BSONInfo>(scope).newObject(&thisValue);
|
||||
auto* runtime = getCommonRuntime(cx);
|
||||
getProto<BSONInfo>(runtime).newObject(&thisValue);
|
||||
BSONInfo::make(cx, &thisValue, std::move(resumeTokenDataBson), nullptr, true);
|
||||
|
||||
args.rval().setObjectOrNull(thisValue);
|
||||
|
||||
67
src/mongo/scripting/mozjs/shell/runtime.h
Normal file
67
src/mongo/scripting/mozjs/shell/runtime.h
Normal file
@ -0,0 +1,67 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/util/modules.h"
|
||||
|
||||
struct JSContext;
|
||||
|
||||
namespace mongo::mozjs {
|
||||
|
||||
struct CursorHandleInfo;
|
||||
struct CursorInfo;
|
||||
struct MongoExternalInfo;
|
||||
struct SessionInfo;
|
||||
struct URIInfo;
|
||||
|
||||
/**
|
||||
* Shell-specific prototype accessors for JS types that only exist in the
|
||||
* full mongo shell (cursors, connections, sessions, URIs).
|
||||
*/
|
||||
class MONGO_MOD_PUB MozJSShellRuntimeInterface {
|
||||
public:
|
||||
virtual ~MozJSShellRuntimeInterface() = default;
|
||||
|
||||
virtual WrapType<CursorHandleInfo>& cursorHandleProto() = 0;
|
||||
virtual WrapType<CursorInfo>& cursorProto() = 0;
|
||||
virtual WrapType<MongoExternalInfo>& mongoExternalProto() = 0;
|
||||
virtual WrapType<SessionInfo>& sessionProto() = 0;
|
||||
virtual WrapType<URIInfo>& uriProto() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the MozJSShellRuntimeInterface from a JSContext.
|
||||
* Use this only when shell-specific features are needed.
|
||||
* For common code, use getCommonRuntime() from scope.h instead.
|
||||
*/
|
||||
MozJSShellRuntimeInterface* getShellRuntime(JSContext* cx);
|
||||
|
||||
} // namespace mongo::mozjs
|
||||
195
src/mongo/scripting/mozjs/wasm/BUILD.bazel
Normal file
195
src/mongo/scripting/mozjs/wasm/BUILD.bazel
Normal file
@ -0,0 +1,195 @@
|
||||
load("//bazel:mongo_src_rules.bzl", "mongo_cc_benchmark", "mongo_cc_binary", "mongo_cc_library", "mongo_cc_unit_test") # @unused mongo_cc_binary mongo_cc_library
|
||||
load("//bazel/toolchains/cc/mongo_wasm/toolchain:with_wasi_config.bzl", "with_wasi_config")
|
||||
load("//src/mongo/scripting/mozjs/wasm/engine:linkset.bzl", "cc_linkset")
|
||||
|
||||
# Gate WASM module compilation behind --define=build_mozjs_wasm=true.
|
||||
# Without this flag the test compiles and runs but skips (no .wasm module found).
|
||||
config_setting(
|
||||
name = "build_mozjs_wasm",
|
||||
define_values = {"build_mozjs_wasm": "true"},
|
||||
)
|
||||
|
||||
exports_files([
|
||||
"status.cpp",
|
||||
])
|
||||
|
||||
cc_linkset(
|
||||
name = "mongo_base_linkset",
|
||||
extra_flags = [
|
||||
"-lc++",
|
||||
"-lc++abi",
|
||||
"-lwasi-emulated-signal",
|
||||
"-lwasi-emulated-mman",
|
||||
"-lwasi-emulated-process-clocks",
|
||||
"-lwasi-emulated-getpid",
|
||||
"-Wl,--gc-sections",
|
||||
"-Wl,--no-entry",
|
||||
"-Wl,--export-memory",
|
||||
"-Wl,--stack-first",
|
||||
"-mexec-model=reactor",
|
||||
],
|
||||
deps = ["//src/mongo:base"],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "spidermonkey_wasip2_dist_release_from_source",
|
||||
srcs = [
|
||||
"@spidermonkey//:mach",
|
||||
"@spidermonkey//:srcs",
|
||||
"support/rust_shims/src/lib.rs",
|
||||
"support/rust_shims/Cargo.toml.template",
|
||||
"scripts/build_spidermonkey_wasip2.sh",
|
||||
],
|
||||
outs = ["spidermonkey-wasip2-release.tar.gz"],
|
||||
cmd = """
|
||||
export WASI_SDK_BIN_FILES="$(locations @wasi_sdk//:bin)"
|
||||
export SPIDER_MACH_PATH="$(location @spidermonkey//:mach)"
|
||||
export RUST_SHIMS_LIB_RS="$(location support/rust_shims/src/lib.rs)"
|
||||
export CARGO_TEMPLATE_PATH="$(location support/rust_shims/Cargo.toml.template)"
|
||||
export OUTPUT="$@"
|
||||
bash $(location scripts/build_spidermonkey_wasip2.sh)
|
||||
""",
|
||||
tags = [
|
||||
"local",
|
||||
"manual",
|
||||
"no-sandbox",
|
||||
],
|
||||
tools = [
|
||||
"@wasi_sdk//:bin",
|
||||
],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "spidermonkey_wasip2_dist_release",
|
||||
actual = select({
|
||||
"@platforms//os:wasi": "@spidermonkey_wasip2_dist//file",
|
||||
"//conditions:default": ":spidermonkey_wasip2_dist_release_from_source",
|
||||
}),
|
||||
)
|
||||
|
||||
# Source files for WASM compilation (excludes test files and stubs)
|
||||
filegroup(
|
||||
name = "wasm_sources",
|
||||
srcs = glob(
|
||||
["*.cpp"],
|
||||
exclude = [
|
||||
"status.cpp", # Included in wasi_sources filegroup
|
||||
],
|
||||
) + [
|
||||
# engine/ is its own package so glob cannot reach it; list explicitly.
|
||||
"//src/mongo/scripting/mozjs/wasm/engine:engine.cpp",
|
||||
"//src/mongo/scripting/mozjs/wasm/engine:error.cpp",
|
||||
],
|
||||
)
|
||||
|
||||
# Rust shims for WASI linkage (provides encoding_* symbols)
|
||||
# These are built by spidermonkey_wasip2_dist_release and included in the tarball.
|
||||
# This genrule extracts them from the tarball.
|
||||
genrule(
|
||||
name = "rust_shims",
|
||||
srcs = [
|
||||
":spidermonkey_wasip2_dist_release",
|
||||
"scripts/extract_rust_shims.sh",
|
||||
],
|
||||
outs = ["rust_shims.a"],
|
||||
cmd = """
|
||||
export TARBALL="$(location :spidermonkey_wasip2_dist_release)"
|
||||
export OUTPUT="$@"
|
||||
bash $(location scripts/extract_rust_shims.sh)
|
||||
""",
|
||||
tags = ["manual"],
|
||||
tools = [],
|
||||
)
|
||||
|
||||
genrule(
|
||||
name = "mozjs_wasm_api",
|
||||
srcs = [
|
||||
":wasm_sources",
|
||||
"//src/mongo/scripting/mozjs/common:wasi_sources",
|
||||
"//src/mongo/scripting/mozjs/wasm/engine:exception_stubs.cpp",
|
||||
"//src/mongo/scripting/mozjs/wasm/engine:api.cpp",
|
||||
":spidermonkey_wasip2_dist_release",
|
||||
":rust_shims",
|
||||
":mongo_base_linkset",
|
||||
"//src/mongo/base:error_codes_header",
|
||||
"//src/mongo:mongo_config_header",
|
||||
"//src/mongo:base",
|
||||
|
||||
# WIT + generated bindings
|
||||
"//src/mongo/scripting/mozjs/wasm/wit:mozjs.wit",
|
||||
"//src/mongo/scripting/mozjs/wasm/wit_gen/generated:api.c",
|
||||
"//src/mongo/scripting/mozjs/wasm/wit_gen/generated:api.h",
|
||||
"//src/mongo/scripting/mozjs/wasm/wit_gen/generated:api_component_type.o",
|
||||
|
||||
# Build scripts
|
||||
"scripts/compile_mozjs_wasm_api.sh",
|
||||
"scripts/compile_wasi_source.sh",
|
||||
],
|
||||
outs = ["mozjs_wasm_api.wasm"],
|
||||
cmd = """
|
||||
export WASI_SDK_BIN_FILES="$(locations @wasi_sdk//:bin)"
|
||||
export SM_TARBALL="$(location :spidermonkey_wasip2_dist_release)"
|
||||
export RUST_SHIMS_PATH="$(location :rust_shims)"
|
||||
export WASM_SOURCES="$(locations :wasm_sources)"
|
||||
export COMMON_WASI_SOURCES="$(locations //src/mongo/scripting/mozjs/common:wasi_sources)"
|
||||
export EXCEPTION_STUBS_SRC="$(location //src/mongo/scripting/mozjs/wasm/engine:exception_stubs.cpp)"
|
||||
export MOZJS_API_SRC="$(location //src/mongo/scripting/mozjs/wasm/engine:api.cpp)"
|
||||
export WIT_API_C="$(location //src/mongo/scripting/mozjs/wasm/wit_gen/generated:api.c)"
|
||||
export WIT_COMPONENT_TYPE_OBJ="$(location //src/mongo/scripting/mozjs/wasm/wit_gen/generated:api_component_type.o)"
|
||||
export ERROR_CODES_HEADER_FILES="$(locations //src/mongo/base:error_codes_header)"
|
||||
export CONFIG_HEADER_FILES="$(locations //src/mongo:mongo_config_header)"
|
||||
export LINKSET_FILES="$(locations :mongo_base_linkset)"
|
||||
export COMPILE_HELPER="$(location scripts/compile_wasi_source.sh)"
|
||||
export OUTPUT="$@"
|
||||
bash $(location scripts/compile_mozjs_wasm_api.sh)
|
||||
""",
|
||||
tags = [
|
||||
"local",
|
||||
"manual",
|
||||
"no-sandbox",
|
||||
],
|
||||
target_compatible_with = select({
|
||||
"@platforms//os:wasi": [],
|
||||
"//conditions:default": ["@platforms//:incompatible"],
|
||||
}),
|
||||
tools = [
|
||||
"@wasi_sdk//:bin",
|
||||
],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
with_wasi_config(
|
||||
name = "mozjs_wasm_api_data_from_source",
|
||||
srcs = [":mozjs_wasm_api"],
|
||||
tags = ["manual"],
|
||||
target_compatible_with = ["@platforms//os:linux"],
|
||||
)
|
||||
|
||||
alias(
|
||||
name = "mozjs_wasm_api_data",
|
||||
actual = select({
|
||||
":build_mozjs_wasm": ":mozjs_wasm_api_data_from_source",
|
||||
"//conditions:default": "@mozjs_wasm//file",
|
||||
}),
|
||||
)
|
||||
|
||||
mongo_cc_unit_test(
|
||||
name = "wasm_mozjs_test",
|
||||
size = "large",
|
||||
timeout = "long",
|
||||
srcs = ["//src/mongo/scripting/mozjs/wasm/engine:engine_test.cpp"],
|
||||
auto_header = False,
|
||||
data = [":mozjs_wasm_api_data"],
|
||||
tags = [
|
||||
"incompatible_with_bazel_remote_test",
|
||||
"local",
|
||||
"mongo_unittest_eighth_group",
|
||||
"mozjs_wasm_tests",
|
||||
"no-sandbox",
|
||||
],
|
||||
target_compatible_with = ["@platforms//os:linux"],
|
||||
deps = [
|
||||
"//src/mongo:base",
|
||||
"@crates//:wasmtime_c",
|
||||
],
|
||||
)
|
||||
51
src/mongo/scripting/mozjs/wasm/cabi_realloc.cpp
Normal file
51
src/mongo/scripting/mozjs/wasm/cabi_realloc.cpp
Normal file
@ -0,0 +1,51 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
// mozglue stub for rust hooks
|
||||
extern "C" void install_rust_hooks() {}
|
||||
|
||||
// Canonical ABI allocator hook used by the component model.
|
||||
// Required for wasm-component-ld to produce a valid component.
|
||||
extern "C" void* cabi_realloc(void* ptr, size_t old_size, size_t align, size_t new_size) {
|
||||
(void)old_size;
|
||||
(void)align;
|
||||
if (new_size == 0) {
|
||||
std::free(ptr);
|
||||
return nullptr;
|
||||
}
|
||||
void* result = std::realloc(ptr, new_size);
|
||||
if (!result) {
|
||||
// realloc failed -- original ptr is still valid but we cannot grow.
|
||||
// In WASM component model this is typically fatal.
|
||||
std::abort();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
1
src/mongo/scripting/mozjs/wasm/engine/BUILD.bazel
Normal file
1
src/mongo/scripting/mozjs/wasm/engine/BUILD.bazel
Normal file
@ -0,0 +1 @@
|
||||
exports_files(glob(["*"]))
|
||||
81
src/mongo/scripting/mozjs/wasm/engine/README.md
Normal file
81
src/mongo/scripting/mozjs/wasm/engine/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
# MozJS WASM Engine
|
||||
|
||||
The WASM engine wraps SpiderMonkey into a WASI Preview 2 component
|
||||
(`mozjs_wasm_api.wasm`) that the host (mongod/mongos) loads via Wasmtime. The
|
||||
host calls exported WIT functions to create JS contexts, compile functions,
|
||||
invoke them with BSON arguments, and read back BSON results — all inside a
|
||||
sandboxed WebAssembly instance.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Host (Wasmtime)
|
||||
│
|
||||
▼
|
||||
WIT exports ── api.cpp extern "C" functions generated by wit-bindgen
|
||||
│
|
||||
▼
|
||||
MozJSScriptEngine ── engine.cpp/.h manages JSContext, function slots, BSON ↔ JS
|
||||
│
|
||||
▼
|
||||
SpiderMonkey (libjs_static.a, compiled for wasm32-wasip2)
|
||||
```
|
||||
|
||||
Key files in this directory:
|
||||
|
||||
| File | Purpose |
|
||||
| ----------------------- | ----------------------------------------------------------------------------------------------- |
|
||||
| `engine.h / engine.cpp` | `MozJSScriptEngine` — owns the SpiderMonkey runtime, prototype installer, function handle table |
|
||||
| `api.cpp` | Implements each WIT export; bridges WIT types to `MozJSScriptEngine` |
|
||||
| `error.h / error.cpp` | `ExecutionCheck` — wraps JSAPI calls and captures exceptions into `wasm_mozjs_error_t` |
|
||||
| `exception_stubs.cpp` | C++ exception / RTTI stubs (WASM has no native exception support) |
|
||||
| `utils.h` | `cabi_realloc` helper and string utilities |
|
||||
| `engine_test.cpp` | Unit tests — loads the `.wasm` module via Wasmtime's component model |
|
||||
| `linkset.bzl` | `cc_linkset` Starlark rule for collecting linker inputs into response files |
|
||||
|
||||
## WIT Interface
|
||||
|
||||
The public API is defined in `../wit/mozjs.wit` (package `mongo:mozjs`, world
|
||||
`api`). The world exports a single `mozjs` interface:
|
||||
|
||||
- `initialize-engine` / `shutdown-engine` / `interrupt-current-op` — lifecycle
|
||||
- `create-function(source)` → `function-handle` — compile JS source
|
||||
- `invoke-function(handle, bson)` — call a compiled function with BSON args
|
||||
- `get-return-value-bson` — retrieve the last return value as BSON
|
||||
- `set-global` / `get-global` — read/write named JS globals as BSON
|
||||
|
||||
C bindings live in `../wit_gen/generated/` and are produced by:
|
||||
|
||||
```bash
|
||||
wit-bindgen c ../wit --out-dir ../wit_gen/generated
|
||||
```
|
||||
|
||||
The generated `api.h` declares the `exports_*` symbols that `api.cpp`
|
||||
implements. The generated `api_component_type.o` is linked into the final
|
||||
`.wasm` to embed the component type section.
|
||||
|
||||
## Building
|
||||
|
||||
Everything is driven by Bazel from `../BUILD.bazel`.
|
||||
|
||||
Without `--define=build_mozjs_wasm=true` a prebuilt `.wasm` is fetched from S3
|
||||
instead.
|
||||
|
||||
**Build WASM module and run unit tests:**
|
||||
|
||||
```bash
|
||||
bazel test //src/mongo/scripting/mozjs/wasm:wasm_mozjs_test --define=build_mozjs_wasm=true --spawn_strategy=local
|
||||
```
|
||||
|
||||
Tests load `mozjs_wasm_api.wasm` into Wasmtime, instantiate it as a WASI
|
||||
Preview 2 component, and exercise every WIT export.
|
||||
|
||||
## Dependencies
|
||||
|
||||
| Dependency | How it's pulled in |
|
||||
| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **SpiderMonkey** | `@spidermonkey//` Bazel repo (version in `../spider-monkey/`). Built for `wasm32-wasip2` by `scripts/build_spidermonkey_wasip2.sh`. |
|
||||
| **WASI SDK** | `@wasi_sdk//` Bazel repo (`bazel/toolchains/cc/mongo_wasm/`). Provides `clang` cross-compiler and WASI sysroot. |
|
||||
| **Rust shims** | `../support/rust_shims/` — small Rust crate providing `encoding_c` / `encoding_c_mem` symbols needed by SpiderMonkey's ICU layer. Built during the SpiderMonkey build, extracted by `scripts/extract_rust_shims.sh`. |
|
||||
| **Wasmtime** | `@crates//:wasmtime_c`. Used by the host side and tests, not compiled into the `.wasm`. |
|
||||
| **MongoDB base** | `//src/mongo:base` — linked into the WASM module via the `cc_linkset` rule so that code like `BSONObj`, `Status`, etc. is available inside the sandbox. |
|
||||
478
src/mongo/scripting/mozjs/wasm/engine/api.cpp
Normal file
478
src/mongo/scripting/mozjs/wasm/engine/api.cpp
Normal file
@ -0,0 +1,478 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/scripting/mozjs/wasm/wit_gen/generated/api.h"
|
||||
|
||||
#include "mongo/bson/bsonelement.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/scripting/mozjs/shared/mozjs_error_types.h"
|
||||
#include "mongo/scripting/mozjs/shared/mozjs_wasm_startup_options.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#include "engine.h"
|
||||
#include "error.h"
|
||||
|
||||
namespace {
|
||||
|
||||
// Maximum JavaScript source code size (1 MB)
|
||||
constexpr size_t kMaxJsSourceSize = 1 * 1024 * 1024;
|
||||
|
||||
// Maximum BSON document size (16mb)
|
||||
constexpr size_t kMaxBsonSize = 16 * 1024 * 1024;
|
||||
|
||||
// Maximum number of functions that can be created
|
||||
constexpr size_t kMaxFunctions = 10000;
|
||||
|
||||
// Default operation timeout in microseconds (30 seconds)
|
||||
// This prevents infinite loops in JavaScript code.
|
||||
constexpr size_t kDefaultOpTimeoutMicros = 30 * 1000 * 1000;
|
||||
|
||||
// Default JS heap size in MB for the WASM engine.
|
||||
constexpr uint32_t kDefaultHeapSizeMB = 100;
|
||||
|
||||
} // namespace
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
// Thread-safety: This module is designed for single-threaded use within a
|
||||
// single WASM instance. Each WASM instance gets its own linear memory and
|
||||
// thus its own copy of g_engine. Do not share a WASM instance across threads.
|
||||
static MozJSScriptEngine g_engine;
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
|
||||
static void set_opt_string(api_option_string_t* out, const char* ptr, size_t len) {
|
||||
if (ptr && len > 0) {
|
||||
out->is_some = true;
|
||||
api_string_dup_n(&out->val, ptr, len);
|
||||
} else {
|
||||
out->is_some = false;
|
||||
out->val.ptr = nullptr;
|
||||
out->val.len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void fill_wit_error(exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* out,
|
||||
const mongo::mozjs::wasm::wasm_mozjs_error_t& in) {
|
||||
out->code = static_cast<exports_mongo_mozjs_mozjs_err_code_t>(in.code);
|
||||
|
||||
set_opt_string(&out->msg, in.msg, in.msg_len);
|
||||
set_opt_string(&out->filename, in.filename, in.filename_len);
|
||||
set_opt_string(&out->stack, in.stack, in.stack_len);
|
||||
|
||||
out->line = in.line;
|
||||
out->column = in.column;
|
||||
}
|
||||
|
||||
static bool return_err(exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err,
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t* native_err) {
|
||||
if (!native_err) {
|
||||
return false;
|
||||
}
|
||||
fill_wit_error(err, *native_err);
|
||||
mongo::mozjs::wasm::clear_error(native_err);
|
||||
// wit-bindgen C convention: return false means error (adapter does: is_err = !retval)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns false on allocation failure so callers can propagate OOM errors.
|
||||
static bool list_u8_dup(api_list_u8_t* out, const uint8_t* data, size_t len) {
|
||||
if (len == 0) {
|
||||
out->ptr = nullptr;
|
||||
out->len = 0;
|
||||
return true;
|
||||
}
|
||||
auto* p = static_cast<uint8_t*>(std::malloc(len));
|
||||
if (!p) {
|
||||
out->ptr = nullptr;
|
||||
out->len = 0;
|
||||
return false; // OOM
|
||||
}
|
||||
std::memcpy(p, data, len);
|
||||
out->ptr = p;
|
||||
out->len = len;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validates BSON data before constructing BSONObj to prevent crashes from
|
||||
// malformed input. This performs basic structural validation.
|
||||
|
||||
static bool validate_bson(const uint8_t* data,
|
||||
size_t len,
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t* err) {
|
||||
if (len < 5 || len > kMaxBsonSize) {
|
||||
if (err) {
|
||||
err->code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
mongo::mozjs::wasm::set_string(&err->msg,
|
||||
&err->msg_len,
|
||||
len < 5 ? "BSON too small (minimum 5 bytes)"
|
||||
: "BSON exceeds 16 MB maximum");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int32_t declared_size;
|
||||
std::memcpy(&declared_size, data, sizeof(declared_size));
|
||||
|
||||
if (declared_size < 5 || static_cast<size_t>(declared_size) > len ||
|
||||
data[declared_size - 1] != 0) {
|
||||
if (err) {
|
||||
err->code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
mongo::mozjs::wasm::set_string(
|
||||
&err->msg, &err->msg_len, "BSON header invalid (size mismatch or no terminator)");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Track function count for limiting.
|
||||
static size_t g_function_count = 0;
|
||||
|
||||
// Exported Functions from `mongo:mozjs/mozjs`
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_initialize_engine(
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
// TODO SERVER-116051: Make wasm_mozjs_startup_options_t an argument.
|
||||
mongo::mozjs::wasm::wasm_mozjs_startup_options_t opt{};
|
||||
opt.opTimeout = opt.opTimeout > 0 ? opt.opTimeout : kDefaultOpTimeoutMicros;
|
||||
opt.heapSize = opt.heapSize > 0 ? opt.heapSize : kDefaultHeapSizeMB;
|
||||
int64_t rc = mongo::mozjs::wasm::g_engine.init(&opt, &e);
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0; // Set the ok value
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_shutdown_engine(
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
int64_t rc = mongo::mozjs::wasm::g_engine.shutdown(&e);
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
g_function_count = 0;
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_interrupt_current_op(
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
int64_t rc = mongo::mozjs::wasm::g_engine.interrupt(&e);
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_create_function(
|
||||
api_list_u8_t* source,
|
||||
exports_mongo_mozjs_mozjs_function_handle_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
const uint8_t* bytes = source ? source->ptr : nullptr;
|
||||
size_t len = source ? source->len : 0;
|
||||
|
||||
if (len > kMaxJsSourceSize) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
mongo::mozjs::wasm::set_string(&e.msg, &e.msg_len, "JS source exceeds maximum size (1 MB)");
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
if (g_function_count >= kMaxFunctions) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_NOMEM;
|
||||
mongo::mozjs::wasm::set_string(
|
||||
&e.msg, &e.msg_len, "Maximum function count reached (10000)");
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
uint64_t handle = 0;
|
||||
int64_t rc = mongo::mozjs::wasm::g_engine.createFunction(bytes, len, &handle, &e);
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
g_function_count++;
|
||||
*ret = static_cast<exports_mongo_mozjs_mozjs_function_handle_t>(handle);
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_invoke_function(
|
||||
exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* bson,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (handle == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj argsObj;
|
||||
if (bson && bson->ptr && bson->len > 0) {
|
||||
if (!validate_bson(bson->ptr, bson->len, &e)) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
argsObj = mongo::BSONObj(reinterpret_cast<const char*>(bson->ptr));
|
||||
}
|
||||
|
||||
mongo::BSONObj outBson;
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.invokeFunction(
|
||||
static_cast<uint64_t>(handle), std::move(argsObj), &outBson, &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_get_return_value_bson(
|
||||
api_list_u8_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
mongo::BSONObj out;
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.getReturnValueBson(&out, &e));
|
||||
if (rc != mongo::mozjs::wasm::SM_OK) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
if (!list_u8_dup(ret, reinterpret_cast<const uint8_t*>(out.objdata()), out.objsize())) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_NOMEM;
|
||||
mongo::mozjs::wasm::set_string(
|
||||
&e.msg, &e.msg_len, "OOM: failed to allocate BSON return buffer");
|
||||
return return_err(err, &e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_set_global(
|
||||
api_string_t* name,
|
||||
api_list_u8_t* bson_value,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (!name || name->len == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj valueObj;
|
||||
if (bson_value && bson_value->ptr && bson_value->len > 0) {
|
||||
if (!validate_bson(bson_value->ptr, bson_value->len, &e)) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
valueObj = mongo::BSONObj(reinterpret_cast<const char*>(bson_value->ptr));
|
||||
}
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.setGlobal(
|
||||
reinterpret_cast<const char*>(name->ptr), name->len, valueObj, &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_get_global(
|
||||
api_string_t* name, api_list_u8_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (!name || name->len == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj out;
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.getGlobal(
|
||||
reinterpret_cast<const char*>(name->ptr), name->len, &out, &e));
|
||||
|
||||
if (rc != mongo::mozjs::wasm::SM_OK) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
if (!list_u8_dup(ret, reinterpret_cast<const uint8_t*>(out.objdata()), out.objsize())) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_NOMEM;
|
||||
mongo::mozjs::wasm::set_string(
|
||||
&e.msg, &e.msg_len, "OOM: failed to allocate global return buffer");
|
||||
return return_err(err, &e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_set_global_value(
|
||||
api_string_t* name,
|
||||
api_list_u8_t* bson_element,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (!name || name->len == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj valueObj;
|
||||
if (bson_element && bson_element->ptr && bson_element->len > 0) {
|
||||
if (!validate_bson(bson_element->ptr, bson_element->len, &e)) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
valueObj = mongo::BSONObj(reinterpret_cast<const char*>(bson_element->ptr));
|
||||
}
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.setGlobalValue(
|
||||
reinterpret_cast<const char*>(name->ptr), name->len, valueObj, &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_setup_emit(
|
||||
int64_t* maybe_byte_limit,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
bool hasLimit = (maybe_byte_limit != nullptr);
|
||||
int64_t limit = hasLimit ? *maybe_byte_limit : 0;
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.setupEmit(limit, hasLimit, &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_invoke_predicate(
|
||||
exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* document,
|
||||
bool* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (handle == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj docObj;
|
||||
if (document && document->ptr && document->len > 0) {
|
||||
if (!validate_bson(document->ptr, document->len, &e)) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
docObj = mongo::BSONObj(reinterpret_cast<const char*>(document->ptr));
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.invokePredicate(
|
||||
static_cast<uint64_t>(handle), std::move(docObj), &result, &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = result;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_invoke_map(
|
||||
exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* document,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
|
||||
if (handle == 0) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_INVALID_ARG;
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
mongo::BSONObj docObj;
|
||||
if (document && document->ptr && document->len > 0) {
|
||||
if (!validate_bson(document->ptr, document->len, &e)) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
docObj = mongo::BSONObj(reinterpret_cast<const char*>(document->ptr));
|
||||
}
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.invokeMap(
|
||||
static_cast<uint64_t>(handle), std::move(docObj), &e));
|
||||
|
||||
if (rc == mongo::mozjs::wasm::SM_OK) {
|
||||
if (ret)
|
||||
*ret = 0;
|
||||
return true;
|
||||
}
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
extern "C" bool exports_mongo_mozjs_mozjs_drain_emit_buffer(
|
||||
api_list_u8_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err) {
|
||||
mongo::mozjs::wasm::wasm_mozjs_error_t e{};
|
||||
mongo::BSONObj out;
|
||||
|
||||
auto rc = static_cast<int64_t>(mongo::mozjs::wasm::g_engine.drainEmitBuffer(&out, &e));
|
||||
if (rc != mongo::mozjs::wasm::SM_OK) {
|
||||
return return_err(err, &e);
|
||||
}
|
||||
|
||||
if (!list_u8_dup(ret, reinterpret_cast<const uint8_t*>(out.objdata()), out.objsize())) {
|
||||
e.code = mongo::mozjs::wasm::SM_E_NOMEM;
|
||||
mongo::mozjs::wasm::set_string(&e.msg, &e.msg_len, "OOM: failed to allocate emit buffer");
|
||||
return return_err(err, &e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
942
src/mongo/scripting/mozjs/wasm/engine/engine.cpp
Normal file
942
src/mongo/scripting/mozjs/wasm/engine/engine.cpp
Normal file
@ -0,0 +1,942 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "engine.h"
|
||||
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/bson/bsonobjbuilder.h"
|
||||
#include "mongo/scripting/mozjs/common/error.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bindata.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/types/code.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbpointer.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbref.h"
|
||||
#include "mongo/scripting/mozjs/common/types/maxkey.h"
|
||||
#include "mongo/scripting/mozjs/common/types/minkey.h"
|
||||
#include "mongo/scripting/mozjs/common/types/nativefunction.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberdecimal.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberint.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberlong.h"
|
||||
#include "mongo/scripting/mozjs/common/types/oid.h"
|
||||
#include "mongo/scripting/mozjs/common/types/regexp.h"
|
||||
#include "mongo/scripting/mozjs/common/types/status.h"
|
||||
#include "mongo/scripting/mozjs/common/types/timestamp.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/valuewriter.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "error.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsfriendapi.h"
|
||||
|
||||
#include "js/CompilationAndEvaluation.h"
|
||||
#include "js/Conversions.h"
|
||||
#include "js/ErrorReport.h"
|
||||
#include "js/Exception.h"
|
||||
#include "js/GlobalObject.h"
|
||||
#include "js/Initialization.h"
|
||||
#include "js/Interrupt.h"
|
||||
#include "js/Realm.h"
|
||||
#include "js/SourceText.h"
|
||||
#include "js/String.h"
|
||||
#include "js/Value.h"
|
||||
#include "js/ValueArray.h"
|
||||
#include <mozilla/Utf8.h>
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
uint32_t g_wasmJsHeapLimitMB = 0;
|
||||
|
||||
const char* const kInvokeResult = "__returnValue";
|
||||
|
||||
FunctionSlot* MozJSScriptEngine::resolveHandle(uint64_t handle, wasm_mozjs_error_t* err) {
|
||||
if (handle == 0 || handle > _slots.size()) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "invalid function handle");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
FunctionSlot& slot = _slots[handle - 1];
|
||||
if (!slot.fn) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "function handle has no function");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
return &slot;
|
||||
}
|
||||
|
||||
MozJSScriptEngine::~MozJSScriptEngine() {
|
||||
if (_initialized) {
|
||||
shutdown(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::init(const wasm_mozjs_startup_options_t* opt,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
|
||||
if (_initialized)
|
||||
return SM_OK;
|
||||
|
||||
if (!JS_Init()) {
|
||||
if (err) {
|
||||
err->code = SM_E_INTERNAL;
|
||||
set_string(&err->msg, &err->msg_len, "JS_Init failed");
|
||||
}
|
||||
return SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
// Create context with reasonable heap size for WASM builds
|
||||
constexpr size_t kMaxHeapSizeMB = 2048; // 2 GB upper bound
|
||||
if (opt->heapSize == 0 || opt->heapSize > kMaxHeapSizeMB) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "heapSize out of valid range (1-2048 MB)");
|
||||
}
|
||||
JS_ShutDown();
|
||||
return SM_E_INVALID_ARG;
|
||||
}
|
||||
_cx = JS_NewContext(static_cast<size_t>(opt->heapSize) * 1024 * 1024);
|
||||
if (!_cx) {
|
||||
if (err) {
|
||||
err->code = SM_E_OOM;
|
||||
set_string(&err->msg, &err->msg_len, "JS_NewContext failed");
|
||||
}
|
||||
JS_ShutDown();
|
||||
return SM_E_OOM;
|
||||
}
|
||||
|
||||
// Store configured heap limit for GlobalInfo::getJSHeapLimitMB.
|
||||
g_wasmJsHeapLimitMB = opt->heapSize;
|
||||
|
||||
_rt = JS_GetRuntime(_cx);
|
||||
if (!_rt) {
|
||||
if (err) {
|
||||
err->code = SM_E_INTERNAL;
|
||||
set_string(&err->msg, &err->msg_len, "JS_GetRuntime returned null");
|
||||
}
|
||||
JS_DestroyContext(_cx);
|
||||
_cx = nullptr;
|
||||
JS_ShutDown();
|
||||
return SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
// Initialize self-hosted code (required before creating global)
|
||||
ExecutionCheck chk(_cx, err);
|
||||
if (!chk.ok(JS::InitSelfHostedCode(_cx), SM_E_INTERNAL)) {
|
||||
JS_DestroyContext(_cx);
|
||||
_cx = nullptr;
|
||||
JS_ShutDown();
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
// Create global object (must be done before setting interrupt callback)
|
||||
static const JSClass _globalclass = {
|
||||
"global", JSCLASS_GLOBAL_FLAGS, &JS::DefaultGlobalClassOps};
|
||||
|
||||
JS::RealmOptions ro;
|
||||
_global.init(_cx);
|
||||
{
|
||||
JS::RootedObject g(_cx,
|
||||
chk.okPtr(JS_NewGlobalObject(
|
||||
_cx, &_globalclass, nullptr, JS::DontFireOnNewGlobalHook, ro)));
|
||||
if (!g) {
|
||||
shutdown(nullptr);
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
_global = g;
|
||||
}
|
||||
|
||||
// Enter realm and initialize standard classes
|
||||
{
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
if (!chk.ok(JS::InitRealmStandardClasses(_cx), SM_E_INTERNAL)) {
|
||||
shutdown(nullptr);
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Fire the new global hook after initialization (as per SpiderMonkey docs)
|
||||
JS_FireOnNewGlobalObject(_cx, _global);
|
||||
// JS_FireOnNewGlobalObject doesn't return a value, but we check for exceptions
|
||||
if (!chk.ok(!JS_IsExceptionPending(_cx), SM_E_INTERNAL)) {
|
||||
shutdown(nullptr);
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
// Stash pointer for interrupt callback (after global is set up)
|
||||
JS_SetContextPrivate(_cx, static_cast<MozJSCommonRuntimeInterface*>(this));
|
||||
|
||||
// Set up interrupt callback for timeouts/cancel/interrupt
|
||||
if (!JS_AddInterruptCallback(_cx, &MozJSScriptEngine::interruptCallback)) {
|
||||
if (err) {
|
||||
err->code = SM_E_INTERNAL;
|
||||
set_string(&err->msg, &err->msg_len, "JS_AddInterruptCallback failed");
|
||||
}
|
||||
shutdown(nullptr);
|
||||
return SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
_prototypeInstaller = std::make_unique<MozJSPrototypeInstaller>(_cx);
|
||||
|
||||
{
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
_internedStrings = std::make_unique<InternedStringTable>(_cx);
|
||||
_prototypeInstaller->installTypes(_global);
|
||||
}
|
||||
|
||||
_initialized = true;
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::shutdown(wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
|
||||
if (!_initialized && !_cx)
|
||||
return SM_OK;
|
||||
|
||||
// Drop all PersistentRooted objects while the context is alive.
|
||||
_slots.clear();
|
||||
_internedStrings.reset();
|
||||
_prototypeInstaller.reset();
|
||||
|
||||
_global.reset();
|
||||
if (_cx) {
|
||||
// Do NOT null context-private before DestroyContext:
|
||||
// GC finalizers need getCommonRuntime() during teardown.
|
||||
JS_DestroyContext(_cx);
|
||||
_cx = nullptr;
|
||||
}
|
||||
|
||||
JS_ShutDown();
|
||||
|
||||
_rt = nullptr;
|
||||
_initialized = false;
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::interrupt(wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx)
|
||||
return SM_E_BAD_STATE;
|
||||
|
||||
ExecutionCheck chk(_cx, err);
|
||||
// Request interrupt callback - host side manages interrupt state
|
||||
JS_RequestInterruptCallback(_cx);
|
||||
// JS_RequestInterruptCallback doesn't return a value, but we check for exceptions
|
||||
if (!chk.ok(!JS_IsExceptionPending(_cx), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::createFunction(const uint8_t* src,
|
||||
size_t len,
|
||||
uint64_t* out_handle,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!src || len == 0 || !out_handle)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
std::string code_str;
|
||||
code_str.reserve(len + 2);
|
||||
code_str += '(';
|
||||
code_str.append(reinterpret_cast<const char*>(src), len);
|
||||
code_str += ')';
|
||||
|
||||
JS::CompileOptions opts(_cx);
|
||||
opts.setFileAndLine("wasm:function", 1);
|
||||
|
||||
JS::SourceText<mozilla::Utf8Unit> text;
|
||||
// Use .data() and .size() like in implscope.cpp
|
||||
// The CharT overload accepts const char* for UTF-8
|
||||
if (!chk.ok(text.init(_cx, code_str.data(), code_str.size(), JS::SourceOwnership::Borrowed),
|
||||
SM_E_ENCODING)) {
|
||||
return err ? err->code : SM_E_ENCODING;
|
||||
}
|
||||
|
||||
JS::RootedValue v(_cx);
|
||||
if (!chk.ok(JS::Evaluate(_cx, opts, text, &v), SM_E_COMPILE)) {
|
||||
// classify common path
|
||||
if (err && err->code == SM_E_PENDING_EXCEPTION)
|
||||
err->code = SM_E_COMPILE;
|
||||
return err ? err->code : SM_E_COMPILE;
|
||||
}
|
||||
|
||||
if (!v.isObject() || !js::IsFunctionObject(v.toObjectOrNull())) {
|
||||
if (err) {
|
||||
err->code = SM_E_TYPE;
|
||||
set_string(
|
||||
&err->msg, &err->msg_len, "createFunction: evaluated value is not a function");
|
||||
}
|
||||
return SM_E_TYPE;
|
||||
}
|
||||
|
||||
_slots.emplace_back(_cx);
|
||||
FunctionSlot& slot = _slots.back();
|
||||
slot.fn = &v.toObject();
|
||||
|
||||
*out_handle = static_cast<uint64_t>(_slots.size()); // 1-based
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::invokeFunction(uint64_t handle,
|
||||
mongo::BSONObj&& argsObject,
|
||||
mongo::BSONObj* outBson,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
|
||||
auto* slot = resolveHandle(handle, err);
|
||||
if (!slot)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
const int nargs = argsObject.nFields();
|
||||
|
||||
JS::RootedValueVector args(_cx);
|
||||
|
||||
if (nargs) {
|
||||
BSONObjIterator it(argsObject);
|
||||
for (int i = 0; i < nargs; i++) {
|
||||
BSONElement next = it.next();
|
||||
|
||||
JS::RootedValue value(_cx);
|
||||
ValueReader(_cx, &value).fromBSONElement(next, argsObject, false /*readOnlyArgs*/);
|
||||
|
||||
if (!args.append(value)) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "Failed to append property");
|
||||
}
|
||||
return SM_E_INVALID_ARG;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JS::RootedValue out(_cx);
|
||||
JS::RootedObject funcObj(_cx, slot->fn);
|
||||
JS::RootedValue funVal(_cx, JS::ObjectValue(*funcObj));
|
||||
bool success = JS::Call(_cx, _global, funVal, args, &out);
|
||||
|
||||
if (!chk.ok(success, SM_E_RUNTIME)) {
|
||||
if (err && err->code == SM_E_PENDING_EXCEPTION)
|
||||
err->code = SM_E_RUNTIME;
|
||||
return err ? err->code : SM_E_RUNTIME;
|
||||
}
|
||||
|
||||
if (_emitByteLimit > 0 && _emitBytesUsed > _emitByteLimit) {
|
||||
if (err) {
|
||||
err->code = SM_E_RUNTIME;
|
||||
set_string(&err->msg, &err->msg_len, "emit() exceeded memory limit");
|
||||
}
|
||||
return SM_E_RUNTIME;
|
||||
}
|
||||
|
||||
// Store return value on global (same key as implscope) so getReturnValueBson can read it.
|
||||
ObjectWrapper(_cx, _global).setValue(kInvokeResult, out);
|
||||
|
||||
if (outBson) {
|
||||
// Also write return value as BSON when caller provides a buffer (wrapped like implscope).
|
||||
JS::RootedObject rout(_cx, JS_NewPlainObject(_cx));
|
||||
if (rout) {
|
||||
ObjectWrapper wout(_cx, rout);
|
||||
wout.setValue(kInvokeResult, out);
|
||||
*outBson = wout.toBSON();
|
||||
}
|
||||
}
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::getReturnValueBson(mongo::BSONObj* out, wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!out)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
JS::RootedValue rval(_cx);
|
||||
JS::RootedString key_str(_cx, chk.okPtr(JS_NewStringCopyZ(_cx, kInvokeResult)));
|
||||
if (!key_str) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
JS::RootedId rid(_cx);
|
||||
if (!chk.ok(JS_StringToId(_cx, key_str, &rid), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
if (!chk.ok(JS_GetPropertyById(_cx, _global, rid, &rval), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
// Wrap value in object for BSON (same pattern as implscope) so primitives serialize correctly.
|
||||
JS::RootedObject rout(_cx, JS_NewPlainObject(_cx));
|
||||
if (!rout) {
|
||||
if (err) {
|
||||
err->code = SM_E_INTERNAL;
|
||||
set_string(
|
||||
&err->msg, &err->msg_len, "getReturnValueBson: failed to create wrapper object");
|
||||
}
|
||||
return SM_E_INTERNAL;
|
||||
}
|
||||
ObjectWrapper wout(_cx, rout);
|
||||
wout.setValue(kInvokeResult, rval);
|
||||
*out = wout.toBSON();
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::invokePredicate(uint64_t handle,
|
||||
mongo::BSONObj&& document,
|
||||
bool* outResult,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!outResult)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
auto* slot = resolveHandle(handle, err);
|
||||
if (!slot)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
JS::RootedValue smrecv(_cx);
|
||||
if (!document.isEmpty())
|
||||
ValueReader(_cx, &smrecv).fromBSON(document, nullptr, false);
|
||||
else
|
||||
smrecv.setObjectOrNull(_global);
|
||||
|
||||
JS::RootedValueVector args(_cx);
|
||||
|
||||
JS::RootedValue out(_cx);
|
||||
JS::RootedObject obj(_cx, smrecv.toObjectOrNull());
|
||||
JS::RootedObject funcObj(_cx, slot->fn);
|
||||
JS::RootedValue funVal(_cx, JS::ObjectValue(*funcObj));
|
||||
bool success = JS::Call(_cx, obj, funVal, args, &out);
|
||||
|
||||
if (!chk.ok(success, SM_E_RUNTIME)) {
|
||||
if (err && err->code == SM_E_PENDING_EXCEPTION)
|
||||
err->code = SM_E_RUNTIME;
|
||||
return err ? err->code : SM_E_RUNTIME;
|
||||
}
|
||||
|
||||
*outResult = JS::ToBoolean(out);
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::invokeMap(uint64_t handle,
|
||||
mongo::BSONObj&& document,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
|
||||
auto* slot = resolveHandle(handle, err);
|
||||
if (!slot)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
JS::RootedValue smrecv(_cx);
|
||||
if (!document.isEmpty())
|
||||
ValueReader(_cx, &smrecv).fromBSON(document, nullptr, false);
|
||||
else
|
||||
smrecv.setObjectOrNull(_global);
|
||||
|
||||
JS::RootedValueVector args(_cx);
|
||||
|
||||
JS::RootedValue out(_cx);
|
||||
JS::RootedObject obj(_cx, smrecv.toObjectOrNull());
|
||||
JS::RootedObject funcObj(_cx, slot->fn);
|
||||
JS::RootedValue funVal(_cx, JS::ObjectValue(*funcObj));
|
||||
bool success = JS::Call(_cx, obj, funVal, args, &out);
|
||||
|
||||
if (!chk.ok(success, SM_E_RUNTIME)) {
|
||||
if (err && err->code == SM_E_PENDING_EXCEPTION)
|
||||
err->code = SM_E_RUNTIME;
|
||||
return err ? err->code : SM_E_RUNTIME;
|
||||
}
|
||||
|
||||
if (_emitByteLimit > 0 && _emitBytesUsed > _emitByteLimit) {
|
||||
if (err) {
|
||||
err->code = SM_E_RUNTIME;
|
||||
set_string(&err->msg, &err->msg_len, "emit() exceeded memory limit");
|
||||
}
|
||||
return SM_E_RUNTIME;
|
||||
}
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::setGlobal(const char* name,
|
||||
size_t name_len,
|
||||
const mongo::BSONObj& value,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!name || name_len == 0)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
// Create a JS plain object and populate from BSON fields.
|
||||
JS::RootedObject jsObj(_cx, JS_NewPlainObject(_cx));
|
||||
if (!jsObj) {
|
||||
if (err) {
|
||||
err->code = SM_E_OOM;
|
||||
set_string(&err->msg, &err->msg_len, "setGlobal: JS_NewPlainObject failed");
|
||||
}
|
||||
return SM_E_OOM;
|
||||
}
|
||||
|
||||
for (BSONObjIterator it(value); it.more();) {
|
||||
BSONElement elem = it.next();
|
||||
JS::RootedValue val(_cx);
|
||||
ValueReader(_cx, &val).fromBSONElement(elem, value, false /* readOnlyArgs */);
|
||||
|
||||
if (!chk.ok(JS_SetProperty(_cx, jsObj, elem.fieldName(), val), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
}
|
||||
|
||||
// Set the constructed object as a property on the global.
|
||||
JS::RootedValue objVal(_cx, JS::ObjectValue(*jsObj));
|
||||
std::string nameStr(name, name_len);
|
||||
if (!chk.ok(JS_SetProperty(_cx, _global, nameStr.c_str(), objVal), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::getGlobal(const char* name,
|
||||
size_t name_len,
|
||||
mongo::BSONObj* out,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!name || name_len == 0 || !out)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
std::string nameStr(name, name_len);
|
||||
|
||||
// Check if the property exists on the global object.
|
||||
bool found = false;
|
||||
if (!chk.ok(JS_HasProperty(_cx, _global, nameStr.c_str(), &found), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
if (!found) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "getGlobal: property not found");
|
||||
}
|
||||
return SM_E_INVALID_ARG;
|
||||
}
|
||||
|
||||
JS::RootedValue rval(_cx);
|
||||
if (!chk.ok(JS_GetProperty(_cx, _global, nameStr.c_str(), &rval), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
if (rval.isUndefined()) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "getGlobal: property is undefined");
|
||||
}
|
||||
return SM_E_INVALID_ARG;
|
||||
}
|
||||
|
||||
// Convert the JS value to BSON.
|
||||
if (rval.isObject()) {
|
||||
JS::RootedObject robj(_cx, &rval.toObject());
|
||||
ObjectWrapper wrapper(_cx, robj);
|
||||
*out = wrapper.toBSON();
|
||||
} else {
|
||||
// Wrap a non-object (primitive) value in a BSON document.
|
||||
JS::RootedObject wrapObj(_cx, JS_NewPlainObject(_cx));
|
||||
if (!wrapObj) {
|
||||
if (err) {
|
||||
err->code = SM_E_OOM;
|
||||
}
|
||||
return SM_E_OOM;
|
||||
}
|
||||
ObjectWrapper wout(_cx, wrapObj);
|
||||
wout.setValue("__value", rval);
|
||||
*out = wout.toBSON();
|
||||
}
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::setGlobalValue(const char* name,
|
||||
size_t name_len,
|
||||
const mongo::BSONObj& singleElementDoc,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!name || name_len == 0)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
BSONObjIterator it(singleElementDoc);
|
||||
if (!it.more()) {
|
||||
if (err) {
|
||||
err->code = SM_E_INVALID_ARG;
|
||||
set_string(&err->msg, &err->msg_len, "setGlobalValue: empty BSON document");
|
||||
}
|
||||
return SM_E_INVALID_ARG;
|
||||
}
|
||||
|
||||
BSONElement elem = it.next();
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
ExecutionCheck chk(_cx, err);
|
||||
|
||||
JS::RootedValue val(_cx);
|
||||
ValueReader(_cx, &val).fromBSONElement(elem, singleElementDoc, false);
|
||||
|
||||
std::string nameStr(name, name_len);
|
||||
if (!chk.ok(JS_SetProperty(_cx, _global, nameStr.c_str(), val), SM_E_INTERNAL)) {
|
||||
return err ? err->code : SM_E_INTERNAL;
|
||||
}
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
BSONObj MozJSScriptEngine::_emitCallback(const BSONObj& args, void* data) {
|
||||
auto* engine = static_cast<MozJSScriptEngine*>(data);
|
||||
|
||||
BSONObjIterator it(args);
|
||||
BSONElement keyElem = it.more() ? it.next() : BSONElement();
|
||||
BSONElement valElem = it.more() ? it.next() : BSONElement();
|
||||
|
||||
BSONObjBuilder b;
|
||||
if (keyElem.type() == BSONType::undefined || keyElem.eoo())
|
||||
b.appendNull("k");
|
||||
else
|
||||
b.appendAs(keyElem, "k");
|
||||
|
||||
if (valElem.eoo())
|
||||
b.appendNull("v");
|
||||
else
|
||||
b.appendAs(valElem, "v");
|
||||
|
||||
BSONObj doc = b.obj();
|
||||
engine->_emitBytesUsed += doc.objsize();
|
||||
if (engine->_emitBytesUsed <= engine->_emitByteLimit)
|
||||
engine->_emitBuffer.push_back(std::move(doc));
|
||||
|
||||
return BSONObj();
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::setupEmit(int64_t byteLimit,
|
||||
bool hasByteLimit,
|
||||
wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized || !_cx || !_global)
|
||||
return SM_E_BAD_STATE;
|
||||
|
||||
_emitByteLimit = hasByteLimit ? byteLimit : (16 * 1024 * 1024);
|
||||
_emitBytesUsed = 0;
|
||||
_emitBuffer.clear();
|
||||
|
||||
JSAutoRealm ar(_cx, _global);
|
||||
|
||||
JS::RootedObject obj(_cx);
|
||||
NativeFunctionInfo::make(_cx, &obj, _emitCallback, this);
|
||||
|
||||
JS::RootedValue value(_cx);
|
||||
value.setObjectOrNull(obj);
|
||||
ObjectWrapper(_cx, _global).setValue("emit", value);
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
err_code_t MozJSScriptEngine::drainEmitBuffer(mongo::BSONObj* out, wasm_mozjs_error_t* err) {
|
||||
clear_error(err);
|
||||
if (!_initialized)
|
||||
return SM_E_BAD_STATE;
|
||||
if (!out)
|
||||
return SM_E_INVALID_ARG;
|
||||
|
||||
BSONObjBuilder builder;
|
||||
BSONArrayBuilder arr(builder.subarrayStart("emits"));
|
||||
for (const auto& doc : _emitBuffer) {
|
||||
arr.append(doc);
|
||||
}
|
||||
arr.done();
|
||||
builder.append("bytesUsed", static_cast<long long>(_emitBytesUsed));
|
||||
|
||||
*out = builder.obj();
|
||||
|
||||
_emitBuffer.clear();
|
||||
_emitBytesUsed = 0;
|
||||
|
||||
return SM_OK;
|
||||
}
|
||||
|
||||
bool MozJSScriptEngine::interruptCallback(JSContext* cx) {
|
||||
// Interrupt callback is called by MozJS when JS_RequestInterruptCallback is invoked.
|
||||
// The host side manages interrupt/cancel state and termination logic.
|
||||
// This callback just allows the interrupt to be processed - the host side
|
||||
// should check return codes and exceptions to determine if execution was terminated.
|
||||
(void)cx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::gc() {
|
||||
if (!_initialized || !_cx) {
|
||||
return;
|
||||
}
|
||||
JS_GC(_cx);
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::sleep(Milliseconds ms) {
|
||||
auto count = ms.count();
|
||||
if (count <= 0)
|
||||
return;
|
||||
struct timespec ts;
|
||||
ts.tv_sec = static_cast<time_t>(count / 1000);
|
||||
ts.tv_nsec = static_cast<long>((count % 1000) * 1000000L);
|
||||
nanosleep(&ts, nullptr);
|
||||
}
|
||||
|
||||
std::size_t MozJSScriptEngine::getGeneration() const {
|
||||
// Return a constant generation for WASM
|
||||
return 1;
|
||||
}
|
||||
|
||||
JS::HandleId MozJSScriptEngine::getInternedStringId(InternedString name) {
|
||||
return _internedStrings->getInternedString(name);
|
||||
}
|
||||
|
||||
std::int64_t* MozJSScriptEngine::trackedNewInt64(std::int64_t value) {
|
||||
auto* p = new std::int64_t(value);
|
||||
trackNewPointer(p);
|
||||
return p;
|
||||
}
|
||||
|
||||
WrapType<NumberLongInfo>& MozJSScriptEngine::numberLongProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->numberLongProto();
|
||||
}
|
||||
|
||||
WrapType<NumberIntInfo>& MozJSScriptEngine::numberIntProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->numberIntProto();
|
||||
}
|
||||
|
||||
WrapType<NumberDecimalInfo>& MozJSScriptEngine::numberDecimalProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->numberDecimalProto();
|
||||
}
|
||||
|
||||
WrapType<OIDInfo>& MozJSScriptEngine::oidProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->oidProto();
|
||||
}
|
||||
|
||||
WrapType<BinDataInfo>& MozJSScriptEngine::binDataProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->binDataProto();
|
||||
}
|
||||
|
||||
WrapType<TimestampInfo>& MozJSScriptEngine::timestampProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->timestampProto();
|
||||
}
|
||||
|
||||
WrapType<MaxKeyInfo>& MozJSScriptEngine::maxKeyProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->maxKeyProto();
|
||||
}
|
||||
|
||||
WrapType<MinKeyInfo>& MozJSScriptEngine::minKeyProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->minKeyProto();
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::trackNewPointer(void* ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::trackDeletePointer(void* ptr) {
|
||||
(void)ptr;
|
||||
}
|
||||
|
||||
WrapType<CodeInfo>& MozJSScriptEngine::codeProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->codeProto();
|
||||
}
|
||||
|
||||
WrapType<DBPointerInfo>& MozJSScriptEngine::dbPointerProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->dbPointerProto();
|
||||
}
|
||||
|
||||
WrapType<NativeFunctionInfo>& MozJSScriptEngine::nativeFunctionProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->nativeFunctionProto();
|
||||
}
|
||||
|
||||
WrapType<ErrorInfo>& MozJSScriptEngine::errorProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->errorProto();
|
||||
}
|
||||
|
||||
WrapType<MongoStatusInfo>& MozJSScriptEngine::mongoStatusProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->statusProto();
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::setStatus(Status status) {
|
||||
_status = std::move(status);
|
||||
}
|
||||
|
||||
bool MozJSScriptEngine::isJavaScriptProtectionEnabled() const {
|
||||
// JavaScript protection (--enableJavaScriptProtection) is not applicable
|
||||
// in WASM builds where the engine runs in a sandboxed component.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool MozJSScriptEngine::requiresOwnedObjects() const {
|
||||
// WASM engine does not require BSON objects to be owned.
|
||||
return false;
|
||||
}
|
||||
|
||||
void MozJSScriptEngine::newFunction(StringData raw, JS::MutableHandleValue out) {
|
||||
// Compile the code string as a function expression, same as createFunction().
|
||||
std::string wrapped;
|
||||
wrapped.reserve(raw.size() + 2);
|
||||
wrapped += '(';
|
||||
wrapped.append(raw.data(), raw.size());
|
||||
wrapped += ')';
|
||||
|
||||
JS::CompileOptions opts(_cx);
|
||||
opts.setFileAndLine("wasm:newFunction", 1);
|
||||
|
||||
JS::SourceText<mozilla::Utf8Unit> text;
|
||||
if (!text.init(_cx, wrapped.data(), wrapped.size(), JS::SourceOwnership::Borrowed)) {
|
||||
return; // JS exception is pending
|
||||
}
|
||||
|
||||
JS::Evaluate(_cx, opts, text, out);
|
||||
// If Evaluate fails, JS exception is pending — caller handles it.
|
||||
}
|
||||
|
||||
WrapType<BSONInfo>& MozJSScriptEngine::bsonProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->bsonProto();
|
||||
}
|
||||
|
||||
WrapType<DBRefInfo>& MozJSScriptEngine::dbRefProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->dbRefProto();
|
||||
}
|
||||
|
||||
WrapType<RegExpInfo>& MozJSScriptEngine::regExpProto() {
|
||||
if (!_prototypeInstaller) {
|
||||
__builtin_trap(); // WASM trap: prototypeInstaller not initialized
|
||||
}
|
||||
return _prototypeInstaller->regExpProto();
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
312
src/mongo/scripting/mozjs/wasm/engine/engine.h
Normal file
312
src/mongo/scripting/mozjs/wasm/engine/engine.h
Normal file
@ -0,0 +1,312 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/bson/bsonobj.h"
|
||||
#include "mongo/scripting/mozjs/common/error.h"
|
||||
#include "mongo/scripting/mozjs/common/global.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bindata.h"
|
||||
#include "mongo/scripting/mozjs/common/types/bson.h"
|
||||
#include "mongo/scripting/mozjs/common/types/code.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbpointer.h"
|
||||
#include "mongo/scripting/mozjs/common/types/dbref.h"
|
||||
#include "mongo/scripting/mozjs/common/types/maxkey.h"
|
||||
#include "mongo/scripting/mozjs/common/types/minkey.h"
|
||||
#include "mongo/scripting/mozjs/common/types/nativefunction.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberdecimal.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberint.h"
|
||||
#include "mongo/scripting/mozjs/common/types/numberlong.h"
|
||||
#include "mongo/scripting/mozjs/common/types/oid.h"
|
||||
#include "mongo/scripting/mozjs/common/types/regexp.h"
|
||||
#include "mongo/scripting/mozjs/common/types/status.h"
|
||||
#include "mongo/scripting/mozjs/common/types/timestamp.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
#include "mongo/scripting/mozjs/shared/mozjs_wasm_startup_options.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/TypeDecls.h"
|
||||
|
||||
struct JSContext;
|
||||
struct JSRuntime;
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
extern uint32_t g_wasmJsHeapLimitMB;
|
||||
|
||||
class ExecutionCheck;
|
||||
|
||||
struct FunctionSlot {
|
||||
JS::PersistentRootedObject fn;
|
||||
explicit FunctionSlot(JSContext* cx) : fn(cx) {}
|
||||
};
|
||||
|
||||
class MozJSPrototypeInstaller {
|
||||
public:
|
||||
explicit MozJSPrototypeInstaller(JSContext* context)
|
||||
: _cx(context),
|
||||
_globalProto(_cx),
|
||||
_binDataProto(_cx),
|
||||
_bsonProto(_cx),
|
||||
_codeProto(_cx),
|
||||
_dbPointerProto(_cx),
|
||||
_dbRefProto(_cx),
|
||||
_errorProto(_cx),
|
||||
_maxKeyProto(_cx),
|
||||
_minKeyProto(_cx),
|
||||
_nativeFunctionProto(_cx),
|
||||
_numberDecimalProto(_cx),
|
||||
_numberIntProto(_cx),
|
||||
_numberLongProto(_cx),
|
||||
_oidProto(_cx),
|
||||
_regExpProto(_cx),
|
||||
_statusProto(_cx),
|
||||
_timestampProto(_cx) {
|
||||
invariant(_cx);
|
||||
}
|
||||
|
||||
WrapType<GlobalInfo>& globalProto() {
|
||||
return _globalProto;
|
||||
}
|
||||
WrapType<BinDataInfo>& binDataProto() {
|
||||
return _binDataProto;
|
||||
}
|
||||
WrapType<BSONInfo>& bsonProto() {
|
||||
return _bsonProto;
|
||||
}
|
||||
WrapType<CodeInfo>& codeProto() {
|
||||
return _codeProto;
|
||||
}
|
||||
WrapType<DBPointerInfo>& dbPointerProto() {
|
||||
return _dbPointerProto;
|
||||
}
|
||||
WrapType<DBRefInfo>& dbRefProto() {
|
||||
return _dbRefProto;
|
||||
}
|
||||
WrapType<ErrorInfo>& errorProto() {
|
||||
return _errorProto;
|
||||
}
|
||||
WrapType<MaxKeyInfo>& maxKeyProto() {
|
||||
return _maxKeyProto;
|
||||
}
|
||||
WrapType<MinKeyInfo>& minKeyProto() {
|
||||
return _minKeyProto;
|
||||
}
|
||||
WrapType<NativeFunctionInfo>& nativeFunctionProto() {
|
||||
return _nativeFunctionProto;
|
||||
}
|
||||
WrapType<NumberDecimalInfo>& numberDecimalProto() {
|
||||
return _numberDecimalProto;
|
||||
}
|
||||
WrapType<NumberIntInfo>& numberIntProto() {
|
||||
return _numberIntProto;
|
||||
}
|
||||
WrapType<NumberLongInfo>& numberLongProto() {
|
||||
return _numberLongProto;
|
||||
}
|
||||
WrapType<OIDInfo>& oidProto() {
|
||||
return _oidProto;
|
||||
}
|
||||
WrapType<RegExpInfo>& regExpProto() {
|
||||
return _regExpProto;
|
||||
}
|
||||
WrapType<MongoStatusInfo>& statusProto() {
|
||||
return _statusProto;
|
||||
}
|
||||
WrapType<TimestampInfo>& timestampProto() {
|
||||
return _timestampProto;
|
||||
}
|
||||
|
||||
|
||||
void installTypes(JS::HandleObject global) {
|
||||
// GlobalInfo cannot use install() because JSCLASS_GLOBAL_FLAGS
|
||||
// prevents JS_InitClass on an existing global. Install its
|
||||
// freeFunctions (print, gc, sleep, etc.) directly.
|
||||
JS_DefineFunctions(_cx, global, GlobalInfo::freeFunctions);
|
||||
_binDataProto.install(global);
|
||||
_bsonProto.install(global);
|
||||
_codeProto.install(global);
|
||||
_dbPointerProto.install(global);
|
||||
_dbRefProto.install(global);
|
||||
_errorProto.install(global);
|
||||
_maxKeyProto.install(global);
|
||||
_minKeyProto.install(global);
|
||||
_nativeFunctionProto.install(global);
|
||||
_numberDecimalProto.install(global);
|
||||
_numberIntProto.install(global);
|
||||
_numberLongProto.install(global);
|
||||
_oidProto.install(global);
|
||||
_regExpProto.install(global);
|
||||
_timestampProto.install(global);
|
||||
_statusProto.install(global);
|
||||
}
|
||||
|
||||
private:
|
||||
JSContext* _cx = nullptr;
|
||||
WrapType<GlobalInfo> _globalProto;
|
||||
WrapType<BinDataInfo> _binDataProto;
|
||||
WrapType<BSONInfo> _bsonProto;
|
||||
WrapType<CodeInfo> _codeProto;
|
||||
WrapType<DBPointerInfo> _dbPointerProto;
|
||||
WrapType<DBRefInfo> _dbRefProto;
|
||||
WrapType<ErrorInfo> _errorProto;
|
||||
WrapType<MaxKeyInfo> _maxKeyProto;
|
||||
WrapType<MinKeyInfo> _minKeyProto;
|
||||
WrapType<NativeFunctionInfo> _nativeFunctionProto;
|
||||
WrapType<NumberDecimalInfo> _numberDecimalProto;
|
||||
WrapType<NumberIntInfo> _numberIntProto;
|
||||
WrapType<NumberLongInfo> _numberLongProto;
|
||||
WrapType<OIDInfo> _oidProto;
|
||||
WrapType<RegExpInfo> _regExpProto;
|
||||
WrapType<MongoStatusInfo> _statusProto;
|
||||
WrapType<TimestampInfo> _timestampProto;
|
||||
};
|
||||
|
||||
class MozJSScriptEngine : private MozJSCommonRuntimeInterface {
|
||||
public:
|
||||
MozJSScriptEngine() = default;
|
||||
~MozJSScriptEngine();
|
||||
|
||||
MozJSScriptEngine(const MozJSScriptEngine&) = delete;
|
||||
MozJSScriptEngine& operator=(const MozJSScriptEngine&) = delete;
|
||||
|
||||
err_code_t init(const wasm_mozjs_startup_options_t* opt, wasm_mozjs_error_t* err);
|
||||
err_code_t shutdown(wasm_mozjs_error_t* err);
|
||||
err_code_t interrupt(wasm_mozjs_error_t* err);
|
||||
err_code_t createFunction(const uint8_t* src,
|
||||
size_t len,
|
||||
uint64_t* out_handle,
|
||||
wasm_mozjs_error_t* err);
|
||||
err_code_t invokeFunction(uint64_t handle,
|
||||
mongo::BSONObj&& bsonArgs,
|
||||
mongo::BSONObj* outBson,
|
||||
wasm_mozjs_error_t* err);
|
||||
|
||||
/// Invoke a predicate: document becomes `this` e.g `this.someVar`, returns bool.
|
||||
err_code_t invokePredicate(uint64_t handle,
|
||||
mongo::BSONObj&& document,
|
||||
bool* outResult,
|
||||
wasm_mozjs_error_t* err);
|
||||
|
||||
/// Invoke a map function: document becomes `this`, emits buffered.
|
||||
err_code_t invokeMap(uint64_t handle, mongo::BSONObj&& document, wasm_mozjs_error_t* err);
|
||||
err_code_t getReturnValueBson(mongo::BSONObj* out, wasm_mozjs_error_t* err);
|
||||
|
||||
/// Set a named global variable from a BSON-encoded value.
|
||||
err_code_t setGlobal(const char* name,
|
||||
size_t name_len,
|
||||
const mongo::BSONObj& value,
|
||||
wasm_mozjs_error_t* err);
|
||||
|
||||
/// Get a named global variable as BSON-encoded bytes.
|
||||
err_code_t getGlobal(const char* name,
|
||||
size_t name_len,
|
||||
mongo::BSONObj* out,
|
||||
wasm_mozjs_error_t* err);
|
||||
|
||||
/// Set a named global to a single BSON element's JS value directly.
|
||||
err_code_t setGlobalValue(const char* name,
|
||||
size_t name_len,
|
||||
const mongo::BSONObj& singleElementDoc,
|
||||
wasm_mozjs_error_t* err);
|
||||
|
||||
/// Set up the emit() built-in for mapReduce. Resets the emit buffer.
|
||||
err_code_t setupEmit(int64_t byteLimit, bool hasByteLimit, wasm_mozjs_error_t* err);
|
||||
|
||||
/// Drain the emit buffer: returns accumulated {k,v} pairs, then clears.
|
||||
err_code_t drainEmitBuffer(mongo::BSONObj* out, wasm_mozjs_error_t* err);
|
||||
|
||||
// MozJSCommonRuntimeInterface implementation
|
||||
void gc() override;
|
||||
void sleep(Milliseconds ms) override;
|
||||
std::size_t getGeneration() const override;
|
||||
JS::HandleId getInternedStringId(InternedString name) override;
|
||||
std::int64_t* trackedNewInt64(std::int64_t value) override;
|
||||
WrapType<NumberLongInfo>& numberLongProto() override;
|
||||
WrapType<NumberIntInfo>& numberIntProto() override;
|
||||
WrapType<NumberDecimalInfo>& numberDecimalProto() override;
|
||||
WrapType<OIDInfo>& oidProto() override;
|
||||
WrapType<BinDataInfo>& binDataProto() override;
|
||||
WrapType<TimestampInfo>& timestampProto() override;
|
||||
WrapType<MaxKeyInfo>& maxKeyProto() override;
|
||||
WrapType<MinKeyInfo>& minKeyProto() override;
|
||||
WrapType<CodeInfo>& codeProto() override;
|
||||
WrapType<DBPointerInfo>& dbPointerProto() override;
|
||||
WrapType<NativeFunctionInfo>& nativeFunctionProto() override;
|
||||
WrapType<ErrorInfo>& errorProto() override;
|
||||
WrapType<MongoStatusInfo>& mongoStatusProto() override;
|
||||
WrapType<BSONInfo>& bsonProto() override;
|
||||
WrapType<DBRefInfo>& dbRefProto() override;
|
||||
WrapType<RegExpInfo>& regExpProto() override;
|
||||
|
||||
void setStatus(Status status) override;
|
||||
bool isJavaScriptProtectionEnabled() const override;
|
||||
void newFunction(StringData code, JS::MutableHandleValue out) override;
|
||||
bool requiresOwnedObjects() const override;
|
||||
void trackNewPointer(void* ptr) override;
|
||||
void trackDeletePointer(void* ptr) override;
|
||||
|
||||
private:
|
||||
FunctionSlot* resolveHandle(uint64_t handle, wasm_mozjs_error_t* err);
|
||||
static bool interruptCallback(JSContext* cx);
|
||||
|
||||
bool _initialized = false;
|
||||
|
||||
JSContext* _cx = nullptr;
|
||||
JSRuntime* _rt = nullptr;
|
||||
JS::PersistentRootedObject _global;
|
||||
|
||||
std::vector<FunctionSlot> _slots;
|
||||
std::unique_ptr<MozJSPrototypeInstaller> _prototypeInstaller;
|
||||
std::unique_ptr<InternedStringTable> _internedStrings;
|
||||
|
||||
Status _status = Status::OK();
|
||||
|
||||
std::vector<mongo::BSONObj> _emitBuffer;
|
||||
int64_t _emitBytesUsed = 0;
|
||||
int64_t _emitByteLimit = 16 * 1024 * 1024; // default 16 MB
|
||||
|
||||
static mongo::BSONObj _emitCallback(const mongo::BSONObj& args, void* data);
|
||||
};
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
3353
src/mongo/scripting/mozjs/wasm/engine/engine_test.cpp
Normal file
3353
src/mongo/scripting/mozjs/wasm/engine/engine_test.cpp
Normal file
File diff suppressed because it is too large
Load Diff
104
src/mongo/scripting/mozjs/wasm/engine/error.cpp
Normal file
104
src/mongo/scripting/mozjs/wasm/engine/error.cpp
Normal file
@ -0,0 +1,104 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "error.h"
|
||||
|
||||
#include "js/ErrorReport.h"
|
||||
#include "js/Exception.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
void ExecutionCheck::capture(err_code_t fallback) {
|
||||
if (!_out)
|
||||
return;
|
||||
|
||||
clear_error(_out);
|
||||
_out->code = fallback;
|
||||
|
||||
if (!JS_IsExceptionPending(_cx))
|
||||
return;
|
||||
|
||||
_out->code = SM_E_PENDING_EXCEPTION;
|
||||
|
||||
JS::ExceptionStack exnStack(_cx);
|
||||
if (!JS::StealPendingExceptionStack(_cx, &exnStack)) {
|
||||
JS_ClearPendingException(_cx);
|
||||
return;
|
||||
}
|
||||
|
||||
JS::ErrorReportBuilder report(_cx);
|
||||
if (!report.init(_cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
|
||||
JS_ClearPendingException(_cx);
|
||||
return;
|
||||
}
|
||||
|
||||
JSErrorReport* r = report.report();
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
if (r->message())
|
||||
set_string(&_out->msg, &_out->msg_len, r->message().c_str());
|
||||
if (r->filename)
|
||||
set_string(&_out->filename, &_out->filename_len, r->filename.c_str());
|
||||
_out->line = r->lineno;
|
||||
_out->column = r->column.oneOriginValue();
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
|
||||
JSString* mongoErrorReportToString(JSContext* cx, JSErrorReport* reportp) {
|
||||
if (!reportp) {
|
||||
return JS_NewStringCopyZ(cx, "Unknown error");
|
||||
}
|
||||
|
||||
std::string msg;
|
||||
constexpr size_t kErrorMsgReserveSize = 256;
|
||||
msg.reserve(kErrorMsgReserveSize);
|
||||
if (reportp->message()) {
|
||||
msg.append(reportp->message().c_str());
|
||||
} else {
|
||||
msg.append("JavaScript error");
|
||||
}
|
||||
|
||||
if (reportp->filename) {
|
||||
msg.append(" at ");
|
||||
msg.append(reportp->filename.c_str());
|
||||
if (reportp->lineno > 0) {
|
||||
msg.push_back(':');
|
||||
msg.append(std::to_string(reportp->lineno));
|
||||
}
|
||||
}
|
||||
|
||||
return JS_NewStringCopyZ(cx, msg.c_str());
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
107
src/mongo/scripting/mozjs/wasm/engine/error.h
Normal file
107
src/mongo/scripting/mozjs/wasm/engine/error.h
Normal file
@ -0,0 +1,107 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/scripting/mozjs/shared/mozjs_error_types.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
inline void clear_error(wasm_mozjs_error_t* e) {
|
||||
if (!e)
|
||||
return;
|
||||
|
||||
if (e->msg) {
|
||||
cabi_realloc(e->msg, e->msg_len + 1, 1, 0);
|
||||
e->msg = nullptr;
|
||||
e->msg_len = 0;
|
||||
}
|
||||
if (e->filename) {
|
||||
cabi_realloc(e->filename, e->filename_len + 1, 1, 0);
|
||||
e->filename = nullptr;
|
||||
e->filename_len = 0;
|
||||
}
|
||||
if (e->stack) {
|
||||
cabi_realloc(e->stack, e->stack_len + 1, 1, 0);
|
||||
e->stack = nullptr;
|
||||
e->stack_len = 0;
|
||||
}
|
||||
|
||||
e->code = SM_OK;
|
||||
e->line = 0;
|
||||
e->column = 0;
|
||||
}
|
||||
|
||||
// Execution check helper for error handling
|
||||
class ExecutionCheck {
|
||||
public:
|
||||
ExecutionCheck(JSContext* cx, wasm_mozjs_error_t* out_err) : _cx(cx), _out(out_err) {
|
||||
clear_error(out_err);
|
||||
}
|
||||
|
||||
bool ok(bool success, err_code_t onFail = SM_E_JSAPI_FAIL) {
|
||||
if (success)
|
||||
return true;
|
||||
capture(onFail);
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* okPtr(T* p, err_code_t onFail = SM_E_JSAPI_FAIL) {
|
||||
if (p)
|
||||
return p;
|
||||
capture(onFail);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
void capture(err_code_t fallback);
|
||||
|
||||
JSContext* _cx;
|
||||
wasm_mozjs_error_t* _out;
|
||||
};
|
||||
|
||||
|
||||
} // namespace wasm
|
||||
|
||||
// WASI-compatible error report to string (declaration).
|
||||
// Implementation in error.cpp.
|
||||
JSString* mongoErrorReportToString(JSContext* cx, JSErrorReport* reportp);
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
146
src/mongo/scripting/mozjs/wasm/engine/exception_stubs.cpp
Normal file
146
src/mongo/scripting/mozjs/wasm/engine/exception_stubs.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
|
||||
// Boost source_location stub
|
||||
namespace boost {
|
||||
struct source_location {
|
||||
const char* file_name() const {
|
||||
return "";
|
||||
}
|
||||
const char* function_name() const {
|
||||
return "";
|
||||
}
|
||||
unsigned int line() const {
|
||||
return 0;
|
||||
}
|
||||
unsigned int column() const {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
} // namespace boost
|
||||
|
||||
extern "C" {
|
||||
|
||||
// Exception allocation
|
||||
void* __cxa_allocate_exception(size_t thrown_size) {
|
||||
(void)thrown_size;
|
||||
std::fprintf(stderr,
|
||||
"FATAL: __cxa_allocate_exception called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void __cxa_free_exception(void* thrown_exception) {
|
||||
(void)thrown_exception;
|
||||
std::fprintf(stderr, "FATAL: __cxa_free_exception called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
// Throwing
|
||||
void __cxa_throw(void* thrown_exception, void* tinfo, void (*dest)(void*)) {
|
||||
(void)thrown_exception;
|
||||
(void)tinfo;
|
||||
(void)dest;
|
||||
std::fprintf(stderr, "FATAL: __cxa_throw called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
void __cxa_rethrow(void) {
|
||||
std::fprintf(stderr, "FATAL: __cxa_rethrow called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
// Catching
|
||||
void* __cxa_begin_catch(void* exception_object) {
|
||||
(void)exception_object;
|
||||
std::fprintf(stderr, "FATAL: __cxa_begin_catch called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void __cxa_end_catch(void) {
|
||||
std::fprintf(stderr, "FATAL: __cxa_end_catch called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
// WASM-specific exception handling
|
||||
struct __wasm_lpad_context_t {
|
||||
int selector;
|
||||
void* exception;
|
||||
};
|
||||
struct __wasm_lpad_context_t __wasm_lpad_context = {0, nullptr};
|
||||
|
||||
int _Unwind_CallPersonality(void* exception_object) {
|
||||
(void)exception_object;
|
||||
std::fprintf(stderr,
|
||||
"FATAL: _Unwind_CallPersonality called - exceptions not supported in WASM\n");
|
||||
__builtin_trap();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Additional exception-related functions
|
||||
void* __cxa_get_exception_ptr(void* exception_object) {
|
||||
(void)exception_object;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void __cxa_pure_virtual(void) {
|
||||
std::fprintf(stderr, "FATAL: pure virtual function called\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
void __cxa_deleted_virtual(void) {
|
||||
std::fprintf(stderr, "FATAL: deleted virtual function called\n");
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
} // extern "C"
|
||||
|
||||
// Boost exception stubs
|
||||
namespace boost {
|
||||
|
||||
void throw_exception(std::exception const& e) {
|
||||
std::fprintf(stderr, "FATAL: boost::throw_exception called: %s\n", e.what());
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
void throw_exception(std::exception const& e, boost::source_location const& loc) {
|
||||
std::fprintf(stderr,
|
||||
"FATAL: boost::throw_exception called at %s:%u: %s\n",
|
||||
loc.file_name(),
|
||||
loc.line(),
|
||||
e.what());
|
||||
__builtin_trap();
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
111
src/mongo/scripting/mozjs/wasm/engine/linkset.bzl
Normal file
111
src/mongo/scripting/mozjs/wasm/engine/linkset.bzl
Normal file
@ -0,0 +1,111 @@
|
||||
"""Expose C++ link information (objects/libs/flags) via response files for genrules."""
|
||||
|
||||
def _as_list(x):
|
||||
if x == None:
|
||||
return []
|
||||
t = type(x)
|
||||
if t == "depset":
|
||||
return x.to_list()
|
||||
if t == "list" or t == "tuple":
|
||||
return x
|
||||
|
||||
# File or string or other singletons
|
||||
return [x]
|
||||
|
||||
def _collect_files_from_library(lib):
|
||||
"""Return (objects, libs) from a LibraryToLink in a Bazel-version-tolerant way."""
|
||||
objs = []
|
||||
|
||||
# Object files (may be depsets)
|
||||
if hasattr(lib, "objects"):
|
||||
objs += _as_list(lib.objects)
|
||||
if hasattr(lib, "pic_objects"):
|
||||
objs += _as_list(lib.pic_objects)
|
||||
|
||||
libs = []
|
||||
|
||||
# Prefer static archives; include others if present
|
||||
for attr in (
|
||||
"static_library",
|
||||
"pic_static_library",
|
||||
"dynamic_library",
|
||||
"interface_library",
|
||||
):
|
||||
if hasattr(lib, attr):
|
||||
val = getattr(lib, attr)
|
||||
libs += _as_list(val)
|
||||
return objs, libs
|
||||
|
||||
def _cc_linkset_impl(ctx):
|
||||
linking_ctxs = [d[CcInfo].linking_context for d in ctx.attr.deps]
|
||||
merged = cc_common.merge_linking_contexts(linking_contexts = linking_ctxs)
|
||||
|
||||
objs_ordered = []
|
||||
libs_ordered = []
|
||||
flags_ordered = []
|
||||
|
||||
# Iterate in Bazel's link order
|
||||
linker_inputs_list = merged.linker_inputs.to_list()
|
||||
|
||||
# Collect from all deps - include both direct and transitive
|
||||
for li in linker_inputs_list:
|
||||
if hasattr(li, "user_link_flags"):
|
||||
flags_ordered += list(li.user_link_flags)
|
||||
for lib in li.libraries:
|
||||
o, libs = _collect_files_from_library(lib)
|
||||
|
||||
# Collect all objects and libraries
|
||||
objs_ordered += o
|
||||
libs_ordered += libs
|
||||
|
||||
noncode = []
|
||||
if hasattr(merged, "non_code_inputs"):
|
||||
noncode = merged.non_code_inputs.to_list()
|
||||
|
||||
# Emit response files
|
||||
objs_rsp = ctx.actions.declare_file(ctx.label.name + ".objects.rsp")
|
||||
libs_rsp = ctx.actions.declare_file(ctx.label.name + ".libs.rsp")
|
||||
flags_rsp = ctx.actions.declare_file(ctx.label.name + ".flags.rsp")
|
||||
|
||||
def _write_list(out, items):
|
||||
lines = []
|
||||
for it in items:
|
||||
if hasattr(it, "path"):
|
||||
lines.append(it.path)
|
||||
else:
|
||||
lines.append(str(it))
|
||||
content = "\n".join(lines)
|
||||
if content:
|
||||
content += "\n"
|
||||
ctx.actions.write(out, content)
|
||||
|
||||
_write_list(objs_rsp, objs_ordered)
|
||||
_write_list(libs_rsp, libs_ordered + noncode)
|
||||
_write_list(flags_rsp, flags_ordered + ctx.attr.extra_flags)
|
||||
|
||||
# Expose everything so genrules can use $(locations :target)
|
||||
all_files = depset(
|
||||
direct = [objs_rsp, libs_rsp, flags_rsp] + objs_ordered + libs_ordered + noncode,
|
||||
)
|
||||
|
||||
return [
|
||||
DefaultInfo(files = all_files),
|
||||
OutputGroupInfo(
|
||||
rsp = depset([objs_rsp, libs_rsp, flags_rsp]),
|
||||
objects = depset(objs_ordered),
|
||||
libs = depset(libs_ordered),
|
||||
),
|
||||
]
|
||||
|
||||
cc_linkset = rule(
|
||||
implementation = _cc_linkset_impl,
|
||||
attrs = {
|
||||
"deps": attr.label_list(providers = [CcInfo]),
|
||||
"extra_flags": attr.string_list(default = []),
|
||||
"_cc_toolchain": attr.label(
|
||||
default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
|
||||
),
|
||||
},
|
||||
toolchains = ["@bazel_tools//tools/cpp:toolchain_type"],
|
||||
fragments = ["cpp"],
|
||||
)
|
||||
89
src/mongo/scripting/mozjs/wasm/engine/utils.h
Normal file
89
src/mongo/scripting/mozjs/wasm/engine/utils.h
Normal file
@ -0,0 +1,89 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
extern "C" void* cabi_realloc(void* ptr, size_t old_size, size_t align, size_t new_size);
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
namespace wasm {
|
||||
|
||||
/**
|
||||
* Set a string field, allocating memory via cabi_realloc.
|
||||
* If the field already has a value, it will be freed first.
|
||||
*/
|
||||
inline void set_string(char** dst, size_t* dst_len, const char* src) {
|
||||
if (!dst || !dst_len)
|
||||
return;
|
||||
|
||||
// Free existing string
|
||||
if (*dst) {
|
||||
cabi_realloc(*dst, *dst_len + 1, 1, 0);
|
||||
*dst = nullptr;
|
||||
*dst_len = 0;
|
||||
}
|
||||
|
||||
if (!src || src[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t src_len = std::strlen(src);
|
||||
|
||||
char* allocated = static_cast<char*>(cabi_realloc(nullptr, 0, 1, src_len + 1));
|
||||
if (!allocated) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::memcpy(allocated, src, src_len);
|
||||
allocated[src_len] = '\0';
|
||||
|
||||
*dst = allocated;
|
||||
*dst_len = src_len;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a C string into a fixed-size output buffer (always null-terminates).
|
||||
*/
|
||||
inline void set_cstr(char* dst, size_t cap, const char* src) {
|
||||
if (!dst || cap == 0)
|
||||
return;
|
||||
if (!src) {
|
||||
dst[0] = '\0';
|
||||
return;
|
||||
}
|
||||
std::snprintf(dst, cap, "%s", src);
|
||||
}
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
44
src/mongo/scripting/mozjs/wasm/helpers.cpp
Normal file
44
src/mongo/scripting/mozjs/wasm/helpers.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
|
||||
#include "gc/GCContext.h"
|
||||
#include "vm/JSContext.h"
|
||||
#include "vm/Runtime.h"
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
|
||||
JSContext* freeOpToJSContext(JS::GCContext* gcCtx) {
|
||||
return gcCtx->runtime()->mainContextFromOwnThread();
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
126
src/mongo/scripting/mozjs/wasm/scripts/README.md
Normal file
126
src/mongo/scripting/mozjs/wasm/scripts/README.md
Normal file
@ -0,0 +1,126 @@
|
||||
# WASM Build Scripts
|
||||
|
||||
These scripts implement the WASM build steps that Bazel runs via genrules. You can run them **from Bazel** (as usual) or **standalone** for debugging or CI. All behaviour is controlled by environment variables; each script's header lists every variable and a short example.
|
||||
|
||||
---
|
||||
|
||||
## Run order
|
||||
|
||||
To build **mozjs_wasm_api.wasm** (the main WASM API module) outside Bazel, run scripts in this order:
|
||||
|
||||
| Step | Script | Produces |
|
||||
| ---- | ------------------------------ | ------------------------------------- |
|
||||
| 1 | `build_spidermonkey_wasip2.sh` | SpiderMonkey tarball (libs + headers) |
|
||||
| 2 | `extract_rust_shims.sh` | `rust_shims.a` (from that tarball) |
|
||||
| 3 | `compile_mozjs_wasm_api.sh` | `mozjs_wasm_api.wasm` |
|
||||
|
||||
Step 3 needs the tarball, `rust_shims.a`, the MongoDB base **linkset** response files (`.libs.rsp` etc.), and all MozJS/WIT sources. The linkset files normally come from building `//src/mongo/scripting/mozjs/wasm:mongo_base_linkset` in Bazel; for a fully standalone build you'd need to copy those RSP files out of the Bazel output tree.
|
||||
|
||||
**Optional:** `link_mongo_base_wasm.sh` builds **mongo_base.wasm** (MongoDB base as a single WASM). It only needs the same linkset RSP files and the WASI SDK; it does not depend on SpiderMonkey. Run it whenever you have the linkset and want `mongo_base.wasm`.
|
||||
|
||||
**Helper (not run directly):** `compile_wasi_source.sh` — compiles one C/C++ file at a time; called by `compile_mozjs_wasm_api.sh` via `xargs`.
|
||||
|
||||
---
|
||||
|
||||
## Invocation examples
|
||||
|
||||
Assume you are in the **MongoDB repo root** and have a WASI SDK at `$WASI_SDK` (e.g. `/opt/wasi-sdk` or `$HOME/wasi-sdk-24.0`). Paths below are relative to the repo root unless noted.
|
||||
|
||||
### 1. Build SpiderMonkey (WASI Preview 2)
|
||||
|
||||
Produces a tarball with `libjs_static.a`, headers, and Rust shims. Requires SpiderMonkey source (e.g. from Bazel's external repo or a local gecko checkout) and the Rust shim sources under `support/rust_shims/`.
|
||||
|
||||
```bash
|
||||
cd src/mongo/scripting/mozjs/wasm
|
||||
|
||||
export WASI_SDK_PATH=/opt/wasi-sdk
|
||||
export SPIDER_MACH_PATH=/path/to/gecko-dev/mach # or path from Bazel execroot to mach
|
||||
export RUST_SHIMS_LIB_RS=support/rust_shims/src/lib.rs
|
||||
export CARGO_TEMPLATE_PATH=support/rust_shims/Cargo.toml.template
|
||||
export OUTPUT=out/spidermonkey-wasip2-release.tar.gz
|
||||
|
||||
mkdir -p out
|
||||
bash scripts/build_spidermonkey_wasip2.sh
|
||||
```
|
||||
|
||||
### 2. Extract Rust shims from the tarball
|
||||
|
||||
```bash
|
||||
cd src/mongo/scripting/mozjs/wasm
|
||||
|
||||
export TARBALL=out/spidermonkey-wasip2-release.tar.gz
|
||||
export OUTPUT=out/rust_shims.a
|
||||
|
||||
bash scripts/extract_rust_shims.sh
|
||||
```
|
||||
|
||||
### 3. Build mongo_base.wasm (optional)
|
||||
|
||||
You need the three linkset response files (e.g. from a Bazel build of `:mongo_base_linkset`). If they live in `bazel-bin/.../mongo_base_linkset/` as `*.objects.rsp`, `*.libs.rsp`, `*.flags.rsp`:
|
||||
|
||||
```bash
|
||||
cd src/mongo/scripting/mozjs/wasm
|
||||
|
||||
export CXX=/opt/wasi-sdk/bin/wasm32-wasi-clang++
|
||||
export OBJS_RSP=/path/to/xxx.objects.rsp
|
||||
export LIBS_RSP=/path/to/xxx.libs.rsp
|
||||
export FLAGS_RSP=/path/to/xxx.flags.rsp
|
||||
export OUTPUT=out/mongo_base.wasm
|
||||
|
||||
bash scripts/link_mongo_base_wasm.sh
|
||||
```
|
||||
|
||||
Or pass the directory and let the script find the RSPs:
|
||||
|
||||
```bash
|
||||
export LINKSET_FILES="/path/to/foo.objects.rsp /path/to/foo.libs.rsp /path/to/foo.flags.rsp"
|
||||
export OUTPUT=out/mongo_base.wasm
|
||||
bash scripts/link_mongo_base_wasm.sh
|
||||
```
|
||||
|
||||
### 4. Build mozjs_wasm_api.wasm
|
||||
|
||||
Requires the SpiderMonkey tarball, `rust_shims.a`, the linkset files, all MozJS/WIT sources, and the WIT component-type object. Easiest is to run from the package dir and point at Bazel's outputs for linkset and WIT object; fill in the source lists from the genrule in `BUILD.bazel` if needed.
|
||||
|
||||
```bash
|
||||
cd src/mongo/scripting/mozjs/wasm
|
||||
|
||||
export WASI_SDK_PATH=/opt/wasi-sdk
|
||||
export CXX=$WASI_SDK_PATH/bin/wasm32-wasip2-clang++
|
||||
export CC=$WASI_SDK_PATH/bin/wasm32-wasip2-clang
|
||||
|
||||
export SM_TARBALL=out/spidermonkey-wasip2-release.tar.gz
|
||||
export RUST_SHIMS_PATH=out/rust_shims.a
|
||||
export WIT_COMPONENT_TYPE_OBJ=wit_gen/generated/api_component_type.o # or path in bazel-bin
|
||||
|
||||
# Paths to linkset RSP files (e.g. from bazel build of :mongo_base_linkset)
|
||||
export LINKSET_FILES="/path/to/xxx.objects.rsp /path/to/xxx.libs.rsp /path/to/xxx.flags.rsp"
|
||||
|
||||
# Header parents for error_codes and mongo config (e.g. bazel-bin dirs)
|
||||
export ERROR_CODES_HEADER_FILES=/path/to/error_codes
|
||||
export CONFIG_HEADER_FILES=/path/to/mongo_config
|
||||
|
||||
# Source file lists (space-separated); see BUILD.bazel genrule "mozjs_wasm_api_genrule" for full list
|
||||
export WASM_SOURCES="engine/engine.cpp engine/error.cpp helpers.cpp ..."
|
||||
export COMMON_WASI_SOURCES="/path/to/common/wasi_sources..."
|
||||
export EXCEPTION_STUBS_SRC=engine/exception_stubs.cpp
|
||||
export MOZJS_API_SRC=engine/api.cpp
|
||||
export WIT_API_C=wit_gen/generated/api.c
|
||||
|
||||
export OUTPUT=out/mozjs_wasm_api.wasm
|
||||
bash scripts/compile_mozjs_wasm_api.sh
|
||||
```
|
||||
|
||||
For the exact source lists and paths, run the corresponding genrule once and inspect the `cmd` in `BUILD.bazel`, or run the build via Bazel and use the script only for repros.
|
||||
|
||||
---
|
||||
|
||||
## What each script does
|
||||
|
||||
| Script | Purpose |
|
||||
| -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **build_spidermonkey_wasip2.sh** | Builds SpiderMonkey for WASI Preview 2 (mozconfig + `mach build`), builds the Rust encoding shims, and packs static libs, headers, and extra objects into a tarball. |
|
||||
| **extract_rust_shims.sh** | Unpacks `libmongo_wasip2_rust_shims.a` from the SpiderMonkey tarball into a single `.a` file. |
|
||||
| **compile_mozjs_wasm_api.sh** | Unpacks the SM tarball, compiles all MozJS wrapper and WIT glue sources in parallel, gathers MongoDB base libs from the linkset, and links `mozjs_wasm_api.wasm`. |
|
||||
| **link_mongo_base_wasm.sh** | Links the MongoDB base library into a single `mongo_base.wasm` using the WASI linkset RSP files and `wasm32-wasi-clang++`. |
|
||||
| **compile_wasi_source.sh** | Compiles one C or C++ file for WASI. Used by `compile_mozjs_wasm_api.sh` via `xargs`; not intended to be run directly. |
|
||||
286
src/mongo/scripting/mozjs/wasm/scripts/build_spidermonkey_wasip2.sh
Executable file
286
src/mongo/scripting/mozjs/wasm/scripts/build_spidermonkey_wasip2.sh
Executable file
@ -0,0 +1,286 @@
|
||||
#!/bin/bash
|
||||
# Builds SpiderMonkey for WASI Preview 2, producing a tarball with:
|
||||
# - libjs_static.a (the JS engine)
|
||||
# - headers (public + internal, .h/.hpp only)
|
||||
# - libjsrust.a (if available)
|
||||
# - libmongo_wasip2_rust_shims.a (encoding shims)
|
||||
# - obj-extra/ (support objects outside libjs_static.a)
|
||||
#
|
||||
# Required environment variables:
|
||||
# OUTPUT - Output tarball path (.tar.gz)
|
||||
# SPIDER_MACH_PATH - Path to SpiderMonkey's mach file
|
||||
# RUST_SHIMS_LIB_RS - Path to support/rust_shims/src/lib.rs
|
||||
# CARGO_TEMPLATE_PATH - Path to support/rust_shims/Cargo.toml.template
|
||||
#
|
||||
# Compiler discovery (set one of):
|
||||
# WASI_SDK_BIN_FILES - Space-separated wasi_sdk bin files (auto-discovers clang)
|
||||
# WASI_SDK_PATH - Direct path to WASI SDK root
|
||||
#
|
||||
# Example standalone usage:
|
||||
# SPIDER_MACH_PATH=/path/to/gecko-dev/mach \
|
||||
# RUST_SHIMS_LIB_RS=support/rust_shims/src/lib.rs \
|
||||
# CARGO_TEMPLATE_PATH=support/rust_shims/Cargo.toml.template \
|
||||
# WASI_SDK_PATH=/opt/wasi-sdk \
|
||||
# OUTPUT=spidermonkey-wasip2-release.tar.gz \
|
||||
# bash scripts/build_spidermonkey_wasip2.sh
|
||||
|
||||
set -euo pipefail
|
||||
# Enable verbose tracing only when VERBOSE is set (reduces CI log noise).
|
||||
[[ -n "${VERBOSE:-}" ]] && set -x
|
||||
|
||||
EXECROOT="${EXECROOT:-$(pwd -P)}"
|
||||
|
||||
# Resolve output to absolute path.
|
||||
case "$OUTPUT" in
|
||||
/*) OUT_TAR_ABS="$OUTPUT" ;;
|
||||
*) OUT_TAR_ABS="$EXECROOT/$OUTPUT" ;;
|
||||
esac
|
||||
OUT_DIR_ABS="$(dirname "$OUT_TAR_ABS")"
|
||||
|
||||
PKG_DIR="$OUT_DIR_ABS/pkg"
|
||||
WORK_DIR="$OUT_DIR_ABS/work"
|
||||
rm -rf "$PKG_DIR"
|
||||
mkdir -p "$PKG_DIR/lib" "$WORK_DIR"
|
||||
|
||||
# Keep a real HOME so rustup doesn't trip over Bazel's sandbox home.
|
||||
REAL_HOME="$(getent passwd "$(id -u)" | cut -d: -f6 || true)"
|
||||
if [ -z "$REAL_HOME" ]; then
|
||||
if [ -n "${HOME:-}" ]; then
|
||||
REAL_HOME="$HOME"
|
||||
else
|
||||
echo "ERROR: Could not determine HOME directory; set HOME or ensure getent is available." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
export HOME="$REAL_HOME"
|
||||
export MOZ_FETCHES_DIR="$HOME/.mozbuild"
|
||||
|
||||
# --- Locate WASI SDK ---
|
||||
if [ -z "${WASI_SDK_PATH:-}" ]; then
|
||||
WASI_CLANG=""
|
||||
for f in ${WASI_SDK_BIN_FILES:-}; do
|
||||
case "$f" in */clang)
|
||||
WASI_CLANG="$f"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
if [ -z "$WASI_CLANG" ]; then
|
||||
echo "ERROR: clang not found. Set WASI_SDK_PATH or WASI_SDK_BIN_FILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
WASI_SDK_PATH="$(cd "$(dirname "$WASI_CLANG")/.." && pwd)"
|
||||
fi
|
||||
|
||||
# --- Copy SpiderMonkey source into a writable directory ---
|
||||
SPIDER_ROOT="$(cd "$(dirname "$SPIDER_MACH_PATH")" && pwd)"
|
||||
SRC_ROOT="$WORK_DIR/spidermonkey-src"
|
||||
rm -rf "$SRC_ROOT"
|
||||
mkdir -p "$SRC_ROOT"
|
||||
cp -a "$SPIDER_ROOT/." "$SRC_ROOT/"
|
||||
SRC_ROOT_REAL="$(cd "$SRC_ROOT" && pwd -P)"
|
||||
|
||||
MOZCONFIG="$SRC_ROOT_REAL/mozconfig-release"
|
||||
OBJDIR="$WORK_DIR/obj-release-wasip2"
|
||||
mkdir -p "$OBJDIR"
|
||||
OBJDIR_REAL="$(cd "$OBJDIR" && pwd -P)"
|
||||
|
||||
cat >"$MOZCONFIG" <<'EOF'
|
||||
ac_add_options --enable-project=js
|
||||
ac_add_options --disable-js-shell
|
||||
ac_add_options --target=wasm32-unknown-wasip2
|
||||
ac_add_options --without-system-zlib
|
||||
ac_add_options --without-intl-api
|
||||
ac_add_options --disable-jit
|
||||
ac_add_options --disable-shared-js
|
||||
ac_add_options --disable-shared-memory
|
||||
ac_add_options --disable-tests
|
||||
ac_add_options --disable-clang-plugin
|
||||
ac_add_options --enable-optimize=-Oz
|
||||
ac_add_options --enable-portable-baseline-interp
|
||||
mk_add_options AUTOCLOBBER=1
|
||||
EOF
|
||||
|
||||
echo "mk_add_options MOZ_OBJDIR=$OBJDIR_REAL" >>"$MOZCONFIG"
|
||||
echo "ac_add_options --prefix=$OBJDIR_REAL/dist" >>"$MOZCONFIG"
|
||||
echo "ac_add_options --disable-stdcxx-compat" >>"$MOZCONFIG"
|
||||
echo "ac_add_options --disable-debug" >>"$MOZCONFIG"
|
||||
echo "ac_add_options --disable-debug-symbols" >>"$MOZCONFIG"
|
||||
echo "ac_add_options --with-sysroot=$WASI_SDK_PATH/share/wasi-sysroot" >>"$MOZCONFIG"
|
||||
|
||||
# Use WASI Preview2 compiler wrappers.
|
||||
export WASI_SDK_PATH="$WASI_SDK_PATH"
|
||||
export CC="$WASI_SDK_PATH/bin/wasm32-wasip2-clang"
|
||||
export CXX="$WASI_SDK_PATH/bin/wasm32-wasip2-clang++"
|
||||
export AR="$WASI_SDK_PATH/bin/llvm-ar"
|
||||
export RANLIB="$WASI_SDK_PATH/bin/llvm-ranlib"
|
||||
export HOST_CC="$(command -v clang)"
|
||||
export HOST_CXX="$(command -v clang++)"
|
||||
|
||||
# Enable per-function/data sections so the linker's --gc-sections can remove
|
||||
# unused code. Also hide internal symbols to aid dead-code elimination.
|
||||
WASM_SIZE_FLAGS="-ffunction-sections -fdata-sections -fvisibility=hidden"
|
||||
export CFLAGS="${CFLAGS:-} $WASM_SIZE_FLAGS"
|
||||
export CXXFLAGS="${CXXFLAGS:-} $WASM_SIZE_FLAGS"
|
||||
|
||||
# Let cargo/rustc know what target to build for when SpiderMonkey builds Rust support code.
|
||||
export RUST_TARGET=wasm32-wasip2
|
||||
|
||||
# Configure expects a Rust stdlib for wasm32-wasip2 to be installed.
|
||||
# This is a local (non-hermetic) build step by design (tags = local/no-sandbox).
|
||||
if [ -x "$HOME/.cargo/bin/rustup" ]; then
|
||||
"$HOME/.cargo/bin/rustup" target add wasm32-wasip2 || true
|
||||
elif command -v rustup >/dev/null 2>&1; then
|
||||
rustup target add wasm32-wasip2 || true
|
||||
fi
|
||||
|
||||
export MOZCONFIG="$MOZCONFIG"
|
||||
cd "$SRC_ROOT_REAL"
|
||||
python3 "./mach" -v --no-interactive build
|
||||
|
||||
# Build Rust support library if present/required (best-effort).
|
||||
python3 "./mach" -v --no-interactive build js/src/rust || true
|
||||
|
||||
LIBJS_A="$OBJDIR_REAL/js/src/build/libjs_static.a"
|
||||
test -s "$LIBJS_A"
|
||||
cp "$LIBJS_A" "$PKG_DIR/lib/libjs_static.a"
|
||||
|
||||
# Headers for embedders (needed to compile our wrapper).
|
||||
if [ -d "$OBJDIR_REAL/dist/include" ]; then
|
||||
cp -Lr "$OBJDIR_REAL/dist/include" "$PKG_DIR/include"
|
||||
# Generated defines header is used by some includes.
|
||||
if [ -f "$OBJDIR_REAL/js/src/js-confdefs.h" ]; then
|
||||
cp "$OBJDIR_REAL/js/src/js-confdefs.h" "$PKG_DIR/include/js-confdefs.h"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Internal SpiderMonkey headers (js/src) for gc/GCContext.h, vm/Runtime.h etc.
|
||||
# Copy source headers first, then overlay generated headers from objdir.
|
||||
if [ -d "$SRC_ROOT_REAL/js/src" ]; then
|
||||
echo "Copying js/src internal headers (*.h, *.hpp only) from source..." >&2
|
||||
mkdir -p "$PKG_DIR/include/src"
|
||||
# Copy only header files -- not .cpp, tests, or build configs which bloat
|
||||
# the tarball by hundreds of megabytes.
|
||||
(cd "$SRC_ROOT_REAL/js/src" && find . \( -name '*.h' -o -name '*.hpp' \) -type f) | while IFS= read -r hdr; do
|
||||
mkdir -p "$PKG_DIR/include/src/$(dirname "$hdr")"
|
||||
cp "$SRC_ROOT_REAL/js/src/$hdr" "$PKG_DIR/include/src/$hdr"
|
||||
done
|
||||
fi
|
||||
if [ -d "$SRC_ROOT_REAL/mfbt" ]; then
|
||||
echo "Copying mfbt headers (*.h, *.hpp only) from source..." >&2
|
||||
mkdir -p "$PKG_DIR/include/mfbt"
|
||||
(cd "$SRC_ROOT_REAL/mfbt" && find . \( -name '*.h' -o -name '*.hpp' \) -type f) | while IFS= read -r hdr; do
|
||||
mkdir -p "$PKG_DIR/include/mfbt/$(dirname "$hdr")"
|
||||
cp "$SRC_ROOT_REAL/mfbt/$hdr" "$PKG_DIR/include/mfbt/$hdr"
|
||||
done
|
||||
fi
|
||||
# Overlay generated .h files from objdir (e.g., wasm/WasmBuiltinModuleGenerated.h)
|
||||
if [ -d "$OBJDIR_REAL/js/src" ]; then
|
||||
echo "Overlaying generated headers from objdir..." >&2
|
||||
find "$OBJDIR_REAL/js/src" -name "*.h" -type f 2>/dev/null | while read f; do
|
||||
rel="${f#$OBJDIR_REAL/js/src/}"
|
||||
mkdir -p "$PKG_DIR/include/src/$(dirname "$rel")"
|
||||
cp "$f" "$PKG_DIR/include/src/$rel" 2>/dev/null || true
|
||||
done
|
||||
fi
|
||||
|
||||
# If a Rust static lib exists, include it (may satisfy encoding_* symbols).
|
||||
# Name can vary by toolchain/version, so accept libjsrust*.a.
|
||||
RUST_LIB="$(find "$OBJDIR_REAL" -type f -name 'libjsrust*.a' | head -n1 || true)"
|
||||
if [ -n "$RUST_LIB" ] && [ -f "$RUST_LIB" ]; then
|
||||
cp "$RUST_LIB" "$PKG_DIR/lib/libjsrust.a"
|
||||
fi
|
||||
|
||||
# Build a single Rust staticlib that bundles the encoding shims + Rust
|
||||
# runtime, so the final wasm can be fully linked without `env::...`
|
||||
# imports.
|
||||
CARGO="$(command -v cargo || true)"
|
||||
if [ -z "$CARGO" ] && [ -x "$HOME/.cargo/bin/cargo" ]; then
|
||||
CARGO="$HOME/.cargo/bin/cargo"
|
||||
fi
|
||||
if [ -z "$CARGO" ]; then
|
||||
echo "ERROR: cargo not found; cannot build Rust encoding staticlib" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RUST_SHIMS_DIR="$WORK_DIR/rust-shims"
|
||||
rm -rf "$RUST_SHIMS_DIR"
|
||||
mkdir -p "$RUST_SHIMS_DIR/src"
|
||||
|
||||
# Copy lib.rs from support files.
|
||||
# Path may be relative to EXECROOT or absolute.
|
||||
case "$RUST_SHIMS_LIB_RS" in
|
||||
/*) cp "$RUST_SHIMS_LIB_RS" "$RUST_SHIMS_DIR/src/lib.rs" ;;
|
||||
*) cp "$EXECROOT/$RUST_SHIMS_LIB_RS" "$RUST_SHIMS_DIR/src/lib.rs" ;;
|
||||
esac
|
||||
|
||||
# Generate Cargo.toml from template with SpiderMonkey root path.
|
||||
case "$CARGO_TEMPLATE_PATH" in
|
||||
/*) sed "s|{{SPIDERMONKEY_ROOT}}|$SRC_ROOT_REAL|g" "$CARGO_TEMPLATE_PATH" >"$RUST_SHIMS_DIR/Cargo.toml" ;;
|
||||
*) sed "s|{{SPIDERMONKEY_ROOT}}|$SRC_ROOT_REAL|g" "$EXECROOT/$CARGO_TEMPLATE_PATH" >"$RUST_SHIMS_DIR/Cargo.toml" ;;
|
||||
esac
|
||||
|
||||
export CARGO_TARGET_DIR="$OBJDIR_REAL/cargo-target"
|
||||
(cd "$RUST_SHIMS_DIR" && "$CARGO" build --release --target wasm32-wasip2)
|
||||
SHIMS_A="$CARGO_TARGET_DIR/wasm32-wasip2/release/libmongo_wasip2_rust_shims.a"
|
||||
if [ ! -f "$SHIMS_A" ]; then
|
||||
echo "ERROR: rust shims archive not found: $SHIMS_A" >&2
|
||||
exit 1
|
||||
fi
|
||||
cp "$SHIMS_A" "$PKG_DIR/lib/libmongo_wasip2_rust_shims.a"
|
||||
|
||||
# Include additional build outputs (outside libjs_static.a) needed to link.
|
||||
# The downstream linker uses libjs_static.a directly for SpiderMonkey objects,
|
||||
# so we do NOT extract individual .o files from the archive (which would
|
||||
# roughly double the tarball size). Only the "extra" objects that live outside
|
||||
# the archive are needed here.
|
||||
#
|
||||
# Paths are relative to MOZ_OBJDIR (OBJDIR_REAL).
|
||||
EXTRA_ROOT="$PKG_DIR/obj-extra"
|
||||
for rel in \
|
||||
memory/build/Unified_cpp_memory_build0.o \
|
||||
memory/mozalloc/Unified_cpp_memory_mozalloc0.o \
|
||||
mozglue/misc/AutoProfilerLabel.o \
|
||||
mozglue/misc/ConditionVariable_noop.o \
|
||||
mozglue/misc/MmapFaultHandler.o \
|
||||
mozglue/misc/Mutex_noop.o \
|
||||
mozglue/misc/Now.o \
|
||||
mozglue/misc/Printf.o \
|
||||
mozglue/misc/StackWalk.o \
|
||||
mozglue/misc/TimeStamp.o \
|
||||
mozglue/misc/TimeStamp_posix.o \
|
||||
mozglue/misc/Uptime.o \
|
||||
mozglue/misc/Decimal.o \
|
||||
mozglue/misc/SIMD.o \
|
||||
mfbt/lz4.o \
|
||||
mfbt/lz4frame.o \
|
||||
mfbt/lz4hc.o \
|
||||
mfbt/xxhash.o \
|
||||
mfbt/Unified_cpp_mfbt0.o \
|
||||
mfbt/Unified_cpp_mfbt1.o; do
|
||||
if [ -f "$OBJDIR_REAL/$rel" ]; then
|
||||
mkdir -p "$EXTRA_ROOT/$(dirname "$rel")"
|
||||
cp "$OBJDIR_REAL/$rel" "$EXTRA_ROOT/$rel"
|
||||
else
|
||||
echo "WARN: extra object missing (skipping): $OBJDIR_REAL/$rel" >&2
|
||||
fi
|
||||
done
|
||||
|
||||
# Strip debug info from archives and objects to further reduce tarball size.
|
||||
# Even with --disable-debug-symbols the toolchain may leave some sections.
|
||||
STRIP="${WASI_SDK_PATH}/bin/llvm-strip"
|
||||
if [ -x "$STRIP" ]; then
|
||||
echo "Stripping debug info from libraries and objects..." >&2
|
||||
for lib in "$PKG_DIR"/lib/*.a; do
|
||||
[ -f "$lib" ] && "$STRIP" --strip-debug "$lib" 2>/dev/null || true
|
||||
done
|
||||
find "$PKG_DIR/obj-extra" -name '*.o' -type f 2>/dev/null | while IFS= read -r obj; do
|
||||
"$STRIP" --strip-debug "$obj" 2>/dev/null || true
|
||||
done
|
||||
else
|
||||
echo "WARN: llvm-strip not found at $STRIP; skipping strip step" >&2
|
||||
fi
|
||||
|
||||
mkdir -p "$(dirname "$OUT_TAR_ABS")"
|
||||
tar -C "$PKG_DIR" -czf "$OUT_TAR_ABS" .
|
||||
test -s "$OUT_TAR_ABS"
|
||||
268
src/mongo/scripting/mozjs/wasm/scripts/compile_mozjs_wasm_api.sh
Executable file
268
src/mongo/scripting/mozjs/wasm/scripts/compile_mozjs_wasm_api.sh
Executable file
@ -0,0 +1,268 @@
|
||||
#!/bin/bash
|
||||
# Compiles and links the MozJS WASM API module (mozjs_wasm_api.wasm).
|
||||
#
|
||||
# This script unpacks a SpiderMonkey tarball, compiles MozJS wrapper sources
|
||||
# against it, collects MongoDB base libraries, and links everything into a
|
||||
# single WASI Preview 2 WASM component.
|
||||
#
|
||||
# Required environment variables:
|
||||
# OUTPUT - Output .wasm file path
|
||||
# SM_TARBALL - Path to spidermonkey-wasip2-release.tar.gz
|
||||
# RUST_SHIMS_PATH - Path to extracted rust_shims.a
|
||||
# WIT_COMPONENT_TYPE_OBJ - Path to WIT component type object file
|
||||
#
|
||||
# Source files (space-separated paths):
|
||||
# WASM_SOURCES - MozJS WASM wrapper sources
|
||||
# COMMON_WASI_SOURCES - Common WASI source files
|
||||
# EXCEPTION_STUBS_SRC - engine/exception_stubs.cpp
|
||||
# MOZJS_API_SRC - engine/api.cpp
|
||||
# WIT_API_C - WIT-generated api.c glue
|
||||
#
|
||||
# Header discovery:
|
||||
# ERROR_CODES_HEADER_FILES - Space-separated error_codes header file paths
|
||||
# CONFIG_HEADER_FILES - Space-separated mongo_config header file paths
|
||||
#
|
||||
# Linker inputs:
|
||||
# LINKSET_FILES - Space-separated linkset output files
|
||||
#
|
||||
# Compiler discovery (set one of):
|
||||
# CXX / CC - Direct paths to wasm32-wasip2-clang++ / clang
|
||||
# WASI_SDK_BIN_FILES - Space-separated wasi_sdk bin files (auto-discovers)
|
||||
#
|
||||
# Compile helper (optional, auto-discovered from script directory):
|
||||
# COMPILE_HELPER - Path to compile_wasi_source.sh
|
||||
#
|
||||
# Example standalone usage:
|
||||
# CXX=/opt/wasi-sdk/bin/wasm32-wasip2-clang++ \
|
||||
# CC=/opt/wasi-sdk/bin/wasm32-wasip2-clang \
|
||||
# SM_TARBALL=spidermonkey-wasip2-release.tar.gz \
|
||||
# RUST_SHIMS_PATH=rust_shims.a \
|
||||
# WASM_SOURCES="engine.cpp error.cpp helpers.cpp" \
|
||||
# WIT_COMPONENT_TYPE_OBJ=api_component_type.o \
|
||||
# OUTPUT=mozjs_wasm_api.wasm \
|
||||
# bash scripts/compile_mozjs_wasm_api.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
EXECROOT="${EXECROOT:-$(pwd -P)}"
|
||||
START_TIME=$(date +%s)
|
||||
|
||||
# ============================================================
|
||||
# STEP 1: Find compilers and setup
|
||||
# ============================================================
|
||||
echo "=== STEP 1: Finding WASI compilers ===" >&2
|
||||
if [ -z "${CXX:-}" ] || [ -z "${CC:-}" ]; then
|
||||
for f in ${WASI_SDK_BIN_FILES:-}; do
|
||||
case "$f" in
|
||||
*/wasm32-wasip2-clang++) CXX="$f" ;;
|
||||
*/wasm32-wasip2-clang) CC="$f" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
if [ -z "${CXX:-}" ]; then
|
||||
echo "ERROR: wasm32-wasip2-clang++ not found. Set CXX or WASI_SDK_BIN_FILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${CC:-}" ]; then
|
||||
echo "ERROR: wasm32-wasip2-clang not found. Set CC or WASI_SDK_BIN_FILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
WASI_SDK_ROOT="$(cd "$(dirname "$CXX")/.." && pwd -P)"
|
||||
SYSROOT="${SYSROOT:-$WASI_SDK_ROOT/share/wasi-sysroot}"
|
||||
echo "Using CXX: $CXX" >&2
|
||||
echo "Using CC: $CC" >&2
|
||||
|
||||
# ============================================================
|
||||
# STEP 2: Unpack SpiderMonkey
|
||||
# ============================================================
|
||||
echo "=== STEP 2: Unpacking SpiderMonkey ===" >&2
|
||||
OUT_DIR="$(dirname "$OUTPUT")"
|
||||
STAGE="$OUT_DIR/sm_stage"
|
||||
rm -rf "$STAGE"
|
||||
mkdir -p "$STAGE"
|
||||
tar -xzf "$SM_TARBALL" -C "$STAGE"
|
||||
|
||||
test -f "$STAGE/lib/libjs_static.a" || (
|
||||
echo "ERROR: libjs_static.a not found" >&2
|
||||
exit 1
|
||||
)
|
||||
test -d "$STAGE/include" || (
|
||||
echo "ERROR: include directory not found" >&2
|
||||
exit 1
|
||||
)
|
||||
|
||||
cp "$RUST_SHIMS_PATH" "$STAGE/lib/libmongo_wasip2_rust_shims.a"
|
||||
echo "SpiderMonkey unpacked to: $STAGE" >&2
|
||||
|
||||
# ============================================================
|
||||
# STEP 3: Setup compilation environment
|
||||
# ============================================================
|
||||
echo "=== STEP 3: Setting up compilation environment ===" >&2
|
||||
OBJ_DIR="$STAGE/objs"
|
||||
mkdir -p "$OBJ_DIR"
|
||||
|
||||
ERROR_CODES_H_PARENT=""
|
||||
CONFIG_H_PARENT=""
|
||||
for f in ${ERROR_CODES_HEADER_FILES:-}; do
|
||||
ERROR_CODES_H_PARENT="$(dirname "$(dirname "$f")")"
|
||||
break
|
||||
done
|
||||
for f in ${CONFIG_HEADER_FILES:-}; do
|
||||
CONFIG_H_PARENT="$(dirname "$(dirname "$f")")"
|
||||
break
|
||||
done
|
||||
|
||||
BOOST_INCLUDES="-I$EXECROOT/src/third_party/boost"
|
||||
[ -d "$EXECROOT/src/third_party/boost/libs/numeric/conversion/include" ] &&
|
||||
BOOST_INCLUDES="$BOOST_INCLUDES -I$EXECROOT/src/third_party/boost/libs/numeric/conversion/include"
|
||||
for lib_include in "$EXECROOT/src/third_party/boost/libs"/*/include; do
|
||||
[ -d "$lib_include" ] && BOOST_INCLUDES="$BOOST_INCLUDES -I$lib_include"
|
||||
done
|
||||
|
||||
# Auto-detect Bazel bin/src path for generated headers (e.g., error_codes.h).
|
||||
# This must work on any host architecture (aarch64, x86_64, etc.).
|
||||
BAZEL_BIN_SRC=""
|
||||
for d in "$EXECROOT"/bazel-out/*/bin/src; do
|
||||
if [ -d "$d" ]; then
|
||||
BAZEL_BIN_SRC="$d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# ============================================================
|
||||
# STEP 4: Compile MozJS library sources (including WIT glue)
|
||||
# ============================================================
|
||||
echo "=== STEP 4: Compiling MozJS library sources ===" >&2
|
||||
MOZJS_SRCS="${WASM_SOURCES:-}"
|
||||
MOZJS_SRCS="$MOZJS_SRCS ${COMMON_WASI_SOURCES:-}"
|
||||
MOZJS_SRCS="$MOZJS_SRCS ${EXCEPTION_STUBS_SRC:-}"
|
||||
MOZJS_SRCS="$MOZJS_SRCS ${MOZJS_API_SRC:-}"
|
||||
|
||||
# Add wit-bindgen generated glue (C) to compilation inputs.
|
||||
MOZJS_SRCS="$MOZJS_SRCS ${WIT_API_C:-}"
|
||||
|
||||
SRC_LIST="$STAGE/src_list.txt"
|
||||
>"$SRC_LIST"
|
||||
# Note: intentionally unquoted to split space-separated paths (Bazel paths never contain spaces).
|
||||
for src in $MOZJS_SRCS; do
|
||||
[ -n "$src" ] && echo "$src" >>"$SRC_LIST"
|
||||
done
|
||||
|
||||
# Locate the compile helper script.
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
COMPILE_HELPER="${COMPILE_HELPER:-$SCRIPT_DIR/compile_wasi_source.sh}"
|
||||
|
||||
NPROC=$(nproc 2>/dev/null || echo 8)
|
||||
echo "Compiling $(wc -l <"$SRC_LIST") files with $NPROC parallel jobs" >&2
|
||||
|
||||
OBJ_LIST="$STAGE/obj_list.txt"
|
||||
if ! cat "$SRC_LIST" | xargs -P "$NPROC" -I {} bash "$COMPILE_HELPER" {} \
|
||||
"$CXX" "$CC" "$SYSROOT" "$STAGE" "$OBJ_DIR" "$EXECROOT" \
|
||||
"$ERROR_CODES_H_PARENT" "$CONFIG_H_PARENT" "$BOOST_INCLUDES" \
|
||||
"$BAZEL_BIN_SRC" >"$OBJ_LIST" 2>&1; then
|
||||
echo "ERROR: Parallel compilation failed" >&2
|
||||
cat "$OBJ_LIST" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OBJ_FILES=""
|
||||
for obj in "$OBJ_DIR"/*.o; do
|
||||
[ -f "$obj" ] && OBJ_FILES="$OBJ_FILES $obj"
|
||||
done
|
||||
COMPILE_COUNT=$(ls -1 "$OBJ_DIR"/*.o 2>/dev/null | wc -l)
|
||||
echo "Compiled $COMPILE_COUNT files successfully" >&2
|
||||
|
||||
# ============================================================
|
||||
# STEP 5: Collect MongoDB base libraries
|
||||
# ============================================================
|
||||
echo "=== STEP 5: Collecting MongoDB base libraries ===" >&2
|
||||
|
||||
MONGO_BASE_LIBS=""
|
||||
MONGO_BASE_LIBS_RSP=""
|
||||
for f in ${LINKSET_FILES:-}; do
|
||||
case "$f" in
|
||||
*.libs.rsp) MONGO_BASE_LIBS_RSP="$f" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Use only the .a archives from libs.rsp (they contain all objects).
|
||||
# We skip objects.rsp to avoid duplicate symbols since the archives
|
||||
# contain the same objects that would appear there.
|
||||
if [ -n "$MONGO_BASE_LIBS_RSP" ] && [ -f "$MONGO_BASE_LIBS_RSP" ]; then
|
||||
LIB_COUNT=0
|
||||
while IFS= read -r lib_path || [ -n "$lib_path" ]; do
|
||||
[ -z "$lib_path" ] && continue
|
||||
case "$lib_path" in "#"*) continue ;; esac
|
||||
if [ ! "$lib_path" = "${lib_path#/}" ]; then
|
||||
LIB_ABS="$lib_path"
|
||||
else
|
||||
LIB_ABS="$EXECROOT/$lib_path"
|
||||
fi
|
||||
if [ -f "$LIB_ABS" ]; then
|
||||
MONGO_BASE_LIBS="$MONGO_BASE_LIBS $LIB_ABS"
|
||||
LIB_COUNT=$((LIB_COUNT + 1))
|
||||
fi
|
||||
done <"$MONGO_BASE_LIBS_RSP"
|
||||
echo "Found $LIB_COUNT libraries from libs.rsp" >&2
|
||||
fi
|
||||
|
||||
# ============================================================
|
||||
# STEP 6: Final linking (include wit component type object)
|
||||
# ============================================================
|
||||
echo "=== STEP 6: Linking final WASM binary ===" >&2
|
||||
EXTRA_OBJS="$(find "$STAGE/obj-extra" -type f -name '*.o' 2>/dev/null | tr '\n' ' ' || true)"
|
||||
JSRUST_LIB=""
|
||||
[ -f "$STAGE/lib/libjsrust.a" ] && JSRUST_LIB="$STAGE/lib/libjsrust.a"
|
||||
|
||||
echo "Linking with:" >&2
|
||||
echo " MozJS objects: $(echo $OBJ_FILES | wc -w)" >&2
|
||||
echo " Mongo base libs: $(echo $MONGO_BASE_LIBS | wc -w)" >&2
|
||||
|
||||
if ! "$CXX" --sysroot="$SYSROOT" -std=c++20 -Oz \
|
||||
-flto -fexceptions \
|
||||
$OBJ_FILES \
|
||||
"$WIT_COMPONENT_TYPE_OBJ" \
|
||||
"$STAGE/lib/libjs_static.a" \
|
||||
"$STAGE/lib/libmongo_wasip2_rust_shims.a" \
|
||||
$JSRUST_LIB \
|
||||
$EXTRA_OBJS \
|
||||
$MONGO_BASE_LIBS \
|
||||
-L"$SYSROOT/lib/wasm32-wasip2" \
|
||||
-lc++ -lc++abi \
|
||||
-lwasi-emulated-getpid \
|
||||
-lwasi-emulated-signal \
|
||||
-lwasi-emulated-mman \
|
||||
-lwasi-emulated-process-clocks \
|
||||
-mexec-model=reactor \
|
||||
-Wl,--export=cabi_realloc \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,--strip-all \
|
||||
-Wl,--no-entry \
|
||||
-o "$OUTPUT" 2>&1; then
|
||||
echo "ERROR: Linking failed" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
test -s "$OUTPUT"
|
||||
ls -lh "$OUTPUT"
|
||||
|
||||
# Post-link size optimization with wasm-opt.
|
||||
# wasm-opt can typically shrink a WASM binary by another 10-20% through
|
||||
# WASM-specific optimisations (dead code removal, constant folding, etc.).
|
||||
WASM_OPT="$(command -v wasm-opt 2>/dev/null || true)"
|
||||
if [ -z "$WASM_OPT" ] && [ -x "$WASI_SDK_ROOT/bin/wasm-opt" ]; then
|
||||
WASM_OPT="$WASI_SDK_ROOT/bin/wasm-opt"
|
||||
fi
|
||||
if [ -n "$WASM_OPT" ]; then
|
||||
echo "Running wasm-opt -Oz on $OUTPUT ..." >&2
|
||||
"$WASM_OPT" -Oz --enable-bulk-memory --enable-sign-ext \
|
||||
-o "$OUTPUT.opt" "$OUTPUT" 2>&1 && mv "$OUTPUT.opt" "$OUTPUT"
|
||||
ls -lh "$OUTPUT"
|
||||
else
|
||||
echo "NOTE: wasm-opt not found; skipping post-link optimisation" >&2
|
||||
fi
|
||||
|
||||
END_TIME=$(date +%s)
|
||||
ELAPSED=$((END_TIME - START_TIME))
|
||||
echo "=== SUCCESS: Built mozjs_wasm_api.wasm in ${ELAPSED}s ===" >&2
|
||||
96
src/mongo/scripting/mozjs/wasm/scripts/compile_wasi_source.sh
Executable file
96
src/mongo/scripting/mozjs/wasm/scripts/compile_wasi_source.sh
Executable file
@ -0,0 +1,96 @@
|
||||
#!/bin/bash
|
||||
# Compiles a single C/C++ source file for WASI.
|
||||
# Used as a helper by compile_mozjs_wasm_api.sh for parallel compilation via xargs.
|
||||
#
|
||||
# Positional arguments:
|
||||
# $1 - Source file path
|
||||
# $2 - C++ compiler (wasm32-wasip2-clang++)
|
||||
# $3 - C compiler (wasm32-wasip2-clang)
|
||||
# $4 - Sysroot path
|
||||
# $5 - Stage directory (contains unpacked SpiderMonkey headers)
|
||||
# $6 - Object output directory
|
||||
# $7 - Execution root (repo root)
|
||||
# $8 - Error codes header parent directory
|
||||
# $9 - Config header parent directory
|
||||
# $10 - Boost include flags
|
||||
# $11 - Bazel bin/src path (e.g., bazel-out/k8-fastbuild/bin/src)
|
||||
#
|
||||
# Example standalone usage:
|
||||
# bash scripts/compile_wasi_source.sh myfile.cpp \
|
||||
# /opt/wasi-sdk/bin/wasm32-wasip2-clang++ \
|
||||
# /opt/wasi-sdk/bin/wasm32-wasip2-clang \
|
||||
# /opt/wasi-sdk/share/wasi-sysroot \
|
||||
# /tmp/sm_stage /tmp/objs /path/to/repo \
|
||||
# /path/to/error_codes_parent /path/to/config_parent \
|
||||
# "-I/path/to/boost" \
|
||||
# /path/to/bazel-out/k8-fastbuild/bin/src
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
src="$1"
|
||||
CXX="$2"
|
||||
CC="$3"
|
||||
SYSROOT="$4"
|
||||
STAGE="$5"
|
||||
OBJ_DIR="$6"
|
||||
EXECROOT="$7"
|
||||
ERROR_CODES_H_PARENT="$8"
|
||||
CONFIG_H_PARENT="$9"
|
||||
BOOST_INCLUDES="${10}"
|
||||
BAZEL_BIN_SRC="${11:-}"
|
||||
|
||||
# Auto-detect bazel-out bin/src path if not provided.
|
||||
# Searches for any bazel-out/*/bin/src directory under EXECROOT.
|
||||
if [ -z "$BAZEL_BIN_SRC" ]; then
|
||||
for d in "$EXECROOT"/bazel-out/*/bin/src; do
|
||||
if [ -d "$d" ]; then
|
||||
BAZEL_BIN_SRC="$d"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Derive a unique object name by prefixing with the parent directory.
|
||||
# This avoids collisions when files from different directories share the
|
||||
# same basename (e.g. common/error.cpp vs engine/error.cpp, or
|
||||
# generated/api.c vs engine/api.cpp).
|
||||
parent="$(basename "$(dirname "$src")")"
|
||||
base="$(basename "$src")"
|
||||
OBJ_NAME="${parent}_${base%.*}.o"
|
||||
OBJ_PATH="$OBJ_DIR/$OBJ_NAME"
|
||||
|
||||
ext="${src##*.}"
|
||||
COMP="$CXX"
|
||||
STD="-std=c++20"
|
||||
if [ "$ext" = "c" ]; then
|
||||
COMP="$CC"
|
||||
STD="-std=c17"
|
||||
fi
|
||||
|
||||
# Build include flags for Bazel-generated headers (e.g., error_codes.h, config.h).
|
||||
BAZEL_BIN_INCLUDE=""
|
||||
if [ -n "$BAZEL_BIN_SRC" ] && [ -d "$BAZEL_BIN_SRC" ]; then
|
||||
BAZEL_BIN_INCLUDE="-I$BAZEL_BIN_SRC"
|
||||
fi
|
||||
|
||||
"$COMP" --sysroot="$SYSROOT" $STD -Oz \
|
||||
-flto -fexceptions -ffunction-sections -fdata-sections \
|
||||
-fvisibility=hidden \
|
||||
-D_WASI_EMULATED_SIGNAL -D_WASI_EMULATED_MMAN \
|
||||
-D_WASI_EMULATED_PROCESS_CLOCKS -D_WASI_EMULATED_GETPID \
|
||||
-DMOZ_HAS_MOZGLUE \
|
||||
-I"$STAGE/include" -I"$STAGE/include/js" -I"$STAGE/include/mozilla" \
|
||||
-I"$STAGE/include/src" -I"$STAGE/include/mfbt" \
|
||||
-include "$STAGE/include/js-confdefs.h" \
|
||||
-I"$EXECROOT/src" $BAZEL_BIN_INCLUDE \
|
||||
-I"$EXECROOT/src/mongo/scripting/mozjs/wasm" \
|
||||
-I"$EXECROOT/src/mongo/scripting/mozjs/common" \
|
||||
-I"$EXECROOT/src/mongo/scripting/mozjs/common/types" \
|
||||
-I"$EXECROOT/src/third_party/abseil-cpp/dist" \
|
||||
-I"$EXECROOT/src/mongo/scripting/mozjs/wasm/wit_gen/generated" \
|
||||
-DMONGO_MOZJS_WASI_BUILD \
|
||||
$BOOST_INCLUDES \
|
||||
-I"$ERROR_CODES_H_PARENT" -I"$CONFIG_H_PARENT" \
|
||||
-c "$src" -o "$OBJ_PATH" 2>&1
|
||||
|
||||
echo "$OBJ_PATH"
|
||||
49
src/mongo/scripting/mozjs/wasm/scripts/extract_rust_shims.sh
Executable file
49
src/mongo/scripting/mozjs/wasm/scripts/extract_rust_shims.sh
Executable file
@ -0,0 +1,49 @@
|
||||
#!/bin/bash
|
||||
# Extracts the Rust encoding-shims static library from a SpiderMonkey WASI tarball.
|
||||
#
|
||||
# Required environment variables:
|
||||
# TARBALL - Path to spidermonkey-wasip2-release.tar.gz
|
||||
# OUTPUT - Output path for the extracted .a file
|
||||
#
|
||||
# Example standalone usage:
|
||||
# TARBALL=spidermonkey-wasip2-release.tar.gz \
|
||||
# OUTPUT=rust_shims.a \
|
||||
# bash scripts/extract_rust_shims.sh
|
||||
|
||||
set -euo pipefail
|
||||
if [ -z "${TARBALL:-}" ]; then
|
||||
echo "ERROR: TARBALL not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "${OUTPUT:-}" ]; then
|
||||
echo "ERROR: OUTPUT not set." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OUT_DIR="$(dirname "$OUTPUT")"
|
||||
STAGE="$OUT_DIR/rust_stage"
|
||||
rm -rf "$STAGE"
|
||||
mkdir -p "$STAGE"
|
||||
|
||||
# Extract lib directory from tarball (tarball has ./lib/ prefix).
|
||||
tar -xzf "$TARBALL" -C "$STAGE" --strip-components=0 ./lib/libmongo_wasip2_rust_shims.a 2>/dev/null || {
|
||||
# Try without ./ prefix if that fails.
|
||||
if ! tar -xzf "$TARBALL" -C "$STAGE" lib/libmongo_wasip2_rust_shims.a 2>/dev/null; then
|
||||
echo "WARNING: Could not extract rust shims with either path prefix; will check below." >&2
|
||||
fi
|
||||
}
|
||||
|
||||
RUST_SHIMS="$STAGE/lib/libmongo_wasip2_rust_shims.a"
|
||||
if [ ! -f "$RUST_SHIMS" ]; then
|
||||
# Try alternative path.
|
||||
RUST_SHIMS="$STAGE/./lib/libmongo_wasip2_rust_shims.a"
|
||||
fi
|
||||
|
||||
if [ -f "$RUST_SHIMS" ]; then
|
||||
cp "$RUST_SHIMS" "$OUTPUT"
|
||||
echo "Extracted rust shims to: $OUTPUT" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "ERROR: Rust shims not found in tarball" >&2
|
||||
exit 1
|
||||
82
src/mongo/scripting/mozjs/wasm/scripts/link_mongo_base_wasm.sh
Executable file
82
src/mongo/scripting/mozjs/wasm/scripts/link_mongo_base_wasm.sh
Executable file
@ -0,0 +1,82 @@
|
||||
#!/bin/bash
|
||||
# Links mongo_base.wasm from WASI linkset response files.
|
||||
#
|
||||
# Required environment variables:
|
||||
# OUTPUT - Output .wasm file path
|
||||
#
|
||||
# Compiler discovery (set one of):
|
||||
# CXX - Direct path to wasm32-wasip2-clang++
|
||||
# WASI_SDK_BIN_FILES - Space-separated wasi_sdk bin files (auto-discovers compiler)
|
||||
#
|
||||
# RSP files (set one of):
|
||||
# OBJS_RSP / LIBS_RSP / FLAGS_RSP - Direct paths to response files
|
||||
# LINKSET_FILES - Space-separated linkset outputs (auto-discovers RSPs)
|
||||
#
|
||||
# Example standalone usage:
|
||||
# CXX=/path/to/wasm32-wasip2-clang++ \
|
||||
# OBJS_RSP=objs.rsp LIBS_RSP=libs.rsp FLAGS_RSP=flags.rsp \
|
||||
# OUTPUT=mongo_base.wasm \
|
||||
# bash scripts/link_mongo_base_wasm.sh
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# --- Discover compiler ---
|
||||
# Prefer wasip2 (WASI Preview 2) compiler to match the rest of the build.
|
||||
if [ -z "${CXX:-}" ]; then
|
||||
for f in ${WASI_SDK_BIN_FILES:-}; do
|
||||
case "$f" in */wasm32-wasip2-clang++)
|
||||
CXX="$f"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
# Fallback to WASI Preview 1 compiler name if wasip2 variant not found.
|
||||
if [ -z "${CXX:-}" ]; then
|
||||
for f in ${WASI_SDK_BIN_FILES:-}; do
|
||||
case "$f" in */wasm32-wasi-clang++)
|
||||
CXX="$f"
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
if [ -z "${CXX:-}" ]; then
|
||||
echo "ERROR: wasm32-wasip2-clang++ (or wasm32-wasi-clang++) not found. Set CXX or WASI_SDK_BIN_FILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# --- Discover RSP files ---
|
||||
if [ -z "${OBJS_RSP:-}" ] || [ -z "${LIBS_RSP:-}" ] || [ -z "${FLAGS_RSP:-}" ]; then
|
||||
for f in ${LINKSET_FILES:-}; do
|
||||
case "$f" in
|
||||
*.objects.rsp) OBJS_RSP="$f" ;;
|
||||
*.libs.rsp) LIBS_RSP="$f" ;;
|
||||
*.flags.rsp) FLAGS_RSP="$f" ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
if [ -z "${OBJS_RSP:-}" ] || [ -z "${LIBS_RSP:-}" ] || [ -z "${FLAGS_RSP:-}" ]; then
|
||||
echo "ERROR: Missing RSP files (.objects/.libs/.flags)." >&2
|
||||
echo "Set OBJS_RSP/LIBS_RSP/FLAGS_RSP or LINKSET_FILES." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Select the correct --target based on the compiler name.
|
||||
# wasip2 compiler requires wasip2 target; wasi compiler requires wasi target.
|
||||
WASM_TARGET="wasm32-wasip2"
|
||||
|
||||
echo "Linking mongo_base.wasm" >&2
|
||||
echo " CXX: $CXX" >&2
|
||||
echo " TARGET: $WASM_TARGET" >&2
|
||||
echo " OBJS_RSP: $OBJS_RSP" >&2
|
||||
echo " LIBS_RSP: $LIBS_RSP" >&2
|
||||
echo " FLAGS_RSP: $FLAGS_RSP" >&2
|
||||
|
||||
"$CXX" --target="$WASM_TARGET" -std=c++20 -Oz -fexceptions \
|
||||
@"$OBJS_RSP" @"$LIBS_RSP" @"$FLAGS_RSP" \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,--strip-all \
|
||||
-o "$OUTPUT"
|
||||
|
||||
echo "Output: $OUTPUT" >&2
|
||||
6
src/mongo/scripting/mozjs/wasm/spider-monkey/BUILD.bazel
Normal file
6
src/mongo/scripting/mozjs/wasm/spider-monkey/BUILD.bazel
Normal file
@ -0,0 +1,6 @@
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files([
|
||||
"spider-monkey-repository",
|
||||
"spider-monkey-version",
|
||||
])
|
||||
@ -0,0 +1 @@
|
||||
https://github.com/mozilla-firefox/firefox
|
||||
@ -0,0 +1 @@
|
||||
FIREFOX_140_6_0esr_RELEASE
|
||||
80
src/mongo/scripting/mozjs/wasm/spidermonkey_repo.bzl
Normal file
80
src/mongo/scripting/mozjs/wasm/spidermonkey_repo.bzl
Normal file
@ -0,0 +1,80 @@
|
||||
load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe")
|
||||
|
||||
def _strip(s):
|
||||
return s.strip(" \t\r\n")
|
||||
|
||||
def _basename_no_git(url):
|
||||
u = url
|
||||
if u.endswith("/"):
|
||||
u = u[:-1]
|
||||
if u.endswith(".git"):
|
||||
u = u[:-4]
|
||||
|
||||
# last path component
|
||||
parts = u.split("/")
|
||||
return parts[-1] if parts else "repo"
|
||||
|
||||
def _spidermonkey_repository_impl(rctx):
|
||||
repo_url = _strip(rctx.read(rctx.attr.repository_file))
|
||||
version = _strip(rctx.read(rctx.attr.version_file))
|
||||
|
||||
if not repo_url:
|
||||
fail("spidermonkey_repository: repository_file was empty")
|
||||
if not version:
|
||||
fail("spidermonkey_repository: version_file was empty")
|
||||
|
||||
# Normalize.
|
||||
if repo_url.endswith("/"):
|
||||
repo_url = repo_url[:-1]
|
||||
if repo_url.endswith(".git"):
|
||||
repo_url = repo_url[:-4]
|
||||
|
||||
repo_name = _basename_no_git(repo_url)
|
||||
|
||||
# GitHub archive URL for tags.
|
||||
# Example: https://github.com/mozilla-firefox/firefox/archive/refs/tags/FIREFOX_140_6_0esr_RELEASE.tar.gz
|
||||
archive_url = "{}/archive/refs/tags/{}.tar.gz".format(repo_url, version)
|
||||
strip_prefix = "{}-{}".format(repo_name, version)
|
||||
|
||||
if not rctx.attr.sha256:
|
||||
# buildifier: disable=print
|
||||
print("WARNING: spidermonkey_repository: sha256 not set for %s; download integrity will not be verified." % archive_url)
|
||||
|
||||
rctx.download_and_extract(
|
||||
url = archive_url,
|
||||
sha256 = rctx.attr.sha256,
|
||||
stripPrefix = strip_prefix,
|
||||
)
|
||||
|
||||
# Minimal BUILD file so downstream genrules can depend on `:mach` and `:srcs`.
|
||||
rctx.file(
|
||||
"BUILD.bazel",
|
||||
content = """
|
||||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
exports_files(["mach"])
|
||||
|
||||
filegroup(
|
||||
name = "srcs",
|
||||
srcs = glob(
|
||||
["**"],
|
||||
exclude = [
|
||||
"**/.git/**",
|
||||
"**/.hg/**",
|
||||
],
|
||||
),
|
||||
)
|
||||
""",
|
||||
)
|
||||
|
||||
spidermonkey_repository = repository_rule(
|
||||
implementation = _spidermonkey_repository_impl,
|
||||
attrs = {
|
||||
# Labels in the main workspace that contain the repo URL and tag/version.
|
||||
"repository_file": attr.label(mandatory = True, allow_single_file = True),
|
||||
"version_file": attr.label(mandatory = True, allow_single_file = True),
|
||||
# Optional, but recommended for hermeticity. If empty, Bazel will not verify.
|
||||
"sha256": attr.string(default = ""),
|
||||
},
|
||||
doc = "Downloads SpiderMonkey/Firefox source from a repo+tag stored in files, and exposes `:mach` and `:srcs`.",
|
||||
)
|
||||
149
src/mongo/scripting/mozjs/wasm/status.cpp
Normal file
149
src/mongo/scripting/mozjs/wasm/status.cpp
Normal file
@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#include "mongo/scripting/mozjs/common/types/status.h"
|
||||
|
||||
#include "mongo/base/error_codes.h"
|
||||
#include "mongo/base/status.h"
|
||||
#include "mongo/scripting/mozjs/common/error.h"
|
||||
#include "mongo/scripting/mozjs/common/freeOpToJSContext.h"
|
||||
#include "mongo/scripting/mozjs/common/internedstring.h"
|
||||
#include "mongo/scripting/mozjs/common/objectwrapper.h"
|
||||
#include "mongo/scripting/mozjs/common/runtime.h"
|
||||
#include "mongo/scripting/mozjs/common/valuereader.h"
|
||||
#include "mongo/scripting/mozjs/common/wrapconstrainedmethod.h"
|
||||
#include "mongo/scripting/mozjs/common/wraptype.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
|
||||
#include <jsapi.h>
|
||||
#include <jsfriendapi.h>
|
||||
|
||||
#include <js/CallArgs.h>
|
||||
#include <js/ComparisonOperators.h>
|
||||
#include <js/Object.h>
|
||||
#include <js/PropertyDescriptor.h>
|
||||
#include <js/RootingAPI.h>
|
||||
#include <js/TypeDecls.h>
|
||||
#include <js/ValueArray.h>
|
||||
|
||||
namespace mongo {
|
||||
namespace mozjs {
|
||||
|
||||
const char* const MongoStatusInfo::className = "MongoStatus";
|
||||
const char* const MongoStatusInfo::inheritFrom = "Error";
|
||||
|
||||
Status MongoStatusInfo::toStatus(JSContext* cx, JS::HandleObject object) {
|
||||
return *JS::GetMaybePtrFromReservedSlot<Status>(object, StatusSlot);
|
||||
}
|
||||
|
||||
Status MongoStatusInfo::toStatus(JSContext* cx, JS::HandleValue value) {
|
||||
return *JS::GetMaybePtrFromReservedSlot<Status>(value.toObjectOrNull(), StatusSlot);
|
||||
}
|
||||
|
||||
void MongoStatusInfo::fromStatus(JSContext* cx, Status status, JS::MutableHandleValue value) {
|
||||
invariant(status != Status::OK());
|
||||
auto runtime = getCommonRuntime(cx);
|
||||
|
||||
JS::RootedValue undef(cx);
|
||||
undef.setUndefined();
|
||||
|
||||
JS::RootedValueArray<1> args(cx);
|
||||
ValueReader(cx, args[0]).fromStringData(status.reason());
|
||||
JS::RootedObject error(cx);
|
||||
getProto<ErrorInfo>(runtime).newInstance(args, &error);
|
||||
|
||||
JS::RootedObject thisv(cx);
|
||||
getProto<MongoStatusInfo>(runtime).newObjectWithProto(&thisv, error);
|
||||
ObjectWrapper thisvObj(cx, thisv);
|
||||
thisvObj.defineProperty(InternedString::code,
|
||||
JSPROP_ENUMERATE,
|
||||
smUtils::wrapConstrainedMethod<Functions::code, false, MongoStatusInfo>,
|
||||
nullptr);
|
||||
|
||||
thisvObj.defineProperty(
|
||||
InternedString::reason,
|
||||
JSPROP_ENUMERATE,
|
||||
smUtils::wrapConstrainedMethod<Functions::reason, false, MongoStatusInfo>,
|
||||
nullptr);
|
||||
|
||||
// We intentionally omit JSPROP_ENUMERATE to match how Error.prototype.stack is a non-enumerable
|
||||
// property.
|
||||
thisvObj.defineProperty(
|
||||
InternedString::stack,
|
||||
0,
|
||||
smUtils::wrapConstrainedMethod<Functions::stack, false, MongoStatusInfo>,
|
||||
nullptr);
|
||||
|
||||
JS::SetReservedSlot(
|
||||
thisv, StatusSlot, JS::PrivateValue(trackedNew<Status>(runtime, std::move(status))));
|
||||
|
||||
value.setObjectOrNull(thisv);
|
||||
}
|
||||
|
||||
void MongoStatusInfo::finalize(JS::GCContext* gcCtx, JSObject* obj) {
|
||||
auto status = JS::GetMaybePtrFromReservedSlot<Status>(obj, StatusSlot);
|
||||
|
||||
if (status)
|
||||
trackedDelete(getCommonRuntime(freeOpToJSContext(gcCtx)), status);
|
||||
}
|
||||
|
||||
void MongoStatusInfo::Functions::code::call(JSContext* cx, JS::CallArgs args) {
|
||||
args.rval().setInt32(toStatus(cx, args.thisv()).code());
|
||||
}
|
||||
|
||||
void MongoStatusInfo::Functions::reason::call(JSContext* cx, JS::CallArgs args) {
|
||||
ValueReader(cx, args.rval()).fromStringData(toStatus(cx, args.thisv()).reason());
|
||||
}
|
||||
|
||||
void MongoStatusInfo::Functions::stack::call(JSContext* cx, JS::CallArgs args) {
|
||||
JS::RootedObject thisv(cx, args.thisv().toObjectOrNull());
|
||||
JS::RootedObject parent(cx);
|
||||
|
||||
if (!JS_GetPrototype(cx, thisv, &parent)) {
|
||||
uasserted(ErrorCodes::JSInterpreterFailure, "Couldn't get prototype");
|
||||
}
|
||||
|
||||
ObjectWrapper parentWrapper(cx, parent);
|
||||
|
||||
auto status = toStatus(cx, args.thisv());
|
||||
// WASI version: Skip JSExceptionInfo handling as its not available in WASI builds
|
||||
// Just return the parent's stack property
|
||||
parentWrapper.getValue(InternedString::stack, args.rval());
|
||||
}
|
||||
|
||||
void MongoStatusInfo::postInstall(JSContext* cx, JS::HandleObject global, JS::HandleObject proto) {
|
||||
auto runtime = getCommonRuntime(cx);
|
||||
JS::SetReservedSlot(proto,
|
||||
StatusSlot,
|
||||
JS::PrivateValue(trackedNew<Status>(
|
||||
runtime, Status(ErrorCodes::UnknownError, "Mongo Status Prototype"))));
|
||||
}
|
||||
|
||||
} // namespace mozjs
|
||||
} // namespace mongo
|
||||
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "mongo_wasip2_rust_shims"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
encoding_c = { path = "{{SPIDERMONKEY_ROOT}}/third_party/rust/encoding_c" }
|
||||
encoding_c_mem = { path = "{{SPIDERMONKEY_ROOT}}/third_party/rust/encoding_c_mem" }
|
||||
45
src/mongo/scripting/mozjs/wasm/support/rust_shims/src/lib.rs
Normal file
45
src/mongo/scripting/mozjs/wasm/support/rust_shims/src/lib.rs
Normal file
@ -0,0 +1,45 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
// Rust shims for WASI linkage.
|
||||
// Force-link the encoding shim crates so their exported C symbols are present.
|
||||
extern crate encoding_c;
|
||||
extern crate encoding_c_mem;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn mongo_wasip2_rust_shims_keepalive() {
|
||||
// Touch at least one symbol from each crate so the linker keeps them.
|
||||
unsafe {
|
||||
let p = core::ptr::null::<u8>();
|
||||
let _ = encoding_c::encoding_utf8_valid_up_to(p, 0);
|
||||
let q = core::ptr::null::<u16>();
|
||||
let _ = encoding_c_mem::encoding_mem_is_utf8_latin1(p, 0);
|
||||
let _ = q;
|
||||
}
|
||||
}
|
||||
3
src/mongo/scripting/mozjs/wasm/wit/BUILD.bazel
Normal file
3
src/mongo/scripting/mozjs/wasm/wit/BUILD.bazel
Normal file
@ -0,0 +1,3 @@
|
||||
exports_files([
|
||||
"mozjs.wit",
|
||||
])
|
||||
35
src/mongo/scripting/mozjs/wasm/wit/README.md
Normal file
35
src/mongo/scripting/mozjs/wasm/wit/README.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Add more methods to public API:
|
||||
|
||||
```
|
||||
world api {
|
||||
export mozjstest: func() -> s32;
|
||||
export mynewfunc: func() -> s32;
|
||||
}
|
||||
```
|
||||
|
||||
# Install wit-gen on your dev VM
|
||||
|
||||
```
|
||||
cargo install --git https://github.com/bytecodealliance/wit-bindgen --locked wit-bindgen-cli
|
||||
```
|
||||
|
||||
# Run the bindgen
|
||||
|
||||
```
|
||||
cd src/mongo/scripting/mozjs/wasm
|
||||
wit-bindgen c ./wit --out-dir wit_gen/generated
|
||||
```
|
||||
|
||||
# Implement the symbol in engine/api.cpp
|
||||
|
||||
```
|
||||
extern "C" int32_t exports_api_mozjstest(void) {
|
||||
return 1234;
|
||||
}
|
||||
|
||||
extern "C" int32_t exports_api_mynewfunc(void) {
|
||||
return ...;
|
||||
}
|
||||
```
|
||||
|
||||
# Commit the generated files in wit_gen/generated
|
||||
90
src/mongo/scripting/mozjs/wasm/wit/mozjs.wit
Normal file
90
src/mongo/scripting/mozjs/wasm/wit/mozjs.wit
Normal file
@ -0,0 +1,90 @@
|
||||
package mongo:mozjs;
|
||||
|
||||
interface mozjs {
|
||||
/// Error codes mirrored from `err_code_t`.
|
||||
enum err-code {
|
||||
ok,
|
||||
e-invalid-arg,
|
||||
e-bad-state,
|
||||
e-nomem,
|
||||
e-io,
|
||||
e-timeout,
|
||||
e-not-supported,
|
||||
e-internal,
|
||||
|
||||
e-jsapi-fail,
|
||||
e-pending-exception,
|
||||
e-no-exception,
|
||||
e-terminated,
|
||||
e-oom,
|
||||
|
||||
e-compile,
|
||||
e-runtime,
|
||||
e-module,
|
||||
e-promise-rejection,
|
||||
e-stack-overflow,
|
||||
|
||||
e-type,
|
||||
e-encoding,
|
||||
}
|
||||
|
||||
/// Mirrors `wasm_mozjs_error_t` (but as owned values).
|
||||
record wasm-mozjs-error {
|
||||
code: err-code,
|
||||
|
||||
msg: option<string>,
|
||||
filename: option<string>,
|
||||
stack: option<string>,
|
||||
|
||||
line: u32,
|
||||
column: u32,
|
||||
}
|
||||
|
||||
type ok = u16;
|
||||
|
||||
/// Opaque function handle.
|
||||
type function-handle = u64;
|
||||
|
||||
/// Initialize/shutdown/interrupt
|
||||
initialize-engine: func() -> result<ok, wasm-mozjs-error>;
|
||||
shutdown-engine: func() -> result<ok, wasm-mozjs-error>;
|
||||
interrupt-current-op: func() -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Create a JS function from source.
|
||||
create-function: func(source: list<u8>) -> result<function-handle, wasm-mozjs-error>;
|
||||
|
||||
/// Invoke a function with BSON args (no `this.` binding).
|
||||
/// Used by $function, $accumulator, mapReduce.reduce, mapReduce.finalize.
|
||||
invoke-function: func(handle: function-handle, bson: list<u8>) -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Invoke a predicate with the document as `this`, returns bool directly.
|
||||
/// Used by $where: function() { return this.age > 18; }
|
||||
invoke-predicate: func(handle: function-handle, document: list<u8>) -> result<bool, wasm-mozjs-error>;
|
||||
|
||||
/// Invoke a map function with the document as `this`; emits buffered internally.
|
||||
/// Used by mapReduce.map: function() { emit(this.key, this.value); }
|
||||
invoke-map: func(handle: function-handle, document: list<u8>) -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Get last return value as BSON bytes.
|
||||
get-return-value-bson: func() -> result<list<u8>, wasm-mozjs-error>;
|
||||
|
||||
/// Set a named global variable from a BSON-encoded value.
|
||||
set-global: func(name: string, bson-value: list<u8>) -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Get a named global variable as BSON-encoded bytes.
|
||||
get-global: func(name: string) -> result<list<u8>, wasm-mozjs-error>;
|
||||
|
||||
/// Set a named global to a single BSON element's JS value directly.
|
||||
set-global-value: func(name: string, bson-element: list<u8>) -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Set up the emit() built-in for mapReduce. Resets the emit buffer.
|
||||
/// Optional byte-limit caps total emitted bytes (default 16 MiB).
|
||||
setup-emit: func(byte-limit: option<s64>) -> result<ok, wasm-mozjs-error>;
|
||||
|
||||
/// Drain the emit buffer: returns accumulated {k,v} pairs as BSON, then clears.
|
||||
drain-emit-buffer: func() -> result<list<u8>, wasm-mozjs-error>;
|
||||
}
|
||||
|
||||
world api {
|
||||
export mozjs;
|
||||
}
|
||||
7
src/mongo/scripting/mozjs/wasm/wit_gen/BUILD.bazel
Normal file
7
src/mongo/scripting/mozjs/wasm/wit_gen/BUILD.bazel
Normal file
@ -0,0 +1,7 @@
|
||||
exports_files(
|
||||
glob([
|
||||
"*.h",
|
||||
"*.cpp",
|
||||
"*.defs",
|
||||
]),
|
||||
)
|
||||
@ -0,0 +1,7 @@
|
||||
exports_files(
|
||||
glob([
|
||||
"*.h",
|
||||
"*.c",
|
||||
"*.o",
|
||||
]),
|
||||
)
|
||||
1428
src/mongo/scripting/mozjs/wasm/wit_gen/generated/api.c
Normal file
1428
src/mongo/scripting/mozjs/wasm/wit_gen/generated/api.c
Normal file
File diff suppressed because it is too large
Load Diff
186
src/mongo/scripting/mozjs/wasm/wit_gen/generated/api.h
Normal file
186
src/mongo/scripting/mozjs/wasm/wit_gen/generated/api.h
Normal file
@ -0,0 +1,186 @@
|
||||
// Generated by `wit-bindgen` 0.51.0. DO NOT EDIT!
|
||||
#ifndef __BINDINGS_API_H
|
||||
#define __BINDINGS_API_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct api_string_t {
|
||||
uint8_t* ptr;
|
||||
size_t len;
|
||||
} api_string_t;
|
||||
|
||||
// Error codes mirrored from `err_code_t`.
|
||||
typedef uint8_t exports_mongo_mozjs_mozjs_err_code_t;
|
||||
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_OK 0
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_INVALID_ARG 1
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_BAD_STATE 2
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_NOMEM 3
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_IO 4
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_TIMEOUT 5
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_NOT_SUPPORTED 6
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_INTERNAL 7
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_JSAPI_FAIL 8
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_PENDING_EXCEPTION 9
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_NO_EXCEPTION 10
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_TERMINATED 11
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_OOM 12
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_COMPILE 13
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_RUNTIME 14
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_MODULE 15
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_PROMISE_REJECTION 16
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_STACK_OVERFLOW 17
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_TYPE 18
|
||||
#define EXPORTS_MONGO_MOZJS_MOZJS_ERR_CODE_E_ENCODING 19
|
||||
|
||||
typedef struct {
|
||||
bool is_some;
|
||||
api_string_t val;
|
||||
} api_option_string_t;
|
||||
|
||||
// Mirrors `wasm_mozjs_error_t` (but as owned values).
|
||||
typedef struct exports_mongo_mozjs_mozjs_wasm_mozjs_error_t {
|
||||
exports_mongo_mozjs_mozjs_err_code_t code;
|
||||
api_option_string_t msg;
|
||||
api_option_string_t filename;
|
||||
api_option_string_t stack;
|
||||
uint32_t line;
|
||||
uint32_t column;
|
||||
} exports_mongo_mozjs_mozjs_wasm_mozjs_error_t;
|
||||
|
||||
typedef uint16_t exports_mongo_mozjs_mozjs_ok_t;
|
||||
|
||||
// Opaque function handle.
|
||||
typedef uint64_t exports_mongo_mozjs_mozjs_function_handle_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_err;
|
||||
union {
|
||||
exports_mongo_mozjs_mozjs_ok_t ok;
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t err;
|
||||
} val;
|
||||
} exports_mongo_mozjs_mozjs_result_ok_wasm_mozjs_error_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t* ptr;
|
||||
size_t len;
|
||||
} api_list_u8_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_err;
|
||||
union {
|
||||
exports_mongo_mozjs_mozjs_function_handle_t ok;
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t err;
|
||||
} val;
|
||||
} exports_mongo_mozjs_mozjs_result_function_handle_wasm_mozjs_error_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_err;
|
||||
union {
|
||||
bool ok;
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t err;
|
||||
} val;
|
||||
} exports_mongo_mozjs_mozjs_result_bool_wasm_mozjs_error_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_err;
|
||||
union {
|
||||
api_list_u8_t ok;
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t err;
|
||||
} val;
|
||||
} exports_mongo_mozjs_mozjs_result_list_u8_wasm_mozjs_error_t;
|
||||
|
||||
typedef struct {
|
||||
bool is_some;
|
||||
int64_t val;
|
||||
} api_option_s64_t;
|
||||
|
||||
// Exported Functions from `mongo:mozjs/mozjs`
|
||||
bool exports_mongo_mozjs_mozjs_initialize_engine(exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_shutdown_engine(exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_interrupt_current_op(
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_create_function(api_list_u8_t* source,
|
||||
exports_mongo_mozjs_mozjs_function_handle_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_invoke_function(exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* bson,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_invoke_predicate(exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* document,
|
||||
bool* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_invoke_map(exports_mongo_mozjs_mozjs_function_handle_t handle,
|
||||
api_list_u8_t* document,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_get_return_value_bson(
|
||||
api_list_u8_t* ret, exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_set_global(api_string_t* name,
|
||||
api_list_u8_t* bson_value,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_get_global(api_string_t* name,
|
||||
api_list_u8_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_set_global_value(api_string_t* name,
|
||||
api_list_u8_t* bson_element,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_setup_emit(int64_t* maybe_byte_limit,
|
||||
exports_mongo_mozjs_mozjs_ok_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
bool exports_mongo_mozjs_mozjs_drain_emit_buffer(api_list_u8_t* ret,
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* err);
|
||||
|
||||
// Helper Functions
|
||||
|
||||
void api_option_string_free(api_option_string_t* ptr);
|
||||
|
||||
void exports_mongo_mozjs_mozjs_wasm_mozjs_error_free(
|
||||
exports_mongo_mozjs_mozjs_wasm_mozjs_error_t* ptr);
|
||||
|
||||
void exports_mongo_mozjs_mozjs_result_ok_wasm_mozjs_error_free(
|
||||
exports_mongo_mozjs_mozjs_result_ok_wasm_mozjs_error_t* ptr);
|
||||
|
||||
void api_list_u8_free(api_list_u8_t* ptr);
|
||||
|
||||
void exports_mongo_mozjs_mozjs_result_function_handle_wasm_mozjs_error_free(
|
||||
exports_mongo_mozjs_mozjs_result_function_handle_wasm_mozjs_error_t* ptr);
|
||||
|
||||
void exports_mongo_mozjs_mozjs_result_bool_wasm_mozjs_error_free(
|
||||
exports_mongo_mozjs_mozjs_result_bool_wasm_mozjs_error_t* ptr);
|
||||
|
||||
void exports_mongo_mozjs_mozjs_result_list_u8_wasm_mozjs_error_free(
|
||||
exports_mongo_mozjs_mozjs_result_list_u8_wasm_mozjs_error_t* ptr);
|
||||
|
||||
void api_option_s64_free(api_option_s64_t* ptr);
|
||||
|
||||
// Sets the string `ret` to reference the input string `s` without copying it
|
||||
void api_string_set(api_string_t* ret, const char* s);
|
||||
|
||||
// Creates a copy of the input nul-terminated string `s` and
|
||||
// stores it into the component model string `ret`.
|
||||
void api_string_dup(api_string_t* ret, const char* s);
|
||||
|
||||
// Creates a copy of the input string `s` with length `len` and
|
||||
// stores it into the component model string `ret`.
|
||||
// The length is specified in code units (bytes for UTF-8, 16-bit values for UTF-16).
|
||||
void api_string_dup_n(api_string_t* ret, const char* s, size_t len);
|
||||
|
||||
// Deallocates the string pointed to by `ret`, deallocating
|
||||
// the memory behind the string.
|
||||
void api_string_free(api_string_t* ret);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
Binary file not shown.
54
src/mongo/scripting/oid_validation.h
Normal file
54
src/mongo/scripting/oid_validation.h
Normal file
@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Copyright (C) 2026-present MongoDB, Inc.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the Server Side Public License, version 1,
|
||||
* as published by MongoDB, Inc.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* Server Side Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the Server Side Public License
|
||||
* along with this program. If not, see
|
||||
* <http://www.mongodb.com/licensing/server-side-public-license>.
|
||||
*
|
||||
* As a special exception, the copyright holders give permission to link the
|
||||
* code of portions of this program with the OpenSSL library under certain
|
||||
* conditions as described in each individual source file and distribute
|
||||
* linked combinations including the program with the OpenSSL library. You
|
||||
* must comply with the Server Side Public License in all respects for
|
||||
* all of the code used other than as permitted herein. If you modify file(s)
|
||||
* with this exception, you may extend this exception to your version of the
|
||||
* file(s), but you are not obligated to do so. If you do not wish to do so,
|
||||
* delete this exception statement from your version. If you delete this
|
||||
* exception statement from all source files in the program, then also delete
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "mongo/base/string_data.h"
|
||||
#include "mongo/util/assert_util.h"
|
||||
#include "mongo/util/ctype.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace mongo {
|
||||
|
||||
/**
|
||||
* Validates that a string is a valid ObjectId string (24 hex characters).
|
||||
*
|
||||
* This function was extracted from Scope::validateObjectIdString to decouple
|
||||
* ObjectId validation from the Scope class and engine.h dependencies.
|
||||
*/
|
||||
inline void validateObjectIdString(StringData str) {
|
||||
uassert(10448, "invalid object id: length", str.size() == 24);
|
||||
auto isAllHex = [](StringData s) {
|
||||
return std::all_of(s.begin(), s.end(), [](char c) { return mongo::ctype::isXdigit(c); });
|
||||
};
|
||||
uassert(10430, "invalid object id: not hex", isAllHex(str));
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
@ -71,6 +71,7 @@ mongo_cc_library(
|
||||
private_hdrs = [
|
||||
"//src/mongo/base:string_data.h",
|
||||
"//src/mongo/scripting:engine.h",
|
||||
"//src/mongo/scripting:js_regex.h",
|
||||
],
|
||||
deps = [
|
||||
"//src/mongo:base",
|
||||
|
||||
@ -43,6 +43,32 @@
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
// If building for WASI/wasm, noop all debugger functionality to avoid POSIX/signal/fork issues.
|
||||
// See https://github.com/WebAssembly/WASI/issues/166
|
||||
#if defined(__wasi__) || defined(__WASI__) || defined(__wasm__) || defined(__wasm32__)
|
||||
|
||||
namespace mongo {
|
||||
|
||||
void breakpoint() {
|
||||
// no-op under WASI
|
||||
}
|
||||
|
||||
void setupSIGTRAPforDebugger() {
|
||||
// no-op under WASI
|
||||
}
|
||||
|
||||
void waitForDebugger() {
|
||||
// no-op under WASI
|
||||
}
|
||||
|
||||
bool isDebuggerActive() {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
|
||||
#else // non-WASI builds
|
||||
|
||||
#ifndef _WIN32
|
||||
namespace {
|
||||
std::once_flag breakpointOnceFlag;
|
||||
@ -199,3 +225,5 @@ bool isDebuggerActive() {
|
||||
}
|
||||
|
||||
} // namespace mongo
|
||||
#endif
|
||||
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
#include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
#if !defined(_WIN32) && !defined(__wasi__)
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
@ -64,6 +64,8 @@ public:
|
||||
std::string message(int e) const override {
|
||||
#ifdef _WIN32
|
||||
return systemError(e).message();
|
||||
#elif defined(__wasi__)
|
||||
return "getaddrinfo error " + std::to_string(e) + " (networking not supported in WASI)";
|
||||
#else
|
||||
return gai_strerror(e);
|
||||
#endif
|
||||
|
||||
@ -228,7 +228,7 @@ private:
|
||||
PROCESS_INFORMATION _process;
|
||||
DWORD _exitcode = STILL_ACTIVE;
|
||||
};
|
||||
#else
|
||||
#elif !defined(__wasi__)
|
||||
class ProcessStream {
|
||||
public:
|
||||
ProcessStream(const std::string& cmd) {
|
||||
@ -291,6 +291,41 @@ private:
|
||||
int _fd;
|
||||
int _exitcode = 1;
|
||||
};
|
||||
#else
|
||||
// WASI doesn't support process execution
|
||||
// See: https://github.com/WebAssembly/WASI/issues/414
|
||||
class ProcessStream {
|
||||
public:
|
||||
ProcessStream(const std::string& cmd) {
|
||||
uasserted(ErrorCodes::OperationFailed,
|
||||
str::stream() << "Shell execution not supported in WASI environment: " << cmd);
|
||||
}
|
||||
|
||||
int close() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool eof() {
|
||||
return true;
|
||||
}
|
||||
|
||||
Status wait(Milliseconds duration) {
|
||||
(void)duration;
|
||||
return {ErrorCodes::OperationFailed, "Shell execution not supported in WASI"};
|
||||
}
|
||||
|
||||
void read(StringBuilder& sb, size_t len) {
|
||||
(void)sb;
|
||||
(void)len;
|
||||
}
|
||||
|
||||
~ProcessStream() = default;
|
||||
|
||||
private:
|
||||
ProcessStream() = delete;
|
||||
ProcessStream(const ProcessStream&) = delete;
|
||||
ProcessStream& operator=(const ProcessStream&) = delete;
|
||||
};
|
||||
#endif
|
||||
} // namespace
|
||||
} // namespace mongo
|
||||
|
||||
@ -285,7 +285,7 @@ void myPureCallHandler() {
|
||||
abruptQuit(SIGABRT);
|
||||
}
|
||||
|
||||
#else
|
||||
#elif !defined(__wasi__)
|
||||
|
||||
extern "C" void abruptQuitAction(int signalNum, siginfo_t*, void*) {
|
||||
if (gSynchronousSignalHandlerCb) {
|
||||
@ -361,6 +361,37 @@ void setupSignalTestingHandler() {
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
// WASI doesn't support signals currently. The effort tracking the support
|
||||
// for signals is https://github.com/WebAssembly/WASI/issues/414,
|
||||
// See https://github.com/WebAssembly/WASI/issues/166
|
||||
extern "C" void abruptQuitAction(int signalNum, void*, void*) {
|
||||
abruptQuit(signalNum);
|
||||
};
|
||||
|
||||
void printSigInfo(const void* siginfo) {
|
||||
(void)siginfo;
|
||||
}
|
||||
|
||||
extern "C" void abruptQuitWithAddrSignal(int signalNum, void* siginfo, void* ucontext_erased) {
|
||||
(void)siginfo;
|
||||
(void)ucontext_erased;
|
||||
abruptQuit(signalNum);
|
||||
}
|
||||
|
||||
extern "C" void noopSignalHandler(int signalNum, void*, void*) {
|
||||
(void)signalNum;
|
||||
}
|
||||
|
||||
extern "C" typedef void(sigAction_t)(int signum, void* info, void* context);
|
||||
|
||||
void installSignalHandler(int signal, sigAction_t handler) {
|
||||
(void)signal;
|
||||
(void)handler;
|
||||
}
|
||||
|
||||
void setupSignalTestingHandler() {}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
@ -382,7 +413,7 @@ void endProcessWithSignal(int signalNum) {
|
||||
// The exception filter exits the process
|
||||
quickExit(ExitCode::abrupt);
|
||||
}
|
||||
#else
|
||||
#elif !defined(__wasi__)
|
||||
// This works by restoring the system-default handler for the given signal, unblocking the
|
||||
// signal, and re-raising it, in order to get the system default termination behavior (i.e.,
|
||||
// dumping core, or just exiting).
|
||||
@ -398,6 +429,10 @@ void endProcessWithSignal(int signalNum) {
|
||||
invariant(sigprocmask(SIG_UNBLOCK, &unblockSignalMask, nullptr) == 0);
|
||||
|
||||
raise(signalNum);
|
||||
#else
|
||||
// WASI doesn't support signals, just exit with the appropriate code
|
||||
(void)signalNum;
|
||||
quickExit(ExitCode::abrupt);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -410,7 +445,7 @@ void setupSynchronousSignalHandlers() {
|
||||
_set_purecall_handler(myPureCallHandler);
|
||||
_set_invalid_parameter_handler(myInvalidParameterHandler);
|
||||
setWindowsUnhandledExceptionFilter();
|
||||
#else
|
||||
#elif !defined(__wasi__)
|
||||
static constexpr struct {
|
||||
int signal;
|
||||
sigAction_t* function; // signal ignored if nullptr
|
||||
@ -435,6 +470,8 @@ void setupSynchronousSignalHandlers() {
|
||||
#if defined(MONGO_STACKTRACE_CAN_DUMP_ALL_THREADS)
|
||||
setupStackTraceSignalAction(stackTraceSignal());
|
||||
#endif
|
||||
#else
|
||||
// WASI doesn't support signals, so signal setup is a no-op
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -451,7 +488,7 @@ void reportOutOfMemoryErrorAndExit() {
|
||||
}
|
||||
|
||||
void clearSignalMask() {
|
||||
#ifndef _WIN32
|
||||
#if !defined(_WIN32) && !defined(__wasi__)
|
||||
// We need to make sure that all signals are unmasked so signals are handled correctly
|
||||
sigset_t unblockSignalMask;
|
||||
invariant(sigemptyset(&unblockSignalMask) == 0);
|
||||
|
||||
@ -27,7 +27,9 @@
|
||||
* it in the license file.
|
||||
*/
|
||||
|
||||
#ifndef __wasi__
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#include <fmt/format.h>
|
||||
// IWYU pragma: no_include "cxxabi.h"
|
||||
@ -241,6 +243,7 @@ void printMetadata(StackTraceSink& sink, const StackTraceAddressMetadata& meta)
|
||||
}
|
||||
|
||||
void mergeDlInfo(StackTraceAddressMetadata& f) {
|
||||
#ifndef __wasi__
|
||||
if (f.file() && f.symbol())
|
||||
return;
|
||||
Dl_info dli;
|
||||
@ -258,6 +261,10 @@ void mergeDlInfo(StackTraceAddressMetadata& f) {
|
||||
if (!f.symbol() && dli.dli_saddr) {
|
||||
f.symbol().assign(reinterpret_cast<uintptr_t>(dli.dli_saddr), dli.dli_sname);
|
||||
}
|
||||
#else
|
||||
// WASI doesn't support dynamic linking, so we can't get symbol info
|
||||
(void)f;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MONGO_STACKTRACE_BACKEND == MONGO_STACKTRACE_BACKEND_LIBUNWIND
|
||||
|
||||
@ -637,7 +637,8 @@ private:
|
||||
// Find minimum timer resolution of OS
|
||||
Nanoseconds getMinimumTimerResolution() {
|
||||
Nanoseconds minTimerResolution;
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__)
|
||||
#if defined(__linux__) || defined(__FreeBSD__) || defined(__EMSCRIPTEN__) || defined(__wasi__) || \
|
||||
defined(__wasm__) || defined(__wasm32__) || defined(__wasm64__)
|
||||
struct timespec tp;
|
||||
int ret = clock_getres(CLOCK_REALTIME, &tp);
|
||||
if (ret == -1) {
|
||||
|
||||
14
src/third_party/IntelRDFPMathLib20U1/BUILD.bazel
vendored
14
src/third_party/IntelRDFPMathLib20U1/BUILD.bazel
vendored
@ -22,20 +22,30 @@ INTEL_RDFP_MATHLIB_DEFINES = [
|
||||
"DECIMAL_CALL_BY_REFERENCE=0",
|
||||
"DECIMAL_GLOBAL_ROUNDING=0",
|
||||
"DECIMAL_GLOBAL_EXCEPTION_FLAGS=0",
|
||||
"UNCHANGED_BINARY_STATUS_FLAGS=0",
|
||||
"USE_COMPILER_F128_TYPE=0",
|
||||
"USE_COMPILER_F80_TYPE=0",
|
||||
"USE_NATIVE_QUAD_TYPE=0",
|
||||
] + select({
|
||||
# WASI/WASM: treat as Linux-like.
|
||||
# Do NOT define UNCHANGED_BINARY_STATUS_FLAGS so that
|
||||
# BID_OPT_RESTORE_BINARY_FLAGS() becomes a no-op (WASI has no fenv.h).
|
||||
"@platforms//os:wasi": [
|
||||
"LINUX=1",
|
||||
"linux=1",
|
||||
],
|
||||
"@platforms//os:linux": [
|
||||
"LINUX=1",
|
||||
"linux=1",
|
||||
"UNCHANGED_BINARY_STATUS_FLAGS=0",
|
||||
],
|
||||
"@platforms//os:macos": [
|
||||
"LINUX=1",
|
||||
"mach=1",
|
||||
"UNCHANGED_BINARY_STATUS_FLAGS=0",
|
||||
],
|
||||
"//conditions:default": [
|
||||
"UNCHANGED_BINARY_STATUS_FLAGS=0",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}) + select({
|
||||
"@platforms//cpu:s390x": [
|
||||
"s390x=1",
|
||||
|
||||
14
src/third_party/boost/BUILD.bazel
vendored
14
src/third_party/boost/BUILD.bazel
vendored
@ -217,6 +217,10 @@ mongo_cc_library(
|
||||
"libs/thread/src/pthread/once.cpp",
|
||||
"libs/thread/src/pthread/thread.cpp",
|
||||
],
|
||||
"@platforms//os:wasi": [
|
||||
"libs/thread/src/pthread/once.cpp",
|
||||
"libs/thread/src/pthread/thread.cpp",
|
||||
],
|
||||
"@platforms//os:windows": [
|
||||
"libs/thread/src/win32/thread.cpp",
|
||||
"libs/thread/src/win32/thread_primitives.cpp",
|
||||
@ -231,6 +235,9 @@ mongo_cc_library(
|
||||
"@platforms//os:macos": [
|
||||
"libs/thread/src/pthread/once_atomic.cpp",
|
||||
],
|
||||
"@platforms//os:wasi": [
|
||||
"libs/thread/src/pthread/once_atomic.cpp",
|
||||
],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
copts = BOOST_COPTS,
|
||||
@ -242,6 +249,9 @@ mongo_cc_library(
|
||||
"@platforms//os:macos": [
|
||||
"BOOST_THREAD_PTHREAD",
|
||||
],
|
||||
"@platforms//os:wasi": [
|
||||
"BOOST_THREAD_PTHREAD",
|
||||
],
|
||||
"@platforms//os:windows": [
|
||||
"BOOST_THREAD_WIN32",
|
||||
],
|
||||
@ -287,9 +297,7 @@ mongo_cc_library(
|
||||
"libs/log/src/trivial.cpp",
|
||||
] + select({
|
||||
"@platforms//os:windows": [],
|
||||
"//conditions:default": [
|
||||
"libs/log/src/syslog_backend.cpp",
|
||||
],
|
||||
"//conditions:default": ["libs/log/src/syslog_backend.cpp"],
|
||||
}),
|
||||
hdrs = BOOST_LIB_HEADERS,
|
||||
copts = BOOST_COPTS + select({
|
||||
|
||||
12
src/third_party/fmt/BUILD.bazel
vendored
12
src/third_party/fmt/BUILD.bazel
vendored
@ -6,8 +6,11 @@ mongo_cc_library(
|
||||
name = "fmt",
|
||||
srcs = [
|
||||
"dist/src/format.cc",
|
||||
"dist/src/os.cc",
|
||||
],
|
||||
] + select({
|
||||
# os.cc uses POSIX I/O (dup, dup2, pipe) not available in WASI
|
||||
"@platforms//os:wasi": [],
|
||||
"//conditions:default": ["dist/src/os.cc"],
|
||||
}),
|
||||
hdrs = glob([
|
||||
"dist/include/fmt/*.h",
|
||||
]),
|
||||
@ -21,4 +24,9 @@ mongo_cc_library(
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
includes = ["dist/include"],
|
||||
local_defines = select({
|
||||
# Disable POSIX file operations for WASI
|
||||
"@platforms//os:wasi": ["FMT_USE_FCNTL=0"],
|
||||
"//conditions:default": [],
|
||||
}),
|
||||
)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user