[modules] Implement SyntheticModule::ResolveExport and SyntheticModule::Evaluate.
ResolveExport and Evaluate are the final unimplemented SyntheticModule methods; with this change the implementation is complete. Test-api unit tests are also provided. Bug: v8:9292 Change-Id: Ieb7643cc5b6495dd201a51f04199d2406a703e52 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1681187 Reviewed-by: Adam Klein <adamk@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Commit-Queue: Dan Clark <daniec@microsoft.com> Cr-Commit-Position: refs/heads/master@{#62582}
This commit is contained in:
parent
e6b853ef16
commit
749f0727a2
@ -1330,9 +1330,12 @@ class V8_EXPORT Module {
|
||||
/*
|
||||
* Callback defined in the embedder. This is responsible for setting
|
||||
* the module's exported values with calls to SetSyntheticModuleExport().
|
||||
* The callback must return a Value to indicate success (where no
|
||||
* exception was thrown) and return an empy MaybeLocal to indicate falure
|
||||
* (where an exception was thrown).
|
||||
*/
|
||||
typedef bool (*SyntheticModuleEvaluationSteps)(Local<Context> context,
|
||||
Local<Module> module);
|
||||
typedef MaybeLocal<Value> (*SyntheticModuleEvaluationSteps)(
|
||||
Local<Context> context, Local<Module> module);
|
||||
|
||||
/**
|
||||
* Creates a new SyntheticModule with the specified export names, where
|
||||
|
@ -138,6 +138,9 @@ MaybeHandle<Cell> Module::ResolveExport(Isolate* isolate, Handle<Module> module,
|
||||
Handle<String> export_name,
|
||||
MessageLocation loc, bool must_resolve,
|
||||
Module::ResolveSet* resolve_set) {
|
||||
DCHECK_GE(module->status(), kPreInstantiating);
|
||||
DCHECK_NE(module->status(), kEvaluating);
|
||||
|
||||
if (module->IsSourceTextModule()) {
|
||||
return SourceTextModule::ResolveExport(
|
||||
isolate, Handle<SourceTextModule>::cast(module), module_specifier,
|
||||
@ -145,7 +148,7 @@ MaybeHandle<Cell> Module::ResolveExport(Isolate* isolate, Handle<Module> module,
|
||||
} else {
|
||||
return SyntheticModule::ResolveExport(
|
||||
isolate, Handle<SyntheticModule>::cast(module), module_specifier,
|
||||
export_name, loc, must_resolve, resolve_set);
|
||||
export_name, loc, must_resolve);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,8 +173,6 @@ MaybeHandle<Cell> SourceTextModule::ResolveExport(
|
||||
Isolate* isolate, Handle<SourceTextModule> module,
|
||||
Handle<String> module_specifier, Handle<String> export_name,
|
||||
MessageLocation loc, bool must_resolve, Module::ResolveSet* resolve_set) {
|
||||
DCHECK_GE(module->status(), kPreInstantiating);
|
||||
DCHECK_NE(module->status(), kEvaluating);
|
||||
Handle<Object> object(module->exports().Lookup(export_name), isolate);
|
||||
if (object->IsCell()) {
|
||||
// Already resolved (e.g. because it's a local export).
|
||||
|
@ -34,8 +34,20 @@ void SyntheticModule::SetExport(Isolate* isolate,
|
||||
MaybeHandle<Cell> SyntheticModule::ResolveExport(
|
||||
Isolate* isolate, Handle<SyntheticModule> module,
|
||||
Handle<String> module_specifier, Handle<String> export_name,
|
||||
MessageLocation loc, bool must_resolve, ResolveSet* resolve_set) {
|
||||
UNREACHABLE(); // TODO(SyntheticModules) implement
|
||||
MessageLocation loc, bool must_resolve) {
|
||||
Handle<Object> object(module->exports().Lookup(export_name), isolate);
|
||||
if (object->IsCell()) {
|
||||
return Handle<Cell>::cast(object);
|
||||
}
|
||||
|
||||
if (must_resolve) {
|
||||
return isolate->Throw<Cell>(
|
||||
isolate->factory()->NewSyntaxError(MessageTemplate::kUnresolvableExport,
|
||||
module_specifier, export_name),
|
||||
&loc);
|
||||
}
|
||||
|
||||
return MaybeHandle<Cell>();
|
||||
}
|
||||
|
||||
// Implements Synthetic Module Record's Instantiate concrete method :
|
||||
@ -73,7 +85,23 @@ bool SyntheticModule::FinishInstantiate(Isolate* isolate,
|
||||
// https://heycam.github.io/webidl/#smr-evaluate
|
||||
MaybeHandle<Object> SyntheticModule::Evaluate(Isolate* isolate,
|
||||
Handle<SyntheticModule> module) {
|
||||
UNREACHABLE(); // TODO(SyntheticModules) implement
|
||||
module->SetStatus(kEvaluating);
|
||||
|
||||
v8::Module::SyntheticModuleEvaluationSteps evaluation_steps =
|
||||
FUNCTION_CAST<v8::Module::SyntheticModuleEvaluationSteps>(
|
||||
module->evaluation_steps().foreign_address());
|
||||
v8::Local<v8::Value> result;
|
||||
if (!evaluation_steps(
|
||||
Utils::ToLocal(Handle<Context>::cast(isolate->native_context())),
|
||||
Utils::ToLocal(Handle<Module>::cast(module)))
|
||||
.ToLocal(&result)) {
|
||||
isolate->PromoteScheduledException();
|
||||
module->RecordError(isolate);
|
||||
return MaybeHandle<Object>();
|
||||
}
|
||||
|
||||
module->SetStatus(kEvaluated);
|
||||
return Utils::OpenHandle(*result);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
@ -47,7 +47,7 @@ class SyntheticModule : public Module {
|
||||
static V8_WARN_UNUSED_RESULT MaybeHandle<Cell> ResolveExport(
|
||||
Isolate* isolate, Handle<SyntheticModule> module,
|
||||
Handle<String> module_specifier, Handle<String> export_name,
|
||||
MessageLocation loc, bool must_resolve, ResolveSet* resolve_set);
|
||||
MessageLocation loc, bool must_resolve);
|
||||
|
||||
static V8_WARN_UNUSED_RESULT bool PrepareInstantiate(
|
||||
Isolate* isolate, Handle<SyntheticModule> module,
|
||||
|
@ -23566,11 +23566,33 @@ v8::MaybeLocal<Module> UnexpectedModuleResolveCallback(Local<Context> context,
|
||||
CHECK_WITH_MSG(false, "Unexpected call to resolve callback");
|
||||
}
|
||||
|
||||
bool UnexpectedSyntheticModuleEvaluationStepsCallback(Local<Context> context,
|
||||
Local<Module> module) {
|
||||
v8::MaybeLocal<Value> UnexpectedSyntheticModuleEvaluationStepsCallback(
|
||||
Local<Context> context, Local<Module> module) {
|
||||
CHECK_WITH_MSG(false, "Unexpected call to synthetic module re callback");
|
||||
}
|
||||
|
||||
static int synthetic_module_callback_count;
|
||||
|
||||
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallback(
|
||||
Local<Context> context, Local<Module> module) {
|
||||
synthetic_module_callback_count++;
|
||||
return v8::Undefined(reinterpret_cast<v8::Isolate*>(context->GetIsolate()));
|
||||
}
|
||||
|
||||
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallbackFail(
|
||||
Local<Context> context, Local<Module> module) {
|
||||
synthetic_module_callback_count++;
|
||||
context->GetIsolate()->ThrowException(
|
||||
v8_str("SyntheticModuleEvaluationStepsCallbackFail exception"));
|
||||
return v8::MaybeLocal<Value>();
|
||||
}
|
||||
|
||||
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallbackSetExport(
|
||||
Local<Context> context, Local<Module> module) {
|
||||
module->SetSyntheticModuleExport(v8_str("test_export"), v8_num(42));
|
||||
return v8::Undefined(reinterpret_cast<v8::Isolate*>(context->GetIsolate()));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
Local<Module> CompileAndInstantiateModule(v8::Isolate* isolate,
|
||||
@ -23628,6 +23650,28 @@ Local<Module> CompileAndInstantiateModuleFromCache(
|
||||
|
||||
} // namespace
|
||||
|
||||
v8::MaybeLocal<Module> SyntheticModuleResolveCallback(Local<Context> context,
|
||||
Local<String> specifier,
|
||||
Local<Module> referrer) {
|
||||
std::vector<v8::Local<v8::String>> export_names{v8_str("test_export")};
|
||||
Local<Module> module = CreateAndInstantiateSyntheticModule(
|
||||
context->GetIsolate(),
|
||||
v8_str("SyntheticModuleResolveCallback-TestSyntheticModule"), context,
|
||||
export_names, SyntheticModuleEvaluationStepsCallbackSetExport);
|
||||
return v8::MaybeLocal<Module>(module);
|
||||
}
|
||||
|
||||
v8::MaybeLocal<Module> SyntheticModuleThatThrowsDuringEvaluateResolveCallback(
|
||||
Local<Context> context, Local<String> specifier, Local<Module> referrer) {
|
||||
std::vector<v8::Local<v8::String>> export_names{v8_str("test_export")};
|
||||
Local<Module> module = CreateAndInstantiateSyntheticModule(
|
||||
context->GetIsolate(),
|
||||
v8_str("SyntheticModuleThatThrowsDuringEvaluateResolveCallback-"
|
||||
"TestSyntheticModule"),
|
||||
context, export_names, SyntheticModuleEvaluationStepsCallbackFail);
|
||||
return v8::MaybeLocal<Module>(module);
|
||||
}
|
||||
|
||||
TEST(ModuleCodeCache) {
|
||||
v8::Isolate::CreateParams create_params;
|
||||
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
||||
@ -23763,6 +23807,144 @@ TEST(SyntheticModuleSetExports) {
|
||||
->Equals(*v8::Utils::OpenHandle(*bar_string)));
|
||||
}
|
||||
|
||||
TEST(SyntheticModuleEvaluationStepsNoThrow) {
|
||||
synthetic_module_callback_count = 0;
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::Isolate::Scope iscope(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope cscope(context);
|
||||
|
||||
std::vector<v8::Local<v8::String>> export_names{v8_str("default")};
|
||||
|
||||
Local<Module> module = CreateAndInstantiateSyntheticModule(
|
||||
isolate,
|
||||
v8_str("SyntheticModuleEvaluationStepsNoThrow-TestSyntheticModule"),
|
||||
context, export_names, SyntheticModuleEvaluationStepsCallback);
|
||||
CHECK_EQ(synthetic_module_callback_count, 0);
|
||||
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
|
||||
CHECK(completion_value->IsUndefined());
|
||||
CHECK_EQ(synthetic_module_callback_count, 1);
|
||||
CHECK_EQ(module->GetStatus(), Module::kEvaluated);
|
||||
}
|
||||
|
||||
TEST(SyntheticModuleEvaluationStepsThrow) {
|
||||
synthetic_module_callback_count = 0;
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::Isolate::Scope iscope(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = CcTest::isolate()->GetCurrentContext();
|
||||
v8::Context::Scope cscope(context);
|
||||
|
||||
std::vector<v8::Local<v8::String>> export_names{v8_str("default")};
|
||||
|
||||
Local<Module> module = CreateAndInstantiateSyntheticModule(
|
||||
isolate,
|
||||
v8_str("SyntheticModuleEvaluationStepsThrow-TestSyntheticModule"),
|
||||
context, export_names, SyntheticModuleEvaluationStepsCallbackFail);
|
||||
TryCatch try_catch(isolate);
|
||||
CHECK_EQ(synthetic_module_callback_count, 0);
|
||||
v8::MaybeLocal<Value> completion_value = module->Evaluate(context);
|
||||
CHECK(completion_value.IsEmpty());
|
||||
CHECK_EQ(synthetic_module_callback_count, 1);
|
||||
CHECK_EQ(module->GetStatus(), Module::kErrored);
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
TEST(SyntheticModuleEvaluationStepsSetExport) {
|
||||
synthetic_module_callback_count = 0;
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
auto i_isolate = reinterpret_cast<i::Isolate*>(isolate);
|
||||
v8::Isolate::Scope iscope(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope cscope(context);
|
||||
|
||||
Local<String> test_export_string = v8_str("test_export");
|
||||
std::vector<v8::Local<v8::String>> export_names{test_export_string};
|
||||
|
||||
Local<Module> module = CreateAndInstantiateSyntheticModule(
|
||||
isolate,
|
||||
v8_str("SyntheticModuleEvaluationStepsSetExport-TestSyntheticModule"),
|
||||
context, export_names, SyntheticModuleEvaluationStepsCallbackSetExport);
|
||||
|
||||
i::Handle<i::SyntheticModule> i_module =
|
||||
i::Handle<i::SyntheticModule>::cast(v8::Utils::OpenHandle(*module));
|
||||
i::Handle<i::ObjectHashTable> exports(i_module->exports(), i_isolate);
|
||||
|
||||
i::Handle<i::Cell> test_export_cell =
|
||||
i::Handle<i::Cell>::cast(i::Handle<i::Object>(
|
||||
exports->Lookup(v8::Utils::OpenHandle(*test_export_string)),
|
||||
i_isolate));
|
||||
CHECK(test_export_cell->value().IsUndefined());
|
||||
|
||||
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
|
||||
CHECK(completion_value->IsUndefined());
|
||||
CHECK_EQ(42, test_export_cell->value().Number());
|
||||
CHECK_EQ(module->GetStatus(), Module::kEvaluated);
|
||||
}
|
||||
|
||||
TEST(ImportFromSyntheticModule) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::Isolate::Scope iscope(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope cscope(context);
|
||||
|
||||
Local<String> url = v8_str("www.test.com");
|
||||
Local<String> source_text = v8_str(
|
||||
"import {test_export} from './synthetic.module';"
|
||||
"(function() { return test_export; })();");
|
||||
v8::ScriptOrigin origin(url, Local<v8::Integer>(), Local<v8::Integer>(),
|
||||
Local<v8::Boolean>(), Local<v8::Integer>(),
|
||||
Local<v8::Value>(), Local<v8::Boolean>(),
|
||||
Local<v8::Boolean>(), True(isolate));
|
||||
v8::ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
module->InstantiateModule(context, SyntheticModuleResolveCallback)
|
||||
.ToChecked();
|
||||
|
||||
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
|
||||
CHECK_EQ(42, completion_value->Int32Value(context).FromJust());
|
||||
}
|
||||
|
||||
TEST(ImportFromSyntheticModuleThrow) {
|
||||
LocalContext env;
|
||||
v8::Isolate* isolate = env->GetIsolate();
|
||||
v8::Isolate::Scope iscope(isolate);
|
||||
v8::HandleScope scope(isolate);
|
||||
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
||||
v8::Context::Scope cscope(context);
|
||||
|
||||
Local<String> url = v8_str("www.test.com");
|
||||
Local<String> source_text = v8_str(
|
||||
"import {test_export} from './synthetic.module';"
|
||||
"(function() { return test_export; })();");
|
||||
v8::ScriptOrigin origin(url, Local<v8::Integer>(), Local<v8::Integer>(),
|
||||
Local<v8::Boolean>(), Local<v8::Integer>(),
|
||||
Local<v8::Value>(), Local<v8::Boolean>(),
|
||||
Local<v8::Boolean>(), True(isolate));
|
||||
v8::ScriptCompiler::Source source(source_text, origin);
|
||||
Local<Module> module =
|
||||
v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
||||
module
|
||||
->InstantiateModule(
|
||||
context, SyntheticModuleThatThrowsDuringEvaluateResolveCallback)
|
||||
.ToChecked();
|
||||
|
||||
CHECK_EQ(module->GetStatus(), Module::kInstantiated);
|
||||
TryCatch try_catch(isolate);
|
||||
v8::MaybeLocal<Value> completion_value = module->Evaluate(context);
|
||||
CHECK(completion_value.IsEmpty());
|
||||
CHECK_EQ(module->GetStatus(), Module::kErrored);
|
||||
CHECK(try_catch.HasCaught());
|
||||
}
|
||||
|
||||
// Tests that the code cache does not confuse the same source code compiled as a
|
||||
// script and as a module.
|
||||
TEST(CodeCacheModuleScriptMismatch) {
|
||||
|
Loading…
Reference in New Issue
Block a user