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