Commit 749f0727 authored by Daniel Clark's avatar Daniel Clark Committed by Commit Bot

[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/+/1681187Reviewed-by: 's avatarAdam Klein <adamk@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Dan Clark <daniec@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#62582}
parent e6b853ef
......@@ -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) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment