Index: src/d8.cc |
diff --git a/src/d8.cc b/src/d8.cc |
index 976510c464009a19f91bc9e7a0101decdc536fe4..fb90c94abb85276ff0c6cabe33dd4518b84ec311 100644 |
--- a/src/d8.cc |
+++ b/src/d8.cc |
@@ -324,6 +324,8 @@ static int32_t convertToUint(Local<Value> value_in, TryCatch* try_catch) { |
} |
+// 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_"; |
@@ -337,7 +339,7 @@ Handle<Value> Shell::CreateExternalArrayBuffer(Handle<Object> buffer, |
} |
uint8_t* data = new uint8_t[length]; |
if (data == NULL) { |
- return ThrowException(String::New("Memory allocation failed.")); |
+ return ThrowException(String::New("Memory allocation failed")); |
} |
memset(data, 0, length); |
@@ -366,11 +368,11 @@ Handle<Value> Shell::ArrayBuffer(const Arguments& args) { |
if (args.Length() == 0) { |
return ThrowException( |
- String::New("ArrayBuffer constructor must have one parameter.")); |
+ String::New("ArrayBuffer constructor must have one argument")); |
} |
TryCatch try_catch; |
int32_t length = convertToUint(args[0], &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
return CreateExternalArrayBuffer(args.This(), length); |
} |
@@ -432,7 +434,7 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
bool init_from_array = false; |
if (args.Length() == 0) { |
return ThrowException( |
- String::New("Array constructor must have at least one parameter.")); |
+ String::New("Array constructor must have at least one argument")); |
} |
if (args[0]->IsObject() && |
!args[0]->ToObject()->GetHiddenValue( |
@@ -441,19 +443,19 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
buffer = args[0]->ToObject(); |
int32_t bufferLength = |
convertToUint(buffer->Get(String::New("byteLength")), &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ 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.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
if (byteOffset > bufferLength) { |
return ThrowException(String::New("byteOffset out of bounds")); |
} |
if (byteOffset % element_size != 0) { |
return ThrowException( |
- String::New("byteOffset must be multiple of element_size")); |
+ String::New("byteOffset must be multiple of element size")); |
} |
} |
@@ -462,11 +464,11 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
length = byteLength / element_size; |
if (byteLength % element_size != 0) { |
return ThrowException( |
- String::New("buffer size must be multiple of element_size")); |
+ String::New("buffer size must be multiple of element size")); |
} |
} else { |
length = convertToUint(args[2], &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
byteLength = length * element_size; |
if (byteOffset + byteLength > bufferLength) { |
return ThrowException(String::New("length out of bounds")); |
@@ -478,12 +480,12 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
// Construct from array. |
length = convertToUint( |
args[0]->ToObject()->Get(String::New("length")), &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ 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.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
} |
byteLength = length * element_size; |
byteOffset = 0; |
@@ -510,39 +512,96 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, |
} |
-Handle<Value> Shell::SubArray(const Arguments& args) { |
+Handle<Value> Shell::ArrayBufferSlice(const Arguments& args) { |
TryCatch try_catch; |
if (!args.This()->IsObject()) { |
return ThrowException( |
- String::New("subarray invoked on non-object receiver.")); |
+ String::New("'slice' invoked on non-object receiver")); |
+ } |
+ |
+ Local<Object> self = args.This(); |
+ Local<Value> marker = |
+ self->GetHiddenValue(String::New(kArrayBufferMarkerPropName)); |
+ if (marker.IsEmpty()) { |
+ return ThrowException( |
+ String::New("'slice' invoked on wrong receiver type")); |
+ } |
+ |
+ int32_t length = |
+ convertToUint(self->Get(String::New("byteLength")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ |
+ if (args.Length() == 0) { |
+ return ThrowException( |
+ String::New("'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<Function> constructor = Local<Function>::Cast(self->GetConstructor()); |
+ Handle<Value> new_args[] = { Uint32::New(end - begin) }; |
+ Handle<Value> result = constructor->NewInstance(1, new_args); |
+ if (try_catch.HasCaught()) return result; |
+ Handle<Object> buffer = result->ToObject(); |
+ uint8_t* dest = |
+ static_cast<uint8_t*>(buffer->GetIndexedPropertiesExternalArrayData()); |
+ uint8_t* src = begin + static_cast<uint8_t*>( |
+ self->GetIndexedPropertiesExternalArrayData()); |
+ memcpy(dest, src, end - begin); |
+ |
+ return buffer; |
+} |
+ |
+ |
+Handle<Value> Shell::ArraySubArray(const Arguments& args) { |
+ TryCatch try_catch; |
+ |
+ if (!args.This()->IsObject()) { |
+ return ThrowException( |
+ String::New("'subarray' invoked on non-object receiver")); |
} |
Local<Object> self = args.This(); |
Local<Value> marker = self->GetHiddenValue(String::New(kArrayMarkerPropName)); |
if (marker.IsEmpty()) { |
return ThrowException( |
- String::New("subarray invoked on wrong receiver type.")); |
+ String::New("'subarray' invoked on wrong receiver type")); |
} |
Handle<Object> buffer = self->Get(String::New("buffer"))->ToObject(); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
int32_t length = |
convertToUint(self->Get(String::New("length")), &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
int32_t byteOffset = |
convertToUint(self->Get(String::New("byteOffset")), &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
int32_t element_size = |
convertToUint(self->Get(String::New("BYTES_PER_ELEMENT")), &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
if (args.Length() == 0) { |
return ThrowException( |
- String::New("subarray must have at least one parameter.")); |
+ String::New("'subarray' must have at least one argument")); |
} |
int32_t begin = convertToInt(args[0], &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
if (begin < 0) begin += length; |
if (begin < 0) begin = 0; |
if (begin > length) begin = length; |
@@ -552,7 +611,7 @@ Handle<Value> Shell::SubArray(const Arguments& args) { |
end = length; |
} else { |
end = convertToInt(args[1], &try_catch); |
- if (try_catch.HasCaught()) return try_catch.Exception(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
if (end < 0) end += length; |
if (end < 0) end = 0; |
if (end > length) end = length; |
@@ -570,6 +629,147 @@ Handle<Value> Shell::SubArray(const Arguments& args) { |
} |
+Handle<Value> Shell::ArraySet(const Arguments& args) { |
+ TryCatch try_catch; |
+ |
+ if (!args.This()->IsObject()) { |
+ return ThrowException( |
+ String::New("'set' invoked on non-object receiver")); |
+ } |
+ |
+ Local<Object> self = args.This(); |
+ Local<Value> marker = self->GetHiddenValue(String::New(kArrayMarkerPropName)); |
+ if (marker.IsEmpty()) { |
+ return ThrowException( |
+ String::New("'set' invoked on wrong receiver type")); |
+ } |
+ int32_t length = |
+ convertToUint(self->Get(String::New("length")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ int32_t element_size = |
+ convertToUint(self->Get(String::New("BYTES_PER_ELEMENT")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ |
+ if (args.Length() == 0) { |
+ return ThrowException( |
+ String::New("'set' must have at least one argument")); |
+ } |
+ if (!args[0]->IsObject() || |
+ !args[0]->ToObject()->Has(String::New("length"))) { |
+ return ThrowException( |
+ String::New("'set' invoked with non-array argument")); |
+ } |
+ Handle<Object> source = args[0]->ToObject(); |
+ int32_t source_length = |
+ convertToUint(source->Get(String::New("length")), &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 ThrowException(String::New("offset or source length out of bounds")); |
+ } |
+ |
+ int32_t source_element_size; |
+ if (source->GetHiddenValue(String::New(kArrayMarkerPropName)).IsEmpty()) { |
+ source_element_size = 0; |
+ } else { |
+ source_element_size = |
+ convertToUint(source->Get(String::New("BYTES_PER_ELEMENT")), &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<Object> buffer = self->Get(String::New("buffer"))->ToObject(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ Handle<Object> source_buffer = |
+ source->Get(String::New("buffer"))->ToObject(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ int32_t byteOffset = |
+ convertToUint(self->Get(String::New("byteOffset")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ int32_t source_byteOffset = |
+ convertToUint(source->Get(String::New("byteOffset")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ |
+ uint8_t* dest = byteOffset + offset * element_size + static_cast<uint8_t*>( |
+ buffer->GetIndexedPropertiesExternalArrayData()); |
+ uint8_t* src = source_byteOffset + static_cast<uint8_t*>( |
+ 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<Object> buffer = self->Get(String::New("buffer"))->ToObject(); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ Handle<Object> source_buffer = |
+ source->Get(String::New("buffer"))->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(String::New("byteOffset")), &try_catch); |
+ if (try_catch.HasCaught()) return try_catch.ReThrow(); |
+ int32_t source_byteOffset = |
+ convertToUint(source->Get(String::New("byteOffset")), &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<Value> 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(); |
+} |
+ |
+ |
void Shell::ExternalArrayWeakCallback(Persistent<Value> object, void* data) { |
HandleScope scope; |
int32_t length = |
@@ -919,10 +1119,22 @@ class BZip2Decompressor : public v8::StartupDataDecompressor { |
#endif |
+Handle<FunctionTemplate> Shell::CreateArrayBufferTemplate( |
+ InvocationCallback fun) { |
+ Handle<FunctionTemplate> buffer_template = FunctionTemplate::New(fun); |
+ Local<Template> proto_template = buffer_template->PrototypeTemplate(); |
+ proto_template->Set(String::New("slice"), |
+ FunctionTemplate::New(ArrayBufferSlice)); |
+ return buffer_template; |
+} |
+ |
+ |
Handle<FunctionTemplate> Shell::CreateArrayTemplate(InvocationCallback fun) { |
Handle<FunctionTemplate> array_template = FunctionTemplate::New(fun); |
Local<Template> proto_template = array_template->PrototypeTemplate(); |
- proto_template->Set(String::New("subarray"), FunctionTemplate::New(SubArray)); |
+ proto_template->Set(String::New("set"), FunctionTemplate::New(ArraySet)); |
+ proto_template->Set(String::New("subarray"), |
+ FunctionTemplate::New(ArraySubArray)); |
return array_template; |
} |
@@ -948,7 +1160,7 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { |
PropertyAttribute attr = |
static_cast<PropertyAttribute>(ReadOnly | DontDelete); |
global_template->Set(String::New("ArrayBuffer"), |
- CreateArrayTemplate(ArrayBuffer), attr); |
+ CreateArrayBufferTemplate(ArrayBuffer), attr); |
global_template->Set(String::New("Int8Array"), |
CreateArrayTemplate(Int8Array), attr); |
global_template->Set(String::New("Uint8Array"), |