Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Side by Side Diff: src/runtime/runtime-array.cc

Issue 638423003: Split off remaining runtime functions in runtime.cc. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/runtime/runtime-api.cc ('k') | src/runtime/runtime-debug.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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/v8.h"
6
7 #include "src/arguments.h"
8 #include "src/runtime/runtime.h"
9 #include "src/runtime/runtime-utils.h"
10
11 namespace v8 {
12 namespace internal {
13
14 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) {
15 HandleScope scope(isolate);
16 DCHECK(args.length() == 1);
17 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0);
18 Object* length = prototype->length();
19 RUNTIME_ASSERT(length->IsSmi() && Smi::cast(length)->value() == 0);
20 RUNTIME_ASSERT(prototype->HasFastSmiOrObjectElements());
21 // This is necessary to enable fast checks for absence of elements
22 // on Array.prototype and below.
23 prototype->set_elements(isolate->heap()->empty_fixed_array());
24 return Smi::FromInt(0);
25 }
26
27
28 static void InstallBuiltin(Isolate* isolate, Handle<JSObject> holder,
29 const char* name, Builtins::Name builtin_name) {
30 Handle<String> key = isolate->factory()->InternalizeUtf8String(name);
31 Handle<Code> code(isolate->builtins()->builtin(builtin_name));
32 Handle<JSFunction> optimized =
33 isolate->factory()->NewFunctionWithoutPrototype(key, code);
34 optimized->shared()->DontAdaptArguments();
35 JSObject::AddProperty(holder, key, optimized, NONE);
36 }
37
38
39 RUNTIME_FUNCTION(Runtime_SpecialArrayFunctions) {
40 HandleScope scope(isolate);
41 DCHECK(args.length() == 0);
42 Handle<JSObject> holder =
43 isolate->factory()->NewJSObject(isolate->object_function());
44
45 InstallBuiltin(isolate, holder, "pop", Builtins::kArrayPop);
46 InstallBuiltin(isolate, holder, "push", Builtins::kArrayPush);
47 InstallBuiltin(isolate, holder, "shift", Builtins::kArrayShift);
48 InstallBuiltin(isolate, holder, "unshift", Builtins::kArrayUnshift);
49 InstallBuiltin(isolate, holder, "slice", Builtins::kArraySlice);
50 InstallBuiltin(isolate, holder, "splice", Builtins::kArraySplice);
51 InstallBuiltin(isolate, holder, "concat", Builtins::kArrayConcat);
52
53 return *holder;
54 }
55
56
57 RUNTIME_FUNCTION(Runtime_TransitionElementsKind) {
58 HandleScope scope(isolate);
59 RUNTIME_ASSERT(args.length() == 2);
60 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
61 CONVERT_ARG_HANDLE_CHECKED(Map, map, 1);
62 JSObject::TransitionElementsKind(array, map->elements_kind());
63 return *array;
64 }
65
66
67 // Push an object unto an array of objects if it is not already in the
68 // array. Returns true if the element was pushed on the stack and
69 // false otherwise.
70 RUNTIME_FUNCTION(Runtime_PushIfAbsent) {
71 HandleScope scope(isolate);
72 DCHECK(args.length() == 2);
73 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
74 CONVERT_ARG_HANDLE_CHECKED(JSReceiver, element, 1);
75 RUNTIME_ASSERT(array->HasFastSmiOrObjectElements());
76 int length = Smi::cast(array->length())->value();
77 FixedArray* elements = FixedArray::cast(array->elements());
78 for (int i = 0; i < length; i++) {
79 if (elements->get(i) == *element) return isolate->heap()->false_value();
80 }
81
82 // Strict not needed. Used for cycle detection in Array join implementation.
83 RETURN_FAILURE_ON_EXCEPTION(
84 isolate, JSObject::SetFastElement(array, length, element, SLOPPY, true));
85 return isolate->heap()->true_value();
86 }
87
88
89 /**
90 * A simple visitor visits every element of Array's.
91 * The backend storage can be a fixed array for fast elements case,
92 * or a dictionary for sparse array. Since Dictionary is a subtype
93 * of FixedArray, the class can be used by both fast and slow cases.
94 * The second parameter of the constructor, fast_elements, specifies
95 * whether the storage is a FixedArray or Dictionary.
96 *
97 * An index limit is used to deal with the situation that a result array
98 * length overflows 32-bit non-negative integer.
99 */
100 class ArrayConcatVisitor {
101 public:
102 ArrayConcatVisitor(Isolate* isolate, Handle<FixedArray> storage,
103 bool fast_elements)
104 : isolate_(isolate),
105 storage_(Handle<FixedArray>::cast(
106 isolate->global_handles()->Create(*storage))),
107 index_offset_(0u),
108 fast_elements_(fast_elements),
109 exceeds_array_limit_(false) {}
110
111 ~ArrayConcatVisitor() { clear_storage(); }
112
113 void visit(uint32_t i, Handle<Object> elm) {
114 if (i > JSObject::kMaxElementCount - index_offset_) {
115 exceeds_array_limit_ = true;
116 return;
117 }
118 uint32_t index = index_offset_ + i;
119
120 if (fast_elements_) {
121 if (index < static_cast<uint32_t>(storage_->length())) {
122 storage_->set(index, *elm);
123 return;
124 }
125 // Our initial estimate of length was foiled, possibly by
126 // getters on the arrays increasing the length of later arrays
127 // during iteration.
128 // This shouldn't happen in anything but pathological cases.
129 SetDictionaryMode();
130 // Fall-through to dictionary mode.
131 }
132 DCHECK(!fast_elements_);
133 Handle<SeededNumberDictionary> dict(
134 SeededNumberDictionary::cast(*storage_));
135 Handle<SeededNumberDictionary> result =
136 SeededNumberDictionary::AtNumberPut(dict, index, elm);
137 if (!result.is_identical_to(dict)) {
138 // Dictionary needed to grow.
139 clear_storage();
140 set_storage(*result);
141 }
142 }
143
144 void increase_index_offset(uint32_t delta) {
145 if (JSObject::kMaxElementCount - index_offset_ < delta) {
146 index_offset_ = JSObject::kMaxElementCount;
147 } else {
148 index_offset_ += delta;
149 }
150 // If the initial length estimate was off (see special case in visit()),
151 // but the array blowing the limit didn't contain elements beyond the
152 // provided-for index range, go to dictionary mode now.
153 if (fast_elements_ &&
154 index_offset_ >
155 static_cast<uint32_t>(FixedArrayBase::cast(*storage_)->length())) {
156 SetDictionaryMode();
157 }
158 }
159
160 bool exceeds_array_limit() { return exceeds_array_limit_; }
161
162 Handle<JSArray> ToArray() {
163 Handle<JSArray> array = isolate_->factory()->NewJSArray(0);
164 Handle<Object> length =
165 isolate_->factory()->NewNumber(static_cast<double>(index_offset_));
166 Handle<Map> map = JSObject::GetElementsTransitionMap(
167 array, fast_elements_ ? FAST_HOLEY_ELEMENTS : DICTIONARY_ELEMENTS);
168 array->set_map(*map);
169 array->set_length(*length);
170 array->set_elements(*storage_);
171 return array;
172 }
173
174 private:
175 // Convert storage to dictionary mode.
176 void SetDictionaryMode() {
177 DCHECK(fast_elements_);
178 Handle<FixedArray> current_storage(*storage_);
179 Handle<SeededNumberDictionary> slow_storage(
180 SeededNumberDictionary::New(isolate_, current_storage->length()));
181 uint32_t current_length = static_cast<uint32_t>(current_storage->length());
182 for (uint32_t i = 0; i < current_length; i++) {
183 HandleScope loop_scope(isolate_);
184 Handle<Object> element(current_storage->get(i), isolate_);
185 if (!element->IsTheHole()) {
186 Handle<SeededNumberDictionary> new_storage =
187 SeededNumberDictionary::AtNumberPut(slow_storage, i, element);
188 if (!new_storage.is_identical_to(slow_storage)) {
189 slow_storage = loop_scope.CloseAndEscape(new_storage);
190 }
191 }
192 }
193 clear_storage();
194 set_storage(*slow_storage);
195 fast_elements_ = false;
196 }
197
198 inline void clear_storage() {
199 GlobalHandles::Destroy(Handle<Object>::cast(storage_).location());
200 }
201
202 inline void set_storage(FixedArray* storage) {
203 storage_ =
204 Handle<FixedArray>::cast(isolate_->global_handles()->Create(storage));
205 }
206
207 Isolate* isolate_;
208 Handle<FixedArray> storage_; // Always a global handle.
209 // Index after last seen index. Always less than or equal to
210 // JSObject::kMaxElementCount.
211 uint32_t index_offset_;
212 bool fast_elements_ : 1;
213 bool exceeds_array_limit_ : 1;
214 };
215
216
217 static uint32_t EstimateElementCount(Handle<JSArray> array) {
218 uint32_t length = static_cast<uint32_t>(array->length()->Number());
219 int element_count = 0;
220 switch (array->GetElementsKind()) {
221 case FAST_SMI_ELEMENTS:
222 case FAST_HOLEY_SMI_ELEMENTS:
223 case FAST_ELEMENTS:
224 case FAST_HOLEY_ELEMENTS: {
225 // Fast elements can't have lengths that are not representable by
226 // a 32-bit signed integer.
227 DCHECK(static_cast<int32_t>(FixedArray::kMaxLength) >= 0);
228 int fast_length = static_cast<int>(length);
229 Handle<FixedArray> elements(FixedArray::cast(array->elements()));
230 for (int i = 0; i < fast_length; i++) {
231 if (!elements->get(i)->IsTheHole()) element_count++;
232 }
233 break;
234 }
235 case FAST_DOUBLE_ELEMENTS:
236 case FAST_HOLEY_DOUBLE_ELEMENTS: {
237 // Fast elements can't have lengths that are not representable by
238 // a 32-bit signed integer.
239 DCHECK(static_cast<int32_t>(FixedDoubleArray::kMaxLength) >= 0);
240 int fast_length = static_cast<int>(length);
241 if (array->elements()->IsFixedArray()) {
242 DCHECK(FixedArray::cast(array->elements())->length() == 0);
243 break;
244 }
245 Handle<FixedDoubleArray> elements(
246 FixedDoubleArray::cast(array->elements()));
247 for (int i = 0; i < fast_length; i++) {
248 if (!elements->is_the_hole(i)) element_count++;
249 }
250 break;
251 }
252 case DICTIONARY_ELEMENTS: {
253 Handle<SeededNumberDictionary> dictionary(
254 SeededNumberDictionary::cast(array->elements()));
255 int capacity = dictionary->Capacity();
256 for (int i = 0; i < capacity; i++) {
257 Handle<Object> key(dictionary->KeyAt(i), array->GetIsolate());
258 if (dictionary->IsKey(*key)) {
259 element_count++;
260 }
261 }
262 break;
263 }
264 case SLOPPY_ARGUMENTS_ELEMENTS:
265 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
266 case EXTERNAL_##TYPE##_ELEMENTS: \
267 case TYPE##_ELEMENTS:
268
269 TYPED_ARRAYS(TYPED_ARRAY_CASE)
270 #undef TYPED_ARRAY_CASE
271 // External arrays are always dense.
272 return length;
273 }
274 // As an estimate, we assume that the prototype doesn't contain any
275 // inherited elements.
276 return element_count;
277 }
278
279
280 template <class ExternalArrayClass, class ElementType>
281 static void IterateExternalArrayElements(Isolate* isolate,
282 Handle<JSObject> receiver,
283 bool elements_are_ints,
284 bool elements_are_guaranteed_smis,
285 ArrayConcatVisitor* visitor) {
286 Handle<ExternalArrayClass> array(
287 ExternalArrayClass::cast(receiver->elements()));
288 uint32_t len = static_cast<uint32_t>(array->length());
289
290 DCHECK(visitor != NULL);
291 if (elements_are_ints) {
292 if (elements_are_guaranteed_smis) {
293 for (uint32_t j = 0; j < len; j++) {
294 HandleScope loop_scope(isolate);
295 Handle<Smi> e(Smi::FromInt(static_cast<int>(array->get_scalar(j))),
296 isolate);
297 visitor->visit(j, e);
298 }
299 } else {
300 for (uint32_t j = 0; j < len; j++) {
301 HandleScope loop_scope(isolate);
302 int64_t val = static_cast<int64_t>(array->get_scalar(j));
303 if (Smi::IsValid(static_cast<intptr_t>(val))) {
304 Handle<Smi> e(Smi::FromInt(static_cast<int>(val)), isolate);
305 visitor->visit(j, e);
306 } else {
307 Handle<Object> e =
308 isolate->factory()->NewNumber(static_cast<ElementType>(val));
309 visitor->visit(j, e);
310 }
311 }
312 }
313 } else {
314 for (uint32_t j = 0; j < len; j++) {
315 HandleScope loop_scope(isolate);
316 Handle<Object> e = isolate->factory()->NewNumber(array->get_scalar(j));
317 visitor->visit(j, e);
318 }
319 }
320 }
321
322
323 // Used for sorting indices in a List<uint32_t>.
324 static int compareUInt32(const uint32_t* ap, const uint32_t* bp) {
325 uint32_t a = *ap;
326 uint32_t b = *bp;
327 return (a == b) ? 0 : (a < b) ? -1 : 1;
328 }
329
330
331 static void CollectElementIndices(Handle<JSObject> object, uint32_t range,
332 List<uint32_t>* indices) {
333 Isolate* isolate = object->GetIsolate();
334 ElementsKind kind = object->GetElementsKind();
335 switch (kind) {
336 case FAST_SMI_ELEMENTS:
337 case FAST_ELEMENTS:
338 case FAST_HOLEY_SMI_ELEMENTS:
339 case FAST_HOLEY_ELEMENTS: {
340 Handle<FixedArray> elements(FixedArray::cast(object->elements()));
341 uint32_t length = static_cast<uint32_t>(elements->length());
342 if (range < length) length = range;
343 for (uint32_t i = 0; i < length; i++) {
344 if (!elements->get(i)->IsTheHole()) {
345 indices->Add(i);
346 }
347 }
348 break;
349 }
350 case FAST_HOLEY_DOUBLE_ELEMENTS:
351 case FAST_DOUBLE_ELEMENTS: {
352 if (object->elements()->IsFixedArray()) {
353 DCHECK(object->elements()->length() == 0);
354 break;
355 }
356 Handle<FixedDoubleArray> elements(
357 FixedDoubleArray::cast(object->elements()));
358 uint32_t length = static_cast<uint32_t>(elements->length());
359 if (range < length) length = range;
360 for (uint32_t i = 0; i < length; i++) {
361 if (!elements->is_the_hole(i)) {
362 indices->Add(i);
363 }
364 }
365 break;
366 }
367 case DICTIONARY_ELEMENTS: {
368 Handle<SeededNumberDictionary> dict(
369 SeededNumberDictionary::cast(object->elements()));
370 uint32_t capacity = dict->Capacity();
371 for (uint32_t j = 0; j < capacity; j++) {
372 HandleScope loop_scope(isolate);
373 Handle<Object> k(dict->KeyAt(j), isolate);
374 if (dict->IsKey(*k)) {
375 DCHECK(k->IsNumber());
376 uint32_t index = static_cast<uint32_t>(k->Number());
377 if (index < range) {
378 indices->Add(index);
379 }
380 }
381 }
382 break;
383 }
384 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
385 case TYPE##_ELEMENTS: \
386 case EXTERNAL_##TYPE##_ELEMENTS:
387
388 TYPED_ARRAYS(TYPED_ARRAY_CASE)
389 #undef TYPED_ARRAY_CASE
390 {
391 uint32_t length = static_cast<uint32_t>(
392 FixedArrayBase::cast(object->elements())->length());
393 if (range <= length) {
394 length = range;
395 // We will add all indices, so we might as well clear it first
396 // and avoid duplicates.
397 indices->Clear();
398 }
399 for (uint32_t i = 0; i < length; i++) {
400 indices->Add(i);
401 }
402 if (length == range) return; // All indices accounted for already.
403 break;
404 }
405 case SLOPPY_ARGUMENTS_ELEMENTS: {
406 MaybeHandle<Object> length_obj =
407 Object::GetProperty(object, isolate->factory()->length_string());
408 double length_num = length_obj.ToHandleChecked()->Number();
409 uint32_t length = static_cast<uint32_t>(DoubleToInt32(length_num));
410 ElementsAccessor* accessor = object->GetElementsAccessor();
411 for (uint32_t i = 0; i < length; i++) {
412 if (accessor->HasElement(object, object, i)) {
413 indices->Add(i);
414 }
415 }
416 break;
417 }
418 }
419
420 PrototypeIterator iter(isolate, object);
421 if (!iter.IsAtEnd()) {
422 // The prototype will usually have no inherited element indices,
423 // but we have to check.
424 CollectElementIndices(
425 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter)), range,
426 indices);
427 }
428 }
429
430
431 /**
432 * A helper function that visits elements of a JSArray in numerical
433 * order.
434 *
435 * The visitor argument called for each existing element in the array
436 * with the element index and the element's value.
437 * Afterwards it increments the base-index of the visitor by the array
438 * length.
439 * Returns false if any access threw an exception, otherwise true.
440 */
441 static bool IterateElements(Isolate* isolate, Handle<JSArray> receiver,
442 ArrayConcatVisitor* visitor) {
443 uint32_t length = static_cast<uint32_t>(receiver->length()->Number());
444 switch (receiver->GetElementsKind()) {
445 case FAST_SMI_ELEMENTS:
446 case FAST_ELEMENTS:
447 case FAST_HOLEY_SMI_ELEMENTS:
448 case FAST_HOLEY_ELEMENTS: {
449 // Run through the elements FixedArray and use HasElement and GetElement
450 // to check the prototype for missing elements.
451 Handle<FixedArray> elements(FixedArray::cast(receiver->elements()));
452 int fast_length = static_cast<int>(length);
453 DCHECK(fast_length <= elements->length());
454 for (int j = 0; j < fast_length; j++) {
455 HandleScope loop_scope(isolate);
456 Handle<Object> element_value(elements->get(j), isolate);
457 if (!element_value->IsTheHole()) {
458 visitor->visit(j, element_value);
459 } else {
460 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
461 if (!maybe.has_value) return false;
462 if (maybe.value) {
463 // Call GetElement on receiver, not its prototype, or getters won't
464 // have the correct receiver.
465 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
466 isolate, element_value,
467 Object::GetElement(isolate, receiver, j), false);
468 visitor->visit(j, element_value);
469 }
470 }
471 }
472 break;
473 }
474 case FAST_HOLEY_DOUBLE_ELEMENTS:
475 case FAST_DOUBLE_ELEMENTS: {
476 // Empty array is FixedArray but not FixedDoubleArray.
477 if (length == 0) break;
478 // Run through the elements FixedArray and use HasElement and GetElement
479 // to check the prototype for missing elements.
480 if (receiver->elements()->IsFixedArray()) {
481 DCHECK(receiver->elements()->length() == 0);
482 break;
483 }
484 Handle<FixedDoubleArray> elements(
485 FixedDoubleArray::cast(receiver->elements()));
486 int fast_length = static_cast<int>(length);
487 DCHECK(fast_length <= elements->length());
488 for (int j = 0; j < fast_length; j++) {
489 HandleScope loop_scope(isolate);
490 if (!elements->is_the_hole(j)) {
491 double double_value = elements->get_scalar(j);
492 Handle<Object> element_value =
493 isolate->factory()->NewNumber(double_value);
494 visitor->visit(j, element_value);
495 } else {
496 Maybe<bool> maybe = JSReceiver::HasElement(receiver, j);
497 if (!maybe.has_value) return false;
498 if (maybe.value) {
499 // Call GetElement on receiver, not its prototype, or getters won't
500 // have the correct receiver.
501 Handle<Object> element_value;
502 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
503 isolate, element_value,
504 Object::GetElement(isolate, receiver, j), false);
505 visitor->visit(j, element_value);
506 }
507 }
508 }
509 break;
510 }
511 case DICTIONARY_ELEMENTS: {
512 Handle<SeededNumberDictionary> dict(receiver->element_dictionary());
513 List<uint32_t> indices(dict->Capacity() / 2);
514 // Collect all indices in the object and the prototypes less
515 // than length. This might introduce duplicates in the indices list.
516 CollectElementIndices(receiver, length, &indices);
517 indices.Sort(&compareUInt32);
518 int j = 0;
519 int n = indices.length();
520 while (j < n) {
521 HandleScope loop_scope(isolate);
522 uint32_t index = indices[j];
523 Handle<Object> element;
524 ASSIGN_RETURN_ON_EXCEPTION_VALUE(
525 isolate, element, Object::GetElement(isolate, receiver, index),
526 false);
527 visitor->visit(index, element);
528 // Skip to next different index (i.e., omit duplicates).
529 do {
530 j++;
531 } while (j < n && indices[j] == index);
532 }
533 break;
534 }
535 case EXTERNAL_UINT8_CLAMPED_ELEMENTS: {
536 Handle<ExternalUint8ClampedArray> pixels(
537 ExternalUint8ClampedArray::cast(receiver->elements()));
538 for (uint32_t j = 0; j < length; j++) {
539 Handle<Smi> e(Smi::FromInt(pixels->get_scalar(j)), isolate);
540 visitor->visit(j, e);
541 }
542 break;
543 }
544 case EXTERNAL_INT8_ELEMENTS: {
545 IterateExternalArrayElements<ExternalInt8Array, int8_t>(
546 isolate, receiver, true, true, visitor);
547 break;
548 }
549 case EXTERNAL_UINT8_ELEMENTS: {
550 IterateExternalArrayElements<ExternalUint8Array, uint8_t>(
551 isolate, receiver, true, true, visitor);
552 break;
553 }
554 case EXTERNAL_INT16_ELEMENTS: {
555 IterateExternalArrayElements<ExternalInt16Array, int16_t>(
556 isolate, receiver, true, true, visitor);
557 break;
558 }
559 case EXTERNAL_UINT16_ELEMENTS: {
560 IterateExternalArrayElements<ExternalUint16Array, uint16_t>(
561 isolate, receiver, true, true, visitor);
562 break;
563 }
564 case EXTERNAL_INT32_ELEMENTS: {
565 IterateExternalArrayElements<ExternalInt32Array, int32_t>(
566 isolate, receiver, true, false, visitor);
567 break;
568 }
569 case EXTERNAL_UINT32_ELEMENTS: {
570 IterateExternalArrayElements<ExternalUint32Array, uint32_t>(
571 isolate, receiver, true, false, visitor);
572 break;
573 }
574 case EXTERNAL_FLOAT32_ELEMENTS: {
575 IterateExternalArrayElements<ExternalFloat32Array, float>(
576 isolate, receiver, false, false, visitor);
577 break;
578 }
579 case EXTERNAL_FLOAT64_ELEMENTS: {
580 IterateExternalArrayElements<ExternalFloat64Array, double>(
581 isolate, receiver, false, false, visitor);
582 break;
583 }
584 default:
585 UNREACHABLE();
586 break;
587 }
588 visitor->increase_index_offset(length);
589 return true;
590 }
591
592
593 /**
594 * Array::concat implementation.
595 * See ECMAScript 262, 15.4.4.4.
596 * TODO(581): Fix non-compliance for very large concatenations and update to
597 * following the ECMAScript 5 specification.
598 */
599 RUNTIME_FUNCTION(Runtime_ArrayConcat) {
600 HandleScope handle_scope(isolate);
601 DCHECK(args.length() == 1);
602
603 CONVERT_ARG_HANDLE_CHECKED(JSArray, arguments, 0);
604 int argument_count = static_cast<int>(arguments->length()->Number());
605 RUNTIME_ASSERT(arguments->HasFastObjectElements());
606 Handle<FixedArray> elements(FixedArray::cast(arguments->elements()));
607
608 // Pass 1: estimate the length and number of elements of the result.
609 // The actual length can be larger if any of the arguments have getters
610 // that mutate other arguments (but will otherwise be precise).
611 // The number of elements is precise if there are no inherited elements.
612
613 ElementsKind kind = FAST_SMI_ELEMENTS;
614
615 uint32_t estimate_result_length = 0;
616 uint32_t estimate_nof_elements = 0;
617 for (int i = 0; i < argument_count; i++) {
618 HandleScope loop_scope(isolate);
619 Handle<Object> obj(elements->get(i), isolate);
620 uint32_t length_estimate;
621 uint32_t element_estimate;
622 if (obj->IsJSArray()) {
623 Handle<JSArray> array(Handle<JSArray>::cast(obj));
624 length_estimate = static_cast<uint32_t>(array->length()->Number());
625 if (length_estimate != 0) {
626 ElementsKind array_kind =
627 GetPackedElementsKind(array->map()->elements_kind());
628 if (IsMoreGeneralElementsKindTransition(kind, array_kind)) {
629 kind = array_kind;
630 }
631 }
632 element_estimate = EstimateElementCount(array);
633 } else {
634 if (obj->IsHeapObject()) {
635 if (obj->IsNumber()) {
636 if (IsMoreGeneralElementsKindTransition(kind, FAST_DOUBLE_ELEMENTS)) {
637 kind = FAST_DOUBLE_ELEMENTS;
638 }
639 } else if (IsMoreGeneralElementsKindTransition(kind, FAST_ELEMENTS)) {
640 kind = FAST_ELEMENTS;
641 }
642 }
643 length_estimate = 1;
644 element_estimate = 1;
645 }
646 // Avoid overflows by capping at kMaxElementCount.
647 if (JSObject::kMaxElementCount - estimate_result_length < length_estimate) {
648 estimate_result_length = JSObject::kMaxElementCount;
649 } else {
650 estimate_result_length += length_estimate;
651 }
652 if (JSObject::kMaxElementCount - estimate_nof_elements < element_estimate) {
653 estimate_nof_elements = JSObject::kMaxElementCount;
654 } else {
655 estimate_nof_elements += element_estimate;
656 }
657 }
658
659 // If estimated number of elements is more than half of length, a
660 // fixed array (fast case) is more time and space-efficient than a
661 // dictionary.
662 bool fast_case = (estimate_nof_elements * 2) >= estimate_result_length;
663
664 if (fast_case && kind == FAST_DOUBLE_ELEMENTS) {
665 Handle<FixedArrayBase> storage =
666 isolate->factory()->NewFixedDoubleArray(estimate_result_length);
667 int j = 0;
668 bool failure = false;
669 if (estimate_result_length > 0) {
670 Handle<FixedDoubleArray> double_storage =
671 Handle<FixedDoubleArray>::cast(storage);
672 for (int i = 0; i < argument_count; i++) {
673 Handle<Object> obj(elements->get(i), isolate);
674 if (obj->IsSmi()) {
675 double_storage->set(j, Smi::cast(*obj)->value());
676 j++;
677 } else if (obj->IsNumber()) {
678 double_storage->set(j, obj->Number());
679 j++;
680 } else {
681 JSArray* array = JSArray::cast(*obj);
682 uint32_t length = static_cast<uint32_t>(array->length()->Number());
683 switch (array->map()->elements_kind()) {
684 case FAST_HOLEY_DOUBLE_ELEMENTS:
685 case FAST_DOUBLE_ELEMENTS: {
686 // Empty array is FixedArray but not FixedDoubleArray.
687 if (length == 0) break;
688 FixedDoubleArray* elements =
689 FixedDoubleArray::cast(array->elements());
690 for (uint32_t i = 0; i < length; i++) {
691 if (elements->is_the_hole(i)) {
692 // TODO(jkummerow/verwaest): We could be a bit more clever
693 // here: Check if there are no elements/getters on the
694 // prototype chain, and if so, allow creation of a holey
695 // result array.
696 // Same thing below (holey smi case).
697 failure = true;
698 break;
699 }
700 double double_value = elements->get_scalar(i);
701 double_storage->set(j, double_value);
702 j++;
703 }
704 break;
705 }
706 case FAST_HOLEY_SMI_ELEMENTS:
707 case FAST_SMI_ELEMENTS: {
708 FixedArray* elements(FixedArray::cast(array->elements()));
709 for (uint32_t i = 0; i < length; i++) {
710 Object* element = elements->get(i);
711 if (element->IsTheHole()) {
712 failure = true;
713 break;
714 }
715 int32_t int_value = Smi::cast(element)->value();
716 double_storage->set(j, int_value);
717 j++;
718 }
719 break;
720 }
721 case FAST_HOLEY_ELEMENTS:
722 case FAST_ELEMENTS:
723 DCHECK_EQ(0, length);
724 break;
725 default:
726 UNREACHABLE();
727 }
728 }
729 if (failure) break;
730 }
731 }
732 if (!failure) {
733 Handle<JSArray> array = isolate->factory()->NewJSArray(0);
734 Smi* length = Smi::FromInt(j);
735 Handle<Map> map;
736 map = JSObject::GetElementsTransitionMap(array, kind);
737 array->set_map(*map);
738 array->set_length(length);
739 array->set_elements(*storage);
740 return *array;
741 }
742 // In case of failure, fall through.
743 }
744
745 Handle<FixedArray> storage;
746 if (fast_case) {
747 // The backing storage array must have non-existing elements to preserve
748 // holes across concat operations.
749 storage =
750 isolate->factory()->NewFixedArrayWithHoles(estimate_result_length);
751 } else {
752 // TODO(126): move 25% pre-allocation logic into Dictionary::Allocate
753 uint32_t at_least_space_for =
754 estimate_nof_elements + (estimate_nof_elements >> 2);
755 storage = Handle<FixedArray>::cast(
756 SeededNumberDictionary::New(isolate, at_least_space_for));
757 }
758
759 ArrayConcatVisitor visitor(isolate, storage, fast_case);
760
761 for (int i = 0; i < argument_count; i++) {
762 Handle<Object> obj(elements->get(i), isolate);
763 if (obj->IsJSArray()) {
764 Handle<JSArray> array = Handle<JSArray>::cast(obj);
765 if (!IterateElements(isolate, array, &visitor)) {
766 return isolate->heap()->exception();
767 }
768 } else {
769 visitor.visit(0, obj);
770 visitor.increase_index_offset(1);
771 }
772 }
773
774 if (visitor.exceeds_array_limit()) {
775 THROW_NEW_ERROR_RETURN_FAILURE(
776 isolate,
777 NewRangeError("invalid_array_length", HandleVector<Object>(NULL, 0)));
778 }
779 return *visitor.ToArray();
780 }
781
782
783 // Moves all own elements of an object, that are below a limit, to positions
784 // starting at zero. All undefined values are placed after non-undefined values,
785 // and are followed by non-existing element. Does not change the length
786 // property.
787 // Returns the number of non-undefined elements collected.
788 // Returns -1 if hole removal is not supported by this method.
789 RUNTIME_FUNCTION(Runtime_RemoveArrayHoles) {
790 HandleScope scope(isolate);
791 DCHECK(args.length() == 2);
792 CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
793 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]);
794 return *JSObject::PrepareElementsForSort(object, limit);
795 }
796
797
798 // Move contents of argument 0 (an array) to argument 1 (an array)
799 RUNTIME_FUNCTION(Runtime_MoveArrayContents) {
800 HandleScope scope(isolate);
801 DCHECK(args.length() == 2);
802 CONVERT_ARG_HANDLE_CHECKED(JSArray, from, 0);
803 CONVERT_ARG_HANDLE_CHECKED(JSArray, to, 1);
804 JSObject::ValidateElements(from);
805 JSObject::ValidateElements(to);
806
807 Handle<FixedArrayBase> new_elements(from->elements());
808 ElementsKind from_kind = from->GetElementsKind();
809 Handle<Map> new_map = JSObject::GetElementsTransitionMap(to, from_kind);
810 JSObject::SetMapAndElements(to, new_map, new_elements);
811 to->set_length(from->length());
812
813 JSObject::ResetElements(from);
814 from->set_length(Smi::FromInt(0));
815
816 JSObject::ValidateElements(to);
817 return *to;
818 }
819
820
821 // How many elements does this object/array have?
822 RUNTIME_FUNCTION(Runtime_EstimateNumberOfElements) {
823 HandleScope scope(isolate);
824 DCHECK(args.length() == 1);
825 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0);
826 Handle<FixedArrayBase> elements(array->elements(), isolate);
827 SealHandleScope shs(isolate);
828 if (elements->IsDictionary()) {
829 int result =
830 Handle<SeededNumberDictionary>::cast(elements)->NumberOfElements();
831 return Smi::FromInt(result);
832 } else {
833 DCHECK(array->length()->IsSmi());
834 // For packed elements, we know the exact number of elements
835 int length = elements->length();
836 ElementsKind kind = array->GetElementsKind();
837 if (IsFastPackedElementsKind(kind)) {
838 return Smi::FromInt(length);
839 }
840 // For holey elements, take samples from the buffer checking for holes
841 // to generate the estimate.
842 const int kNumberOfHoleCheckSamples = 97;
843 int increment = (length < kNumberOfHoleCheckSamples)
844 ? 1
845 : static_cast<int>(length / kNumberOfHoleCheckSamples);
846 ElementsAccessor* accessor = array->GetElementsAccessor();
847 int holes = 0;
848 for (int i = 0; i < length; i += increment) {
849 if (!accessor->HasElement(array, array, i, elements)) {
850 ++holes;
851 }
852 }
853 int estimate = static_cast<int>((kNumberOfHoleCheckSamples - holes) /
854 kNumberOfHoleCheckSamples * length);
855 return Smi::FromInt(estimate);
856 }
857 }
858
859
860 // Returns an array that tells you where in the [0, length) interval an array
861 // might have elements. Can either return an array of keys (positive integers
862 // or undefined) or a number representing the positive length of an interval
863 // starting at index 0.
864 // Intervals can span over some keys that are not in the object.
865 RUNTIME_FUNCTION(Runtime_GetArrayKeys) {
866 HandleScope scope(isolate);
867 DCHECK(args.length() == 2);
868 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
869 CONVERT_NUMBER_CHECKED(uint32_t, length, Uint32, args[1]);
870 if (array->elements()->IsDictionary()) {
871 Handle<FixedArray> keys = isolate->factory()->empty_fixed_array();
872 for (PrototypeIterator iter(isolate, array,
873 PrototypeIterator::START_AT_RECEIVER);
874 !iter.IsAtEnd(); iter.Advance()) {
875 if (PrototypeIterator::GetCurrent(iter)->IsJSProxy() ||
876 JSObject::cast(*PrototypeIterator::GetCurrent(iter))
877 ->HasIndexedInterceptor()) {
878 // Bail out if we find a proxy or interceptor, likely not worth
879 // collecting keys in that case.
880 return *isolate->factory()->NewNumberFromUint(length);
881 }
882 Handle<JSObject> current =
883 Handle<JSObject>::cast(PrototypeIterator::GetCurrent(iter));
884 Handle<FixedArray> current_keys =
885 isolate->factory()->NewFixedArray(current->NumberOfOwnElements(NONE));
886 current->GetOwnElementKeys(*current_keys, NONE);
887 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
888 isolate, keys, FixedArray::UnionOfKeys(keys, current_keys));
889 }
890 // Erase any keys >= length.
891 // TODO(adamk): Remove this step when the contract of %GetArrayKeys
892 // is changed to let this happen on the JS side.
893 for (int i = 0; i < keys->length(); i++) {
894 if (NumberToUint32(keys->get(i)) >= length) keys->set_undefined(i);
895 }
896 return *isolate->factory()->NewJSArrayWithElements(keys);
897 } else {
898 RUNTIME_ASSERT(array->HasFastSmiOrObjectElements() ||
899 array->HasFastDoubleElements());
900 uint32_t actual_length = static_cast<uint32_t>(array->elements()->length());
901 return *isolate->factory()->NewNumberFromUint(Min(actual_length, length));
902 }
903 }
904
905
906 static Object* ArrayConstructorCommon(Isolate* isolate,
907 Handle<JSFunction> constructor,
908 Handle<AllocationSite> site,
909 Arguments* caller_args) {
910 Factory* factory = isolate->factory();
911
912 bool holey = false;
913 bool can_use_type_feedback = true;
914 if (caller_args->length() == 1) {
915 Handle<Object> argument_one = caller_args->at<Object>(0);
916 if (argument_one->IsSmi()) {
917 int value = Handle<Smi>::cast(argument_one)->value();
918 if (value < 0 || value >= JSObject::kInitialMaxFastElementArray) {
919 // the array is a dictionary in this case.
920 can_use_type_feedback = false;
921 } else if (value != 0) {
922 holey = true;
923 }
924 } else {
925 // Non-smi length argument produces a dictionary
926 can_use_type_feedback = false;
927 }
928 }
929
930 Handle<JSArray> array;
931 if (!site.is_null() && can_use_type_feedback) {
932 ElementsKind to_kind = site->GetElementsKind();
933 if (holey && !IsFastHoleyElementsKind(to_kind)) {
934 to_kind = GetHoleyElementsKind(to_kind);
935 // Update the allocation site info to reflect the advice alteration.
936 site->SetElementsKind(to_kind);
937 }
938
939 // We should allocate with an initial map that reflects the allocation site
940 // advice. Therefore we use AllocateJSObjectFromMap instead of passing
941 // the constructor.
942 Handle<Map> initial_map(constructor->initial_map(), isolate);
943 if (to_kind != initial_map->elements_kind()) {
944 initial_map = Map::AsElementsKind(initial_map, to_kind);
945 }
946
947 // If we don't care to track arrays of to_kind ElementsKind, then
948 // don't emit a memento for them.
949 Handle<AllocationSite> allocation_site;
950 if (AllocationSite::GetMode(to_kind) == TRACK_ALLOCATION_SITE) {
951 allocation_site = site;
952 }
953
954 array = Handle<JSArray>::cast(factory->NewJSObjectFromMap(
955 initial_map, NOT_TENURED, true, allocation_site));
956 } else {
957 array = Handle<JSArray>::cast(factory->NewJSObject(constructor));
958
959 // We might need to transition to holey
960 ElementsKind kind = constructor->initial_map()->elements_kind();
961 if (holey && !IsFastHoleyElementsKind(kind)) {
962 kind = GetHoleyElementsKind(kind);
963 JSObject::TransitionElementsKind(array, kind);
964 }
965 }
966
967 factory->NewJSArrayStorage(array, 0, 0, DONT_INITIALIZE_ARRAY_ELEMENTS);
968
969 ElementsKind old_kind = array->GetElementsKind();
970 RETURN_FAILURE_ON_EXCEPTION(
971 isolate, ArrayConstructInitializeElements(array, caller_args));
972 if (!site.is_null() &&
973 (old_kind != array->GetElementsKind() || !can_use_type_feedback)) {
974 // The arguments passed in caused a transition. This kind of complexity
975 // can't be dealt with in the inlined hydrogen array constructor case.
976 // We must mark the allocationsite as un-inlinable.
977 site->SetDoNotInlineCall();
978 }
979 return *array;
980 }
981
982
983 RUNTIME_FUNCTION(Runtime_ArrayConstructor) {
984 HandleScope scope(isolate);
985 // If we get 2 arguments then they are the stub parameters (constructor, type
986 // info). If we get 4, then the first one is a pointer to the arguments
987 // passed by the caller, and the last one is the length of the arguments
988 // passed to the caller (redundant, but useful to check on the deoptimizer
989 // with an assert).
990 Arguments empty_args(0, NULL);
991 bool no_caller_args = args.length() == 2;
992 DCHECK(no_caller_args || args.length() == 4);
993 int parameters_start = no_caller_args ? 0 : 1;
994 Arguments* caller_args =
995 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]);
996 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
997 CONVERT_ARG_HANDLE_CHECKED(Object, type_info, parameters_start + 1);
998 #ifdef DEBUG
999 if (!no_caller_args) {
1000 CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 2);
1001 DCHECK(arg_count == caller_args->length());
1002 }
1003 #endif
1004
1005 Handle<AllocationSite> site;
1006 if (!type_info.is_null() &&
1007 *type_info != isolate->heap()->undefined_value()) {
1008 site = Handle<AllocationSite>::cast(type_info);
1009 DCHECK(!site->SitePointsToLiteral());
1010 }
1011
1012 return ArrayConstructorCommon(isolate, constructor, site, caller_args);
1013 }
1014
1015
1016 RUNTIME_FUNCTION(Runtime_InternalArrayConstructor) {
1017 HandleScope scope(isolate);
1018 Arguments empty_args(0, NULL);
1019 bool no_caller_args = args.length() == 1;
1020 DCHECK(no_caller_args || args.length() == 3);
1021 int parameters_start = no_caller_args ? 0 : 1;
1022 Arguments* caller_args =
1023 no_caller_args ? &empty_args : reinterpret_cast<Arguments*>(args[0]);
1024 CONVERT_ARG_HANDLE_CHECKED(JSFunction, constructor, parameters_start);
1025 #ifdef DEBUG
1026 if (!no_caller_args) {
1027 CONVERT_SMI_ARG_CHECKED(arg_count, parameters_start + 1);
1028 DCHECK(arg_count == caller_args->length());
1029 }
1030 #endif
1031 return ArrayConstructorCommon(isolate, constructor,
1032 Handle<AllocationSite>::null(), caller_args);
1033 }
1034
1035
1036 RUNTIME_FUNCTION(Runtime_NormalizeElements) {
1037 HandleScope scope(isolate);
1038 DCHECK(args.length() == 1);
1039 CONVERT_ARG_HANDLE_CHECKED(JSObject, array, 0);
1040 RUNTIME_ASSERT(!array->HasExternalArrayElements() &&
1041 !array->HasFixedTypedArrayElements());
1042 JSObject::NormalizeElements(array);
1043 return *array;
1044 }
1045
1046
1047 // TODO(dcarney): remove this function when TurboFan supports it.
1048 // Takes the object to be iterated over and the result of GetPropertyNamesFast
1049 // Returns pair (cache_array, cache_type).
1050 RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ForInInit) {
1051 SealHandleScope scope(isolate);
1052 DCHECK(args.length() == 2);
1053 // This simulates CONVERT_ARG_HANDLE_CHECKED for calls returning pairs.
1054 // Not worth creating a macro atm as this function should be removed.
1055 if (!args[0]->IsJSReceiver() || !args[1]->IsObject()) {
1056 Object* error = isolate->ThrowIllegalOperation();
1057 return MakePair(error, isolate->heap()->undefined_value());
1058 }
1059 Handle<JSReceiver> object = args.at<JSReceiver>(0);
1060 Handle<Object> cache_type = args.at<Object>(1);
1061 if (cache_type->IsMap()) {
1062 // Enum cache case.
1063 if (Map::EnumLengthBits::decode(Map::cast(*cache_type)->bit_field3()) ==
1064 0) {
1065 // 0 length enum.
1066 // Can't handle this case in the graph builder,
1067 // so transform it into the empty fixed array case.
1068 return MakePair(isolate->heap()->empty_fixed_array(), Smi::FromInt(1));
1069 }
1070 return MakePair(object->map()->instance_descriptors()->GetEnumCache(),
1071 *cache_type);
1072 } else {
1073 // FixedArray case.
1074 Smi* new_cache_type = Smi::FromInt(object->IsJSProxy() ? 0 : 1);
1075 return MakePair(*Handle<FixedArray>::cast(cache_type), new_cache_type);
1076 }
1077 }
1078
1079
1080 // TODO(dcarney): remove this function when TurboFan supports it.
1081 RUNTIME_FUNCTION(Runtime_ForInCacheArrayLength) {
1082 SealHandleScope shs(isolate);
1083 DCHECK(args.length() == 2);
1084 CONVERT_ARG_HANDLE_CHECKED(Object, cache_type, 0);
1085 CONVERT_ARG_HANDLE_CHECKED(FixedArray, array, 1);
1086 int length = 0;
1087 if (cache_type->IsMap()) {
1088 length = Map::cast(*cache_type)->EnumLength();
1089 } else {
1090 DCHECK(cache_type->IsSmi());
1091 length = array->length();
1092 }
1093 return Smi::FromInt(length);
1094 }
1095
1096
1097 // TODO(dcarney): remove this function when TurboFan supports it.
1098 // Takes (the object to be iterated over,
1099 // cache_array from ForInInit,
1100 // cache_type from ForInInit,
1101 // the current index)
1102 // Returns pair (array[index], needs_filtering).
1103 RUNTIME_FUNCTION_RETURN_PAIR(Runtime_ForInNext) {
1104 SealHandleScope scope(isolate);
1105 DCHECK(args.length() == 4);
1106 int32_t index;
1107 // This simulates CONVERT_ARG_HANDLE_CHECKED for calls returning pairs.
1108 // Not worth creating a macro atm as this function should be removed.
1109 if (!args[0]->IsJSReceiver() || !args[1]->IsFixedArray() ||
1110 !args[2]->IsObject() || !args[3]->ToInt32(&index)) {
1111 Object* error = isolate->ThrowIllegalOperation();
1112 return MakePair(error, isolate->heap()->undefined_value());
1113 }
1114 Handle<JSReceiver> object = args.at<JSReceiver>(0);
1115 Handle<FixedArray> array = args.at<FixedArray>(1);
1116 Handle<Object> cache_type = args.at<Object>(2);
1117 // Figure out first if a slow check is needed for this object.
1118 bool slow_check_needed = false;
1119 if (cache_type->IsMap()) {
1120 if (object->map() != Map::cast(*cache_type)) {
1121 // Object transitioned. Need slow check.
1122 slow_check_needed = true;
1123 }
1124 } else {
1125 // No slow check needed for proxies.
1126 slow_check_needed = Smi::cast(*cache_type)->value() == 1;
1127 }
1128 return MakePair(array->get(index),
1129 isolate->heap()->ToBoolean(slow_check_needed));
1130 }
1131
1132
1133 RUNTIME_FUNCTION(RuntimeReference_IsArray) {
1134 SealHandleScope shs(isolate);
1135 DCHECK(args.length() == 1);
1136 CONVERT_ARG_CHECKED(Object, obj, 0);
1137 return isolate->heap()->ToBoolean(obj->IsJSArray());
1138 }
1139
1140
1141 RUNTIME_FUNCTION(RuntimeReference_HasCachedArrayIndex) {
1142 SealHandleScope shs(isolate);
1143 DCHECK(args.length() == 1);
1144 return isolate->heap()->false_value();
1145 }
1146
1147
1148 RUNTIME_FUNCTION(RuntimeReference_GetCachedArrayIndex) {
1149 SealHandleScope shs(isolate);
1150 DCHECK(args.length() == 1);
1151 return isolate->heap()->undefined_value();
1152 }
1153
1154
1155 RUNTIME_FUNCTION(RuntimeReference_FastOneByteArrayJoin) {
1156 SealHandleScope shs(isolate);
1157 DCHECK(args.length() == 2);
1158 return isolate->heap()->undefined_value();
1159 }
1160 }
1161 } // namespace v8::internal
OLDNEW
« no previous file with comments | « src/runtime/runtime-api.cc ('k') | src/runtime/runtime-debug.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698