2018-07-26 06:42:03 +00:00
|
|
|
// Copyright 2018 the V8 project authors. All rights reserved.
|
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
|
|
|
|
2019-05-17 12:13:44 +00:00
|
|
|
#include "src/api/api-inl.h"
|
|
|
|
#include "src/api/api.h"
|
2019-05-22 07:55:37 +00:00
|
|
|
#include "src/execution/isolate.h"
|
2019-02-14 21:10:30 +00:00
|
|
|
#include "src/heap/heap-inl.h"
|
2018-07-26 06:42:03 +00:00
|
|
|
#include "src/heap/spaces.h"
|
2019-05-23 08:51:46 +00:00
|
|
|
#include "src/objects/objects-inl.h"
|
2018-07-26 06:42:03 +00:00
|
|
|
#include "test/cctest/cctest.h"
|
|
|
|
#include "test/cctest/heap/heap-tester.h"
|
|
|
|
#include "test/cctest/heap/heap-utils.h"
|
|
|
|
|
|
|
|
#define TEST_STR "tests are great!"
|
|
|
|
|
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
|
|
|
namespace heap {
|
|
|
|
|
|
|
|
// Adapted from cctest/test-api.cc
|
|
|
|
class TestOneByteResource : public v8::String::ExternalOneByteStringResource {
|
|
|
|
public:
|
|
|
|
explicit TestOneByteResource(const char* data, int* counter = nullptr,
|
|
|
|
size_t offset = 0)
|
|
|
|
: orig_data_(data),
|
|
|
|
data_(data + offset),
|
|
|
|
length_(strlen(data) - offset),
|
|
|
|
counter_(counter) {}
|
|
|
|
|
2018-09-14 14:02:15 +00:00
|
|
|
~TestOneByteResource() override {
|
2018-07-26 06:42:03 +00:00
|
|
|
i::DeleteArray(orig_data_);
|
|
|
|
if (counter_ != nullptr) ++*counter_;
|
|
|
|
}
|
|
|
|
|
2018-09-14 14:02:15 +00:00
|
|
|
const char* data() const override { return data_; }
|
2018-07-26 06:42:03 +00:00
|
|
|
|
2018-09-14 14:02:15 +00:00
|
|
|
size_t length() const override { return length_; }
|
2018-07-26 06:42:03 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
const char* orig_data_;
|
|
|
|
const char* data_;
|
|
|
|
size_t length_;
|
|
|
|
int* counter_;
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST(ExternalString_ExternalBackingStoreSizeIncreases) {
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
|
|
|
|
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
|
|
|
|
|
|
|
|
const size_t backing_store_before =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
|
|
|
|
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
|
|
|
|
USE(es);
|
|
|
|
|
|
|
|
const size_t backing_store_after =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExternalString_ExternalBackingStoreSizeDecreases) {
|
|
|
|
ManualGCScope manual_gc_scope;
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
|
|
|
|
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
|
|
|
|
|
|
|
|
const size_t backing_store_before =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
|
|
|
|
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
|
|
|
|
USE(es);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap::GcAndSweep(heap, OLD_SPACE);
|
|
|
|
|
|
|
|
const size_t backing_store_after =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
CHECK_EQ(0, backing_store_after - backing_store_before);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExternalString_ExternalBackingStoreSizeIncreasesMarkCompact) {
|
|
|
|
if (FLAG_never_compact) return;
|
|
|
|
ManualGCScope manual_gc_scope;
|
|
|
|
FLAG_manual_evacuation_candidates_selection = true;
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
|
|
|
|
heap::AbandonCurrentlyFreeMemory(heap->old_space());
|
|
|
|
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
|
|
|
|
|
|
|
|
const size_t backing_store_before =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
v8::Local<v8::String> es = v8::String::NewExternalOneByte(
|
|
|
|
isolate, new TestOneByteResource(i::StrDup(TEST_STR))).ToLocalChecked();
|
|
|
|
v8::internal::Handle<v8::internal::String> esh = v8::Utils::OpenHandle(*es);
|
|
|
|
|
2019-01-15 00:23:43 +00:00
|
|
|
Page* page_before_gc = Page::FromHeapObject(*esh);
|
2018-07-26 06:42:03 +00:00
|
|
|
heap::ForceEvacuationCandidate(page_before_gc);
|
|
|
|
|
|
|
|
CcTest::CollectAllGarbage();
|
|
|
|
|
|
|
|
const size_t backing_store_after =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
CHECK_EQ(es->Length(), backing_store_after - backing_store_before);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap::GcAndSweep(heap, OLD_SPACE);
|
|
|
|
const size_t backing_store_after =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
CHECK_EQ(0, backing_store_after - backing_store_before);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(ExternalString_ExternalBackingStoreSizeIncreasesAfterExternalization) {
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
Heap* heap = reinterpret_cast<Isolate*>(isolate)->heap();
|
|
|
|
ExternalBackingStoreType type = ExternalBackingStoreType::kExternalString;
|
|
|
|
size_t old_backing_store_before = 0, new_backing_store_before = 0;
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
new_backing_store_before =
|
|
|
|
heap->new_space()->ExternalBackingStoreBytes(type);
|
|
|
|
old_backing_store_before =
|
|
|
|
heap->old_space()->ExternalBackingStoreBytes(type);
|
|
|
|
|
|
|
|
// Allocate normal string in the new gen.
|
|
|
|
v8::Local<v8::String> str =
|
|
|
|
v8::String::NewFromUtf8(isolate, TEST_STR, v8::NewStringType::kNormal)
|
|
|
|
.ToLocalChecked();
|
|
|
|
|
|
|
|
CHECK_EQ(0, heap->new_space()->ExternalBackingStoreBytes(type) -
|
|
|
|
new_backing_store_before);
|
|
|
|
|
|
|
|
// Trigger GCs so that the newly allocated string moves to old gen.
|
|
|
|
heap::GcAndSweep(heap, NEW_SPACE); // in survivor space now
|
|
|
|
heap::GcAndSweep(heap, NEW_SPACE); // in old gen now
|
|
|
|
|
|
|
|
bool success =
|
|
|
|
str->MakeExternal(new TestOneByteResource(i::StrDup(TEST_STR)));
|
|
|
|
CHECK(success);
|
|
|
|
|
|
|
|
CHECK_EQ(str->Length(), heap->old_space()->ExternalBackingStoreBytes(type) -
|
|
|
|
old_backing_store_before);
|
|
|
|
}
|
|
|
|
|
|
|
|
heap::GcAndSweep(heap, OLD_SPACE);
|
|
|
|
|
|
|
|
CHECK_EQ(0, heap->old_space()->ExternalBackingStoreBytes(type) -
|
|
|
|
old_backing_store_before);
|
|
|
|
}
|
|
|
|
|
2018-07-30 17:35:38 +00:00
|
|
|
TEST(ExternalString_PromotedThinString) {
|
|
|
|
ManualGCScope manual_gc_scope;
|
|
|
|
CcTest::InitializeVM();
|
|
|
|
LocalContext env;
|
|
|
|
v8::Isolate* isolate = env->GetIsolate();
|
|
|
|
i::Isolate* i_isolate = CcTest::i_isolate();
|
|
|
|
i::Factory* factory = i_isolate->factory();
|
|
|
|
Heap* heap = i_isolate->heap();
|
|
|
|
|
|
|
|
{
|
|
|
|
v8::HandleScope handle_scope(isolate);
|
|
|
|
|
|
|
|
// New external string in the old space.
|
|
|
|
v8::internal::Handle<v8::internal::String> string1 =
|
|
|
|
factory
|
|
|
|
->NewExternalStringFromOneByte(
|
|
|
|
new TestOneByteResource(i::StrDup(TEST_STR)))
|
|
|
|
.ToHandleChecked();
|
|
|
|
|
|
|
|
// Internalize external string.
|
|
|
|
i::Handle<i::String> isymbol1 = factory->InternalizeString(string1);
|
|
|
|
CHECK(isymbol1->IsInternalizedString());
|
|
|
|
CHECK(string1->IsExternalString());
|
2019-02-11 15:07:56 +00:00
|
|
|
CHECK(!heap->InYoungGeneration(*isymbol1));
|
2018-07-30 17:35:38 +00:00
|
|
|
|
|
|
|
// New external string in the young space. This string has the same content
|
|
|
|
// as the previous one (that was already internalized).
|
|
|
|
v8::Local<v8::String> string2 =
|
|
|
|
v8::String::NewFromUtf8(isolate, TEST_STR, v8::NewStringType::kNormal)
|
|
|
|
.ToLocalChecked();
|
|
|
|
bool success =
|
|
|
|
string2->MakeExternal(new TestOneByteResource(i::StrDup(TEST_STR)));
|
|
|
|
CHECK(success);
|
|
|
|
|
|
|
|
// Internalize (it will create a thin string in the new space).
|
|
|
|
i::Handle<i::String> istring = v8::Utils::OpenHandle(*string2);
|
|
|
|
i::Handle<i::String> isymbol2 = factory->InternalizeString(istring);
|
|
|
|
CHECK(isymbol2->IsInternalizedString());
|
|
|
|
CHECK(istring->IsThinString());
|
2019-02-11 15:07:56 +00:00
|
|
|
CHECK(heap->InYoungGeneration(*istring));
|
2018-07-30 17:35:38 +00:00
|
|
|
|
|
|
|
// Collect thin string. References to the thin string will be updated to
|
|
|
|
// point to the actual external string in the old space.
|
|
|
|
heap::GcAndSweep(heap, NEW_SPACE);
|
|
|
|
|
|
|
|
USE(isymbol1);
|
|
|
|
USE(isymbol2);
|
|
|
|
}
|
|
|
|
}
|
2018-07-26 06:42:03 +00:00
|
|
|
} // namespace heap
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|
|
|
|
|
|
|
|
#undef TEST_STR
|