Line data Source code
1 : // Copyright 2016 the V8 project authors. All rights reserved.
2 : // Use of this source code is governed by a BSD-style license that can be
3 : // found in the LICENSE file.
4 :
5 : #include "src/api-inl.h"
6 : #include "src/handles-inl.h"
7 : #include "src/heap/factory.h"
8 : #include "src/isolate.h"
9 : #include "src/objects-inl.h"
10 : #include "src/v8.h"
11 : #include "test/cctest/cctest.h"
12 :
13 : namespace v8 {
14 : namespace internal {
15 :
16 70 : static void CheckObject(Isolate* isolate, Handle<Object> obj,
17 : const char* string) {
18 140 : Object print_string = *Object::NoSideEffectsToString(isolate, obj);
19 70 : CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
20 70 : }
21 :
22 5 : static void CheckSmi(Isolate* isolate, int value, const char* string) {
23 : Handle<Object> handle(Smi::FromInt(value), isolate);
24 5 : CheckObject(isolate, handle, string);
25 5 : }
26 :
27 5 : static void CheckString(Isolate* isolate, const char* value,
28 : const char* string) {
29 5 : Handle<String> handle(isolate->factory()->NewStringFromAsciiChecked(value));
30 5 : CheckObject(isolate, handle, string);
31 5 : }
32 :
33 5 : static void CheckNumber(Isolate* isolate, double value, const char* string) {
34 5 : Handle<Object> number = isolate->factory()->NewNumber(value);
35 10 : CHECK(number->IsNumber());
36 5 : CheckObject(isolate, number, string);
37 5 : }
38 :
39 : static void CheckBoolean(Isolate* isolate, bool value, const char* string) {
40 : CheckObject(isolate, value ? isolate->factory()->true_value()
41 : : isolate->factory()->false_value(),
42 15 : string);
43 : }
44 :
45 28342 : TEST(NoSideEffectsToString) {
46 5 : CcTest::InitializeVM();
47 : Isolate* isolate = CcTest::i_isolate();
48 : Factory* factory = isolate->factory();
49 :
50 : HandleScope scope(isolate);
51 :
52 5 : CheckString(isolate, "fisk hest", "fisk hest");
53 5 : CheckNumber(isolate, 42.3, "42.3");
54 5 : CheckSmi(isolate, 42, "42");
55 : CheckBoolean(isolate, true, "true");
56 : CheckBoolean(isolate, false, "false");
57 : CheckBoolean(isolate, false, "false");
58 : Handle<Object> smi_42 = handle(Smi::FromInt(42), isolate);
59 : CheckObject(isolate, BigInt::FromNumber(isolate, smi_42).ToHandleChecked(),
60 10 : "42");
61 5 : CheckObject(isolate, factory->undefined_value(), "undefined");
62 5 : CheckObject(isolate, factory->null_value(), "null");
63 :
64 5 : CheckObject(isolate, factory->error_to_string(), "[object Error]");
65 : CheckObject(isolate, factory->unscopables_symbol(),
66 5 : "Symbol(Symbol.unscopables)");
67 : CheckObject(isolate, factory->NewError(isolate->error_function(),
68 : factory->empty_string()),
69 5 : "Error");
70 : CheckObject(isolate, factory->NewError(
71 : isolate->error_function(),
72 : factory->NewStringFromAsciiChecked("fisk hest")),
73 5 : "Error: fisk hest");
74 : CheckObject(isolate, factory->NewJSObject(isolate->object_function()),
75 10 : "#<Object>");
76 5 : }
77 :
78 28342 : TEST(EnumCache) {
79 5 : LocalContext env;
80 5 : v8::Isolate* isolate = env->GetIsolate();
81 : i::Factory* factory = CcTest::i_isolate()->factory();
82 10 : v8::HandleScope scope(isolate);
83 :
84 : // Create a nice transition tree:
85 : // (a) --> (b) --> (c) shared DescriptorArray 1
86 : // |
87 : // +---> (cc) shared DescriptorArray 2
88 : CompileRun(
89 : "function O(a) { this.a = 1 };"
90 :
91 : "a = new O();"
92 :
93 : "b = new O();"
94 : "b.b = 2;"
95 :
96 : "c = new O();"
97 : "c.b = 2;"
98 : "c.c = 3;"
99 :
100 : "cc = new O();"
101 : "cc.b = 2;"
102 : "cc.cc = 4;");
103 :
104 : Handle<JSObject> a = Handle<JSObject>::cast(v8::Utils::OpenHandle(
105 25 : *env->Global()->Get(env.local(), v8_str("a")).ToLocalChecked()));
106 : Handle<JSObject> b = Handle<JSObject>::cast(v8::Utils::OpenHandle(
107 25 : *env->Global()->Get(env.local(), v8_str("b")).ToLocalChecked()));
108 : Handle<JSObject> c = Handle<JSObject>::cast(v8::Utils::OpenHandle(
109 25 : *env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked()));
110 : Handle<JSObject> cc = Handle<JSObject>::cast(v8::Utils::OpenHandle(
111 25 : *env->Global()->Get(env.local(), v8_str("cc")).ToLocalChecked()));
112 :
113 : // Check the transition tree.
114 15 : CHECK_EQ(a->map()->instance_descriptors(), b->map()->instance_descriptors());
115 15 : CHECK_EQ(b->map()->instance_descriptors(), c->map()->instance_descriptors());
116 15 : CHECK_NE(c->map()->instance_descriptors(), cc->map()->instance_descriptors());
117 15 : CHECK_NE(b->map()->instance_descriptors(), cc->map()->instance_descriptors());
118 :
119 : // Check that the EnumLength is unset.
120 5 : CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
121 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
122 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
123 5 : CHECK_EQ(cc->map()->EnumLength(), kInvalidEnumCacheSentinel);
124 :
125 : // Check that the EnumCache is empty.
126 15 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(),
127 : *factory->empty_enum_cache());
128 15 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(),
129 : *factory->empty_enum_cache());
130 15 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(),
131 : *factory->empty_enum_cache());
132 15 : CHECK_EQ(cc->map()->instance_descriptors()->enum_cache(),
133 : *factory->empty_enum_cache());
134 :
135 : // The EnumCache is shared on the DescriptorArray, creating it on {cc} has no
136 : // effect on the other maps.
137 : CompileRun("var s = 0; for (let key in cc) { s += cc[key] };");
138 : {
139 5 : CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
140 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
141 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
142 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
143 :
144 15 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(),
145 : *factory->empty_enum_cache());
146 15 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(),
147 : *factory->empty_enum_cache());
148 15 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(),
149 : *factory->empty_enum_cache());
150 :
151 5 : EnumCache enum_cache = cc->map()->instance_descriptors()->enum_cache();
152 10 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
153 10 : CHECK_EQ(enum_cache->keys()->length(), 3);
154 10 : CHECK_EQ(enum_cache->indices()->length(), 3);
155 : }
156 :
157 : // Initializing the EnumCache for the the topmost map {a} will not create the
158 : // cache for the other maps.
159 : CompileRun("var s = 0; for (let key in a) { s += a[key] };");
160 : {
161 5 : CHECK_EQ(a->map()->EnumLength(), 1);
162 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
163 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
164 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
165 :
166 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
167 : // {c} only.
168 5 : EnumCache enum_cache = a->map()->instance_descriptors()->enum_cache();
169 10 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
170 15 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
171 : *factory->empty_enum_cache());
172 10 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
173 10 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
174 10 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
175 10 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
176 :
177 10 : CHECK_EQ(enum_cache->keys()->length(), 1);
178 10 : CHECK_EQ(enum_cache->indices()->length(), 1);
179 : }
180 :
181 : // Creating the EnumCache for {c} will create a new EnumCache on the shared
182 : // DescriptorArray.
183 : Handle<EnumCache> previous_enum_cache(
184 10 : a->map()->instance_descriptors()->enum_cache(), a->GetIsolate());
185 : Handle<FixedArray> previous_keys(previous_enum_cache->keys(),
186 10 : a->GetIsolate());
187 : Handle<FixedArray> previous_indices(previous_enum_cache->indices(),
188 10 : a->GetIsolate());
189 : CompileRun("var s = 0; for (let key in c) { s += c[key] };");
190 : {
191 5 : CHECK_EQ(a->map()->EnumLength(), 1);
192 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
193 5 : CHECK_EQ(c->map()->EnumLength(), 3);
194 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
195 :
196 5 : EnumCache enum_cache = c->map()->instance_descriptors()->enum_cache();
197 10 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
198 : // The keys and indices caches are updated.
199 10 : CHECK_EQ(enum_cache, *previous_enum_cache);
200 10 : CHECK_NE(enum_cache->keys(), *previous_keys);
201 10 : CHECK_NE(enum_cache->indices(), *previous_indices);
202 5 : CHECK_EQ(previous_keys->length(), 1);
203 5 : CHECK_EQ(previous_indices->length(), 1);
204 10 : CHECK_EQ(enum_cache->keys()->length(), 3);
205 10 : CHECK_EQ(enum_cache->indices()->length(), 3);
206 :
207 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
208 : // {c} only.
209 15 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
210 : *factory->empty_enum_cache());
211 10 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
212 15 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
213 : *previous_enum_cache);
214 10 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
215 10 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
216 10 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
217 : }
218 :
219 : // {b} can reuse the existing EnumCache, hence we only need to set the correct
220 : // EnumLength on the map without modifying the cache itself.
221 : previous_enum_cache =
222 10 : handle(a->map()->instance_descriptors()->enum_cache(), a->GetIsolate());
223 10 : previous_keys = handle(previous_enum_cache->keys(), a->GetIsolate());
224 10 : previous_indices = handle(previous_enum_cache->indices(), a->GetIsolate());
225 : CompileRun("var s = 0; for (let key in b) { s += b[key] };");
226 : {
227 5 : CHECK_EQ(a->map()->EnumLength(), 1);
228 5 : CHECK_EQ(b->map()->EnumLength(), 2);
229 5 : CHECK_EQ(c->map()->EnumLength(), 3);
230 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
231 :
232 5 : EnumCache enum_cache = c->map()->instance_descriptors()->enum_cache();
233 10 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
234 : // The keys and indices caches are not updated.
235 10 : CHECK_EQ(enum_cache, *previous_enum_cache);
236 10 : CHECK_EQ(enum_cache->keys(), *previous_keys);
237 10 : CHECK_EQ(enum_cache->indices(), *previous_indices);
238 10 : CHECK_EQ(enum_cache->keys()->length(), 3);
239 10 : CHECK_EQ(enum_cache->indices()->length(), 3);
240 :
241 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
242 : // {c} only.
243 15 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
244 : *factory->empty_enum_cache());
245 10 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
246 15 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
247 : *previous_enum_cache);
248 10 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
249 10 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
250 10 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
251 5 : }
252 5 : }
253 :
254 : } // namespace internal
255 85011 : } // namespace v8
|