module: detect ESM syntax by trying to recompile as SourceTextModule · nodejs/node@b07ad39
@@ -102,9 +102,17 @@ ModuleWrap* ModuleWrap::GetFromModule(Environment* env,
102102return nullptr;
103103}
104104105-// 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]);
106114// new ModuleWrap(url, context, source, lineOffset, columOffset,
107-// hostDefinedOption)
115+// idSymbol);
108116// new ModuleWrap(url, context, exportNames, evaluationCallback[, cjsModule])
109117void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
110118CHECK(args.IsConstructCall());
@@ -134,7 +142,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
134142int column_offset = 0;
135143136144bool synthetic = args[2]->IsArray();
137-145+ bool can_use_builtin_cache = false;
138146 Local<PrimitiveArray> host_defined_options =
139147PrimitiveArray::New(isolate, HostDefinedOptions::kLength);
140148 Local<Symbol> id_symbol;
@@ -143,20 +151,24 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
143151// cjsModule])
144152CHECK(args[3]->IsFunction());
145153 } else {
146-// new ModuleWrap(url, context, source, lineOffset, columOffset, cachedData)
154+// new ModuleWrap(url, context, source, lineOffset, columOffset[,
155+// cachedData]);
147156// new ModuleWrap(url, context, source, lineOffset, columOffset,
148-// hostDefinedOption)
157+// idSymbol);
149158CHECK(args[2]->IsString());
150159CHECK(args[3]->IsNumber());
151160 line_offset = args[3].As<Int32>()->Value();
152161CHECK(args[4]->IsNumber());
153162 column_offset = args[4].As<Int32>()->Value();
154163if (args[5]->IsSymbol()) {
155164 id_symbol = args[5].As<Symbol>();
165+ can_use_builtin_cache =
166+ (id_symbol ==
167+ realm->isolate_data()->source_text_module_default_hdo());
156168 } else {
157169 id_symbol = Symbol::New(isolate, url);
158170 }
159- host_defined_options->Set(isolate, HostDefinedOptions::kID, id_symbol);
171+ host_defined_options = GetHostDefinedOptions(isolate, id_symbol);
160172161173if (that->SetPrivate(context,
162174 realm->isolate_data()->host_defined_option_symbol(),
@@ -189,36 +201,34 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
189201module = Module::CreateSyntheticModule(isolate, url, export_names,
190202 SyntheticModuleEvaluationStepsCallback);
191203 } 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+ }
193212if (args[5]->IsArrayBufferView()) {
213+CHECK(!can_use_builtin_cache); // We don't use this option internally.
194214 Local<ArrayBufferView> cached_data_buf = args[5].As<ArrayBufferView>();
195215uint8_t* data =
196216static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());
197-cached_data =
217+user_cached_data =
198218new ScriptCompiler::CachedData(data + cached_data_buf->ByteOffset(),
199219 cached_data_buf->ByteLength());
200220 }
201-202221 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)
222232 .ToLocal(&module)) {
223233if (try_catch.HasCaught() && !try_catch.HasTerminated()) {
224234CHECK(!try_catch.Message().IsEmpty());
@@ -231,8 +241,9 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
231241 }
232242return;
233243 }
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) {
236247THROW_ERR_VM_MODULE_CACHED_DATA_REJECTED(
237248 realm, "cachedData buffer was rejected");
238249 try_catch.ReThrow();
@@ -275,6 +286,57 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {
275286 args.GetReturnValue().Set(that);
276287}
277288289+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+278340static Local<Object> createImportAttributesContainer(
279341 Realm* realm,
280342 Isolate* isolate,