1 // Copyright 2014 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/debug/debug.h"
6 #include "src/execution/arguments-inl.h"
7 #include "src/execution/isolate-inl.h"
8 #include "src/execution/protectors-inl.h"
9 #include "src/heap/factory.h"
10 #include "src/heap/heap-inl.h"  // For ToBoolean. TODO(jkummerow): Drop.
11 #include "src/heap/heap-write-barrier-inl.h"
12 #include "src/logging/counters.h"
13 #include "src/numbers/conversions-inl.h"
14 #include "src/objects/allocation-site-inl.h"
15 #include "src/objects/arguments-inl.h"
16 #include "src/objects/elements.h"
17 #include "src/objects/hash-table-inl.h"
18 #include "src/objects/js-array-inl.h"
19 #include "src/objects/prototype.h"
20 #include "src/runtime/runtime-utils.h"
21 
22 namespace v8 {
23 namespace internal {
24 
RUNTIME_FUNCTION(Runtime_TransitionElementsKind)25 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
26   HandleScope scope(isolate);
27   DCHECK_EQ(2, args.length());
28   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
29   CONVERT_ARG_HANDLE_CHECKED(Map, to_map, 1);
30   ElementsKind to_kind = to_map->elements_kind();
31   ElementsAccessor::ForKind(to_kind)->TransitionElementsKind(object, to_map);
32   return *object;
33 }
34 
RUNTIME_FUNCTION(Runtime_TransitionElementsKindWithKind)35 RUNTIME_FUNCTION(Runtime_TransitionElementsKindWithKind) {
36   HandleScope scope(isolate);
37   DCHECK_EQ(2, args.length());
38   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
39   CONVERT_ARG_HANDLE_CHECKED(Smi, elements_kind_smi, 1);
40   ElementsKind to_kind = static_cast<ElementsKind>(elements_kind_smi->value());
41   JSObject::TransitionElementsKind(object, to_kind);
42   return *object;
43 }
44 
RUNTIME_FUNCTION(Runtime_NewArray)45 RUNTIME_FUNCTION(Runtime_NewArray) {
46   HandleScope scope(isolate);
47   DCHECK_LE(3, args.length());
48   int const argc = args.length() - 3;
49   // argv points to the arguments constructed by the JavaScript call.
50 #ifdef V8_REVERSE_JSARGS
51   JavaScriptArguments argv(argc, args.address_of_arg_at(0));
52   CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, argc);
53 #else
54   JavaScriptArguments argv(argc, args.address_of_arg_at(1));
55   CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
56 #endif
57   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
58   CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
59   // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
60   Handle<AllocationSite> site = type_info->IsAllocationSite()
61                                     ? Handle<AllocationSite>::cast(type_info)
62                                     : Handle<AllocationSite>::null();
63 
64   Factory* factory = isolate->factory();
65 
66   // If called through new, new.target can be:
67   // - a subclass of constructor,
68   // - a proxy wrapper around constructor, or
69   // - the constructor itself.
70   // If called through Reflect.construct, it's guaranteed to be a constructor by
71   // REFLECT_CONSTRUCT_PREPARE.
72   DCHECK(new_target->IsConstructor());
73 
74   bool holey = false;
75   bool can_use_type_feedback = !site.is_null();
76   bool can_inline_array_constructor = true;
77   if (argv.length() == 1) {
78     Handle<Object> argument_one = argv.at<Object>(0);
79     if (argument_one->IsSmi()) {
80       int value = Handle<Smi>::cast(argument_one)->value();
81       if (value < 0 ||
82           JSArray::SetLengthWouldNormalize(isolate->heap(), value)) {
83         // the array is a dictionary in this case.
84         can_use_type_feedback = false;
85       } else if (value != 0) {
86         holey = true;
87         if (value >= JSArray::kInitialMaxFastElementArray) {
88           can_inline_array_constructor = false;
89         }
90       }
91     } else {
92       // Non-smi length argument produces a dictionary
93       can_use_type_feedback = false;
94     }
95   }
96 
97   Handle<Map> initial_map;
98   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
99       isolate, initial_map,
100       JSFunction::GetDerivedMap(isolate, constructor, new_target));
101 
102   ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
103                                                : initial_map->elements_kind();
104   if (holey && !IsHoleyElementsKind(to_kind)) {
105     to_kind = GetHoleyElementsKind(to_kind);
106     // Update the allocation site info to reflect the advice alteration.
107     if (!site.is_null()) site->SetElementsKind(to_kind);
108   }
109 
110   // We should allocate with an initial map that reflects the allocation site
111   // advice. Therefore we use AllocateJSObjectFromMap instead of passing
112   // the constructor.
113   initial_map = Map::AsElementsKind(isolate, initial_map, to_kind);
114 
115   // If we don't care to track arrays of to_kind ElementsKind, then
116   // don't emit a memento for them.
117   Handle<AllocationSite> allocation_site;
118   if (AllocationSite::ShouldTrack(to_kind)) {
119     allocation_site = site;
120   }
121 
122   Handle<JSArray> array = Handle<JSArray>::cast(factory->NewJSObjectFromMap(
123       initial_map, AllocationType::kYoung, allocation_site));
124 
125   factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
126 
127   ElementsKind old_kind = array->GetElementsKind();
128   RETURN_FAILURE_ON_EXCEPTION(isolate,
129                               ArrayConstructInitializeElements(array, &argv));
130   if (!site.is_null()) {
131     if ((old_kind != array->GetElementsKind() || !can_use_type_feedback ||
132          !can_inline_array_constructor)) {
133       // The arguments passed in caused a transition. This kind of complexity
134       // can't be dealt with in the inlined optimized array constructor case.
135       // We must mark the allocationsite as un-inlinable.
136       site->SetDoNotInlineCall();
137     }
138   } else {
139     if (old_kind != array->GetElementsKind() || !can_inline_array_constructor) {
140       // We don't have an AllocationSite for this Array constructor invocation,
141       // i.e. it might a call from Array#map or from an Array subclass, so we
142       // just flip the bit on the global protector cell instead.
143       // TODO(bmeurer): Find a better way to mark this. Global protectors
144       // tend to back-fire over time...
145       if (Protectors::IsArrayConstructorIntact(isolate)) {
146         Protectors::InvalidateArrayConstructor(isolate);
147       }
148     }
149   }
150 
151   return *array;
152 }
153 
RUNTIME_FUNCTION(Runtime_NormalizeElements)154 RUNTIME_FUNCTION(Runtime_NormalizeElements) {
155   HandleScope scope(isolate);
156   DCHECK_EQ(1, args.length());
157   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
158   CHECK(!array->HasTypedArrayElements());
159   CHECK(!array->IsJSGlobalProxy());
160   JSObject::NormalizeElements(array);
161   return *array;
162 }
163 
164 // GrowArrayElements returns a sentinel Smi if the object was normalized or if
165 // the key is negative.
RUNTIME_FUNCTION(Runtime_GrowArrayElements)166 RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
167   HandleScope scope(isolate);
168   DCHECK_EQ(2, args.length());
169   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
170   CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
171   uint32_t index;
172   if (key->IsSmi()) {
173     int value = Smi::ToInt(*key);
174     if (value < 0) return Smi::zero();
175     index = static_cast<uint32_t>(value);
176   } else {
177     CHECK(key->IsHeapNumber());
178     double value = HeapNumber::cast(*key).value();
179     if (value < 0 || value > std::numeric_limits<uint32_t>::max()) {
180       return Smi::zero();
181     }
182     index = static_cast<uint32_t>(value);
183   }
184 
185   uint32_t capacity = static_cast<uint32_t>(object->elements().length());
186 
187   if (index >= capacity) {
188     if (!object->GetElementsAccessor()->GrowCapacity(object, index)) {
189       return Smi::zero();
190     }
191   }
192 
193   return object->elements();
194 }
195 
196 // ES6 22.1.2.2 Array.isArray
RUNTIME_FUNCTION(Runtime_ArrayIsArray)197 RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
198   HandleScope shs(isolate);
199   DCHECK_EQ(1, args.length());
200   CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
201   Maybe<bool> result = Object::IsArray(object);
202   MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
203   return isolate->heap()->ToBoolean(result.FromJust());
204 }
205 
RUNTIME_FUNCTION(Runtime_IsArray)206 RUNTIME_FUNCTION(Runtime_IsArray) {
207   SealHandleScope shs(isolate);
208   DCHECK_EQ(1, args.length());
209   CONVERT_ARG_CHECKED(Object, obj, 0);
210   return isolate->heap()->ToBoolean(obj.IsJSArray());
211 }
212 
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor)213 RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
214   HandleScope scope(isolate);
215   DCHECK_EQ(1, args.length());
216   CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
217   RETURN_RESULT_OR_FAILURE(
218       isolate, Object::ArraySpeciesConstructor(isolate, original_array));
219 }
220 
221 // ES7 22.1.3.11 Array.prototype.includes
RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow)222 RUNTIME_FUNCTION(Runtime_ArrayIncludes_Slow) {
223   HandleScope shs(isolate);
224   DCHECK_EQ(3, args.length());
225   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
226   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
227 
228   // Let O be ? ToObject(this value).
229   Handle<JSReceiver> object;
230   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
231       isolate, object,
232       Object::ToObject(isolate, Handle<Object>(args[0], isolate)));
233 
234   // Let len be ? ToLength(? Get(O, "length")).
235   int64_t len;
236   {
237     if (object->map().instance_type() == JS_ARRAY_TYPE) {
238       uint32_t len32 = 0;
239       bool success = JSArray::cast(*object).length().ToArrayLength(&len32);
240       DCHECK(success);
241       USE(success);
242       len = len32;
243     } else {
244       Handle<Object> len_;
245       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
246           isolate, len_,
247           Object::GetProperty(isolate, object,
248                               isolate->factory()->length_string()));
249 
250       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
251                                          Object::ToLength(isolate, len_));
252       len = static_cast<int64_t>(len_->Number());
253       DCHECK_EQ(len, len_->Number());
254     }
255   }
256 
257   if (len == 0) return ReadOnlyRoots(isolate).false_value();
258 
259   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
260   // produces the value 0.)
261   int64_t index = 0;
262   if (!from_index->IsUndefined(isolate)) {
263     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
264                                        Object::ToInteger(isolate, from_index));
265 
266     if (V8_LIKELY(from_index->IsSmi())) {
267       int start_from = Smi::ToInt(*from_index);
268       if (start_from < 0) {
269         index = std::max<int64_t>(len + start_from, 0);
270       } else {
271         index = start_from;
272       }
273     } else {
274       DCHECK(from_index->IsHeapNumber());
275       double start_from = from_index->Number();
276       if (start_from >= len) return ReadOnlyRoots(isolate).false_value();
277       if (V8_LIKELY(std::isfinite(start_from))) {
278         if (start_from < 0) {
279           index = static_cast<int64_t>(std::max<double>(start_from + len, 0));
280         } else {
281           index = start_from;
282         }
283       }
284     }
285 
286     DCHECK_GE(index, 0);
287   }
288 
289   // If the receiver is not a special receiver type, and the length is a valid
290   // element index, perform fast operation tailored to specific ElementsKinds.
291   if (!object->map().IsSpecialReceiverMap() &&
292       len <= JSObject::kMaxElementCount &&
293       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
294     Handle<JSObject> obj = Handle<JSObject>::cast(object);
295     ElementsAccessor* elements = obj->GetElementsAccessor();
296     Maybe<bool> result =
297         elements->IncludesValue(isolate, obj, search_element, index, len);
298     MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
299     return *isolate->factory()->ToBoolean(result.FromJust());
300   }
301 
302   // Otherwise, perform slow lookups for special receiver types.
303   for (; index < len; ++index) {
304     HandleScope iteration_hs(isolate);
305 
306     // Let elementK be the result of ? Get(O, ! ToString(k)).
307     Handle<Object> element_k;
308     {
309       LookupIterator::Key key(isolate, static_cast<double>(index));
310       LookupIterator it(isolate, object, key);
311       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
312                                          Object::GetProperty(&it));
313     }
314 
315     // If SameValueZero(searchElement, elementK) is true, return true.
316     if (search_element->SameValueZero(*element_k)) {
317       return ReadOnlyRoots(isolate).true_value();
318     }
319   }
320   return ReadOnlyRoots(isolate).false_value();
321 }
322 
RUNTIME_FUNCTION(Runtime_ArrayIndexOf)323 RUNTIME_FUNCTION(Runtime_ArrayIndexOf) {
324   HandleScope hs(isolate);
325   DCHECK_EQ(3, args.length());
326   CONVERT_ARG_HANDLE_CHECKED(Object, search_element, 1);
327   CONVERT_ARG_HANDLE_CHECKED(Object, from_index, 2);
328 
329   // Let O be ? ToObject(this value).
330   Handle<JSReceiver> object;
331   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
332       isolate, object,
333       Object::ToObject(isolate, args.at(0), "Array.prototype.indexOf"));
334 
335   // Let len be ? ToLength(? Get(O, "length")).
336   int64_t len;
337   {
338     if (object->IsJSArray()) {
339       uint32_t len32 = 0;
340       bool success = JSArray::cast(*object).length().ToArrayLength(&len32);
341       DCHECK(success);
342       USE(success);
343       len = len32;
344     } else {
345       Handle<Object> len_;
346       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
347           isolate, len_,
348           Object::GetProperty(isolate, object,
349                               isolate->factory()->length_string()));
350 
351       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len_,
352                                          Object::ToLength(isolate, len_));
353       len = static_cast<int64_t>(len_->Number());
354       DCHECK_EQ(len, len_->Number());
355     }
356   }
357 
358   if (len == 0) return Smi::FromInt(-1);
359 
360   // Let n be ? ToInteger(fromIndex). (If fromIndex is undefined, this step
361   // produces the value 0.)
362   int64_t start_from;
363   {
364     ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, from_index,
365                                        Object::ToInteger(isolate, from_index));
366     double fp = from_index->Number();
367     if (fp > len) return Smi::FromInt(-1);
368     if (V8_LIKELY(fp >=
369                   static_cast<double>(std::numeric_limits<int64_t>::min()))) {
370       DCHECK(fp < std::numeric_limits<int64_t>::max());
371       start_from = static_cast<int64_t>(fp);
372     } else {
373       start_from = std::numeric_limits<int64_t>::min();
374     }
375   }
376 
377   int64_t index;
378   if (start_from >= 0) {
379     index = start_from;
380   } else {
381     index = len + start_from;
382     if (index < 0) {
383       index = 0;
384     }
385   }
386 
387   // If the receiver is not a special receiver type, and the length fits
388   // uint32_t, perform fast operation tailored to specific ElementsKinds.
389   if (!object->map().IsSpecialReceiverMap() && len <= kMaxUInt32 &&
390       JSObject::PrototypeHasNoElements(isolate, JSObject::cast(*object))) {
391     Handle<JSObject> obj = Handle<JSObject>::cast(object);
392     ElementsAccessor* elements = obj->GetElementsAccessor();
393     Maybe<int64_t> result = elements->IndexOfValue(isolate, obj, search_element,
394                                                    static_cast<uint32_t>(index),
395                                                    static_cast<uint32_t>(len));
396     MAYBE_RETURN(result, ReadOnlyRoots(isolate).exception());
397     return *isolate->factory()->NewNumberFromInt64(result.FromJust());
398   }
399 
400   // Otherwise, perform slow lookups for special receiver types.
401   for (; index < len; ++index) {
402     HandleScope iteration_hs(isolate);
403     // Let elementK be the result of ? Get(O, ! ToString(k)).
404     Handle<Object> element_k;
405     {
406       LookupIterator::Key key(isolate, static_cast<double>(index));
407       LookupIterator it(isolate, object, key);
408       Maybe<bool> present = JSReceiver::HasProperty(&it);
409       MAYBE_RETURN(present, ReadOnlyRoots(isolate).exception());
410       if (!present.FromJust()) continue;
411       ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, element_k,
412                                          Object::GetProperty(&it));
413       if (search_element->StrictEquals(*element_k)) {
414         return *isolate->factory()->NewNumberFromInt64(index);
415       }
416     }
417   }
418   return Smi::FromInt(-1);
419 }
420 
421 }  // namespace internal
422 }  // namespace v8
423