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