b097a8e5de
String::NewFromLiteral is a templated function that takes a char[N] argument that can be used as an alternative to String::NewFromUtf8 and returns a Local<String> rather than a MaybeLocal<String> reducing the number of ToLocalChecked() or other checks. Since the string length is known at compile time, it can statically assert that the length is less than String::kMaxLength, which means that it can never fail at runtime. This also converts all found uses of NewFromUtf8 taking a string literal or a variable initialized from a string literal to use the new API. In some cases the types of stored string literals are changed from const char* to const char[] to ensure the size is retained. This API does introduce a small difference compared to NewFromUtf8. For a case like "abc\0def", NewFromUtf8 (using length -1 to infer length) would treat this as a 3 character string, whereas the new API will treat it as a 7 character string. As a drive-by fix, this also fixes all redundant uses of v8::NewStringType::kNormal when passed to any of the String::New* functions. Change-Id: Id96a44bc068d9c4eaa634aea688e024675a0e5b3 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2089935 Commit-Queue: Dan Elphick <delphick@chromium.org> Reviewed-by: Mathias Bynens <mathias@chromium.org> Reviewed-by: Mythri Alle <mythria@chromium.org> Reviewed-by: Clemens Backes <clemensb@chromium.org> Reviewed-by: Ulan Degenbaev <ulan@chromium.org> Cr-Commit-Position: refs/heads/master@{#66622}
226 lines
7.3 KiB
C++
226 lines
7.3 KiB
C++
// 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.
|
|
|
|
#include "src/api/api-inl.h"
|
|
#include "src/api/api.h"
|
|
#include "src/execution/isolate.h"
|
|
#include "src/heap/heap-inl.h"
|
|
#include "src/heap/spaces.h"
|
|
#include "src/objects/objects-inl.h"
|
|
#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) {}
|
|
|
|
~TestOneByteResource() override {
|
|
i::DeleteArray(orig_data_);
|
|
if (counter_ != nullptr) ++*counter_;
|
|
}
|
|
|
|
const char* data() const override { return data_; }
|
|
|
|
size_t length() const override { return length_; }
|
|
|
|
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);
|
|
|
|
Page* page_before_gc = Page::FromHeapObject(*esh);
|
|
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::NewFromUtf8Literal(isolate, TEST_STR);
|
|
|
|
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);
|
|
}
|
|
|
|
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());
|
|
CHECK(!heap->InYoungGeneration(*isymbol1));
|
|
|
|
// 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::NewFromUtf8Literal(isolate, TEST_STR);
|
|
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());
|
|
CHECK(heap->InYoungGeneration(*istring));
|
|
|
|
// 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);
|
|
}
|
|
}
|
|
} // namespace heap
|
|
} // namespace internal
|
|
} // namespace v8
|
|
|
|
#undef TEST_STR
|