• 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/runtime/runtime-utils.h"
6 
7 #include "src/arguments.h"
8 #include "src/code-stubs.h"
9 #include "src/conversions-inl.h"
10 #include "src/elements.h"
11 #include "src/factory.h"
12 #include "src/isolate-inl.h"
13 #include "src/keys.h"
14 #include "src/messages.h"
15 #include "src/prototype.h"
16 
17 namespace v8 {
18 namespace internal {
19 
RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup)20 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
21   HandleScope scope(isolate);
22   DCHECK(args.length() == 1);
23   CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0);
24   Object* length = prototype->length();
25   CHECK(length->IsSmi());
26   CHECK(Smi::cast(length)->value() == 0);
27   CHECK(prototype->HasFastSmiOrObjectElements());
28   // This is necessary to enable fast checks for absence of elements
29   // on Array.prototype and below.
30   prototype->set_elements(isolate->heap()->empty_fixed_array());
31   return Smi::FromInt(0);
32 }
33 
InstallCode(Isolate * isolate,Handle<JSObject> holder,const char * name,Handle<Code> code)34 static void InstallCode(Isolate* isolate, Handle<JSObject> holder,
35                         const char* name, Handle<Code> code) {
36   Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
37   Handle<JSFunction> optimized =
38       isolate->factory()->NewFunctionWithoutPrototype(key, code);
39   optimized->shared()->DontAdaptArguments();
40   JSObject::AddProperty(holder, key, optimized, NONE);
41 }
42 
InstallBuiltin(Isolate * isolate,Handle<JSObject> holder,const char * name,Builtins::Name builtin_name)43 static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder,
44                            const char* name, Builtins::Name builtin_name) {
45   InstallCode(isolate, holder, name,
46               handle(isolate->builtins()->builtin(builtin_name), isolate));
47 }
48 
RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions)49 RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
50   HandleScope scope(isolate);
51   DCHECK(args.length() == 0);
52   Handle<JSObject> holder =
53       isolate->factory()->NewJSObject(isolate->object_function());
54 
55   InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
56   FastArrayPushStub stub(isolate);
57   InstallCode(isolate, holder, "push", stub.GetCode());
58   InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
59   InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
60   InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
61   InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
62 
63   return *holder;
64 }
65 
66 
RUNTIME_FUNCTION(Runtime_FixedArrayGet)67 RUNTIME_FUNCTION(Runtime_FixedArrayGet) {
68   SealHandleScope shs(isolate);
69   DCHECK(args.length() == 2);
70   CONVERT_ARG_CHECKED(FixedArray, object, 0);
71   CONVERT_SMI_ARG_CHECKED(index, 1);
72   return object->get(index);
73 }
74 
75 
RUNTIME_FUNCTION(Runtime_FixedArraySet)76 RUNTIME_FUNCTION(Runtime_FixedArraySet) {
77   SealHandleScope shs(isolate);
78   DCHECK(args.length() == 3);
79   CONVERT_ARG_CHECKED(FixedArray, object, 0);
80   CONVERT_SMI_ARG_CHECKED(index, 1);
81   CONVERT_ARG_CHECKED(Object, value, 2);
82   object->set(index, value);
83   return isolate->heap()->undefined_value();
84 }
85 
86 
RUNTIME_FUNCTION(Runtime_TransitionElementsKind)87 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
88   HandleScope scope(isolate);
89   DCHECK(args.length() == 2);
90   CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
91   CONVERT_ARG_HANDLE_CHECKED(Map, map, 1);
92   JSObject::TransitionElementsKind(array, map->elements_kind());
93   return *array;
94 }
95 
96 
97 // Moves all own elements of an object, that are below a limit, to positions
98 // starting at zero. All undefined values are placed after non-undefined values,
99 // and are followed by non-existing element. Does not change the length
100 // property.
101 // Returns the number of non-undefined elements collected.
102 // Returns -1 if hole removal is not supported by this method.
RUNTIME_FUNCTION(Runtime_RemoveArrayHoles)103 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
104   HandleScope scope(isolate);
105   DCHECK(args.length() == 2);
106   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, object, 0);
107   CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
108   if (object->IsJSProxy()) return Smi::FromInt(-1);
109   return *JSObject::PrepareElementsForSort(Handle<JSObject>::cast(object),
110                                            limit);
111 }
112 
113 
114 // Move contents of argument 0 (an array) to argument 1 (an array)
RUNTIME_FUNCTION(Runtime_MoveArrayContents)115 RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
116   HandleScope scope(isolate);
117   DCHECK(args.length() == 2);
118   CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
119   CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
120   JSObject::ValidateElements(from);
121   JSObject::ValidateElements(to);
122 
123   Handle<FixedArrayBase> new_elements(from->elements());
124   ElementsKind from_kind = from->GetElementsKind();
125   Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
126   JSObject::SetMapAndElements(to, new_map, new_elements);
127   to->set_length(from->length());
128 
129   JSObject::ResetElements(from);
130   from->set_length(Smi::FromInt(0));
131 
132   JSObject::ValidateElements(to);
133   return *to;
134 }
135 
136 
137 // How many elements does this object/array have?
RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements)138 RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
139   HandleScope scope(isolate);
140   DCHECK(args.length() == 1);
141   CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
142   Handle<FixedArrayBase> elements(array->elements(), isolate);
143   SealHandleScope shs(isolate);
144   if (elements->IsDictionary()) {
145     int result =
146         Handle<SeededNumberDictionary>::cast(elements)->NumberOfElements();
147     return Smi::FromInt(result);
148   } else {
149     DCHECK(array->length()->IsSmi());
150     // For packed elements, we know the exact number of elements
151     int length = elements->length();
152     ElementsKind kind = array->GetElementsKind();
153     if (IsFastPackedElementsKind(kind)) {
154       return Smi::FromInt(length);
155     }
156     // For holey elements, take samples from the buffer checking for holes
157     // to generate the estimate.
158     const int kNumberOfHoleCheckSamples = 97;
159     int increment = (length < kNumberOfHoleCheckSamples)
160                         ? 1
161                         : static_cast<int>(length / kNumberOfHoleCheckSamples);
162     ElementsAccessor* accessor = array->GetElementsAccessor();
163     int holes = 0;
164     for (int i = 0; i < length; i += increment) {
165       if (!accessor->HasElement(array, i, elements)) {
166         ++holes;
167       }
168     }
169     int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
170                                     kNumberOfHoleCheckSamples * length);
171     return Smi::FromInt(estimate);
172   }
173 }
174 
175 
176 // Returns an array that tells you where in the [0, length) interval an array
177 // might have elements.  Can either return an array of keys (positive integers
178 // or undefined) or a number representing the positive length of an interval
179 // starting at index 0.
180 // Intervals can span over some keys that are not in the object.
RUNTIME_FUNCTION(Runtime_GetArrayKeys)181 RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
182   HandleScope scope(isolate);
183   DCHECK(args.length() == 2);
184   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
185   CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
186   ElementsKind kind = array->GetElementsKind();
187 
188   if (IsFastElementsKind(kind) || IsFixedTypedArrayElementsKind(kind)) {
189     uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
190     return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
191   }
192 
193   if (kind == FAST_STRING_WRAPPER_ELEMENTS) {
194     int string_length =
195         String::cast(Handle<JSValue>::cast(array)->value())->length();
196     int backing_store_length = array->elements()->length();
197     return *isolate->factory()->NewNumberFromUint(
198         Min(length,
199             static_cast<uint32_t>(Max(string_length, backing_store_length))));
200   }
201 
202   KeyAccumulator accumulator(isolate, KeyCollectionMode::kOwnOnly,
203                              ALL_PROPERTIES);
204   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
205        !iter.IsAtEnd(); iter.Advance()) {
206     if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() ||
207         PrototypeIterator::GetCurrent<JSObject>(iter)
208             ->HasIndexedInterceptor()) {
209       // Bail out if we find a proxy or interceptor, likely not worth
210       // collecting keys in that case.
211       return *isolate->factory()->NewNumberFromUint(length);
212     }
213     Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
214     accumulator.CollectOwnElementIndices(array, current);
215   }
216   // Erase any keys >= length.
217   Handle<FixedArray> keys =
218       accumulator.GetKeys(GetKeysConversion::kKeepNumbers);
219   int j = 0;
220   for (int i = 0; i < keys->length(); i++) {
221     if (NumberToUint32(keys->get(i)) >= length) continue;
222     if (i != j) keys->set(j, keys->get(i));
223     j++;
224   }
225 
226   if (j != keys->length()) {
227     isolate->heap()->RightTrimFixedArray<Heap::CONCURRENT_TO_SWEEPER>(
228         *keys, keys->length() - j);
229   }
230 
231   return *isolate->factory()->NewJSArrayWithElements(keys);
232 }
233 
234 
235 namespace {
236 
ArrayConstructorCommon(Isolate * isolate,Handle<JSFunction> constructor,Handle<JSReceiver> new_target,Handle<AllocationSite> site,Arguments * caller_args)237 Object* ArrayConstructorCommon(Isolate* isolate, Handle<JSFunction> constructor,
238                                Handle<JSReceiver> new_target,
239                                Handle<AllocationSite> site,
240                                Arguments* caller_args) {
241   Factory* factory = isolate->factory();
242 
243   // If called through new, new.target can be:
244   // - a subclass of constructor,
245   // - a proxy wrapper around constructor, or
246   // - the constructor itself.
247   // If called through Reflect.construct, it's guaranteed to be a constructor by
248   // REFLECT_CONSTRUCT_PREPARE.
249   DCHECK(new_target->IsConstructor());
250 
251   bool holey = false;
252   bool can_use_type_feedback = !site.is_null();
253   bool can_inline_array_constructor = true;
254   if (caller_args->length() == 1) {
255     Handle<Object> argument_one = caller_args->at<Object>(0);
256     if (argument_one->IsSmi()) {
257       int value = Handle<Smi>::cast(argument_one)->value();
258       if (value < 0 ||
259           JSArray::SetLengthWouldNormalize(isolate->heap(), value)) {
260         // the array is a dictionary in this case.
261         can_use_type_feedback = false;
262       } else if (value != 0) {
263         holey = true;
264         if (value >= JSArray::kInitialMaxFastElementArray) {
265           can_inline_array_constructor = false;
266         }
267       }
268     } else {
269       // Non-smi length argument produces a dictionary
270       can_use_type_feedback = false;
271     }
272   }
273 
274   Handle<Map> initial_map;
275   ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
276       isolate, initial_map,
277       JSFunction::GetDerivedMap(isolate, constructor, new_target));
278 
279   ElementsKind to_kind = can_use_type_feedback ? site->GetElementsKind()
280                                                : initial_map->elements_kind();
281   if (holey && !IsFastHoleyElementsKind(to_kind)) {
282     to_kind = GetHoleyElementsKind(to_kind);
283     // Update the allocation site info to reflect the advice alteration.
284     if (!site.is_null()) site->SetElementsKind(to_kind);
285   }
286 
287   // We should allocate with an initial map that reflects the allocation site
288   // advice. Therefore we use AllocateJSObjectFromMap instead of passing
289   // the constructor.
290   if (to_kind != initial_map->elements_kind()) {
291     initial_map = Map::AsElementsKind(initial_map, to_kind);
292   }
293 
294   // If we don't care to track arrays of to_kind ElementsKind, then
295   // don't emit a memento for them.
296   Handle<AllocationSite> allocation_site;
297   if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
298     allocation_site = site;
299   }
300 
301   Handle<JSArray> array = Handle<JSArray>::cast(
302       factory->NewJSObjectFromMap(initial_map, NOT_TENURED, allocation_site));
303 
304   factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
305 
306   ElementsKind old_kind = array->GetElementsKind();
307   RETURN_FAILURE_ON_EXCEPTION(
308       isolate, ArrayConstructInitializeElements(array, caller_args));
309   if (!site.is_null() &&
310       (old_kind != array->GetElementsKind() || !can_use_type_feedback ||
311        !can_inline_array_constructor)) {
312     // The arguments passed in caused a transition. This kind of complexity
313     // can't be dealt with in the inlined hydrogen array constructor case.
314     // We must mark the allocationsite as un-inlinable.
315     site->SetDoNotInlineCall();
316   }
317 
318   return *array;
319 }
320 
321 }  // namespace
322 
RUNTIME_FUNCTION(Runtime_NewArray)323 RUNTIME_FUNCTION(Runtime_NewArray) {
324   HandleScope scope(isolate);
325   DCHECK_LE(3, args.length());
326   int const argc = args.length() - 3;
327   // TODO(bmeurer): Remove this Arguments nonsense.
328   Arguments argv(argc, args.arguments() - 1);
329   CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, 0);
330   CONVERT_ARG_HANDLE_CHECKED(JSReceiver, new_target, argc + 1);
331   CONVERT_ARG_HANDLE_CHECKED(HeapObject, type_info, argc + 2);
332   // TODO(bmeurer): Use MaybeHandle to pass around the AllocationSite.
333   Handle<AllocationSite> site = type_info->IsAllocationSite()
334                                     ? Handle<AllocationSite>::cast(type_info)
335                                     : Handle<AllocationSite>::null();
336   return ArrayConstructorCommon(isolate, constructor, new_target, site, &argv);
337 }
338 
RUNTIME_FUNCTION(Runtime_NormalizeElements)339 RUNTIME_FUNCTION(Runtime_NormalizeElements) {
340   HandleScope scope(isolate);
341   DCHECK(args.length() == 1);
342   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
343   CHECK(!array->HasFixedTypedArrayElements());
344   CHECK(!array->IsJSGlobalProxy());
345   JSObject::NormalizeElements(array);
346   return *array;
347 }
348 
349 
350 // GrowArrayElements returns a sentinel Smi if the object was normalized.
RUNTIME_FUNCTION(Runtime_GrowArrayElements)351 RUNTIME_FUNCTION(Runtime_GrowArrayElements) {
352   HandleScope scope(isolate);
353   DCHECK(args.length() == 2);
354   CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
355   CONVERT_NUMBER_CHECKED(int, key, Int32, args[1]);
356 
357   if (key < 0) {
358     return object->elements();
359   }
360 
361   uint32_t capacity = static_cast<uint32_t>(object->elements()->length());
362   uint32_t index = static_cast<uint32_t>(key);
363 
364   if (index >= capacity) {
365     if (object->WouldConvertToSlowElements(index)) {
366       // We don't want to allow operations that cause lazy deopt. Return a Smi
367       // as a signal that optimized code should eagerly deoptimize.
368       return Smi::FromInt(0);
369     }
370 
371     uint32_t new_capacity = JSObject::NewElementsCapacity(index + 1);
372     object->GetElementsAccessor()->GrowCapacityAndConvert(object, new_capacity);
373   }
374 
375   // On success, return the fixed array elements.
376   return object->elements();
377 }
378 
379 
RUNTIME_FUNCTION(Runtime_HasComplexElements)380 RUNTIME_FUNCTION(Runtime_HasComplexElements) {
381   HandleScope scope(isolate);
382   DCHECK(args.length() == 1);
383   CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
384   for (PrototypeIterator iter(isolate, array, kStartAtReceiver);
385        !iter.IsAtEnd(); iter.Advance()) {
386     if (PrototypeIterator::GetCurrent(iter)->IsJSProxy()) {
387       return isolate->heap()->true_value();
388     }
389     Handle<JSObject> current = PrototypeIterator::GetCurrent<JSObject>(iter);
390     if (current->HasIndexedInterceptor()) {
391       return isolate->heap()->true_value();
392     }
393     if (!current->HasDictionaryElements()) continue;
394     if (current->element_dictionary()->HasComplexElements()) {
395       return isolate->heap()->true_value();
396     }
397   }
398   return isolate->heap()->false_value();
399 }
400 
401 // ES6 22.1.2.2 Array.isArray
RUNTIME_FUNCTION(Runtime_ArrayIsArray)402 RUNTIME_FUNCTION(Runtime_ArrayIsArray) {
403   HandleScope shs(isolate);
404   DCHECK(args.length() == 1);
405   CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
406   Maybe<bool> result = Object::IsArray(object);
407   MAYBE_RETURN(result, isolate->heap()->exception());
408   return isolate->heap()->ToBoolean(result.FromJust());
409 }
410 
RUNTIME_FUNCTION(Runtime_IsArray)411 RUNTIME_FUNCTION(Runtime_IsArray) {
412   SealHandleScope shs(isolate);
413   DCHECK(args.length() == 1);
414   CONVERT_ARG_CHECKED(Object, obj, 0);
415   return isolate->heap()->ToBoolean(obj->IsJSArray());
416 }
417 
RUNTIME_FUNCTION(Runtime_HasCachedArrayIndex)418 RUNTIME_FUNCTION(Runtime_HasCachedArrayIndex) {
419   SealHandleScope shs(isolate);
420   DCHECK(args.length() == 1);
421   return isolate->heap()->false_value();
422 }
423 
424 
RUNTIME_FUNCTION(Runtime_GetCachedArrayIndex)425 RUNTIME_FUNCTION(Runtime_GetCachedArrayIndex) {
426   // This can never be reached, because Runtime_HasCachedArrayIndex always
427   // returns false.
428   UNIMPLEMENTED();
429   return nullptr;
430 }
431 
432 
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor)433 RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
434   HandleScope scope(isolate);
435   DCHECK(args.length() == 1);
436   CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
437   RETURN_RESULT_OR_FAILURE(
438       isolate, Object::ArraySpeciesConstructor(isolate, original_array));
439 }
440 
441 }  // namespace internal
442 }  // namespace v8
443