summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUjjwal Sharma <usharma1998@gmail.com>2018-11-07 22:17:09 +0530
committerUjjwal Sharma <usharma1998@gmail.com>2019-02-19 20:58:37 +0530
commit5f8ccecaa2e44c4a04db95ccd278a7078c14dd77 (patch)
treee56a5d9bff97dc73c5898f637d19e78627762529
parentd345b0dc128d99afc8476f58ed5546b43d52d30a (diff)
downloadnode-new-5f8ccecaa2e44c4a04db95ccd278a7078c14dd77.tar.gz
module: revert module._compile to original state if module is patched
PR-URL: https://github.com/nodejs/node/pull/21573 Fixes: https://github.com/nodejs/node/issues/17396 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: Gus Caplan <me@gus.host> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net>
-rw-r--r--lib/assert.js11
-rw-r--r--lib/internal/modules/cjs/helpers.js11
-rw-r--r--lib/internal/modules/cjs/loader.js100
-rw-r--r--src/env-inl.h3
-rw-r--r--src/env.cc11
-rw-r--r--src/env.h10
-rw-r--r--src/module_wrap.cc2
-rw-r--r--src/module_wrap.h1
-rw-r--r--src/node_contextify.cc55
-rw-r--r--src/node_contextify.h2
-rw-r--r--test/fixtures/cjs-module-wrap.js9
-rw-r--r--test/parallel/test-module-wrap.js9
-rw-r--r--test/parallel/test-v8-coverage.js26
13 files changed, 201 insertions, 49 deletions
diff --git a/lib/assert.js b/lib/assert.js
index 013ae6e674..94e9406393 100644
--- a/lib/assert.js
+++ b/lib/assert.js
@@ -39,7 +39,6 @@ let isDeepEqual;
let isDeepStrictEqual;
let parseExpressionAt;
let findNodeAround;
-let columnOffset = 0;
let decoder;
function lazyLoadComparison() {
@@ -256,16 +255,6 @@ function getErrMessage(message, fn) {
const line = call.getLineNumber() - 1;
let column = call.getColumnNumber() - 1;
- // Line number one reports the wrong column due to being wrapped in a
- // function. Remove that offset to get the actual call.
- if (line === 0) {
- if (columnOffset === 0) {
- const { wrapper } = require('internal/modules/cjs/loader');
- columnOffset = wrapper[0].length;
- }
- column -= columnOffset;
- }
-
const identifier = `${filename}${line}${column}`;
if (errorCache.has(identifier)) {
diff --git a/lib/internal/modules/cjs/helpers.js b/lib/internal/modules/cjs/helpers.js
index 1a2a91c509..2e5290b452 100644
--- a/lib/internal/modules/cjs/helpers.js
+++ b/lib/internal/modules/cjs/helpers.js
@@ -1,6 +1,9 @@
'use strict';
const { validateString } = require('internal/validators');
+const path = require('path');
+const { pathToFileURL } = require('internal/url');
+const { URL } = require('url');
const {
CHAR_LINE_FEED,
@@ -145,10 +148,18 @@ function addBuiltinLibsToObject(object) {
});
}
+function normalizeReferrerURL(referrer) {
+ if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
+ return pathToFileURL(referrer).href;
+ }
+ return new URL(referrer).href;
+}
+
module.exports = exports = {
addBuiltinLibsToObject,
builtinLibs,
makeRequireFunction,
+ normalizeReferrerURL,
requireDepth: 0,
stripBOM,
stripShebang
diff --git a/lib/internal/modules/cjs/loader.js b/lib/internal/modules/cjs/loader.js
index f1c98529b0..e22294f337 100644
--- a/lib/internal/modules/cjs/loader.js
+++ b/lib/internal/modules/cjs/loader.js
@@ -29,7 +29,6 @@ const assert = require('internal/assert');
const fs = require('fs');
const internalFS = require('internal/fs/utils');
const path = require('path');
-const { URL } = require('url');
const {
internalModuleReadJSON,
internalModuleStat
@@ -37,6 +36,7 @@ const {
const { safeGetenv } = internalBinding('credentials');
const {
makeRequireFunction,
+ normalizeReferrerURL,
requireDepth,
stripBOM,
stripShebang
@@ -48,6 +48,7 @@ const experimentalModules = getOptionValue('--experimental-modules');
const manifest = getOptionValue('--experimental-policy') ?
require('internal/process/policy').manifest :
null;
+const { compileFunction } = internalBinding('contextify');
const {
ERR_INVALID_ARG_VALUE,
@@ -129,15 +130,52 @@ Module._extensions = Object.create(null);
var modulePaths = [];
Module.globalPaths = [];
-Module.wrap = function(script) {
+let patched = false;
+
+// eslint-disable-next-line func-style
+let wrap = function(script) {
return Module.wrapper[0] + script + Module.wrapper[1];
};
-Module.wrapper = [
+const wrapper = [
'(function (exports, require, module, __filename, __dirname) { ',
'\n});'
];
+let wrapperProxy = new Proxy(wrapper, {
+ set(target, property, value, receiver) {
+ patched = true;
+ return Reflect.set(target, property, value, receiver);
+ },
+
+ defineProperty(target, property, descriptor) {
+ patched = true;
+ return Object.defineProperty(target, property, descriptor);
+ }
+});
+
+Object.defineProperty(Module, 'wrap', {
+ get() {
+ return wrap;
+ },
+
+ set(value) {
+ patched = true;
+ wrap = value;
+ }
+});
+
+Object.defineProperty(Module, 'wrapper', {
+ get() {
+ return wrapperProxy;
+ },
+
+ set(value) {
+ patched = true;
+ wrapperProxy = value;
+ }
+});
+
const debug = util.debuglog('module');
Module._debug = util.deprecate(debug, 'Module._debug is deprecated.',
@@ -680,13 +718,6 @@ Module.prototype.require = function(id) {
// (needed for setting breakpoint when called with --inspect-brk)
var resolvedArgv;
-function normalizeReferrerURL(referrer) {
- if (typeof referrer === 'string' && path.isAbsolute(referrer)) {
- return pathToFileURL(referrer).href;
- }
- return new URL(referrer).href;
-}
-
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
@@ -700,13 +731,48 @@ Module.prototype._compile = function(content, filename) {
content = stripShebang(content);
- const compiledWrapper = vm.compileFunction(content, [
- 'exports',
- 'require',
- 'module',
- '__filename',
- '__dirname',
- ], { filename });
+ let compiledWrapper;
+ if (patched) {
+ const wrapper = Module.wrap(content);
+ compiledWrapper = vm.runInThisContext(wrapper, {
+ filename,
+ lineOffset: 0,
+ displayErrors: true,
+ importModuleDynamically: experimentalModules ? async (specifier) => {
+ if (asyncESM === undefined) lazyLoadESM();
+ const loader = await asyncESM.loaderPromise;
+ return loader.import(specifier, normalizeReferrerURL(filename));
+ } : undefined,
+ });
+ } else {
+ compiledWrapper = compileFunction(
+ content,
+ filename,
+ 0,
+ 0,
+ undefined,
+ false,
+ undefined,
+ [],
+ [
+ 'exports',
+ 'require',
+ 'module',
+ '__filename',
+ '__dirname',
+ ]
+ );
+ if (experimentalModules) {
+ const { callbackMap } = internalBinding('module_wrap');
+ callbackMap.set(compiledWrapper, {
+ importModuleDynamically: async (specifier) => {
+ if (asyncESM === undefined) lazyLoadESM();
+ const loader = await asyncESM.loaderPromise;
+ return loader.import(specifier, normalizeReferrerURL(filename));
+ }
+ });
+ }
+ }
var inspectorWrapper = null;
if (process._breakFirstLine && process._eval == null) {
diff --git a/src/env-inl.h b/src/env-inl.h
index b115f9686b..47331a6195 100644
--- a/src/env-inl.h
+++ b/src/env-inl.h
@@ -461,6 +461,9 @@ inline uint32_t Environment::get_next_module_id() {
inline uint32_t Environment::get_next_script_id() {
return script_id_counter_++;
}
+inline uint32_t Environment::get_next_function_id() {
+ return function_id_counter_++;
+}
Environment::ShouldNotAbortOnUncaughtScope::ShouldNotAbortOnUncaughtScope(
Environment* env)
diff --git a/src/env.cc b/src/env.cc
index ce2911251e..63c5d65001 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -252,6 +252,11 @@ Environment::Environment(IsolateData* isolate_data,
isolate()->SetPromiseRejectCallback(task_queue::PromiseRejectCallback);
}
+CompileFnEntry::CompileFnEntry(Environment* env, uint32_t id)
+ : env(env), id(id) {
+ env->compile_fn_entries.insert(this);
+}
+
Environment::~Environment() {
isolate()->GetHeapProfiler()->RemoveBuildEmbedderGraphCallback(
BuildEmbedderGraph, this);
@@ -260,6 +265,12 @@ Environment::~Environment() {
// CleanupHandles() should have removed all of them.
CHECK(file_handle_read_wrap_freelist_.empty());
+ // dispose the Persistent references to the compileFunction
+ // wrappers used in the dynamic import callback
+ for (auto& entry : compile_fn_entries) {
+ delete entry;
+ }
+
HandleScope handle_scope(isolate());
#if HAVE_INSPECTOR
diff --git a/src/env.h b/src/env.h
index 20b2ca3841..26d448c450 100644
--- a/src/env.h
+++ b/src/env.h
@@ -450,6 +450,12 @@ struct ContextInfo {
bool is_default = false;
};
+struct CompileFnEntry {
+ Environment* env;
+ uint32_t id;
+ CompileFnEntry(Environment* env, uint32_t id);
+};
+
// Listing the AsyncWrap provider types first enables us to cast directly
// from a provider type to a debug category.
#define DEBUG_CATEGORY_NAMES(V) \
@@ -720,9 +726,12 @@ class Environment {
std::unordered_map<uint32_t, loader::ModuleWrap*> id_to_module_map;
std::unordered_map<uint32_t, contextify::ContextifyScript*>
id_to_script_map;
+ std::unordered_set<CompileFnEntry*> compile_fn_entries;
+ std::unordered_map<uint32_t, Persistent<v8::Function>> id_to_function_map;
inline uint32_t get_next_module_id();
inline uint32_t get_next_script_id();
+ inline uint32_t get_next_function_id();
std::unordered_map<std::string, const loader::PackageConfig>
package_json_cache;
@@ -1006,6 +1015,7 @@ class Environment {
uint32_t module_id_counter_ = 0;
uint32_t script_id_counter_ = 0;
+ uint32_t function_id_counter_ = 0;
AliasedBuffer<uint32_t, v8::Uint32Array> should_abort_on_uncaught_toggle_;
int should_not_abort_scope_counter_ = 0;
diff --git a/src/module_wrap.cc b/src/module_wrap.cc
index 4414e874ff..f642440bff 100644
--- a/src/module_wrap.cc
+++ b/src/module_wrap.cc
@@ -752,6 +752,8 @@ static MaybeLocal<Promise> ImportModuleDynamically(
} else if (type == ScriptType::kModule) {
ModuleWrap* wrap = ModuleWrap::GetFromID(env, id);
object = wrap->object();
+ } else if (type == ScriptType::kFunction) {
+ object = env->id_to_function_map.find(id)->second.Get(iso);
} else {
UNREACHABLE();
}
diff --git a/src/module_wrap.h b/src/module_wrap.h
index 372e02bc5d..dc34685fed 100644
--- a/src/module_wrap.h
+++ b/src/module_wrap.h
@@ -20,6 +20,7 @@ enum PackageMainCheck : bool {
enum ScriptType : int {
kScript,
kModule,
+ kFunction,
};
enum HostDefinedOptions : int {
diff --git a/src/node_contextify.cc b/src/node_contextify.cc
index 343342408b..61f60576d2 100644
--- a/src/node_contextify.cc
+++ b/src/node_contextify.cc
@@ -285,6 +285,15 @@ void ContextifyContext::WeakCallback(
delete context;
}
+void ContextifyContext::WeakCallbackCompileFn(
+ const WeakCallbackInfo<CompileFnEntry>& data) {
+ CompileFnEntry* entry = data.GetParameter();
+ if (entry->env->compile_fn_entries.erase(entry) != 0) {
+ entry->env->id_to_function_map.erase(entry->id);
+ delete entry;
+ }
+}
+
// static
ContextifyContext* ContextifyContext::ContextFromContextifiedSandbox(
Environment* env,
@@ -1027,7 +1036,30 @@ void ContextifyContext::CompileFunction(
data + cached_data_buf->ByteOffset(), cached_data_buf->ByteLength());
}
- ScriptOrigin origin(filename, line_offset, column_offset, True(isolate));
+ // Get the function id
+ uint32_t id = env->get_next_function_id();
+
+ // Set host_defined_options
+ Local<PrimitiveArray> host_defined_options =
+ PrimitiveArray::New(isolate, loader::HostDefinedOptions::kLength);
+ host_defined_options->Set(
+ isolate,
+ loader::HostDefinedOptions::kType,
+ Number::New(isolate, loader::ScriptType::kFunction));
+ host_defined_options->Set(
+ isolate, loader::HostDefinedOptions::kID, Number::New(isolate, id));
+
+ ScriptOrigin origin(filename,
+ line_offset, // line offset
+ column_offset, // column offset
+ True(isolate), // is cross origin
+ Local<Integer>(), // script id
+ Local<Value>(), // source map URL
+ False(isolate), // is opaque (?)
+ False(isolate), // is WASM
+ False(isolate), // is ES Module
+ host_defined_options);
+
ScriptCompiler::Source source(code, origin, cached_data);
ScriptCompiler::CompileOptions options;
if (source.GetCachedData() == nullptr) {
@@ -1061,38 +1093,45 @@ void ContextifyContext::CompileFunction(
}
}
- MaybeLocal<Function> maybe_fun = ScriptCompiler::CompileFunctionInContext(
+ MaybeLocal<Function> maybe_fn = ScriptCompiler::CompileFunctionInContext(
parsing_context, &source, params.size(), params.data(),
context_extensions.size(), context_extensions.data(), options);
- Local<Function> fun;
- if (maybe_fun.IsEmpty() || !maybe_fun.ToLocal(&fun)) {
+ if (maybe_fn.IsEmpty()) {
DecorateErrorStack(env, try_catch);
try_catch.ReThrow();
return;
}
+ Local<Function> fn = maybe_fn.ToLocalChecked();
+ env->id_to_function_map.emplace(std::piecewise_construct,
+ std::make_tuple(id),
+ std::make_tuple(isolate, fn));
+ CompileFnEntry* gc_entry = new CompileFnEntry(env, id);
+ env->id_to_function_map[id].SetWeak(gc_entry,
+ WeakCallbackCompileFn,
+ v8::WeakCallbackType::kParameter);
if (produce_cached_data) {
const std::unique_ptr<ScriptCompiler::CachedData> cached_data(
- ScriptCompiler::CreateCodeCacheForFunction(fun));
+ ScriptCompiler::CreateCodeCacheForFunction(fn));
bool cached_data_produced = cached_data != nullptr;
if (cached_data_produced) {
MaybeLocal<Object> buf = Buffer::Copy(
env,
reinterpret_cast<const char*>(cached_data->data),
cached_data->length);
- if (fun->Set(
+ if (fn->Set(
parsing_context,
env->cached_data_string(),
buf.ToLocalChecked()).IsNothing()) return;
}
- if (fun->Set(
+ if (fn->Set(
parsing_context,
env->cached_data_produced_string(),
Boolean::New(isolate, cached_data_produced)).IsNothing()) return;
}
- args.GetReturnValue().Set(fun);
+ args.GetReturnValue().Set(fn);
}
diff --git a/src/node_contextify.h b/src/node_contextify.h
index 631671cbf1..4186e5190f 100644
--- a/src/node_contextify.h
+++ b/src/node_contextify.h
@@ -61,6 +61,8 @@ class ContextifyContext {
const v8::FunctionCallbackInfo<v8::Value>& args);
static void WeakCallback(
const v8::WeakCallbackInfo<ContextifyContext>& data);
+ static void WeakCallbackCompileFn(
+ const v8::WeakCallbackInfo<CompileFnEntry>& data);
static void PropertyGetterCallback(
v8::Local<v8::Name> property,
const v8::PropertyCallbackInfo<v8::Value>& args);
diff --git a/test/fixtures/cjs-module-wrap.js b/test/fixtures/cjs-module-wrap.js
new file mode 100644
index 0000000000..2e11cc1a3b
--- /dev/null
+++ b/test/fixtures/cjs-module-wrap.js
@@ -0,0 +1,9 @@
+const assert = require('assert');
+const m = require('module');
+
+global.mwc = 0;
+m.wrapper[0] += 'global.mwc = (global.mwc || 0 ) + 1;';
+
+require('./not-main-module.js');
+assert.strictEqual(mwc, 1);
+delete global.mwc;
diff --git a/test/parallel/test-module-wrap.js b/test/parallel/test-module-wrap.js
new file mode 100644
index 0000000000..639300cb6a
--- /dev/null
+++ b/test/parallel/test-module-wrap.js
@@ -0,0 +1,9 @@
+'use strict';
+require('../common');
+const fixtures = require('../common/fixtures');
+const { execFileSync } = require('child_process');
+
+const cjsModuleWrapTest = fixtures.path('cjs-module-wrap.js');
+const node = process.argv[0];
+
+execFileSync(node, [cjsModuleWrapTest], { stdio: 'pipe' });
diff --git a/test/parallel/test-v8-coverage.js b/test/parallel/test-v8-coverage.js
index 3db8b81d55..5be68bf4b7 100644
--- a/test/parallel/test-v8-coverage.js
+++ b/test/parallel/test-v8-coverage.js
@@ -27,9 +27,9 @@ function nextdir() {
const fixtureCoverage = getFixtureCoverage('basic.js', coverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[1].count, 0);
}
// Outputs coverage when process.exit(1) exits process.
@@ -43,9 +43,9 @@ function nextdir() {
const fixtureCoverage = getFixtureCoverage('exit-1.js', coverageDirectory);
assert.ok(fixtureCoverage, 'coverage not found for file');
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[1].count, 0);
}
// Outputs coverage when process.kill(process.pid, "SIGINT"); exits process.
@@ -61,9 +61,9 @@ function nextdir() {
const fixtureCoverage = getFixtureCoverage('sigint.js', coverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[1].count, 0);
}
// outputs coverage from subprocess.
@@ -78,9 +78,9 @@ function nextdir() {
coverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[2].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[2].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
}
// outputs coverage from worker.
@@ -95,9 +95,9 @@ function nextdir() {
coverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[2].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[2].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
}
// Does not output coverage if NODE_V8_COVERAGE is empty.
@@ -125,7 +125,7 @@ function nextdir() {
coverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[0].count, 1);
}
// Outputs coverage when the coverage directory is not absolute.
@@ -144,9 +144,9 @@ function nextdir() {
absoluteCoverageDirectory);
assert.ok(fixtureCoverage);
// first branch executed.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[0].count, 1);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[0].count, 1);
// second branch did not execute.
- assert.strictEqual(fixtureCoverage.functions[1].ranges[1].count, 0);
+ assert.strictEqual(fixtureCoverage.functions[0].ranges[1].count, 0);
}
// Extracts the coverage object for a given fixture name.