◐ Shell
clean mode source ↗

module: detect ESM syntax by trying to recompile as SourceTextModule · nodejs/node@b07ad39

@@ -102,9 +102,17 @@ ModuleWrap* ModuleWrap::GetFromModule(Environment* env,

102102

return 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])

109117

void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {

110118

CHECK(args.IsConstructCall());

@@ -134,7 +142,7 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {

134142

int column_offset = 0;

135143136144

bool synthetic = args[2]->IsArray();

137-145+

bool can_use_builtin_cache = false;

138146

Local<PrimitiveArray> host_defined_options =

139147

PrimitiveArray::New(isolate, HostDefinedOptions::kLength);

140148

Local<Symbol> id_symbol;

@@ -143,20 +151,24 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {

143151

// cjsModule])

144152

CHECK(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);

149158

CHECK(args[2]->IsString());

150159

CHECK(args[3]->IsNumber());

151160

line_offset = args[3].As<Int32>()->Value();

152161

CHECK(args[4]->IsNumber());

153162

column_offset = args[4].As<Int32>()->Value();

154163

if (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);

160172161173

if (that->SetPrivate(context,

162174

realm->isolate_data()->host_defined_option_symbol(),

@@ -189,36 +201,34 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {

189201

module = 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+

}

193212

if (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>();

195215

uint8_t* data =

196216

static_cast<uint8_t*>(cached_data_buf->Buffer()->Data());

197-

cached_data =

217+

user_cached_data =

198218

new 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)) {

223233

if (try_catch.HasCaught() && !try_catch.HasTerminated()) {

224234

CHECK(!try_catch.Message().IsEmpty());

@@ -231,8 +241,9 @@ void ModuleWrap::New(const FunctionCallbackInfo<Value>& args) {

231241

}

232242

return;

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) {

236247

THROW_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+278340

static Local<Object> createImportAttributesContainer(

279341

Realm* realm,

280342

Isolate* isolate,