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