// Copyright 2017 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "src/builtins/builtins-array-gen.h" #include "src/builtins/builtins-iterator-gen.h" #include "src/builtins/builtins-string-gen.h" #include "src/builtins/builtins-typed-array-gen.h" #include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins.h" #include "src/codegen/code-stub-assembler.h" #include "src/execution/frame-constants.h" #include "src/heap/factory-inl.h" #include "src/objects/allocation-site-inl.h" #include "src/objects/arguments-inl.h" #include "src/objects/property-cell.h" namespace v8 { namespace internal { using Node = compiler::Node; using IteratorRecord = TorqueStructIteratorRecord; ArrayBuiltinsAssembler::ArrayBuiltinsAssembler( compiler::CodeAssemblerState* state) : CodeStubAssembler(state), k_(this), a_(this), fully_spec_compliant_(this, {&k_, &a_}) {} void ArrayBuiltinsAssembler::TypedArrayMapResultGenerator() { // 6. Let A be ? TypedArraySpeciesCreate(O, len). TNode original_array = CAST(o()); const char* method_name = "%TypedArray%.prototype.map"; TNode a = TypedArraySpeciesCreateByLength( context(), method_name, original_array, len()); // In the Spec and our current implementation, the length check is already // performed in TypedArraySpeciesCreate. CSA_ASSERT(this, UintPtrLessThanOrEqual(len(), LoadJSTypedArrayLength(a))); fast_typed_array_target_ = Word32Equal(LoadElementsKind(original_array), LoadElementsKind(a)); a_ = a; } // See tc39.github.io/ecma262/#sec-%typedarray%.prototype.map. TNode ArrayBuiltinsAssembler::TypedArrayMapProcessor( TNode k_value, TNode k) { // 8. c. Let mapped_value be ? Call(callbackfn, T, « kValue, k, O »). TNode k_number = ChangeUintPtrToTagged(k); TNode mapped_value = Call(context(), callbackfn(), this_arg(), k_value, k_number, o()); Label fast(this), slow(this), done(this), detached(this, Label::kDeferred); // 8. d. Perform ? Set(A, Pk, mapped_value, true). // Since we know that A is a TypedArray, this always ends up in // #sec-integer-indexed-exotic-objects-set-p-v-receiver and then // tc39.github.io/ecma262/#sec-integerindexedelementset . Branch(fast_typed_array_target_, &fast, &slow); BIND(&fast); // #sec-integerindexedelementset // 5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let // numValue be ? ToBigInt(v). // 6. Otherwise, let numValue be ? ToNumber(value). TNode num_value; if (source_elements_kind_ == BIGINT64_ELEMENTS || source_elements_kind_ == BIGUINT64_ELEMENTS) { num_value = ToBigInt(context(), mapped_value); } else { num_value = ToNumber_Inline(context(), mapped_value); } // The only way how this can bailout is because of a detached buffer. // TODO(v8:4153): Consider checking IsDetachedBuffer() and calling // TypedArrayBuiltinsAssembler::StoreJSTypedArrayElementFromNumeric() here // instead to avoid converting k_number back to UintPtrT. EmitElementStore(CAST(a()), k_number, num_value, source_elements_kind_, KeyedAccessStoreMode::STANDARD_STORE, &detached, context()); Goto(&done); BIND(&slow); { SetPropertyStrict(context(), a(), k_number, mapped_value); Goto(&done); } BIND(&detached); // tc39.github.io/ecma262/#sec-integerindexedelementset // 8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception. ThrowTypeError(context_, MessageTemplate::kDetachedOperation, name_); BIND(&done); return a(); } void ArrayBuiltinsAssembler::ReturnFromBuiltin(TNode value) { if (argc_ == nullptr) { Return(value); } else { // argc_ doesn't include the receiver, so it has to be added back in // manually. PopAndReturn(IntPtrAdd(argc_, IntPtrConstant(1)), value); } } void ArrayBuiltinsAssembler::InitIteratingArrayBuiltinBody( TNode context, TNode receiver, TNode callbackfn, TNode this_arg, TNode argc) { context_ = context; receiver_ = receiver; callbackfn_ = callbackfn; this_arg_ = this_arg; argc_ = argc; } void ArrayBuiltinsAssembler::GenerateIteratingTypedArrayBuiltinBody( const char* name, const BuiltinResultGenerator& generator, const CallResultProcessor& processor, ForEachDirection direction) { name_ = name; // ValidateTypedArray: tc39.github.io/ecma262/#sec-validatetypedarray Label throw_not_typed_array(this, Label::kDeferred); GotoIf(TaggedIsSmi(receiver_), &throw_not_typed_array); TNode typed_array_map = LoadMap(CAST(receiver_)); GotoIfNot(IsJSTypedArrayMap(typed_array_map), &throw_not_typed_array); TNode typed_array = CAST(receiver_); o_ = typed_array; TNode array_buffer = LoadJSArrayBufferViewBuffer(typed_array); ThrowIfArrayBufferIsDetached(context_, array_buffer, name_); len_ = LoadJSTypedArrayLength(typed_array); Label throw_not_callable(this, Label::kDeferred); Label distinguish_types(this); GotoIf(TaggedIsSmi(callbackfn_), &throw_not_callable); Branch(IsCallableMap(LoadMap(CAST(callbackfn_))), &distinguish_types, &throw_not_callable); BIND(&throw_not_typed_array); ThrowTypeError(context_, MessageTemplate::kNotTypedArray); BIND(&throw_not_callable); ThrowTypeError(context_, MessageTemplate::kCalledNonCallable, callbackfn_); Label unexpected_instance_type(this); BIND(&unexpected_instance_type); Unreachable(); std::vector elements_kinds = { #define ELEMENTS_KIND(Type, type, TYPE, ctype) TYPE##_ELEMENTS, TYPED_ARRAYS(ELEMENTS_KIND) #undef ELEMENTS_KIND }; std::list