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