From 8bb85af38dbdb3a32eed619e7bec1ccd82731c86 Mon Sep 17 00:00:00 2001 From: "dslomov@chromium.org" Date: Thu, 16 May 2013 11:54:25 +0000 Subject: [PATCH] Revert "Remove d8 implementation of ArrayBuffer and typed arrays." This reverts commit r14706. That commit had a stale change from another change list. git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14707 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/d8.cc | 660 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/runtime.cc | 2 +- 2 files changed, 658 insertions(+), 4 deletions(-) diff --git a/src/d8.cc b/src/d8.cc index b95432e..7ac78cc 100644 --- a/src/d8.cc +++ b/src/d8.cc @@ -89,15 +89,38 @@ static Handle Throw(const char* message) { } +// TODO(rossberg): should replace these by proper uses of HasInstance, +// once we figure out a good way to make the templates global. +const char kArrayBufferMarkerPropName[] = "d8::_is_array_buffer_"; +const char kArrayMarkerPropName[] = "d8::_is_typed_array_"; + + +#define FOR_EACH_STRING(V) \ + V(ArrayBuffer, "ArrayBuffer") \ + V(ArrayBufferMarkerPropName, kArrayBufferMarkerPropName) \ + V(ArrayMarkerPropName, kArrayMarkerPropName) \ + V(buffer, "buffer") \ + V(byteLength, "byteLength") \ + V(byteOffset, "byteOffset") \ + V(BYTES_PER_ELEMENT, "BYTES_PER_ELEMENT") \ + V(length, "length") + class PerIsolateData { public: explicit PerIsolateData(Isolate* isolate) : isolate_(isolate), realms_(NULL) { HandleScope scope(isolate); +#define INIT_STRING(name, value) \ + name##_string_ = Persistent::New(isolate, String::NewSymbol(value)); + FOR_EACH_STRING(INIT_STRING) +#undef INIT_STRING isolate->SetData(this); } ~PerIsolateData() { +#define DISPOSE_STRING(name, value) name##_string_.Dispose(isolate_); + FOR_EACH_STRING(DISPOSE_STRING) +#undef DISPOSE_STRING isolate_->SetData(NULL); // Not really needed, just to be sure... } @@ -105,6 +128,13 @@ class PerIsolateData { return reinterpret_cast(isolate->GetData()); } +#define DEFINE_STRING_GETTER(name, value) \ + static Handle name##_string(Isolate* isolate) { \ + return Handle(*Get(isolate)->name##_string_); \ + } + FOR_EACH_STRING(DEFINE_STRING_GETTER) +#undef DEFINE_STRING_GETTER + class RealmScope { public: explicit RealmScope(PerIsolateData* data); @@ -123,6 +153,10 @@ class PerIsolateData { Persistent* realms_; Persistent realm_shared_; +#define DEFINE_MEMBER(name, value) Persistent name##_string_; + FOR_EACH_STRING(DEFINE_MEMBER) +#undef DEFINE_MEMBER + int RealmFind(Handle context); }; @@ -527,6 +561,565 @@ Handle Shell::Load(const Arguments& args) { return Undefined(args.GetIsolate()); } +static int32_t convertToInt(Local value_in, TryCatch* try_catch) { + if (value_in->IsInt32()) { + return value_in->Int32Value(); + } + + Local number = value_in->ToNumber(); + if (try_catch->HasCaught()) return 0; + + ASSERT(number->IsNumber()); + Local int32 = number->ToInt32(); + if (try_catch->HasCaught() || int32.IsEmpty()) return 0; + + int32_t value = int32->Int32Value(); + if (try_catch->HasCaught()) return 0; + + return value; +} + + +static int32_t convertToUint(Local value_in, TryCatch* try_catch) { + int32_t raw_value = convertToInt(value_in, try_catch); + if (try_catch->HasCaught()) return 0; + + if (raw_value < 0) { + Throw("Array length must not be negative."); + return 0; + } + + static const int kMaxLength = 0x3fffffff; +#ifndef V8_SHARED + ASSERT(kMaxLength == i::ExternalArray::kMaxLength); +#endif // V8_SHARED + if (raw_value > static_cast(kMaxLength)) { + Throw("Array length exceeds maximum length."); + } + return raw_value; +} + + +Handle Shell::CreateExternalArrayBuffer(Isolate* isolate, + Handle buffer, + int32_t length) { + static const int32_t kMaxSize = 0x7fffffff; + // Make sure the total size fits into a (signed) int. + if (length < 0 || length > kMaxSize) { + return Throw("ArrayBuffer exceeds maximum size (2G)"); + } + uint8_t* data = new uint8_t[length]; + if (data == NULL) { + return Throw("Memory allocation failed"); + } + memset(data, 0, length); + + buffer->SetHiddenValue( + PerIsolateData::ArrayBufferMarkerPropName_string(isolate), True()); + Persistent persistent_array = + Persistent::New(isolate, buffer); + persistent_array.MakeWeak(isolate, data, ExternalArrayWeakCallback); + persistent_array.MarkIndependent(isolate); + isolate->AdjustAmountOfExternalAllocatedMemory(length); + + buffer->SetIndexedPropertiesToExternalArrayData( + data, v8::kExternalByteArray, length); + buffer->Set(PerIsolateData::byteLength_string(isolate), + Int32::New(length, isolate), + ReadOnly); + + return buffer; +} + + +Handle Shell::ArrayBuffer(const Arguments& args) { + if (!args.IsConstructCall()) { + Handle* rec_args = new Handle[args.Length()]; + for (int i = 0; i < args.Length(); ++i) rec_args[i] = args[i]; + Handle result = args.Callee()->NewInstance(args.Length(), rec_args); + delete[] rec_args; + return result; + } + + if (args.Length() == 0) { + return Throw("ArrayBuffer constructor must have one argument"); + } + TryCatch try_catch; + int32_t length = convertToUint(args[0], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + return CreateExternalArrayBuffer(args.GetIsolate(), args.This(), length); +} + + +Handle Shell::CreateExternalArray(Isolate* isolate, + Handle array, + Handle buffer, + ExternalArrayType type, + int32_t length, + int32_t byteLength, + int32_t byteOffset, + int32_t element_size) { + ASSERT(element_size == 1 || element_size == 2 || + element_size == 4 || element_size == 8); + ASSERT(byteLength == length * element_size); + + void* data = buffer->GetIndexedPropertiesExternalArrayData(); + ASSERT(data != NULL); + + array->SetIndexedPropertiesToExternalArrayData( + static_cast(data) + byteOffset, type, length); + array->SetHiddenValue(PerIsolateData::ArrayMarkerPropName_string(isolate), + Int32::New(type, isolate)); + array->Set(PerIsolateData::byteLength_string(isolate), + Int32::New(byteLength, isolate), + ReadOnly); + array->Set(PerIsolateData::byteOffset_string(isolate), + Int32::New(byteOffset, isolate), + ReadOnly); + array->Set(PerIsolateData::length_string(isolate), + Int32::New(length, isolate), + ReadOnly); + array->Set(PerIsolateData::BYTES_PER_ELEMENT_string(isolate), + Int32::New(element_size, isolate)); + array->Set(PerIsolateData::buffer_string(isolate), + buffer, + ReadOnly); + + return array; +} + + +Handle Shell::CreateExternalArray(const Arguments& args, + ExternalArrayType type, + int32_t element_size) { + Isolate* isolate = args.GetIsolate(); + if (!args.IsConstructCall()) { + Handle* rec_args = new Handle[args.Length()]; + for (int i = 0; i < args.Length(); ++i) rec_args[i] = args[i]; + Handle result = args.Callee()->NewInstance(args.Length(), rec_args); + delete[] rec_args; + return result; + } + + TryCatch try_catch; + ASSERT(element_size == 1 || element_size == 2 || + element_size == 4 || element_size == 8); + + // All of the following constructors are supported: + // TypedArray(unsigned long length) + // TypedArray(type[] array) + // TypedArray(TypedArray array) + // TypedArray(ArrayBuffer buffer, + // optional unsigned long byteOffset, + // optional unsigned long length) + Handle buffer; + int32_t length; + int32_t byteLength; + int32_t byteOffset; + bool init_from_array = false; + if (args.Length() == 0) { + return Throw("Array constructor must have at least one argument"); + } + if (args[0]->IsObject() && + !args[0]->ToObject()->GetHiddenValue( + PerIsolateData::ArrayBufferMarkerPropName_string(isolate)).IsEmpty()) { + // Construct from ArrayBuffer. + buffer = args[0]->ToObject(); + int32_t bufferLength = convertToUint( + buffer->Get(PerIsolateData::byteLength_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + if (args.Length() < 2 || args[1]->IsUndefined()) { + byteOffset = 0; + } else { + byteOffset = convertToUint(args[1], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + if (byteOffset > bufferLength) { + return Throw("byteOffset out of bounds"); + } + if (byteOffset % element_size != 0) { + return Throw("byteOffset must be multiple of element size"); + } + } + + if (args.Length() < 3 || args[2]->IsUndefined()) { + byteLength = bufferLength - byteOffset; + length = byteLength / element_size; + if (byteLength % element_size != 0) { + return Throw("buffer size must be multiple of element size"); + } + } else { + length = convertToUint(args[2], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + byteLength = length * element_size; + if (byteOffset + byteLength > bufferLength) { + return Throw("length out of bounds"); + } + } + } else { + if (args[0]->IsObject() && + args[0]->ToObject()->Has(PerIsolateData::length_string(isolate))) { + // Construct from array. + Local value = + args[0]->ToObject()->Get(PerIsolateData::length_string(isolate)); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + length = convertToUint(value, &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + init_from_array = true; + } else { + // Construct from size. + length = convertToUint(args[0], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + } + byteLength = length * element_size; + byteOffset = 0; + + Handle global = Context::GetCurrent()->Global(); + Handle array_buffer = + global->Get(PerIsolateData::ArrayBuffer_string(isolate)); + ASSERT(!try_catch.HasCaught() && array_buffer->IsFunction()); + Handle buffer_args[] = { Uint32::New(byteLength, isolate) }; + Handle result = Handle::Cast(array_buffer)->NewInstance( + 1, buffer_args); + if (try_catch.HasCaught()) return result; + buffer = result->ToObject(); + } + + Handle array = + CreateExternalArray(isolate, args.This(), buffer, type, length, + byteLength, byteOffset, element_size); + + if (init_from_array) { + Handle init = args[0]->ToObject(); + for (int i = 0; i < length; ++i) { + Local value = init->Get(i); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + array->Set(i, value); + } + } + + return array; +} + + +Handle Shell::ArrayBufferSlice(const Arguments& args) { + TryCatch try_catch; + + if (!args.This()->IsObject()) { + return Throw("'slice' invoked on non-object receiver"); + } + + Isolate* isolate = args.GetIsolate(); + Local self = args.This(); + Local marker = self->GetHiddenValue( + PerIsolateData::ArrayBufferMarkerPropName_string(isolate)); + if (marker.IsEmpty()) { + return Throw("'slice' invoked on wrong receiver type"); + } + + int32_t length = convertToUint( + self->Get(PerIsolateData::byteLength_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + if (args.Length() == 0) { + return Throw("'slice' must have at least one argument"); + } + int32_t begin = convertToInt(args[0], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + if (begin < 0) begin += length; + if (begin < 0) begin = 0; + if (begin > length) begin = length; + + int32_t end; + if (args.Length() < 2 || args[1]->IsUndefined()) { + end = length; + } else { + end = convertToInt(args[1], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + if (end < 0) end += length; + if (end < 0) end = 0; + if (end > length) end = length; + if (end < begin) end = begin; + } + + Local constructor = Local::Cast(self->GetConstructor()); + Handle new_args[] = { Uint32::New(end - begin, isolate) }; + Handle result = constructor->NewInstance(1, new_args); + if (try_catch.HasCaught()) return result; + Handle buffer = result->ToObject(); + uint8_t* dest = + static_cast(buffer->GetIndexedPropertiesExternalArrayData()); + uint8_t* src = begin + static_cast( + self->GetIndexedPropertiesExternalArrayData()); + memcpy(dest, src, end - begin); + + return buffer; +} + + +Handle Shell::ArraySubArray(const Arguments& args) { + TryCatch try_catch; + + if (!args.This()->IsObject()) { + return Throw("'subarray' invoked on non-object receiver"); + } + + Isolate* isolate = args.GetIsolate(); + Local self = args.This(); + Local marker = + self->GetHiddenValue(PerIsolateData::ArrayMarkerPropName_string(isolate)); + if (marker.IsEmpty()) { + return Throw("'subarray' invoked on wrong receiver type"); + } + + Handle buffer = + self->Get(PerIsolateData::buffer_string(isolate))->ToObject(); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t length = convertToUint( + self->Get(PerIsolateData::length_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t byteOffset = convertToUint( + self->Get(PerIsolateData::byteOffset_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t element_size = convertToUint( + self->Get(PerIsolateData::BYTES_PER_ELEMENT_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + if (args.Length() == 0) { + return Throw("'subarray' must have at least one argument"); + } + int32_t begin = convertToInt(args[0], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + if (begin < 0) begin += length; + if (begin < 0) begin = 0; + if (begin > length) begin = length; + + int32_t end; + if (args.Length() < 2 || args[1]->IsUndefined()) { + end = length; + } else { + end = convertToInt(args[1], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + if (end < 0) end += length; + if (end < 0) end = 0; + if (end > length) end = length; + if (end < begin) end = begin; + } + + length = end - begin; + byteOffset += begin * element_size; + + Local constructor = Local::Cast(self->GetConstructor()); + Handle construct_args[] = { + buffer, Uint32::New(byteOffset, isolate), Uint32::New(length, isolate) + }; + return constructor->NewInstance(3, construct_args); +} + + +Handle Shell::ArraySet(const Arguments& args) { + TryCatch try_catch; + + if (!args.This()->IsObject()) { + return Throw("'set' invoked on non-object receiver"); + } + + Isolate* isolate = args.GetIsolate(); + Local self = args.This(); + Local marker = + self->GetHiddenValue(PerIsolateData::ArrayMarkerPropName_string(isolate)); + if (marker.IsEmpty()) { + return Throw("'set' invoked on wrong receiver type"); + } + int32_t length = convertToUint( + self->Get(PerIsolateData::length_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t element_size = convertToUint( + self->Get(PerIsolateData::BYTES_PER_ELEMENT_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + if (args.Length() == 0) { + return Throw("'set' must have at least one argument"); + } + if (!args[0]->IsObject() || + !args[0]->ToObject()->Has(PerIsolateData::length_string(isolate))) { + return Throw("'set' invoked with non-array argument"); + } + Handle source = args[0]->ToObject(); + int32_t source_length = convertToUint( + source->Get(PerIsolateData::length_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + int32_t offset; + if (args.Length() < 2 || args[1]->IsUndefined()) { + offset = 0; + } else { + offset = convertToUint(args[1], &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + } + if (offset + source_length > length) { + return Throw("offset or source length out of bounds"); + } + + int32_t source_element_size; + if (source->GetHiddenValue( + PerIsolateData::ArrayMarkerPropName_string(isolate)).IsEmpty()) { + source_element_size = 0; + } else { + source_element_size = convertToUint( + source->Get(PerIsolateData::BYTES_PER_ELEMENT_string(isolate)), + &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + } + + if (element_size == source_element_size && + self->GetConstructor()->StrictEquals(source->GetConstructor())) { + // Use memmove on the array buffers. + Handle buffer = + self->Get(PerIsolateData::buffer_string(isolate))->ToObject(); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + Handle source_buffer = + source->Get(PerIsolateData::buffer_string(isolate))->ToObject(); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t byteOffset = convertToUint( + self->Get(PerIsolateData::byteOffset_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t source_byteOffset = convertToUint( + source->Get(PerIsolateData::byteOffset_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + uint8_t* dest = byteOffset + offset * element_size + static_cast( + buffer->GetIndexedPropertiesExternalArrayData()); + uint8_t* src = source_byteOffset + static_cast( + source_buffer->GetIndexedPropertiesExternalArrayData()); + memmove(dest, src, source_length * element_size); + } else if (source_element_size == 0) { + // Source is not a typed array, copy element-wise sequentially. + for (int i = 0; i < source_length; ++i) { + self->Set(offset + i, source->Get(i)); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + } + } else { + // Need to copy element-wise to make the right conversions. + Handle buffer = + self->Get(PerIsolateData::buffer_string(isolate))->ToObject(); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + Handle source_buffer = + source->Get(PerIsolateData::buffer_string(isolate))->ToObject(); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + if (buffer->StrictEquals(source_buffer)) { + // Same backing store, need to handle overlap correctly. + // This gets a bit tricky in the case of different element sizes + // (which, of course, is extremely unlikely to ever occur in practice). + int32_t byteOffset = convertToUint( + self->Get(PerIsolateData::byteOffset_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + int32_t source_byteOffset = convertToUint( + source->Get(PerIsolateData::byteOffset_string(isolate)), &try_catch); + if (try_catch.HasCaught()) return try_catch.ReThrow(); + + // Copy as much as we can from left to right. + int i = 0; + int32_t next_dest_offset = byteOffset + (offset + 1) * element_size; + int32_t next_src_offset = source_byteOffset + source_element_size; + while (i < length && next_dest_offset <= next_src_offset) { + self->Set(offset + i, source->Get(i)); + ++i; + next_dest_offset += element_size; + next_src_offset += source_element_size; + } + // Of what's left, copy as much as we can from right to left. + int j = length - 1; + int32_t dest_offset = byteOffset + (offset + j) * element_size; + int32_t src_offset = source_byteOffset + j * source_element_size; + while (j >= i && dest_offset >= src_offset) { + self->Set(offset + j, source->Get(j)); + --j; + dest_offset -= element_size; + src_offset -= source_element_size; + } + // There can be at most 8 entries left in the middle that need buffering + // (because the largest element_size is 8 times the smallest). + ASSERT(j+1 - i <= 8); + Handle temp[8]; + for (int k = i; k <= j; ++k) { + temp[k - i] = source->Get(k); + } + for (int k = i; k <= j; ++k) { + self->Set(offset + k, temp[k - i]); + } + } else { + // Different backing stores, safe to copy element-wise sequentially. + for (int i = 0; i < source_length; ++i) + self->Set(offset + i, source->Get(i)); + } + } + + return Undefined(args.GetIsolate()); +} + + +void Shell::ExternalArrayWeakCallback(v8::Isolate* isolate, + Persistent* object, + uint8_t* data) { + HandleScope scope(isolate); + int32_t length = (*object)->Get( + PerIsolateData::byteLength_string(isolate))->Uint32Value(); + isolate->AdjustAmountOfExternalAllocatedMemory(-length); + delete[] data; + object->Dispose(isolate); +} + + +Handle Shell::Int8Array(const Arguments& args) { + return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); +} + + +Handle Shell::Uint8Array(const Arguments& args) { + return CreateExternalArray(args, kExternalUnsignedByteArray, sizeof(uint8_t)); +} + + +Handle Shell::Int16Array(const Arguments& args) { + return CreateExternalArray(args, kExternalShortArray, sizeof(int16_t)); +} + + +Handle Shell::Uint16Array(const Arguments& args) { + return CreateExternalArray( + args, kExternalUnsignedShortArray, sizeof(uint16_t)); +} + + +Handle Shell::Int32Array(const Arguments& args) { + return CreateExternalArray(args, kExternalIntArray, sizeof(int32_t)); +} + + +Handle Shell::Uint32Array(const Arguments& args) { + return CreateExternalArray(args, kExternalUnsignedIntArray, sizeof(uint32_t)); +} + + +Handle Shell::Float32Array(const Arguments& args) { + return CreateExternalArray( + args, kExternalFloatArray, sizeof(float)); // NOLINT +} + + +Handle Shell::Float64Array(const Arguments& args) { + return CreateExternalArray( + args, kExternalDoubleArray, sizeof(double)); // NOLINT +} + + +Handle Shell::Uint8ClampedArray(const Arguments& args) { + return CreateExternalArray(args, kExternalPixelArray, sizeof(uint8_t)); +} + Handle Shell::Quit(const Arguments& args) { int exit_code = args[0]->Int32Value(); @@ -819,6 +1412,26 @@ class BZip2Decompressor : public v8::StartupDataDecompressor { #endif +Handle Shell::CreateArrayBufferTemplate( + InvocationCallback fun) { + Handle buffer_template = FunctionTemplate::New(fun); + Local