From af566197b4537eba4f313cb2092f35af91f5f2cf Mon Sep 17 00:00:00 2001 From: Ryan Hyun Choi Date: Mon, 23 May 2022 16:00:36 +0900 Subject: [PATCH] LWNode_Release_220523_f4ad606 Change-Id: Ic06bc6da70f07a0124171e9dfe926bed8e5fb78b Signed-off-by: Ryan Choi --- lib/internal/bootstrap/loaders.prod.js | 339 ++++++++++++++++++ .../code/escargotshim/src/api/stack-trace.cc | 4 +- .../src/api/utils/logger/logger-util.cc | 14 +- node.gyp | 1 + 4 files changed, 350 insertions(+), 8 deletions(-) create mode 100644 lib/internal/bootstrap/loaders.prod.js diff --git a/lib/internal/bootstrap/loaders.prod.js b/lib/internal/bootstrap/loaders.prod.js new file mode 100644 index 0000000..3419b7e --- /dev/null +++ b/lib/internal/bootstrap/loaders.prod.js @@ -0,0 +1,339 @@ +// This file creates the internal module & binding loaders used by built-in +// modules. In contrast, user land modules are loaded using +// lib/internal/modules/cjs/loader.js (CommonJS Modules) or +// lib/internal/modules/esm/* (ES Modules). +// +// This file is compiled and run by node.cc before bootstrap/node.js +// was called, therefore the loaders are bootstraped before we start to +// actually bootstrap Node.js. It creates the following objects: +// +// C++ binding loaders: +// - process.binding(): the legacy C++ binding loader, accessible from user land +// because it is an object attached to the global process object. +// These C++ bindings are created using NODE_BUILTIN_MODULE_CONTEXT_AWARE() +// and have their nm_flags set to NM_F_BUILTIN. We do not make any guarantees +// about the stability of these bindings, but still have to take care of +// compatibility issues caused by them from time to time. +// - process._linkedBinding(): intended to be used by embedders to add +// additional C++ bindings in their applications. These C++ bindings +// can be created using NODE_MODULE_CONTEXT_AWARE_CPP() with the flag +// NM_F_LINKED. +// - internalBinding(): the private internal C++ binding loader, inaccessible +// from user land unless through `require('internal/test/binding')`. +// These C++ bindings are created using NODE_MODULE_CONTEXT_AWARE_INTERNAL() +// and have their nm_flags set to NM_F_INTERNAL. +// +// Internal JavaScript module loader: +// - NativeModule: a minimal module system used to load the JavaScript core +// modules found in lib/**/*.js and deps/**/*.js. All core modules are +// compiled into the node binary via node_javascript.cc generated by js2c.py, +// so they can be loaded faster without the cost of I/O. This class makes the +// lib/internal/*, deps/internal/* modules and internalBinding() available by +// default to core modules, and lets the core modules require itself via +// require('internal/bootstrap/loaders') even when this file is not written in +// CommonJS style. +// +// Other objects: +// - process.moduleLoadList: an array recording the bindings and the modules +// loaded in the process and the order in which they are loaded. + +'use strict'; + +// This file is compiled as if it's wrapped in a function with arguments +// passed by node::RunBootstrapping() +/* global process, getLinkedBinding, getInternalBinding, primordials */ + +const { + Error, + Map, + ObjectCreate, + ObjectDefineProperty, + ObjectKeys, + ObjectPrototypeHasOwnProperty, + ReflectGet, + SafeSet, + String, + TypeError, +} = primordials; + +// Set up process.moduleLoadList. +const moduleLoadList = []; +ObjectDefineProperty(process, 'moduleLoadList', { + value: moduleLoadList, + configurable: true, + enumerable: true, + writable: false +}); + + +// internalBindingWhitelist contains the name of internalBinding modules +// that are whitelisted for access via process.binding()... This is used +// to provide a transition path for modules that are being moved over to +// internalBinding. +const internalBindingWhitelist = new SafeSet([ + 'async_wrap', + 'buffer', + 'cares_wrap', + 'config', + 'constants', + 'contextify', + 'crypto', + 'fs', + 'fs_event_wrap', + 'http_parser', + 'icu', + 'inspector', + 'js_stream', + 'natives', + 'os', + 'pipe_wrap', + 'process_wrap', + 'signal_wrap', + 'spawn_sync', + 'stream_wrap', + 'tcp_wrap', + 'tls_wrap', + 'tty_wrap', + 'udp_wrap', + 'url', + 'util', + 'uv', + 'v8', + 'zlib' +]); + +// @lwnode +function testCodeGenerationFromStringsAllowed() { + // related to: --allow-code-generation-from-strings + try { + eval(); + } catch (error) { + return false; + } + return true; +}; + +const userModuleBlacklist = [ + 'v8', + ...(testCodeGenerationFromStringsAllowed() ? [] : ['vm']), +]; + +// Set up process.binding() and process._linkedBinding(). +{ + const bindingObj = ObjectCreate(null); + + process.binding = function binding(module) { + module = String(module); + // Deprecated specific process.binding() modules, but not all, allow + // selective fallback to internalBinding for the deprecated ones. + if (internalBindingWhitelist.has(module)) { + return internalBinding(module); + } + // eslint-disable-next-line no-restricted-syntax + throw new Error(`No such module: ${module}`); + }; + + process._linkedBinding = function _linkedBinding(module) { + module = String(module); + let mod = bindingObj[module]; + if (typeof mod !== 'object') + mod = bindingObj[module] = getLinkedBinding(module); + return mod; + }; +} + +// Set up internalBinding() in the closure. +let internalBinding; +{ + const bindingObj = ObjectCreate(null); + // eslint-disable-next-line no-global-assign + internalBinding = function internalBinding(module) { + let mod = bindingObj[module]; + if (typeof mod !== 'object') { + mod = bindingObj[module] = getInternalBinding(module); + moduleLoadList.push(`Internal Binding ${module}`); + } + return mod; + }; +} + +const loaderId = 'internal/bootstrap/loaders'; +const { + moduleIds, + compileFunction +} = internalBinding('native_module'); + +const getOwn = (target, property, receiver) => { + return ObjectPrototypeHasOwnProperty(target, property) ? + ReflectGet(target, property, receiver) : + undefined; +}; + +/** + * An internal abstraction for the built-in JavaScript modules of Node.js. + * Be careful not to expose this to user land unless --expose-internals is + * used, in which case there is no compatibility guarantee about this class. + */ +class NativeModule { + /** + * A map from the module IDs to the module instances. + * @type {Map} + */ + static map = new Map(moduleIds.map((id) => [id, new NativeModule(id)])); + + constructor(id) { + this.filename = `${id}.js`; + this.id = id; + // this.canBeRequiredByUsers = !id.startsWith('internal/'); + // @lwnode + this.canBeRequiredByUsers = + !id.startsWith("internal/") && !userModuleBlacklist.includes(id); + + // The CJS exports object of the module. + this.exports = {}; + // States used to work around circular dependencies. + this.loaded = false; + this.loading = false; + + // The following properties are used by the ESM implementation and only + // initialized when the native module is loaded by users. + /** + * The C++ ModuleWrap binding used to interface with the ESM implementation. + * @type {ModuleWrap|undefined} + */ + this.module = undefined; + /** + * Exported names for the ESM imports. + * @type {string[]|undefined} + */ + this.exportKeys = undefined; + } + + // To be called during pre-execution when --expose-internals is on. + // Enables the user-land module loader to access internal modules. + static exposeInternals() { + for (const [id, mod] of NativeModule.map) { + // Do not expose this to user land even with --expose-internals. + if (id !== loaderId) { + mod.canBeRequiredByUsers = true; + } + } + } + + static exists(id) { + return NativeModule.map.has(id); + } + + static canBeRequiredByUsers(id) { + const mod = NativeModule.map.get(id); + return mod && mod.canBeRequiredByUsers; + } + + // Used by user-land module loaders to compile and load builtins. + compileForPublicLoader() { + if (!this.canBeRequiredByUsers) { + // No code because this is an assertion against bugs + // eslint-disable-next-line no-restricted-syntax + throw new Error(`Should not compile ${this.id} for public use`); + } + this.compileForInternalLoader(); + if (!this.exportKeys) { + // When using --expose-internals, we do not want to reflect the named + // exports from core modules as this can trigger unnecessary getters. + const internal = this.id.startsWith('internal/'); + this.exportKeys = internal ? [] : ObjectKeys(this.exports); + } + this.getESMFacade(); + this.syncExports(); + return this.exports; + } + + getESMFacade() { + if (this.module) return this.module; + const { ModuleWrap } = internalBinding('module_wrap'); + const url = `node:${this.id}`; + const nativeModule = this; + this.module = new ModuleWrap( + url, undefined, [...this.exportKeys, 'default'], + function() { + nativeModule.syncExports(); + this.setExport('default', nativeModule.exports); + }); + // Ensure immediate sync execution to capture exports now + this.module.instantiate(); + this.module.evaluate(-1, false); + return this.module; + } + + // Provide named exports for all builtin libraries so that the libraries + // may be imported in a nicer way for ESM users. The default export is left + // as the entire namespace (module.exports) and updates when this function is + // called so that APMs and other behavior are supported. + syncExports() { + const names = this.exportKeys; + if (this.module) { + for (let i = 0; i < names.length; i++) { + const exportName = names[i]; + if (exportName === 'default') continue; + this.module.setExport(exportName, + getOwn(this.exports, exportName, this.exports)); + } + } + } + + compileForInternalLoader() { + if (this.loaded || this.loading) { + return this.exports; + } + + const id = this.id; + this.loading = true; + + try { + const requireFn = this.id.startsWith('internal/deps/') ? + requireWithFallbackInDeps : nativeModuleRequire; + + const fn = compileFunction(id); + fn(this.exports, requireFn, this, process, internalBinding, primordials); + + this.loaded = true; + } finally { + this.loading = false; + } + + moduleLoadList.push(`NativeModule ${id}`); + return this.exports; + } +} + +// Think of this as module.exports in this file even though it is not +// written in CommonJS style. +const loaderExports = { + internalBinding, + NativeModule, + require: nativeModuleRequire +}; + +function nativeModuleRequire(id) { + if (id === loaderId) { + return loaderExports; + } + + const mod = NativeModule.map.get(id); + // Can't load the internal errors module from here, have to use a raw error. + // eslint-disable-next-line no-restricted-syntax + if (!mod) throw new TypeError(`Missing internal module '${id}'`); + return mod.compileForInternalLoader(); +} + +// Allow internal modules from dependencies to require +// other modules from dependencies by providing fallbacks. +function requireWithFallbackInDeps(request) { + if (!NativeModule.map.has(request)) { + request = `internal/deps/${request}`; + } + return nativeModuleRequire(request); +} + +// Pass the exports back to C++ land for C++ internals to use. +return loaderExports; diff --git a/lwnode/code/escargotshim/src/api/stack-trace.cc b/lwnode/code/escargotshim/src/api/stack-trace.cc index 4333a1a..509bb13 100644 --- a/lwnode/code/escargotshim/src/api/stack-trace.cc +++ b/lwnode/code/escargotshim/src/api/stack-trace.cc @@ -138,9 +138,7 @@ ValueRef* StackTrace::captureStackTraceCallback(ExecutionStateRef* state, int stacktraceStartIdx = 1; double stackTraceLimit = 0; - if (!getStackTraceLimit(state, stackTraceLimit)) { - stackTraceVector = nullptr; - } else { + if (getStackTraceLimit(state, stackTraceLimit)) { size_t maxPrintStackSize = std::min(stackTraceLimit, (double)stackTraceData.size()); for (size_t i = 0; i < maxPrintStackSize; i++) { diff --git a/lwnode/code/escargotshim/src/api/utils/logger/logger-util.cc b/lwnode/code/escargotshim/src/api/utils/logger/logger-util.cc index ba4872f..307ccbb 100644 --- a/lwnode/code/escargotshim/src/api/utils/logger/logger-util.cc +++ b/lwnode/code/escargotshim/src/api/utils/logger/logger-util.cc @@ -24,13 +24,17 @@ #include "api/global.h" std::string getPrettyFunctionName(const std::string fullname) { - std::smatch match; - const std::regex re(R"(([\w\:~]+)\()"); + try { + std::smatch match; + const std::regex re(R"(([\w\:~]+)\()"); - if (std::regex_search(fullname, match, re) && match.size() > 1) { - return match.str(1); + if (std::regex_search(fullname, match, re) && match.size() > 1) { + return match.str(1); + } + return ""; + } catch (std::regex_error& e) { + return ""; } - return ""; } std::string createCodeLocation(const char* functionName, diff --git a/node.gyp b/node.gyp index 0889978..6995d4e 100644 --- a/node.gyp +++ b/node.gyp @@ -30,6 +30,7 @@ 'library_files': [ 'lib/internal/bootstrap/environment.js', 'lib/internal/bootstrap/loaders.js', + # 'lib/internal/bootstrap/loaders.prod.js', 'lib/internal/bootstrap/node.js', 'lib/internal/bootstrap/pre_execution.js', 'lib/internal/bootstrap/switches/does_own_process_state.js', -- 2.34.1