| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 132a5c1 commit b07ad39
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -114,7 +114,7 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE | |||
| 114 | 114 | // but this gets called again from `defaultLoad`/`defaultLoadSync`. | |
| 115 | 115 | if (getOptionValue('--experimental-detect-module')) { | |
| 116 | 116 | const format = source ? | |
| 117 | - (containsModuleSyntax(`${source}`, fileURLToPath(url)) ? 'module' : 'commonjs') : | ||
| 117 | + (containsModuleSyntax(`${source}`, fileURLToPath(url), url) ? 'module' : 'commonjs') : | ||
| 118 | 118 | null; | |
| 119 | 119 | if (format === 'module') { | |
| 120 | 120 | // This module has a .js extension, a package.json with no `type` field, and ESM syntax. | |
@@ -158,7 +158,7 @@ function getFileProtocolModuleFormat(url, context = { __proto__: null }, ignoreE | |||
| 158 | 158 | if (!source) { return null; } | |
| 159 | 159 | const format = getFormatOfExtensionlessFile(url); | |
| 160 | 160 | if (format === 'module') { | |
| 161 | - return containsModuleSyntax(`${source}`, fileURLToPath(url)) ? 'module' : 'commonjs'; | ||
| 161 | + return containsModuleSyntax(`${source}`, fileURLToPath(url), url) ? 'module' : 'commonjs'; | ||
| 162 | 162 | } | |
| 163 | 163 | return format; | |
| 164 | 164 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,15 +19,6 @@ const { | |||
| 19 | 19 | } = require('internal/errors').codes; | |
| 20 | 20 | const { BuiltinModule } = require('internal/bootstrap/realm'); | |
| 21 | 21 | ||
| 22 | - const { | ||
| 23 | - shouldRetryAsESM: contextifyShouldRetryAsESM, | ||
| 24 | - constants: { | ||
| 25 | - syntaxDetectionErrors: { | ||
| 26 | - esmSyntaxErrorMessages, | ||
| 27 | - throwsOnlyInCommonJSErrorMessages, | ||
| 28 | - }, | ||
| 29 | - }, | ||
| 30 | - } = internalBinding('contextify'); | ||
| 31 | 22 | const { validateString } = require('internal/validators'); | |
| 32 | 23 | const fs = require('fs'); // Import all of `fs` so that it can be monkey-patched. | |
| 33 | 24 | const internalFS = require('internal/fs/utils'); | |
@@ -338,30 +329,6 @@ function urlToFilename(url) { | |||
| 338 | 329 | return url; | |
| 339 | 330 | } | |
| 340 | 331 | ||
| 341 | - let esmSyntaxErrorMessagesSet; // Declared lazily in shouldRetryAsESM | ||
| 342 | - let throwsOnlyInCommonJSErrorMessagesSet; // Declared lazily in shouldRetryAsESM | ||
| 343 | - /** | ||
| 344 | - * After an attempt to parse a module as CommonJS throws an error, should we try again as ESM? | ||
| 345 | - * We only want to try again as ESM if the error is due to syntax that is only valid in ESM; and if the CommonJS parse | ||
| 346 | - * throws on an error that would not have been a syntax error in ESM (like via top-level `await` or a lexical | ||
| 347 | - * redeclaration of one of the CommonJS variables) then we need to parse again to see if it would have thrown in ESM. | ||
| 348 | - * @param {string} errorMessage The string message thrown by V8 when attempting to parse as CommonJS | ||
| 349 | - * @param {string} source Module contents | ||
| 350 | - */ | ||
| 351 | - function shouldRetryAsESM(errorMessage, source) { | ||
| 352 | - esmSyntaxErrorMessagesSet ??= new SafeSet(esmSyntaxErrorMessages); | ||
| 353 | - if (esmSyntaxErrorMessagesSet.has(errorMessage)) { | ||
| 354 | - return true; | ||
| 355 | - } | ||
| 356 | - | ||
| 357 | - throwsOnlyInCommonJSErrorMessagesSet ??= new SafeSet(throwsOnlyInCommonJSErrorMessages); | ||
| 358 | - if (throwsOnlyInCommonJSErrorMessagesSet.has(errorMessage)) { | ||
| 359 | - return /** @type {boolean} */(contextifyShouldRetryAsESM(source)); | ||
| 360 | - } | ||
| 361 | - | ||
| 362 | - return false; | ||
| 363 | - } | ||
| 364 | - | ||
| 365 | 332 | ||
| 366 | 333 | // Whether we have started executing any user-provided CJS code. | |
| 367 | 334 | // This is set right before we call the wrapped CJS code (not after, | |
@@ -396,7 +363,6 @@ module.exports = { | |||
| 396 | 363 | loadBuiltinModule, | |
| 397 | 364 | makeRequireFunction, | |
| 398 | 365 | normalizeReferrerURL, | |
| 399 | - shouldRetryAsESM, | ||
| 400 | 366 | stripBOM, | |
| 401 | 367 | toRealPath, | |
| 402 | 368 | hasStartedUserCJSExecution() { | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,7 +1,9 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | 2 | ||
| 3 | 3 | const { | |
| 4 | + ObjectGetPrototypeOf, | ||
| 4 | 5 | StringPrototypeEndsWith, | |
| 6 | + SyntaxErrorPrototype, | ||
| 5 | 7 | } = primordials; | |
| 6 | 8 | ||
| 7 | 9 | const { getOptionValue } = require('internal/options'); | |
@@ -155,27 +157,29 @@ function runEntryPointWithESMLoader(callback) { | |||
| 155 | 157 | function executeUserEntryPoint(main = process.argv[1]) { | |
| 156 | 158 | const resolvedMain = resolveMainPath(main); | |
| 157 | 159 | const useESMLoader = shouldUseESMLoader(resolvedMain); | |
| 158 | - | ||
| 160 | + let mainURL; | ||
| 159 | 161 | // Unless we know we should use the ESM loader to handle the entry point per the checks in `shouldUseESMLoader`, first | |
| 160 | 162 | // try to run the entry point via the CommonJS loader; and if that fails under certain conditions, retry as ESM. | |
| 161 | 163 | let retryAsESM = false; | |
| 162 | 164 | if (!useESMLoader) { | |
| 163 | 165 | const cjsLoader = require('internal/modules/cjs/loader'); | |
| 164 | 166 | const { Module } = cjsLoader; | |
| 165 | 167 | if (getOptionValue('--experimental-detect-module')) { | |
| 168 | + // TODO(joyeecheung): handle this in the CJS loader. Don't try-catch here. | ||
| 166 | 169 | try { | |
| 167 | 170 | // Module._load is the monkey-patchable CJS module loader. | |
| 168 | 171 | Module._load(main, null, true); | |
| 169 | 172 | } catch (error) { | |
| 170 | - const source = cjsLoader.entryPointSource; | ||
| 171 | - const { shouldRetryAsESM } = require('internal/modules/helpers'); | ||
| 172 | - retryAsESM = shouldRetryAsESM(error.message, source); | ||
| 173 | - // In case the entry point is a large file, such as a bundle, | ||
| 174 | - // ensure no further references can prevent it being garbage-collected. | ||
| 175 | - cjsLoader.entryPointSource = undefined; | ||
| 173 | + if (error != null && ObjectGetPrototypeOf(error) === SyntaxErrorPrototype) { | ||
| 174 | + const { shouldRetryAsESM } = internalBinding('contextify'); | ||
| 175 | + const mainPath = resolvedMain || main; | ||
| 176 | + mainURL = pathToFileURL(mainPath).href; | ||
| 177 | + retryAsESM = shouldRetryAsESM(error.message, cjsLoader.entryPointSource, mainURL); | ||
| 178 | + // In case the entry point is a large file, such as a bundle, | ||
| 179 | + // ensure no further references can prevent it being garbage-collected. | ||
| 180 | + cjsLoader.entryPointSource = undefined; | ||
| 181 | + } | ||
| 176 | 182 | if (!retryAsESM) { | |
| 177 | - const { enrichCJSError } = require('internal/modules/esm/translators'); | ||
| 178 | - enrichCJSError(error, source, resolvedMain); | ||
| 179 | 183 | throw error; | |
| 180 | 184 | } | |
| 181 | 185 | } | |
@@ -186,7 +190,9 @@ function executeUserEntryPoint(main = process.argv[1]) { | |||
| 186 | 190 | ||
| 187 | 191 | if (useESMLoader || retryAsESM) { | |
| 188 | 192 | const mainPath = resolvedMain || main; | |
| 189 | - const mainURL = pathToFileURL(mainPath).href; | ||
| 193 | + if (mainURL === undefined) { | ||
| 194 | + mainURL = pathToFileURL(mainPath).href; | ||
| 195 | + } | ||
| 190 | 196 | ||
| 191 | 197 | runEntryPointWithESMLoader((cascadedLoader) => { | |
| 192 | 198 | // Note that if the graph contains unfinished TLA, this may never resolve | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -102,9 +102,17 @@ ModuleWrap* ModuleWrap::GetFromModule(Environment* env, | |||
| 102 | 102 | return nullptr; | |
| 103 | 103 | } | |
| 104 | 104 | ||
| 105 | - // new ModuleWrap(url, context, source, lineOffset, columnOffset, cachedData) | ||
| 105 | + Local<PrimitiveArray> ModuleWrap::GetHostDefinedOptions( | ||
| 106 | + Isolate* isolate, Local<Symbol> id_symbol) { | ||
| 107 | + Local<PrimitiveArray> host_defined_options = | ||
| 108 | + PrimitiveArray::New(isolate, HostDefinedOptions::kLength); | ||
| 109 | + host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol); | ||
| 110 | + return host_defined_options; | ||
| 111 | + } | ||
| 112 | + | ||
| 113 | + // new ModuleWrap(url, context, source, lineOffset, columnOffset[, cachedData]); | ||
| 106 | 114 | // new ModuleWrap(url, context, source, lineOffset, columOffset, | |
| 107 | - // hostDefinedOption) | ||
| 115 | + // idSymbol); | ||
| 108 | 116 | // new ModuleWrap(url, context, exportNames, evaluationCallback[, cjsModule]) | |
| 109 | 117 | void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |
| 110 | 118 | CHECK(args.IsConstructCall()); | |
@@ -134,7 +142,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |||
| 134 | 142 | int column_offset = 0; | |
| 135 | 143 | ||
| 136 | 144 | bool synthetic = args[2]->IsArray(); | |
| 137 | - | ||
| 145 | + bool can_use_builtin_cache = false; | ||
| 138 | 146 | Local<PrimitiveArray> host_defined_options = | |
| 139 | 147 | PrimitiveArray::New(isolate, HostDefinedOptions::kLength); | |
| 140 | 148 | Local<Symbol> id_symbol; | |
@@ -143,20 +151,24 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |||
| 143 | 151 | // cjsModule]) | |
| 144 | 152 | CHECK(args[3]->IsFunction()); | |
| 145 | 153 | } else { | |
| 146 | - // new ModuleWrap(url, context, source, lineOffset, columOffset, cachedData) | ||
| 154 | + // new ModuleWrap(url, context, source, lineOffset, columOffset[, | ||
| 155 | + // cachedData]); | ||
| 147 | 156 | // new ModuleWrap(url, context, source, lineOffset, columOffset, | |
| 148 | - // hostDefinedOption) | ||
| 157 | + // idSymbol); | ||
| 149 | 158 | CHECK(args[2]->IsString()); | |
| 150 | 159 | CHECK(args[3]->IsNumber()); | |
| 151 | 160 | line_offset = args[3].As<Int32>()->Value(); | |
| 152 | 161 | CHECK(args[4]->IsNumber()); | |
| 153 | 162 | column_offset = args[4].As<Int32>()->Value(); | |
| 154 | 163 | if (args[5]->IsSymbol()) { | |
| 155 | 164 | id_symbol = args[5].As<Symbol>(); | |
| 165 | + can_use_builtin_cache = | ||
| 166 | + (id_symbol == | ||
| 167 | + realm->isolate_data()->source_text_module_default_hdo()); | ||
| 156 | 168 | } else { | |
| 157 | 169 | id_symbol = Symbol::New(isolate, url); | |
| 158 | 170 | } | |
| 159 | - host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol); | ||
| 171 | + host_defined_options = GetHostDefinedOptions(isolate, id_symbol); | ||
| 160 | 172 | ||
| 161 | 173 | if (that->SetPrivate(context, | |
| 162 | 174 | realm->isolate_data()->host_defined_option_symbol(), | |
@@ -189,36 +201,34 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |||
| 189 | 201 | module = Module::CreateSyntheticModule(isolate, url, export_names, | |
| 190 | 202 | SyntheticModuleEvaluationStepsCallback); | |
| 191 | 203 | } else { | |
| 192 | - ScriptCompiler::CachedData* cached_data = nullptr; | ||
| 204 | + // When we are compiling for the default loader, this will be | ||
| 205 | + // std::nullopt, and CompileSourceTextModule() should use | ||
| 206 | + // on-disk cache (not present on v20.x). | ||
| 207 | + std::optional<v8::ScriptCompiler::CachedData*> user_cached_data; | ||
| 208 | + if (id_symbol != | ||
| 209 | + realm->isolate_data()->source_text_module_default_hdo()) { | ||
| 210 | + user_cached_data = nullptr; | ||
| 211 | + } | ||
| 193 | 212 | if (args[5]->IsArrayBufferView()) { | |
| 213 | + CHECK(!can_use_builtin_cache); // We don't use this option internally. | ||
| 194 | 214 | Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>(); | |
| 195 | 215 | uint8_t* data = | |
| 196 | 216 | static_cast<uint8_t*>(cached_data_buf->Buffer()->Data()); | |
| 197 | - cached_data = | ||
| 217 | + user_cached_data = | ||
| 198 | 218 | new ScriptCompiler::CachedData(data + cached_data_buf->ByteOffset(), | |
| 199 | 219 | cached_data_buf->ByteLength()); | |
| 200 | 220 | } | |
| 201 | - | ||
| 202 | 221 | Local<String> source_text = args[2].As<String>(); | |
| 203 | - ScriptOrigin origin(isolate, | ||
| 204 | - url, | ||
| 205 | - line_offset, | ||
| 206 | - column_offset, | ||
| 207 | - true, // is cross origin | ||
| 208 | - -1, // script id | ||
| 209 | - Local<Value>(), // source map URL | ||
| 210 | - false, // is opaque (?) | ||
| 211 | - false, // is WASM | ||
| 212 | - true, // is ES Module | ||
| 213 | - host_defined_options); | ||
| 214 | - ScriptCompiler::Source source(source_text, origin, cached_data); | ||
| 215 | - ScriptCompiler::CompileOptions options; | ||
| 216 | - if (source.GetCachedData() == nullptr) { | ||
| 217 | - options = ScriptCompiler::kNoCompileOptions; | ||
| 218 | - } else { | ||
| 219 | - options = ScriptCompiler::kConsumeCodeCache; | ||
| 220 | - } | ||
| 221 | - if (!ScriptCompiler::CompileModule(isolate, &source, options) | ||
| 222 | + | ||
| 223 | + bool cache_rejected = false; | ||
| 224 | + if (!CompileSourceTextModule(realm, | ||
| 225 | + source_text, | ||
| 226 | + url, | ||
| 227 | + line_offset, | ||
| 228 | + column_offset, | ||
| 229 | + host_defined_options, | ||
| 230 | + user_cached_data, | ||
| 231 | + &cache_rejected) | ||
| 222 | 232 | .ToLocal(&module)) { | |
| 223 | 233 | if (try_catch.HasCaught() && !try_catch.HasTerminated()) { | |
| 224 | 234 | CHECK(!try_catch.Message().IsEmpty()); | |
@@ -231,8 +241,9 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |||
| 231 | 241 | } | |
| 232 | 242 | return; | |
| 233 | 243 | } | |
| 234 | - if (options == ScriptCompiler::kConsumeCodeCache && | ||
| 235 | - source.GetCachedData()->rejected) { | ||
| 244 | + | ||
| 245 | + if (user_cached_data.has_value() && user_cached_data.value() != nullptr && | ||
| 246 | + cache_rejected) { | ||
| 236 | 247 | THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED( | |
| 237 | 248 | realm, "cachedData buffer was rejected"); | |
| 238 | 249 | try_catch.ReThrow(); | |
@@ -275,6 +286,57 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) { | |||
| 275 | 286 | args.GetReturnValue().Set(that); | |
| 276 | 287 | } | |
| 277 | 288 | ||
| 289 | + MaybeLocal<Module> ModuleWrap::CompileSourceTextModule( | ||
| 290 | + Realm* realm, | ||
| 291 | + Local<String> source_text, | ||
| 292 | + Local<String> url, | ||
| 293 | + int line_offset, | ||
| 294 | + int column_offset, | ||
| 295 | + Local<PrimitiveArray> host_defined_options, | ||
| 296 | + std::optional<ScriptCompiler::CachedData*> user_cached_data, | ||
| 297 | + bool* cache_rejected) { | ||
| 298 | + Isolate* isolate = realm->isolate(); | ||
| 299 | + EscapableHandleScope scope(isolate); | ||
| 300 | + ScriptOrigin origin(isolate, | ||
| 301 | + url, | ||
| 302 | + line_offset, | ||
| 303 | + column_offset, | ||
| 304 | + true, // is cross origin | ||
| 305 | + -1, // script id | ||
| 306 | + Local<Value>(), // source map URL | ||
| 307 | + false, // is opaque (?) | ||
| 308 | + false, // is WASM | ||
| 309 | + true, // is ES Module | ||
| 310 | + host_defined_options); | ||
| 311 | + ScriptCompiler::CachedData* cached_data = nullptr; | ||
| 312 | + // When compiling for the default loader, user_cached_data is std::nullptr. | ||
| 313 | + // When compiling for vm.Module, it's either nullptr or a pointer to the | ||
| 314 | + // cached data. | ||
| 315 | + if (user_cached_data.has_value()) { | ||
| 316 | + cached_data = user_cached_data.value(); | ||
| 317 | + } | ||
| 318 | + | ||
| 319 | + ScriptCompiler::Source source(source_text, origin, cached_data); | ||
| 320 | + ScriptCompiler::CompileOptions options; | ||
| 321 | + if (cached_data == nullptr) { | ||
| 322 | + options = ScriptCompiler::kNoCompileOptions; | ||
| 323 | + } else { | ||
| 324 | + options = ScriptCompiler::kConsumeCodeCache; | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + Local<Module> module; | ||
| 328 | + if (!ScriptCompiler::CompileModule(isolate, &source, options) | ||
| 329 | + .ToLocal(&module)) { | ||
| 330 | + return scope.EscapeMaybe(MaybeLocal<Module>()); | ||
| 331 | + } | ||
| 332 | + | ||
| 333 | + if (options == ScriptCompiler::kConsumeCodeCache) { | ||
| 334 | + *cache_rejected = source.GetCachedData()->rejected; | ||
| 335 | + } | ||
| 336 | + | ||
| 337 | + return scope.Escape(module); | ||
| 338 | + } | ||
| 339 | + | ||
| 278 | 340 | static Local<Object> createImportAttributesContainer( | |
| 279 | 341 | Realm* realm, | |
| 280 | 342 | Isolate* isolate, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,10 +3,12 @@ | |||
| 3 | 3 | ||
| 4 | 4 | #if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | |
| 5 | 5 | ||
| 6 | - #include <unordered_map> | ||
| 6 | + #include <optional> | ||
| 7 | 7 | #include <string> | |
| 8 | + #include <unordered_map> | ||
| 8 | 9 | #include <vector> | |
| 9 | 10 | #include "base_object.h" | |
| 11 | + #include "v8-script.h" | ||
| 10 | 12 | ||
| 11 | 13 | namespace node { | |
| 12 | 14 | ||
@@ -68,6 +70,23 @@ class ModuleWrap : public BaseObject { | |||
| 68 | 70 | return true; | |
| 69 | 71 | } | |
| 70 | 72 | ||
| 73 | + static v8::Local<v8::PrimitiveArray> GetHostDefinedOptions( | ||
| 74 | + v8::Isolate* isolate, v8::Local<v8::Symbol> symbol); | ||
| 75 | + | ||
| 76 | + // When user_cached_data is not std::nullopt, use the code cache if it's not | ||
| 77 | + // nullptr, otherwise don't use code cache. | ||
| 78 | + // TODO(joyeecheung): when it is std::nullopt, use on-disk cache | ||
| 79 | + // See: https://github.com/nodejs/node/issues/47472 | ||
| 80 | + static v8::MaybeLocal<v8::Module> CompileSourceTextModule( | ||
| 81 | + Realm* realm, | ||
| 82 | + v8::Local<v8::String> source_text, | ||
| 83 | + v8::Local<v8::String> url, | ||
| 84 | + int line_offset, | ||
| 85 | + int column_offset, | ||
| 86 | + v8::Local<v8::PrimitiveArray> host_defined_options, | ||
| 87 | + std::optional<v8::ScriptCompiler::CachedData*> user_cached_data, | ||
| 88 | + bool* cache_rejected); | ||
| 89 | + | ||
| 71 | 90 | private: | |
| 72 | 91 | ModuleWrap(Realm* realm, | |
| 73 | 92 | v8::Local<v8::Object> object, | |
| Back | FazBrowse Home | New Git URL |
0 commit comments