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
|
|
|
|
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/assembler-inl.h"
|
|
|
|
#include "src/codegen/compilation-cache.h"
|
|
|
|
#include "src/codegen/compiler.h"
|
|
|
|
#include "src/codegen/macro-assembler-inl.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"
|
2015-07-31 11:07:50 +00:00
|
|
|
#include "src/debug/debug.h"
|
2019-02-14 21:10:30 +00:00
|
|
|
#include "src/heap/heap-inl.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-16 10:26:57 +00:00
|
|
|
#include "src/init/bootstrapper.h"
|
2020-09-16 08:16:01 +00:00
|
|
|
#include "src/init/v8.h"
|
2018-10-31 09:09:54 +00:00
|
|
|
#include "src/interpreter/interpreter.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-07-25 14:11:56 +00:00
|
|
|
#include "src/objects/js-array-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"
|
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
|
|
|
|
// with the flag --always-opt.
|
|
|
|
FLAG_always_opt = 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;
|
2015-03-05 14:46:39 +00:00
|
|
|
|
2018-10-26 14:33:30 +00:00
|
|
|
void Dispose() {
|
|
|
|
startup.Dispose();
|
|
|
|
read_only.Dispose();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// 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;
|
2018-11-15 12:49:20 +00:00
|
|
|
DisableEmbeddedBlobRefcounting();
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
|
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);
|
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
|
|
|
isolate->Init(nullptr, nullptr, false);
|
2015-02-25 11:14:40 +00:00
|
|
|
return v8_isolate;
|
|
|
|
}
|
2018-10-26 14:33:30 +00:00
|
|
|
|
2019-09-10 01:19:59 +00:00
|
|
|
static v8::Isolate* NewIsolateFromBlob(const StartupBlobs& blobs) {
|
2018-10-26 14:33:30 +00:00
|
|
|
SnapshotData startup_snapshot(blobs.startup);
|
|
|
|
SnapshotData read_only_snapshot(blobs.read_only);
|
|
|
|
const bool kEnableSerializer = false;
|
|
|
|
const bool kGenerateHeap = false;
|
|
|
|
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
|
|
|
|
v8::Isolate::Scope isolate_scope(v8_isolate);
|
|
|
|
i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate);
|
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
|
|
|
isolate->Init(&startup_snapshot, &read_only_snapshot, false);
|
2018-10-26 14:33:30 +00:00
|
|
|
return v8_isolate;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2018-10-26 14:33:30 +00:00
|
|
|
v8::Isolate* v8_isolate = NewIsolate(kEnableSerializer, kGenerateHeap);
|
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
|
|
|
|
|
|
|
private:
|
|
|
|
// Creates an Isolate instance configured for testing.
|
|
|
|
static v8::Isolate* NewIsolate(bool with_serializer, bool generate_heap) {
|
|
|
|
i::Isolate* 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);
|
[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
|
|
|
|
2018-10-26 14:33:30 +00:00
|
|
|
return v8_isolate;
|
2015-04-29 09:54:34 +00:00
|
|
|
}
|
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
|
|
|
|
2014-09-19 08:01:35 +00:00
|
|
|
Isolate* internal_isolate = reinterpret_cast<Isolate*>(isolate);
|
2016-09-07 10:02:58 +00:00
|
|
|
internal_isolate->heap()->CollectAllAvailableGarbage(
|
|
|
|
i::GarbageCollectionReason::kTesting);
|
2018-10-16 08:57:54 +00:00
|
|
|
|
2020-09-16 08:16:01 +00:00
|
|
|
SafepointScope safepoint(internal_isolate->heap());
|
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(internal_isolate);
|
2020-09-16 08:16:01 +00:00
|
|
|
|
2020-09-17 15:19:09 +00:00
|
|
|
DisallowGarbageCollection no_gc;
|
2020-04-28 09:53:18 +00:00
|
|
|
ReadOnlySerializer read_only_serializer(internal_isolate,
|
|
|
|
Snapshot::kDefaultSerializerFlags);
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.SerializeReadOnlyRoots();
|
|
|
|
|
2020-04-28 09:53:18 +00:00
|
|
|
StartupSerializer ser(internal_isolate, Snapshot::kDefaultSerializerFlags,
|
|
|
|
&read_only_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();
|
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);
|
2017-08-28 15:49:54 +00:00
|
|
|
return {WritePayload(startup_snapshot.RawData()),
|
2018-10-16 08:57:54 +00:00
|
|
|
WritePayload(read_only_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
|
2014-09-19 08:01:35 +00:00
|
|
|
isolate->heap()->Verify();
|
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,
|
|
|
|
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.
|
2017-02-06 10:18:05 +00:00
|
|
|
heap->CollectAllAvailableGarbage(i::GarbageCollectionReason::kTesting);
|
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
|
|
|
|
2020-09-16 08:16:01 +00:00
|
|
|
SafepointScope safepoint(heap);
|
|
|
|
|
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();
|
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SnapshotByteSink startup_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
StartupSerializer startup_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_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
|
|
|
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.FinalizeSerialization();
|
|
|
|
|
|
|
|
SnapshotData read_only_snapshot(&read_only_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());
|
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
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
base::Vector<const byte> context_blob;
|
2020-04-22 09:40:35 +00:00
|
|
|
SerializeContext(&startup_blob, &read_only_blob, &context_blob);
|
|
|
|
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();
|
2020-04-22 09:40:35 +00:00
|
|
|
context_blob.Dispose();
|
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;
|
|
|
|
base::Vector<const byte> context_blob;
|
2020-04-22 09:40:35 +00:00
|
|
|
SerializeContext(&startup_blob, &read_only_blob, &context_blob);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
2018-10-31 09:09:54 +00:00
|
|
|
StartupBlobs blobs = {startup_blob, read_only_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-06-17 15:43:55 +00:00
|
|
|
static void SerializeCustomContext(base::Vector<const byte>* startup_blob_out,
|
|
|
|
base::Vector<const byte>* read_only_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.
|
2016-09-07 10:02:58 +00:00
|
|
|
isolate->heap()->CollectAllAvailableGarbage(
|
|
|
|
i::GarbageCollectionReason::kTesting);
|
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
|
|
|
|
2020-09-16 08:16:01 +00:00
|
|
|
SafepointScope safepoint(isolate->heap());
|
|
|
|
|
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();
|
|
|
|
|
2015-02-25 11:14:40 +00:00
|
|
|
SnapshotByteSink startup_sink;
|
2020-04-28 09:53:18 +00:00
|
|
|
StartupSerializer startup_serializer(
|
|
|
|
isolate, Snapshot::kDefaultSerializerFlags, &read_only_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
|
|
|
|
2018-10-16 08:57:54 +00:00
|
|
|
read_only_serializer.FinalizeSerialization();
|
|
|
|
|
|
|
|
SnapshotData read_only_snapshot(&read_only_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());
|
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;
|
|
|
|
base::Vector<const byte> context_blob;
|
2020-04-22 09:40:35 +00:00
|
|
|
SerializeCustomContext(&startup_blob, &read_only_blob, &context_blob);
|
2015-02-25 11:14:40 +00:00
|
|
|
|
2018-10-31 09:09:54 +00:00
|
|
|
StartupBlobs blobs = {startup_blob, read_only_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(),
|
|
|
|
UPDATE_WEAK_WRITE_BARRIER);
|
|
|
|
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);
|
2015-05-12 13:52:26 +00:00
|
|
|
Handle<Object> property = JSReceiver::GetDataProperty(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(
|
|
|
|
// String would be internalized if it came from a literal so create "A"
|
|
|
|
// via a function call.
|
|
|
|
var global = String.fromCharCode(65);
|
|
|
|
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>());
|
|
|
|
CHECK_EQ(std::string(str.ToCString().get()), "A");
|
|
|
|
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>());
|
|
|
|
CHECK_EQ(re->TypeTag(), JSRegExp::ATOM);
|
|
|
|
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();
|
|
|
|
i::FLAG_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();
|
|
|
|
i::FLAG_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();
|
|
|
|
i::FLAG_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.
|
|
|
|
CHECK_NULL(buffer->backing_store());
|
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.
|
|
|
|
CHECK_NULL(buffer->backing_store());
|
|
|
|
|
|
|
|
buffer = GetBufferFromTypedArray(CompileRun("y"));
|
|
|
|
CHECK_NULL(buffer->backing_store());
|
|
|
|
|
|
|
|
buffer = GetBufferFromTypedArray(CompileRun("z"));
|
|
|
|
// The resulting buffer should be off-heap.
|
|
|
|
CHECK_NOT_NULL(buffer->backing_store());
|
|
|
|
}
|
|
|
|
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();
|
|
|
|
i::FLAG_allow_natives_syntax = true;
|
|
|
|
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) {
|
|
|
|
FLAG_enable_lazy_source_positions = true;
|
2019-08-21 09:25:07 +00:00
|
|
|
FLAG_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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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
|
Revert "s390: [arm] Add missing RELATIVE_CODE_TARGET iteration"
This reverts commit 9d3cca1cd3ad7c6653cab1cdf111d356f33f77cd.
Reason for revert: Only the test needs to be skipped on s390. Refer to this: https://crrev.com/c/1981505
Original change's description:
> s390: [arm] Add missing RELATIVE_CODE_TARGET iteration
>
> Port b766299d2c382cc9817e73225bbebe29ce62b9d1
> Port 9592b043eed86db91a441d4bf78b7f0c8c2ce4dd
> Port d915b8d668615a7d6d75cf7a61d3ca5a3d139799
>
> Original Commit Message:
>
> Code object iteration was missing logic for RELATIVE_CODE_TARGET
> reloc entries. Garbage collection could thus miss objects that were
> referenced only as targets of pc-relative calls or jumps.
>
> RELATIVE_CODE_TARGETs are only used on arm, mips, and s390 and only
> at mksnapshot-time.
>
> This exposed another issue in that the interpreter entry trampoline
> copy we generate for profiling *did* contain relative calls in
> runtime-accessible code. This is a problem, since code space on arm is,
> by default, too large to be fully addressable through pc-relative
> calls. This CL thus also disables the related
> FLAG_interpreted_frames_native_stack feature on arm.
>
> objects.
>
> R=jgruber@chromium.org, joransiu@ca.ibm.com, jyan@ca.ibm.com, michael_dawson@ca.ibm.com
> BUG=
> LOG=N
>
> Change-Id: Ifbcaed98d90a2730f0d6a8a7d32c621dab1ff5b2
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2087693
> Reviewed-by: Jakob Gruber <jgruber@chromium.org>
> Reviewed-by: Junliang Yan <jyan@ca.ibm.com>
> Commit-Queue: Milad Farazmand <miladfar@ca.ibm.com>
> Cr-Commit-Position: refs/heads/master@{#66644}
TBR=michael_dawson@ca.ibm.com,mlippautz@chromium.org,jyan@ca.ibm.com,jgruber@chromium.org,joransiu@ca.ibm.com,miladfar@ca.ibm.com
# Not skipping CQ checks because original CL landed > 1 day ago.
Change-Id: Id645a9def23d278235ff77f25249d2187e8105ca
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2196521
Reviewed-by: Milad Farazmand <miladfar@ca.ibm.com>
Reviewed-by: Jakob Gruber <jgruber@chromium.org>
Commit-Queue: Milad Farazmand <miladfar@ca.ibm.com>
Cr-Commit-Position: refs/heads/master@{#67751}
2020-05-12 13:38:05 +00:00
|
|
|
#ifndef V8_TARGET_ARCH_ARM
|
2019-04-22 23:39:50 +00:00
|
|
|
TEST(CodeSerializerOnePlusOneWithInterpretedFramesNativeStack) {
|
|
|
|
FLAG_interpreted_frames_native_stack = true;
|
|
|
|
// We pass false because this test will create IET copies (which are
|
|
|
|
// builtins).
|
|
|
|
TestCodeSerializerOnePlusOneImpl(false);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
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
|
|
|
|
|
|
|
DisallowCompilation no_compile_expected(isolate);
|
2021-08-09 13:21:43 +00:00
|
|
|
Handle<SharedFunctionInfo> copy =
|
|
|
|
CompileScript(isolate, src, default_script_details, cache,
|
|
|
|
v8::ScriptCompiler::kConsumeCodeCache);
|
|
|
|
|
|
|
|
{
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.host_defined_options =
|
|
|
|
default_script_details.host_defined_options;
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*shared.ToHandleChecked(), *copy);
|
|
|
|
}
|
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;
|
2021-08-04 08:54:47 +00:00
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*shared.ToHandleChecked(), *copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// 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;
|
2021-08-04 08:54:47 +00:00
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK_EQ(*shared.ToHandleChecked(), *copy);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different string should fail:
|
|
|
|
ScriptDetails script_details(
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked("other"));
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK(shared.is_null());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different position should fail:
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.line_offset = 0xFF;
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK(shared.is_null());
|
|
|
|
}
|
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;
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK(shared.is_null());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different language mode should fail:
|
|
|
|
ScriptDetails script_details(src);
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kStrict);
|
|
|
|
CHECK(shared.is_null());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// 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);
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
|
|
|
CHECK(shared.is_null());
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Lookup with different host_defined_options should fail:
|
2021-08-18 08:55:21 +00:00
|
|
|
// TODO(10284): Enable host-defined options check again
|
2021-08-04 08:54:47 +00:00
|
|
|
ScriptDetails script_details(src);
|
|
|
|
script_details.host_defined_options = isolate->factory()->NewFixedArray(5);
|
|
|
|
MaybeHandle<SharedFunctionInfo> shared =
|
|
|
|
isolate->compilation_cache()->LookupScript(src, script_details,
|
|
|
|
LanguageMode::kSloppy);
|
2021-08-18 08:55:21 +00:00
|
|
|
CHECK_EQ(*shared.ToHandleChecked(), *copy);
|
2021-08-04 08:54:47 +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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, orig_fun, global, 0, nullptr).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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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.
|
|
|
|
FLAG_always_opt = false;
|
|
|
|
|
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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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) {
|
2021-04-21 00:32:15 +00:00
|
|
|
if (!FLAG_incremental_marking) return;
|
2017-06-13 14:39:46 +00:00
|
|
|
if (FLAG_never_compact) return;
|
2017-08-23 15:15:27 +00:00
|
|
|
ManualGCScope manual_gc_scope;
|
2017-05-10 12:54:01 +00:00
|
|
|
FLAG_always_opt = false;
|
|
|
|
const char* filter_flag = "--turbo-filter=NOTHING";
|
2019-04-23 18:17:01 +00:00
|
|
|
FlagList::SetFlagsFromString(filter_flag, strlen(filter_flag));
|
2017-05-10 12:54:01 +00:00
|
|
|
FLAG_manual_evacuation_candidates_selection = true;
|
|
|
|
|
|
|
|
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();
|
|
|
|
marking->StartBlackAllocationForTesting();
|
|
|
|
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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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(
|
2014-10-27 16:40:04 +00:00
|
|
|
isolate->global_object(), f->NewStringFromAsciiChecked("s"));
|
|
|
|
CHECK(isolate->heap()->InSpace(HeapObject::cast(*property), LO_SPACE));
|
2015-05-12 13:52:26 +00:00
|
|
|
property = JSReceiver::GetDataProperty(isolate->global_object(),
|
|
|
|
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
|
|
|
|
2017-10-13 16:33:03 +00:00
|
|
|
USE(Execution::Call(isolate, copy_fun, global, 0, nullptr));
|
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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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 =
|
2017-10-13 16:33:03 +00:00
|
|
|
Execution::Call(isolate, copy_fun, global, 0, nullptr).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;
|
|
|
|
|
|
|
|
static void SerializerCodeEventListener(const v8::JitCodeEvent* event) {
|
|
|
|
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(
|
2017-12-01 12:50:41 +00:00
|
|
|
const char* 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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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) {
|
2015-02-09 14:49:35 +00:00
|
|
|
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(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,
|
|
|
|
SerializerCodeEventListener);
|
|
|
|
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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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) {
|
|
|
|
const char* source =
|
|
|
|
"function f() {"
|
|
|
|
" return function g() {"
|
|
|
|
" return 'abc';"
|
|
|
|
" }"
|
|
|
|
"}"
|
|
|
|
"f()() + 'def'";
|
2017-12-01 12:50:41 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache =
|
2018-02-20 14:14:54 +00:00
|
|
|
CompileRunAndProduceCache(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,
|
|
|
|
SerializerCodeEventListener);
|
|
|
|
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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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.
|
|
|
|
bool prev_always_opt_value = FLAG_always_opt;
|
|
|
|
FLAG_always_opt = false;
|
|
|
|
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
|
|
|
v8::ScriptCompiler::CachedData* cache =
|
2018-02-20 14:14:54 +00:00
|
|
|
CompileRunAndProduceCache(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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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
|
|
|
BytecodeArray bytecode = sfi->GetBytecodeArray(i_isolate2);
|
2017-12-01 12:50:41 +00:00
|
|
|
CHECK_EQ(bytecode.osr_loop_nesting_level(), 0);
|
|
|
|
|
|
|
|
{
|
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.
|
|
|
|
FLAG_always_opt = prev_always_opt_value;
|
|
|
|
}
|
|
|
|
|
2017-09-26 08:48:52 +00:00
|
|
|
TEST(CodeSerializerFlagChange) {
|
2015-02-06 15:20:40 +00:00
|
|
|
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(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
|
|
|
|
2015-02-09 14:49:35 +00:00
|
|
|
FLAG_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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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) {
|
2015-02-09 14:49:35 +00:00
|
|
|
const char* source = "function f() { return 'abc'; }; f() + 'def'";
|
2018-02-20 14:14:54 +00:00
|
|
|
v8::ScriptCompiler::CachedData* cache = CompileRunAndProduceCache(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);
|
|
|
|
|
|
|
|
v8::Local<v8::String> source_str = v8_str(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) {
|
2016-11-18 12:55:53 +00:00
|
|
|
if (!FLAG_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
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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();
|
2016-11-14 07:17:22 +00:00
|
|
|
v8::Local<v8::External> null_external =
|
|
|
|
v8::External::New(isolate, nullptr);
|
|
|
|
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);
|
|
|
|
|
|
|
|
a->SetInternalField(2, null_external);
|
|
|
|
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());
|
|
|
|
CHECK_NULL(v8::Local<v8::External>::Cast(a2)->Value());
|
|
|
|
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
|
|
|
|
|
|
|
v8::Local<v8::AccessorSignature> accessor_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::AccessorSignature::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));
|
|
|
|
CHECK_EQ(6u, creator.AddData(accessor_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());
|
|
|
|
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::AccessorSignature>(6)
|
|
|
|
.ToLocalChecked();
|
|
|
|
CHECK(
|
|
|
|
isolate->GetDataFromSnapshotOnce<v8::AccessorSignature>(6).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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
// Create a new context from default context snapshot. This will
|
|
|
|
// create a new global object from a new global object template
|
|
|
|
// without the interceptor.
|
|
|
|
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);
|
2016-12-08 12:44:29 +00:00
|
|
|
v8::TryCatch try_catch(isolate);
|
|
|
|
CHECK(CompileRun("x").IsEmpty());
|
|
|
|
CHECK(try_catch.HasCaught());
|
|
|
|
}
|
|
|
|
{
|
|
|
|
// 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();
|
|
|
|
i::FLAG_rehash_snapshot = true;
|
|
|
|
i::FLAG_hash_seed = 42;
|
|
|
|
i::FLAG_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
|
|
|
}
|
|
|
|
|
|
|
|
i::FLAG_hash_seed = 1337;
|
|
|
|
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();
|
|
|
|
i::FLAG_rehash_snapshot = true;
|
|
|
|
i::FLAG_hash_seed = 42;
|
|
|
|
i::FLAG_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
|
|
|
}
|
|
|
|
|
|
|
|
i::FLAG_hash_seed = 1337;
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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();
|
2018-03-19 10:38:06 +00:00
|
|
|
i::FLAG_allow_natives_syntax = true;
|
|
|
|
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 09:51:51 +00:00
|
|
|
// TODO(leszeks): Fix off-thread deserialization.
|
|
|
|
ScriptDetails script_details(
|
|
|
|
isolate->factory()->NewStringFromAsciiChecked(source));
|
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;
|
|
|
|
}
|
|
|
|
|
2018-03-27 10:44:13 +00:00
|
|
|
TEST(CachedCompileFunctionInContext) {
|
|
|
|
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 =
|
|
|
|
v8::ScriptCompiler::CompileFunctionInContext(
|
|
|
|
env.local(), &script_source, 1, &arg_str, 0, nullptr,
|
|
|
|
v8::ScriptCompiler::kEagerCompile)
|
|
|
|
.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 =
|
|
|
|
v8::ScriptCompiler::CompileFunctionInContext(
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2020-11-26 10:08:27 +00:00
|
|
|
class V8_NODISCARD DisableLazySourcePositionScope {
|
2019-11-19 07:00:02 +00:00
|
|
|
public:
|
|
|
|
DisableLazySourcePositionScope()
|
|
|
|
: backup_value_(FLAG_enable_lazy_source_positions) {
|
|
|
|
FLAG_enable_lazy_source_positions = false;
|
|
|
|
}
|
|
|
|
~DisableLazySourcePositionScope() {
|
|
|
|
FLAG_enable_lazy_source_positions = backup_value_;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2017-08-31 12:34:55 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|