Skip to content

Commit

Permalink
[api] Add v8::ScriptCompiler::CachedData::CompatibilityCheck()
Browse files Browse the repository at this point in the history
This patch adds a new API v8::ScriptCompiler::CachedData::CompatibilityCheck()
in order to allow embedders to check if the code cache can be used in
the current isolate without looking up for the source code. It also returns more detailed reasons about why the code cache cannot be used
when it's bound to be rejected. This makes it possible to enforce
portability checks in case code code becomes CPU-dependent in the
future.

Refs: nodejs/node#42566 (comment)

Change-Id: Ia1d677b949050add961af6fbf62c44342c061312
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/4905290
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#90833}
  • Loading branch information
joyeecheung authored and V8 LUCI CQ committed Nov 9, 2023
1 parent 45d4d77 commit efb1133
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 16 deletions.
21 changes: 21 additions & 0 deletions include/v8-script.h
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,27 @@ class V8_EXPORT ScriptCompiler {
CachedData(const uint8_t* data, int length,
BufferPolicy buffer_policy = BufferNotOwned);
~CachedData();

enum CompatibilityCheckResult {
// Don't change order/existing values of this enum since it keys into the
// `code_cache_reject_reason` histogram. Append-only!
kSuccess = 0,
kMagicNumberMismatch = 1,
kVersionMismatch = 2,
kSourceMismatch = 3,
kFlagsMismatch = 5,
kChecksumMismatch = 6,
kInvalidHeader = 7,
kLengthMismatch = 8,
kReadOnlySnapshotChecksumMismatch = 9,

// This should always point at the last real enum value.
kLast = kReadOnlySnapshotChecksumMismatch
};

// Check if the CachedData can be loaded in the given isolate.
CompatibilityCheckResult CompatibilityCheck(Isolate* isolate);

// TODO(marja): Async compilation; add constructors which take a callback
// which will be called when V8 no longer needs the data.
const uint8_t* data;
Expand Down
12 changes: 12 additions & 0 deletions src/api/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1963,6 +1963,18 @@ ScriptCompiler::CachedData::~CachedData() {
}
}

ScriptCompiler::CachedData::CompatibilityCheckResult
ScriptCompiler::CachedData::CompatibilityCheck(Isolate* isolate) {
i::AlignedCachedData aligned(data, length);
i::Isolate* i_isolate = reinterpret_cast<i::Isolate*>(isolate);
i::SerializedCodeSanityCheckResult result;
i::SerializedCodeData scd =
i::SerializedCodeData::FromCachedDataWithoutSource(
i_isolate->AsLocalIsolate(), &aligned, &result);
return static_cast<ScriptCompiler::CachedData::CompatibilityCheckResult>(
result);
}

ScriptCompiler::StreamedSource::StreamedSource(
std::unique_ptr<ExternalSourceStream> stream, Encoding encoding)
: impl_(new i::ScriptStreamingData(std::move(stream), encoding)) {}
Expand Down
19 changes: 3 additions & 16 deletions src/snapshot/code-serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,22 +49,9 @@ class V8_EXPORT_PRIVATE AlignedCachedData {
int length_;
};

enum class SerializedCodeSanityCheckResult {
// Don't change order/existing values of this enum since it keys into the
// `code_cache_reject_reason` histogram. Append-only!
kSuccess = 0,
kMagicNumberMismatch = 1,
kVersionMismatch = 2,
kSourceMismatch = 3,
kFlagsMismatch = 5,
kChecksumMismatch = 6,
kInvalidHeader = 7,
kLengthMismatch = 8,
kReadOnlySnapshotChecksumMismatch = 9,

// This should always point at the last real enum value.
kLast = kReadOnlySnapshotChecksumMismatch
};
typedef v8::ScriptCompiler::CachedData::CompatibilityCheckResult
SerializedCodeSanityCheckResult;

// If this fails, update the static_assert AND the code_cache_reject_reason
// histogram definition.
static_assert(static_cast<int>(SerializedCodeSanityCheckResult::kLast) == 9);
Expand Down
72 changes: 72 additions & 0 deletions test/cctest/test-serialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2799,6 +2799,78 @@ TEST(CodeSerializerFlagChange) {
isolate2->Dispose();
}

TEST(CachedDataCompatibilityCheck) {
{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
// Hand-craft a zero-filled cached data which cannot be valid.
int length = 64;
uint8_t* payload = new uint8_t[length];
memset(payload, 0, length);
v8::ScriptCompiler::CachedData cache(
payload, length, v8::ScriptCompiler::CachedData::BufferOwned);
{
v8::Isolate::Scope iscope(isolate);
v8::ScriptCompiler::CachedData::CompatibilityCheckResult result =
cache.CompatibilityCheck(isolate);
CHECK_NE(result, v8::ScriptCompiler::CachedData::kSuccess);
}
isolate->Dispose();
}

const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
std::unique_ptr<v8::ScriptCompiler::CachedData> cache;
{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope iscope(isolate);
v8::HandleScope scope(isolate);
v8::Local<v8::Context> context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
v8::ScriptCompiler::Source source(v8_str(js_source),
{isolate, v8_str("test")});
v8::Local<v8::UnboundScript> script =
v8::ScriptCompiler::CompileUnboundScript(
isolate, &source, v8::ScriptCompiler::kEagerCompile)
.ToLocalChecked();
cache.reset(ScriptCompiler::CreateCodeCache(script));
}
isolate->Dispose();
}

{
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope iscope(isolate);
v8::ScriptCompiler::CachedData::CompatibilityCheckResult result =
cache->CompatibilityCheck(isolate);
CHECK_EQ(result, v8::ScriptCompiler::CachedData::kSuccess);
}
isolate->Dispose();
}

{
v8_flags.allow_natives_syntax =
true; // Flag change should trigger cache reject.
FlagList::EnforceFlagImplications();
v8::Isolate::CreateParams create_params;
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
v8::Isolate* isolate = v8::Isolate::New(create_params);
{
v8::Isolate::Scope iscope(isolate);
v8::ScriptCompiler::CachedData::CompatibilityCheckResult result =
cache->CompatibilityCheck(isolate);
CHECK_EQ(result, v8::ScriptCompiler::CachedData::kFlagsMismatch);
}
isolate->Dispose();
}
}

TEST(CodeSerializerBitFlip) {
i::v8_flags.verify_snapshot_checksum = true;
const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
Expand Down

0 comments on commit efb1133

Please sign in to comment.