| // Copyright 2019 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 "test/cctest/test-api.h" |
| |
| #include "src/api/api-inl.h" |
| |
| using ::v8::Array; |
| using ::v8::Context; |
| using ::v8::Local; |
| using ::v8::Value; |
| |
| namespace { |
| |
| void CheckElementValue(i::Isolate* isolate, int expected, |
| i::Handle<i::Object> obj, int offset) { |
| i::Object element = |
| *i::Object::GetElement(isolate, obj, offset).ToHandleChecked(); |
| CHECK_EQ(expected, i::Smi::ToInt(element)); |
| } |
| |
| template <class ElementType> |
| void ObjectWithExternalArrayTestHelper(Local<Context> context, |
| v8::Local<v8::TypedArray> obj, |
| int element_count, |
| i::ExternalArrayType array_type, |
| int64_t low, int64_t high) { |
| i::Handle<i::JSTypedArray> jsobj = v8::Utils::OpenHandle(*obj); |
| v8::Isolate* v8_isolate = context->GetIsolate(); |
| i::Isolate* isolate = reinterpret_cast<i::Isolate*>(v8_isolate); |
| obj->Set(context, v8_str("field"), v8::Int32::New(v8_isolate, 1503)) |
| .FromJust(); |
| CHECK(context->Global()->Set(context, v8_str("ext_array"), obj).FromJust()); |
| v8::Local<v8::Value> result = CompileRun("ext_array.field"); |
| CHECK_EQ(1503, result->Int32Value(context).FromJust()); |
| result = CompileRun("ext_array[1]"); |
| CHECK_EQ(1, result->Int32Value(context).FromJust()); |
| |
| // Check assigned smis |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = i;" |
| "}" |
| "var sum = 0;" |
| "for (var i = 0; i < 8; i++) {" |
| " sum += ext_array[i];" |
| "}" |
| "sum;"); |
| |
| CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| // Check pass through of assigned smis |
| result = CompileRun( |
| "var sum = 0;" |
| "for (var i = 0; i < 8; i++) {" |
| " sum += ext_array[i] = ext_array[i] = -i;" |
| "}" |
| "sum;"); |
| CHECK_EQ(-28, result->Int32Value(context).FromJust()); |
| |
| // Check assigned smis in reverse order |
| result = CompileRun( |
| "for (var i = 8; --i >= 0; ) {" |
| " ext_array[i] = i;" |
| "}" |
| "var sum = 0;" |
| "for (var i = 0; i < 8; i++) {" |
| " sum += ext_array[i];" |
| "}" |
| "sum;"); |
| CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| |
| // Check pass through of assigned HeapNumbers |
| result = CompileRun( |
| "var sum = 0;" |
| "for (var i = 0; i < 16; i+=2) {" |
| " sum += ext_array[i] = ext_array[i] = (-i * 0.5);" |
| "}" |
| "sum;"); |
| CHECK_EQ(-28, result->Int32Value(context).FromJust()); |
| |
| // Check assigned HeapNumbers |
| result = CompileRun( |
| "for (var i = 0; i < 16; i+=2) {" |
| " ext_array[i] = (i * 0.5);" |
| "}" |
| "var sum = 0;" |
| "for (var i = 0; i < 16; i+=2) {" |
| " sum += ext_array[i];" |
| "}" |
| "sum;"); |
| CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| |
| // Check assigned HeapNumbers in reverse order |
| result = CompileRun( |
| "for (var i = 14; i >= 0; i-=2) {" |
| " ext_array[i] = (i * 0.5);" |
| "}" |
| "var sum = 0;" |
| "for (var i = 0; i < 16; i+=2) {" |
| " sum += ext_array[i];" |
| "}" |
| "sum;"); |
| CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| |
| i::ScopedVector<char> test_buf(1024); |
| |
| // Check legal boundary conditions. |
| // The repeated loads and stores ensure the ICs are exercised. |
| const char* boundary_program = |
| "var res = 0;" |
| "for (var i = 0; i < 16; i++) {" |
| " ext_array[i] = %lld;" |
| " if (i > 8) {" |
| " res = ext_array[i];" |
| " }" |
| "}" |
| "res;"; |
| i::SNPrintF(test_buf, boundary_program, low); |
| result = CompileRun(test_buf.begin()); |
| CHECK_EQ(low, result->IntegerValue(context).FromJust()); |
| |
| i::SNPrintF(test_buf, boundary_program, high); |
| result = CompileRun(test_buf.begin()); |
| CHECK_EQ(high, result->IntegerValue(context).FromJust()); |
| |
| // Check misprediction of type in IC. |
| result = CompileRun( |
| "var tmp_array = ext_array;" |
| "var sum = 0;" |
| "for (var i = 0; i < 8; i++) {" |
| " tmp_array[i] = i;" |
| " sum += tmp_array[i];" |
| " if (i == 4) {" |
| " tmp_array = {};" |
| " }" |
| "}" |
| "sum;"); |
| // Force GC to trigger verification. |
| CcTest::CollectAllGarbage(); |
| CHECK_EQ(28, result->Int32Value(context).FromJust()); |
| |
| // Make sure out-of-range loads do not throw. |
| i::SNPrintF(test_buf, |
| "var caught_exception = false;" |
| "try {" |
| " ext_array[%d];" |
| "} catch (e) {" |
| " caught_exception = true;" |
| "}" |
| "caught_exception;", |
| element_count); |
| result = CompileRun(test_buf.begin()); |
| CHECK(!result->BooleanValue(v8_isolate)); |
| |
| // Make sure out-of-range stores do not throw. |
| i::SNPrintF(test_buf, |
| "var caught_exception = false;" |
| "try {" |
| " ext_array[%d] = 1;" |
| "} catch (e) {" |
| " caught_exception = true;" |
| "}" |
| "caught_exception;", |
| element_count); |
| result = CompileRun(test_buf.begin()); |
| CHECK(!result->BooleanValue(v8_isolate)); |
| |
| // Check other boundary conditions, values and operations. |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[7] = undefined;" |
| "}" |
| "ext_array[7];"); |
| CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| if (array_type == i::kExternalFloat64Array || |
| array_type == i::kExternalFloat32Array) { |
| CHECK(std::isnan( |
| i::Object::GetElement(isolate, jsobj, 7).ToHandleChecked()->Number())); |
| } else { |
| CheckElementValue(isolate, 0, jsobj, 7); |
| } |
| |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[6] = '2.3';" |
| "}" |
| "ext_array[6];"); |
| CHECK_EQ(2, result->Int32Value(context).FromJust()); |
| CHECK_EQ(2, static_cast<int>(i::Object::GetElement(isolate, jsobj, 6) |
| .ToHandleChecked() |
| ->Number())); |
| |
| if (array_type != i::kExternalFloat32Array && |
| array_type != i::kExternalFloat64Array) { |
| // Though the specification doesn't state it, be explicit about |
| // converting NaNs and +/-Infinity to zero. |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = 5;" |
| "}" |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = NaN;" |
| "}" |
| "ext_array[5];"); |
| CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| CheckElementValue(isolate, 0, jsobj, 5); |
| |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = 5;" |
| "}" |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = Infinity;" |
| "}" |
| "ext_array[5];"); |
| int expected_value = |
| (array_type == i::kExternalUint8ClampedArray) ? 255 : 0; |
| CHECK_EQ(expected_value, result->Int32Value(context).FromJust()); |
| CheckElementValue(isolate, expected_value, jsobj, 5); |
| |
| result = CompileRun( |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = 5;" |
| "}" |
| "for (var i = 0; i < 8; i++) {" |
| " ext_array[i] = -Infinity;" |
| "}" |
| "ext_array[5];"); |
| CHECK_EQ(0, result->Int32Value(context).FromJust()); |
| CheckElementValue(isolate, 0, jsobj, 5); |
| |
| // Check truncation behavior of integral arrays. |
| const char* unsigned_data = |
| "var source_data = [0.6, 10.6];" |
| "var expected_results = [0, 10];"; |
| const char* signed_data = |
| "var source_data = [0.6, 10.6, -0.6, -10.6];" |
| "var expected_results = [0, 10, 0, -10];"; |
| const char* pixel_data = |
| "var source_data = [0.6, 10.6];" |
| "var expected_results = [1, 11];"; |
| bool is_unsigned = (array_type == i::kExternalUint8Array || |
| array_type == i::kExternalUint16Array || |
| array_type == i::kExternalUint32Array); |
| bool is_pixel_data = array_type == i::kExternalUint8ClampedArray; |
| |
| i::SNPrintF(test_buf, |
| "%s" |
| "var all_passed = true;" |
| "for (var i = 0; i < source_data.length; i++) {" |
| " for (var j = 0; j < 8; j++) {" |
| " ext_array[j] = source_data[i];" |
| " }" |
| " all_passed = all_passed &&" |
| " (ext_array[5] == expected_results[i]);" |
| "}" |
| "all_passed;", |
| (is_unsigned ? unsigned_data |
| : (is_pixel_data ? pixel_data : signed_data))); |
| result = CompileRun(test_buf.begin()); |
| CHECK(result->BooleanValue(v8_isolate)); |
| } |
| |
| { |
| ElementType* data_ptr = static_cast<ElementType*>(jsobj->DataPtr()); |
| for (int i = 0; i < element_count; i++) { |
| data_ptr[i] = static_cast<ElementType>(i); |
| } |
| } |
| |
| bool old_natives_flag_sentry = i::FLAG_allow_natives_syntax; |
| i::FLAG_allow_natives_syntax = true; |
| |
| // Test complex assignments |
| result = CompileRun( |
| "function ee_op_test_complex_func(sum) {" |
| " for (var i = 0; i < 40; ++i) {" |
| " sum += (ext_array[i] += 1);" |
| " sum += (ext_array[i] -= 1);" |
| " } " |
| " return sum;" |
| "};" |
| "%PrepareFunctionForOptimization(ee_op_test_complex_func);" |
| "sum=0;" |
| "sum=ee_op_test_complex_func(sum);" |
| "sum=ee_op_test_complex_func(sum);" |
| "%OptimizeFunctionOnNextCall(ee_op_test_complex_func);" |
| "sum=ee_op_test_complex_func(sum);" |
| "sum;"); |
| CHECK_EQ(4800, result->Int32Value(context).FromJust()); |
| |
| // Test count operations |
| result = CompileRun( |
| "function ee_op_test_count_func(sum) {" |
| " for (var i = 0; i < 40; ++i) {" |
| " sum += (++ext_array[i]);" |
| " sum += (--ext_array[i]);" |
| " } " |
| " return sum;" |
| "};" |
| "%PrepareFunctionForOptimization(ee_op_test_count_func);" |
| "sum=0;" |
| "sum=ee_op_test_count_func(sum);" |
| "sum=ee_op_test_count_func(sum);" |
| "%OptimizeFunctionOnNextCall(ee_op_test_count_func);" |
| "sum=ee_op_test_count_func(sum);" |
| "sum;"); |
| CHECK_EQ(4800, result->Int32Value(context).FromJust()); |
| |
| i::FLAG_allow_natives_syntax = old_natives_flag_sentry; |
| |
| result = CompileRun( |
| "ext_array[3] = 33;" |
| "delete ext_array[3];" |
| "ext_array[3];"); |
| CHECK_EQ(33, result->Int32Value(context).FromJust()); |
| |
| result = CompileRun( |
| "ext_array[0] = 10; ext_array[1] = 11;" |
| "ext_array[2] = 12; ext_array[3] = 13;" |
| "try { ext_array.__defineGetter__('2', function() { return 120; }); }" |
| "catch (e) { }" |
| "ext_array[2];"); |
| CHECK_EQ(12, result->Int32Value(context).FromJust()); |
| |
| result = CompileRun( |
| "var js_array = new Array(40);" |
| "js_array[0] = 77;" |
| "js_array;"); |
| CHECK_EQ(77, v8::Object::Cast(*result) |
| ->Get(context, v8_str("0")) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| |
| result = CompileRun( |
| "ext_array[1] = 23;" |
| "ext_array.__proto__ = [];" |
| "js_array.__proto__ = ext_array;" |
| "js_array.concat(ext_array);"); |
| CHECK_EQ(77, v8::Object::Cast(*result) |
| ->Get(context, v8_str("0")) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| CHECK_EQ(23, v8::Object::Cast(*result) |
| ->Get(context, v8_str("1")) |
| .ToLocalChecked() |
| ->Int32Value(context) |
| .FromJust()); |
| |
| result = CompileRun("ext_array[1] = 23;"); |
| CHECK_EQ(23, result->Int32Value(context).FromJust()); |
| } |
| |
| template <typename ElementType, typename TypedArray, class ArrayBufferType> |
| void TypedArrayTestHelper(i::ExternalArrayType array_type, int64_t low, |
| int64_t high) { |
| const int kElementCount = 50; |
| |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<ArrayBufferType> ab = |
| ArrayBufferType::New(isolate, (kElementCount + 2) * sizeof(ElementType)); |
| Local<TypedArray> ta = |
| TypedArray::New(ab, 2 * sizeof(ElementType), kElementCount); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(ta); |
| CHECK_EQ(kElementCount, static_cast<int>(ta->Length())); |
| CHECK_EQ(2 * sizeof(ElementType), ta->ByteOffset()); |
| CHECK_EQ(kElementCount * sizeof(ElementType), ta->ByteLength()); |
| CHECK(ab->Equals(env.local(), ta->Buffer()).FromJust()); |
| |
| ElementType* data = |
| reinterpret_cast<ElementType*>(ab->GetBackingStore()->Data()) + 2; |
| for (int i = 0; i < kElementCount; i++) { |
| data[i] = static_cast<ElementType>(i); |
| } |
| |
| ObjectWithExternalArrayTestHelper<ElementType>(env.local(), ta, kElementCount, |
| array_type, low, high); |
| } |
| |
| } // namespace |
| |
| THREADED_TEST(Uint8Array) { |
| TypedArrayTestHelper<uint8_t, v8::Uint8Array, v8::ArrayBuffer>( |
| i::kExternalUint8Array, 0, 0xFF); |
| } |
| |
| THREADED_TEST(Int8Array) { |
| TypedArrayTestHelper<int8_t, v8::Int8Array, v8::ArrayBuffer>( |
| i::kExternalInt8Array, -0x80, 0x7F); |
| } |
| |
| THREADED_TEST(Uint16Array) { |
| TypedArrayTestHelper<uint16_t, v8::Uint16Array, v8::ArrayBuffer>( |
| i::kExternalUint16Array, 0, 0xFFFF); |
| } |
| |
| THREADED_TEST(Int16Array) { |
| TypedArrayTestHelper<int16_t, v8::Int16Array, v8::ArrayBuffer>( |
| i::kExternalInt16Array, -0x8000, 0x7FFF); |
| } |
| |
| THREADED_TEST(Uint32Array) { |
| TypedArrayTestHelper<uint32_t, v8::Uint32Array, v8::ArrayBuffer>( |
| i::kExternalUint32Array, 0, UINT_MAX); |
| } |
| |
| THREADED_TEST(Int32Array) { |
| TypedArrayTestHelper<int32_t, v8::Int32Array, v8::ArrayBuffer>( |
| i::kExternalInt32Array, INT_MIN, INT_MAX); |
| } |
| |
| THREADED_TEST(Float32Array) { |
| TypedArrayTestHelper<float, v8::Float32Array, v8::ArrayBuffer>( |
| i::kExternalFloat32Array, -500, 500); |
| } |
| |
| THREADED_TEST(Float64Array) { |
| TypedArrayTestHelper<double, v8::Float64Array, v8::ArrayBuffer>( |
| i::kExternalFloat64Array, -500, 500); |
| } |
| |
| THREADED_TEST(Uint8ClampedArray) { |
| TypedArrayTestHelper<uint8_t, v8::Uint8ClampedArray, v8::ArrayBuffer>( |
| i::kExternalUint8ClampedArray, 0, 0xFF); |
| } |
| |
| THREADED_TEST(DataView) { |
| const int kSize = 50; |
| |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::ArrayBuffer> ab = v8::ArrayBuffer::New(isolate, 2 + kSize); |
| Local<v8::DataView> dv = v8::DataView::New(ab, 2, kSize); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| CHECK_EQ(2u, dv->ByteOffset()); |
| CHECK_EQ(kSize, static_cast<int>(dv->ByteLength())); |
| CHECK(ab->Equals(env.local(), dv->Buffer()).FromJust()); |
| } |
| |
| THREADED_TEST(SharedUint8Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<uint8_t, v8::Uint8Array, v8::SharedArrayBuffer>( |
| i::kExternalUint8Array, 0, 0xFF); |
| } |
| |
| THREADED_TEST(SharedInt8Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<int8_t, v8::Int8Array, v8::SharedArrayBuffer>( |
| i::kExternalInt8Array, -0x80, 0x7F); |
| } |
| |
| THREADED_TEST(SharedUint16Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<uint16_t, v8::Uint16Array, v8::SharedArrayBuffer>( |
| i::kExternalUint16Array, 0, 0xFFFF); |
| } |
| |
| THREADED_TEST(SharedInt16Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<int16_t, v8::Int16Array, v8::SharedArrayBuffer>( |
| i::kExternalInt16Array, -0x8000, 0x7FFF); |
| } |
| |
| THREADED_TEST(SharedUint32Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<uint32_t, v8::Uint32Array, v8::SharedArrayBuffer>( |
| i::kExternalUint32Array, 0, UINT_MAX); |
| } |
| |
| THREADED_TEST(SharedInt32Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<int32_t, v8::Int32Array, v8::SharedArrayBuffer>( |
| i::kExternalInt32Array, INT_MIN, INT_MAX); |
| } |
| |
| THREADED_TEST(SharedFloat32Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<float, v8::Float32Array, v8::SharedArrayBuffer>( |
| i::kExternalFloat32Array, -500, 500); |
| } |
| |
| THREADED_TEST(SharedFloat64Array) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<double, v8::Float64Array, v8::SharedArrayBuffer>( |
| i::kExternalFloat64Array, -500, 500); |
| } |
| |
| THREADED_TEST(SharedUint8ClampedArray) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| TypedArrayTestHelper<uint8_t, v8::Uint8ClampedArray, v8::SharedArrayBuffer>( |
| i::kExternalUint8ClampedArray, 0, 0xFF); |
| } |
| |
| THREADED_TEST(SharedDataView) { |
| i::FLAG_harmony_sharedarraybuffer = true; |
| const int kSize = 50; |
| |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| Local<v8::SharedArrayBuffer> ab = |
| v8::SharedArrayBuffer::New(isolate, 2 + kSize); |
| Local<v8::DataView> dv = v8::DataView::New(ab, 2, kSize); |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(dv); |
| CHECK_EQ(2u, dv->ByteOffset()); |
| CHECK_EQ(kSize, static_cast<int>(dv->ByteLength())); |
| CHECK(ab->Equals(env.local(), dv->Buffer()).FromJust()); |
| } |
| |
| #define IS_ARRAY_BUFFER_VIEW_TEST(View) \ |
| THREADED_TEST(Is##View) { \ |
| LocalContext env; \ |
| v8::Isolate* isolate = env->GetIsolate(); \ |
| v8::HandleScope handle_scope(isolate); \ |
| \ |
| Local<Value> result = CompileRun( \ |
| "var ab = new ArrayBuffer(128);" \ |
| "new " #View "(ab)"); \ |
| CHECK(result->IsArrayBufferView()); \ |
| CHECK(result->Is##View()); \ |
| CheckInternalFieldsAreZero<v8::ArrayBufferView>(result.As<v8::View>()); \ |
| } |
| |
| IS_ARRAY_BUFFER_VIEW_TEST(Uint8Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Int8Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Uint16Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Int16Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Uint32Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Int32Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Float32Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Float64Array) |
| IS_ARRAY_BUFFER_VIEW_TEST(Uint8ClampedArray) |
| IS_ARRAY_BUFFER_VIEW_TEST(DataView) |
| |
| #undef IS_ARRAY_BUFFER_VIEW_TEST |
| |
| TEST(InternalFieldsOnTypedArray) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Context> context = env.local(); |
| Context::Scope context_scope(context); |
| v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1); |
| v8::Local<v8::Uint8Array> array = v8::Uint8Array::New(buffer, 0, 1); |
| for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| CHECK_EQ(static_cast<void*>(nullptr), |
| array->GetAlignedPointerFromInternalField(i)); |
| } |
| } |
| |
| TEST(InternalFieldsOnDataView) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope scope(isolate); |
| v8::Local<v8::Context> context = env.local(); |
| Context::Scope context_scope(context); |
| v8::Local<v8::ArrayBuffer> buffer = v8::ArrayBuffer::New(isolate, 1); |
| v8::Local<v8::DataView> array = v8::DataView::New(buffer, 0, 1); |
| for (int i = 0; i < v8::ArrayBufferView::kInternalFieldCount; i++) { |
| CHECK_EQ(static_cast<void*>(nullptr), |
| array->GetAlignedPointerFromInternalField(i)); |
| } |
| } |
| |
| namespace { |
| void TestOnHeapHasBuffer(const char* array_name, size_t elem_size) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<char> source(128); |
| // Test on-heap sizes. |
| for (size_t size = 0; size <= i::JSTypedArray::kMaxSizeInHeap; |
| size += elem_size) { |
| size_t length = size / elem_size; |
| i::SNPrintF(source, "new %sArray(%zu)", array_name, length); |
| auto typed_array = |
| v8::Local<v8::TypedArray>::Cast(CompileRun(source.begin())); |
| |
| CHECK_EQ(length, typed_array->Length()); |
| |
| // Should not (yet) have a buffer. |
| CHECK(!typed_array->HasBuffer()); |
| |
| // Get the buffer and check its length. |
| i::Handle<i::JSTypedArray> i_typed_array = |
| v8::Utils::OpenHandle(*typed_array); |
| auto i_array_buffer1 = i_typed_array->GetBuffer(); |
| CHECK_EQ(size, i_array_buffer1->byte_length()); |
| CHECK(typed_array->HasBuffer()); |
| |
| // Should have the same buffer each time. |
| auto i_array_buffer2 = i_typed_array->GetBuffer(); |
| CHECK(i_array_buffer1.is_identical_to(i_array_buffer2)); |
| } |
| } |
| |
| void TestOffHeapHasBuffer(const char* array_name, size_t elem_size) { |
| LocalContext env; |
| v8::Isolate* isolate = env->GetIsolate(); |
| v8::HandleScope handle_scope(isolate); |
| |
| i::ScopedVector<char> source(128); |
| // Test off-heap sizes. |
| size_t size = i::JSTypedArray::kMaxSizeInHeap; |
| for (int i = 0; i < 3; i++) { |
| size_t length = 1 + (size / elem_size); |
| i::SNPrintF(source, "new %sArray(%zu)", array_name, length); |
| auto typed_array = |
| v8::Local<v8::TypedArray>::Cast(CompileRun(source.begin())); |
| CHECK_EQ(length, typed_array->Length()); |
| |
| // Should already have a buffer. |
| CHECK(typed_array->HasBuffer()); |
| |
| // Get the buffer and check its length. |
| i::Handle<i::JSTypedArray> i_typed_array = |
| v8::Utils::OpenHandle(*typed_array); |
| auto i_array_buffer1 = i_typed_array->GetBuffer(); |
| CHECK_EQ(length * elem_size, i_array_buffer1->byte_length()); |
| |
| size *= 2; |
| } |
| } |
| |
| } // namespace |
| |
| #define TEST_HAS_BUFFER(array_name, elem_size) \ |
| TEST(OnHeap_##array_name##Array_HasBuffer) { \ |
| TestOnHeapHasBuffer(#array_name, elem_size); \ |
| } \ |
| TEST(OffHeap_##array_name##_HasBuffer) { \ |
| TestOffHeapHasBuffer(#array_name, elem_size); \ |
| } |
| |
| TEST_HAS_BUFFER(Uint8, 1) |
| TEST_HAS_BUFFER(Int8, 1) |
| TEST_HAS_BUFFER(Uint16, 2) |
| TEST_HAS_BUFFER(Int16, 2) |
| TEST_HAS_BUFFER(Uint32, 4) |
| TEST_HAS_BUFFER(Int32, 4) |
| TEST_HAS_BUFFER(Float32, 4) |
| TEST_HAS_BUFFER(Float64, 8) |
| |
| #undef TEST_HAS_BUFFER |