Index: src/d8.cc |
diff --git a/src/d8.cc b/src/d8.cc |
index ad850f5ee780334d7cff606d6da2710005969b11..927d536f8b10d458933f771b7b255114834c82a6 100644 |
--- a/src/d8.cc |
+++ b/src/d8.cc |
@@ -1,4 +1,4 @@ |
-// Copyright 2011 the V8 project authors. All rights reserved. |
+// Copyright 2012 the V8 project authors. All rights reserved. |
// Redistribution and use in source and binary forms, with or without |
// modification, are permitted provided that the following conditions are |
// met: |
@@ -281,63 +281,161 @@ Handle<Value> Shell::Load(const Arguments& args) { |
return Undefined(); |
} |
+static size_t convertToUint(Local<Value> value_in, TryCatch* try_catch) { |
+ if (value_in->IsUint32()) { |
+ return value_in->Uint32Value(); |
+ } |
+ |
+ Local<Value> number = value_in->ToNumber(); |
+ if (try_catch->HasCaught()) return 0; |
+ |
+ ASSERT(number->IsNumber()); |
+ Local<Int32> int32 = number->ToInt32(); |
+ if (try_catch->HasCaught() || int32.IsEmpty()) return 0; |
+ |
+ int32_t raw_value = int32->Int32Value(); |
+ if (try_catch->HasCaught()) return 0; |
+ |
+ if (raw_value < 0) { |
+ ThrowException(String::New("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<int32_t>(kMaxLength)) { |
+ ThrowException( |
+ String::New("Array length exceeds maximum length.")); |
+ } |
+ return static_cast<size_t>(raw_value); |
+} |
+ |
+ |
+const char kArrayBufferReferencePropName[] = "_is_array_buffer_"; |
+const char kArrayBufferMarkerPropName[] = "_array_buffer_ref_"; |
+ |
Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
ExternalArrayType type, |
size_t element_size) { |
+ TryCatch try_catch; |
+ bool is_array_buffer_construct = element_size == 0; |
+ if (is_array_buffer_construct) { |
+ type = v8::kExternalByteArray; |
+ element_size = 1; |
+ } |
ASSERT(element_size == 1 || element_size == 2 || element_size == 4 || |
element_size == 8); |
- if (args.Length() != 1) { |
+ if (args.Length() == 0) { |
return ThrowException( |
- String::New("Array constructor needs one parameter.")); |
+ String::New("Array constructor must have at least one " |
+ "parameter.")); |
} |
- static const int kMaxLength = 0x3fffffff; |
-#ifndef V8_SHARED |
- ASSERT(kMaxLength == i::ExternalArray::kMaxLength); |
-#endif // V8_SHARED |
- size_t length = 0; |
- TryCatch try_catch; |
- if (args[0]->IsUint32()) { |
- length = args[0]->Uint32Value(); |
- } else { |
- Local<Number> number = args[0]->ToNumber(); |
- if (number.IsEmpty()) { |
- ASSERT(try_catch.HasCaught()); |
- return try_catch.Exception(); |
+ bool first_arg_is_array_buffer = |
+ args[0]->IsObject() && |
+ args[0]->ToObject()->Get( |
+ String::New(kArrayBufferMarkerPropName))->IsTrue(); |
+ // Currently, only the following constructors are supported: |
+ // TypedArray(unsigned long length) |
+ // TypedArray(ArrayBuffer buffer, |
+ // optional unsigned long byteOffset, |
+ // optional unsigned long length) |
+ if (args.Length() > 3) { |
+ return ThrowException( |
+ String::New("Array constructor from ArrayBuffer must " |
+ "have 1-3 parameters.")); |
+ } |
+ |
+ Local<Value> length_value = (args.Length() < 3) |
+ ? (first_arg_is_array_buffer |
+ ? args[0]->ToObject()->Get(String::New("length")) |
+ : args[0]) |
+ : args[2]; |
+ size_t length = convertToUint(length_value, &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.Exception(); |
+ |
+ void* data = NULL; |
+ size_t offset = 0; |
+ |
+ Handle<Object> array = Object::New(); |
+ if (first_arg_is_array_buffer) { |
+ Handle<Object> derived_from = args[0]->ToObject(); |
+ data = derived_from->GetIndexedPropertiesExternalArrayData(); |
+ |
+ size_t array_buffer_length = convertToUint( |
+ derived_from->Get(String::New("length")), |
+ &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.Exception(); |
+ |
+ if (data == NULL && array_buffer_length != 0) { |
+ return ThrowException( |
+ String::New("ArrayBuffer doesn't have data")); |
} |
- ASSERT(number->IsNumber()); |
- Local<Int32> int32 = number->ToInt32(); |
- if (int32.IsEmpty()) { |
- if (try_catch.HasCaught()) { |
- return try_catch.Exception(); |
+ |
+ if (args.Length() > 1) { |
+ offset = convertToUint(args[1], &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.Exception(); |
+ |
+ // The given byteOffset must be a multiple of the element size of the |
+ // specific type, otherwise an exception is raised. |
+ if (offset % element_size != 0) { |
+ return ThrowException( |
+ String::New("offset must be multiple of element_size")); |
} |
} |
- int32_t raw_length = int32->Int32Value(); |
- if (try_catch.HasCaught()) { |
- return try_catch.Exception(); |
+ |
+ if (offset > array_buffer_length) { |
+ return ThrowException( |
+ String::New("byteOffset must be less than ArrayBuffer length.")); |
} |
- if (raw_length < 0) { |
- return ThrowException(String::New("Array length must not be negative.")); |
+ |
+ if (args.Length() == 2) { |
+ // If length is not explicitly specified, the length of the ArrayBuffer |
+ // minus the byteOffset must be a multiple of the element size of the |
+ // specific type, or an exception is raised. |
+ length = array_buffer_length - offset; |
+ } |
+ |
+ if (args.Length() != 3) { |
+ if (length % element_size != 0) { |
+ return ThrowException( |
+ String::New("ArrayBuffer length minus the byteOffset must be a " |
+ "multiple of the element size")); |
+ } |
+ length /= element_size; |
} |
- if (raw_length > static_cast<int32_t>(kMaxLength)) { |
+ |
+ // If a given byteOffset and length references an area beyond the end of |
+ // the ArrayBuffer an exception is raised. |
+ if (offset + (length * element_size) > array_buffer_length) { |
return ThrowException( |
- String::New("Array length exceeds maximum length.")); |
+ String::New("length references an area beyond the end of the " |
+ "ArrayBuffer")); |
} |
- length = static_cast<size_t>(raw_length); |
- } |
- if (length > static_cast<size_t>(kMaxLength)) { |
- return ThrowException(String::New("Array length exceeds maximum length.")); |
+ |
+ // Hold a reference to the ArrayBuffer so its buffer doesn't get collected. |
+ array->Set(String::New(kArrayBufferReferencePropName), args[0], ReadOnly); |
} |
- void* data = calloc(length, element_size); |
- if (data == NULL) { |
- return ThrowException(String::New("Memory allocation failed.")); |
+ |
+ if (is_array_buffer_construct) { |
+ array->Set(String::New(kArrayBufferMarkerPropName), True(), ReadOnly); |
} |
- Handle<Object> array = Object::New(); |
+ |
Persistent<Object> persistent_array = Persistent<Object>::New(array); |
persistent_array.MakeWeak(data, ExternalArrayWeakCallback); |
persistent_array.MarkIndependent(); |
- array->SetIndexedPropertiesToExternalArrayData(data, type, |
- static_cast<int>(length)); |
+ if (data == NULL && length != 0) { |
+ data = calloc(length, element_size); |
+ if (data == NULL) { |
+ return ThrowException(String::New("Memory allocation failed.")); |
+ } |
+ } |
+ |
+ array->SetIndexedPropertiesToExternalArrayData( |
+ reinterpret_cast<uint8_t*>(data) + offset, type, |
+ static_cast<int>(length)); |
array->Set(String::New("length"), |
Int32::New(static_cast<int32_t>(length)), ReadOnly); |
array->Set(String::New("BYTES_PER_ELEMENT"), |
@@ -347,11 +445,22 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) { |
- free(data); |
+ HandleScope scope; |
+ Handle<String> prop_name = String::New(kArrayBufferReferencePropName); |
+ Handle<Object> converted_object = object->ToObject(); |
+ Local<Value> prop_value = converted_object->Get(prop_name); |
+ if (data != NULL && !prop_value->IsObject()) { |
+ free(data); |
+ } |
object.Dispose(); |
} |
+Handle<Value> Shell::ArrayBuffer(const Arguments& args) { |
+ return CreateExternalArray(args, v8::kExternalByteArray, 0); |
+} |
+ |
+ |
Handle<Value> Shell::Int8Array(const Arguments& args) { |
return CreateExternalArray(args, v8::kExternalByteArray, sizeof(int8_t)); |
} |
@@ -693,6 +802,8 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { |
FunctionTemplate::New(DisableProfiler)); |
// Bind the handlers for external arrays. |
+ global_template->Set(String::New("ArrayBuffer"), |
+ FunctionTemplate::New(ArrayBuffer)); |
global_template->Set(String::New("Int8Array"), |
FunctionTemplate::New(Int8Array)); |
global_template->Set(String::New("Uint8Array"), |