2010-01-15 14:20:31 +00:00
|
|
|
// Copyright 2007-2010 the V8 project authors. All rights reserved.
|
2008-08-22 13:33:59 +00:00
|
|
|
// Redistribution and use in source and binary forms, with or without
|
|
|
|
// modification, are permitted provided that the following conditions are
|
|
|
|
// met:
|
|
|
|
//
|
|
|
|
// * Redistributions of source code must retain the above copyright
|
|
|
|
// notice, this list of conditions and the following disclaimer.
|
|
|
|
// * Redistributions in binary form must reproduce the above
|
|
|
|
// copyright notice, this list of conditions and the following
|
|
|
|
// disclaimer in the documentation and/or other materials provided
|
|
|
|
// with the distribution.
|
|
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
|
|
// contributors may be used to endorse or promote products derived
|
|
|
|
// from this software without specific prior written permission.
|
|
|
|
//
|
|
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
|
|
|
|
#include <signal.h>
|
2014-06-03 08:12:43 +00:00
|
|
|
#include <sys/stat.h>
|
2013-05-02 20:18:42 +00:00
|
|
|
|
2021-08-23 13:01:06 +00:00
|
|
|
#include "include/v8-extension.h"
|
|
|
|
#include "include/v8-function.h"
|
|
|
|
#include "include/v8-locker.h"
|
2019-05-17 12:13:44 +00:00
|
|
|
#include "src/api/api-inl.h"
|
2019-05-21 09:30:15 +00:00
|
|
|
#include "src/codegen/compilation-cache.h"
|
|
|
|
#include "src/codegen/compiler.h"
|
2021-08-04 07:36:35 +00:00
|
|
|
#include "src/codegen/script-details.h"
|
2020-09-17 15:19:09 +00:00
|
|
|
#include "src/common/assert-scope.h"
|
2019-02-14 21:10:30 +00:00
|
|
|
#include "src/heap/heap-inl.h"
|
2022-06-22 18:24:28 +00:00
|
|
|
#include "src/heap/parked-scope.h"
|
2019-03-13 15:24:44 +00:00
|
|
|
#include "src/heap/read-only-heap.h"
|
2020-09-16 08:16:01 +00:00
|
|
|
#include "src/heap/safepoint.h"
|
2014-08-05 08:18:22 +00:00
|
|
|
#include "src/heap/spaces.h"
|
2019-05-24 13:51:59 +00:00
|
|
|
#include "src/numbers/hash-seed-inl.h"
|
2018-08-16 16:01:36 +00:00
|
|
|
#include "src/objects/js-array-buffer-inl.h"
|
2018-12-18 07:42:37 +00:00
|
|
|
#include "src/objects/js-regexp-inl.h"
|
2019-05-23 08:51:46 +00:00
|
|
|
#include "src/objects/objects-inl.h"
|
2014-09-25 07:16:15 +00:00
|
|
|
#include "src/runtime/runtime.h"
|
2016-03-01 14:42:57 +00:00
|
|
|
#include "src/snapshot/code-serializer.h"
|
2020-04-22 10:40:59 +00:00
|
|
|
#include "src/snapshot/context-deserializer.h"
|
|
|
|
#include "src/snapshot/context-serializer.h"
|
2019-02-28 16:07:25 +00:00
|
|
|
#include "src/snapshot/read-only-deserializer.h"
|
2018-10-16 08:57:54 +00:00
|
|
|
#include "src/snapshot/read-only-serializer.h"
|
2021-10-08 22:52:07 +00:00
|
|
|
#include "src/snapshot/shared-heap-deserializer.h"
|
|
|
|
#include "src/snapshot/shared-heap-serializer.h"
|
2020-02-07 20:22:04 +00:00
|
|
|
#include "src/snapshot/snapshot-compression.h"
|
2015-03-27 15:28:55 +00:00
|
|
|
#include "src/snapshot/snapshot.h"
|
2017-08-02 14:00:37 +00:00
|
|
|
#include "src/snapshot/startup-deserializer.h"
|
2016-03-01 14:42:57 +00:00
|
|
|
#include "src/snapshot/startup-serializer.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
2016-05-20 13:30:22 +00:00
|
|
|
#include "test/cctest/heap/heap-utils.h"
|
2017-04-07 13:31:29 +00:00
|
|
|
#include "test/cctest/setup-isolate-for-tests.h"
|
2008-08-22 13:33:59 +00:00
|
|
|
|
2017-08-31 12:34:55 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2008-08-22 13:33:59 +00:00
|
|
|
|
2017-12-01 12:50:41 +00:00
|
|
|
enum CodeCacheType { kLazy, kEager, kAfterExecute };
|
|
|
|
|
2016-11-04 12:29:20 +00:00
|
|
|
void DisableAlwaysOpt() {
|
|
|
|
// Isolates prepared for serialization do not optimize. The only exception is
|
2022-04-28 14:22:23 +00:00
|
|
|
// with the flag --always-turbofan.
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.always_turbofan = false;
|
2015-03-05 14:46:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-26 14:33:30 +00:00
|
|
|
// A convenience struct to simplify management of the blobs required to
|
|
|
|
// deserialize an isolate.
|
|
|
|
struct StartupBlobs {
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> startup;
|
|
|
|
base::Vector<const byte> read_only;
|
2021-10-08 22:52:07 +00:00
|
|
|
base::Vector<const byte> shared_space;
|
2015-03-05 14:46:39 +00:00
|
|
|
|
2018-10-26 14:33:30 +00:00
|
|
|
void Dispose() {
|
|
|
|
startup.Dispose();
|
|
|
|
read_only.Dispose();
|
2021-10-08 22:52:07 +00:00
|
|
|
shared_space.Dispose();
|
2018-10-26 14:33:30 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// TestSerializer is used for testing isolate serialization.
|
|
|
|
class TestSerializer {
|
2015-02-25 11:14:40 +00:00
|
|
|
public:
|
2018-10-26 14:33:30 +00:00
|
|
|
static v8::Isolate* NewIsolateInitialized() {
|
[builtins] Properly handle cctest heap creation in embedded builds
Some cctests force fresh creation of heap constants, even though the
cctest binary itself is an embedded snapshot build (i.e.: a snapshot
blob exists, and a binary-embedded blob exists). This breaks a few
assumptions, for example that off-heap builtins have a single,
canonical off-heap code range.
Unfortunately this isn't that easy to fix. I see a few alternatives:
1. In builtins setup, if an embedded blob exists, regenerate the
builtins for their metadata (things like the safepoint table offset),
and then replace them by off-heap trampolines.
2. As above, but deserialize the trampolines from the snapshot blob.
3. As above, but pack required metadata into the embedded blob and
create trampolines from there.
4. Act as if the embedded blob does not exist.
Alternative 1 does not work because the generated code can be slightly
different at at runtime vs. mksnapshot-time. Alternative 2 is out
because we do not have access to the snapshot blob in TestIsolate
setup. Alternative 3 is probably the preferred option but would be a
more involved change.
This CL takes path 4. It's not an optimal solution, but it can be
replace by alternative 3 later.
TBR=ulan@chromium.org
Bug: v8:7718, v8:7751
Change-Id: I36c024cb0179615011c886ed3978bc95f0d197ac
Reviewed-on: https://chromium-review.googlesource.com/1098924
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53886}
2018-06-20 08:55:51 +00:00
|
|
|
const bool kEnableSerializer = true;
|
|
|
|
const bool kGenerateHeap = true;
|
2021-10-08 22:52:07 +00:00
|
|
|
const bool kIsShared = false;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2021-10-08 22:52:07 +00:00
|
|
|
v8::Isolate* v8_isolate =
|
|
|
|
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
2018-10-26 14:33:30 +00:00
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
2021-10-08 22:52:07 +00:00
|
|
|
isolate->Init(nullptr, nullptr, nullptr, false);
|
2015-02-25 11:14:40 +00:00
|
|
|
return v8_isolate;
|
|
|
|
}
|
2018-10-26 14:33:30 +00:00
|
|
|
|
|
|
|
// Wraps v8::Isolate::New, but with a test isolate under the hood.
|
2017-04-07 13:31:29 +00:00
|
|
|
// Allows flexibility to bootstrap with or without snapshot even when
|
|
|
|
// the production Isolate class has one or the other behavior baked in.
|
2018-10-26 14:33:30 +00:00
|
|
|
static v8::Isolate* NewIsolate(const v8::Isolate::CreateParams& params) {
|
[builtins] Properly handle cctest heap creation in embedded builds
Some cctests force fresh creation of heap constants, even though the
cctest binary itself is an embedded snapshot build (i.e.: a snapshot
blob exists, and a binary-embedded blob exists). This breaks a few
assumptions, for example that off-heap builtins have a single,
canonical off-heap code range.
Unfortunately this isn't that easy to fix. I see a few alternatives:
1. In builtins setup, if an embedded blob exists, regenerate the
builtins for their metadata (things like the safepoint table offset),
and then replace them by off-heap trampolines.
2. As above, but deserialize the trampolines from the snapshot blob.
3. As above, but pack required metadata into the embedded blob and
create trampolines from there.
4. Act as if the embedded blob does not exist.
Alternative 1 does not work because the generated code can be slightly
different at at runtime vs. mksnapshot-time. Alternative 2 is out
because we do not have access to the snapshot blob in TestIsolate
setup. Alternative 3 is probably the preferred option but would be a
more involved change.
This CL takes path 4. It's not an optimal solution, but it can be
replace by alternative 3 later.
TBR=ulan@chromium.org
Bug: v8:7718, v8:7751
Change-Id: I36c024cb0179615011c886ed3978bc95f0d197ac
Reviewed-on: https://chromium-review.googlesource.com/1098924
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Yang Guo <yangguo@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53886}
2018-06-20 08:55:51 +00:00
|
|
|
const bool kEnableSerializer = false;
|
|
|
|
const bool kGenerateHeap = params.snapshot_blob == nullptr;
|
2021-10-08 22:52:07 +00:00
|
|
|
const bool kIsShared = false;
|
|
|
|
v8::Isolate* v8_isolate =
|
|
|
|
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
|
2018-04-17 14:35:13 +00:00
|
|
|
v8::Isolate::Initialize(v8_isolate, params);
|
|
|
|
return v8_isolate;
|
2017-04-07 13:31:29 +00:00
|
|
|
}
|
2018-10-26 14:33:30 +00:00
|
|
|
|
2022-08-26 22:07:04 +00:00
|
|
|
static v8::Isolate* NewIsolateFromBlob(const StartupBlobs& blobs) {
|
|
|
|
SnapshotData startup_snapshot(blobs.startup);
|
|
|
|
SnapshotData read_only_snapshot(blobs.read_only);
|
|
|
|
SnapshotData shared_space_snapshot(blobs.shared_space);
|
|
|
|
const bool kEnableSerializer = false;
|
|
|
|
const bool kGenerateHeap = false;
|
|
|
|
const bool kIsShared = false;
|
|
|
|
v8::Isolate* v8_isolate =
|
|
|
|
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
|
|
|
isolate->Init(&startup_snapshot, &read_only_snapshot,
|
|
|
|
&shared_space_snapshot, false);
|
2018-10-26 14:33:30 +00:00
|
|
|
return v8_isolate;
|
2015-04-29 09:54:34 +00:00
|
|
|
}
|
2021-10-08 22:52:07 +00:00
|
|
|
|
2022-08-26 22:07:04 +00:00
|
|
|
static void InitializeProcessWideSharedIsolateFromBlob(
|
|
|
|
const StartupBlobs& blobs) {
|
|
|
|
base::MutexGuard guard(
|
|
|
|
i::Isolate::process_wide_shared_isolate_mutex_.Pointer());
|
|
|
|
CHECK_NULL(i::Isolate::process_wide_shared_isolate_);
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
SnapshotData startup_snapshot(blobs.startup);
|
|
|
|
SnapshotData read_only_snapshot(blobs.read_only);
|
|
|
|
SnapshotData shared_space_snapshot(blobs.shared_space);
|
|
|
|
const bool kEnableSerializer = false;
|
|
|
|
const bool kGenerateHeap = false;
|
2022-08-26 22:07:04 +00:00
|
|
|
const bool kIsShared = true;
|
2021-10-08 22:52:07 +00:00
|
|
|
v8::Isolate* v8_isolate =
|
2022-08-26 22:07:04 +00:00
|
|
|
NewIsolate(kEnableSerializer, kGenerateHeap, kIsShared);
|
2021-10-08 22:52:07 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
|
|
|
isolate->Init(&startup_snapshot, &read_only_snapshot,
|
|
|
|
&shared_space_snapshot, false);
|
2022-08-26 22:07:04 +00:00
|
|
|
i::Isolate::process_wide_shared_isolate_ = isolate;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DeleteProcessWideSharedIsolate() {
|
|
|
|
i::Isolate::DeleteProcessWideSharedIsolate();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
// Creates an Isolate instance configured for testing.
|
|
|
|
static v8::Isolate* NewIsolate(bool with_serializer, bool generate_heap,
|
|
|
|
bool is_shared) {
|
|
|
|
i::Isolate* isolate;
|
|
|
|
if (is_shared) {
|
|
|
|
isolate = i::Isolate::Allocate(true);
|
|
|
|
} else {
|
|
|
|
isolate = i::Isolate::New();
|
|
|
|
}
|
|
|
|
v8::Isolate* v8_isolate = reinterpret_cast<v8::Isolate*>(isolate);
|
|
|
|
|
|
|
|
if (with_serializer) isolate->enable_serializer();
|
|
|
|
isolate->set_array_buffer_allocator(CcTest::array_buffer_allocator());
|
|
|
|
isolate->setup_delegate_ = new SetupIsolateDelegateForTests(generate_heap);
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
return v8_isolate;
|
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
};
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
static base::Vector<const byte> WritePayload(
|
|
|
|
const base::Vector<const byte>& payload) {
|
2016-03-10 09:57:47 +00:00
|
|
|
int length = payload.length();
|
|
|
|
byte* blob = NewArray<byte>(length);
|
|
|
|
memcpy(blob, payload.begin(), length);
|
2021-06-17 15:43:55 +00:00
|
|
|
return base::Vector<const byte>(const_cast<const byte*>(blob), length);
|
2014-10-23 11:23:57 +00:00
|
|
|
}
|
2010-01-27 08:25:48 +00:00
|
|
|
|
2018-04-19 13:45:52 +00:00
|
|
|
namespace {
|
|
|
|
|
2019-05-16 12:19:13 +00:00
|
|
|
// Convenience wrapper around the convenience wrapper.
|
|
|
|
v8::StartupData CreateSnapshotDataBlob(const char* embedded_source) {
|
2019-06-19 14:36:43 +00:00
|
|
|
v8::StartupData data = CreateSnapshotDataBlobInternal(
|
2018-12-18 07:42:37 +00:00
|
|
|
v8::SnapshotCreator::FunctionCodeHandling::kClear, embedded_source);
|
2019-06-19 14:36:43 +00:00
|
|
|
return data;
|
2018-12-18 07:42:37 +00:00
|
|
|
}
|
|
|
|
|
2018-04-19 13:45:52 +00:00
|
|
|
} // namespace
|
|
|
|
|
2017-08-28 15:49:54 +00:00
|
|
|
static StartupBlobs Serialize(v8::Isolate* isolate) {
|
2009-10-27 11:54:01 +00:00
|
|
|
// We have to create one context. One reason for this is so that the builtins
|
2017-12-19 19:42:59 +00:00
|
|
|
// can be loaded from self hosted JS builtins and their addresses can be
|
|
|
|
// processed. This will clear the pending fixups array, which would otherwise
|
|
|
|
// contain GC roots that would confuse the serialization/deserialization
|
|
|
|
// process.
|
2014-09-19 08:01:35 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2013-05-08 07:45:16 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
v8::Context::New(isolate);
|
|
|
|
}
|
2013-09-19 12:06:27 +00:00
|
|
|
|
2022-04-27 18:52:38 +00:00
|
|
|
Isolate* i_isolate = reinterpret_cast<Isolate*>(isolate);
|
2022-09-01 17:40:05 +00:00
|
|
|
CcTest::CollectAllAvailableGarbage(i_isolate);
|
2018-10-16 08:57:54 +00:00
|
|
|
|
2022-10-25 14:05:07 +00:00
|
|
|
IsolateSafepointScope safepoint(i_isolate->heap());
|
2022-04-27 18:52:38 +00:00
|
|
|
HandleScope scope(i_isolate);
|
2020-09-16 08:16:01 +00:00
|
|
|
|
2020-09-17 15:19:09 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
2022-04-27 18:52:38 +00:00
|
|
|
ReadOnlySerializer read_only_serializer(i_isolate,
|
2020-04-28 09:53:18 +00:00
|
|
|
Snapshot::kDefaultSerializerFlags);
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.SerializeReadOnlyRoots();
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
SharedHeapSerializer shared_space_serializer(
|
2022-04-27 18:52:38 +00:00
|
|
|
i_isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
|
2021-10-08 22:52:07 +00:00
|
|
|
|
2022-04-27 18:52:38 +00:00
|
|
|
StartupSerializer ser(i_isolate, Snapshot::kDefaultSerializerFlags,
|
2021-10-08 22:52:07 +00:00
|
|
|
&read_only_serializer, &shared_space_serializer);
|
2020-05-04 12:34:40 +00:00
|
|
|
ser.SerializeStrongReferences(no_gc);
|
2017-08-28 15:49:54 +00:00
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
ser.SerializeWeakReferencesAndDeferred();
|
2021-10-08 22:52:07 +00:00
|
|
|
|
|
|
|
shared_space_serializer.FinalizeSerialization();
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.FinalizeSerialization();
|
2017-08-28 15:49:54 +00:00
|
|
|
SnapshotData startup_snapshot(&ser);
|
2018-10-16 08:57:54 +00:00
|
|
|
SnapshotData read_only_snapshot(&read_only_serializer);
|
2021-10-08 22:52:07 +00:00
|
|
|
SnapshotData shared_space_snapshot(&shared_space_serializer);
|
2017-08-28 15:49:54 +00:00
|
|
|
return {WritePayload(startup_snapshot.RawData()),
|
2021-10-08 22:52:07 +00:00
|
|
|
WritePayload(read_only_snapshot.RawData()),
|
|
|
|
WritePayload(shared_space_snapshot.RawData())};
|
2008-08-22 13:33:59 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> ConstructSource(base::Vector<const char> head,
|
|
|
|
base::Vector<const char> body,
|
|
|
|
base::Vector<const char> tail,
|
|
|
|
int repeats) {
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
size_t source_length = head.size() + body.size() * repeats + tail.size();
|
|
|
|
char* source = NewArray<char>(source_length);
|
2019-04-29 11:06:49 +00:00
|
|
|
CopyChars(source, head.begin(), head.length());
|
2015-01-27 14:56:51 +00:00
|
|
|
for (int i = 0; i < repeats; i++) {
|
2019-04-29 11:06:49 +00:00
|
|
|
CopyChars(source + head.length() + i * body.length(), body.begin(),
|
2015-01-27 14:56:51 +00:00
|
|
|
body.length());
|
|
|
|
}
|
2019-04-29 11:06:49 +00:00
|
|
|
CopyChars(source + head.length() + repeats * body.length(), tail.begin(),
|
2015-01-27 14:56:51 +00:00
|
|
|
tail.length());
|
2021-06-17 15:43:55 +00:00
|
|
|
return base::VectorOf(source, source_length);
|
2015-01-27 14:56:51 +00:00
|
|
|
}
|
|
|
|
|
2019-09-10 01:19:59 +00:00
|
|
|
static v8::Isolate* Deserialize(const StartupBlobs& blobs) {
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolateFromBlob(blobs);
|
2014-09-19 08:01:35 +00:00
|
|
|
CHECK(isolate);
|
|
|
|
return isolate;
|
2008-08-22 13:33:59 +00:00
|
|
|
}
|
|
|
|
|
2014-09-19 08:01:35 +00:00
|
|
|
static void SanityCheck(v8::Isolate* v8_isolate) {
|
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
|
|
|
v8::HandleScope scope(v8_isolate);
|
2012-10-15 06:34:22 +00:00
|
|
|
#ifdef VERIFY_HEAP
|
2022-08-30 07:18:43 +00:00
|
|
|
HeapVerifier::VerifyHeap(isolate->heap());
|
2008-08-22 13:33:59 +00:00
|
|
|
#endif
|
2013-06-04 10:30:05 +00:00
|
|
|
CHECK(isolate->global_object()->IsJSObject());
|
|
|
|
CHECK(isolate->native_context()->IsContext());
|
2021-06-17 15:43:55 +00:00
|
|
|
isolate->factory()->InternalizeString(base::StaticCharVector("Empty"));
|
2008-08-22 13:33:59 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 08:40:17 +00:00
|
|
|
void TestStartupSerializerOnceImpl() {
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolateInitialized();
|
2017-08-28 15:49:54 +00:00
|
|
|
StartupBlobs blobs = Serialize(isolate);
|
2017-10-05 18:08:53 +00:00
|
|
|
isolate->Dispose();
|
2017-08-28 15:49:54 +00:00
|
|
|
isolate = Deserialize(blobs);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2008-08-22 13:33:59 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Local<v8::Context> env = v8::Context::New(isolate);
|
|
|
|
env->Enter();
|
2009-11-25 12:55:33 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SanityCheck(isolate);
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2009-11-25 12:55:33 +00:00
|
|
|
}
|
|
|
|
|
2018-07-03 08:40:17 +00:00
|
|
|
UNINITIALIZED_TEST(StartupSerializerOnce) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
TestStartupSerializerOnceImpl();
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
UNINITIALIZED_TEST(StartupSerializerTwice) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolateInitialized();
|
2017-08-28 15:49:54 +00:00
|
|
|
StartupBlobs blobs1 = Serialize(isolate);
|
|
|
|
StartupBlobs blobs2 = Serialize(isolate);
|
2017-10-05 18:08:53 +00:00
|
|
|
isolate->Dispose();
|
2017-08-28 15:49:54 +00:00
|
|
|
blobs1.Dispose();
|
|
|
|
isolate = Deserialize(blobs2);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2009-11-25 12:55:33 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Local<v8::Context> env = v8::Context::New(isolate);
|
|
|
|
env->Enter();
|
2009-10-27 11:54:01 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SanityCheck(isolate);
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs2.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2009-10-27 11:54:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
UNINITIALIZED_TEST(StartupSerializerOnceRunScript) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolateInitialized();
|
2017-08-28 15:49:54 +00:00
|
|
|
StartupBlobs blobs = Serialize(isolate);
|
2017-10-05 18:08:53 +00:00
|
|
|
isolate->Dispose();
|
2017-08-28 15:49:54 +00:00
|
|
|
isolate = Deserialize(blobs);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Local<v8::Context> env = v8::Context::New(isolate);
|
|
|
|
env->Enter();
|
2009-10-27 11:54:01 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
const char* c_source = "\"1234\".length";
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Script> script = v8_compile(c_source);
|
|
|
|
v8::Maybe<int32_t> result = script->Run(isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(isolate->GetCurrentContext());
|
|
|
|
CHECK_EQ(4, result.FromJust());
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2009-10-27 11:54:01 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
UNINITIALIZED_TEST(StartupSerializerTwiceRunScript) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolateInitialized();
|
2017-08-28 15:49:54 +00:00
|
|
|
StartupBlobs blobs1 = Serialize(isolate);
|
|
|
|
StartupBlobs blobs2 = Serialize(isolate);
|
2017-10-05 18:08:53 +00:00
|
|
|
isolate->Dispose();
|
2017-08-28 15:49:54 +00:00
|
|
|
blobs1.Dispose();
|
|
|
|
isolate = Deserialize(blobs2);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2009-11-25 12:55:33 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Local<v8::Context> env = v8::Context::New(isolate);
|
|
|
|
env->Enter();
|
2009-11-25 12:55:33 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
const char* c_source = "\"1234\".length";
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Script> script = v8_compile(c_source);
|
|
|
|
v8::Maybe<int32_t> result = script->Run(isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(isolate->GetCurrentContext());
|
|
|
|
CHECK_EQ(4, result.FromJust());
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs2.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2009-11-25 12:55:33 +00:00
|
|
|
}
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
static void SerializeContext(base::Vector<const byte>* startup_blob_out,
|
|
|
|
base::Vector<const byte>* read_only_blob_out,
|
2021-10-08 22:52:07 +00:00
|
|
|
base::Vector<const byte>* shared_space_blob_out,
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte>* context_blob_out) {
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = TestSerializer::NewIsolateInitialized();
|
2015-02-25 11:14:40 +00:00
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
|
|
|
Heap* heap = isolate->heap();
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Persistent<v8::Context> env;
|
|
|
|
{
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
env.Reset(v8_isolate, v8::Context::New(v8_isolate));
|
|
|
|
}
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(!env.IsEmpty());
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
|
|
|
|
}
|
2017-02-06 10:18:05 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
// If we don't do this then we end up with a stray root pointing at the
|
|
|
|
// context even after we have disposed of env.
|
2022-09-01 17:40:05 +00:00
|
|
|
CcTest::CollectAllAvailableGarbage(isolate);
|
2010-03-23 11:40:38 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
|
|
|
|
}
|
2014-09-18 21:57:10 +00:00
|
|
|
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
HandleScope scope(isolate);
|
2018-11-23 10:06:32 +00:00
|
|
|
i::Context raw_context = i::Context::cast(*v8::Utils::OpenPersistent(env));
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
env.Reset();
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2022-10-25 14:05:07 +00:00
|
|
|
IsolateSafepointScope safepoint(heap);
|
2020-09-16 08:16:01 +00:00
|
|
|
|
2020-09-17 15:19:09 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
2018-10-16 08:57:54 +00:00
|
|
|
SnapshotByteSink read_only_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
ReadOnlySerializer read_only_serializer(isolate,
|
|
|
|
Snapshot::kDefaultSerializerFlags);
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.SerializeReadOnlyRoots();
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
SharedHeapSerializer shared_space_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
|
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SnapshotByteSink startup_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
StartupSerializer startup_serializer(
|
2021-10-08 22:52:07 +00:00
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer,
|
|
|
|
&shared_space_serializer);
|
2020-05-04 12:34:40 +00:00
|
|
|
startup_serializer.SerializeStrongReferences(no_gc);
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotByteSink context_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
ContextSerializer context_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &startup_serializer,
|
|
|
|
v8::SerializeInternalFieldsCallback());
|
2020-05-04 11:25:58 +00:00
|
|
|
context_serializer.Serialize(&raw_context, no_gc);
|
2017-08-28 15:49:54 +00:00
|
|
|
|
2015-05-15 07:42:40 +00:00
|
|
|
startup_serializer.SerializeWeakReferencesAndDeferred();
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
shared_space_serializer.FinalizeSerialization();
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.FinalizeSerialization();
|
|
|
|
|
|
|
|
SnapshotData read_only_snapshot(&read_only_serializer);
|
2021-10-08 22:52:07 +00:00
|
|
|
SnapshotData shared_space_snapshot(&shared_space_serializer);
|
2016-06-10 10:58:10 +00:00
|
|
|
SnapshotData startup_snapshot(&startup_serializer);
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData context_snapshot(&context_serializer);
|
2014-10-23 11:23:57 +00:00
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
*context_blob_out = WritePayload(context_snapshot.RawData());
|
2016-03-10 09:57:47 +00:00
|
|
|
*startup_blob_out = WritePayload(startup_snapshot.RawData());
|
2018-10-16 08:57:54 +00:00
|
|
|
*read_only_blob_out = WritePayload(read_only_snapshot.RawData());
|
2021-10-08 22:52:07 +00:00
|
|
|
*shared_space_blob_out = WritePayload(shared_space_snapshot.RawData());
|
2012-06-19 18:38:03 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
v8_isolate->Dispose();
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
|
|
|
|
2022-08-17 07:35:35 +00:00
|
|
|
#ifdef SNAPSHOT_COMPRESSION
|
2020-02-07 20:22:04 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCompression) {
|
|
|
|
DisableAlwaysOpt();
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> startup_blob;
|
|
|
|
base::Vector<const byte> read_only_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
base::Vector<const byte> shared_space_blob;
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> context_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
SerializeContext(&startup_blob, &read_only_blob, &shared_space_blob,
|
|
|
|
&context_blob);
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData original_snapshot_data(context_blob);
|
2020-02-07 20:22:04 +00:00
|
|
|
SnapshotData compressed =
|
|
|
|
i::SnapshotCompression::Compress(&original_snapshot_data);
|
|
|
|
SnapshotData decompressed =
|
|
|
|
i::SnapshotCompression::Decompress(compressed.RawData());
|
2020-04-22 09:40:35 +00:00
|
|
|
CHECK_EQ(context_blob, decompressed.RawData());
|
2020-02-07 20:22:04 +00:00
|
|
|
|
|
|
|
startup_blob.Dispose();
|
|
|
|
read_only_blob.Dispose();
|
2021-10-08 22:52:07 +00:00
|
|
|
shared_space_blob.Dispose();
|
2020-04-22 09:40:35 +00:00
|
|
|
context_blob.Dispose();
|
2020-02-07 20:22:04 +00:00
|
|
|
}
|
2022-08-17 07:35:35 +00:00
|
|
|
#endif // SNAPSHOT_COMPRESSION
|
2020-02-07 20:22:04 +00:00
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
UNINITIALIZED_TEST(ContextSerializerContext) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> startup_blob;
|
|
|
|
base::Vector<const byte> read_only_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
base::Vector<const byte> shared_space_blob;
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> context_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
SerializeContext(&startup_blob, &read_only_blob, &shared_space_blob,
|
|
|
|
&context_blob);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
StartupBlobs blobs = {startup_blob, read_only_blob, shared_space_blob};
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = TestSerializer::NewIsolateFromBlob(blobs);
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(v8_isolate);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
2010-03-23 11:40:38 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
|
|
|
HandleScope handle_scope(isolate);
|
|
|
|
Handle<Object> root;
|
|
|
|
Handle<JSGlobalProxy> global_proxy =
|
2016-11-04 10:02:02 +00:00
|
|
|
isolate->factory()->NewUninitializedJSGlobalProxy(
|
2017-03-17 13:26:05 +00:00
|
|
|
JSGlobalProxy::SizeWithEmbedderFields(0));
|
2014-09-18 21:57:10 +00:00
|
|
|
{
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData snapshot_data(context_blob);
|
|
|
|
root = ContextDeserializer::DeserializeContext(
|
2017-08-08 11:19:19 +00:00
|
|
|
isolate, &snapshot_data, false, global_proxy,
|
|
|
|
v8::DeserializeInternalFieldsCallback())
|
2015-12-01 23:35:57 +00:00
|
|
|
.ToHandleChecked();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(root->IsContext());
|
|
|
|
CHECK(Handle<Context>::cast(root)->global_proxy() == *global_proxy);
|
|
|
|
}
|
2014-09-19 08:01:35 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
Handle<Object> root2;
|
|
|
|
{
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData snapshot_data(context_blob);
|
|
|
|
root2 = ContextDeserializer::DeserializeContext(
|
2017-08-08 11:19:19 +00:00
|
|
|
isolate, &snapshot_data, false, global_proxy,
|
|
|
|
v8::DeserializeInternalFieldsCallback())
|
2015-12-01 23:35:57 +00:00
|
|
|
.ToHandleChecked();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(root2->IsContext());
|
|
|
|
CHECK(!root.is_identical_to(root2));
|
2015-01-13 08:48:00 +00:00
|
|
|
}
|
2020-04-22 09:40:35 +00:00
|
|
|
context_blob.Dispose();
|
2015-01-13 08:48:00 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
v8_isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2015-01-13 08:48:00 +00:00
|
|
|
}
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
static void SerializeCustomContext(
|
|
|
|
base::Vector<const byte>* startup_blob_out,
|
|
|
|
base::Vector<const byte>* read_only_blob_out,
|
|
|
|
base::Vector<const byte>* shared_space_blob_out,
|
|
|
|
base::Vector<const byte>* context_blob_out) {
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = TestSerializer::NewIsolateInitialized();
|
2015-02-25 11:14:40 +00:00
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Persistent<v8::Context> env;
|
|
|
|
{
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
env.Reset(v8_isolate, v8::Context::New(v8_isolate));
|
|
|
|
}
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(!env.IsEmpty());
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
v8::Local<v8::Context>::New(v8_isolate, env)->Enter();
|
|
|
|
// After execution, e's function context refers to the global object.
|
|
|
|
CompileRun(
|
|
|
|
"var e;"
|
|
|
|
"(function() {"
|
|
|
|
" e = function(s) { return eval (s); }"
|
|
|
|
"})();"
|
|
|
|
"var o = this;"
|
2016-03-16 15:02:41 +00:00
|
|
|
"var r = Math.random();"
|
|
|
|
"var c = Math.sin(0) + Math.cos(0);"
|
2015-02-25 11:14:40 +00:00
|
|
|
"var f = (function(a, b) { return a + b; }).bind(1, 2, 3);"
|
2018-11-27 08:32:53 +00:00
|
|
|
"var s = parseInt('12345');"
|
|
|
|
"var p = 0;"
|
|
|
|
"(async ()=>{ p = await 42; })();");
|
2015-02-25 11:14:40 +00:00
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source = ConstructSource(
|
|
|
|
base::StaticCharVector("function g() { return [,"),
|
|
|
|
base::StaticCharVector("1,"),
|
|
|
|
base::StaticCharVector("];} a = g(); b = g(); b.push(1);"), 100000);
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
v8::MaybeLocal<v8::String> source_str =
|
|
|
|
v8::String::NewFromUtf8(v8_isolate, source.begin(),
|
|
|
|
v8::NewStringType::kNormal, source.length());
|
2015-10-08 09:48:05 +00:00
|
|
|
CompileRun(source_str.ToLocalChecked());
|
2015-02-25 11:14:40 +00:00
|
|
|
source.Dispose();
|
|
|
|
}
|
|
|
|
// If we don't do this then we end up with a stray root pointing at the
|
|
|
|
// context even after we have disposed of env.
|
2022-09-01 17:40:05 +00:00
|
|
|
CcTest::CollectAllAvailableGarbage(isolate);
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(v8_isolate);
|
|
|
|
v8::Local<v8::Context>::New(v8_isolate, env)->Exit();
|
|
|
|
}
|
2015-01-13 08:48:00 +00:00
|
|
|
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
HandleScope scope(isolate);
|
2018-11-23 10:06:32 +00:00
|
|
|
i::Context raw_context = i::Context::cast(*v8::Utils::OpenPersistent(env));
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
env.Reset();
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2022-10-25 14:05:07 +00:00
|
|
|
IsolateSafepointScope safepoint(isolate->heap());
|
2020-09-16 08:16:01 +00:00
|
|
|
|
2020-09-17 15:19:09 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
2018-10-16 08:57:54 +00:00
|
|
|
SnapshotByteSink read_only_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
ReadOnlySerializer read_only_serializer(isolate,
|
|
|
|
Snapshot::kDefaultSerializerFlags);
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.SerializeReadOnlyRoots();
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
SharedHeapSerializer shared_space_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer);
|
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SnapshotByteSink startup_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
StartupSerializer startup_serializer(
|
2021-10-08 22:52:07 +00:00
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_serializer,
|
|
|
|
&shared_space_serializer);
|
2020-05-04 12:34:40 +00:00
|
|
|
startup_serializer.SerializeStrongReferences(no_gc);
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotByteSink context_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
ContextSerializer context_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &startup_serializer,
|
|
|
|
v8::SerializeInternalFieldsCallback());
|
2020-05-04 11:25:58 +00:00
|
|
|
context_serializer.Serialize(&raw_context, no_gc);
|
2017-08-28 15:49:54 +00:00
|
|
|
|
2015-05-15 07:42:40 +00:00
|
|
|
startup_serializer.SerializeWeakReferencesAndDeferred();
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
shared_space_serializer.FinalizeSerialization();
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.FinalizeSerialization();
|
|
|
|
|
|
|
|
SnapshotData read_only_snapshot(&read_only_serializer);
|
2021-10-08 22:52:07 +00:00
|
|
|
SnapshotData shared_space_snapshot(&shared_space_serializer);
|
2016-06-10 10:58:10 +00:00
|
|
|
SnapshotData startup_snapshot(&startup_serializer);
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData context_snapshot(&context_serializer);
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
*context_blob_out = WritePayload(context_snapshot.RawData());
|
2016-03-10 09:57:47 +00:00
|
|
|
*startup_blob_out = WritePayload(startup_snapshot.RawData());
|
2018-10-16 08:57:54 +00:00
|
|
|
*read_only_blob_out = WritePayload(read_only_snapshot.RawData());
|
2021-10-08 22:52:07 +00:00
|
|
|
*shared_space_blob_out = WritePayload(shared_space_snapshot.RawData());
|
2015-01-13 08:48:00 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
v8_isolate->Dispose();
|
2015-01-13 08:48:00 +00:00
|
|
|
}
|
|
|
|
|
2020-04-22 09:40:35 +00:00
|
|
|
UNINITIALIZED_TEST(ContextSerializerCustomContext) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> startup_blob;
|
|
|
|
base::Vector<const byte> read_only_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
base::Vector<const byte> shared_space_blob;
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const byte> context_blob;
|
2021-10-08 22:52:07 +00:00
|
|
|
SerializeCustomContext(&startup_blob, &read_only_blob, &shared_space_blob,
|
|
|
|
&context_blob);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
StartupBlobs blobs = {startup_blob, read_only_blob, shared_space_blob};
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = TestSerializer::NewIsolateFromBlob(blobs);
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(v8_isolate);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
2015-01-13 08:48:00 +00:00
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
|
|
|
|
HandleScope handle_scope(isolate);
|
|
|
|
Handle<Object> root;
|
|
|
|
Handle<JSGlobalProxy> global_proxy =
|
2016-11-04 10:02:02 +00:00
|
|
|
isolate->factory()->NewUninitializedJSGlobalProxy(
|
2017-03-17 13:26:05 +00:00
|
|
|
JSGlobalProxy::SizeWithEmbedderFields(0));
|
2015-01-13 08:48:00 +00:00
|
|
|
{
|
2020-04-22 09:40:35 +00:00
|
|
|
SnapshotData snapshot_data(context_blob);
|
|
|
|
root = ContextDeserializer::DeserializeContext(
|
2017-08-08 11:19:19 +00:00
|
|
|
isolate, &snapshot_data, false, global_proxy,
|
|
|
|
v8::DeserializeInternalFieldsCallback())
|
2015-12-01 23:35:57 +00:00
|
|
|
.ToHandleChecked();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(root->IsContext());
|
|
|
|
Handle<Context> context = Handle<Context>::cast(root);
|
2016-03-10 11:18:13 +00:00
|
|
|
|
|
|
|
// Add context to the weak native context list
|
|
|
|
context->set(Context::NEXT_CONTEXT_LINK,
|
|
|
|
isolate->heap()->native_contexts_list(),
|
2022-05-31 08:02:41 +00:00
|
|
|
UPDATE_WRITE_BARRIER);
|
2016-03-10 11:18:13 +00:00
|
|
|
isolate->heap()->set_native_contexts_list(*context);
|
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(context->global_proxy() == *global_proxy);
|
|
|
|
Handle<String> o = isolate->factory()->NewStringFromAsciiChecked("o");
|
|
|
|
Handle<JSObject> global_object(context->global_object(), isolate);
|
2022-02-18 18:15:48 +00:00
|
|
|
Handle<Object> property =
|
|
|
|
JSReceiver::GetDataProperty(isolate, global_object, o);
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(property.is_identical_to(global_proxy));
|
|
|
|
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Context> v8_context = v8::Utils::ToLocal(context);
|
2015-02-25 11:14:40 +00:00
|
|
|
v8::Context::Scope context_scope(v8_context);
|
2015-10-08 09:48:05 +00:00
|
|
|
double r = CompileRun("r")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Value();
|
2016-03-16 15:02:41 +00:00
|
|
|
CHECK(0.0 <= r && r < 1.0);
|
|
|
|
// Math.random still works.
|
|
|
|
double random = CompileRun("Math.random()")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Value();
|
|
|
|
CHECK(0.0 <= random && random < 1.0);
|
|
|
|
double c = CompileRun("c")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Value();
|
|
|
|
CHECK_EQ(1, c);
|
2015-10-08 09:48:05 +00:00
|
|
|
int f = CompileRun("f()")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(v8_isolate->GetCurrentContext())
|
|
|
|
.FromJust();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK_EQ(5, f);
|
2015-10-08 09:48:05 +00:00
|
|
|
f = CompileRun("e('f()')")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(v8_isolate->GetCurrentContext())
|
|
|
|
.FromJust();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK_EQ(5, f);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::String> s = CompileRun("s")
|
|
|
|
->ToString(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(s->Equals(v8_isolate->GetCurrentContext(), v8_str("12345"))
|
|
|
|
.FromJust());
|
2018-11-27 08:32:53 +00:00
|
|
|
v8::Local<v8::String> p = CompileRun("p")
|
|
|
|
->ToString(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(
|
|
|
|
p->Equals(v8_isolate->GetCurrentContext(), v8_str("42")).FromJust());
|
2015-10-08 09:48:05 +00:00
|
|
|
int a = CompileRun("a.length")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(v8_isolate->GetCurrentContext())
|
|
|
|
.FromJust();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK_EQ(100001, a);
|
2015-10-08 09:48:05 +00:00
|
|
|
int b = CompileRun("b.length")
|
|
|
|
->ToNumber(v8_isolate->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(v8_isolate->GetCurrentContext())
|
|
|
|
.FromJust();
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK_EQ(100002, b);
|
2010-03-23 11:40:38 +00:00
|
|
|
}
|
2020-04-22 09:40:35 +00:00
|
|
|
context_blob.Dispose();
|
2015-02-25 11:14:40 +00:00
|
|
|
}
|
|
|
|
v8_isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
blobs.Dispose();
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2015-02-25 11:14:40 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlob1) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2015-02-25 11:14:40 +00:00
|
|
|
const char* source1 = "function f() { return 42; }";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
2015-04-29 09:54:34 +00:00
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolate(params1);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope c_scope(context);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("f()")->Int32Value(isolate1->GetCurrentContext());
|
|
|
|
CHECK_EQ(42, result.FromJust());
|
2015-02-25 11:14:40 +00:00
|
|
|
CHECK(CompileRun("this.g")->IsUndefined());
|
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-11-04 09:04:11 +00:00
|
|
|
}
|
|
|
|
|
2019-01-14 13:27:52 +00:00
|
|
|
static void UnreachableCallback(const FunctionCallbackInfo<Value>& args) {
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobOverwriteGlobal) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
const char* source1 = "function f() { return 42; }";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2019-01-14 13:27:52 +00:00
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
|
|
|
// Test that the snapshot overwrites the object template when there are
|
|
|
|
// duplicate global properties.
|
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolate(params1);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate1);
|
|
|
|
global_template->Set(
|
2020-09-09 11:07:28 +00:00
|
|
|
isolate1, "f",
|
|
|
|
v8::FunctionTemplate::New(isolate1, UnreachableCallback));
|
2019-01-14 13:27:52 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::New(isolate1, nullptr, global_template);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("f()")->Int32Value(isolate1->GetCurrentContext());
|
|
|
|
CHECK_EQ(42, result.FromJust());
|
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2018-12-10 13:24:52 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobStringNotInternalized) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
const char* source1 =
|
|
|
|
R"javascript(
|
2022-07-29 10:35:23 +00:00
|
|
|
// String would be internalized if it came from a literal so create "AB"
|
2018-12-10 13:24:52 +00:00
|
|
|
// via a function call.
|
2022-07-29 10:35:23 +00:00
|
|
|
var global = String.fromCharCode(65, 66);
|
2018-12-10 13:24:52 +00:00
|
|
|
function f() { return global; }
|
|
|
|
)javascript";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-12-10 13:24:52 +00:00
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolate(params1);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
v8::Local<v8::Value> result = CompileRun("f()").As<v8::Value>();
|
|
|
|
CHECK(result->IsString());
|
|
|
|
i::String str = *v8::Utils::OpenHandle(*result.As<v8::String>());
|
2022-07-29 10:35:23 +00:00
|
|
|
CHECK_EQ(std::string(str.ToCString().get()), "AB");
|
2018-12-10 13:24:52 +00:00
|
|
|
CHECK(!str.IsInternalizedString());
|
2019-03-13 15:24:44 +00:00
|
|
|
CHECK(!i::ReadOnlyHeap::Contains(str));
|
2018-12-10 13:24:52 +00:00
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2018-12-18 07:42:37 +00:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
void TestCustomSnapshotDataBlobWithIrregexpCode(
|
|
|
|
v8::SnapshotCreator::FunctionCodeHandling function_code_handling) {
|
2018-12-11 13:51:16 +00:00
|
|
|
DisableAlwaysOpt();
|
|
|
|
const char* source =
|
2019-01-10 14:49:55 +00:00
|
|
|
"var re1 = /\\/\\*[^*]*\\*+([^/*][^*]*\\*+)*\\//;\n"
|
|
|
|
"function f() { return '/* a comment */'.search(re1); }\n"
|
|
|
|
"function g() { return 'not a comment'.search(re1); }\n"
|
|
|
|
"function h() { return '// this is a comment'.search(re1); }\n"
|
|
|
|
"var re2 = /a/;\n"
|
|
|
|
"function i() { return '/* a comment */'.search(re2); }\n"
|
|
|
|
"f(); f(); g(); g(); h(); h(); i(); i();\n";
|
2018-12-11 13:51:16 +00:00
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-12-18 07:42:37 +00:00
|
|
|
v8::StartupData data1 =
|
2019-04-30 12:43:23 +00:00
|
|
|
CreateSnapshotDataBlobInternal(function_code_handling, source);
|
2018-12-11 13:51:16 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolate(params1);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope c_scope(context);
|
2018-12-18 07:42:37 +00:00
|
|
|
{
|
|
|
|
// Check that compiled irregexp code has not been flushed prior to
|
|
|
|
// serialization.
|
|
|
|
i::Handle<i::JSRegExp> re =
|
2019-01-10 14:49:55 +00:00
|
|
|
Utils::OpenHandle(*CompileRun("re1").As<v8::RegExp>());
|
2018-12-18 07:42:37 +00:00
|
|
|
CHECK_EQ(re->HasCompiledCode(),
|
|
|
|
function_code_handling ==
|
|
|
|
v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
}
|
2018-12-11 13:51:16 +00:00
|
|
|
{
|
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("f()")->Int32Value(isolate1->GetCurrentContext());
|
|
|
|
CHECK_EQ(0, result.FromJust());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("g()")->Int32Value(isolate1->GetCurrentContext());
|
|
|
|
CHECK_EQ(-1, result.FromJust());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("h()")->Int32Value(isolate1->GetCurrentContext());
|
|
|
|
CHECK_EQ(-1, result.FromJust());
|
|
|
|
}
|
2019-01-10 14:49:55 +00:00
|
|
|
{
|
|
|
|
// Check that ATOM regexp remains valid.
|
|
|
|
i::Handle<i::JSRegExp> re =
|
|
|
|
Utils::OpenHandle(*CompileRun("re2").As<v8::RegExp>());
|
2021-09-29 10:08:27 +00:00
|
|
|
CHECK_EQ(re->type_tag(), JSRegExp::ATOM);
|
2019-01-10 14:49:55 +00:00
|
|
|
CHECK(!re->HasCompiledCode());
|
|
|
|
}
|
2018-12-11 13:51:16 +00:00
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2018-12-18 07:42:37 +00:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithIrregexpCodeKeepCode) {
|
|
|
|
TestCustomSnapshotDataBlobWithIrregexpCode(
|
|
|
|
v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithIrregexpCodeClearCode) {
|
|
|
|
TestCustomSnapshotDataBlobWithIrregexpCode(
|
|
|
|
v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotChecksum) {
|
2018-09-25 09:19:30 +00:00
|
|
|
DisableAlwaysOpt();
|
|
|
|
const char* source1 = "function f() { return 42; }";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-09-25 09:19:30 +00:00
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
|
|
|
CHECK(i::Snapshot::VerifyChecksum(&data1));
|
|
|
|
const_cast<char*>(data1.data)[142] = data1.data[142] ^ 4; // Flip a bit.
|
|
|
|
CHECK(!i::Snapshot::VerifyChecksum(&data1));
|
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2018-09-25 09:19:30 +00:00
|
|
|
}
|
|
|
|
|
2017-08-09 09:08:20 +00:00
|
|
|
struct InternalFieldData {
|
|
|
|
uint32_t data;
|
|
|
|
};
|
|
|
|
|
|
|
|
v8::StartupData SerializeInternalFields(v8::Local<v8::Object> holder, int index,
|
|
|
|
void* data) {
|
2018-11-06 10:59:57 +00:00
|
|
|
if (data == reinterpret_cast<void*>(2000)) {
|
|
|
|
// Used for SnapshotCreatorTemplates test. We check that none of the fields
|
|
|
|
// have been cleared yet.
|
|
|
|
CHECK_NOT_NULL(holder->GetAlignedPointerFromInternalField(1));
|
|
|
|
} else {
|
|
|
|
CHECK_EQ(reinterpret_cast<void*>(2016), data);
|
|
|
|
}
|
|
|
|
if (index != 1) return {nullptr, 0};
|
2017-08-09 09:08:20 +00:00
|
|
|
InternalFieldData* embedder_field = static_cast<InternalFieldData*>(
|
|
|
|
holder->GetAlignedPointerFromInternalField(index));
|
2017-08-16 10:54:46 +00:00
|
|
|
if (embedder_field == nullptr) return {nullptr, 0};
|
2017-08-09 09:08:20 +00:00
|
|
|
int size = sizeof(*embedder_field);
|
|
|
|
char* payload = new char[size];
|
|
|
|
// We simply use memcpy to serialize the content.
|
|
|
|
memcpy(payload, embedder_field, size);
|
|
|
|
return {payload, size};
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<InternalFieldData*> deserialized_data;
|
|
|
|
|
|
|
|
void DeserializeInternalFields(v8::Local<v8::Object> holder, int index,
|
|
|
|
v8::StartupData payload, void* data) {
|
2017-08-16 10:54:46 +00:00
|
|
|
if (payload.raw_size == 0) {
|
|
|
|
holder->SetAlignedPointerInInternalField(index, nullptr);
|
|
|
|
return;
|
|
|
|
}
|
2017-08-09 09:08:20 +00:00
|
|
|
CHECK_EQ(reinterpret_cast<void*>(2017), data);
|
|
|
|
InternalFieldData* embedder_field = new InternalFieldData{0};
|
|
|
|
memcpy(embedder_field, payload.data, payload.raw_size);
|
|
|
|
holder->SetAlignedPointerInInternalField(index, embedder_field);
|
|
|
|
deserialized_data.push_back(embedder_field);
|
|
|
|
}
|
|
|
|
|
2019-05-27 11:31:49 +00:00
|
|
|
using Int32Expectations = std::vector<std::tuple<const char*, int32_t>>;
|
2017-08-09 09:08:20 +00:00
|
|
|
|
|
|
|
void TestInt32Expectations(const Int32Expectations& expectations) {
|
|
|
|
for (const auto& e : expectations) {
|
|
|
|
ExpectInt32(std::get<0>(e), std::get<1>(e));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-07 18:18:14 +00:00
|
|
|
void TypedArrayTestHelper(
|
|
|
|
const char* code, const Int32Expectations& expectations,
|
|
|
|
const char* code_to_run_after_restore = nullptr,
|
2020-02-17 17:13:07 +00:00
|
|
|
const Int32Expectations& after_restore_expectations = Int32Expectations(),
|
|
|
|
v8::ArrayBuffer::Allocator* allocator = nullptr) {
|
2017-08-09 09:08:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(code);
|
|
|
|
TestInt32Expectations(expectations);
|
|
|
|
creator.SetDefaultContext(
|
|
|
|
context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields, reinterpret_cast<void*>(2016)));
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.snapshot_blob = &blob;
|
2020-02-17 17:13:07 +00:00
|
|
|
create_params.array_buffer_allocator =
|
|
|
|
allocator != nullptr ? allocator : CcTest::array_buffer_allocator();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(create_params);
|
2017-08-09 09:08:20 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(
|
2017-10-13 16:33:03 +00:00
|
|
|
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::MaybeLocal<v8::Value>(),
|
|
|
|
v8::DeserializeInternalFieldsCallback(DeserializeInternalFields,
|
|
|
|
reinterpret_cast<void*>(2017)));
|
2017-08-16 10:54:46 +00:00
|
|
|
CHECK(deserialized_data.empty()); // We do not expect any embedder data.
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
TestInt32Expectations(expectations);
|
2017-11-07 18:18:14 +00:00
|
|
|
if (code_to_run_after_restore) {
|
|
|
|
CompileRun(code_to_run_after_restore);
|
|
|
|
}
|
|
|
|
TestInt32Expectations(after_restore_expectations);
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] blob.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithOffHeapTypedArray) {
|
2017-08-09 09:08:20 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Uint8Array(128);"
|
|
|
|
"x[0] = 12;"
|
|
|
|
"var arr = new Array(17);"
|
|
|
|
"arr[1] = 24;"
|
|
|
|
"var y = new Uint32Array(arr);"
|
|
|
|
"var buffer = new ArrayBuffer(128);"
|
|
|
|
"var z = new Int16Array(buffer);"
|
|
|
|
"z[0] = 48;";
|
|
|
|
Int32Expectations expectations = {std::make_tuple("x[0]", 12),
|
|
|
|
std::make_tuple("y[1]", 24),
|
|
|
|
std::make_tuple("z[0]", 48)};
|
|
|
|
|
|
|
|
TypedArrayTestHelper(code, expectations);
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobSharedArrayBuffer) {
|
2017-08-09 09:08:20 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Int32Array([12, 24, 48, 96]);"
|
|
|
|
"var y = new Uint8Array(x.buffer)";
|
|
|
|
Int32Expectations expectations = {
|
2017-09-14 22:25:41 +00:00
|
|
|
std::make_tuple("x[0]", 12),
|
|
|
|
std::make_tuple("x[1]", 24),
|
|
|
|
#if !V8_TARGET_BIG_ENDIAN
|
|
|
|
std::make_tuple("y[0]", 12),
|
|
|
|
std::make_tuple("y[1]", 0),
|
|
|
|
std::make_tuple("y[2]", 0),
|
|
|
|
std::make_tuple("y[3]", 0),
|
|
|
|
std::make_tuple("y[4]", 24)
|
|
|
|
#else
|
|
|
|
std::make_tuple("y[3]", 12),
|
|
|
|
std::make_tuple("y[2]", 0),
|
|
|
|
std::make_tuple("y[1]", 0),
|
|
|
|
std::make_tuple("y[0]", 0),
|
|
|
|
std::make_tuple("y[7]", 24)
|
|
|
|
#endif
|
|
|
|
};
|
2017-08-09 09:08:20 +00:00
|
|
|
|
|
|
|
TypedArrayTestHelper(code, expectations);
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobArrayBufferWithOffset) {
|
2017-11-07 18:18:14 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Int32Array([12, 24, 48, 96]);"
|
|
|
|
"var y = new Int32Array(x.buffer, 4, 2)";
|
|
|
|
Int32Expectations expectations = {
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
std::make_tuple("x[1]", 24),
|
|
|
|
std::make_tuple("x[2]", 48),
|
|
|
|
std::make_tuple("y[0]", 24),
|
|
|
|
std::make_tuple("y[1]", 48),
|
2017-11-07 18:18:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// Verify that the typed arrays use the same buffer (not independent copies).
|
|
|
|
const char* code_to_run_after_restore = "x[2] = 57; y[0] = 42;";
|
|
|
|
Int32Expectations after_restore_expectations = {
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
std::make_tuple("x[1]", 42),
|
|
|
|
std::make_tuple("y[1]", 57),
|
2017-11-07 18:18:14 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
TypedArrayTestHelper(code, expectations, code_to_run_after_restore,
|
|
|
|
after_restore_expectations);
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobDataView) {
|
2017-08-09 09:08:20 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Int8Array([1, 2, 3, 4]);"
|
|
|
|
"var v = new DataView(x.buffer)";
|
|
|
|
Int32Expectations expectations = {std::make_tuple("v.getInt8(0)", 1),
|
|
|
|
std::make_tuple("v.getInt8(1)", 2),
|
|
|
|
std::make_tuple("v.getInt16(0)", 258),
|
|
|
|
std::make_tuple("v.getInt16(1)", 515)};
|
|
|
|
|
|
|
|
TypedArrayTestHelper(code, expectations);
|
|
|
|
}
|
|
|
|
|
2020-02-17 17:13:07 +00:00
|
|
|
namespace {
|
|
|
|
class AlternatingArrayBufferAllocator : public v8::ArrayBuffer::Allocator {
|
|
|
|
public:
|
|
|
|
AlternatingArrayBufferAllocator()
|
|
|
|
: allocation_fails_(false),
|
|
|
|
allocator_(v8::ArrayBuffer::Allocator::NewDefaultAllocator()) {}
|
|
|
|
~AlternatingArrayBufferAllocator() { delete allocator_; }
|
|
|
|
void* Allocate(size_t length) override {
|
|
|
|
allocation_fails_ = !allocation_fails_;
|
|
|
|
if (allocation_fails_) return nullptr;
|
|
|
|
return allocator_->Allocate(length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void* AllocateUninitialized(size_t length) override {
|
|
|
|
return this->Allocate(length);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Free(void* data, size_t size) override { allocator_->Free(data, size); }
|
|
|
|
|
|
|
|
void* Reallocate(void* data, size_t old_length, size_t new_length) override {
|
|
|
|
return allocator_->Reallocate(data, old_length, new_length);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool allocation_fails_;
|
|
|
|
v8::ArrayBuffer::Allocator* allocator_;
|
|
|
|
};
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotManyArrayBuffers) {
|
|
|
|
const char* code =
|
|
|
|
"var buffers = [];"
|
|
|
|
"for (let i = 0; i < 70; i++) buffers.push(new Uint8Array(1000));";
|
|
|
|
Int32Expectations expectations = {std::make_tuple("buffers.length", 70)};
|
|
|
|
std::unique_ptr<v8::ArrayBuffer::Allocator> allocator(
|
|
|
|
new AlternatingArrayBufferAllocator());
|
|
|
|
TypedArrayTestHelper(code, expectations, nullptr, Int32Expectations(),
|
|
|
|
allocator.get());
|
|
|
|
}
|
|
|
|
|
2018-12-11 10:22:02 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobDetachedArrayBuffer) {
|
2017-08-09 09:08:20 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Int16Array([12, 24, 48]);"
|
2018-12-11 10:22:02 +00:00
|
|
|
"%ArrayBufferDetach(x.buffer);";
|
2017-08-09 09:08:20 +00:00
|
|
|
Int32Expectations expectations = {std::make_tuple("x.buffer.byteLength", 0),
|
|
|
|
std::make_tuple("x.length", 0)};
|
|
|
|
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(code);
|
|
|
|
TestInt32Expectations(expectations);
|
|
|
|
creator.SetDefaultContext(
|
|
|
|
context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields, reinterpret_cast<void*>(2016)));
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(create_params);
|
2017-08-09 09:08:20 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(
|
2017-10-13 16:33:03 +00:00
|
|
|
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::MaybeLocal<v8::Value>(),
|
|
|
|
v8::DeserializeInternalFieldsCallback(DeserializeInternalFields,
|
|
|
|
reinterpret_cast<void*>(2017)));
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
TestInt32Expectations(expectations);
|
|
|
|
|
|
|
|
v8::Local<v8::Value> x = CompileRun("x");
|
|
|
|
CHECK(x->IsTypedArray());
|
|
|
|
i::Handle<i::JSTypedArray> array =
|
|
|
|
i::Handle<i::JSTypedArray>::cast(v8::Utils::OpenHandle(*x));
|
2018-12-11 10:22:02 +00:00
|
|
|
CHECK(array->WasDetached());
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] blob.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
i::Handle<i::JSArrayBuffer> GetBufferFromTypedArray(
|
|
|
|
v8::Local<v8::Value> typed_array) {
|
|
|
|
CHECK(typed_array->IsTypedArray());
|
|
|
|
|
|
|
|
i::Handle<i::JSArrayBufferView> view = i::Handle<i::JSArrayBufferView>::cast(
|
|
|
|
v8::Utils::OpenHandle(*typed_array));
|
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
return i::handle(i::JSArrayBuffer::cast(view->buffer()), view->GetIsolate());
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobOnOrOffHeapTypedArray) {
|
2017-08-09 09:08:20 +00:00
|
|
|
const char* code =
|
|
|
|
"var x = new Uint8Array(8);"
|
|
|
|
"x[0] = 12;"
|
|
|
|
"x[7] = 24;"
|
|
|
|
"var y = new Int16Array([12, 24, 48]);"
|
|
|
|
"var z = new Int32Array(64);"
|
|
|
|
"z[0] = 96;";
|
|
|
|
Int32Expectations expectations = {
|
|
|
|
std::make_tuple("x[0]", 12), std::make_tuple("x[7]", 24),
|
|
|
|
std::make_tuple("y[2]", 48), std::make_tuple("z[0]", 96)};
|
|
|
|
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(code);
|
|
|
|
TestInt32Expectations(expectations);
|
2020-09-29 13:40:21 +00:00
|
|
|
i::Handle<i::JSArrayBuffer> buffer =
|
|
|
|
GetBufferFromTypedArray(CompileRun("x"));
|
|
|
|
// The resulting buffer should be on-heap.
|
2021-11-23 18:11:23 +00:00
|
|
|
CHECK(buffer->IsEmpty());
|
2017-08-09 09:08:20 +00:00
|
|
|
creator.SetDefaultContext(
|
|
|
|
context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields, reinterpret_cast<void*>(2016)));
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(create_params);
|
2017-08-09 09:08:20 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(
|
2017-10-13 16:33:03 +00:00
|
|
|
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
|
2017-08-09 09:08:20 +00:00
|
|
|
v8::MaybeLocal<v8::Value>(),
|
|
|
|
v8::DeserializeInternalFieldsCallback(DeserializeInternalFields,
|
|
|
|
reinterpret_cast<void*>(2017)));
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
TestInt32Expectations(expectations);
|
|
|
|
|
|
|
|
i::Handle<i::JSArrayBuffer> buffer =
|
|
|
|
GetBufferFromTypedArray(CompileRun("x"));
|
|
|
|
// The resulting buffer should be on-heap.
|
2021-11-23 18:11:23 +00:00
|
|
|
CHECK(buffer->IsEmpty());
|
2017-08-09 09:08:20 +00:00
|
|
|
|
|
|
|
buffer = GetBufferFromTypedArray(CompileRun("y"));
|
2021-11-23 18:11:23 +00:00
|
|
|
CHECK(buffer->IsEmpty());
|
2017-08-09 09:08:20 +00:00
|
|
|
|
|
|
|
buffer = GetBufferFromTypedArray(CompileRun("z"));
|
|
|
|
// The resulting buffer should be off-heap.
|
2021-11-23 18:11:23 +00:00
|
|
|
CHECK(!buffer->IsEmpty());
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] blob.data; // We can dispose of the snapshot blob now.
|
2019-03-07 10:14:29 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobTypedArrayNoEmbedderFieldCallback) {
|
|
|
|
const char* code = "var x = new Uint8Array(8);";
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2019-03-07 10:14:29 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(code);
|
|
|
|
creator.SetDefaultContext(context, v8::SerializeInternalFieldsCallback());
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(
|
|
|
|
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
|
|
|
|
v8::MaybeLocal<v8::Value>(), v8::DeserializeInternalFieldsCallback());
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-08-09 09:08:20 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlob2) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2016-11-04 09:04:11 +00:00
|
|
|
const char* source2 =
|
|
|
|
"function f() { return g() * 2; }"
|
|
|
|
"function g() { return 43; }"
|
|
|
|
"/./.test('a')";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData data2 = CreateSnapshotDataBlob(source2);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params2;
|
|
|
|
params2.snapshot_blob = &data2;
|
2015-04-29 09:54:34 +00:00
|
|
|
params2.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate2 = TestSerializer::NewIsolate(params2);
|
2015-02-25 11:14:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate2);
|
|
|
|
v8::HandleScope h_scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
|
|
|
v8::Context::Scope c_scope(context);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("f()")->Int32Value(isolate2->GetCurrentContext());
|
|
|
|
CHECK_EQ(86, result.FromJust());
|
|
|
|
result = CompileRun("g()")->Int32Value(isolate2->GetCurrentContext());
|
|
|
|
CHECK_EQ(43, result.FromJust());
|
2010-01-27 08:25:48 +00:00
|
|
|
}
|
2015-02-25 11:14:40 +00:00
|
|
|
isolate2->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data2.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2010-01-14 14:46:31 +00:00
|
|
|
}
|
|
|
|
|
2015-06-19 09:07:23 +00:00
|
|
|
static void SerializationFunctionTemplate(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
args.GetReturnValue().Set(args[0]);
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobOutdatedContextWithOverflow) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2015-06-19 09:07:23 +00:00
|
|
|
const char* source1 =
|
|
|
|
"var o = {};"
|
|
|
|
"(function() {"
|
|
|
|
" function f1(x) { return f2(x) instanceof Array; }"
|
|
|
|
" function f2(x) { return foo.bar(x); }"
|
|
|
|
" o.a = f2.bind(null);"
|
|
|
|
" o.b = 1;"
|
|
|
|
" o.c = 2;"
|
|
|
|
" o.d = 3;"
|
|
|
|
" o.e = 4;"
|
|
|
|
"})();\n";
|
|
|
|
|
|
|
|
const char* source2 = "o.a(42)";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData data = CreateSnapshotDataBlob(source1);
|
2015-06-19 09:07:23 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &data;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2015-06-19 09:07:23 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
|
|
|
|
v8::Local<v8::ObjectTemplate> global = v8::ObjectTemplate::New(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> property = v8::ObjectTemplate::New(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> function =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializationFunctionTemplate);
|
|
|
|
property->Set(isolate, "bar", function);
|
|
|
|
global->Set(isolate, "foo", property);
|
|
|
|
|
2017-10-13 16:33:03 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate, nullptr, global);
|
2015-06-19 09:07:23 +00:00
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
v8::Local<v8::Value> result = CompileRun(source2);
|
2019-06-21 13:32:50 +00:00
|
|
|
v8::Maybe<bool> compare =
|
|
|
|
v8_str("42")->Equals(isolate->GetCurrentContext(), result);
|
2015-10-08 09:48:05 +00:00
|
|
|
CHECK(compare.FromJust());
|
2015-06-19 09:07:23 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2015-06-19 09:07:23 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithLocker) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate0 = v8::Isolate::New(create_params);
|
2015-03-03 16:14:04 +00:00
|
|
|
{
|
|
|
|
v8::Locker locker(isolate0);
|
|
|
|
v8::Isolate::Scope i_scope(isolate0);
|
|
|
|
v8::HandleScope h_scope(isolate0);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate0);
|
|
|
|
v8::Context::Scope c_scope(context);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("Math.cos(0)")->Int32Value(isolate0->GetCurrentContext());
|
|
|
|
CHECK_EQ(1, result.FromJust());
|
2015-03-03 16:14:04 +00:00
|
|
|
}
|
|
|
|
isolate0->Dispose();
|
|
|
|
|
|
|
|
const char* source1 = "function f() { return 42; }";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
2015-03-03 16:14:04 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
2015-04-29 09:54:34 +00:00
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolate(params1);
|
2015-03-03 16:14:04 +00:00
|
|
|
{
|
|
|
|
v8::Locker locker(isolate1);
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope c_scope(context);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result = CompileRun("f()")->Int32Value(context);
|
|
|
|
CHECK_EQ(42, result.FromJust());
|
2015-03-03 16:14:04 +00:00
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data1.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2015-03-03 16:14:04 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobStackOverflow) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2015-05-15 07:42:40 +00:00
|
|
|
const char* source =
|
|
|
|
"var a = [0];"
|
|
|
|
"var b = a;"
|
|
|
|
"for (var i = 0; i < 10000; i++) {"
|
|
|
|
" var c = [i];"
|
|
|
|
" b.push(c);"
|
|
|
|
" b.push(c);"
|
|
|
|
" b = c;"
|
|
|
|
"}";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData data = CreateSnapshotDataBlob(source);
|
2015-05-15 07:42:40 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &data;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2015-05-15 07:42:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
const char* test =
|
|
|
|
"var sum = 0;"
|
|
|
|
"while (a) {"
|
|
|
|
" sum += a[0];"
|
|
|
|
" a = a[1];"
|
|
|
|
"}"
|
|
|
|
"sum";
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun(test)->Int32Value(isolate->GetCurrentContext());
|
|
|
|
CHECK_EQ(9999 * 5000, result.FromJust());
|
2015-05-15 07:42:40 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2015-05-15 07:42:40 +00:00
|
|
|
}
|
|
|
|
|
2016-03-17 10:32:36 +00:00
|
|
|
bool IsCompiled(const char* name) {
|
|
|
|
return i::Handle<i::JSFunction>::cast(
|
|
|
|
v8::Utils::OpenHandle(*CompileRun(name)))
|
|
|
|
->shared()
|
|
|
|
.is_compiled();
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotDataBlobWithWarmup) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2016-06-30 08:41:05 +00:00
|
|
|
const char* warmup = "Math.abs(1); Math.random = 1;";
|
2016-03-17 10:32:36 +00:00
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2019-05-16 12:19:13 +00:00
|
|
|
v8::StartupData cold = CreateSnapshotDataBlob(nullptr);
|
|
|
|
v8::StartupData warm = WarmUpSnapshotDataBlobInternal(cold, warmup);
|
2016-03-17 10:32:36 +00:00
|
|
|
delete[] cold.data;
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &warm;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-03-17 10:32:36 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
// Running the warmup script has effect on whether functions are
|
|
|
|
// pre-compiled, but does not pollute the context.
|
2016-06-30 08:41:05 +00:00
|
|
|
CHECK(IsCompiled("Math.abs"));
|
2017-10-19 12:55:32 +00:00
|
|
|
CHECK(IsCompiled("String.raw"));
|
2016-06-30 08:41:05 +00:00
|
|
|
CHECK(CompileRun("Math.random")->IsFunction());
|
2016-03-17 10:32:36 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] warm.data;
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-03-17 10:32:36 +00:00
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithWarmup) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2016-03-17 10:32:36 +00:00
|
|
|
const char* source =
|
2016-06-30 08:41:05 +00:00
|
|
|
"function f() { return Math.abs(1); }\n"
|
2016-10-18 12:44:48 +00:00
|
|
|
"function g() { return String.raw(1); }\n"
|
2016-10-07 08:58:43 +00:00
|
|
|
"Object.valueOf(1);"
|
2016-03-17 10:32:36 +00:00
|
|
|
"var a = 5";
|
|
|
|
const char* warmup = "a = f()";
|
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-04-19 13:45:52 +00:00
|
|
|
v8::StartupData cold = CreateSnapshotDataBlob(source);
|
2019-05-16 12:19:13 +00:00
|
|
|
v8::StartupData warm = WarmUpSnapshotDataBlobInternal(cold, warmup);
|
2016-03-17 10:32:36 +00:00
|
|
|
delete[] cold.data;
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &warm;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-03-17 10:32:36 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
// Running the warmup script has effect on whether functions are
|
|
|
|
// pre-compiled, but does not pollute the context.
|
|
|
|
CHECK(IsCompiled("f"));
|
2016-06-30 08:41:05 +00:00
|
|
|
CHECK(IsCompiled("Math.abs"));
|
2016-03-17 10:32:36 +00:00
|
|
|
CHECK(!IsCompiled("g"));
|
2017-10-19 12:55:32 +00:00
|
|
|
CHECK(IsCompiled("String.raw"));
|
2018-08-28 09:14:58 +00:00
|
|
|
CHECK(IsCompiled("Array.prototype.lastIndexOf"));
|
2016-03-17 10:32:36 +00:00
|
|
|
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] warm.data;
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-03-17 10:32:36 +00:00
|
|
|
}
|
2015-05-15 07:42:40 +00:00
|
|
|
|
2020-08-28 10:13:16 +00:00
|
|
|
namespace {
|
|
|
|
v8::StartupData CreateCustomSnapshotWithKeep() {
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::Local<v8::String> source_str = v8_str(
|
|
|
|
"function f() { return Math.abs(1); }\n"
|
|
|
|
"function g() { return String.raw(1); }");
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate, v8_str("test"));
|
2020-08-28 10:13:16 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin);
|
|
|
|
CompileRun(isolate->GetCurrentContext(), &source,
|
|
|
|
v8::ScriptCompiler::kEagerCompile);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobWithKeep) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob = CreateCustomSnapshotWithKeep();
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CHECK(IsCompiled("f"));
|
|
|
|
CHECK(IsCompiled("g"));
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2018-11-15 12:49:20 +00:00
|
|
|
UNINITIALIZED_TEST(CustomSnapshotDataBlobImmortalImmovableRoots) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2016-03-17 13:15:56 +00:00
|
|
|
// Flood the startup snapshot with shared function infos. If they are
|
|
|
|
// serialized before the immortal immovable root, the root will no longer end
|
|
|
|
// up on the first page.
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source =
|
|
|
|
ConstructSource(base::StaticCharVector("var a = [];"),
|
|
|
|
base::StaticCharVector("a.push(function() {return 7});"),
|
|
|
|
base::StaticCharVector("\0"), 10000);
|
2016-03-17 13:15:56 +00:00
|
|
|
|
2019-04-30 12:43:23 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
v8::StartupData data = CreateSnapshotDataBlob(source.begin());
|
2016-03-17 13:15:56 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &data;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-03-17 13:15:56 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
CHECK_EQ(7, CompileRun("a[0]()")->Int32Value(context).FromJust());
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
source.Dispose();
|
2017-11-07 07:51:41 +00:00
|
|
|
delete[] data.data; // We can dispose of the snapshot blob now.
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-03-17 13:15:56 +00:00
|
|
|
}
|
|
|
|
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
TEST(TestThatAlwaysSucceeds) {}
|
2009-10-22 19:09:09 +00:00
|
|
|
|
|
|
|
TEST(TestThatAlwaysFails) {
|
|
|
|
bool ArtificialFailure = false;
|
|
|
|
CHECK(ArtificialFailure);
|
|
|
|
}
|
|
|
|
|
2014-07-14 11:22:03 +00:00
|
|
|
int CountBuiltins() {
|
|
|
|
// Check that we have not deserialized any additional builtin.
|
2019-06-13 10:51:22 +00:00
|
|
|
HeapObjectIterator iterator(CcTest::heap());
|
2020-11-20 16:57:36 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
2014-07-14 11:22:03 +00:00
|
|
|
int counter = 0;
|
2019-05-31 10:59:12 +00:00
|
|
|
for (HeapObject obj = iterator.Next(); !obj.is_null();
|
|
|
|
obj = iterator.Next()) {
|
2020-08-05 11:48:03 +00:00
|
|
|
if (obj.IsCode() && Code::cast(obj).kind() == CodeKind::BUILTIN) counter++;
|
2014-07-14 11:22:03 +00:00
|
|
|
}
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
|
2015-02-06 17:52:20 +00:00
|
|
|
static Handle<SharedFunctionInfo> CompileScript(
|
2021-08-09 13:21:43 +00:00
|
|
|
Isolate* isolate, Handle<String> source,
|
|
|
|
const ScriptDetails& script_details, AlignedCachedData* cached_data,
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
v8::ScriptCompiler::CompileOptions options) {
|
2021-08-17 08:52:17 +00:00
|
|
|
return Compiler::GetSharedFunctionInfoForScriptWithCachedData(
|
|
|
|
isolate, source, script_details, cached_data, options,
|
2021-08-09 13:21:43 +00:00
|
|
|
ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE)
|
2017-10-04 22:48:12 +00:00
|
|
|
.ToHandleChecked();
|
2015-02-06 17:52:20 +00:00
|
|
|
}
|
|
|
|
|
2018-02-20 14:14:54 +00:00
|
|
|
static Handle<SharedFunctionInfo> CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
Isolate* isolate, Handle<String> source,
|
|
|
|
const ScriptDetails& script_details, AlignedCachedData** out_cached_data,
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
v8::ScriptCompiler::CompileOptions options) {
|
2018-02-20 14:14:54 +00:00
|
|
|
Handle<SharedFunctionInfo> sfi =
|
|
|
|
Compiler::GetSharedFunctionInfoForScript(
|
2021-08-17 08:52:17 +00:00
|
|
|
isolate, source, script_details, options,
|
2021-08-04 07:36:35 +00:00
|
|
|
ScriptCompiler::kNoCacheNoReason, NOT_NATIVES_CODE)
|
2018-02-20 14:14:54 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
std::unique_ptr<ScriptCompiler::CachedData> cached_data(
|
2018-04-16 07:56:17 +00:00
|
|
|
ScriptCompiler::CreateCodeCache(ToApiHandle<UnboundScript>(sfi)));
|
2018-02-20 14:14:54 +00:00
|
|
|
uint8_t* buffer = NewArray<uint8_t>(cached_data->length);
|
|
|
|
MemCopy(buffer, cached_data->data, cached_data->length);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
*out_cached_data = new i::AlignedCachedData(buffer, cached_data->length);
|
|
|
|
(*out_cached_data)->AcquireDataOwnership();
|
2018-02-20 14:14:54 +00:00
|
|
|
return sfi;
|
|
|
|
}
|
|
|
|
|
2019-08-20 15:14:41 +00:00
|
|
|
TEST(CodeSerializerWithProfiler) {
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.enable_lazy_source_positions = true;
|
|
|
|
v8_flags.stress_lazy_source_positions = false;
|
2019-08-20 15:14:41 +00:00
|
|
|
|
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2019-08-20 15:14:41 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
const char* source = "1 + 1";
|
|
|
|
|
|
|
|
Handle<String> orig_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2019-08-20 15:14:41 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
Handle<String> copy_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2019-08-20 15:14:41 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
CHECK(!orig_source.is_identical_to(copy_source));
|
|
|
|
CHECK(orig_source->Equals(*copy_source));
|
|
|
|
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2019-08-20 15:14:41 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
ScriptDetails default_script_details;
|
2019-08-20 15:14:41 +00:00
|
|
|
Handle<SharedFunctionInfo> orig = CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
isolate, orig_source, default_script_details, &cache,
|
2019-08-20 15:14:41 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
|
|
|
|
2020-12-17 16:02:56 +00:00
|
|
|
CHECK(!orig->GetBytecodeArray(isolate).HasSourcePositionTable());
|
2019-08-20 15:14:41 +00:00
|
|
|
|
2021-08-02 12:19:01 +00:00
|
|
|
isolate->SetIsProfiling(true);
|
2019-08-20 15:14:41 +00:00
|
|
|
|
|
|
|
// This does not assert that no compilation can happen as source position
|
|
|
|
// collection could trigger it.
|
|
|
|
Handle<SharedFunctionInfo> copy =
|
2021-08-09 13:21:43 +00:00
|
|
|
CompileScript(isolate, copy_source, default_script_details, cache,
|
2019-08-20 15:14:41 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
|
|
|
|
|
|
|
// Since the profiler is now enabled, source positions should be collected
|
|
|
|
// after deserialization.
|
2020-12-17 16:02:56 +00:00
|
|
|
CHECK(copy->GetBytecodeArray(isolate).HasSourcePositionTable());
|
2019-08-20 15:14:41 +00:00
|
|
|
|
|
|
|
delete cache;
|
|
|
|
}
|
|
|
|
|
2019-04-22 23:39:50 +00:00
|
|
|
void TestCodeSerializerOnePlusOneImpl(bool verify_builtins_count = true) {
|
2014-07-15 08:46:47 +00:00
|
|
|
LocalContext context;
|
2014-07-16 06:59:14 +00:00
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-07-14 11:22:03 +00:00
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
2014-07-14 11:22:03 +00:00
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
const char* source = "1 + 1";
|
2014-07-14 11:22:03 +00:00
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
Handle<String> orig_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2014-07-16 06:59:14 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
Handle<String> copy_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2014-07-16 06:59:14 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
CHECK(!orig_source.is_identical_to(copy_source));
|
|
|
|
CHECK(orig_source->Equals(*copy_source));
|
2014-07-14 11:22:03 +00:00
|
|
|
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-07-14 11:22:03 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
ScriptDetails default_script_details;
|
2018-02-20 14:14:54 +00:00
|
|
|
Handle<SharedFunctionInfo> orig = CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
isolate, orig_source, default_script_details, &cache,
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-07-14 11:22:03 +00:00
|
|
|
|
|
|
|
int builtins_count = CountBuiltins();
|
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, copy_source, default_script_details, cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-07-16 06:59:14 +00:00
|
|
|
}
|
Change ScriptCompiler::CompileOptions to allow for two 'cache' modes
(parser or code) and to be explicit about cache consumption or production
(rather than making presence of cached_data imply one or the other.)
Also add a --cache flag to d8, to allow testing the functionality.
-----------------------------
API change
Reason: Currently, V8 supports a 'parser cache' for repeatedly executing the same script. We'd like to add a 2nd mode that would cache code, and would like to let the embedder decide which mode they chose (if any).
Note: Previously, the 'use cached data' property was implied by the presence of the cached data itself. (That is, kNoCompileOptions and source->cached_data != NULL.) That is no longer sufficient, since the presence of data is no longer sufficient to determine /which kind/ of data is present.
Changes from old behaviour:
- If you previously didn't use caching, nothing changes.
Example:
v8::CompileUnbound(isolate, source, kNoCompileOptions);
- If you previously used caching, it worked like this:
- 1st run:
v8::CompileUnbound(isolate, source, kProduceToCache);
Then, source->cached_data would contain the
data-to-be cached. This remains the same, except you
need to tell V8 which type of data you want.
v8::CompileUnbound(isolate, source, kProduceParserCache);
- 2nd run:
v8::CompileUnbound(isolate, source, kNoCompileOptions);
with source->cached_data set to the data you received in
the first run. This will now ignore the cached data, and
you need to explicitly tell V8 to use it:
v8::CompileUnbound(isolate, source, kConsumeParserCache);
-----------------------------
BUG=
R=marja@chromium.org, yangguo@chromium.org
Review URL: https://codereview.chromium.org/389573006
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22431 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-16 12:18:33 +00:00
|
|
|
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK_NE(*orig, *copy);
|
2014-07-16 06:59:14 +00:00
|
|
|
CHECK(Script::cast(copy->script()).source() == *copy_source);
|
2014-07-15 10:17:22 +00:00
|
|
|
|
2014-07-15 08:46:47 +00:00
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
2014-07-15 08:46:47 +00:00
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK_EQ(2, Handle<Smi>::cast(copy_result)->value());
|
2014-07-14 11:22:03 +00:00
|
|
|
|
2019-04-22 23:39:50 +00:00
|
|
|
if (verify_builtins_count) CHECK_EQ(builtins_count, CountBuiltins());
|
2014-07-14 11:22:03 +00:00
|
|
|
|
|
|
|
delete cache;
|
|
|
|
}
|
2014-07-15 08:46:47 +00:00
|
|
|
|
2018-07-05 07:59:35 +00:00
|
|
|
TEST(CodeSerializerOnePlusOne) { TestCodeSerializerOnePlusOneImpl(); }
|
|
|
|
|
2019-04-22 23:39:50 +00:00
|
|
|
// See bug v8:9122
|
|
|
|
TEST(CodeSerializerOnePlusOneWithInterpretedFramesNativeStack) {
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.interpreted_frames_native_stack = true;
|
2019-04-22 23:39:50 +00:00
|
|
|
// We pass false because this test will create IET copies (which are
|
|
|
|
// builtins).
|
|
|
|
TestCodeSerializerOnePlusOneImpl(false);
|
|
|
|
}
|
|
|
|
|
2018-07-12 07:40:05 +00:00
|
|
|
TEST(CodeSerializerOnePlusOneWithDebugger) {
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
static v8::debug::DebugDelegate dummy_delegate;
|
|
|
|
v8::debug::SetDebugDelegate(CcTest::isolate(), &dummy_delegate);
|
|
|
|
TestCodeSerializerOnePlusOneImpl();
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerPromotedToCompilationCache) {
|
2015-03-16 13:19:10 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
const char* source = "1 + 1";
|
|
|
|
|
2021-08-04 08:54:47 +00:00
|
|
|
Handle<String> src = isolate->factory()->NewStringFromAsciiChecked(source);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2015-03-16 13:19:10 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<FixedArray> default_host_defined_options =
|
|
|
|
isolate->factory()->NewFixedArray(2);
|
|
|
|
default_host_defined_options->set(0, Smi::FromInt(0));
|
|
|
|
const char* default_host_defined_option_1_string = "custom string";
|
|
|
|
Handle<String> default_host_defined_option_1 =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(
|
|
|
|
default_host_defined_option_1_string);
|
|
|
|
default_host_defined_options->set(1, *default_host_defined_option_1);
|
|
|
|
|
|
|
|
ScriptDetails default_script_details(src);
|
|
|
|
default_script_details.host_defined_options = default_host_defined_options;
|
|
|
|
CompileScriptAndProduceCache(isolate, src, default_script_details, &cache,
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2015-03-16 13:19:10 +00:00
|
|
|
|
2022-06-02 17:46:17 +00:00
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
|
|
|
copy = CompileScript(isolate, src, default_script_details, cache,
|
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
|
|
|
}
|
2021-08-09 13:21:43 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.host_defined_options =
|
|
|
|
default_script_details.host_defined_options;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
|
2021-08-09 13:21:43 +00:00
|
|
|
}
|
2015-03-16 13:19:10 +00:00
|
|
|
|
2021-08-04 08:54:47 +00:00
|
|
|
{
|
2021-08-09 13:21:43 +00:00
|
|
|
// Lookup with strictly equal host_defined_options should succeed:
|
2021-08-04 08:54:47 +00:00
|
|
|
ScriptDetails script_details(src);
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<FixedArray> host_defined_options =
|
|
|
|
isolate->factory()->NewFixedArray(2);
|
|
|
|
host_defined_options->set(0, default_host_defined_options->get(0));
|
|
|
|
Handle<String> host_defined_option_1 =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(
|
|
|
|
default_host_defined_option_1_string);
|
|
|
|
host_defined_options->set(1, *host_defined_option_1);
|
|
|
|
script_details.host_defined_options = host_defined_options;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different string with same contents should succeed:
|
|
|
|
ScriptDetails script_details(
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(source));
|
2021-08-09 13:21:43 +00:00
|
|
|
script_details.host_defined_options =
|
|
|
|
default_script_details.host_defined_options;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2021-10-18 08:25:43 +00:00
|
|
|
// Lookup with different name string should fail:
|
2021-08-04 08:54:47 +00:00
|
|
|
ScriptDetails script_details(
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked("other"));
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different position should fail:
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.line_offset = 0xFF;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
2017-02-06 10:18:05 +00:00
|
|
|
|
2021-08-04 08:54:47 +00:00
|
|
|
{
|
|
|
|
// Lookup with different position should fail:
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.column_offset = 0xFF;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different language mode should fail:
|
|
|
|
ScriptDetails script_details(src);
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kStrict);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different script_options should fail
|
|
|
|
ScriptOriginOptions origin_options(false, true);
|
|
|
|
CHECK_NE(ScriptOriginOptions().Flags(), origin_options.Flags());
|
|
|
|
ScriptDetails script_details(src, origin_options);
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different host_defined_options should fail:
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.host_defined_options = isolate->factory()->NewFixedArray(5);
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK(lookup_result.script().is_null() &&
|
|
|
|
lookup_result.toplevel_sfi().is_null());
|
2021-08-04 08:54:47 +00:00
|
|
|
}
|
2015-03-16 13:19:10 +00:00
|
|
|
|
2022-06-02 17:46:17 +00:00
|
|
|
// Compile the script again with different options.
|
|
|
|
ScriptDetails alternative_script_details(src);
|
|
|
|
Handle<SharedFunctionInfo> alternative_toplevel_sfi =
|
|
|
|
Compiler::GetSharedFunctionInfoForScript(
|
|
|
|
isolate, src, alternative_script_details,
|
|
|
|
ScriptCompiler::kNoCompileOptions, ScriptCompiler::kNoCacheNoReason,
|
|
|
|
NOT_NATIVES_CODE)
|
|
|
|
.ToHandleChecked();
|
|
|
|
CHECK_NE(*copy, *alternative_toplevel_sfi);
|
|
|
|
|
|
|
|
{
|
|
|
|
// The original script can still be found.
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.host_defined_options =
|
|
|
|
default_script_details.host_defined_options;
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(), *copy);
|
2022-06-02 17:46:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// The new script can also be found.
|
|
|
|
ScriptDetails script_details(src);
|
2022-06-15 15:27:28 +00:00
|
|
|
auto lookup_result = isolate->compilation_cache()->LookupScript(
|
|
|
|
src, script_details, LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*lookup_result.toplevel_sfi().ToHandleChecked(),
|
|
|
|
*alternative_toplevel_sfi);
|
2022-06-02 17:46:17 +00:00
|
|
|
}
|
|
|
|
|
2015-03-16 13:19:10 +00:00
|
|
|
delete cache;
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerInternalizedString) {
|
2014-07-15 08:46:47 +00:00
|
|
|
LocalContext context;
|
2014-07-16 06:59:14 +00:00
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-07-16 06:59:14 +00:00
|
|
|
|
2014-07-15 08:46:47 +00:00
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
const char* source = "'string1'";
|
2014-07-15 08:46:47 +00:00
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
Handle<String> orig_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2014-07-16 06:59:14 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
Handle<String> copy_source = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2014-07-16 06:59:14 +00:00
|
|
|
.ToHandleChecked();
|
|
|
|
CHECK(!orig_source.is_identical_to(copy_source));
|
|
|
|
CHECK(orig_source->Equals(*copy_source));
|
2014-07-15 08:46:47 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
2014-07-15 08:46:47 +00:00
|
|
|
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
i::AlignedCachedData* cached_data = nullptr;
|
2018-02-20 14:14:54 +00:00
|
|
|
Handle<SharedFunctionInfo> orig = CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
isolate, orig_source, ScriptDetails(), &cached_data,
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-07-15 08:46:47 +00:00
|
|
|
Handle<JSFunction> orig_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, orig, isolate->native_context()}
|
|
|
|
.Build();
|
2014-07-15 08:46:47 +00:00
|
|
|
Handle<Object> orig_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, orig_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK(orig_result->IsInternalizedString());
|
|
|
|
|
|
|
|
int builtins_count = CountBuiltins();
|
|
|
|
|
2014-07-16 06:59:14 +00:00
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, copy_source, ScriptDetails(), cached_data,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-07-16 06:59:14 +00:00
|
|
|
}
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK_NE(*orig, *copy);
|
2014-07-16 06:59:14 +00:00
|
|
|
CHECK(Script::cast(copy->script()).source() == *copy_source);
|
2014-07-15 10:17:22 +00:00
|
|
|
|
2014-07-15 08:46:47 +00:00
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK_NE(*orig_fun, *copy_fun);
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-07-15 08:46:47 +00:00
|
|
|
CHECK(orig_result.is_identical_to(copy_result));
|
|
|
|
Handle<String> expected =
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked("string1");
|
|
|
|
|
|
|
|
CHECK(Handle<String>::cast(copy_result)->Equals(*expected));
|
|
|
|
CHECK_EQ(builtins_count, CountBuiltins());
|
|
|
|
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
delete cached_data;
|
2014-07-15 08:46:47 +00:00
|
|
|
}
|
2014-07-23 07:16:32 +00:00
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerLargeCodeObject) {
|
2014-09-25 07:32:13 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
2016-08-09 12:16:48 +00:00
|
|
|
// The serializer only tests the shared code, which is always the unoptimized
|
|
|
|
// code. Don't even bother generating optimized code to avoid timeouts.
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.always_turbofan = false;
|
2016-08-09 12:16:48 +00:00
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source = ConstructSource(
|
|
|
|
base::StaticCharVector("var j=1; if (j == 0) {"),
|
|
|
|
base::StaticCharVector(
|
2018-01-04 19:15:04 +00:00
|
|
|
"for (let i of Object.prototype) for (let k = 0; k < 0; ++k);"),
|
2021-06-17 15:43:55 +00:00
|
|
|
base::StaticCharVector("} j=7; j"), 2000);
|
2014-09-25 07:32:13 +00:00
|
|
|
Handle<String> source_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
isolate->factory()->NewStringFromUtf8(source).ToHandleChecked();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig =
|
|
|
|
CompileScriptAndProduceCache(isolate, source_str, ScriptDetails(), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2020-12-17 16:02:56 +00:00
|
|
|
CHECK(isolate->heap()->InSpace(orig->abstract_code(isolate), LO_SPACE));
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_str, ScriptDetails(), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-09-25 07:32:13 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
int result_int;
|
|
|
|
CHECK(copy_result->ToInt32(&result_int));
|
|
|
|
CHECK_EQ(7, result_int);
|
|
|
|
|
|
|
|
delete cache;
|
|
|
|
source.Dispose();
|
|
|
|
}
|
|
|
|
|
2017-05-10 12:54:01 +00:00
|
|
|
TEST(CodeSerializerLargeCodeObjectWithIncrementalMarking) {
|
2022-09-15 16:54:55 +00:00
|
|
|
if (!v8_flags.incremental_marking) return;
|
|
|
|
if (!v8_flags.compact) return;
|
2017-08-23 15:15:27 +00:00
|
|
|
ManualGCScope manual_gc_scope;
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.always_turbofan = false;
|
2017-05-10 12:54:01 +00:00
|
|
|
const char* filter_flag = "--turbo-filter=NOTHING";
|
2019-04-23 18:17:01 +00:00
|
|
|
FlagList::SetFlagsFromString(filter_flag, strlen(filter_flag));
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.manual_evacuation_candidates_selection = true;
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
Heap* heap = isolate->heap();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source = ConstructSource(
|
|
|
|
base::StaticCharVector("var j=1; if (j == 0) {"),
|
|
|
|
base::StaticCharVector("for (var i = 0; i < Object.prototype; i++);"),
|
|
|
|
base::StaticCharVector("} j=7; var s = 'happy_hippo'; j"), 20000);
|
2017-05-10 12:54:01 +00:00
|
|
|
Handle<String> source_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
isolate->factory()->NewStringFromUtf8(source).ToHandleChecked();
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
// Create a string on an evacuation candidate in old space.
|
|
|
|
Handle<String> moving_object;
|
|
|
|
Page* ec_page;
|
|
|
|
{
|
2020-03-02 13:52:18 +00:00
|
|
|
AlwaysAllocateScopeForTesting always_allocate(heap);
|
2017-05-10 12:54:01 +00:00
|
|
|
heap::SimulateFullSpace(heap->old_space());
|
|
|
|
moving_object = isolate->factory()->InternalizeString(
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked("happy_hippo"));
|
2019-01-15 00:23:43 +00:00
|
|
|
ec_page = Page::FromHeapObject(*moving_object);
|
2017-05-10 12:54:01 +00:00
|
|
|
}
|
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2017-05-10 12:54:01 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig =
|
|
|
|
CompileScriptAndProduceCache(isolate, source_str, ScriptDetails(), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2017-05-10 12:54:01 +00:00
|
|
|
|
2020-12-17 16:02:56 +00:00
|
|
|
CHECK(heap->InSpace(orig->abstract_code(isolate), LO_SPACE));
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
// Pretend that incremental marking is on when deserialization begins.
|
|
|
|
heap::ForceEvacuationCandidate(ec_page);
|
|
|
|
heap::SimulateIncrementalMarking(heap, false);
|
|
|
|
IncrementalMarking* marking = heap->incremental_marking();
|
2022-08-09 18:02:04 +00:00
|
|
|
CHECK(marking->black_allocation());
|
2017-05-10 12:54:01 +00:00
|
|
|
CHECK(marking->IsCompacting());
|
|
|
|
CHECK(MarkCompactCollector::IsOnEvacuationCandidate(*moving_object));
|
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_str, ScriptDetails(), cache,
|
2017-05-10 12:54:01 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
// We should have missed a write barrier. Complete incremental marking
|
|
|
|
// to flush out the bug.
|
|
|
|
heap::SimulateIncrementalMarking(heap, true);
|
|
|
|
CcTest::CollectAllGarbage();
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2017-05-10 12:54:01 +00:00
|
|
|
|
|
|
|
int result_int;
|
|
|
|
CHECK(copy_result->ToInt32(&result_int));
|
|
|
|
CHECK_EQ(7, result_int);
|
|
|
|
|
|
|
|
delete cache;
|
|
|
|
source.Dispose();
|
|
|
|
}
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerLargeStrings) {
|
2014-09-25 07:32:13 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2014-10-27 16:40:04 +00:00
|
|
|
Factory* f = isolate->factory();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source_s = ConstructSource(
|
|
|
|
base::StaticCharVector("var s = \""), base::StaticCharVector("abcdef"),
|
|
|
|
base::StaticCharVector("\";"), 1000000);
|
|
|
|
base::Vector<const char> source_t = ConstructSource(
|
|
|
|
base::StaticCharVector("var t = \""), base::StaticCharVector("uvwxyz"),
|
|
|
|
base::StaticCharVector("\"; s + t"), 999999);
|
2014-09-25 07:32:13 +00:00
|
|
|
Handle<String> source_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
f->NewConsString(f->NewStringFromUtf8(source_s).ToHandleChecked(),
|
|
|
|
f->NewStringFromUtf8(source_t).ToHandleChecked())
|
2014-10-27 16:40:04 +00:00
|
|
|
.ToHandleChecked();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig =
|
|
|
|
CompileScriptAndProduceCache(isolate, source_str, ScriptDetails(), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_str, ScriptDetails(), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-09-25 07:32:13 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-09-25 07:32:13 +00:00
|
|
|
|
2014-10-27 16:40:04 +00:00
|
|
|
CHECK_EQ(6 * 1999999, Handle<String>::cast(copy_result)->length());
|
2015-05-12 13:52:26 +00:00
|
|
|
Handle<Object> property = JSReceiver::GetDataProperty(
|
2022-02-18 18:15:48 +00:00
|
|
|
isolate, isolate->global_object(), f->NewStringFromAsciiChecked("s"));
|
2014-10-27 16:40:04 +00:00
|
|
|
CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
|
2022-02-18 18:15:48 +00:00
|
|
|
property = JSReceiver::GetDataProperty(isolate, isolate->global_object(),
|
2015-05-12 13:52:26 +00:00
|
|
|
f->NewStringFromAsciiChecked("t"));
|
2014-10-27 16:40:04 +00:00
|
|
|
CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
|
2014-10-24 12:40:05 +00:00
|
|
|
// Make sure we do not serialize too much, e.g. include the source string.
|
2014-10-27 16:40:04 +00:00
|
|
|
CHECK_LT(cache->length(), 13000000);
|
2014-09-25 07:32:13 +00:00
|
|
|
|
|
|
|
delete cache;
|
2014-10-27 16:40:04 +00:00
|
|
|
source_s.Dispose();
|
|
|
|
source_t.Dispose();
|
2014-09-25 07:32:13 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerThreeBigStrings) {
|
2014-10-15 14:04:53 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
Factory* f = isolate->factory();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-10-15 14:04:53 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
2019-02-06 15:23:06 +00:00
|
|
|
const int32_t length_of_a = kMaxRegularHeapObjectSize * 2;
|
|
|
|
const int32_t length_of_b = kMaxRegularHeapObjectSize / 2;
|
|
|
|
const int32_t length_of_c = kMaxRegularHeapObjectSize / 2;
|
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source_a = ConstructSource(
|
|
|
|
base::StaticCharVector("var a = \""), base::StaticCharVector("a"),
|
|
|
|
base::StaticCharVector("\";"), length_of_a);
|
2014-10-15 14:04:53 +00:00
|
|
|
Handle<String> source_a_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
f->NewStringFromUtf8(source_a).ToHandleChecked();
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source_b = ConstructSource(
|
|
|
|
base::StaticCharVector("var b = \""), base::StaticCharVector("b"),
|
|
|
|
base::StaticCharVector("\";"), length_of_b);
|
2014-10-15 14:04:53 +00:00
|
|
|
Handle<String> source_b_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
f->NewStringFromUtf8(source_b).ToHandleChecked();
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> source_c = ConstructSource(
|
|
|
|
base::StaticCharVector("var c = \""), base::StaticCharVector("c"),
|
|
|
|
base::StaticCharVector("\";"), length_of_c);
|
2014-10-15 14:04:53 +00:00
|
|
|
Handle<String> source_c_str =
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
f->NewStringFromUtf8(source_c).ToHandleChecked();
|
2014-10-15 14:04:53 +00:00
|
|
|
|
|
|
|
Handle<String> source_str =
|
|
|
|
f->NewConsString(
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
f->NewConsString(source_a_str, source_b_str).ToHandleChecked(),
|
|
|
|
source_c_str)
|
|
|
|
.ToHandleChecked();
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig =
|
|
|
|
CompileScriptAndProduceCache(isolate, source_str, ScriptDetails(), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-10-15 14:04:53 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_str, ScriptDetails(), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-10-15 14:04:53 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2021-10-18 08:25:43 +00:00
|
|
|
USE(Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array()));
|
2014-10-15 14:04:53 +00:00
|
|
|
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Maybe<int32_t> result =
|
|
|
|
CompileRun("(a + b).length")
|
2019-06-21 13:32:50 +00:00
|
|
|
->Int32Value(CcTest::isolate()->GetCurrentContext());
|
2019-02-06 15:23:06 +00:00
|
|
|
CHECK_EQ(length_of_a + length_of_b, result.FromJust());
|
2015-10-08 09:48:05 +00:00
|
|
|
result = CompileRun("(b + c).length")
|
2019-06-21 13:32:50 +00:00
|
|
|
->Int32Value(CcTest::isolate()->GetCurrentContext());
|
2019-02-06 15:23:06 +00:00
|
|
|
CHECK_EQ(length_of_b + length_of_c, result.FromJust());
|
2014-10-15 14:04:53 +00:00
|
|
|
Heap* heap = isolate->heap();
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::String> result_str =
|
|
|
|
CompileRun("a")
|
|
|
|
->ToString(CcTest::isolate()->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
2015-10-15 10:55:12 +00:00
|
|
|
CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), LO_SPACE));
|
2015-10-08 09:48:05 +00:00
|
|
|
result_str = CompileRun("b")
|
|
|
|
->ToString(CcTest::isolate()->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
2015-10-13 13:47:14 +00:00
|
|
|
CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), OLD_SPACE));
|
2018-12-03 19:45:20 +00:00
|
|
|
|
2015-10-08 09:48:05 +00:00
|
|
|
result_str = CompileRun("c")
|
|
|
|
->ToString(CcTest::isolate()->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
2015-10-13 13:47:14 +00:00
|
|
|
CHECK(heap->InSpace(*v8::Utils::OpenHandle(*result_str), OLD_SPACE));
|
2014-10-15 14:04:53 +00:00
|
|
|
|
|
|
|
delete cache;
|
|
|
|
source_a.Dispose();
|
|
|
|
source_b.Dispose();
|
2014-10-16 13:26:28 +00:00
|
|
|
source_c.Dispose();
|
2014-10-15 14:04:53 +00:00
|
|
|
}
|
|
|
|
|
2014-10-02 08:18:03 +00:00
|
|
|
class SerializerOneByteResource
|
|
|
|
: public v8::String::ExternalOneByteStringResource {
|
2014-10-02 07:12:46 +00:00
|
|
|
public:
|
2014-10-02 08:18:03 +00:00
|
|
|
SerializerOneByteResource(const char* data, size_t length)
|
2017-12-22 12:00:58 +00:00
|
|
|
: data_(data), length_(length), dispose_count_(0) {}
|
2018-09-14 15:34:02 +00:00
|
|
|
const char* data() const override { return data_; }
|
|
|
|
size_t length() const override { return length_; }
|
|
|
|
void Dispose() override { dispose_count_++; }
|
2017-12-22 12:00:58 +00:00
|
|
|
int dispose_count() { return dispose_count_; }
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const char* data_;
|
|
|
|
size_t length_;
|
2017-12-22 12:00:58 +00:00
|
|
|
int dispose_count_;
|
2014-10-02 07:12:46 +00:00
|
|
|
};
|
|
|
|
|
2014-10-02 08:18:03 +00:00
|
|
|
class SerializerTwoByteResource : public v8::String::ExternalStringResource {
|
2014-10-02 07:12:46 +00:00
|
|
|
public:
|
2014-10-02 08:18:03 +00:00
|
|
|
SerializerTwoByteResource(const char* data, size_t length)
|
2017-12-22 12:00:58 +00:00
|
|
|
: data_(AsciiToTwoByteString(data)), length_(length), dispose_count_(0) {}
|
2018-09-14 15:34:02 +00:00
|
|
|
~SerializerTwoByteResource() override { DeleteArray<const uint16_t>(data_); }
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-09-14 15:34:02 +00:00
|
|
|
const uint16_t* data() const override { return data_; }
|
|
|
|
size_t length() const override { return length_; }
|
|
|
|
void Dispose() override { dispose_count_++; }
|
2017-12-22 12:00:58 +00:00
|
|
|
int dispose_count() { return dispose_count_; }
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const uint16_t* data_;
|
|
|
|
size_t length_;
|
2017-12-22 12:00:58 +00:00
|
|
|
int dispose_count_;
|
2014-10-02 07:12:46 +00:00
|
|
|
};
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerExternalString) {
|
2014-10-02 07:12:46 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
// Obtain external internalized one-byte string.
|
2021-01-15 20:07:35 +00:00
|
|
|
SerializerOneByteResource one_byte_resource("one_byte", 8);
|
2014-10-02 07:12:46 +00:00
|
|
|
Handle<String> one_byte_string =
|
2021-01-15 20:07:35 +00:00
|
|
|
isolate->factory()->NewStringFromAsciiChecked("one_byte");
|
2014-10-02 07:12:46 +00:00
|
|
|
one_byte_string = isolate->factory()->InternalizeString(one_byte_string);
|
|
|
|
one_byte_string->MakeExternal(&one_byte_resource);
|
|
|
|
CHECK(one_byte_string->IsExternalOneByteString());
|
|
|
|
CHECK(one_byte_string->IsInternalizedString());
|
|
|
|
|
|
|
|
// Obtain external internalized two-byte string.
|
2021-01-15 20:07:35 +00:00
|
|
|
SerializerTwoByteResource two_byte_resource("two_byte", 8);
|
2014-10-02 07:12:46 +00:00
|
|
|
Handle<String> two_byte_string =
|
2021-01-15 20:07:35 +00:00
|
|
|
isolate->factory()->NewStringFromAsciiChecked("two_byte");
|
2014-10-02 07:12:46 +00:00
|
|
|
two_byte_string = isolate->factory()->InternalizeString(two_byte_string);
|
|
|
|
two_byte_string->MakeExternal(&two_byte_resource);
|
|
|
|
CHECK(two_byte_string->IsExternalTwoByteString());
|
|
|
|
CHECK(two_byte_string->IsInternalizedString());
|
|
|
|
|
|
|
|
const char* source =
|
2021-01-15 20:07:35 +00:00
|
|
|
"var o = {} \n"
|
|
|
|
"o.one_byte = 7; \n"
|
|
|
|
"o.two_byte = 8; \n"
|
|
|
|
"o.one_byte + o.two_byte; \n";
|
2021-06-17 15:43:55 +00:00
|
|
|
Handle<String> source_string =
|
|
|
|
isolate->factory()
|
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
|
|
|
.ToHandleChecked();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-02-20 14:14:54 +00:00
|
|
|
Handle<SharedFunctionInfo> orig = CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
isolate, source_string, ScriptDetails(), &cache,
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_string, ScriptDetails(), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-10-02 07:12:46 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2015-10-06 14:50:29 +00:00
|
|
|
CHECK_EQ(15.0, copy_result->Number());
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-07-04 09:31:54 +00:00
|
|
|
// This avoids the GC from trying to free stack allocated resources.
|
|
|
|
i::Handle<i::ExternalOneByteString>::cast(one_byte_string)
|
2018-07-26 06:42:03 +00:00
|
|
|
->SetResource(isolate, nullptr);
|
2018-07-04 09:31:54 +00:00
|
|
|
i::Handle<i::ExternalTwoByteString>::cast(two_byte_string)
|
2018-07-26 06:42:03 +00:00
|
|
|
->SetResource(isolate, nullptr);
|
2014-10-02 07:12:46 +00:00
|
|
|
delete cache;
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerLargeExternalString) {
|
2014-10-02 07:12:46 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
Factory* f = isolate->factory();
|
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
// Create a huge external internalized string to use as variable name.
|
2021-06-17 15:43:55 +00:00
|
|
|
base::Vector<const char> string = ConstructSource(
|
|
|
|
base::StaticCharVector(""), base::StaticCharVector("abcdef"),
|
|
|
|
base::StaticCharVector(""), 999999);
|
[base] Fix {StaticCharVector} and add {StaticOneByteVector}
{StaticCharVector}, according to its name, should return a
{Vector<const char>}. For getting a {Vector<const uint8_t>}, the method
should be called {StaticOneByteVector}, analog to the
{OneByteVector} methods that already exist.
Also, {StaticCharVector} is constexpr, but {StaticOneByteVector} cannot
be, since it contains a {reinterpret_cast}. The same holds for
{Vector::cast} in general.
This CL
- changes the return type of {StaticCharVector} to be
{Vector<const char>},
- introduces a new {StaticOneByteVector} which returns
{Vector<const uint8_t>},
- fixes constexpr annotations at various methods returning {Vector}s,
- refactors users of {StaticCharVector} to either use
{StaticOneByteVector} instead, or work on {char} if that makes more
sense.
R=leszeks@chromium.org
Bug: v8:10426
Change-Id: I71e336097e41ad30f982aa6344ca3d67b3a01fe3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2154196
Commit-Queue: Clemens Backes <clemensb@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67213}
2020-04-17 13:44:55 +00:00
|
|
|
Handle<String> name = f->NewStringFromUtf8(string).ToHandleChecked();
|
2014-10-02 08:18:03 +00:00
|
|
|
SerializerOneByteResource one_byte_resource(
|
2019-04-29 11:06:49 +00:00
|
|
|
reinterpret_cast<const char*>(string.begin()), string.length());
|
2014-10-02 07:12:46 +00:00
|
|
|
name = f->InternalizeString(name);
|
|
|
|
name->MakeExternal(&one_byte_resource);
|
|
|
|
CHECK(name->IsExternalOneByteString());
|
|
|
|
CHECK(name->IsInternalizedString());
|
|
|
|
CHECK(isolate->heap()->InSpace(*name, LO_SPACE));
|
|
|
|
|
|
|
|
// Create the source, which is "var <literal> = 42; <literal>".
|
|
|
|
Handle<String> source_str =
|
|
|
|
f->NewConsString(
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
f->NewConsString(f->NewStringFromAsciiChecked("var "), name)
|
|
|
|
.ToHandleChecked(),
|
|
|
|
f->NewConsString(f->NewStringFromAsciiChecked(" = 42; "), name)
|
|
|
|
.ToHandleChecked())
|
|
|
|
.ToHandleChecked();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig =
|
|
|
|
CompileScriptAndProduceCache(isolate, source_str, ScriptDetails(), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_str, ScriptDetails(), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-10-02 07:12:46 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2015-10-06 14:50:29 +00:00
|
|
|
CHECK_EQ(42.0, copy_result->Number());
|
2014-10-02 07:12:46 +00:00
|
|
|
|
2018-07-04 09:31:54 +00:00
|
|
|
// This avoids the GC from trying to free stack allocated resources.
|
2018-07-26 06:42:03 +00:00
|
|
|
i::Handle<i::ExternalOneByteString>::cast(name)->SetResource(isolate,
|
|
|
|
nullptr);
|
2014-10-02 07:12:46 +00:00
|
|
|
delete cache;
|
|
|
|
string.Dispose();
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerExternalScriptName) {
|
2014-10-02 09:39:13 +00:00
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2014-10-02 09:39:13 +00:00
|
|
|
|
|
|
|
Factory* f = isolate->factory();
|
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
const char* source =
|
|
|
|
"var a = [1, 2, 3, 4];"
|
|
|
|
"a.reduce(function(x, y) { return x + y }, 0)";
|
|
|
|
|
|
|
|
Handle<String> source_string =
|
2021-06-17 15:43:55 +00:00
|
|
|
f->NewStringFromUtf8(base::CStrVector(source)).ToHandleChecked();
|
2014-10-02 09:39:13 +00:00
|
|
|
|
|
|
|
const SerializerOneByteResource one_byte_resource("one_byte", 8);
|
|
|
|
Handle<String> name =
|
|
|
|
f->NewExternalStringFromOneByte(&one_byte_resource).ToHandleChecked();
|
|
|
|
CHECK(name->IsExternalOneByteString());
|
|
|
|
CHECK(!name->IsInternalizedString());
|
|
|
|
|
2018-06-23 09:05:50 +00:00
|
|
|
Handle<JSObject> global(isolate->context().global_object(), isolate);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2014-10-02 09:39:13 +00:00
|
|
|
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> orig = CompileScriptAndProduceCache(
|
|
|
|
isolate, source_string, ScriptDetails(name), &cache,
|
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
2014-10-02 09:39:13 +00:00
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> copy;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
copy = CompileScript(isolate, source_string, ScriptDetails(name), cache,
|
2015-02-06 17:52:20 +00:00
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2014-10-02 09:39:13 +00:00
|
|
|
}
|
|
|
|
CHECK_NE(*orig, *copy);
|
|
|
|
|
|
|
|
Handle<JSFunction> copy_fun =
|
2020-11-10 11:22:49 +00:00
|
|
|
Factory::JSFunctionBuilder{isolate, copy, isolate->native_context()}
|
|
|
|
.Build();
|
2014-10-02 09:39:13 +00:00
|
|
|
|
|
|
|
Handle<Object> copy_result =
|
2021-10-18 08:25:43 +00:00
|
|
|
Execution::CallScript(isolate, copy_fun, global,
|
|
|
|
isolate->factory()->empty_fixed_array())
|
|
|
|
.ToHandleChecked();
|
2014-10-02 09:39:13 +00:00
|
|
|
|
2015-10-06 14:50:29 +00:00
|
|
|
CHECK_EQ(10.0, copy_result->Number());
|
2014-10-02 09:39:13 +00:00
|
|
|
|
2018-07-04 09:31:54 +00:00
|
|
|
// This avoids the GC from trying to free stack allocated resources.
|
2018-07-26 06:42:03 +00:00
|
|
|
i::Handle<i::ExternalOneByteString>::cast(name)->SetResource(isolate,
|
|
|
|
nullptr);
|
2014-10-02 09:39:13 +00:00
|
|
|
delete cache;
|
|
|
|
}
|
|
|
|
|
2014-10-24 08:37:03 +00:00
|
|
|
static bool toplevel_test_code_event_found = false;
|
|
|
|
|
2022-04-14 12:25:06 +00:00
|
|
|
static void SerializerLogEventListener(const v8::JitCodeEvent* event) {
|
2014-10-24 08:37:03 +00:00
|
|
|
if (event->type == v8::JitCodeEvent::CODE_ADDED &&
|
2019-01-11 11:52:27 +00:00
|
|
|
(memcmp(event->name.str, "Script:~ test", 13) == 0 ||
|
|
|
|
memcmp(event->name.str, "Script: test", 12) == 0)) {
|
2014-10-24 08:37:03 +00:00
|
|
|
toplevel_test_code_event_found = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::CachedData* CompileRunAndProduceCache(
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source, CodeCacheType cacheType = CodeCacheType::kLazy) {
|
2014-07-23 07:16:32 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache;
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(create_params);
|
2014-07-23 07:16:32 +00:00
|
|
|
{
|
2014-07-24 12:11:30 +00:00
|
|
|
v8::Isolate::Scope iscope(isolate1);
|
|
|
|
v8::HandleScope scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
2014-07-23 07:16:32 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate1, v8_str("test"));
|
2014-07-23 07:16:32 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin);
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::ScriptCompiler::CompileOptions options;
|
|
|
|
switch (cacheType) {
|
|
|
|
case CodeCacheType::kEager:
|
2018-10-05 12:25:34 +00:00
|
|
|
options = v8::ScriptCompiler::kEagerCompile;
|
2017-12-01 12:50:41 +00:00
|
|
|
break;
|
2018-02-20 14:14:54 +00:00
|
|
|
case CodeCacheType::kLazy:
|
2017-12-01 12:50:41 +00:00
|
|
|
case CodeCacheType::kAfterExecute:
|
|
|
|
options = v8::ScriptCompiler::kNoCompileOptions;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::UnboundScript> script =
|
2017-09-26 08:48:52 +00:00
|
|
|
v8::ScriptCompiler::CompileUnboundScript(isolate1, &source, options)
|
2015-10-08 09:48:05 +00:00
|
|
|
.ToLocalChecked();
|
2014-07-23 07:16:32 +00:00
|
|
|
|
2018-02-20 14:14:54 +00:00
|
|
|
if (cacheType != CodeCacheType::kAfterExecute) {
|
2018-04-16 07:56:17 +00:00
|
|
|
cache = ScriptCompiler::CreateCodeCache(script);
|
2018-02-20 14:14:54 +00:00
|
|
|
}
|
|
|
|
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate1->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> result_string =
|
|
|
|
result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
|
|
|
|
CHECK(result_string->Equals(isolate1->GetCurrentContext(), v8_str("abcdef"))
|
|
|
|
.FromJust());
|
2017-12-01 12:50:41 +00:00
|
|
|
|
|
|
|
if (cacheType == CodeCacheType::kAfterExecute) {
|
2018-04-16 07:56:17 +00:00
|
|
|
cache = ScriptCompiler::CreateCodeCache(script);
|
2017-12-01 12:50:41 +00:00
|
|
|
}
|
|
|
|
CHECK(cache);
|
2014-07-23 07:16:32 +00:00
|
|
|
}
|
2014-07-24 12:11:30 +00:00
|
|
|
isolate1->Dispose();
|
2015-02-09 14:49:35 +00:00
|
|
|
return cache;
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerIsolates) {
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
|
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(js_source);
|
2014-07-23 07:16:32 +00:00
|
|
|
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2014-10-24 08:37:03 +00:00
|
|
|
isolate2->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
|
2022-04-14 12:25:06 +00:00
|
|
|
SerializerLogEventListener);
|
2014-10-24 08:37:03 +00:00
|
|
|
toplevel_test_code_event_found = false;
|
2014-07-23 07:16:32 +00:00
|
|
|
{
|
2014-07-24 12:11:30 +00:00
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
2014-07-23 07:16:32 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2014-07-23 07:16:32 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
|
|
|
v8::Local<v8::UnboundScript> script;
|
|
|
|
{
|
2014-07-24 12:11:30 +00:00
|
|
|
DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
|
2015-10-08 09:48:05 +00:00
|
|
|
script = v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
2014-07-23 07:16:32 +00:00
|
|
|
}
|
2014-11-17 12:16:27 +00:00
|
|
|
CHECK(!cache->rejected);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(result->ToString(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
|
|
|
|
.FromJust());
|
2014-07-23 07:16:32 +00:00
|
|
|
}
|
2015-12-07 05:36:41 +00:00
|
|
|
CHECK(toplevel_test_code_event_found);
|
2014-07-24 12:11:30 +00:00
|
|
|
isolate2->Dispose();
|
2014-07-23 07:16:32 +00:00
|
|
|
}
|
2014-10-17 09:54:48 +00:00
|
|
|
|
2017-09-26 08:48:52 +00:00
|
|
|
TEST(CodeSerializerIsolatesEager) {
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source =
|
2017-09-26 08:48:52 +00:00
|
|
|
"function f() {"
|
|
|
|
" return function g() {"
|
|
|
|
" return 'abc';"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"f()() + 'def'";
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache =
|
2021-10-29 23:41:02 +00:00
|
|
|
CompileRunAndProduceCache(js_source, CodeCacheType::kEager);
|
2017-09-26 08:48:52 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
|
|
|
isolate2->SetJitCodeEventHandler(v8::kJitCodeEventDefault,
|
2022-04-14 12:25:06 +00:00
|
|
|
SerializerLogEventListener);
|
2017-09-26 08:48:52 +00:00
|
|
|
toplevel_test_code_event_found = false;
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2017-09-26 08:48:52 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
|
|
|
v8::Local<v8::UnboundScript> script;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
|
|
|
|
script = v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
|
|
|
}
|
|
|
|
CHECK(!cache->rejected);
|
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(result->ToString(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
CHECK(toplevel_test_code_event_found);
|
|
|
|
isolate2->Dispose();
|
|
|
|
}
|
2015-02-06 15:20:40 +00:00
|
|
|
|
2017-12-01 12:50:41 +00:00
|
|
|
TEST(CodeSerializerAfterExecute) {
|
|
|
|
// We test that no compilations happen when running this code. Forcing
|
|
|
|
// to always optimize breaks this test.
|
2022-09-15 16:54:55 +00:00
|
|
|
bool prev_always_turbofan_value = v8_flags.always_turbofan;
|
|
|
|
v8_flags.always_turbofan = false;
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache =
|
2021-10-29 23:41:02 +00:00
|
|
|
CompileRunAndProduceCache(js_source, CodeCacheType::kAfterExecute);
|
2017-12-01 12:50:41 +00:00
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2020-12-17 16:02:56 +00:00
|
|
|
Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2);
|
2017-12-01 12:50:41 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
|
|
|
v8::Local<v8::UnboundScript> script;
|
|
|
|
{
|
2020-12-17 16:02:56 +00:00
|
|
|
DisallowCompilation no_compile_expected(i_isolate2);
|
2017-12-01 12:50:41 +00:00
|
|
|
script = v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
|
|
|
}
|
|
|
|
CHECK(!cache->rejected);
|
|
|
|
|
|
|
|
Handle<SharedFunctionInfo> sfi = v8::Utils::OpenHandle(*script);
|
|
|
|
CHECK(sfi->HasBytecodeArray());
|
|
|
|
|
|
|
|
{
|
2020-12-17 16:02:56 +00:00
|
|
|
DisallowCompilation no_compile_expected(i_isolate2);
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> result_string =
|
|
|
|
result->ToString(isolate2->GetCurrentContext()).ToLocalChecked();
|
|
|
|
CHECK(
|
|
|
|
result_string->Equals(isolate2->GetCurrentContext(), v8_str("abcdef"))
|
|
|
|
.FromJust());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
isolate2->Dispose();
|
|
|
|
|
|
|
|
// Restore the flags.
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.always_turbofan = prev_always_turbofan_value;
|
2017-12-01 12:50:41 +00:00
|
|
|
}
|
|
|
|
|
2017-09-26 08:48:52 +00:00
|
|
|
TEST(CodeSerializerFlagChange) {
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
|
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(js_source);
|
2015-02-06 15:20:40 +00:00
|
|
|
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2015-03-17 11:04:12 +00:00
|
|
|
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.allow_natives_syntax =
|
|
|
|
true; // Flag change should trigger cache reject.
|
2015-03-17 11:04:12 +00:00
|
|
|
FlagList::EnforceFlagImplications();
|
2015-02-06 15:20:40 +00:00
|
|
|
{
|
2015-02-09 14:49:35 +00:00
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
2015-02-06 15:20:40 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2015-02-09 14:49:35 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
2015-02-09 14:49:35 +00:00
|
|
|
CHECK(cache->rejected);
|
2015-02-06 15:20:40 +00:00
|
|
|
}
|
2015-02-09 14:49:35 +00:00
|
|
|
isolate2->Dispose();
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerBitFlip) {
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.verify_snapshot_checksum = true;
|
2021-10-29 23:41:02 +00:00
|
|
|
const char* js_source = "function f() { return 'abc'; }; f() + 'def'";
|
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(js_source);
|
2015-02-09 14:49:35 +00:00
|
|
|
|
2020-02-07 20:22:04 +00:00
|
|
|
// Arbitrary bit flip.
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
int arbitrary_spot = 237;
|
2020-02-07 20:22:04 +00:00
|
|
|
CHECK_LT(arbitrary_spot, cache->length);
|
|
|
|
const_cast<uint8_t*>(cache->data)[arbitrary_spot] ^= 0x40;
|
2015-02-06 15:20:40 +00:00
|
|
|
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2015-02-06 15:20:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
2021-10-29 23:41:02 +00:00
|
|
|
v8::Local<v8::String> source_str = v8_str(js_source);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2015-02-06 15:20:40 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
2015-02-06 15:20:40 +00:00
|
|
|
CHECK(cache->rejected);
|
|
|
|
}
|
|
|
|
isolate2->Dispose();
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:57:47 +00:00
|
|
|
TEST(CodeSerializerWithHarmonyScoping) {
|
2014-10-17 09:54:48 +00:00
|
|
|
const char* source1 = "'use strict'; let x = 'X'";
|
|
|
|
const char* source2 = "'use strict'; let y = 'Y'";
|
|
|
|
const char* source3 = "'use strict'; x + y";
|
|
|
|
|
|
|
|
v8::ScriptCompiler::CachedData* cache;
|
|
|
|
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(create_params);
|
2014-10-17 09:54:48 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope iscope(isolate1);
|
|
|
|
v8::HandleScope scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(source1);
|
|
|
|
CompileRun(source2);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(source3);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate1, v8_str("test"));
|
2014-10-17 09:54:48 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin);
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::UnboundScript> script =
|
|
|
|
v8::ScriptCompiler::CompileUnboundScript(
|
2018-02-20 14:14:54 +00:00
|
|
|
isolate1, &source, v8::ScriptCompiler::kNoCompileOptions)
|
2015-10-08 09:48:05 +00:00
|
|
|
.ToLocalChecked();
|
2018-04-16 07:56:17 +00:00
|
|
|
cache = v8::ScriptCompiler::CreateCodeCache(script);
|
2018-02-20 14:14:54 +00:00
|
|
|
CHECK(cache);
|
2014-10-17 09:54:48 +00:00
|
|
|
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate1->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> result_str =
|
|
|
|
result->ToString(isolate1->GetCurrentContext()).ToLocalChecked();
|
|
|
|
CHECK(result_str->Equals(isolate1->GetCurrentContext(), v8_str("XY"))
|
|
|
|
.FromJust());
|
2014-10-17 09:54:48 +00:00
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
|
2015-04-29 09:54:34 +00:00
|
|
|
v8::Isolate* isolate2 = v8::Isolate::New(create_params);
|
2014-10-17 09:54:48 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope iscope(isolate2);
|
|
|
|
v8::HandleScope scope(isolate2);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate2);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
// Reverse order of prior running scripts.
|
|
|
|
CompileRun(source2);
|
|
|
|
CompileRun(source1);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(source3);
|
2021-01-12 11:54:50 +00:00
|
|
|
v8::ScriptOrigin origin(isolate2, v8_str("test"));
|
2014-10-17 09:54:48 +00:00
|
|
|
v8::ScriptCompiler::Source source(source_str, origin, cache);
|
|
|
|
v8::Local<v8::UnboundScript> script;
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile(reinterpret_cast<Isolate*>(isolate2));
|
2015-10-08 09:48:05 +00:00
|
|
|
script = v8::ScriptCompiler::CompileUnboundScript(
|
|
|
|
isolate2, &source, v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
2014-10-17 09:54:48 +00:00
|
|
|
}
|
2015-10-08 09:48:05 +00:00
|
|
|
v8::Local<v8::Value> result = script->BindToCurrentContext()
|
|
|
|
->Run(isolate2->GetCurrentContext())
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::String> result_str =
|
|
|
|
result->ToString(isolate2->GetCurrentContext()).ToLocalChecked();
|
|
|
|
CHECK(result_str->Equals(isolate2->GetCurrentContext(), v8_str("XY"))
|
|
|
|
.FromJust());
|
2014-10-17 09:54:48 +00:00
|
|
|
}
|
|
|
|
isolate2->Dispose();
|
|
|
|
}
|
2015-03-05 13:46:31 +00:00
|
|
|
|
2015-06-24 14:26:31 +00:00
|
|
|
TEST(Regress503552) {
|
2022-09-15 16:54:55 +00:00
|
|
|
if (!v8_flags.incremental_marking) return;
|
2015-06-24 14:26:31 +00:00
|
|
|
// Test that the code serializer can deal with weak cells that form a linked
|
|
|
|
// list during incremental marking.
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
|
|
|
|
|
|
|
HandleScope scope(isolate);
|
|
|
|
Handle<String> source = isolate->factory()->NewStringFromAsciiChecked(
|
|
|
|
"function f() {} function g() {}");
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cached_data = nullptr;
|
2018-02-20 14:14:54 +00:00
|
|
|
Handle<SharedFunctionInfo> shared = CompileScriptAndProduceCache(
|
2021-08-09 13:21:43 +00:00
|
|
|
isolate, source, ScriptDetails(), &cached_data,
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
delete cached_data;
|
2015-06-24 14:26:31 +00:00
|
|
|
|
2016-05-20 13:30:22 +00:00
|
|
|
heap::SimulateIncrementalMarking(isolate->heap());
|
2015-06-24 14:26:31 +00:00
|
|
|
|
2018-03-27 10:44:13 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache_data =
|
2018-04-16 07:56:17 +00:00
|
|
|
CodeSerializer::Serialize(shared);
|
2018-03-27 10:44:13 +00:00
|
|
|
delete cache_data;
|
2015-06-24 14:26:31 +00:00
|
|
|
}
|
|
|
|
|
2022-07-11 15:11:35 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorBlobNotCreated) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
v8::Local<v8::String> code = v8_str("throw new Error('test');");
|
|
|
|
CHECK(v8::Script::Compile(context, code)
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Run(context)
|
|
|
|
.IsEmpty());
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
}
|
|
|
|
// SnapshotCreator should be destroyed just fine even when no
|
|
|
|
// blob is created.
|
|
|
|
}
|
|
|
|
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorMultipleContexts) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2016-06-15 15:38:40 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun("var f = function() { return 1; }");
|
2016-12-08 12:44:29 +00:00
|
|
|
creator.SetDefaultContext(context);
|
2016-06-15 15:38:40 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun("var f = function() { return 2; }");
|
2016-12-08 12:44:29 +00:00
|
|
|
CHECK_EQ(0u, creator.AddContext(context));
|
2016-06-15 15:38:40 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2016-12-08 12:44:29 +00:00
|
|
|
CHECK_EQ(1u, creator.AddContext(context));
|
2016-06-15 15:38:40 +00:00
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-06-15 15:38:40 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2016-06-15 15:38:40 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 1);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
2016-06-15 15:38:40 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 2);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Context::FromSnapshot(isolate, 1).ToLocalChecked();
|
2016-06-15 15:38:40 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectUndefined("this.f");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-06-15 15:38:40 +00:00
|
|
|
}
|
|
|
|
|
2018-03-28 13:11:50 +00:00
|
|
|
static int serialized_static_field = 314;
|
|
|
|
|
2016-12-08 12:44:29 +00:00
|
|
|
static void SerializedCallback(
|
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
2018-03-28 13:11:50 +00:00
|
|
|
if (args.Data()->IsExternal()) {
|
|
|
|
CHECK_EQ(args.Data().As<v8::External>()->Value(),
|
|
|
|
static_cast<void*>(&serialized_static_field));
|
|
|
|
int* value =
|
|
|
|
reinterpret_cast<int*>(args.Data().As<v8::External>()->Value());
|
|
|
|
(*value)++;
|
|
|
|
}
|
2016-06-20 15:28:06 +00:00
|
|
|
args.GetReturnValue().Set(v8_num(42));
|
|
|
|
}
|
|
|
|
|
2016-12-08 12:44:29 +00:00
|
|
|
static void SerializedCallbackReplacement(
|
2016-06-20 15:28:06 +00:00
|
|
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
|
|
|
args.GetReturnValue().Set(v8_num(1337));
|
|
|
|
}
|
|
|
|
|
2016-12-08 12:44:29 +00:00
|
|
|
static void NamedPropertyGetterForSerialization(
|
|
|
|
v8::Local<v8::Name> name, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
if (name->Equals(info.GetIsolate()->GetCurrentContext(), v8_str("x"))
|
|
|
|
.FromJust()) {
|
|
|
|
info.GetReturnValue().Set(v8_num(2016));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void AccessorForSerialization(
|
|
|
|
v8::Local<v8::String> property,
|
|
|
|
const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
|
|
info.GetReturnValue().Set(v8_num(2017));
|
|
|
|
}
|
|
|
|
|
2017-12-22 12:00:58 +00:00
|
|
|
static SerializerOneByteResource serializable_one_byte_resource("one_byte", 8);
|
|
|
|
static SerializerTwoByteResource serializable_two_byte_resource("two_byte", 8);
|
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
intptr_t original_external_references[] = {
|
2016-11-14 07:17:22 +00:00
|
|
|
reinterpret_cast<intptr_t>(SerializedCallback),
|
2016-12-08 12:44:29 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serialized_static_field),
|
|
|
|
reinterpret_cast<intptr_t>(&NamedPropertyGetterForSerialization),
|
|
|
|
reinterpret_cast<intptr_t>(&AccessorForSerialization),
|
2017-02-20 12:52:53 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serialized_static_field), // duplicate entry
|
2017-12-22 12:00:58 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serializable_one_byte_resource),
|
|
|
|
reinterpret_cast<intptr_t>(&serializable_two_byte_resource),
|
2016-12-08 12:44:29 +00:00
|
|
|
0};
|
2016-06-20 15:28:06 +00:00
|
|
|
|
|
|
|
intptr_t replaced_external_references[] = {
|
2016-11-14 07:17:22 +00:00
|
|
|
reinterpret_cast<intptr_t>(SerializedCallbackReplacement),
|
2016-12-08 12:44:29 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serialized_static_field),
|
|
|
|
reinterpret_cast<intptr_t>(&NamedPropertyGetterForSerialization),
|
|
|
|
reinterpret_cast<intptr_t>(&AccessorForSerialization),
|
2017-02-20 12:52:53 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serialized_static_field),
|
2017-12-22 12:00:58 +00:00
|
|
|
reinterpret_cast<intptr_t>(&serializable_one_byte_resource),
|
|
|
|
reinterpret_cast<intptr_t>(&serializable_two_byte_resource),
|
2016-12-08 12:44:29 +00:00
|
|
|
0};
|
2016-06-20 15:28:06 +00:00
|
|
|
|
2017-08-28 14:23:14 +00:00
|
|
|
intptr_t short_external_references[] = {
|
|
|
|
reinterpret_cast<intptr_t>(SerializedCallbackReplacement), 0};
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorExternalReferences) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator(original_external_references);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
|
|
|
v8::Local<v8::Value> function =
|
|
|
|
callback->GetFunction(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
|
2017-12-22 12:00:58 +00:00
|
|
|
|
|
|
|
CHECK(context->Global()
|
|
|
|
->Set(context, v8_str("one_byte"),
|
|
|
|
v8::String::NewExternalOneByte(
|
|
|
|
isolate, &serializable_one_byte_resource)
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
CHECK(context->Global()
|
|
|
|
->Set(context, v8_str("two_byte"),
|
|
|
|
v8::String::NewExternalTwoByte(
|
|
|
|
isolate, &serializable_two_byte_resource)
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
ExpectInt32("f()", 42);
|
2017-12-22 12:00:58 +00:00
|
|
|
ExpectString("one_byte", "one_byte");
|
|
|
|
ExpectString("two_byte", "two_byte");
|
2016-12-08 12:44:29 +00:00
|
|
|
creator.SetDefaultContext(context);
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
2017-12-22 12:00:58 +00:00
|
|
|
CHECK_EQ(1, serializable_one_byte_resource.dispose_count());
|
|
|
|
CHECK_EQ(1, serializable_two_byte_resource.dispose_count());
|
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
// Deserialize with the original external reference.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = original_external_references;
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-06-20 15:28:06 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
2017-12-22 12:00:58 +00:00
|
|
|
ExpectString("one_byte", "one_byte");
|
|
|
|
ExpectString("two_byte", "two_byte");
|
2020-07-13 10:00:53 +00:00
|
|
|
v8::Local<v8::String> one_byte = CompileRun("one_byte").As<v8::String>();
|
|
|
|
v8::Local<v8::String> two_byte = CompileRun("two_byte").As<v8::String>();
|
|
|
|
CHECK(one_byte->IsExternalOneByte());
|
|
|
|
CHECK(!one_byte->IsExternalTwoByte());
|
|
|
|
CHECK(!two_byte->IsExternalOneByte());
|
|
|
|
CHECK(two_byte->IsExternalTwoByte());
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
|
2017-12-22 12:00:58 +00:00
|
|
|
CHECK_EQ(2, serializable_one_byte_resource.dispose_count());
|
|
|
|
CHECK_EQ(2, serializable_two_byte_resource.dispose_count());
|
|
|
|
|
2017-08-28 14:23:14 +00:00
|
|
|
// Deserialize with some other external reference.
|
2016-06-20 15:28:06 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = replaced_external_references;
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-06-20 15:28:06 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2017-08-28 14:23:14 +00:00
|
|
|
ExpectInt32("f()", 1337);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
2017-12-22 12:00:58 +00:00
|
|
|
|
|
|
|
CHECK_EQ(3, serializable_one_byte_resource.dispose_count());
|
|
|
|
CHECK_EQ(3, serializable_two_byte_resource.dispose_count());
|
|
|
|
|
2017-08-28 14:23:14 +00:00
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-08-28 14:23:14 +00:00
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorShortExternalReferences) {
|
2017-08-28 14:23:14 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-08-28 14:23:14 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator(original_external_references);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
|
|
|
v8::Local<v8::Value> function =
|
|
|
|
callback->GetFunction(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = short_external_references;
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2017-08-28 14:23:14 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2016-06-20 15:28:06 +00:00
|
|
|
ExpectInt32("f()", 1337);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
|
2017-11-22 14:09:07 +00:00
|
|
|
v8::StartupData CreateSnapshotWithDefaultAndCustom() {
|
|
|
|
v8::SnapshotCreator creator(original_external_references);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun("function f() { return 41; }");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
ExpectInt32("f()", 41);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::Local<v8::FunctionTemplate> function_template =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
|
|
|
v8::Local<v8::Value> function =
|
|
|
|
function_template->GetFunction(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
|
|
|
|
v8::Local<v8::ObjectTemplate> object_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
object_template->SetAccessor(v8_str("x"), AccessorForSerialization);
|
|
|
|
v8::Local<v8::Object> object =
|
|
|
|
object_template->NewInstance(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("o"), object).FromJust());
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("o.x", 2017);
|
|
|
|
creator.AddContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorNoExternalReferencesDefault) {
|
2017-11-22 14:09:07 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-11-22 14:09:07 +00:00
|
|
|
v8::StartupData blob = CreateSnapshotWithDefaultAndCustom();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = nullptr;
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2017-11-22 14:09:07 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 41);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-11-22 14:09:07 +00:00
|
|
|
}
|
|
|
|
|
2018-06-20 09:23:32 +00:00
|
|
|
v8::StartupData CreateCustomSnapshotWithPreparseDataAndNoOuterScope() {
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"var foo = {\n"
|
|
|
|
" // This function is not top-level, but also has no outer scope.\n"
|
|
|
|
" bar: function(){\n"
|
|
|
|
" // Add an inner function so that the outer one has preparse\n"
|
|
|
|
" // scope data.\n"
|
|
|
|
" return function(){}\n"
|
|
|
|
" }\n"
|
|
|
|
"};\n");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorPreparseDataAndNoOuterScope) {
|
2018-06-20 09:23:32 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-06-20 09:23:32 +00:00
|
|
|
v8::StartupData blob = CreateCustomSnapshotWithPreparseDataAndNoOuterScope();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2018-06-20 09:23:32 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2018-06-20 09:23:32 +00:00
|
|
|
}
|
|
|
|
|
2018-06-20 11:52:16 +00:00
|
|
|
v8::StartupData CreateCustomSnapshotArrayJoinWithKeep() {
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"[].join('');\n"
|
|
|
|
"function g() { return String([1,2,3]); }\n");
|
|
|
|
ExpectString("g()", "1,2,3");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorArrayJoinWithKeep) {
|
2018-06-20 11:52:16 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-06-20 11:52:16 +00:00
|
|
|
v8::StartupData blob = CreateCustomSnapshotArrayJoinWithKeep();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2018-06-20 11:52:16 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectString("g()", "1,2,3");
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2018-06-20 11:52:16 +00:00
|
|
|
}
|
|
|
|
|
2019-07-11 15:10:03 +00:00
|
|
|
v8::StartupData CreateCustomSnapshotWithDuplicateFunctions() {
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"function f() { return (() => 'a'); }\n"
|
|
|
|
"let g1 = f();\n"
|
|
|
|
"let g2 = f();\n");
|
|
|
|
ExpectString("g1()", "a");
|
|
|
|
ExpectString("g2()", "a");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorDuplicateFunctions) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob = CreateCustomSnapshotWithDuplicateFunctions();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectString("g1()", "a");
|
|
|
|
ExpectString("g2()", "a");
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2022-08-09 16:05:20 +00:00
|
|
|
#ifndef V8_SHARED_RO_HEAP
|
|
|
|
// We do not support building multiple snapshots when read-only heap is shared.
|
|
|
|
|
2017-11-22 14:09:07 +00:00
|
|
|
TEST(SnapshotCreatorNoExternalReferencesCustomFail1) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
v8::StartupData blob = CreateSnapshotWithDefaultAndCustom();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = nullptr;
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2017-11-22 14:09:07 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SnapshotCreatorNoExternalReferencesCustomFail2) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
v8::StartupData blob = CreateSnapshotWithDefaultAndCustom();
|
|
|
|
|
|
|
|
// Deserialize with an incomplete list of external references.
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = nullptr;
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2017-11-22 14:09:07 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("o.x", 2017);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
2022-08-09 16:05:20 +00:00
|
|
|
#endif // V8_SHARED_RO_HEAP
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorUnknownExternalReferences) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2016-10-18 09:47:07 +00:00
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
|
|
|
v8::Local<v8::Value> function =
|
|
|
|
callback->GetFunction(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("f"), function).FromJust());
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
|
2016-12-08 12:44:29 +00:00
|
|
|
creator.SetDefaultContext(context);
|
2016-10-18 09:47:07 +00:00
|
|
|
}
|
|
|
|
v8::StartupData blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-10-18 09:47:07 +00:00
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorTemplates) {
|
2016-11-04 12:29:20 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::StartupData blob;
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
{
|
2016-10-27 13:35:25 +00:00
|
|
|
InternalFieldData* a1 = new InternalFieldData{11};
|
2018-11-06 10:59:57 +00:00
|
|
|
InternalFieldData* b1 = new InternalFieldData{20};
|
|
|
|
InternalFieldData* c1 = new InternalFieldData{30};
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::SnapshotCreator creator(original_external_references);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::ExtensionConfiguration* no_extension = nullptr;
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
2018-03-28 13:11:50 +00:00
|
|
|
v8::Local<v8::External> external =
|
|
|
|
v8::External::New(isolate, &serialized_static_field);
|
2016-06-21 05:08:38 +00:00
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
2018-03-28 13:11:50 +00:00
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback, external);
|
2020-09-09 11:07:28 +00:00
|
|
|
global_template->Set(isolate, "f", callback);
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::New(isolate, no_extension, global_template);
|
2017-01-09 10:12:04 +00:00
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
context = v8::Context::New(isolate, no_extension, global_template);
|
2016-10-27 13:35:25 +00:00
|
|
|
v8::Local<v8::ObjectTemplate> object_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
2016-11-14 07:17:22 +00:00
|
|
|
object_template->SetInternalFieldCount(3);
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
2018-03-28 13:11:50 +00:00
|
|
|
CHECK_EQ(315, serialized_static_field);
|
2016-10-27 13:35:25 +00:00
|
|
|
|
|
|
|
v8::Local<v8::Object> a =
|
|
|
|
object_template->NewInstance(context).ToLocalChecked();
|
|
|
|
v8::Local<v8::Object> b =
|
|
|
|
object_template->NewInstance(context).ToLocalChecked();
|
|
|
|
v8::Local<v8::Object> c =
|
|
|
|
object_template->NewInstance(context).ToLocalChecked();
|
2022-03-10 18:44:52 +00:00
|
|
|
v8::Local<v8::External> resource_external =
|
|
|
|
v8::External::New(isolate, &serializable_one_byte_resource);
|
2016-11-14 07:17:22 +00:00
|
|
|
v8::Local<v8::External> field_external =
|
|
|
|
v8::External::New(isolate, &serialized_static_field);
|
2018-11-06 10:59:57 +00:00
|
|
|
|
2016-10-27 13:35:25 +00:00
|
|
|
a->SetInternalField(0, b);
|
2018-11-06 10:59:57 +00:00
|
|
|
b->SetInternalField(0, c);
|
|
|
|
|
2016-10-27 13:35:25 +00:00
|
|
|
a->SetAlignedPointerInInternalField(1, a1);
|
2018-11-06 10:59:57 +00:00
|
|
|
b->SetAlignedPointerInInternalField(1, b1);
|
|
|
|
c->SetAlignedPointerInInternalField(1, c1);
|
|
|
|
|
2022-03-10 18:44:52 +00:00
|
|
|
a->SetInternalField(2, resource_external);
|
2018-11-06 10:59:57 +00:00
|
|
|
b->SetInternalField(2, field_external);
|
|
|
|
c->SetInternalField(2, v8_num(35));
|
2016-10-27 13:35:25 +00:00
|
|
|
CHECK(context->Global()->Set(context, v8_str("a"), a).FromJust());
|
|
|
|
|
2017-01-12 12:25:58 +00:00
|
|
|
CHECK_EQ(0u,
|
|
|
|
creator.AddContext(context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields,
|
2018-11-06 10:59:57 +00:00
|
|
|
reinterpret_cast<void*>(2000))));
|
2019-11-27 13:06:20 +00:00
|
|
|
CHECK_EQ(0u, creator.AddData(callback));
|
|
|
|
CHECK_EQ(1u, creator.AddData(global_template));
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
2017-01-09 10:12:04 +00:00
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
2016-10-27 13:35:25 +00:00
|
|
|
|
|
|
|
delete a1;
|
2018-11-06 10:59:57 +00:00
|
|
|
delete b1;
|
|
|
|
delete c1;
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = original_external_references;
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-06-20 15:28:06 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
{
|
|
|
|
// Create a new context without a new object template.
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
2017-01-09 10:12:04 +00:00
|
|
|
v8::Local<v8::Context> context =
|
2017-01-12 12:25:58 +00:00
|
|
|
v8::Context::FromSnapshot(
|
|
|
|
isolate, 0,
|
|
|
|
v8::DeserializeInternalFieldsCallback(
|
|
|
|
DeserializeInternalFields, reinterpret_cast<void*>(2017)))
|
2017-01-09 10:12:04 +00:00
|
|
|
.ToLocalChecked();
|
2016-06-20 15:28:06 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
2018-03-28 13:11:50 +00:00
|
|
|
CHECK_EQ(316, serialized_static_field);
|
2016-06-21 05:08:38 +00:00
|
|
|
|
|
|
|
// Retrieve the snapshotted object template.
|
|
|
|
v8::Local<v8::ObjectTemplate> obj_template =
|
2019-11-27 13:06:20 +00:00
|
|
|
isolate->GetDataFromSnapshotOnce<v8::ObjectTemplate>(1)
|
|
|
|
.ToLocalChecked();
|
2016-06-21 05:08:38 +00:00
|
|
|
CHECK(!obj_template.IsEmpty());
|
|
|
|
v8::Local<v8::Object> object =
|
|
|
|
obj_template->NewInstance(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("o"), object).FromJust());
|
|
|
|
ExpectInt32("o.f()", 42);
|
2018-03-28 13:11:50 +00:00
|
|
|
CHECK_EQ(317, serialized_static_field);
|
2016-06-21 05:08:38 +00:00
|
|
|
// Check that it instantiates to the same prototype.
|
|
|
|
ExpectTrue("o.f.prototype === f.prototype");
|
|
|
|
|
|
|
|
// Retrieve the snapshotted function template.
|
|
|
|
v8::Local<v8::FunctionTemplate> fun_template =
|
2019-11-27 13:06:20 +00:00
|
|
|
isolate->GetDataFromSnapshotOnce<v8::FunctionTemplate>(0)
|
|
|
|
.ToLocalChecked();
|
2016-06-21 05:08:38 +00:00
|
|
|
CHECK(!fun_template.IsEmpty());
|
|
|
|
v8::Local<v8::Function> fun =
|
|
|
|
fun_template->GetFunction(context).ToLocalChecked();
|
|
|
|
CHECK(context->Global()->Set(context, v8_str("g"), fun).FromJust());
|
|
|
|
ExpectInt32("g()", 42);
|
|
|
|
// Check that it instantiates to the same prototype.
|
|
|
|
ExpectTrue("g.prototype === f.prototype");
|
2016-06-28 13:47:35 +00:00
|
|
|
|
2017-03-17 13:26:05 +00:00
|
|
|
// Retrieve embedder fields.
|
2016-10-27 13:35:25 +00:00
|
|
|
v8::Local<v8::Object> a = context->Global()
|
|
|
|
->Get(context, v8_str("a"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->ToObject(context)
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Object> b =
|
|
|
|
a->GetInternalField(0)->ToObject(context).ToLocalChecked();
|
2018-11-06 10:59:57 +00:00
|
|
|
v8::Local<v8::Object> c =
|
|
|
|
b->GetInternalField(0)->ToObject(context).ToLocalChecked();
|
|
|
|
|
2016-10-27 13:35:25 +00:00
|
|
|
InternalFieldData* a1 = reinterpret_cast<InternalFieldData*>(
|
|
|
|
a->GetAlignedPointerFromInternalField(1));
|
2016-11-14 07:17:22 +00:00
|
|
|
v8::Local<v8::Value> a2 = a->GetInternalField(2);
|
|
|
|
|
2018-11-06 10:59:57 +00:00
|
|
|
InternalFieldData* b1 = reinterpret_cast<InternalFieldData*>(
|
|
|
|
b->GetAlignedPointerFromInternalField(1));
|
2016-11-14 07:17:22 +00:00
|
|
|
v8::Local<v8::Value> b2 = b->GetInternalField(2);
|
|
|
|
|
2018-11-06 10:59:57 +00:00
|
|
|
v8::Local<v8::Value> c0 = c->GetInternalField(0);
|
|
|
|
InternalFieldData* c1 = reinterpret_cast<InternalFieldData*>(
|
|
|
|
c->GetAlignedPointerFromInternalField(1));
|
2016-11-14 07:17:22 +00:00
|
|
|
v8::Local<v8::Value> c2 = c->GetInternalField(2);
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2018-11-06 10:59:57 +00:00
|
|
|
CHECK(c0->IsUndefined());
|
|
|
|
|
2016-11-11 13:53:35 +00:00
|
|
|
CHECK_EQ(11u, a1->data);
|
2018-11-06 10:59:57 +00:00
|
|
|
CHECK_EQ(20u, b1->data);
|
|
|
|
CHECK_EQ(30u, c1->data);
|
|
|
|
|
|
|
|
CHECK(a2->IsExternal());
|
2022-03-10 18:44:52 +00:00
|
|
|
CHECK_EQ(static_cast<void*>(&serializable_one_byte_resource),
|
|
|
|
v8::Local<v8::External>::Cast(a2)->Value());
|
2018-11-06 10:59:57 +00:00
|
|
|
CHECK(b2->IsExternal());
|
2016-11-14 07:17:22 +00:00
|
|
|
CHECK_EQ(static_cast<void*>(&serialized_static_field),
|
2018-11-06 10:59:57 +00:00
|
|
|
v8::Local<v8::External>::Cast(b2)->Value());
|
|
|
|
CHECK(c2->IsInt32() && c2->Int32Value(context).FromJust() == 35);
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2019-11-27 13:06:20 +00:00
|
|
|
// Calling GetDataFromSnapshotOnce again returns an empty MaybeLocal.
|
|
|
|
CHECK(
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::ObjectTemplate>(1).IsEmpty());
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::FunctionTemplate>(0)
|
|
|
|
.IsEmpty());
|
2016-12-08 12:44:29 +00:00
|
|
|
CHECK(v8::Context::FromSnapshot(isolate, 1).IsEmpty());
|
2016-10-27 13:35:25 +00:00
|
|
|
|
2016-12-19 12:04:19 +00:00
|
|
|
for (auto data : deserialized_data) delete data;
|
|
|
|
deserialized_data.clear();
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-06-20 15:28:06 +00:00
|
|
|
}
|
|
|
|
|
2021-02-23 19:00:16 +00:00
|
|
|
MaybeLocal<v8::Module> ResolveCallback(Local<v8::Context> context,
|
|
|
|
Local<v8::String> specifier,
|
|
|
|
Local<v8::FixedArray> import_assertions,
|
|
|
|
Local<v8::Module> referrer) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorAddData) {
|
2018-01-08 20:32:18 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-01-08 20:32:18 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
|
2020-09-09 03:25:22 +00:00
|
|
|
// i::PerformCastCheck(Data*) should compile and be no-op
|
|
|
|
{
|
|
|
|
v8::Local<v8::Data> data;
|
|
|
|
i::PerformCastCheck(*data);
|
|
|
|
}
|
|
|
|
|
2018-01-08 20:32:18 +00:00
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
v8::Eternal<v8::Value> eternal_number;
|
|
|
|
v8::Persistent<v8::Value> persistent_number_1;
|
|
|
|
v8::Persistent<v8::Value> persistent_number_2;
|
|
|
|
v8::Persistent<v8::Context> persistent_context;
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
eternal_number.Set(isolate, v8_num(2017));
|
|
|
|
persistent_number_1.Reset(isolate, v8_num(2018));
|
|
|
|
persistent_number_2.Reset(isolate, v8_num(2019));
|
|
|
|
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK_EQ(0u, creator.AddData(context, persistent_number_2.Get(isolate)));
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
context = v8::Context::New(isolate);
|
|
|
|
persistent_context.Reset(isolate, context);
|
|
|
|
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
v8::Local<v8::Object> object = CompileRun("({ p: 12 })").As<v8::Object>();
|
|
|
|
|
|
|
|
v8::Local<v8::ObjectTemplate> object_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
object_template->SetInternalFieldCount(3);
|
|
|
|
|
2018-01-16 09:18:05 +00:00
|
|
|
v8::Local<v8::Private> private_symbol =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("private_symbol"));
|
|
|
|
|
|
|
|
v8::Local<v8::Signature> signature =
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
v8::Signature::New(isolate, v8::FunctionTemplate::New(isolate));
|
2018-01-16 09:18:05 +00:00
|
|
|
|
2021-02-23 19:00:16 +00:00
|
|
|
v8::ScriptOrigin origin(isolate, v8_str(""), {}, {}, {}, {}, {}, {}, {},
|
|
|
|
true);
|
|
|
|
v8::ScriptCompiler::Source source(
|
|
|
|
v8::String::NewFromUtf8Literal(
|
|
|
|
isolate, "export let a = 42; globalThis.a = {};"),
|
|
|
|
origin);
|
|
|
|
v8::Local<v8::Module> module =
|
|
|
|
v8::ScriptCompiler::CompileModule(isolate, &source).ToLocalChecked();
|
|
|
|
module->InstantiateModule(context, ResolveCallback).ToChecked();
|
|
|
|
module->Evaluate(context).ToLocalChecked();
|
|
|
|
|
2018-01-08 20:32:18 +00:00
|
|
|
CHECK_EQ(0u, creator.AddData(context, object));
|
|
|
|
CHECK_EQ(1u, creator.AddData(context, v8_str("context-dependent")));
|
|
|
|
CHECK_EQ(2u, creator.AddData(context, persistent_number_1.Get(isolate)));
|
|
|
|
CHECK_EQ(3u, creator.AddData(context, object_template));
|
|
|
|
CHECK_EQ(4u, creator.AddData(context, persistent_context.Get(isolate)));
|
2021-02-23 19:00:16 +00:00
|
|
|
CHECK_EQ(5u, creator.AddData(context, module));
|
2018-01-08 20:32:18 +00:00
|
|
|
creator.AddContext(context);
|
|
|
|
|
|
|
|
CHECK_EQ(0u, creator.AddData(v8_str("context-independent")));
|
|
|
|
CHECK_EQ(1u, creator.AddData(eternal_number.Get(isolate)));
|
|
|
|
CHECK_EQ(2u, creator.AddData(object_template));
|
|
|
|
CHECK_EQ(3u, creator.AddData(v8::FunctionTemplate::New(isolate)));
|
2018-01-16 09:18:05 +00:00
|
|
|
CHECK_EQ(4u, creator.AddData(private_symbol));
|
|
|
|
CHECK_EQ(5u, creator.AddData(signature));
|
2018-01-08 20:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2018-01-08 20:32:18 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
|
|
|
|
|
|
|
// Check serialized data on the context.
|
|
|
|
v8::Local<v8::Object> object =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::Object>(0).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Object>(0).IsEmpty());
|
|
|
|
CHECK_EQ(12, object->Get(context, v8_str("p"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->Int32Value(context)
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
v8::Local<v8::String> string =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::String>(1).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::String>(1).IsEmpty());
|
|
|
|
CHECK(string->Equals(context, v8_str("context-dependent")).FromJust());
|
|
|
|
|
|
|
|
v8::Local<v8::Number> number =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::Number>(2).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Number>(2).IsEmpty());
|
|
|
|
CHECK_EQ(2018, number->Int32Value(context).FromJust());
|
|
|
|
|
|
|
|
v8::Local<v8::ObjectTemplate> templ =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::ObjectTemplate>(3)
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::ObjectTemplate>(3).IsEmpty());
|
|
|
|
CHECK_EQ(3, templ->InternalFieldCount());
|
|
|
|
|
|
|
|
v8::Local<v8::Context> serialized_context =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::Context>(4).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Context>(4).IsEmpty());
|
|
|
|
CHECK_EQ(*v8::Utils::OpenHandle(*serialized_context),
|
|
|
|
*v8::Utils::OpenHandle(*context));
|
|
|
|
|
2021-02-23 19:00:16 +00:00
|
|
|
v8::Local<v8::Module> serialized_module =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::Module>(5).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Context>(5).IsEmpty());
|
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::Local<v8::Object> mod_ns =
|
|
|
|
serialized_module->GetModuleNamespace().As<v8::Object>();
|
|
|
|
CHECK(mod_ns->Get(context, v8_str("a"))
|
|
|
|
.ToLocalChecked()
|
|
|
|
->StrictEquals(v8_num(42.0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Value>(6).IsEmpty());
|
2018-01-08 20:32:18 +00:00
|
|
|
|
|
|
|
// Check serialized data on the isolate.
|
|
|
|
string = isolate->GetDataFromSnapshotOnce<v8::String>(0).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::String>(0).IsEmpty());
|
|
|
|
CHECK(string->Equals(context, v8_str("context-independent")).FromJust());
|
|
|
|
|
|
|
|
number = isolate->GetDataFromSnapshotOnce<v8::Number>(1).ToLocalChecked();
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::Number>(1).IsEmpty());
|
|
|
|
CHECK_EQ(2017, number->Int32Value(context).FromJust());
|
|
|
|
|
|
|
|
templ = isolate->GetDataFromSnapshotOnce<v8::ObjectTemplate>(2)
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::ObjectTemplate>(2).IsEmpty());
|
|
|
|
CHECK_EQ(3, templ->InternalFieldCount());
|
|
|
|
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::FunctionTemplate>(3)
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::FunctionTemplate>(3).IsEmpty());
|
|
|
|
|
2018-01-16 09:18:05 +00:00
|
|
|
isolate->GetDataFromSnapshotOnce<v8::Private>(4).ToLocalChecked();
|
Reland^4 "[serializer] Allocate during deserialization"
This relands commit 3f4e9bbe43650862884b9719494b216635f3b8c2.
which was a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
The change had an issue that embedders implementing heap tracing (e.g.
Unified Heap with Blink) could be passed an uninitialized pointer if
marking happened during deserialization of an object containing such a
pointer. Because of the 0xdeadbed0 uninitialized filler value, these
embedders would then receive the value 0xdeadbed0deadbed0 as the
'pointer', and crash on dereference.
There is, however, special handling already for null pointers in heap
tracing, also for dealing with not-yet initialized values. So, we can
make the uninitialized Smi filler be 0x00000000, and that will make such
embedded fields have a nullptr representation, making them follow the
normal uninitialized value bailouts.
In addition, it relands the following dependent changes, which are
relanding unchanged and are followup performance improvements.
Relanding them in the same change should allow for cleaner reverts
should they be needed.
This relands commit 76ad3ab59732a0ac400874b5143eb18697813cf3
[identity-map] Change resize heuristic
This relands commit 77cc96aa48a4ec2841b4c1fffd63f2a69c3f59d0
[identity-map] Cache the calculated Hash
This relands commit bee5b996aae018acf87ece5fda74e07678f3bba1
[serializer] Remove Deserializer::Initialize
This relands commit c8f73f22662a8db351123a20761d48006545af37
[serializer] Cache instance type in PostProcessNewObject
This relands commit 4e7c99abdab608ce63c7dcff5a2f891f695d529c
[identity-map] Remove double-lookups in IdentityMap
Original change's description:
> Reland^3 "[serializer] Allocate during deserialization"
>
> This is a reland of c4a062a9588b24a844a30358ac40f811d0020e0b
> which was a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
>
> Fixes TSAN errors from non-atomic writes in the deserializer. Now all
> writes are (relaxed) atomic.
>
> Original change's description:
> > Reland^2 "[serializer] Allocate during deserialization"
> >
> > This is a reland of 28a30c578c1d690b320b602d6c90e7fe1996c7f4
> > which was a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> >
> > The crashes were from calling RegisterDeserializerFinished on a null
> > Isolate pointer, for a deserializer that was never initialised
> > (specifically, ReadOnlyDeserializer when ROHeap is shared).
> >
> > Original change's description:
> > > Reland "[serializer] Allocate during deserialization"
> > >
> > > This is a reland of 5d7a29c90e85476edf44c9e9e495630cf395086a
> > >
> > > This reland shuffles around the order of checks in Heap::AllocateRawWith
> > > to not check the new space addresses until it's known that this is a new
> > > space allocation. This fixes an UBSan failure during read-only space
> > > deserialization, which happens before the new space is initialized.
> > >
> > > It also fixes some issues discovered by --stress-snapshot, around
> > > serializing ThinStrings (which are now elided as part of serialization),
> > > handle counts (I bumped the maximum handle count in that check), and
> > > clearing map transitions (the map backpointer field needed a Smi
> > > uninitialized value check).
> > >
> > > Original change's description:
> > > > [serializer] Allocate during deserialization
> > > >
> > > > This patch removes the concept of reservations and a specialized
> > > > deserializer allocator, and instead makes the deserializer allocate
> > > > directly with the Heap's Allocate method.
> > > >
> > > > The major consequence of this is that the GC can now run during
> > > > deserialization, which means that:
> > > >
> > > > a) Deserialized objects are visible to the GC, and
> > > > b) Objects that the deserializer/deserialized objects point to can
> > > > move.
> > > >
> > > > Point a) is mostly not a problem due to previous work in making
> > > > deserialized objects "GC valid", i.e. making sure that they have a valid
> > > > size before any subsequent allocation/safepoint. We now additionally
> > > > have to initialize the allocated space with a valid tagged value -- this
> > > > is a magic Smi value to keep "uninitialized" checks simple.
> > > >
> > > > Point b) is solved by Handlifying the deserializer. This involves
> > > > changing any vectors of objects into vectors of Handles, and any object
> > > > keyed map into an IdentityMap (we can't use Handles as keys because
> > > > the object's address is no longer a stable hash).
> > > >
> > > > Back-references can no longer be direct chunk offsets, so instead the
> > > > deserializer stores a Handle to each deserialized object, and the
> > > > backreference is an index into this handle array. This encoding could
> > > > be optimized in the future with e.g. a second pass over the serialized
> > > > array which emits a different bytecode for objects that are and aren't
> > > > back-referenced.
> > > >
> > > > Additionally, the slot-walk over objects to initialize them can no
> > > > longer use absolute slot offsets, as again an object may move and its
> > > > slot address would become invalid. Now, slots are walked as relative
> > > > offsets to a Handle to the object, or as absolute slots for the case of
> > > > root pointers. A concept of "slot accessor" is introduced to share the
> > > > code between these two modes, and writing the slot (including write
> > > > barriers) is abstracted into this accessor.
> > > >
> > > > Finally, the Code body walk is modified to deserialize all objects
> > > > referred to by RelocInfos before doing the RelocInfo walk itself. This
> > > > is because RelocInfoIterator uses raw pointers, so we cannot allocate
> > > > during a RelocInfo walk.
> > > >
> > > > As a drive-by, the VariableRawData bytecode is tweaked to use tagged
> > > > size rather than byte size -- the size is expected to be tagged-aligned
> > > > anyway, so now we get an extra few bits in the size encoding.
> > > >
> > > > Bug: chromium:1075999
> > > > Change-Id: I672c42f553f2669888cc5e35d692c1b8ece1845e
> > > > Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2404451
> > > > Commit-Queue: Leszek Swirski <leszeks@chromium.org>
> > > > Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> > > > Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
> > > > Cr-Commit-Position: refs/heads/master@{#70229}
Bug: chromium:1075999
Change-Id: Ib514a4ef16bd02bfb60d046ecbf8fae1ead64a98
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2452689
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70366}
2020-10-07 07:31:16 +00:00
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::Private>(4).IsEmpty());
|
2018-01-16 09:18:05 +00:00
|
|
|
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::Signature>(5).ToLocalChecked();
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::Signature>(5).IsEmpty());
|
|
|
|
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::Value>(7).IsEmpty());
|
2018-01-08 20:32:18 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
{
|
|
|
|
SnapshotCreator creator(nullptr, &blob);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
// Adding data to a snapshot replaces the list of existing data.
|
|
|
|
v8::HandleScope hscope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
context = v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
|
|
|
v8::Local<v8::String> string =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::String>(1).ToLocalChecked();
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::String>(1).IsEmpty());
|
|
|
|
CHECK(string->Equals(context, v8_str("context-dependent")).FromJust());
|
|
|
|
v8::Local<v8::Number> number =
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::Number>(1).ToLocalChecked();
|
|
|
|
CHECK(isolate->GetDataFromSnapshotOnce<v8::Number>(1).IsEmpty());
|
|
|
|
CHECK_EQ(2017, number->Int32Value(context).FromJust());
|
|
|
|
|
|
|
|
CHECK_EQ(0u, creator.AddData(context, v8_num(2016)));
|
|
|
|
CHECK_EQ(0u, creator.AddContext(context));
|
|
|
|
CHECK_EQ(0u, creator.AddData(v8_str("stuff")));
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2018-01-08 20:32:18 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
// Context where we did not re-add data no longer has data.
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Object>(0).IsEmpty());
|
|
|
|
|
|
|
|
// Context where we re-added data has completely new ones.
|
|
|
|
context = v8::Context::FromSnapshot(isolate, 0).ToLocalChecked();
|
|
|
|
v8::Local<v8::Value> value =
|
|
|
|
context->GetDataFromSnapshotOnce<v8::Value>(0).ToLocalChecked();
|
|
|
|
CHECK_EQ(2016, value->Int32Value(context).FromJust());
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::Value>(1).IsEmpty());
|
|
|
|
|
|
|
|
// Ditto for the isolate.
|
|
|
|
v8::Local<v8::String> string =
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::String>(0).ToLocalChecked();
|
|
|
|
CHECK(string->Equals(context, v8_str("stuff")).FromJust());
|
|
|
|
CHECK(context->GetDataFromSnapshotOnce<v8::String>(1).IsEmpty());
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2018-01-08 20:32:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(SnapshotCreatorUnknownHandles) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
v8::StartupData blob;
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
v8::Eternal<v8::Value> eternal_number;
|
|
|
|
v8::Persistent<v8::Value> persistent_number;
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
eternal_number.Set(isolate, v8_num(2017));
|
|
|
|
persistent_number.Reset(isolate, v8_num(2018));
|
|
|
|
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
2021-09-17 14:15:49 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotAccessorDescriptors) {
|
|
|
|
const char* source1 =
|
|
|
|
"var bValue = 38;\n"
|
|
|
|
"Object.defineProperty(this, 'property1', {\n"
|
|
|
|
" get() { return bValue; },\n"
|
|
|
|
" set(newValue) { bValue = newValue; },\n"
|
|
|
|
"});";
|
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(params1);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate1);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
ExpectInt32("this.property1", 38);
|
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
delete[] data1.data;
|
|
|
|
}
|
|
|
|
|
2022-02-14 08:58:10 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotObjectDefinePropertyWhenNewGlobalTemplate) {
|
|
|
|
const char* source1 =
|
|
|
|
"Object.defineProperty(this, 'property1', {\n"
|
|
|
|
" value: 42,\n"
|
|
|
|
" writable: false\n"
|
|
|
|
"});\n"
|
|
|
|
"var bValue = 38;\n"
|
|
|
|
"Object.defineProperty(this, 'property2', {\n"
|
|
|
|
" get() { return bValue; },\n"
|
|
|
|
" set(newValue) { bValue = newValue; }\n"
|
|
|
|
"});";
|
|
|
|
v8::StartupData data1 = CreateSnapshotDataBlob(source1);
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams params1;
|
|
|
|
params1.snapshot_blob = &data1;
|
|
|
|
params1.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
|
|
|
|
v8::Isolate* isolate1 = v8::Isolate::New(params1);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate1);
|
|
|
|
v8::HandleScope h_scope(isolate1);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate1);
|
|
|
|
v8::Local<v8::Context> context =
|
|
|
|
v8::Context::New(isolate1, nullptr, global_template);
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
ExpectInt32("this.property1", 42);
|
|
|
|
ExpectInt32("this.property2", 38);
|
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
delete[] data1.data;
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorIncludeGlobalProxy) {
|
2016-12-08 12:44:29 +00:00
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator(original_external_references);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
// Set default context. This context implicitly does *not* serialize
|
|
|
|
// the global proxy, and upon deserialization one has to be created
|
|
|
|
// in the bootstrapper from the global object template.
|
|
|
|
// Side effects from extensions are persisted though.
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
2020-09-09 11:07:28 +00:00
|
|
|
global_template->Set(isolate, "f", callback);
|
2016-12-08 12:44:29 +00:00
|
|
|
global_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedPropertyGetterForSerialization));
|
|
|
|
v8::Local<v8::Context> context =
|
2018-05-28 08:27:06 +00:00
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2018-05-28 08:27:06 +00:00
|
|
|
CompileRun(
|
|
|
|
"function h() { return 13; };"
|
|
|
|
"function i() { return 14; };"
|
|
|
|
"var o = { p: 7 };");
|
2016-12-08 12:44:29 +00:00
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("h()", 13);
|
|
|
|
ExpectInt32("o.p", 7);
|
|
|
|
ExpectInt32("x", 2016);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// Add additional context. This context implicitly *does* serialize
|
|
|
|
// the global proxy, and upon deserialization one has to be created
|
|
|
|
// in the bootstrapper from the global object template.
|
|
|
|
// Side effects from extensions are persisted.
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::ObjectTemplate> global_template =
|
|
|
|
v8::ObjectTemplate::New(isolate);
|
|
|
|
v8::Local<v8::FunctionTemplate> callback =
|
|
|
|
v8::FunctionTemplate::New(isolate, SerializedCallback);
|
2016-12-16 12:40:38 +00:00
|
|
|
global_template->SetInternalFieldCount(3);
|
2020-09-09 11:07:28 +00:00
|
|
|
global_template->Set(isolate, "f", callback);
|
2016-12-08 12:44:29 +00:00
|
|
|
global_template->SetHandler(v8::NamedPropertyHandlerConfiguration(
|
|
|
|
NamedPropertyGetterForSerialization));
|
|
|
|
global_template->SetAccessor(v8_str("y"), AccessorForSerialization);
|
2016-12-16 13:25:19 +00:00
|
|
|
v8::Local<v8::Private> priv =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("cached"));
|
|
|
|
global_template->SetAccessorProperty(
|
|
|
|
v8_str("cached"),
|
|
|
|
v8::FunctionTemplate::NewWithCache(isolate, SerializedCallback, priv,
|
|
|
|
v8::Local<v8::Value>()));
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Local<v8::Context> context =
|
2018-05-28 08:27:06 +00:00
|
|
|
v8::Context::New(isolate, nullptr, global_template);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::Context::Scope context_scope(context);
|
2016-12-16 13:25:19 +00:00
|
|
|
|
|
|
|
CHECK(context->Global()
|
|
|
|
->SetPrivate(context, priv, v8_str("cached string"))
|
|
|
|
.FromJust());
|
|
|
|
v8::Local<v8::Private> hidden =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("hidden"));
|
|
|
|
CHECK(context->Global()
|
|
|
|
->SetPrivate(context, hidden, v8_str("hidden string"))
|
|
|
|
.FromJust());
|
|
|
|
|
2016-12-08 12:44:29 +00:00
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("x", 2016);
|
|
|
|
ExpectInt32("y", 2017);
|
2016-12-16 13:25:19 +00:00
|
|
|
CHECK(v8_str("hidden string")
|
|
|
|
->Equals(context, context->Global()
|
|
|
|
->GetPrivate(context, hidden)
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
|
2017-01-12 12:25:58 +00:00
|
|
|
CHECK_EQ(0u,
|
|
|
|
creator.AddContext(context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields,
|
|
|
|
reinterpret_cast<void*>(2016))));
|
2016-12-08 12:44:29 +00:00
|
|
|
}
|
2017-01-09 10:12:04 +00:00
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
2016-12-08 12:44:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::Isolate::CreateParams params;
|
|
|
|
params.snapshot_blob = &blob;
|
|
|
|
params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
params.external_references = original_external_references;
|
2017-04-07 13:31:29 +00:00
|
|
|
// Test-appropriate equivalent of v8::Isolate::New.
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(params);
|
2016-12-08 12:44:29 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
2018-05-28 08:27:06 +00:00
|
|
|
// We can introduce new extensions, which could override functions already
|
|
|
|
// in the snapshot.
|
2019-01-31 11:36:20 +00:00
|
|
|
auto extension =
|
2019-09-10 10:12:00 +00:00
|
|
|
std::make_unique<v8::Extension>("new extension",
|
|
|
|
"function i() { return 24; }"
|
|
|
|
"function j() { return 25; }"
|
|
|
|
"let a = 26;"
|
|
|
|
"try {"
|
|
|
|
" if (o.p == 7) o.p++;"
|
|
|
|
"} catch {}");
|
2016-12-08 12:44:29 +00:00
|
|
|
extension->set_auto_enable(true);
|
2019-01-31 11:36:20 +00:00
|
|
|
v8::RegisterExtension(std::move(extension));
|
2016-12-08 12:44:29 +00:00
|
|
|
{
|
2021-09-17 14:15:49 +00:00
|
|
|
// Create a new context from default context snapshot. This will also
|
|
|
|
// deserialize its global object with interceptor.
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("h()", 13);
|
|
|
|
ExpectInt32("i()", 24);
|
|
|
|
ExpectInt32("j()", 25);
|
|
|
|
ExpectInt32("o.p", 8);
|
2019-06-19 07:50:46 +00:00
|
|
|
ExpectInt32("a", 26);
|
2021-09-17 14:15:49 +00:00
|
|
|
ExpectInt32("x", 2016);
|
2016-12-08 12:44:29 +00:00
|
|
|
}
|
|
|
|
{
|
|
|
|
// Create a new context from first additional context snapshot. This
|
|
|
|
// will use the global object from the snapshot, including interceptor.
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context =
|
2017-01-12 12:25:58 +00:00
|
|
|
v8::Context::FromSnapshot(
|
|
|
|
isolate, 0,
|
|
|
|
v8::DeserializeInternalFieldsCallback(
|
|
|
|
DeserializeInternalFields, reinterpret_cast<void*>(2017)))
|
2017-01-09 10:12:04 +00:00
|
|
|
.ToLocalChecked();
|
2017-01-12 12:25:58 +00:00
|
|
|
|
2016-12-13 11:24:24 +00:00
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("i()", 24);
|
|
|
|
ExpectInt32("j()", 25);
|
|
|
|
ExpectInt32("x", 2016);
|
2016-12-16 13:25:19 +00:00
|
|
|
v8::Local<v8::Private> hidden =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("hidden"));
|
|
|
|
CHECK(v8_str("hidden string")
|
|
|
|
->Equals(context, context->Global()
|
|
|
|
->GetPrivate(context, hidden)
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
ExpectString("cached", "cached string");
|
2016-12-13 11:24:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
v8::Local<v8::Object> global = context->Global();
|
2016-12-16 12:40:38 +00:00
|
|
|
CHECK_EQ(3, global->InternalFieldCount());
|
2016-12-13 11:24:24 +00:00
|
|
|
context->DetachGlobal();
|
|
|
|
|
|
|
|
// New context, but reuse global proxy.
|
|
|
|
v8::ExtensionConfiguration* no_extensions = nullptr;
|
|
|
|
v8::Local<v8::Context> context2 =
|
2017-01-12 12:25:58 +00:00
|
|
|
v8::Context::FromSnapshot(
|
|
|
|
isolate, 0,
|
|
|
|
v8::DeserializeInternalFieldsCallback(
|
|
|
|
DeserializeInternalFields, reinterpret_cast<void*>(2017)),
|
|
|
|
no_extensions, global)
|
2016-12-13 11:24:24 +00:00
|
|
|
.ToLocalChecked();
|
|
|
|
{
|
|
|
|
v8::Context::Scope context_scope(context2);
|
|
|
|
ExpectInt32("f()", 42);
|
|
|
|
ExpectInt32("i()", 24);
|
|
|
|
ExpectInt32("j()", 25);
|
|
|
|
ExpectInt32("x", 2016);
|
2016-12-16 13:25:19 +00:00
|
|
|
v8::Local<v8::Private> hidden =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("hidden"));
|
|
|
|
CHECK(v8_str("hidden string")
|
|
|
|
->Equals(context2, context2->Global()
|
|
|
|
->GetPrivate(context2, hidden)
|
|
|
|
.ToLocalChecked())
|
|
|
|
.FromJust());
|
|
|
|
|
|
|
|
// Set cached accessor property again.
|
|
|
|
v8::Local<v8::Private> priv =
|
|
|
|
v8::Private::ForApi(isolate, v8_str("cached"));
|
|
|
|
CHECK(context2->Global()
|
|
|
|
->SetPrivate(context2, priv, v8_str("cached string 1"))
|
|
|
|
.FromJust());
|
|
|
|
ExpectString("cached", "cached string 1");
|
2016-12-13 11:24:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CHECK(context2->Global()->Equals(context2, global).FromJust());
|
2016-12-08 12:44:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2016-12-08 12:44:29 +00:00
|
|
|
}
|
|
|
|
|
2020-05-26 16:07:50 +00:00
|
|
|
UNINITIALIZED_TEST(ReinitializeHashSeedJSCollectionRehashable) {
|
2017-07-18 07:59:59 +00:00
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-07-18 07:59:59 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
2017-11-07 06:50:22 +00:00
|
|
|
// Create an object with an ordered hash table.
|
2017-07-18 07:59:59 +00:00
|
|
|
CompileRun(
|
2017-11-07 06:50:22 +00:00
|
|
|
"var m = new Map();"
|
|
|
|
"m.set('a', 1);"
|
2020-05-26 16:07:50 +00:00
|
|
|
"m.set('b', 2);"
|
|
|
|
"var s = new Set();"
|
|
|
|
"s.add(1);"
|
|
|
|
"s.add(globalThis);");
|
2017-11-07 06:50:22 +00:00
|
|
|
ExpectInt32("m.get('b')", 2);
|
2020-05-26 16:07:50 +00:00
|
|
|
ExpectTrue("s.has(1)");
|
|
|
|
ExpectTrue("s.has(globalThis)");
|
2017-07-18 07:59:59 +00:00
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
2020-05-26 16:07:50 +00:00
|
|
|
CHECK(blob.CanBeRehashed());
|
2017-07-18 07:59:59 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.hash_seed = 1337;
|
2017-07-18 07:59:59 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
2020-05-26 16:07:50 +00:00
|
|
|
// Check that rehashing has been performed.
|
|
|
|
CHECK_EQ(static_cast<uint64_t>(1337),
|
2019-02-14 21:10:30 +00:00
|
|
|
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
2017-07-18 07:59:59 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
2017-11-07 06:50:22 +00:00
|
|
|
ExpectInt32("m.get('b')", 2);
|
2020-05-26 16:07:50 +00:00
|
|
|
ExpectTrue("s.has(1)");
|
|
|
|
ExpectTrue("s.has(globalThis)");
|
2017-11-07 06:50:22 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-11-07 06:50:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ReinitializeHashSeedRehashable) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2017-11-07 06:50:22 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
// Create dictionary mode object.
|
|
|
|
CompileRun(
|
|
|
|
"var a = new Array(10000);"
|
|
|
|
"%NormalizeElements(a);"
|
|
|
|
"a[133] = 1;"
|
|
|
|
"a[177] = 2;"
|
|
|
|
"a[971] = 3;"
|
|
|
|
"a[7997] = 4;"
|
|
|
|
"a[2111] = 5;"
|
|
|
|
"var o = {};"
|
|
|
|
"%OptimizeObjectForAddingMultipleProperties(o, 3);"
|
|
|
|
"o.a = 1;"
|
|
|
|
"o.b = 2;"
|
2018-01-15 10:19:39 +00:00
|
|
|
"o.c = 3;"
|
|
|
|
"var p = { foo: 1 };" // Test rehashing of transition arrays.
|
|
|
|
"p = JSON.parse('{\"foo\": {\"x\": 1}}');");
|
2017-11-07 06:50:22 +00:00
|
|
|
i::Handle<i::Object> i_a = v8::Utils::OpenHandle(*CompileRun("a"));
|
|
|
|
i::Handle<i::Object> i_o = v8::Utils::OpenHandle(*CompileRun("o"));
|
|
|
|
CHECK(i_a->IsJSArray());
|
|
|
|
CHECK(i_a->IsJSObject());
|
|
|
|
CHECK(!i::Handle<i::JSArray>::cast(i_a)->HasFastElements());
|
|
|
|
CHECK(!i::Handle<i::JSObject>::cast(i_o)->HasFastProperties());
|
|
|
|
ExpectInt32("a[2111]", 5);
|
|
|
|
ExpectInt32("o.c", 3);
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
2019-05-02 13:49:45 +00:00
|
|
|
CHECK(blob.CanBeRehashed());
|
2017-11-07 06:50:22 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.hash_seed = 1337;
|
2017-11-07 06:50:22 +00:00
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
// Check that rehashing has been performed.
|
2018-07-16 09:52:50 +00:00
|
|
|
CHECK_EQ(static_cast<uint64_t>(1337),
|
2019-02-14 21:10:30 +00:00
|
|
|
HashSeed(reinterpret_cast<i::Isolate*>(isolate)));
|
2017-11-07 06:50:22 +00:00
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
i::Handle<i::Object> i_a = v8::Utils::OpenHandle(*CompileRun("a"));
|
|
|
|
i::Handle<i::Object> i_o = v8::Utils::OpenHandle(*CompileRun("o"));
|
|
|
|
CHECK(i_a->IsJSArray());
|
|
|
|
CHECK(i_a->IsJSObject());
|
|
|
|
CHECK(!i::Handle<i::JSArray>::cast(i_a)->HasFastElements());
|
|
|
|
CHECK(!i::Handle<i::JSObject>::cast(i_o)->HasFastProperties());
|
|
|
|
ExpectInt32("a[2111]", 5);
|
|
|
|
ExpectInt32("o.c", 3);
|
2017-07-18 07:59:59 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
2018-11-15 12:49:20 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2017-07-18 07:59:59 +00:00
|
|
|
}
|
|
|
|
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
UNINITIALIZED_TEST(ClassFields) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class ClassWithFieldInitializer {"
|
|
|
|
" #field = 1;"
|
|
|
|
" constructor(val) {"
|
|
|
|
" this.#field = val;"
|
|
|
|
" }"
|
|
|
|
" get field() {"
|
|
|
|
" return this.#field;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class ClassWithDefaultConstructor {"
|
|
|
|
" #field = 42;"
|
|
|
|
" get field() {"
|
|
|
|
" return this.#field;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class ClassWithFieldDeclaration {"
|
|
|
|
" #field;"
|
|
|
|
" constructor(val) {"
|
|
|
|
" this.#field = val;"
|
|
|
|
" }"
|
|
|
|
" get field() {"
|
|
|
|
" return this.#field;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class ClassWithPublicField {"
|
|
|
|
" field = 1;"
|
|
|
|
" constructor(val) {"
|
|
|
|
" this.field = val;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class ClassWithFunctionField {"
|
|
|
|
" field = 123;"
|
|
|
|
" func = () => { return this.field; }"
|
|
|
|
"}"
|
|
|
|
"class ClassWithThisInInitializer {"
|
|
|
|
" #field = 123;"
|
|
|
|
" field = this.#field;"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("(new ClassWithFieldInitializer(123)).field", 123);
|
|
|
|
ExpectInt32("(new ClassWithDefaultConstructor()).field", 42);
|
|
|
|
ExpectInt32("(new ClassWithFieldDeclaration(123)).field", 123);
|
|
|
|
ExpectInt32("(new ClassWithPublicField(123)).field", 123);
|
|
|
|
ExpectInt32("(new ClassWithFunctionField()).func()", 123);
|
|
|
|
ExpectInt32("(new ClassWithThisInInitializer()).field", 123);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsReferencePrivateInInitializer) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class A {"
|
|
|
|
" #a = 42;"
|
|
|
|
" a = this.#a;"
|
|
|
|
"}"
|
|
|
|
"let str;"
|
|
|
|
"class ClassWithEval {"
|
|
|
|
" field = eval(str);"
|
|
|
|
"}"
|
|
|
|
"class ClassWithPrivateAndEval {"
|
|
|
|
" #field = 42;"
|
|
|
|
" field = eval(str);"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectInt32("(new A()).a", 42);
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun("str = 'this.#nonexistent'; (new ClassWithEval()).field");
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
ExpectInt32("str = 'this.#field'; (new ClassWithPrivateAndEval()).field",
|
|
|
|
42);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsReferenceClassVariable) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class PrivateFieldClass {"
|
|
|
|
" #consturctor = PrivateFieldClass;"
|
|
|
|
" func() {"
|
|
|
|
" return this.#consturctor;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class PublicFieldClass {"
|
|
|
|
" ctor = PublicFieldClass;"
|
|
|
|
" func() {"
|
|
|
|
" return this.ctor;"
|
|
|
|
" }"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectTrue("new PrivateFieldClass().func() === PrivateFieldClass");
|
|
|
|
ExpectTrue("new PublicFieldClass().func() === PublicFieldClass");
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsNested) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class Outer {"
|
|
|
|
" #odata = 42;"
|
|
|
|
" #inner;"
|
|
|
|
" static getInner() {"
|
|
|
|
" class Inner {"
|
|
|
|
" #idata = 42;"
|
|
|
|
" #outer;"
|
|
|
|
" constructor(outer) {"
|
|
|
|
" this.#outer = outer;"
|
|
|
|
" outer.#inner = this;"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#idata === this.#outer.#odata &&"
|
|
|
|
" this === this.#outer.#inner;"
|
|
|
|
" }"
|
|
|
|
" }"
|
|
|
|
" return Inner;"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#inner.check();"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"const Inner = Outer.getInner();");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectTrue("(new Inner(new Outer)).check()");
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassPrivateMethods) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class JustPrivateMethods {"
|
|
|
|
" #method() { return this.val; }"
|
|
|
|
" get #accessor() { return this.val; };"
|
|
|
|
" set #accessor(val) { this.val = val; }"
|
|
|
|
" method() { return this.#method(); } "
|
|
|
|
" getter() { return this.#accessor; } "
|
|
|
|
" setter(val) { this.#accessor = val } "
|
|
|
|
"}"
|
|
|
|
"class PrivateMethodsAndFields {"
|
|
|
|
" #val = 1;"
|
|
|
|
" #method() { return this.#val; }"
|
|
|
|
" get #accessor() { return this.#val; };"
|
|
|
|
" set #accessor(val) { this.#val = val; }"
|
|
|
|
" method() { return this.#method(); } "
|
|
|
|
" getter() { return this.#accessor; } "
|
|
|
|
" setter(val) { this.#accessor = val } "
|
|
|
|
"}"
|
|
|
|
"class Nested {"
|
|
|
|
" #val = 42;"
|
|
|
|
" static #method(obj) { return obj.#val; }"
|
|
|
|
" getInner() {"
|
|
|
|
" class Inner {"
|
|
|
|
" runEval(obj, str) {"
|
|
|
|
" return eval(str);"
|
|
|
|
" }"
|
|
|
|
" }"
|
|
|
|
" return Inner;"
|
|
|
|
" }"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun("const a = new JustPrivateMethods(); a.setter(42);");
|
|
|
|
ExpectInt32("a.method()", 42);
|
|
|
|
ExpectInt32("a.getter()", 42);
|
|
|
|
CompileRun("const b = new PrivateMethodsAndFields(); b.setter(42);");
|
|
|
|
ExpectInt32("b.method()", 42);
|
|
|
|
ExpectInt32("b.getter()", 42);
|
|
|
|
CompileRun("const c = new (new Nested().getInner());");
|
|
|
|
ExpectInt32("c.runEval(new Nested(), 'Nested.#method(obj)')", 42);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsWithInheritance) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"class Base {"
|
|
|
|
" #a = 'test';"
|
|
|
|
" getA() { return this.#a; }"
|
|
|
|
"}"
|
|
|
|
"class Derived extends Base {"
|
|
|
|
" #b = 1;"
|
|
|
|
" constructor() {"
|
|
|
|
" super();"
|
|
|
|
" this.#b = this.getA();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#b === this.getA();"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class DerivedDefaultConstructor extends Base {"
|
|
|
|
" #b = 1;"
|
|
|
|
" check() {"
|
|
|
|
" return this.#b === 1;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class NestedSuper extends Base {"
|
|
|
|
" #b = 1;"
|
|
|
|
" constructor() {"
|
|
|
|
" const t = () => {"
|
|
|
|
" super();"
|
|
|
|
" };"
|
|
|
|
" t();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#b === 1;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class EvaledSuper extends Base {"
|
|
|
|
" #b = 1;"
|
|
|
|
" constructor() {"
|
|
|
|
" eval('super()');"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#b === 1;"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"class NestedEvaledSuper extends Base {"
|
|
|
|
" #b = 1;"
|
|
|
|
" constructor() {"
|
|
|
|
" const t = () => {"
|
|
|
|
" eval('super()');"
|
|
|
|
" };"
|
|
|
|
" t();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#b === 1;"
|
|
|
|
" }"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectBoolean("(new Derived()).check()", true);
|
|
|
|
ExpectBoolean("(new DerivedDefaultConstructor()).check()", true);
|
|
|
|
ExpectBoolean("(new NestedSuper()).check()", true);
|
|
|
|
ExpectBoolean("(new EvaledSuper()).check()", true);
|
|
|
|
ExpectBoolean("(new NestedEvaledSuper()).check()", true);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsRecalcPrivateNames) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"let heritageFn;"
|
|
|
|
"class Outer {"
|
|
|
|
" #f = 'Outer.#f';"
|
|
|
|
" static Inner = class Inner extends (heritageFn = function () {"
|
|
|
|
" return class Nested {"
|
|
|
|
" exfil(obj) { return obj.#f; }"
|
|
|
|
" exfilEval(obj) { return eval('obj.#f'); }"
|
|
|
|
" };"
|
|
|
|
" }) {"
|
|
|
|
" #f = 'Inner.#f';"
|
|
|
|
" };"
|
|
|
|
"};");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"const o = new Outer;"
|
|
|
|
"const c = new Outer.Inner;"
|
|
|
|
"const D = heritageFn();"
|
|
|
|
"const d = new D;"
|
|
|
|
"let error1;"
|
|
|
|
"let error2;");
|
|
|
|
ExpectString("d.exfil(o)", "Outer.#f");
|
|
|
|
ExpectString("d.exfilEval(o)", "Outer.#f");
|
|
|
|
CompileRun("try { d.exfil(c) } catch(e) { error1 = e; }");
|
|
|
|
ExpectBoolean("error1 instanceof TypeError", true);
|
|
|
|
CompileRun("try { d.exfilEval(c) } catch(e) { error2 = e; }");
|
|
|
|
ExpectBoolean("error2 instanceof TypeError", true);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(ClassFieldsWithBindings) {
|
|
|
|
DisableAlwaysOpt();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.rehash_snapshot = true;
|
|
|
|
i::v8_flags.hash_seed = 42;
|
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
Reland "[class] implement reparsing of class instance member initializers"
This is a reland of 91f08378bc7fe486f92dc81e9a8f9ec78c1d3c21
When the class scope does not need a context, the deserialized
outer scope of the initializer scope would not be the class scope,
and we should not and do not need to use it to fix up the allocation
information of the context-allocated variables. The original patch
did not consider this case and resulted in a regression when we
tried to reparse the initializer function to look for destructuring
assignment errors. This fixes the regression by not deserializing
the class scope that's going to be reparsed, and using the positions
of the scopes to tell whether the scope info matches the reparsed
scope and can be used to fix up the allocation info.
Original change's description:
> [class] implement reparsing of class instance member initializers
>
> Previously, since the source code for the synthetic class instance
> member initializer function was recorded as the span from the first
> initializer to the last initializer, there was no way to reparse the
> class and recompile the initializer function. It was working for
> most use cases because the code for the initializer function was
> generated eagarly and it was usually alive as long as the class was
> alive, so the initializer wouldn't normally be lazily parsed. This
> didn't work, however, when the class was snapshotted with
> v8::SnapshotCreator::FunctionCodeHandling::kClear,
> becuase then we needed to recompile the initializer when the class
> was instantiated. This patch implements the reparsing so that
> these classes can work with FunctionCodeHandling::kClear.
>
> This patch refactors ParserBase::ParseClassLiteral() so that we can
> reuse it for both parsing the class body normally and reparsing it
> to collect initializers. When reparsing the synthetic initializer
> function, we rewind the scanner to the beginning of the class, and
> parse the class body to collect the initializers. During the
> reparsing, field initializers are parsed with the full parser while
> methods of the class are pre-parsed.
>
> A few notable changes:
>
> - Extended the source range of the initializer function to cover the
> entire class so that we can rewind the scanner to parse the class
> body to collect initializers (previously, it starts from the first
> field initializer and ends at the last initializer). This resulted
> some expectation changes in the debugger tests, though the
> initializers remain debuggable.
> - A temporary ClassScope is created during reparsing. After the class
> is reparsed, we use the information from the ScopeInfo to update
> the allocated indices of the variables in the ClassScope.
>
> Bug: v8:10704
> Change-Id: Ifb6431a1447d8844f2a548283d59158742fe9027
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2988830
> Reviewed-by: Leszek Swirski <leszeks@chromium.org>
> Reviewed-by: Toon Verwaest <verwaest@chromium.org>
> Commit-Queue: Joyee Cheung <joyee@igalia.com>
> Cr-Commit-Position: refs/heads/main@{#78299}
Bug: chromium:1278086, chromium:1278085, v8:10704
Change-Id: Iea4f1f6dc398846cbe322adc16f6fffd6d2dfdf3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3325912
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Commit-Queue: Joyee Cheung <joyee@igalia.com>
Cr-Commit-Position: refs/heads/main@{#78745}
2022-01-24 15:35:34 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"function testVarBinding() {"
|
|
|
|
" function FuncWithVar() {"
|
|
|
|
" this.getPrivate = () => 'test';"
|
|
|
|
" }"
|
|
|
|
" class Derived extends FuncWithVar {"
|
|
|
|
" ['computed'] = FuncWithVar;"
|
|
|
|
" #private = FuncWithVar;"
|
|
|
|
" public = FuncWithVar;"
|
|
|
|
" constructor() {"
|
|
|
|
" super();"
|
|
|
|
" this.#private = this.getPrivate();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#private === this.getPrivate() &&"
|
|
|
|
" this.computed === FuncWithVar &&"
|
|
|
|
" this.public === FuncWithVar;"
|
|
|
|
" }"
|
|
|
|
" }"
|
|
|
|
""
|
|
|
|
" return((new Derived()).check());"
|
|
|
|
"}"
|
|
|
|
"class ClassWithLet {"
|
|
|
|
" #private = 'test';"
|
|
|
|
" getPrivate() { return this.#private; }"
|
|
|
|
"}"
|
|
|
|
"function testLetBinding() {"
|
|
|
|
" class Derived extends ClassWithLet {"
|
|
|
|
" ['computed'] = ClassWithLet;"
|
|
|
|
" #private = ClassWithLet;"
|
|
|
|
" public = ClassWithLet;"
|
|
|
|
" constructor() {"
|
|
|
|
" super();"
|
|
|
|
" this.#private = this.getPrivate();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#private === this.getPrivate() &&"
|
|
|
|
" this.computed === ClassWithLet &&"
|
|
|
|
" this.public === ClassWithLet;"
|
|
|
|
" }"
|
|
|
|
" }"
|
|
|
|
""
|
|
|
|
" return((new Derived()).check());"
|
|
|
|
"}"
|
|
|
|
"const ClassWithConst = class {"
|
|
|
|
" #private = 'test';"
|
|
|
|
" getPrivate() { return this.#private; }"
|
|
|
|
"};"
|
|
|
|
"function testConstBinding() {"
|
|
|
|
" class Derived extends ClassWithConst {"
|
|
|
|
" ['computed'] = ClassWithConst;"
|
|
|
|
" #private = ClassWithConst;"
|
|
|
|
" public = ClassWithConst;"
|
|
|
|
" constructor() {"
|
|
|
|
" super();"
|
|
|
|
" this.#private = this.getPrivate();"
|
|
|
|
" }"
|
|
|
|
" check() {"
|
|
|
|
" return this.#private === this.getPrivate() &&"
|
|
|
|
" this.computed === ClassWithConst &&"
|
|
|
|
" this.public === ClassWithConst;"
|
|
|
|
" }"
|
|
|
|
" }"
|
|
|
|
""
|
|
|
|
" return((new Derived()).check());"
|
|
|
|
"}");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
v8::Isolate* isolate = v8::Isolate::New(create_params);
|
|
|
|
{
|
|
|
|
v8::Isolate::Scope isolate_scope(isolate);
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
CHECK(!context.IsEmpty());
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
ExpectBoolean("testVarBinding()", true);
|
|
|
|
ExpectBoolean("testLetBinding()", true);
|
|
|
|
ExpectBoolean("testConstBinding()", true);
|
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2018-11-28 23:53:16 +00:00
|
|
|
void CheckSFIsAreWeak(WeakFixedArray sfis, Isolate* isolate) {
|
2018-03-19 10:38:06 +00:00
|
|
|
CHECK_GT(sfis.length(), 0);
|
|
|
|
int no_of_weak = 0;
|
|
|
|
for (int i = 0; i < sfis.length(); ++i) {
|
2018-10-26 00:23:24 +00:00
|
|
|
MaybeObject maybe_object = sfis.Get(i);
|
2018-12-20 15:47:47 +00:00
|
|
|
HeapObject heap_object;
|
Reland [in-place weak refs] Fix MaybeObject function names
E.g., "ToWeakHeapObject" was misleading, since it didn't convert to a weak heap
object, instead returned a weakly pointed heap object. Change the function names
(in this case, to "GetHeapObjectIfWeak") to reflect this.
Also make casts explicit, if a MaybeObject is an Object, we can call cast<Object>().
Previous version: https://chromium-review.googlesource.com/1219025
BUG=v8:7308
TBR=ishell@chromium.org, ulan@chromium.org, ahaas@chromium.org, yangguo@chromium.org, tebbi@chromium.org
Change-Id: I503d4a2a3a68f85e9e02e1c2f9fc1c4187c8e9a1
Reviewed-on: https://chromium-review.googlesource.com/1226800
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55934}
2018-09-17 07:36:25 +00:00
|
|
|
CHECK(maybe_object->IsWeakOrCleared() ||
|
|
|
|
(maybe_object->GetHeapObjectIfStrong(&heap_object) &&
|
2018-03-19 10:38:06 +00:00
|
|
|
heap_object.IsUndefined(isolate)));
|
Reland [in-place weak refs] Fix MaybeObject function names
E.g., "ToWeakHeapObject" was misleading, since it didn't convert to a weak heap
object, instead returned a weakly pointed heap object. Change the function names
(in this case, to "GetHeapObjectIfWeak") to reflect this.
Also make casts explicit, if a MaybeObject is an Object, we can call cast<Object>().
Previous version: https://chromium-review.googlesource.com/1219025
BUG=v8:7308
TBR=ishell@chromium.org, ulan@chromium.org, ahaas@chromium.org, yangguo@chromium.org, tebbi@chromium.org
Change-Id: I503d4a2a3a68f85e9e02e1c2f9fc1c4187c8e9a1
Reviewed-on: https://chromium-review.googlesource.com/1226800
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#55934}
2018-09-17 07:36:25 +00:00
|
|
|
if (maybe_object->IsWeak()) {
|
2018-03-19 10:38:06 +00:00
|
|
|
++no_of_weak;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
CHECK_GT(no_of_weak, 0);
|
|
|
|
}
|
|
|
|
|
2019-01-31 13:48:53 +00:00
|
|
|
UNINITIALIZED_TEST(WeakArraySerializationInSnapshot) {
|
2018-03-19 10:38:06 +00:00
|
|
|
const char* code = "var my_func = function() { }";
|
|
|
|
|
|
|
|
DisableAlwaysOpt();
|
2019-01-31 13:48:53 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2022-09-15 16:54:55 +00:00
|
|
|
i::v8_flags.allow_natives_syntax = true;
|
2018-03-19 10:38:06 +00:00
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
|
|
|
|
CompileRun(code);
|
|
|
|
creator.SetDefaultContext(
|
|
|
|
context, v8::SerializeInternalFieldsCallback(
|
|
|
|
SerializeInternalFields, reinterpret_cast<void*>(2016)));
|
|
|
|
}
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
}
|
|
|
|
|
|
|
|
v8::Isolate::CreateParams create_params;
|
|
|
|
create_params.snapshot_blob = &blob;
|
|
|
|
create_params.array_buffer_allocator = CcTest::array_buffer_allocator();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* isolate = TestSerializer::NewIsolate(create_params);
|
2018-03-19 10:38:06 +00:00
|
|
|
{
|
|
|
|
v8::Isolate::Scope i_scope(isolate);
|
|
|
|
v8::HandleScope h_scope(isolate);
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(
|
|
|
|
isolate, nullptr, v8::MaybeLocal<v8::ObjectTemplate>(),
|
|
|
|
v8::MaybeLocal<v8::Value>(),
|
|
|
|
v8::DeserializeInternalFieldsCallback(DeserializeInternalFields,
|
|
|
|
reinterpret_cast<void*>(2017)));
|
|
|
|
v8::Context::Scope c_scope(context);
|
|
|
|
|
|
|
|
v8::Local<v8::Value> x = CompileRun("my_func");
|
|
|
|
CHECK(x->IsFunction());
|
|
|
|
Handle<JSFunction> function =
|
|
|
|
Handle<JSFunction>::cast(v8::Utils::OpenHandle(*x));
|
|
|
|
|
|
|
|
// Verify that the pointers in shared_function_infos are weak.
|
2018-11-28 23:53:16 +00:00
|
|
|
WeakFixedArray sfis =
|
2018-03-19 10:38:06 +00:00
|
|
|
Script::cast(function->shared().script()).shared_function_infos();
|
2019-01-31 13:48:53 +00:00
|
|
|
CheckSFIsAreWeak(sfis, reinterpret_cast<i::Isolate*>(isolate));
|
2018-03-19 10:38:06 +00:00
|
|
|
}
|
|
|
|
isolate->Dispose();
|
|
|
|
delete[] blob.data;
|
2019-01-31 13:48:53 +00:00
|
|
|
FreeCurrentEmbeddedBlob();
|
2018-03-19 10:38:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(WeakArraySerializationInCodeCache) {
|
|
|
|
LocalContext context;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()->DisableScriptAndEval();
|
2018-03-19 10:38:06 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
const char* source = "function foo() { }";
|
|
|
|
|
|
|
|
Handle<String> src = isolate->factory()
|
2021-06-17 15:43:55 +00:00
|
|
|
->NewStringFromUtf8(base::CStrVector(source))
|
2018-03-19 10:38:06 +00:00
|
|
|
.ToHandleChecked();
|
[api] Add API for off-thread code cache deserialization
To consume a code cache off-thread
1. The embedder creates a CachedData object wrapping the data blob.
2. The embedder calls ScriptCompiler::StartConsumingCodeCache with the
CachedData, and receives a ScriptCompiler::CodeCacheConsumeTask
which takes ownership of the CachedData.
3. The embedder calls ScriptCompiler::CodeCacheConsumeTask::Run
on a different thread.
4. Once this completes, the embedded passes the completed task as an
optional argument into Source constructor, and calls Compile as
before.
This is roughly similar to how streaming compilation works, with the
QoL improvement that Source owns the CodeCacheConsumeTask and therefore
we can reuse the same Compile method and do the off-thread finalization
behind the scenes inside Compile.
On the v8::internal side, ScriptCompiler::CodeCacheConsumeTask wraps a
v8::internal::BackgroundDeserializeTask, which has a Run and a Finish
method. The Run creates a LocalIsolate (again, similar to
BackgroundCompileTask), calls some helpers on CodeSerializer, and stores
the pre-finalization result in a OffThreadDeserializeData structure.
This stores Persistent Handles to the off-thread initialized SFI and
a vector of Scripts needing fixing up, and it owns the PersistentHandles
object which owns those Handles. Finally, the Finish method consumes
this OffThreadDeserializeData structure, fixes up Scripts, moves the
SFI Handle into the caller HandleScope, and that's it.
Since we don't yet have the source at off-thread deserialization time,
the various code cache sanity checks are done without the source hash
when deserializing, and the Finish method re-does them now that the
source is available.
Bug: chromium:1075999
Change-Id: If1faf35ba3ef840fa4e735581d0b29c96c1d5fc8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3067322
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Omer Katz <omerkatz@chromium.org>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Cr-Commit-Position: refs/heads/master@{#76155}
2021-08-06 13:53:48 +00:00
|
|
|
AlignedCachedData* cache = nullptr;
|
2018-03-19 10:38:06 +00:00
|
|
|
|
2021-08-24 11:45:56 +00:00
|
|
|
ScriptDetails script_details(src);
|
2021-08-09 13:21:43 +00:00
|
|
|
CompileScriptAndProduceCache(isolate, src, script_details, &cache,
|
2018-03-19 10:38:06 +00:00
|
|
|
v8::ScriptCompiler::kNoCompileOptions);
|
|
|
|
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> copy =
|
|
|
|
CompileScript(isolate, src, script_details, cache,
|
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
2018-03-19 10:38:06 +00:00
|
|
|
|
|
|
|
// Verify that the pointers in shared_function_infos are weak.
|
2018-11-28 23:53:16 +00:00
|
|
|
WeakFixedArray sfis = Script::cast(copy->script()).shared_function_infos();
|
2018-03-19 10:38:06 +00:00
|
|
|
CheckSFIsAreWeak(sfis, isolate);
|
|
|
|
|
|
|
|
delete cache;
|
|
|
|
}
|
|
|
|
|
2021-10-26 13:36:11 +00:00
|
|
|
TEST(CachedCompileFunction) {
|
2018-03-27 10:44:13 +00:00
|
|
|
DisableAlwaysOpt();
|
|
|
|
LocalContext env;
|
|
|
|
Isolate* isolate = CcTest::i_isolate();
|
2019-09-18 18:00:44 +00:00
|
|
|
isolate->compilation_cache()
|
|
|
|
->DisableScriptAndEval(); // Disable same-isolate code cache.
|
2018-03-27 10:44:13 +00:00
|
|
|
|
|
|
|
v8::HandleScope scope(CcTest::isolate());
|
|
|
|
|
|
|
|
v8::Local<v8::String> source = v8_str("return x*x;");
|
|
|
|
v8::Local<v8::String> arg_str = v8_str("x");
|
|
|
|
ScriptCompiler::CachedData* cache;
|
|
|
|
{
|
|
|
|
v8::ScriptCompiler::Source script_source(source);
|
|
|
|
v8::Local<v8::Function> fun =
|
2021-10-26 13:36:11 +00:00
|
|
|
v8::ScriptCompiler::CompileFunction(env.local(), &script_source, 1,
|
|
|
|
&arg_str, 0, nullptr,
|
|
|
|
v8::ScriptCompiler::kEagerCompile)
|
2018-03-27 10:44:13 +00:00
|
|
|
.ToLocalChecked();
|
2018-04-16 07:56:17 +00:00
|
|
|
cache = v8::ScriptCompiler::CreateCodeCacheForFunction(fun);
|
2018-03-27 10:44:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
|
|
|
v8::ScriptCompiler::Source script_source(source, cache);
|
|
|
|
v8::Local<v8::Function> fun =
|
2021-10-26 13:36:11 +00:00
|
|
|
v8::ScriptCompiler::CompileFunction(
|
2018-03-27 10:44:13 +00:00
|
|
|
env.local(), &script_source, 1, &arg_str, 0, nullptr,
|
|
|
|
v8::ScriptCompiler::kConsumeCodeCache)
|
|
|
|
.ToLocalChecked();
|
|
|
|
v8::Local<v8::Value> arg = v8_num(3);
|
|
|
|
v8::Local<v8::Value> result =
|
|
|
|
fun->Call(env.local(), v8::Undefined(CcTest::isolate()), 1, &arg)
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK_EQ(9, result->Int32Value(env.local()).FromJust());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-05 19:25:07 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorAnonClassWithKeep) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"function Foo() { return class {}; } \n"
|
|
|
|
"class Bar extends Foo() {}\n"
|
|
|
|
"Foo()\n");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v8::StartupData blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
2022-08-08 09:49:14 +00:00
|
|
|
UNINITIALIZED_TEST(SnapshotCreatorDontDeferByteArrayForTypedArray) {
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
v8::StartupData blob;
|
|
|
|
{
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
CompileRun(
|
|
|
|
"const z = new Uint8Array(1);\n"
|
|
|
|
"class A { \n"
|
|
|
|
" static x() { \n"
|
|
|
|
" } \n"
|
|
|
|
"} \n"
|
|
|
|
"class B extends A {} \n"
|
|
|
|
"B.foo = ''; \n"
|
|
|
|
"class C extends B {} \n"
|
|
|
|
"class D extends C {} \n"
|
|
|
|
"class E extends B {} \n"
|
|
|
|
"function F() {} \n"
|
|
|
|
"Object.setPrototypeOf(F, D); \n");
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
|
|
|
|
blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kClear);
|
|
|
|
CHECK(blob.raw_size > 0 && blob.data != nullptr);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
SnapshotCreator creator(nullptr, &blob);
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
v8::HandleScope scope(isolate);
|
|
|
|
USE(v8::Context::New(isolate));
|
|
|
|
}
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
2020-11-26 10:08:27 +00:00
|
|
|
class V8_NODISCARD DisableLazySourcePositionScope {
|
2019-11-19 07:00:02 +00:00
|
|
|
public:
|
|
|
|
DisableLazySourcePositionScope()
|
2022-09-15 16:54:55 +00:00
|
|
|
: backup_value_(v8_flags.enable_lazy_source_positions) {
|
|
|
|
v8_flags.enable_lazy_source_positions = false;
|
2019-11-19 07:00:02 +00:00
|
|
|
}
|
|
|
|
~DisableLazySourcePositionScope() {
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.enable_lazy_source_positions = backup_value_;
|
2019-11-19 07:00:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool backup_value_;
|
|
|
|
};
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(NoStackFrameCacheSerialization) {
|
|
|
|
// Checks that exceptions caught are not cached in the
|
|
|
|
// stack frame cache during serialization. The individual frames
|
|
|
|
// can point to JSFunction objects, which need to be stored in a
|
|
|
|
// context snapshot, *not* isolate snapshot.
|
|
|
|
DisableAlwaysOpt();
|
|
|
|
DisableLazySourcePositionScope lazy_scope;
|
|
|
|
|
|
|
|
v8::SnapshotCreator creator;
|
|
|
|
v8::Isolate* isolate = creator.GetIsolate();
|
|
|
|
isolate->SetCaptureStackTraceForUncaughtExceptions(true);
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
{
|
|
|
|
v8::Local<v8::Context> context = v8::Context::New(isolate);
|
|
|
|
v8::Context::Scope context_scope(context);
|
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CompileRun(R"(
|
|
|
|
function foo() { throw new Error('bar'); }
|
|
|
|
function bar() {
|
|
|
|
foo();
|
|
|
|
}
|
|
|
|
bar();
|
|
|
|
)");
|
|
|
|
|
|
|
|
creator.SetDefaultContext(context);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v8::StartupData blob =
|
|
|
|
creator.CreateBlob(v8::SnapshotCreator::FunctionCodeHandling::kKeep);
|
|
|
|
|
|
|
|
delete[] blob.data;
|
|
|
|
}
|
|
|
|
|
2021-10-08 22:52:07 +00:00
|
|
|
namespace {
|
|
|
|
void CheckObjectsAreInSharedHeap(Isolate* isolate) {
|
2021-10-13 23:06:24 +00:00
|
|
|
Heap* heap = isolate->heap();
|
|
|
|
HeapObjectIterator iterator(heap);
|
2021-10-08 22:52:07 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
|
|
|
for (HeapObject obj = iterator.Next(); !obj.is_null();
|
|
|
|
obj = iterator.Next()) {
|
2022-06-07 10:25:51 +00:00
|
|
|
const bool expected_in_shared_old =
|
|
|
|
heap->MustBeInSharedOldSpace(obj) ||
|
|
|
|
(obj.IsString() && String::IsInPlaceInternalizable(String::cast(obj)));
|
|
|
|
if (expected_in_shared_old) {
|
2021-10-08 22:52:07 +00:00
|
|
|
CHECK(obj.InSharedHeap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
UNINITIALIZED_TEST(SharedStrings) {
|
|
|
|
// Test that deserializing with --shared-string-table deserializes into the
|
|
|
|
// shared Isolate.
|
|
|
|
|
2022-08-26 22:07:04 +00:00
|
|
|
if (!V8_CAN_CREATE_SHARED_HEAP_BOOL) return;
|
|
|
|
|
|
|
|
// Make all the flags that require a shared heap false before creating the
|
|
|
|
// isolate to serialize.
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.shared_string_table = false;
|
|
|
|
v8_flags.harmony_struct = false;
|
2021-10-08 22:52:07 +00:00
|
|
|
|
|
|
|
v8::Isolate* isolate_to_serialize = TestSerializer::NewIsolateInitialized();
|
|
|
|
StartupBlobs blobs = Serialize(isolate_to_serialize);
|
|
|
|
isolate_to_serialize->Dispose();
|
|
|
|
|
2022-09-15 16:54:55 +00:00
|
|
|
v8_flags.shared_string_table = true;
|
2021-10-08 22:52:07 +00:00
|
|
|
|
2022-10-05 07:39:35 +00:00
|
|
|
if (!v8_flags.shared_space) {
|
|
|
|
TestSerializer::InitializeProcessWideSharedIsolateFromBlob(blobs);
|
|
|
|
}
|
|
|
|
|
2022-08-26 22:07:04 +00:00
|
|
|
v8::Isolate* isolate1 = TestSerializer::NewIsolateFromBlob(blobs);
|
|
|
|
v8::Isolate* isolate2 = TestSerializer::NewIsolateFromBlob(blobs);
|
2021-10-08 22:52:07 +00:00
|
|
|
Isolate* i_isolate1 = reinterpret_cast<Isolate*>(isolate1);
|
|
|
|
Isolate* i_isolate2 = reinterpret_cast<Isolate*>(isolate2);
|
|
|
|
|
|
|
|
CHECK_EQ(i_isolate1->string_table(), i_isolate2->string_table());
|
2022-10-25 16:41:25 +00:00
|
|
|
{
|
|
|
|
ParkedScope parked(i_isolate2->main_thread_local_heap());
|
|
|
|
CheckObjectsAreInSharedHeap(i_isolate1);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
ParkedScope parked(i_isolate1->main_thread_local_heap());
|
|
|
|
CheckObjectsAreInSharedHeap(i_isolate2);
|
|
|
|
}
|
2021-10-08 22:52:07 +00:00
|
|
|
|
2022-06-22 18:24:28 +00:00
|
|
|
{
|
|
|
|
// Because both isolate1 and isolate2 are considered running on the main
|
|
|
|
// thread, one must be parked to avoid deadlock in the shared heap
|
|
|
|
// verification that may happen on client heap disposal.
|
2022-10-05 07:39:35 +00:00
|
|
|
ParkedScope parked(i_isolate1->main_thread_local_isolate());
|
|
|
|
isolate2->Dispose();
|
|
|
|
}
|
|
|
|
isolate1->Dispose();
|
|
|
|
|
|
|
|
if (!v8_flags.shared_space) {
|
|
|
|
TestSerializer::DeleteProcessWideSharedIsolate();
|
2022-06-22 18:24:28 +00:00
|
|
|
}
|
2021-10-08 22:52:07 +00:00
|
|
|
|
|
|
|
blobs.Dispose();
|
|
|
|
FreeCurrentEmbeddedBlob();
|
|
|
|
}
|
|
|
|
|
2017-08-31 12:34:55 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|