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/function-kind.h"
7 : #include "src/globals.h"
8 : #include "src/handles-inl.h"
9 : #include "src/heap/factory.h"
10 : #include "src/isolate.h"
11 : #include "src/objects-inl.h"
12 : #include "src/v8.h"
13 : #include "test/cctest/cctest.h"
14 :
15 : namespace v8 {
16 : namespace internal {
17 :
18 70 : static void CheckObject(Isolate* isolate, Handle<Object> obj,
19 : const char* string) {
20 140 : Object print_string = *Object::NoSideEffectsToString(isolate, obj);
21 70 : CHECK(String::cast(print_string)->IsUtf8EqualTo(CStrVector(string)));
22 70 : }
23 :
24 5 : static void CheckSmi(Isolate* isolate, int value, const char* string) {
25 : Handle<Object> handle(Smi::FromInt(value), isolate);
26 5 : CheckObject(isolate, handle, string);
27 5 : }
28 :
29 5 : static void CheckString(Isolate* isolate, const char* value,
30 : const char* string) {
31 5 : Handle<String> handle(isolate->factory()->NewStringFromAsciiChecked(value));
32 5 : CheckObject(isolate, handle, string);
33 5 : }
34 :
35 5 : static void CheckNumber(Isolate* isolate, double value, const char* string) {
36 5 : Handle<Object> number = isolate->factory()->NewNumber(value);
37 5 : CHECK(number->IsNumber());
38 5 : CheckObject(isolate, number, string);
39 5 : }
40 :
41 : static void CheckBoolean(Isolate* isolate, bool value, const char* string) {
42 15 : CheckObject(isolate, value ? isolate->factory()->true_value()
43 : : isolate->factory()->false_value(),
44 15 : string);
45 : }
46 :
47 26644 : TEST(NoSideEffectsToString) {
48 5 : CcTest::InitializeVM();
49 : Isolate* isolate = CcTest::i_isolate();
50 : Factory* factory = isolate->factory();
51 :
52 : HandleScope scope(isolate);
53 :
54 5 : CheckString(isolate, "fisk hest", "fisk hest");
55 5 : CheckNumber(isolate, 42.3, "42.3");
56 5 : CheckSmi(isolate, 42, "42");
57 : CheckBoolean(isolate, true, "true");
58 : CheckBoolean(isolate, false, "false");
59 : CheckBoolean(isolate, false, "false");
60 : Handle<Object> smi_42 = handle(Smi::FromInt(42), isolate);
61 10 : CheckObject(isolate, BigInt::FromNumber(isolate, smi_42).ToHandleChecked(),
62 5 : "42");
63 5 : CheckObject(isolate, factory->undefined_value(), "undefined");
64 5 : CheckObject(isolate, factory->null_value(), "null");
65 :
66 5 : CheckObject(isolate, factory->error_to_string(), "[object Error]");
67 5 : CheckObject(isolate, factory->unscopables_symbol(),
68 5 : "Symbol(Symbol.unscopables)");
69 5 : CheckObject(isolate, factory->NewError(isolate->error_function(),
70 : factory->empty_string()),
71 5 : "Error");
72 5 : CheckObject(isolate, factory->NewError(
73 : isolate->error_function(),
74 : factory->NewStringFromAsciiChecked("fisk hest")),
75 5 : "Error: fisk hest");
76 10 : CheckObject(isolate, factory->NewJSObject(isolate->object_function()),
77 5 : "#<Object>");
78 5 : }
79 :
80 26644 : TEST(EnumCache) {
81 5 : LocalContext env;
82 5 : v8::Isolate* isolate = env->GetIsolate();
83 : i::Factory* factory = CcTest::i_isolate()->factory();
84 10 : v8::HandleScope scope(isolate);
85 :
86 : // Create a nice transition tree:
87 : // (a) --> (b) --> (c) shared DescriptorArray 1
88 : // |
89 : // +---> (cc) shared DescriptorArray 2
90 : CompileRun(
91 : "function O(a) { this.a = 1 };"
92 :
93 : "a = new O();"
94 :
95 : "b = new O();"
96 : "b.b = 2;"
97 :
98 : "c = new O();"
99 : "c.b = 2;"
100 : "c.c = 3;"
101 :
102 : "cc = new O();"
103 : "cc.b = 2;"
104 : "cc.cc = 4;");
105 :
106 : Handle<JSObject> a = Handle<JSObject>::cast(v8::Utils::OpenHandle(
107 20 : *env->Global()->Get(env.local(), v8_str("a")).ToLocalChecked()));
108 : Handle<JSObject> b = Handle<JSObject>::cast(v8::Utils::OpenHandle(
109 20 : *env->Global()->Get(env.local(), v8_str("b")).ToLocalChecked()));
110 : Handle<JSObject> c = Handle<JSObject>::cast(v8::Utils::OpenHandle(
111 20 : *env->Global()->Get(env.local(), v8_str("c")).ToLocalChecked()));
112 : Handle<JSObject> cc = Handle<JSObject>::cast(v8::Utils::OpenHandle(
113 20 : *env->Global()->Get(env.local(), v8_str("cc")).ToLocalChecked()));
114 :
115 : // Check the transition tree.
116 5 : CHECK_EQ(a->map()->instance_descriptors(), b->map()->instance_descriptors());
117 5 : CHECK_EQ(b->map()->instance_descriptors(), c->map()->instance_descriptors());
118 5 : CHECK_NE(c->map()->instance_descriptors(), cc->map()->instance_descriptors());
119 5 : CHECK_NE(b->map()->instance_descriptors(), cc->map()->instance_descriptors());
120 :
121 : // Check that the EnumLength is unset.
122 5 : CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
123 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
124 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
125 5 : CHECK_EQ(cc->map()->EnumLength(), kInvalidEnumCacheSentinel);
126 :
127 : // Check that the EnumCache is empty.
128 5 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(),
129 : *factory->empty_enum_cache());
130 5 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(),
131 : *factory->empty_enum_cache());
132 5 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(),
133 : *factory->empty_enum_cache());
134 5 : CHECK_EQ(cc->map()->instance_descriptors()->enum_cache(),
135 : *factory->empty_enum_cache());
136 :
137 : // The EnumCache is shared on the DescriptorArray, creating it on {cc} has no
138 : // effect on the other maps.
139 : CompileRun("var s = 0; for (let key in cc) { s += cc[key] };");
140 : {
141 5 : CHECK_EQ(a->map()->EnumLength(), kInvalidEnumCacheSentinel);
142 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
143 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
144 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
145 :
146 5 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(),
147 : *factory->empty_enum_cache());
148 5 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(),
149 : *factory->empty_enum_cache());
150 5 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(),
151 : *factory->empty_enum_cache());
152 :
153 : EnumCache enum_cache = cc->map()->instance_descriptors()->enum_cache();
154 5 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
155 5 : CHECK_EQ(enum_cache->keys()->length(), 3);
156 5 : CHECK_EQ(enum_cache->indices()->length(), 3);
157 : }
158 :
159 : // Initializing the EnumCache for the the topmost map {a} will not create the
160 : // cache for the other maps.
161 : CompileRun("var s = 0; for (let key in a) { s += a[key] };");
162 : {
163 5 : CHECK_EQ(a->map()->EnumLength(), 1);
164 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
165 5 : CHECK_EQ(c->map()->EnumLength(), kInvalidEnumCacheSentinel);
166 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
167 :
168 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
169 : // {c} only.
170 : EnumCache enum_cache = a->map()->instance_descriptors()->enum_cache();
171 5 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
172 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
173 : *factory->empty_enum_cache());
174 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
175 5 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
176 5 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
177 5 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
178 :
179 5 : CHECK_EQ(enum_cache->keys()->length(), 1);
180 5 : CHECK_EQ(enum_cache->indices()->length(), 1);
181 : }
182 :
183 : // Creating the EnumCache for {c} will create a new EnumCache on the shared
184 : // DescriptorArray.
185 : Handle<EnumCache> previous_enum_cache(
186 : a->map()->instance_descriptors()->enum_cache(), a->GetIsolate());
187 : Handle<FixedArray> previous_keys(previous_enum_cache->keys(),
188 : a->GetIsolate());
189 : Handle<FixedArray> previous_indices(previous_enum_cache->indices(),
190 : a->GetIsolate());
191 : CompileRun("var s = 0; for (let key in c) { s += c[key] };");
192 : {
193 5 : CHECK_EQ(a->map()->EnumLength(), 1);
194 5 : CHECK_EQ(b->map()->EnumLength(), kInvalidEnumCacheSentinel);
195 5 : CHECK_EQ(c->map()->EnumLength(), 3);
196 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
197 :
198 : EnumCache enum_cache = c->map()->instance_descriptors()->enum_cache();
199 5 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
200 : // The keys and indices caches are updated.
201 5 : CHECK_EQ(enum_cache, *previous_enum_cache);
202 5 : CHECK_NE(enum_cache->keys(), *previous_keys);
203 5 : CHECK_NE(enum_cache->indices(), *previous_indices);
204 5 : CHECK_EQ(previous_keys->length(), 1);
205 5 : CHECK_EQ(previous_indices->length(), 1);
206 5 : CHECK_EQ(enum_cache->keys()->length(), 3);
207 5 : CHECK_EQ(enum_cache->indices()->length(), 3);
208 :
209 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
210 : // {c} only.
211 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
212 : *factory->empty_enum_cache());
213 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
214 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
215 : *previous_enum_cache);
216 5 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
217 5 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
218 5 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
219 : }
220 :
221 : // {b} can reuse the existing EnumCache, hence we only need to set the correct
222 : // EnumLength on the map without modifying the cache itself.
223 : previous_enum_cache =
224 : handle(a->map()->instance_descriptors()->enum_cache(), a->GetIsolate());
225 : previous_keys = handle(previous_enum_cache->keys(), a->GetIsolate());
226 : previous_indices = handle(previous_enum_cache->indices(), a->GetIsolate());
227 : CompileRun("var s = 0; for (let key in b) { s += b[key] };");
228 : {
229 5 : CHECK_EQ(a->map()->EnumLength(), 1);
230 5 : CHECK_EQ(b->map()->EnumLength(), 2);
231 5 : CHECK_EQ(c->map()->EnumLength(), 3);
232 5 : CHECK_EQ(cc->map()->EnumLength(), 3);
233 :
234 : EnumCache enum_cache = c->map()->instance_descriptors()->enum_cache();
235 5 : CHECK_NE(enum_cache, *factory->empty_enum_cache());
236 : // The keys and indices caches are not updated.
237 5 : CHECK_EQ(enum_cache, *previous_enum_cache);
238 5 : CHECK_EQ(enum_cache->keys(), *previous_keys);
239 5 : CHECK_EQ(enum_cache->indices(), *previous_indices);
240 5 : CHECK_EQ(enum_cache->keys()->length(), 3);
241 5 : CHECK_EQ(enum_cache->indices()->length(), 3);
242 :
243 : // The enum cache is shared on the descriptor array of maps {a}, {b} and
244 : // {c} only.
245 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
246 : *factory->empty_enum_cache());
247 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(), enum_cache);
248 5 : CHECK_NE(cc->map()->instance_descriptors()->enum_cache(),
249 : *previous_enum_cache);
250 5 : CHECK_EQ(a->map()->instance_descriptors()->enum_cache(), enum_cache);
251 5 : CHECK_EQ(b->map()->instance_descriptors()->enum_cache(), enum_cache);
252 5 : CHECK_EQ(c->map()->instance_descriptors()->enum_cache(), enum_cache);
253 : }
254 5 : }
255 :
256 : #define TEST_FUNCTION_KIND(Name) \
257 : TEST(Name) { \
258 : for (int i = 0; i < FunctionKind::kLastFunctionKind; i++) { \
259 : FunctionKind kind = static_cast<FunctionKind>(i); \
260 : CHECK_EQ(FunctionKind##Name(kind), Name(kind)); \
261 : } \
262 : }
263 :
264 0 : bool FunctionKindIsArrowFunction(FunctionKind kind) {
265 85 : switch (kind) {
266 : case FunctionKind::kArrowFunction:
267 : case FunctionKind::kAsyncArrowFunction:
268 : return true;
269 : default:
270 0 : return false;
271 : }
272 : }
273 26729 : TEST_FUNCTION_KIND(IsArrowFunction)
274 :
275 0 : bool FunctionKindIsAsyncGeneratorFunction(FunctionKind kind) {
276 85 : switch (kind) {
277 : case FunctionKind::kAsyncConciseGeneratorMethod:
278 : case FunctionKind::kAsyncGeneratorFunction:
279 : return true;
280 : default:
281 0 : return false;
282 : }
283 : }
284 26729 : TEST_FUNCTION_KIND(IsAsyncGeneratorFunction)
285 :
286 0 : bool FunctionKindIsGeneratorFunction(FunctionKind kind) {
287 85 : switch (kind) {
288 : case FunctionKind::kConciseGeneratorMethod:
289 : case FunctionKind::kAsyncConciseGeneratorMethod:
290 : case FunctionKind::kGeneratorFunction:
291 : case FunctionKind::kAsyncGeneratorFunction:
292 : return true;
293 : default:
294 0 : return false;
295 : }
296 : }
297 26729 : TEST_FUNCTION_KIND(IsGeneratorFunction)
298 :
299 0 : bool FunctionKindIsAsyncFunction(FunctionKind kind) {
300 85 : switch (kind) {
301 : case FunctionKind::kAsyncFunction:
302 : case FunctionKind::kAsyncArrowFunction:
303 : case FunctionKind::kAsyncConciseMethod:
304 : case FunctionKind::kAsyncConciseGeneratorMethod:
305 : case FunctionKind::kAsyncGeneratorFunction:
306 : return true;
307 : default:
308 0 : return false;
309 : }
310 : }
311 26729 : TEST_FUNCTION_KIND(IsAsyncFunction)
312 :
313 0 : bool FunctionKindIsConciseMethod(FunctionKind kind) {
314 : switch (kind) {
315 : case FunctionKind::kConciseMethod:
316 : case FunctionKind::kConciseGeneratorMethod:
317 : case FunctionKind::kAsyncConciseMethod:
318 : case FunctionKind::kAsyncConciseGeneratorMethod:
319 : case FunctionKind::kClassMembersInitializerFunction:
320 : return true;
321 : default:
322 0 : return false;
323 : }
324 : }
325 26729 : TEST_FUNCTION_KIND(IsConciseMethod)
326 :
327 0 : bool FunctionKindIsAccessorFunction(FunctionKind kind) {
328 85 : switch (kind) {
329 : case FunctionKind::kGetterFunction:
330 : case FunctionKind::kSetterFunction:
331 : return true;
332 : default:
333 0 : return false;
334 : }
335 : }
336 26729 : TEST_FUNCTION_KIND(IsAccessorFunction)
337 :
338 0 : bool FunctionKindIsDefaultConstructor(FunctionKind kind) {
339 85 : switch (kind) {
340 : case FunctionKind::kDefaultBaseConstructor:
341 : case FunctionKind::kDefaultDerivedConstructor:
342 : return true;
343 : default:
344 0 : return false;
345 : }
346 : }
347 26729 : TEST_FUNCTION_KIND(IsDefaultConstructor)
348 :
349 0 : bool FunctionKindIsBaseConstructor(FunctionKind kind) {
350 85 : switch (kind) {
351 : case FunctionKind::kBaseConstructor:
352 : case FunctionKind::kDefaultBaseConstructor:
353 : return true;
354 : default:
355 0 : return false;
356 : }
357 : }
358 26729 : TEST_FUNCTION_KIND(IsBaseConstructor)
359 :
360 0 : bool FunctionKindIsDerivedConstructor(FunctionKind kind) {
361 85 : switch (kind) {
362 : case FunctionKind::kDefaultDerivedConstructor:
363 : case FunctionKind::kDerivedConstructor:
364 : return true;
365 : default:
366 0 : return false;
367 : }
368 : }
369 26729 : TEST_FUNCTION_KIND(IsDerivedConstructor)
370 :
371 0 : bool FunctionKindIsClassConstructor(FunctionKind kind) {
372 85 : switch (kind) {
373 : case FunctionKind::kBaseConstructor:
374 : case FunctionKind::kDefaultBaseConstructor:
375 : case FunctionKind::kDefaultDerivedConstructor:
376 : case FunctionKind::kDerivedConstructor:
377 : return true;
378 : default:
379 0 : return false;
380 : }
381 : }
382 26729 : TEST_FUNCTION_KIND(IsClassConstructor)
383 :
384 0 : bool FunctionKindIsConstructable(FunctionKind kind) {
385 85 : switch (kind) {
386 : case FunctionKind::kGetterFunction:
387 : case FunctionKind::kSetterFunction:
388 : case FunctionKind::kArrowFunction:
389 : case FunctionKind::kAsyncArrowFunction:
390 : case FunctionKind::kAsyncFunction:
391 : case FunctionKind::kAsyncConciseMethod:
392 : case FunctionKind::kAsyncConciseGeneratorMethod:
393 : case FunctionKind::kAsyncGeneratorFunction:
394 : case FunctionKind::kGeneratorFunction:
395 : case FunctionKind::kConciseGeneratorMethod:
396 : case FunctionKind::kConciseMethod:
397 : case FunctionKind::kClassMembersInitializerFunction:
398 : return false;
399 : default:
400 0 : return true;
401 : }
402 : }
403 26729 : TEST_FUNCTION_KIND(IsConstructable)
404 :
405 0 : bool FunctionKindIsStrictFunctionWithoutPrototype(FunctionKind kind) {
406 160 : return IsArrowFunction(kind) || IsConciseMethod(kind) ||
407 0 : IsAccessorFunction(kind);
408 : }
409 26729 : TEST_FUNCTION_KIND(IsStrictFunctionWithoutPrototype)
410 :
411 : #undef TEST_FUNCTION_KIND
412 :
413 : } // namespace internal
414 79917 : } // namespace v8
|