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