Skip to content

Commit dc2deb3

Browse files
committed
deps: V8: backport 970d651e4f99
Original commit message: [api][module] Type-check synthetic module evaluation steps Synthetic module evaluation steps are required to return a Promise, which becomes the module's top-level capability. Their signature returned a MaybeLocal<Value> though, so that requirement was only enforced by a CHECK in SyntheticModule::Evaluate(). SyntheticModuleEvaluationSteps now returns a MaybeLocal<Promise>, with a matching CreateSyntheticModule() overload. The old signature remains available as LegacySyntheticModuleEvaluationSteps so that embedders can be migrated in a separate CL; it will be deprecated and then removed once embedders migrate. d8 and the existing tests move to the Promise-returning version, with one cctest checking the legacy version. Bug: 545375591 Change-Id: Id55db730678455f81394bf8a68f66f93d22f5547 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8223168 Reviewed-by: Olivier Flückiger <olivf@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Commit-Queue: Caio Lima <caiolima@igalia.com> Cr-Commit-Position: refs/heads/main@{#109273} Refs: v8/v8@970d651 Co-authored-by: Caio Lima <caiolima@igalia.com>
1 parent 1a8adb1 commit dc2deb3

8 files changed

Lines changed: 118 additions & 18 deletions

File tree

‎common.gypi‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242

4343
# Reset this number to 0 on major V8 upgrades.
4444
# Increment by one for each non-official patch applied to deps/v8.
45-
'v8_embedder_string': '-node.29',
45+
'v8_embedder_string': '-node.30',
4646

4747
##### V8 defaults for Node.js #####
4848

‎deps/v8/include/v8-script.h‎

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,16 @@ class V8_EXPORT Module : public Data {
336336
* exception was thrown) and return an empy MaybeLocal to indicate falure
337337
* (where an exception was thrown).
338338
*/
339-
// TODO(caiolima): Change this to `MaybeLocal<Promise>` given it's expected a
340-
// Promise as return.
341339
using SyntheticModuleEvaluationSteps =
340+
MaybeLocal<Promise> (*)(Local<Context> context, Local<Module> module);
341+
342+
/*
343+
* Deprecated version of SyntheticModuleEvaluationSteps: the returned value is
344+
* still required to be a Promise, but that is only enforced at runtime.
345+
*/
346+
// TODO(https://crbug.com/545375591): Remove once all embedders return a
347+
// MaybeLocal<Promise>.
348+
using LegacySyntheticModuleEvaluationSteps =
342349
MaybeLocal<Value> (*)(Local<Context> context, Local<Module> module);
343350

344351
/**
@@ -353,6 +360,16 @@ class V8_EXPORT Module : public Data {
353360
const MemorySpan<const Local<String>>& export_names,
354361
SyntheticModuleEvaluationSteps evaluation_steps);
355362

363+
// TODO(https://crbug.com/545375591): Advance to V8_DEPRECATED and then remove
364+
// this overload once all embedders have been migrated to the one above.
365+
V8_DEPRECATE_SOON(
366+
"Use the CreateSyntheticModule overload whose evaluation_steps return a "
367+
"MaybeLocal<Promise>")
368+
static Local<Module> CreateSyntheticModule(
369+
Isolate* isolate, Local<String> module_name,
370+
const MemorySpan<const Local<String>>& export_names,
371+
LegacySyntheticModuleEvaluationSteps evaluation_steps);
372+
356373
/**
357374
* Set this module's exported value for the name export_name to the specified
358375
* export_value. This method must be called only on Modules created via

‎deps/v8/src/api/api.cc‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2436,6 +2436,33 @@ Local<Module> Module::CreateSyntheticModule(
24362436
i_module_name, i_export_names, evaluation_steps)));
24372437
}
24382438

2439+
START_ALLOW_USE_DEPRECATED()
2440+
Local<Module> Module::CreateSyntheticModule(
2441+
Isolate* v8_isolate, Local<String> module_name,
2442+
const MemorySpan<const Local<String>>& export_names,
2443+
v8::Module::LegacySyntheticModuleEvaluationSteps evaluation_steps) {
2444+
// TODO(https://crbug.com/545375591): Remove once
2445+
// LegacySyntheticModuleEvaluationSteps is gone.
2446+
#if (__GNUC__ >= 8) || defined(__clang__)
2447+
#pragma GCC diagnostic push
2448+
#pragma GCC diagnostic ignored "-Wcast-function-type"
2449+
#endif
2450+
// Cast from 'v8::MaybeLocal<v8::Value> (*)(v8::Local<v8::Context>,
2451+
// v8::Local<v8::Module>)' to 'v8::MaybeLocal<v8::Promise>
2452+
// (*)(v8::Local<v8::Context>, v8::Local<v8::Module>)'. Both return types are
2453+
// pointer-sized, trivially copyable handle wrappers, so they share the same
2454+
// representation. SyntheticModule::Evaluate() checks at runtime that the
2455+
// returned value really is a Promise.
2456+
auto promise_returning_steps =
2457+
reinterpret_cast<SyntheticModuleEvaluationSteps>(evaluation_steps);
2458+
#if (__GNUC__ >= 8) || defined(__clang__)
2459+
#pragma GCC diagnostic pop
2460+
#endif
2461+
return CreateSyntheticModule(v8_isolate, module_name, export_names,
2462+
promise_returning_steps);
2463+
}
2464+
END_ALLOW_USE_DEPRECATED()
2465+
24392466
Maybe<bool> Module::SetSyntheticModuleExport(Isolate* v8_isolate,
24402467
Local<String> export_name,
24412468
Local<v8::Value> export_value) {

‎deps/v8/src/d8/d8.cc‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1441,7 +1441,7 @@ MaybeLocal<Module> Shell::FetchModuleTree(Local<Module> referrer,
14411441
return result;
14421442
}
14431443

1444-
MaybeLocal<Value> Shell::JSONModuleEvaluationSteps(Local<Context> context,
1444+
MaybeLocal<Promise> Shell::JSONModuleEvaluationSteps(Local<Context> context,
14451445
Local<Module> module) {
14461446
Isolate* isolate = Isolate::GetCurrent();
14471447

‎deps/v8/src/d8/d8.h‎

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -884,9 +884,8 @@ class Shell : public i::AllStatic {
884884
const std::string& file_name,
885885
ModuleType module_type);
886886

887-
static MaybeLocal<Value> JSONModuleEvaluationSteps(Local<Context> context,
888-
Local<Module> module);
889-
887+
static MaybeLocal<Promise> JSONModuleEvaluationSteps(Local<Context> context,
888+
Local<Module> module);
890889
template <class T>
891890
static MaybeLocal<T> CompileString(Isolate* isolate, Local<Context> context,
892891
Local<String> source,

‎deps/v8/src/objects/synthetic-module.cc‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "src/objects/synthetic-module.h"
66

77
#include "src/api/api-inl.h"
8+
#include "src/base/macros.h"
89
#include "src/builtins/accessors.h"
910
#include "src/objects/js-generator-inl.h"
1011
#include "src/objects/module-inl.h"
@@ -105,13 +106,24 @@ bool SyntheticModule::FinishInstantiate(Isolate* isolate,
105106

106107
// Implements Synthetic Module Record's Evaluate concrete method:
107108
// https://heycam.github.io/webidl/#smr-evaluate
109+
// The callback may have been created through the deprecated
110+
// v8::Module::LegacySyntheticModuleEvaluationSteps overload, in which case it
111+
// actually returns a v8::MaybeLocal<v8::Value> and is called here through a
112+
// mismatching signature. Both return types are pointer-sized, trivially
113+
// copyable handle wrappers, so this is safe in practice, but it does trip
114+
// CFI's and UBSan's indirect call checks.
115+
// TODO(https://crbug.com/545375591): Remove DISABLE_CFI_ICALL once the
116+
// deprecated overload is gone.
117+
DISABLE_CFI_ICALL
108118
MaybeDirectHandle<JSPromise> SyntheticModule::Evaluate(
109119
Isolate* isolate, DirectHandle<SyntheticModule> module) {
110120
module->SetStatus(kEvaluating);
111121

112122
v8::Module::SyntheticModuleEvaluationSteps evaluation_steps =
113123
FUNCTION_CAST<v8::Module::SyntheticModuleEvaluationSteps>(
114124
module->evaluation_steps()->foreign_address<kSyntheticModuleTag>());
125+
// Deliberately received as a v8::Local<v8::Value>: the deprecated callback
126+
// signature only promises a Promise, it doesn't guarantee one.
115127
v8::Local<v8::Value> result;
116128
if (!evaluation_steps(Utils::ToLocal(isolate->native_context()),
117129
Utils::ToLocal(Cast<Module>(module)))

‎deps/v8/test/cctest/test-api.cc‎

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24701,37 +24701,48 @@ TEST(CodeCache) {
2470124701
isolate2->Dispose();
2470224702
}
2470324703

24704-
v8::MaybeLocal<Value> UnexpectedSyntheticModuleEvaluationStepsCallback(
24704+
v8::MaybeLocal<Promise> UnexpectedSyntheticModuleEvaluationStepsCallback(
2470524705
Local<Context> context, Local<Module> module) {
2470624706
CHECK_WITH_MSG(false, "Unexpected call to synthetic module re callback");
2470724707
}
2470824708

2470924709
static int synthetic_module_callback_count;
2471024710

24711-
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallback(
24711+
v8::MaybeLocal<Promise> SyntheticModuleEvaluationStepsCallback(
2471224712
Local<Context> context, Local<Module> module) {
2471324713
synthetic_module_callback_count++;
24714-
// Synthetic module evaluation steps must return a Promise.
2471524714
Local<v8::Promise::Resolver> resolver =
2471624715
v8::Promise::Resolver::New(context).ToLocalChecked();
2471724716
resolver->Resolve(context, v8::Undefined(CcTest::isolate())).Check();
2471824717
return resolver->GetPromise();
2471924718
}
2472024719

24721-
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallbackFail(
24720+
v8::MaybeLocal<Promise> SyntheticModuleEvaluationStepsCallbackFail(
2472224721
Local<Context> context, Local<Module> module) {
2472324722
synthetic_module_callback_count++;
2472424723
CcTest::isolate()->ThrowException(
2472524724
v8_str("SyntheticModuleEvaluationStepsCallbackFail exception"));
24726-
return v8::MaybeLocal<Value>();
24725+
return v8::MaybeLocal<Promise>();
2472724726
}
2472824727

24729-
v8::MaybeLocal<Value> SyntheticModuleEvaluationStepsCallbackSetExport(
24728+
// Deprecated version of the evaluation steps, returning a MaybeLocal<Value>
24729+
// that holds a Promise.
24730+
// TODO(https://crbug.com/545375591): Remove together with
24731+
// v8::Module::LegacySyntheticModuleEvaluationSteps.
24732+
v8::MaybeLocal<Value> LegacySyntheticModuleEvaluationStepsCallback(
24733+
Local<Context> context, Local<Module> module) {
24734+
synthetic_module_callback_count++;
24735+
Local<v8::Promise::Resolver> resolver =
24736+
v8::Promise::Resolver::New(context).ToLocalChecked();
24737+
resolver->Resolve(context, v8::Undefined(CcTest::isolate())).Check();
24738+
return resolver->GetPromise();
24739+
}
24740+
24741+
v8::MaybeLocal<Promise> SyntheticModuleEvaluationStepsCallbackSetExport(
2473024742
Local<Context> context, Local<Module> module) {
2473124743
Maybe<bool> set_export_result = module->SetSyntheticModuleExport(
2473224744
CcTest::isolate(), v8_str("test_export"), v8_num(42));
2473324745
CHECK(set_export_result.FromJust());
24734-
// Synthetic module evaluation steps must return a Promise.
2473524746
Local<v8::Promise::Resolver> resolver =
2473624747
v8::Promise::Resolver::New(context).ToLocalChecked();
2473724748
resolver->Resolve(context, v8::Undefined(CcTest::isolate())).Check();
@@ -25074,6 +25085,40 @@ TEST(SyntheticModuleEvaluationStepsNoThrow) {
2507425085
CHECK_EQ(module->GetStatus(), Module::kEvaluated);
2507525086
}
2507625087

25088+
// Covers the deprecated evaluation steps version, where the returned Promise is
25089+
// only checked at runtime.
25090+
// TODO(https://crbug.com/545375591): Remove together with
25091+
// v8::Module::LegacySyntheticModuleEvaluationSteps.
25092+
TEST(SyntheticModuleEvaluationStepsLegacyCallback) {
25093+
synthetic_module_callback_count = 0;
25094+
LocalContext env;
25095+
v8::Isolate* isolate = env.isolate();
25096+
v8::Isolate::Scope iscope(isolate);
25097+
v8::HandleScope scope(isolate);
25098+
v8::Local<v8::Context> context = v8::Context::New(isolate);
25099+
v8::Context::Scope cscope(context);
25100+
25101+
auto export_names = std::to_array<Local<v8::String>>({v8_str("default")});
25102+
25103+
START_ALLOW_USE_DEPRECATED()
25104+
Local<Module> module = v8::Module::CreateSyntheticModule(
25105+
isolate,
25106+
v8_str("SyntheticModuleEvaluationStepsLegacyCallback-"
25107+
"TestSyntheticModule"),
25108+
export_names, LegacySyntheticModuleEvaluationStepsCallback);
25109+
END_ALLOW_USE_DEPRECATED()
25110+
module->InstantiateModule(context, UnexpectedModuleResolveCallback)
25111+
.ToChecked();
25112+
25113+
CHECK_EQ(synthetic_module_callback_count, 0);
25114+
Local<Value> completion_value = module->Evaluate(context).ToLocalChecked();
25115+
CHECK(completion_value->IsPromise());
25116+
Local<v8::Promise> promise(Local<v8::Promise>::Cast(completion_value));
25117+
CHECK_EQ(promise->State(), v8::Promise::kFulfilled);
25118+
CHECK_EQ(synthetic_module_callback_count, 1);
25119+
CHECK_EQ(module->GetStatus(), Module::kEvaluated);
25120+
}
25121+
2507725122
TEST(SyntheticModuleEvaluationStepsThrow) {
2507825123
synthetic_module_callback_count = 0;
2507925124
LocalContext env;
@@ -27098,7 +27143,7 @@ MaybeLocal<Module> CheckResolveModuleWithImportSource(
2709827143

2709927144
return v8::Module::CreateSyntheticModule(
2710027145
isolate, v8_str("my-mod"), {},
27101-
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Value> {
27146+
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Promise> {
2710227147
// Do nothing.
2710327148
Local<v8::Promise::Resolver> resolver =
2710427149
v8::Promise::Resolver::New(context).ToLocalChecked();

‎deps/v8/test/unittests/objects/modules-unittest.cc‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,7 +1686,7 @@ TEST_F(ModuleTest, SyntheticModuleGetResourceName) {
16861686
Local<String> resource_name = NewString("synthetic-module");
16871687
Local<Module> module = Module::CreateSyntheticModule(
16881688
isolate(), resource_name, {},
1689-
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Value> {
1689+
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Promise> {
16901690
// Do nothing.
16911691
Local<v8::Promise::Resolver> resolver =
16921692
v8::Promise::Resolver::New(context).ToLocalChecked();
@@ -1716,12 +1716,12 @@ TEST_F(ModuleTest, SyntheticModuleGetResourceNameInError) {
17161716
Local<String> resource_name = NewString("synthetic-module");
17171717
Local<Module> module = Module::CreateSyntheticModule(
17181718
isolate(), resource_name, {},
1719-
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Value> {
1719+
[](Local<Context> context, Local<Module> module) -> MaybeLocal<Promise> {
17201720
// Throw an error.
17211721
Isolate* isolate = Isolate::GetCurrent();
17221722
isolate->ThrowException(
17231723
v8::String::NewFromUtf8Literal(isolate, "synthetic module error"));
1724-
return MaybeLocal<Value>();
1724+
return MaybeLocal<Promise>();
17251725
});
17261726

17271727
CHECK_EQ(Module::kUninstantiated, module->GetStatus());

0 commit comments

Comments
 (0)