Skip to content

Commit 3697c19

Browse files
joyeecheungtargos
authored andcommitted
vm: use internal versions of compileFunction and Script
Instead of using the public versions of the vm APIs internally, use the internal versions so that we can skip unnecessary argument validation. The public versions would need special care to the generation of host-defined options to hit the isolate compilation cache when imporModuleDynamically isn't used, while internally it's almost always used, so this allows us to handle the host-defined options separately. PR-URL: #50137 Refs: #35375 Reviewed-By: Geoffrey Booth <[email protected]> Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]> Reviewed-By: Antoine du Hamel <[email protected]>
1 parent 56bbc30 commit 3697c19

File tree

8 files changed

+268
-186
lines changed

8 files changed

+268
-186
lines changed

‎lib/internal/modules/cjs/loader.js‎

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ const{
5252
SafeMap,
5353
SafeWeakMap,
5454
String,
55+
Symbol,
5556
StringPrototypeCharAt,
5657
StringPrototypeCharCodeAt,
5758
StringPrototypeEndsWith,
@@ -84,7 +85,12 @@ const{
8485
setOwnProperty,
8586
getLazy,
8687
}=require('internal/util');
87-
const{ internalCompileFunction }=require('internal/vm');
88+
const{
89+
internalCompileFunction,
90+
makeContextifyScript,
91+
runScriptInThisContext,
92+
}=require('internal/vm');
93+
8894
constassert=require('internal/assert');
8995
constfs=require('fs');
9096
constpath=require('path');
@@ -1240,7 +1246,6 @@ Module.prototype.require = function(id){
12401246
letresolvedArgv;
12411247
lethasPausedEntry=false;
12421248
/** @type{import('vm').Script} */
1243-
letScript;
12441249

12451250
/**
12461251
* Wraps the given content in a script and runs it in a new context.
@@ -1250,47 +1255,49 @@ let Script;
12501255
* @param{object} codeCache The SEA code cache
12511256
*/
12521257
functionwrapSafe(filename,content,cjsModuleInstance,codeCache){
1258+
consthostDefinedOptionId=Symbol(`cjs:${filename}`);
1259+
asyncfunctionimportModuleDynamically(specifier,_,importAttributes){
1260+
constcascadedLoader=getCascadedLoader();
1261+
returncascadedLoader.import(specifier,normalizeReferrerURL(filename),
1262+
importAttributes);
1263+
}
12531264
if(patched){
1254-
constwrapper=Module.wrap(content);
1255-
if(Script===undefined){
1256-
({ Script }=require('vm'));
1257-
}
1258-
constscript=newScript(wrapper,{
1259-
filename,
1260-
lineOffset: 0,
1261-
importModuleDynamically: async(specifier,_,importAttributes)=>{
1262-
constcascadedLoader=getCascadedLoader();
1263-
returncascadedLoader.import(specifier,normalizeReferrerURL(filename),
1264-
importAttributes);
1265-
},
1266-
});
1265+
constwrapped=Module.wrap(content);
1266+
constscript=makeContextifyScript(
1267+
wrapped,// code
1268+
filename,// filename
1269+
0,// lineOffset
1270+
0,// columnOffset
1271+
undefined,// cachedData
1272+
false,// produceCachedData
1273+
undefined,// parsingContext
1274+
hostDefinedOptionId,// hostDefinedOptionId
1275+
importModuleDynamically,// importModuleDynamically
1276+
);
12671277

12681278
// Cache the source map for the module if present.
12691279
if(script.sourceMapURL){
12701280
maybeCacheSourceMap(filename,content,this,false,undefined,script.sourceMapURL);
12711281
}
12721282

1273-
returnscript.runInThisContext({
1274-
displayErrors: true,
1275-
});
1283+
returnrunScriptInThisContext(script,true,false);
12761284
}
12771285

1286+
constparams=['exports','require','module','__filename','__dirname'];
12781287
try{
1279-
constresult=internalCompileFunction(content,[
1280-
'exports',
1281-
'require',
1282-
'module',
1283-
'__filename',
1284-
'__dirname',
1285-
],{
1286-
filename,
1287-
cachedData: codeCache,
1288-
importModuleDynamically(specifier,_,importAttributes){
1289-
constcascadedLoader=getCascadedLoader();
1290-
returncascadedLoader.import(specifier,normalizeReferrerURL(filename),
1291-
importAttributes);
1292-
},
1293-
});
1288+
constresult=internalCompileFunction(
1289+
content,// code,
1290+
filename,// filename
1291+
0,// lineOffset
1292+
0,// columnOffset,
1293+
codeCache,// cachedData
1294+
false,// produceCachedData
1295+
undefined,// parsingContext
1296+
undefined,// contextExtensions
1297+
params,// params
1298+
hostDefinedOptionId,// hostDefinedOptionId
1299+
importModuleDynamically,// importModuleDynamically
1300+
);
12941301

12951302
// The code cache is used for SEAs only.
12961303
if(codeCache&&

‎lib/internal/modules/esm/translators.js‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const{
1515
StringPrototypeReplaceAll,
1616
StringPrototypeSlice,
1717
StringPrototypeStartsWith,
18+
Symbol,
1819
SyntaxErrorPrototype,
1920
globalThis: { WebAssembly },
2021
}=primordials;
@@ -192,19 +193,29 @@ function enrichCJSError(err, content, filename){
192193
*/
193194
functionloadCJSModule(module,source,url,filename){
194195
letcompiledWrapper;
196+
asyncfunctionimportModuleDynamically(specifier,_,importAttributes){
197+
returnasyncESM.esmLoader.import(specifier,url,importAttributes);
198+
}
195199
try{
196-
compiledWrapper=internalCompileFunction(source,[
197-
'exports',
198-
'require',
199-
'module',
200-
'__filename',
201-
'__dirname',
202-
],{
203-
filename,
204-
importModuleDynamically(specifier,_,importAttributes){
205-
returnasyncESM.esmLoader.import(specifier,url,importAttributes);
206-
},
207-
}).function;
200+
compiledWrapper=internalCompileFunction(
201+
source,// code,
202+
filename,// filename
203+
0,// lineOffset
204+
0,// columnOffset,
205+
undefined,// cachedData
206+
false,// produceCachedData
207+
undefined,// parsingContext
208+
undefined,// contextExtensions
209+
[// params
210+
'exports',
211+
'require',
212+
'module',
213+
'__filename',
214+
'__dirname',
215+
],
216+
Symbol(`cjs:${filename}`),// hostDefinedOptionsId
217+
importModuleDynamically,// importModuleDynamically
218+
).function;
208219
}catch(err){
209220
enrichCJSError(err,source,url);
210221
throwerr;

‎lib/internal/process/execution.js‎

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
const{
4+
Symbol,
45
RegExpPrototypeExec,
56
globalThis,
67
}=primordials;
@@ -25,7 +26,9 @@ const{
2526
emitAfter,
2627
popAsyncContext,
2728
}=require('internal/async_hooks');
28-
29+
const{
30+
makeContextifyScript, runScriptInThisContext,
31+
}=require('internal/vm');
2932
// shouldAbortOnUncaughtToggle is a typed array for faster
3033
// communication with JS.
3134
const{ shouldAbortOnUncaughtToggle }=internalBinding('util');
@@ -53,7 +56,6 @@ function evalModule(source, print){
5356

5457
functionevalScript(name,body,breakFirstLine,print,shouldLoadESM=false){
5558
constCJSModule=require('internal/modules/cjs/loader').Module;
56-
const{ kVmBreakFirstLineSymbol }=require('internal/util');
5759
const{ pathToFileURL }=require('internal/url');
5860

5961
constcwd=tryGetCwd();
@@ -79,16 +81,25 @@ function evalScript(name, body, breakFirstLine, print, shouldLoadESM = false){
7981
`;
8082
globalThis.__filename=name;
8183
RegExpPrototypeExec(/^/,'');// Necessary to reset RegExp statics before user code runs.
82-
constresult=module._compile(script,`${name}-wrapper`)(()=>
83-
require('vm').runInThisContext(body,{
84-
filename: name,
85-
displayErrors: true,
86-
[kVmBreakFirstLineSymbol]: !!breakFirstLine,
87-
importModuleDynamically(specifier,_,importAttributes){
88-
constloader=asyncESM.esmLoader;
89-
returnloader.import(specifier,baseUrl,importAttributes);
90-
},
91-
}));
84+
constresult=module._compile(script,`${name}-wrapper`)(()=>{
85+
consthostDefinedOptionId=Symbol(name);
86+
asyncfunctionimportModuleDynamically(specifier,_,importAttributes){
87+
constloader=asyncESM.esmLoader;
88+
returnloader.import(specifier,baseUrl,importAttributes);
89+
}
90+
constscript=makeContextifyScript(
91+
body,// code
92+
name,// filename,
93+
0,// lineOffset
94+
0,// columnOffset,
95+
undefined,// cachedData
96+
false,// produceCachedData
97+
undefined,// parsingContext
98+
hostDefinedOptionId,// hostDefinedOptionId
99+
importModuleDynamically,// importModuleDynamically
100+
);
101+
returnrunScriptInThisContext(script,true,!!breakFirstLine);
102+
});
92103
if(print){
93104
const{ log }=require('internal/console/global');
94105
log(result);

‎lib/internal/vm.js‎

Lines changed: 69 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,26 @@
11
'use strict';
22

33
const{
4-
ArrayPrototypeForEach,
4+
ReflectApply,
55
Symbol,
66
}=primordials;
77

88
const{
9+
ContextifyScript,
910
compileFunction,
1011
isContext: _isContext,
1112
}=internalBinding('contextify');
13+
const{
14+
runInContext,
15+
}=ContextifyScript.prototype;
1216
const{
1317
default_host_defined_options,
1418
}=internalBinding('symbols');
1519
const{
16-
validateArray,
17-
validateBoolean,
18-
validateBuffer,
1920
validateFunction,
2021
validateObject,
21-
validateString,
22-
validateStringArray,
2322
kValidateObjectAllowArray,
24-
kValidateObjectAllowNullable,
25-
validateInt32,
2623
}=require('internal/validators');
27-
const{
28-
ERR_INVALID_ARG_TYPE,
29-
}=require('internal/errors').codes;
3024

3125
functionisContext(object){
3226
validateObject(object,'object',kValidateObjectAllowArray);
@@ -50,49 +44,20 @@ function getHostDefinedOptionId(importModuleDynamically, filename){
5044
returnSymbol(filename);
5145
}
5246

53-
functioninternalCompileFunction(code,params,options){
54-
validateString(code,'code');
55-
if(params!==undefined){
56-
validateStringArray(params,'params');
57-
}
58-
const{
59-
filename ='',
60-
columnOffset =0,
61-
lineOffset =0,
62-
cachedData =undefined,
63-
produceCachedData =false,
64-
parsingContext =undefined,
65-
contextExtensions =[],
66-
importModuleDynamically,
67-
}=options;
68-
69-
validateString(filename,'options.filename');
70-
validateInt32(columnOffset,'options.columnOffset');
71-
validateInt32(lineOffset,'options.lineOffset');
72-
if(cachedData!==undefined)
73-
validateBuffer(cachedData,'options.cachedData');
74-
validateBoolean(produceCachedData,'options.produceCachedData');
75-
if(parsingContext!==undefined){
76-
if(
77-
typeofparsingContext!=='object'||
78-
parsingContext===null||
79-
!isContext(parsingContext)
80-
){
81-
thrownewERR_INVALID_ARG_TYPE(
82-
'options.parsingContext',
83-
'Context',
84-
parsingContext,
85-
);
86-
}
87-
}
88-
validateArray(contextExtensions,'options.contextExtensions');
89-
ArrayPrototypeForEach(contextExtensions,(extension,i)=>{
90-
constname=`options.contextExtensions[${i}]`;
91-
validateObject(extension,name,kValidateObjectAllowNullable);
47+
functionregisterImportModuleDynamically(referrer,importModuleDynamically){
48+
const{ importModuleDynamicallyWrap }=require('internal/vm/module');
49+
const{ registerModule }=require('internal/modules/esm/utils');
50+
registerModule(referrer,{
51+
__proto__: null,
52+
importModuleDynamically:
53+
importModuleDynamicallyWrap(importModuleDynamically),
9254
});
55+
}
9356

94-
consthostDefinedOptionId=
95-
getHostDefinedOptionId(importModuleDynamically,filename);
57+
functioninternalCompileFunction(
58+
code,filename,lineOffset,columnOffset,
59+
cachedData,produceCachedData,parsingContext,contextExtensions,
60+
params,hostDefinedOptionId,importModuleDynamically){
9661
constresult=compileFunction(
9762
code,
9863
filename,
@@ -119,23 +84,65 @@ function internalCompileFunction(code, params, options){
11984
}
12085

12186
if(importModuleDynamically!==undefined){
122-
validateFunction(importModuleDynamically,
123-
'options.importModuleDynamically');
124-
const{ importModuleDynamicallyWrap }=require('internal/vm/module');
125-
constwrapped=importModuleDynamicallyWrap(importModuleDynamically);
126-
constfunc=result.function;
127-
const{ registerModule }=require('internal/modules/esm/utils');
128-
registerModule(func,{
129-
__proto__: null,
130-
importModuleDynamically: wrapped,
131-
});
87+
registerImportModuleDynamically(result.function,importModuleDynamically);
13288
}
13389

13490
returnresult;
13591
}
13692

93+
functionmakeContextifyScript(code,
94+
filename,
95+
lineOffset,
96+
columnOffset,
97+
cachedData,
98+
produceCachedData,
99+
parsingContext,
100+
hostDefinedOptionId,
101+
importModuleDynamically){
102+
letscript;
103+
// Calling `ReThrow()` on a native TryCatch does not generate a new
104+
// abort-on-uncaught-exception check. A dummy try/catch in JS land
105+
// protects against that.
106+
try{// eslint-disable-line no-useless-catch
107+
script=newContextifyScript(code,
108+
filename,
109+
lineOffset,
110+
columnOffset,
111+
cachedData,
112+
produceCachedData,
113+
parsingContext,
114+
hostDefinedOptionId);
115+
}catch(e){
116+
throwe;/* node-do-not-add-exception-line */
117+
}
118+
119+
if(importModuleDynamically!==undefined){
120+
registerImportModuleDynamically(script,importModuleDynamically);
121+
}
122+
returnscript;
123+
}
124+
125+
// Internal version of vm.Script.prototype.runInThisContext() which skips
126+
// argument validation.
127+
functionrunScriptInThisContext(script,displayErrors,breakOnFirstLine){
128+
returnReflectApply(
129+
runInContext,
130+
script,
131+
[
132+
null,// sandbox - use current context
133+
-1,// timeout
134+
displayErrors,// displayErrors
135+
false,// breakOnSigint
136+
breakOnFirstLine,// breakOnFirstLine
137+
],
138+
);
139+
}
140+
137141
module.exports={
138142
getHostDefinedOptionId,
139143
internalCompileFunction,
140144
isContext,
145+
makeContextifyScript,
146+
registerImportModuleDynamically,
147+
runScriptInThisContext,
141148
};

0 commit comments

Comments
(0)