Commit 06fef85b authored by Peter Marshall's avatar Peter Marshall Committed by Commit Bot

[builtins] Port TypedArrayConstructByArrayBuffer to CodeStubAssembler.

Part of the performance and refactoring work to move the TypedArray
constructors into CSA. This CL moves ConstructByArrayBuffer from JS
to CSA.

BUG=v8:5977

Change-Id: I0a200e6b3f6261ea2372ea9c3d3ca98e313cf2c5
Reviewed-on: https://chromium-review.googlesource.com/451620
Commit-Queue: Peter Marshall <petermarshall@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43747}
parent d9bcc067
......@@ -2647,6 +2647,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
InstallWithIntrinsicDefaultProto(
isolate, construct_by_length,
Context::TYPED_ARRAY_CONSTRUCT_BY_LENGTH_INDEX);
// %typed_array_construct_by_array_buffer
Handle<JSFunction> construct_by_buffer = SimpleCreateFunction(
isolate,
factory->NewStringFromAsciiChecked("typedArrayConstructByArrayBuffer"),
Builtins::kTypedArrayConstructByArrayBuffer, 5, false);
InstallWithIntrinsicDefaultProto(
isolate, construct_by_buffer,
Context::TYPED_ARRAY_CONSTRUCT_BY_ARRAY_BUFFER_INDEX);
}
{ // -- D a t a V i e w
......
......@@ -23,14 +23,18 @@ class TypedArrayBuiltinsAssembler : public CodeStubAssembler {
template <IterationKind kIterationKind>
void GenerateTypedArrayPrototypeIterationMethod(const char* method_name);
void LoadMapAndElementsSize(Node* array, Variable* typed_map, Variable* size);
void DoInitialize(Node* holder, Node* length, Node* maybe_buffer,
Node* byte_offset, Node* byte_length, Node* initialize,
Node* context);
void LoadMapAndElementsSize(Node* const array, Variable* typed_map,
Variable* size);
void CalculateExternalPointer(Node* const backing_store,
Node* const byte_offset,
Variable* external_pointer);
void DoInitialize(Node* const holder, Node* length, Node* const maybe_buffer,
Node* const byte_offset, Node* byte_length,
Node* const initialize, Node* const context);
};
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* array,
void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* const array,
Variable* typed_map,
Variable* size) {
Label unreachable(this), done(this);
......@@ -75,9 +79,42 @@ void TypedArrayBuiltinsAssembler::LoadMapAndElementsSize(Node* array,
Bind(&done);
}
void TypedArrayBuiltinsAssembler::DoInitialize(
Node* holder, Node* length, Node* maybe_buffer, Node* byte_offset,
Node* byte_length, Node* initialize, Node* context) {
// The byte_offset can be higher than Smi range, in which case to perform the
// pointer arithmetic necessary to calculate external_pointer, converting
// byte_offset to an intptr is more difficult. The max byte_offset is 8 * MaxSmi
// on the particular platform. 32 bit platforms are self-limiting, because we
// 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.
void TypedArrayBuiltinsAssembler::CalculateExternalPointer(
Node* const backing_store, Node* const byte_offset,
Variable* external_pointer) {
Label offset_is_smi(this), offset_not_smi(this), done(this);
Branch(TaggedIsSmi(byte_offset), &offset_is_smi, &offset_not_smi);
Bind(&offset_is_smi);
{
external_pointer->Bind(IntPtrAdd(backing_store, SmiToWord(byte_offset)));
Goto(&done);
}
Bind(&offset_not_smi);
{
Node* heap_number = LoadHeapNumberValue(byte_offset);
Node* intrptr_value = ChangeFloat64ToUintPtr(heap_number);
external_pointer->Bind(IntPtrAdd(backing_store, intrptr_value));
Goto(&done);
}
Bind(&done);
}
void TypedArrayBuiltinsAssembler::DoInitialize(Node* const holder, Node* length,
Node* const maybe_buffer,
Node* const byte_offset,
Node* byte_length,
Node* const initialize,
Node* const context) {
static const int32_t fta_base_data_offset =
FixedTypedArrayBase::kDataOffset - kHeapObjectTag;
......@@ -91,10 +128,6 @@ void TypedArrayBuiltinsAssembler::DoInitialize(
length = ToInteger(context, length, CodeStubAssembler::kTruncateMinusZero);
CSA_ASSERT(this, TaggedIsSmi(length));
byte_offset =
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
CSA_ASSERT(this, TaggedIsSmi(byte_offset));
// byte_length can be -0, get rid of it.
byte_length =
ToInteger(context, byte_length, CodeStubAssembler::kTruncateMinusZero);
......@@ -135,7 +168,7 @@ void TypedArrayBuiltinsAssembler::DoInitialize(
LoadContextElement(native_context, Context::ARRAY_BUFFER_MAP_INDEX);
Node* empty_fixed_array = LoadRoot(Heap::kEmptyFixedArrayRootIndex);
Node* buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
Node* const buffer = Allocate(JSArrayBuffer::kSizeWithInternalFields);
StoreMapNoWriteBarrier(buffer, map);
StoreObjectFieldNoWriteBarrier(buffer, JSArray::kPropertiesOffset,
empty_fixed_array);
......@@ -228,16 +261,17 @@ void TypedArrayBuiltinsAssembler::DoInitialize(
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kBasePointerOffset, SmiConstant(0));
Variable external_pointer(this, MachineType::PointerRepresentation());
Node* backing_store =
LoadObjectField(maybe_buffer, JSArrayBuffer::kBackingStoreOffset,
MachineType::Pointer());
Node* external_pointer = IntPtrAdd(backing_store, SmiToWord(byte_offset));
CalculateExternalPointer(backing_store, byte_offset, &external_pointer);
StoreObjectFieldNoWriteBarrier(
elements, FixedTypedArrayBase::kExternalPointerOffset, external_pointer,
MachineType::PointerRepresentation());
elements, FixedTypedArrayBase::kExternalPointerOffset,
external_pointer.value(), MachineType::PointerRepresentation());
StoreObjectField(holder, JSObject::kElementsOffset, elements);
Goto(&done);
}
......@@ -246,13 +280,13 @@ void TypedArrayBuiltinsAssembler::DoInitialize(
}
TF_BUILTIN(TypedArrayInitialize, TypedArrayBuiltinsAssembler) {
Node* holder = Parameter(1);
Node* const holder = Parameter(1);
Node* length = Parameter(2);
Node* maybe_buffer = Parameter(3);
Node* byte_offset = Parameter(4);
Node* const maybe_buffer = Parameter(3);
Node* const byte_offset = Parameter(4);
Node* byte_length = Parameter(5);
Node* initialize = Parameter(6);
Node* context = Parameter(9);
Node* const initialize = Parameter(6);
Node* const context = Parameter(9);
DoInitialize(holder, length, maybe_buffer, byte_offset, byte_length,
initialize, context);
......@@ -314,6 +348,127 @@ TF_BUILTIN(TypedArrayConstructByLength, TypedArrayBuiltinsAssembler) {
}
}
// ES6 section 22.2.4.5 TypedArray ( buffer [ , byteOffset [ , length ] ] )
TF_BUILTIN(TypedArrayConstructByArrayBuffer, TypedArrayBuiltinsAssembler) {
Node* const holder = Parameter(1);
Node* const buffer = Parameter(2);
Node* byte_offset = Parameter(3);
Node* const length = Parameter(4);
Node* const element_size = Parameter(5);
CSA_ASSERT(this, TaggedIsSmi(element_size));
Node* const context = Parameter(8);
Node* const initialize = BooleanConstant(true);
Variable new_byte_length(this, MachineRepresentation::kTagged,
SmiConstant(0));
Label start_offset_error(this), byte_length_error(this),
invalid_offset_error(this);
Label call_init(this), invalid_length(this), length_undefined(this),
length_defined(this);
Callable add = CodeFactory::Add(isolate());
Callable div = CodeFactory::Divide(isolate());
Callable equal = CodeFactory::Equal(isolate());
Callable greater_than = CodeFactory::GreaterThan(isolate());
Callable less_than = CodeFactory::LessThan(isolate());
Callable mod = CodeFactory::Modulus(isolate());
Callable sub = CodeFactory::Subtract(isolate());
byte_offset =
ToInteger(context, byte_offset, CodeStubAssembler::kTruncateMinusZero);
GotoIf(IsTrue(CallStub(less_than, context, byte_offset, SmiConstant(0))),
&invalid_length);
Node* remainder = CallStub(mod, context, byte_offset, element_size);
// Remainder can be a heap number.
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
&start_offset_error);
// TODO(petermarshall): Throw on detached typedArray.
Branch(IsUndefined(length), &length_undefined, &length_defined);
Bind(&length_undefined);
{
Node* buffer_byte_length =
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
Node* remainder = CallStub(mod, context, buffer_byte_length, element_size);
// Remainder can be a heap number.
GotoIf(IsFalse(CallStub(equal, context, remainder, SmiConstant(0))),
&byte_length_error);
new_byte_length.Bind(
CallStub(sub, context, buffer_byte_length, byte_offset));
Branch(IsTrue(CallStub(less_than, context, new_byte_length.value(),
SmiConstant(0))),
&invalid_offset_error, &call_init);
}
Bind(&length_defined);
{
Node* new_length = ToSmiIndex(length, context, &invalid_length);
new_byte_length.Bind(SmiMul(new_length, element_size));
// Reading the byte length must come after the ToIndex operation, which
// could cause the buffer to become detached.
Node* buffer_byte_length =
LoadObjectField(buffer, JSArrayBuffer::kByteLengthOffset);
Node* end = CallStub(add, context, byte_offset, new_byte_length.value());
Branch(IsTrue(CallStub(greater_than, context, end, buffer_byte_length)),
&invalid_length, &call_init);
}
Bind(&call_init);
{
Node* new_length =
CallStub(div, context, new_byte_length.value(), element_size);
// Force the result into a Smi, or throw a range error if it doesn't fit.
new_length = ToSmiIndex(new_length, context, &invalid_length);
DoInitialize(holder, new_length, buffer, byte_offset,
new_byte_length.value(), initialize, context);
}
Bind(&invalid_offset_error);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidOffset), byte_offset);
Unreachable();
}
Bind(&start_offset_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = HeapConstant(
factory()->NewStringFromAsciiChecked("start offset", TENURED));
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
Bind(&byte_length_error);
{
Node* holder_map = LoadMap(holder);
Node* problem_string = HeapConstant(
factory()->NewStringFromAsciiChecked("byte length", TENURED));
CallRuntime(Runtime::kThrowInvalidTypedArrayAlignment, context, holder_map,
problem_string);
Unreachable();
}
Bind(&invalid_length);
{
CallRuntime(Runtime::kThrowRangeError, context,
SmiConstant(MessageTemplate::kInvalidTypedArrayLength));
Unreachable();
}
}
// ES6 section 22.2.3.1 get %TypedArray%.prototype.buffer
BUILTIN(TypedArrayPrototypeBuffer) {
HandleScope scope(isolate);
......
......@@ -808,6 +808,7 @@ class Isolate;
TFJ(SymbolPrototypeValueOf, 0) \
\
/* TypedArray */ \
TFJ(TypedArrayConstructByArrayBuffer, 5) \
TFJ(TypedArrayConstructByLength, 3) \
TFJ(TypedArrayInitialize, 6) \
CPP(TypedArrayPrototypeBuffer) \
......
......@@ -4213,6 +4213,35 @@ Node* CodeStubAssembler::JSReceiverToPrimitive(Node* context, Node* input) {
return result.value();
}
Node* CodeStubAssembler::ToSmiIndex(Node* const input, Node* const context,
Label* range_error) {
Variable result(this, MachineRepresentation::kTagged, input);
Label check_undefined(this), undefined(this), defined(this),
negative_check(this), done(this);
Branch(TaggedIsSmi(result.value()), &negative_check, &check_undefined);
Bind(&check_undefined);
Branch(IsUndefined(result.value()), &undefined, &defined);
Bind(&defined);
result.Bind(ToInteger(context, result.value(),
CodeStubAssembler::kTruncateMinusZero));
GotoIfNot(TaggedIsSmi(result.value()), range_error);
CSA_ASSERT(this, TaggedIsSmi(result.value()));
Goto(&negative_check);
Bind(&negative_check);
GotoIf(SmiLessThan(result.value(), SmiConstant(0)), range_error);
Goto(&done);
Bind(&undefined);
result.Bind(SmiConstant(0));
Goto(&done);
Bind(&done);
return result.value();
}
Node* CodeStubAssembler::ToInteger(Node* context, Node* input,
ToIntegerTruncationMode mode) {
// We might need to loop once for ToNumber conversion.
......
......@@ -782,6 +782,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
kTruncateMinusZero,
};
// ES6 7.1.17 ToIndex, but jumps to range_error if the result is not a Smi.
Node* ToSmiIndex(Node* const input, Node* const context, Label* range_error);
// Convert any object to an Integer.
Node* ToInteger(Node* context, Node* input,
ToIntegerTruncationMode mode = kNoTruncation);
......
......@@ -1258,6 +1258,7 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
V(ChangeFloat64ToInt32, kArm64Float64ToInt32) \
V(TruncateFloat32ToUint32, kArm64Float32ToUint32) \
V(ChangeFloat64ToUint32, kArm64Float64ToUint32) \
V(ChangeFloat64ToUint64, kArm64Float64ToUint64) \
V(TruncateFloat64ToUint32, kArm64Float64ToUint32) \
V(TruncateFloat64ToFloat32, kArm64Float64ToFloat32) \
V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \
......
......@@ -391,6 +391,13 @@ Node* CodeAssembler::ChangeInt32ToIntPtr(Node* value) {
return value;
}
Node* CodeAssembler::ChangeFloat64ToUintPtr(Node* value) {
if (raw_assembler()->machine()->Is64()) {
return raw_assembler()->ChangeFloat64ToUint64(value);
}
return raw_assembler()->ChangeFloat64ToUint32(value);
}
Node* CodeAssembler::RoundIntPtrToFloat64(Node* value) {
if (raw_assembler()->machine()->Is64()) {
return raw_assembler()->RoundInt64ToFloat64(value);
......
......@@ -153,6 +153,7 @@ typedef std::function<void()> CodeAssemblerCallback;
V(TruncateInt64ToInt32) \
V(ChangeFloat32ToFloat64) \
V(ChangeFloat64ToUint32) \
V(ChangeFloat64ToUint64) \
V(ChangeInt32ToFloat64) \
V(ChangeInt32ToInt64) \
V(ChangeUint32ToFloat64) \
......@@ -297,6 +298,10 @@ class V8_EXPORT_PRIVATE CodeAssembler {
CODE_ASSEMBLER_UNARY_OP_LIST(DECLARE_CODE_ASSEMBLER_UNARY_OP)
#undef DECLARE_CODE_ASSEMBLER_UNARY_OP
// Changes a double to an inptr_t for pointer arithmetic outside of Smi range.
// Assumes that the double can be exactly represented as an int.
Node* ChangeFloat64ToUintPtr(Node* value);
// Changes an intptr_t to a double, e.g. for storing an element index
// outside Smi range in a HeapNumber. Lossless on 32-bit,
// rounds on 64-bit (which doesn't affect valid element indices).
......
......@@ -1229,6 +1229,8 @@ void InstructionSelector::VisitNode(Node* node) {
return MarkAsWord32(node), VisitChangeFloat64ToInt32(node);
case IrOpcode::kChangeFloat64ToUint32:
return MarkAsWord32(node), VisitChangeFloat64ToUint32(node);
case IrOpcode::kChangeFloat64ToUint64:
return MarkAsWord64(node), VisitChangeFloat64ToUint64(node);
case IrOpcode::kFloat64SilenceNaN:
MarkAsFloat64(node);
if (CanProduceSignalingNaN(node->InputAt(0))) {
......@@ -1961,6 +1963,9 @@ void InstructionSelector::VisitChangeUint32ToUint64(Node* node) {
UNIMPLEMENTED();
}
void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) {
UNIMPLEMENTED();
}
void InstructionSelector::VisitTryTruncateFloat32ToInt64(Node* node) {
UNIMPLEMENTED();
......
......@@ -57,7 +57,6 @@ class MachineRepresentationInferrer {
case IrOpcode::kTryTruncateFloat32ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64:
case IrOpcode::kTryTruncateFloat32ToUint64:
case IrOpcode::kTryTruncateFloat64ToUint64:
CHECK_LE(index, static_cast<size_t>(1));
return index == 0 ? MachineRepresentation::kWord64
: MachineRepresentation::kBit;
......@@ -234,6 +233,7 @@ class MachineRepresentationInferrer {
case IrOpcode::kInt64Constant:
case IrOpcode::kRelocatableInt64Constant:
case IrOpcode::kBitcastFloat64ToInt64:
case IrOpcode::kChangeFloat64ToUint64:
MACHINE_BINOP_64_LIST(LABEL) {
representation_vector_[node->id()] =
MachineRepresentation::kWord64;
......@@ -335,6 +335,7 @@ class MachineRepresentationChecker {
case IrOpcode::kFloat64ExtractLowWord32:
case IrOpcode::kFloat64ExtractHighWord32:
case IrOpcode::kBitcastFloat64ToInt64:
case IrOpcode::kTryTruncateFloat64ToInt64:
CheckValueInputForFloat64Op(node, 0);
break;
case IrOpcode::kWord64Equal:
......@@ -427,6 +428,7 @@ class MachineRepresentationChecker {
}
break;
case IrOpcode::kFloat64SilenceNaN:
case IrOpcode::kChangeFloat64ToUint64:
MACHINE_FLOAT64_UNOP_LIST(LABEL) {
CheckValueInputForFloat64Op(node, 0);
}
......
......@@ -140,6 +140,7 @@ MachineType AtomicExchangeRepresentationOf(Operator const* op) {
V(ChangeFloat32ToFloat64, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \
V(ChangeFloat64ToUint64, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat64ToUint32, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat32ToInt32, Operator::kNoProperties, 1, 0, 1) \
V(TruncateFloat32ToUint32, Operator::kNoProperties, 1, 0, 1) \
......
......@@ -301,6 +301,7 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final
const Operator* ChangeFloat32ToFloat64();
const Operator* ChangeFloat64ToInt32(); // narrowing
const Operator* ChangeFloat64ToUint32(); // narrowing
const Operator* ChangeFloat64ToUint64();
const Operator* TruncateFloat64ToUint32();
const Operator* TruncateFloat32ToInt32();
const Operator* TruncateFloat32ToUint32();
......
......@@ -1231,6 +1231,10 @@ void InstructionSelector::VisitChangeFloat64ToUint32(Node* node) {
VisitRR(this, kMips64TruncUwD, node);
}
void InstructionSelector::VisitChangeFloat64ToUint64(Node* node) {
VisitRR(this, kMips64TruncUlD, node);
}
void InstructionSelector::VisitTruncateFloat64ToUint32(Node* node) {
VisitRR(this, kMips64TruncUwD, node);
}
......
......@@ -505,6 +505,7 @@
V(ChangeFloat32ToFloat64) \
V(ChangeFloat64ToInt32) \
V(ChangeFloat64ToUint32) \
V(ChangeFloat64ToUint64) \
V(Float64SilenceNaN) \
V(TruncateFloat64ToUint32) \
V(TruncateFloat32ToInt32) \
......
......@@ -572,6 +572,9 @@ class V8_EXPORT_PRIVATE RawMachineAssembler {
Node* ChangeFloat64ToUint32(Node* a) {
return AddNode(machine()->ChangeFloat64ToUint32(), a);
}
Node* ChangeFloat64ToUint64(Node* a) {
return AddNode(machine()->ChangeFloat64ToUint64(), a);
}
Node* TruncateFloat64ToUint32(Node* a) {
return AddNode(machine()->TruncateFloat64ToUint32(), a);
}
......
......@@ -1370,6 +1370,7 @@ void Verifier::Visitor::Check(Node* node) {
case IrOpcode::kChangeFloat32ToFloat64:
case IrOpcode::kChangeFloat64ToInt32:
case IrOpcode::kChangeFloat64ToUint32:
case IrOpcode::kChangeFloat64ToUint64:
case IrOpcode::kFloat64SilenceNaN:
case IrOpcode::kTruncateFloat64ToUint32:
case IrOpcode::kTruncateFloat32ToInt32:
......
......@@ -1332,6 +1332,7 @@ void VisitFloatUnop(InstructionSelector* selector, Node* node, Node* input,
V(ChangeFloat64ToInt32, kSSEFloat64ToInt32) \
V(ChangeFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(1)) \
V(TruncateFloat64ToUint32, kSSEFloat64ToUint32 | MiscField::encode(0)) \
V(ChangeFloat64ToUint64, kSSEFloat64ToUint64) \
V(TruncateFloat64ToFloat32, kSSEFloat64ToFloat32) \
V(ChangeFloat32ToFloat64, kSSEFloat32ToFloat64) \
V(TruncateFloat32ToInt32, kSSEFloat32ToInt32) \
......
......@@ -68,6 +68,8 @@ enum ContextLookupFlags {
V(REFLECT_DELETE_PROPERTY_INDEX, JSFunction, reflect_delete_property) \
V(SPREAD_ARGUMENTS_INDEX, JSFunction, spread_arguments) \
V(SPREAD_ITERABLE_INDEX, JSFunction, spread_iterable) \
V(TYPED_ARRAY_CONSTRUCT_BY_ARRAY_BUFFER_INDEX, JSFunction, \
typed_array_construct_by_array_buffer) \
V(TYPED_ARRAY_CONSTRUCT_BY_LENGTH_INDEX, JSFunction, \
typed_array_construct_by_length) \
V(TYPED_ARRAY_INITIALIZE_INDEX, JSFunction, typed_array_initialize) \
......
......@@ -127,51 +127,6 @@ function TypedArraySpeciesCreate(exemplar, arg0, arg1, arg2, conservative) {
}
macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
var offset;
if (!IS_UNDEFINED(byteOffset)) {
offset = ToIndex(byteOffset, kInvalidTypedArrayLength);
if (offset % ELEMENT_SIZE !== 0) {
throw %make_range_error(kInvalidTypedArrayAlignment,
"start offset", "NAME", ELEMENT_SIZE);
}
} else {
offset = 0;
}
if (!IS_UNDEFINED(length)) {
length = ToIndex(length, kInvalidTypedArrayLength);
}
if (length > %_MaxSmi()) {
// Note: this is not per spec, but rather a constraint of our current
// representation (which uses smi's).
throw %make_range_error(kInvalidTypedArrayLength);
}
var bufferByteLength = %_ArrayBufferGetByteLength(buffer);
var newByteLength;
if (IS_UNDEFINED(length)) {
if (bufferByteLength % ELEMENT_SIZE !== 0) {
throw %make_range_error(kInvalidTypedArrayAlignment,
"byte length", "NAME", ELEMENT_SIZE);
}
newByteLength = bufferByteLength - offset;
if (newByteLength < 0) {
throw %make_range_error(kInvalidOffset, offset);
}
} else {
newByteLength = length * ELEMENT_SIZE;
if (offset + newByteLength > bufferByteLength) {
throw %make_range_error(kInvalidTypedArrayLength);
}
}
var newLength = newByteLength / ELEMENT_SIZE;
if (newLength > %_MaxSmi()) {
throw %make_range_error(kInvalidTypedArrayLength);
}
%typed_array_initialize(obj, newLength, buffer, offset, newByteLength, true);
}
function NAMEConstructByArrayLike(obj, arrayLike, length) {
var l = ToPositiveInteger(length, kInvalidTypedArrayLength);
......@@ -234,7 +189,8 @@ function NAMEConstructByTypedArray(obj, typedArray) {
function NAMEConstructor(arg1, arg2, arg3) {
if (!IS_UNDEFINED(new.target)) {
if (IS_ARRAYBUFFER(arg1) || IS_SHAREDARRAYBUFFER(arg1)) {
NAMEConstructByArrayBuffer(this, arg1, arg2, arg3);
%typed_array_construct_by_array_buffer(
this, arg1, arg2, arg3, ELEMENT_SIZE);
} else if (IS_TYPEDARRAY(arg1)) {
NAMEConstructByTypedArray(this, arg1);
} else if (IS_RECEIVER(arg1)) {
......
......@@ -120,6 +120,28 @@ RUNTIME_FUNCTION(Runtime_ThrowTypeError) {
#undef THROW_ERROR
RUNTIME_FUNCTION(Runtime_ThrowInvalidTypedArrayAlignment) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Map, map, 0);
CONVERT_ARG_HANDLE_CHECKED(String, problem_string, 1);
ElementsKind kind = map->elements_kind();
Handle<String> type = isolate->factory()->NewStringFromAsciiChecked(
Runtime::ElementsKindToType(kind));
ExternalArrayType external_type =
isolate->factory()->GetArrayTypeFromElementsKind(kind);
size_t size = isolate->factory()->GetExternalArrayElementSize(external_type);
Handle<Object> element_size =
handle(Smi::FromInt(static_cast<int>(size)), isolate);
THROW_NEW_ERROR_RETURN_FAILURE(
isolate, NewRangeError(MessageTemplate::kInvalidTypedArrayAlignment,
problem_string, type, element_size));
}
RUNTIME_FUNCTION(Runtime_UnwindAndFindExceptionHandler) {
SealHandleScope shs(isolate);
DCHECK_EQ(0, args.length());
......
......@@ -61,6 +61,20 @@ void Runtime::ArrayIdToTypeAndSize(int arrayId, ExternalArrayType* array_type,
}
}
const char* Runtime::ElementsKindToType(ElementsKind fixed_elements_kind) {
switch (fixed_elements_kind) {
#define ELEMENTS_KIND_CASE(Type, type, TYPE, ctype, size) \
case TYPE##_ELEMENTS: \
return #Type "Array";
TYPED_ARRAYS(ELEMENTS_KIND_CASE)
#undef ELEMENTS_KIND_CASE
default:
UNREACHABLE();
return "";
}
}
RUNTIME_FUNCTION(Runtime_TypedArrayInitialize) {
HandleScope scope(isolate);
......
......@@ -317,6 +317,7 @@ namespace internal {
F(ThrowIncompatibleMethodReceiver, 2, 1) \
F(ThrowInvalidHint, 1, 1) \
F(ThrowInvalidStringLength, 0, 1) \
F(ThrowInvalidTypedArrayAlignment, 2, 1) \
F(ThrowIteratorResultNotAnObject, 1, 1) \
F(ThrowSymbolIteratorInvalid, 0, 1) \
F(ThrowNonCallableInInstanceOfCheck, 0, 1) \
......@@ -797,6 +798,8 @@ class Runtime : public AllStatic {
ElementsKind* fixed_elements_kind,
size_t* element_size);
static const char* ElementsKindToType(ElementsKind fixed_elements_kind);
static MaybeHandle<JSArray> GetInternalProperties(Isolate* isolate,
Handle<Object>);
};
......
......@@ -29,3 +29,32 @@
}, RangeError);
assertEquals(expected, actual);
})();
(function TestConstructByBufferToPrimitiveOrdering() {
var expected = ["offset.toPrimitive", "length.toPrimitive"];
var actual = [];
var offset = {};
offset[Symbol.toPrimitive] = function() {
actual.push("offset.toPrimitive");
return 1;
};
var length = {};
length[Symbol.toPrimitive] = function() {
actual.push("length.toPrimitive");
return 1;
};
var buffer = new ArrayBuffer(16);
var arr = new Uint8Array(buffer, offset, length);
assertEquals(expected, actual);
assertEquals(1, arr.length);
})();
(function TestByteOffsetToIndexThrowsForNegative() {
var buffer = new ArrayBuffer(16);
assertThrows(function() {
new Uint8Array(buffer, -1);
}, RangeError);
})();
// 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.
// Flags: --allow-natives-syntax
(function TestBufferByteLengthNonSmi() {
var non_smi_byte_length = %_MaxSmi() + 1;
try {
var buffer = new ArrayBuffer(non_smi_byte_length);
} catch (e) {
// The ArrayBuffer allocation can fail on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
var arr = new Uint16Array(buffer);
assertEquals(non_smi_byte_length, arr.byteLength);
assertEquals(non_smi_byte_length / 2, arr.length);
arr = new Uint32Array(buffer);
assertEquals(non_smi_byte_length, arr.byteLength);
assertEquals(non_smi_byte_length / 4, arr.length);
})();
(function TestByteOffsetNonSmi() {
var non_smi_byte_length = %_MaxSmi() + 11;
try {
var buffer = new ArrayBuffer(non_smi_byte_length);
} catch (e) {
// The ArrayBuffer allocation can fail on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
print(buffer.byteLength);
var whole = new Uint16Array(buffer);
whole[non_smi_byte_length / 2 - 1] = 1;
whole[non_smi_byte_length / 2 - 2] = 2;
whole[non_smi_byte_length / 2 - 3] = 3;
whole[non_smi_byte_length / 2 - 4] = 4;
whole[non_smi_byte_length / 2 - 5] = 5;
assertEquals(non_smi_byte_length / 2, whole.length);
assertEquals(1, whole[non_smi_byte_length / 2 - 1]);
var arr = new Uint16Array(buffer, non_smi_byte_length - 10, 5);
assertEquals(non_smi_byte_length, arr.buffer.byteLength);
assertEquals(10, arr.byteLength);
assertEquals(5, arr.length);
assertEquals(5, arr[0]);
assertEquals(4, arr[1]);
assertEquals(3, arr[2]);
assertEquals(2, arr[3]);
assertEquals(1, arr[4]);
})();
......@@ -837,11 +837,21 @@ for(i = 0; i < typedArrayConstructors.length; i++) {
(function TestBufferLengthTooLong() {
try {
var buf = new ArrayBuffer(2147483648);
assertThrows(function() {
new Int8Array(buf);
}, RangeError);
} catch (e) {
// The ArrayBuffer allocation fails on 32-bit archs, so no need to try to
// construct the typed array.
return;
}
assertThrows(function() {
new Int8Array(buf);
}, RangeError);
})();
(function TestByteLengthErrorMessage() {
try {
new Uint32Array(new ArrayBuffer(17));
} catch (e) {
assertEquals("byte length of Uint32Array should be a multiple of 4",
e.message);
}
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment