v8/test/unittests/objects/object-unittest.cc
Simon Zünd 73f3d2b1ae Introduce stack locals black list field on the ScopeInfo object
This CL is a prepatory step towards moving the stack locals blacklist
from the DebugEvaluateContext to the respective {ScopeInfo} objects.

The locals blacklist is used during local debug evaluate to
decide whether a context lookup can advance the context chain
upwards, or if lookup needs to stop at the current scope.

This CL also introduces a "Recreate" static helper method, that
allows an existing ScopeInfo to be cloned, but with a locals
blacklist attached. This will be needed since blacklists are only
created on-demand during debugging.

R=leszeks@chromium.org

Bug: chromium:1027475, v8:9938
Change-Id: I673dbc99ce9fdc84cb5cda3f9710ba2b76ab92ee
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1946349
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65380}
2019-12-09 10:25:27 +00:00

222 lines
8.3 KiB
C++

// Copyright 2016 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 <cmath>
#include <iostream>
#include <limits>
#include "src/api/api-inl.h"
#include "src/codegen/compiler.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/objects-inl.h"
#include "src/objects/objects.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
namespace {
bool IsInStringInstanceTypeList(InstanceType instance_type) {
switch (instance_type) {
#define ASSERT_INSTANCE_TYPE(type, ...) \
STATIC_ASSERT(InstanceType::type < InstanceType::FIRST_NONSTRING_TYPE);
STRING_TYPE_LIST(ASSERT_INSTANCE_TYPE)
#undef ASSERT_INSTANCE_TYPE
#define TEST_INSTANCE_TYPE(type, ...) case InstanceType::type:
STRING_TYPE_LIST(TEST_INSTANCE_TYPE)
#undef TEST_INSTANCE_TYPE
return true;
default:
EXPECT_LE(InstanceType::FIRST_NONSTRING_TYPE, instance_type);
return false;
}
}
void CheckOneInstanceType(InstanceType instance_type) {
if (IsInStringInstanceTypeList(instance_type)) {
EXPECT_TRUE((instance_type & kIsNotStringMask) == kStringTag)
<< "Failing IsString mask check for " << instance_type;
} else {
EXPECT_FALSE((instance_type & kIsNotStringMask) == kStringTag)
<< "Failing !IsString mask check for " << instance_type;
}
}
} // namespace
TEST(Object, InstanceTypeList) {
#define TEST_INSTANCE_TYPE(type) CheckOneInstanceType(InstanceType::type);
INSTANCE_TYPE_LIST(TEST_INSTANCE_TYPE)
#undef TEST_INSTANCE_TYPE
}
TEST(Object, InstanceTypeListOrder) {
int current = 0;
int last = -1;
InstanceType current_type = static_cast<InstanceType>(current);
EXPECT_EQ(current_type, InstanceType::FIRST_TYPE);
EXPECT_EQ(current_type, InstanceType::INTERNALIZED_STRING_TYPE);
#define TEST_INSTANCE_TYPE(type) \
current_type = InstanceType::type; \
current = static_cast<int>(current_type); \
if (current > static_cast<int>(LAST_NAME_TYPE)) { \
EXPECT_LE(last + 1, current); \
} \
EXPECT_LT(last, current) << " INSTANCE_TYPE_LIST is not ordered: " \
<< "last = " << static_cast<InstanceType>(last) \
<< " vs. current = " << current_type; \
last = current;
// Only test hand-written portion of instance type list. The generated portion
// doesn't run the same risk of getting out of order, and it does emit type
// names out of numerical order in one case: JS_OBJECT_TYPE is emitted before
// its subclass types, because types are emitted in depth-first pre-order
// traversal order, and some of its subclass types are numerically earlier.
INSTANCE_TYPE_LIST_BASE(TEST_INSTANCE_TYPE)
#undef TEST_INSTANCE_TYPE
}
TEST(Object, StructListOrder) {
int current = static_cast<int>(InstanceType::FIRST_STRUCT_TYPE);
int last = current - 1;
ASSERT_LT(0, last);
InstanceType current_type = static_cast<InstanceType>(current);
#define TEST_STRUCT(TYPE, class, name) \
current_type = InstanceType::TYPE; \
current = static_cast<int>(current_type); \
EXPECT_LE(last + 1, current) \
<< " STRUCT_LIST is not ordered: " \
<< " last = " << static_cast<InstanceType>(last) \
<< " vs. current = " << current_type; \
last = current;
// Only test the _BASE portion (the hand-coded part). Note that the values are
// not necessarily consecutive because some Structs that need special
// handling, such as those that have multiple Map instances associated, are
// omitted from this list.
STRUCT_LIST_GENERATOR_BASE(STRUCT_LIST_ADAPTER, TEST_STRUCT)
#undef TEST_STRUCT
}
using ObjectWithIsolate = TestWithIsolate;
TEST_F(ObjectWithIsolate, DictionaryGrowth) {
Handle<NumberDictionary> dict = NumberDictionary::New(isolate(), 1);
Handle<Object> value = isolate()->factory()->null_value();
PropertyDetails details = PropertyDetails::Empty();
// This test documents the expected growth behavior of a dictionary getting
// elements added to it one by one.
STATIC_ASSERT(HashTableBase::kMinCapacity == 4);
uint32_t i = 1;
// 3 elements fit into the initial capacity.
for (; i <= 3; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(4, dict->Capacity());
}
// 4th element triggers growth.
DCHECK_EQ(4, i);
for (; i <= 5; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(8, dict->Capacity());
}
// 6th element triggers growth.
DCHECK_EQ(6, i);
for (; i <= 11; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(16, dict->Capacity());
}
// 12th element triggers growth.
DCHECK_EQ(12, i);
for (; i <= 21; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(32, dict->Capacity());
}
// 22nd element triggers growth.
DCHECK_EQ(22, i);
for (; i <= 43; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(64, dict->Capacity());
}
// 44th element triggers growth.
DCHECK_EQ(44, i);
for (; i <= 50; i++) {
dict = NumberDictionary::Add(isolate(), dict, i, value, details);
CHECK_EQ(128, dict->Capacity());
}
// If we grow by larger chunks, the next (sufficiently big) power of 2 is
// chosen as the capacity.
dict = NumberDictionary::New(isolate(), 1);
dict = NumberDictionary::EnsureCapacity(isolate(), dict, 65);
CHECK_EQ(128, dict->Capacity());
dict = NumberDictionary::New(isolate(), 1);
dict = NumberDictionary::EnsureCapacity(isolate(), dict, 30);
CHECK_EQ(64, dict->Capacity());
}
TEST_F(TestWithNativeContext, EmptyFunctionScopeInfo) {
// Check that the empty_function has a properly set up ScopeInfo.
Handle<JSFunction> function = RunJS<JSFunction>("(function(){})");
Handle<ScopeInfo> scope_info(function->shared().scope_info(),
function->GetIsolate());
Handle<ScopeInfo> empty_function_scope_info(
isolate()->empty_function()->shared().scope_info(),
function->GetIsolate());
EXPECT_EQ(scope_info->length(), empty_function_scope_info->length());
EXPECT_EQ(scope_info->Flags(), empty_function_scope_info->Flags());
EXPECT_EQ(scope_info->ParameterCount(),
empty_function_scope_info->ParameterCount());
EXPECT_EQ(scope_info->ContextLocalCount(),
empty_function_scope_info->ContextLocalCount());
}
TEST_F(TestWithNativeContext, RecreateScopeInfoWithLocalsBlacklistWorks) {
// Create a JSFunction to get a {ScopeInfo} we can use for the test.
Handle<JSFunction> function = RunJS<JSFunction>("(function foo() {})");
Handle<ScopeInfo> original_scope_info(function->shared().scope_info(),
isolate());
ASSERT_FALSE(original_scope_info->HasLocalsBlackList());
Handle<String> foo_string =
isolate()->factory()->NewStringFromStaticChars("foo");
Handle<String> bar_string =
isolate()->factory()->NewStringFromStaticChars("bar");
Handle<StringSet> blacklist = StringSet::New(isolate());
StringSet::Add(isolate(), blacklist, foo_string);
Handle<ScopeInfo> scope_info = ScopeInfo::RecreateWithBlackList(
isolate(), original_scope_info, blacklist);
DisallowHeapAllocation no_gc;
EXPECT_TRUE(scope_info->HasLocalsBlackList());
EXPECT_TRUE(scope_info->LocalsBlackList().Has(isolate(), foo_string));
EXPECT_FALSE(scope_info->LocalsBlackList().Has(isolate(), bar_string));
EXPECT_EQ(original_scope_info->length() + 1, scope_info->length());
// Check that all variable fields *before* the blacklist stayed the same.
for (int i = ScopeInfo::kVariablePartIndex;
i < scope_info->LocalsBlackListIndex(); ++i) {
EXPECT_EQ(original_scope_info->get(i), scope_info->get(i));
}
// Check that all variable fields *after* the blacklist stayed the same.
for (int i = scope_info->LocalsBlackListIndex() + 1; i < scope_info->length();
++i) {
EXPECT_EQ(original_scope_info->get(i - 1), scope_info->get(i));
}
}
} // namespace internal
} // namespace v8