diff options
Diffstat (limited to 'chromium/v8/src/builtins')
-rw-r--r-- | chromium/v8/src/builtins/builtins-collections-gen.cc | 9 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-definitions.h | 2 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-proxy-gen.cc | 4 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-string-gen.cc | 29 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-string-gen.h | 2 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-typedarray-gen.cc | 346 | ||||
-rw-r--r-- | chromium/v8/src/builtins/builtins-typedarray.cc | 296 |
7 files changed, 366 insertions, 322 deletions
diff --git a/chromium/v8/src/builtins/builtins-collections-gen.cc b/chromium/v8/src/builtins/builtins-collections-gen.cc index a1928632abd..4aa7fa310b1 100644 --- a/chromium/v8/src/builtins/builtins-collections-gen.cc +++ b/chromium/v8/src/builtins/builtins-collections-gen.cc @@ -314,10 +314,11 @@ TF_BUILTIN(MapConstructor, CollectionsBuiltinsAssembler) { BIND(&if_notobject); { - Node* const exception = MakeTypeError( - MessageTemplate::kIteratorValueNotAnObject, context, next_value); - var_exception.Bind(exception); - Goto(&if_exception); + Node* ret = CallRuntime( + Runtime::kThrowTypeError, context, + SmiConstant(MessageTemplate::kIteratorValueNotAnObject), next_value); + GotoIfException(ret, &if_exception, &var_exception); + Unreachable(); } } diff --git a/chromium/v8/src/builtins/builtins-definitions.h b/chromium/v8/src/builtins/builtins-definitions.h index 746051b6cd9..cc89c4e3650 100644 --- a/chromium/v8/src/builtins/builtins-definitions.h +++ b/chromium/v8/src/builtins/builtins-definitions.h @@ -1024,7 +1024,7 @@ namespace internal { /* ES6 #sec-%typedarray%.prototype.reverse */ \ CPP(TypedArrayPrototypeReverse) \ /* ES6 %TypedArray%.prototype.set */ \ - CPP(TypedArrayPrototypeSet) \ + TFJ(TypedArrayPrototypeSet, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \ /* ES6 #sec-%typedarray%.prototype.slice */ \ CPP(TypedArrayPrototypeSlice) \ /* ES6 #sec-get-%typedarray%.prototype-@@tostringtag */ \ diff --git a/chromium/v8/src/builtins/builtins-proxy-gen.cc b/chromium/v8/src/builtins/builtins-proxy-gen.cc index c4fd34290a8..29c5a4eaeb3 100644 --- a/chromium/v8/src/builtins/builtins-proxy-gen.cc +++ b/chromium/v8/src/builtins/builtins-proxy-gen.cc @@ -491,7 +491,7 @@ void ProxiesCodeStubAssembler::CheckGetSetTrapResult( Node* instance_type = LoadInstanceType(target); TryGetOwnProperty(context, target, target, map, instance_type, name, &if_found_value, &var_value, &var_details, &var_raw_value, - check_passed, &check_in_runtime); + check_passed, &check_in_runtime, kReturnAccessorPair); BIND(&if_found_value); { @@ -589,7 +589,7 @@ void ProxiesCodeStubAssembler::CheckHasTrapResult(Node* context, Node* target, Node* instance_type = LoadInstanceType(target); TryGetOwnProperty(context, target, target, target_map, instance_type, name, &if_found_value, &var_value, &var_details, &var_raw_value, - check_passed, if_bailout); + check_passed, if_bailout, kReturnAccessorPair); // 9.b. If targetDesc is not undefined, then (see 9.b.i. below). BIND(&if_found_value); diff --git a/chromium/v8/src/builtins/builtins-string-gen.cc b/chromium/v8/src/builtins/builtins-string-gen.cc index 9cb0e474e21..8d407b35e61 100644 --- a/chromium/v8/src/builtins/builtins-string-gen.cc +++ b/chromium/v8/src/builtins/builtins-string-gen.cc @@ -1052,9 +1052,9 @@ void StringBuiltinsAssembler::RequireObjectCoercible(Node* const context, } void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( - Node* const context, Node* const object, Handle<Symbol> symbol, - const NodeFunction0& regexp_call, const NodeFunction1& generic_call, - CodeStubArguments* args) { + Node* const context, Node* const object, Node* const maybe_string, + Handle<Symbol> symbol, const NodeFunction0& regexp_call, + const NodeFunction1& generic_call, CodeStubArguments* args) { Label out(this); // Smis definitely don't have an attached symbol. @@ -1084,14 +1084,21 @@ void StringBuiltinsAssembler::MaybeCallFunctionAtSymbol( } // Take the fast path for RegExps. + // There's two conditions: {object} needs to be a fast regexp, and + // {maybe_string} must be a string (we can't call ToString on the fast path + // since it may mutate {object}). { Label stub_call(this), slow_lookup(this); + GotoIf(TaggedIsSmi(maybe_string), &slow_lookup); + GotoIfNot(IsString(maybe_string), &slow_lookup); + RegExpBuiltinsAssembler regexp_asm(state()); regexp_asm.BranchIfFastRegExp(context, object, object_map, &stub_call, &slow_lookup); BIND(&stub_call); + // TODO(jgruber): Add a no-JS scope once it exists. Node* const result = regexp_call(); if (args == nullptr) { Return(result); @@ -1307,12 +1314,10 @@ TF_BUILTIN(StringPrototypeReplace, StringBuiltinsAssembler) { // Redirect to replacer method if {search[@@replace]} is not undefined. MaybeCallFunctionAtSymbol( - context, search, isolate()->factory()->replace_symbol(), + context, search, receiver, isolate()->factory()->replace_symbol(), [=]() { - Node* const subject_string = ToString_Inline(context, receiver); - - return CallBuiltin(Builtins::kRegExpReplace, context, search, - subject_string, replace); + return CallBuiltin(Builtins::kRegExpReplace, context, search, receiver, + replace); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); @@ -1550,12 +1555,10 @@ TF_BUILTIN(StringPrototypeSplit, StringBuiltinsAssembler) { // Redirect to splitter method if {separator[@@split]} is not undefined. MaybeCallFunctionAtSymbol( - context, separator, isolate()->factory()->split_symbol(), + context, separator, receiver, isolate()->factory()->split_symbol(), [=]() { - Node* const subject_string = ToString_Inline(context, receiver); - - return CallBuiltin(Builtins::kRegExpSplit, context, separator, - subject_string, limit); + return CallBuiltin(Builtins::kRegExpSplit, context, separator, receiver, + limit); }, [=](Node* fn) { Callable call_callable = CodeFactory::Call(isolate()); diff --git a/chromium/v8/src/builtins/builtins-string-gen.h b/chromium/v8/src/builtins/builtins-string-gen.h index 607f7b6acb2..c9af3802707 100644 --- a/chromium/v8/src/builtins/builtins-string-gen.h +++ b/chromium/v8/src/builtins/builtins-string-gen.h @@ -82,9 +82,11 @@ class StringBuiltinsAssembler : public CodeStubAssembler { // } // // Contains fast paths for Smi and RegExp objects. + // Important: {regexp_call} may not contain any code that can call into JS. typedef std::function<Node*()> NodeFunction0; typedef std::function<Node*(Node* fn)> NodeFunction1; void MaybeCallFunctionAtSymbol(Node* const context, Node* const object, + Node* const maybe_string, Handle<Symbol> symbol, const NodeFunction0& regexp_call, const NodeFunction1& generic_call, diff --git a/chromium/v8/src/builtins/builtins-typedarray-gen.cc b/chromium/v8/src/builtins/builtins-typedarray-gen.cc index a58f3a4093f..07f122b9098 100644 --- a/chromium/v8/src/builtins/builtins-typedarray-gen.cc +++ b/chromium/v8/src/builtins/builtins-typedarray-gen.cc @@ -10,6 +10,10 @@ namespace v8 { namespace internal { +using compiler::Node; +template <class T> +using TNode = compiler::TNode<T>; + // This is needed for gc_mole which will compile this file without the full set // of GN defined macros. #ifndef V8_TYPED_ARRAY_MAX_SIZE_IN_HEAP @@ -41,9 +45,37 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler { Node* CalculateExternalPointer(Node* backing_store, Node* byte_offset); Node* LoadDataPtr(Node* typed_array); Node* ByteLengthIsValid(Node* byte_length); + + // Loads the element kind of TypedArray instance. + TNode<Word32T> LoadElementsKind(TNode<Object> typed_array); + + // Returns the byte size of an element for a TypedArray elements kind. + TNode<IntPtrT> GetTypedArrayElementSize(TNode<Word32T> elements_kind); + + // Fast path for setting a TypedArray (source) onto another TypedArray + // (target) at an element offset. + void SetTypedArraySource(TNode<Context> context, TNode<JSTypedArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, + Label* call_runtime, Label* if_source_too_large); + + void SetJSArraySource(TNode<Context> context, TNode<JSArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, + Label* call_runtime, Label* if_source_too_large); + + void CallCMemmove(TNode<IntPtrT> dest_ptr, TNode<IntPtrT> src_ptr, + TNode<IntPtrT> byte_length); + + void CallCCopyFastNumberJSArrayElementsToTypedArray( + TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, TNode<IntPtrT> offset); + + void CallCCopyTypedArrayElementsToTypedArray(TNode<JSTypedArray> source, + TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, + TNode<IntPtrT> offset); }; -compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { +Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { CSA_ASSERT(this, IsJSTypedArray(array)); Label unreachable(this), done(this); @@ -96,8 +128,8 @@ compiler::Node* TypedArrayBuiltinsAssembler::LoadMapForType(Node* array) { // can't allocate an array bigger than our 32-bit arithmetic range anyway. 64 // bit platforms could theoretically have an offset up to 2^35 - 1, so we may // need to convert the float heap number to an intptr. -compiler::Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer( - Node* backing_store, Node* byte_offset) { +Node* TypedArrayBuiltinsAssembler::CalculateExternalPointer(Node* backing_store, + Node* byte_offset) { return IntPtrAdd(backing_store, ChangeNumberToIntPtr(byte_offset)); } @@ -518,7 +550,7 @@ TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) { } } -compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { +Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { CSA_ASSERT(this, IsJSTypedArray(typed_array)); Node* elements = LoadElements(typed_array); CSA_ASSERT(this, IsFixedTypedArray(elements)); @@ -529,8 +561,7 @@ compiler::Node* TypedArrayBuiltinsAssembler::LoadDataPtr(Node* typed_array) { return IntPtrAdd(base_pointer, external_pointer); } -compiler::Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid( - Node* byte_length) { +Node* TypedArrayBuiltinsAssembler::ByteLengthIsValid(Node* byte_length) { Label smi(this), done(this); VARIABLE(is_valid, MachineRepresentation::kWord32); GotoIf(TaggedIsSmi(byte_length), &smi); @@ -674,6 +705,309 @@ TF_BUILTIN(TypedArrayPrototypeLength, TypedArrayBuiltinsAssembler) { JSTypedArray::kLengthOffset); } +TNode<Word32T> TypedArrayBuiltinsAssembler::LoadElementsKind( + TNode<Object> typed_array) { + CSA_ASSERT(this, IsJSTypedArray(typed_array)); + return Int32Sub(LoadMapElementsKind(LoadMap(CAST(typed_array))), + Int32Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); +} + +TNode<IntPtrT> TypedArrayBuiltinsAssembler::GetTypedArrayElementSize( + TNode<Word32T> elements_kind) { + TVARIABLE(IntPtrT, element_size); + Label next(this), if_unknown_type(this, Label::kDeferred); + + size_t const kTypedElementsKindCount = LAST_FIXED_TYPED_ARRAY_ELEMENTS_KIND - + FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND + + 1; + + int32_t elements_kinds[kTypedElementsKindCount] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + TYPE##_ELEMENTS - FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; + +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + Label if_##type##array(this); + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + + Label* elements_kind_labels[kTypedElementsKindCount] = { +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) &if_##type##array, + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + }; + + Switch(elements_kind, &if_unknown_type, elements_kinds, elements_kind_labels, + kTypedElementsKindCount); + +#define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ + BIND(&if_##type##array); \ + { \ + element_size = IntPtrConstant(size); \ + Goto(&next); \ + } + TYPED_ARRAYS(TYPED_ARRAY_CASE) +#undef TYPED_ARRAY_CASE + + BIND(&if_unknown_type); + { + element_size = IntPtrConstant(0); + Goto(&next); + } + BIND(&next); + return element_size; +} + +void TypedArrayBuiltinsAssembler::SetTypedArraySource( + TNode<Context> context, TNode<JSTypedArray> source, + TNode<JSTypedArray> target, TNode<IntPtrT> offset, Label* call_runtime, + Label* if_source_too_large) { + CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer( + LoadObjectField(source, JSTypedArray::kBufferOffset)))); + CSA_ASSERT(this, Word32BinaryNot(IsDetachedBuffer( + LoadObjectField(target, JSTypedArray::kBufferOffset)))); + CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0))); + CSA_ASSERT(this, + IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue))); + + // Check for possible range errors. + + TNode<IntPtrT> source_length = + LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset); + TNode<IntPtrT> target_length = + LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset); + TNode<IntPtrT> required_target_length = IntPtrAdd(source_length, offset); + + GotoIf(IntPtrGreaterThan(required_target_length, target_length), + if_source_too_large); + + // Grab pointers and byte lengths we need later on. + + TNode<IntPtrT> target_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(target)); + TNode<IntPtrT> source_data_ptr = UncheckedCast<IntPtrT>(LoadDataPtr(source)); + + TNode<Word32T> source_el_kind = LoadElementsKind(source); + TNode<Word32T> target_el_kind = LoadElementsKind(target); + + TNode<IntPtrT> source_el_size = GetTypedArrayElementSize(source_el_kind); + TNode<IntPtrT> target_el_size = GetTypedArrayElementSize(target_el_kind); + + // A note on byte lengths: both source- and target byte lengths must be valid, + // i.e. it must be possible to allocate an array of the given length. That + // means we're safe from overflows in the following multiplication. + TNode<IntPtrT> source_byte_length = IntPtrMul(source_length, source_el_size); + CSA_ASSERT(this, + IntPtrGreaterThanOrEqual(source_byte_length, IntPtrConstant(0))); + + Label call_memmove(this), fast_c_call(this), out(this); + Branch(Word32Equal(source_el_kind, target_el_kind), &call_memmove, + &fast_c_call); + + BIND(&call_memmove); + { + TNode<IntPtrT> target_start = + IntPtrAdd(target_data_ptr, IntPtrMul(offset, target_el_size)); + CallCMemmove(target_start, source_data_ptr, source_byte_length); + Goto(&out); + } + + BIND(&fast_c_call); + { + // Overlapping backing stores of different element kinds are handled in + // runtime. We're a bit conservative here and bail to runtime if ranges + // overlap and element kinds differ. + + TNode<IntPtrT> target_byte_length = + IntPtrMul(target_length, target_el_size); + CSA_ASSERT(this, + IntPtrGreaterThanOrEqual(target_byte_length, IntPtrConstant(0))); + + TNode<IntPtrT> target_data_end_ptr = + IntPtrAdd(target_data_ptr, target_byte_length); + TNode<IntPtrT> source_data_end_ptr = + IntPtrAdd(source_data_ptr, source_byte_length); + + GotoIfNot( + Word32Or(IntPtrLessThanOrEqual(target_data_end_ptr, source_data_ptr), + IntPtrLessThanOrEqual(source_data_end_ptr, target_data_ptr)), + call_runtime); + + TNode<IntPtrT> source_length = + LoadAndUntagObjectField(source, JSTypedArray::kLengthOffset); + CallCCopyTypedArrayElementsToTypedArray(source, target, source_length, + offset); + Goto(&out); + } + + BIND(&out); +} + +void TypedArrayBuiltinsAssembler::SetJSArraySource( + TNode<Context> context, TNode<JSArray> source, TNode<JSTypedArray> target, + TNode<IntPtrT> offset, Label* call_runtime, Label* if_source_too_large) { + CSA_ASSERT(this, IntPtrGreaterThanOrEqual(offset, IntPtrConstant(0))); + CSA_ASSERT(this, + IntPtrLessThanOrEqual(offset, IntPtrConstant(Smi::kMaxValue))); + + TNode<IntPtrT> source_length = SmiUntag(LoadFastJSArrayLength(source)); + TNode<IntPtrT> target_length = + LoadAndUntagObjectField(target, JSTypedArray::kLengthOffset); + + // Maybe out of bounds? + GotoIf(IntPtrGreaterThan(IntPtrAdd(source_length, offset), target_length), + if_source_too_large); + + // Nothing to do if {source} is empty. + Label out(this), fast_c_call(this); + GotoIf(IntPtrEqual(source_length, IntPtrConstant(0)), &out); + + // Dispatch based on the source elements kind. + { + // These are the supported elements kinds in TryCopyElementsFastNumber. + int32_t values[] = { + PACKED_SMI_ELEMENTS, HOLEY_SMI_ELEMENTS, PACKED_DOUBLE_ELEMENTS, + HOLEY_DOUBLE_ELEMENTS, + }; + Label* labels[] = { + &fast_c_call, &fast_c_call, &fast_c_call, &fast_c_call, + }; + STATIC_ASSERT(arraysize(values) == arraysize(labels)); + + TNode<Int32T> source_elements_kind = LoadMapElementsKind(LoadMap(source)); + Switch(source_elements_kind, call_runtime, values, labels, + arraysize(values)); + } + + BIND(&fast_c_call); + CallCCopyFastNumberJSArrayElementsToTypedArray(context, source, target, + source_length, offset); + Goto(&out); + BIND(&out); +} + +void TypedArrayBuiltinsAssembler::CallCMemmove(TNode<IntPtrT> dest_ptr, + TNode<IntPtrT> src_ptr, + TNode<IntPtrT> byte_length) { + TNode<ExternalReference> memmove = + ExternalConstant(ExternalReference::libc_memmove_function(isolate())); + CallCFunction3(MachineType::AnyTagged(), MachineType::Pointer(), + MachineType::Pointer(), MachineType::UintPtr(), memmove, + dest_ptr, src_ptr, byte_length); +} + +void TypedArrayBuiltinsAssembler:: + CallCCopyFastNumberJSArrayElementsToTypedArray(TNode<Context> context, + TNode<JSArray> source, + TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, + TNode<IntPtrT> offset) { + TNode<ExternalReference> f = ExternalConstant( + ExternalReference::copy_fast_number_jsarray_elements_to_typed_array( + isolate())); + CallCFunction5(MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::UintPtr(), MachineType::UintPtr(), f, context, + source, dest, source_length, offset); +} + +void TypedArrayBuiltinsAssembler::CallCCopyTypedArrayElementsToTypedArray( + TNode<JSTypedArray> source, TNode<JSTypedArray> dest, + TNode<IntPtrT> source_length, TNode<IntPtrT> offset) { + TNode<ExternalReference> f = ExternalConstant( + ExternalReference::copy_typed_array_elements_to_typed_array(isolate())); + CallCFunction4(MachineType::AnyTagged(), MachineType::AnyTagged(), + MachineType::AnyTagged(), MachineType::UintPtr(), + MachineType::UintPtr(), f, source, dest, source_length, + offset); +} + +// ES #sec-get-%typedarray%.prototype.set +TF_BUILTIN(TypedArrayPrototypeSet, TypedArrayBuiltinsAssembler) { + TNode<Context> context = CAST(Parameter(BuiltinDescriptor::kContext)); + CodeStubArguments args( + this, ChangeInt32ToIntPtr(Parameter(BuiltinDescriptor::kArgumentsCount))); + + Label if_source_is_typed_array(this), if_source_is_fast_jsarray(this), + if_offset_is_out_of_bounds(this, Label::kDeferred), + if_source_too_large(this, Label::kDeferred), + if_typed_array_is_neutered(this, Label::kDeferred), + if_receiver_is_not_typedarray(this, Label::kDeferred); + + // Check the receiver is a typed array. + TNode<Object> receiver = args.GetReceiver(); + GotoIf(TaggedIsSmi(receiver), &if_receiver_is_not_typedarray); + GotoIfNot(IsJSTypedArray(receiver), &if_receiver_is_not_typedarray); + + // Normalize offset argument (using ToInteger) and handle heap number cases. + TNode<Object> offset = args.GetOptionalArgumentValue(1, SmiConstant(0)); + TNode<Object> offset_num = ToInteger(context, offset, kTruncateMinusZero); + CSA_ASSERT(this, IsNumberNormalized(offset_num)); + + // Since ToInteger always returns a Smi if the given value is within Smi + // range, and the only corner case of -0.0 has already been truncated to 0.0, + // we can simply throw unless the offset is a non-negative Smi. + // TODO(jgruber): It's an observable spec violation to throw here if + // {offset_num} is a positive number outside the Smi range. Per spec, we need + // to check for detached buffers and call the observable ToObject/ToLength + // operations first. + GotoIfNot(TaggedIsPositiveSmi(offset_num), &if_offset_is_out_of_bounds); + TNode<Smi> offset_smi = CAST(offset_num); + + // Check the receiver is not neutered. + TNode<Object> receiver_buffer = + LoadObjectField(CAST(receiver), JSTypedArray::kBufferOffset); + GotoIf(IsDetachedBuffer(receiver_buffer), &if_typed_array_is_neutered); + + // Check the source argument is valid and whether a fast path can be taken. + Label call_runtime(this); + TNode<Object> source = args.GetOptionalArgumentValue(0); + GotoIf(TaggedIsSmi(source), &call_runtime); + GotoIf(IsJSTypedArray(source), &if_source_is_typed_array); + BranchIfFastJSArray(source, context, &if_source_is_fast_jsarray, + &call_runtime); + + // Fast path for a typed array source argument. + BIND(&if_source_is_typed_array); + { + // Check the source argument is not neutered. + TNode<Object> source_buffer = + LoadObjectField(CAST(source), JSTypedArray::kBufferOffset); + GotoIf(IsDetachedBuffer(source_buffer), &if_typed_array_is_neutered); + + SetTypedArraySource(context, CAST(source), CAST(receiver), + SmiUntag(offset_smi), &call_runtime, + &if_source_too_large); + args.PopAndReturn(UndefinedConstant()); + } + + // Fast path for a fast JSArray source argument. + BIND(&if_source_is_fast_jsarray); + { + SetJSArraySource(context, CAST(source), CAST(receiver), + SmiUntag(offset_smi), &call_runtime, &if_source_too_large); + args.PopAndReturn(UndefinedConstant()); + } + + BIND(&call_runtime); + args.PopAndReturn(CallRuntime(Runtime::kTypedArraySet, context, receiver, + source, offset_smi)); + + BIND(&if_offset_is_out_of_bounds); + ThrowRangeError(context, MessageTemplate::kTypedArraySetOffsetOutOfBounds); + + BIND(&if_source_too_large); + ThrowRangeError(context, MessageTemplate::kTypedArraySetSourceTooLarge); + + BIND(&if_typed_array_is_neutered); + ThrowTypeError(context, MessageTemplate::kDetachedOperation, + "%TypedArray%.prototype.set"); + + BIND(&if_receiver_is_not_typedarray); + ThrowTypeError(context, MessageTemplate::kNotTypedArray); +} + // ES #sec-get-%typedarray%.prototype-@@tostringtag TF_BUILTIN(TypedArrayPrototypeToStringTag, TypedArrayBuiltinsAssembler) { Node* receiver = Parameter(Descriptor::kReceiver); diff --git a/chromium/v8/src/builtins/builtins-typedarray.cc b/chromium/v8/src/builtins/builtins-typedarray.cc index 3d40d3755f8..176a79965b2 100644 --- a/chromium/v8/src/builtins/builtins-typedarray.cc +++ b/chromium/v8/src/builtins/builtins-typedarray.cc @@ -277,302 +277,6 @@ BUILTIN(TypedArrayPrototypeReverse) { return *array; } -namespace { -Object* TypedArrayCopyElements(Handle<JSTypedArray> target, - Handle<JSReceiver> source, Object* length_obj) { - size_t length; - CHECK(TryNumberToSize(length_obj, &length)); - - ElementsAccessor* accessor = target->GetElementsAccessor(); - return accessor->CopyElements(source, target, length); -} - -enum class TypedArraySetResultCodes { - // Set from typed array of the same type. - // This is processed by TypedArraySetFastCases - SAME_TYPE, - // Set from typed array of the different type, overlapping in memory. - OVERLAPPING, - // Set from typed array of the different type, non-overlapping. - NONOVERLAPPING, - // Set from non-typed array. - NON_TYPED_ARRAY -}; - -MaybeHandle<Object> TypedArraySetFromArrayLike(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<Object> source, - int source_length, int offset) { - DCHECK_GE(source_length, 0); - DCHECK_GE(offset, 0); - - for (int i = 0; i < source_length; i++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, i), Object); - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::SetElement(isolate, target, offset + i, - value, LanguageMode::STRICT), - Object); - } - - return target; -} - -MaybeHandle<Object> TypedArraySetFromOverlapping(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<JSTypedArray> source, - int offset) { - DCHECK_GE(offset, 0); - - size_t sourceElementSize = source->element_size(); - size_t targetElementSize = target->element_size(); - - uint32_t source_length = source->length_value(); - if (source_length == 0) return target; - - // Copy left part. - - // First un-mutated byte after the next write - uint32_t target_ptr = 0; - CHECK(target->byte_offset()->ToUint32(&target_ptr)); - target_ptr += (offset + 1) * targetElementSize; - - // Next read at sourcePtr. We do not care for memory changing before - // sourcePtr - we have already copied it. - uint32_t source_ptr = 0; - CHECK(source->byte_offset()->ToUint32(&source_ptr)); - - uint32_t left_index; - for (left_index = 0; left_index < source_length && target_ptr <= source_ptr; - left_index++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, left_index), - Object); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + left_index, value, - LanguageMode::STRICT), - Object); - - target_ptr += targetElementSize; - source_ptr += sourceElementSize; - } - - // Copy right part; - // First unmutated byte before the next write - CHECK(target->byte_offset()->ToUint32(&target_ptr)); - target_ptr += (offset + source_length - 1) * targetElementSize; - - // Next read before sourcePtr. We do not care for memory changing after - // sourcePtr - we have already copied it. - CHECK(target->byte_offset()->ToUint32(&source_ptr)); - source_ptr += source_length * sourceElementSize; - - uint32_t right_index; - DCHECK_GE(source_length, 1); - for (right_index = source_length - 1; - right_index > left_index && target_ptr >= source_ptr; right_index--) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, right_index), - Object); - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + right_index, value, - LanguageMode::STRICT), - Object); - - target_ptr -= targetElementSize; - source_ptr -= sourceElementSize; - } - - std::vector<Handle<Object>> temp(right_index + 1 - left_index); - - for (uint32_t i = left_index; i <= right_index; i++) { - Handle<Object> value; - ASSIGN_RETURN_ON_EXCEPTION(isolate, value, - Object::GetElement(isolate, source, i), Object); - temp[i - left_index] = value; - } - - for (uint32_t i = left_index; i <= right_index; i++) { - Handle<Object> value; - - ASSIGN_RETURN_ON_EXCEPTION( - isolate, value, - Object::SetElement(isolate, target, offset + i, temp[i - left_index], - LanguageMode::STRICT), - Object); - } - - return target; -} - -MaybeHandle<Smi> TypedArraySetFastCases(Isolate* isolate, - Handle<JSTypedArray> target, - Handle<Object> source_obj, - Handle<Object> offset_obj) { - if (!source_obj->IsJSTypedArray()) { - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::NON_TYPED_ARRAY), isolate); - } - - Handle<JSTypedArray> source = Handle<JSTypedArray>::cast(source_obj); - - size_t offset = 0; - CHECK(TryNumberToSize(*offset_obj, &offset)); - size_t target_length = target->length_value(); - size_t source_length = source->length_value(); - size_t target_byte_length = NumberToSize(target->byte_length()); - size_t source_byte_length = NumberToSize(source->byte_length()); - if (offset > target_length || offset + source_length > target_length || - offset + source_length < offset) { // overflow - THROW_NEW_ERROR( - isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge), - Smi); - } - - size_t target_offset = NumberToSize(target->byte_offset()); - size_t source_offset = NumberToSize(source->byte_offset()); - uint8_t* target_base = - static_cast<uint8_t*>(target->GetBuffer()->backing_store()) + - target_offset; - uint8_t* source_base = - static_cast<uint8_t*>(source->GetBuffer()->backing_store()) + - source_offset; - - // Typed arrays of the same type: use memmove. - if (target->type() == source->type()) { - memmove(target_base + offset * target->element_size(), source_base, - source_byte_length); - return MaybeHandle<Smi>(Smi::FromEnum(TypedArraySetResultCodes::SAME_TYPE), - isolate); - } - - // Typed arrays of different types over the same backing store - if ((source_base <= target_base && - source_base + source_byte_length > target_base) || - (target_base <= source_base && - target_base + target_byte_length > source_base)) { - // We do not support overlapping ArrayBuffers - DCHECK(target->GetBuffer()->backing_store() == - source->GetBuffer()->backing_store()); - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::OVERLAPPING), isolate); - } else { // Non-overlapping typed arrays - return MaybeHandle<Smi>( - Smi::FromEnum(TypedArraySetResultCodes::NONOVERLAPPING), isolate); - } -} - -} // anonymous namespace - -// 22.2.3.23%TypedArray%.prototype.set ( overloaded [ , offset ] ) -BUILTIN(TypedArrayPrototypeSet) { - HandleScope scope(isolate); - - Handle<Object> target = args.receiver(); - Handle<Object> obj = args.atOrUndefined(isolate, 1); - Handle<Object> offset = args.atOrUndefined(isolate, 2); - - if (offset->IsUndefined(isolate)) { - offset = Handle<Object>(Smi::kZero, isolate); - } else { - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, offset, - Object::ToInteger(isolate, offset)); - } - - if (offset->Number() < 0) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError(MessageTemplate::kTypedArraySetNegativeOffset)); - } - - if (offset->Number() > Smi::kMaxValue) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge)); - } - - if (!target->IsJSTypedArray()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kNotTypedArray)); - } - auto int_offset = static_cast<int>(offset->Number()); - - Handle<Smi> result_code; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, result_code, - TypedArraySetFastCases(isolate, Handle<JSTypedArray>::cast(target), obj, - offset)); - - switch (static_cast<TypedArraySetResultCodes>(result_code->value())) { - case TypedArraySetResultCodes::SAME_TYPE: { - break; - } - case TypedArraySetResultCodes::OVERLAPPING: { - RETURN_FAILURE_ON_EXCEPTION( - isolate, TypedArraySetFromOverlapping( - isolate, Handle<JSTypedArray>::cast(target), - Handle<JSTypedArray>::cast(obj), int_offset)); - break; - } - case TypedArraySetResultCodes::NONOVERLAPPING: { - if (int_offset == 0) { - TypedArrayCopyElements(Handle<JSTypedArray>::cast(target), - Handle<JSTypedArray>::cast(obj), - Handle<JSTypedArray>::cast(obj)->length()); - } else { - RETURN_FAILURE_ON_EXCEPTION( - isolate, - TypedArraySetFromArrayLike( - isolate, Handle<JSTypedArray>::cast(target), obj, - Handle<JSTypedArray>::cast(obj)->length_value(), int_offset)); - } - break; - } - case TypedArraySetResultCodes::NON_TYPED_ARRAY: { - if (obj->IsNumber()) { - // For number as a first argument, throw TypeError - // instead of silently ignoring the call, so that - // users know they did something wrong. - // (Consistent with Firefox and Blink/WebKit) - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, NewTypeError(MessageTemplate::kInvalidArgument)); - } - - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, obj, - Object::ToObject(isolate, obj)); - - Handle<Object> len; - ASSIGN_RETURN_FAILURE_ON_EXCEPTION( - isolate, len, - Object::GetProperty(obj, isolate->factory()->length_string())); - if (len->IsUndefined(isolate)) { - break; - } - ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, len, - Object::ToLength(isolate, len)); - - DCHECK_GE(int_offset, 0); - if (int_offset + len->Number() > - Handle<JSTypedArray>::cast(target)->length_value()) { - THROW_NEW_ERROR_RETURN_FAILURE( - isolate, - NewRangeError(MessageTemplate::kTypedArraySetSourceTooLarge)); - } - uint32_t int_l; - CHECK(DoubleToUint32IfEqualToSelf(len->Number(), &int_l)); - RETURN_FAILURE_ON_EXCEPTION( - isolate, TypedArraySetFromArrayLike( - isolate, Handle<JSTypedArray>::cast(target), obj, int_l, - int_offset)); - } break; - } - - return *isolate->factory()->undefined_value(); -} - BUILTIN(TypedArrayPrototypeSlice) { HandleScope scope(isolate); |