Apply numeric casts correctly in typed arrays and related code.
authordslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 18 Mar 2014 10:55:29 +0000 (10:55 +0000)
committerdslomov@chromium.org <dslomov@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Tue, 18 Mar 2014 10:55:29 +0000 (10:55 +0000)
R=jkummerow@chromium.org
BUG=353004
LOG=Y

Committed: https://code.google.com/p/v8/source/detail?r=20020

Review URL: https://codereview.chromium.org/201873005

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20022 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/api.cc
src/arraybuffer.js
src/runtime.cc
src/runtime.h
src/typedarray.js
test/mjsunit/regress/regress-353004.js [new file with mode: 0644]

index c5241ec..5dd392f 100644 (file)
@@ -5936,20 +5936,7 @@ void v8::ArrayBuffer::Neuter() {
                   "Only externalized ArrayBuffers can be neutered");
   LOG_API(obj->GetIsolate(), "v8::ArrayBuffer::Neuter()");
   ENTER_V8(isolate);
-
-  for (i::Handle<i::Object> view_obj(obj->weak_first_view(), isolate);
-       !view_obj->IsUndefined();) {
-    i::Handle<i::JSArrayBufferView> view(i::JSArrayBufferView::cast(*view_obj));
-    if (view->IsJSTypedArray()) {
-      i::JSTypedArray::cast(*view)->Neuter();
-    } else if (view->IsJSDataView()) {
-      i::JSDataView::cast(*view)->Neuter();
-    } else {
-      UNREACHABLE();
-    }
-    view_obj = i::handle(view->weak_next(), isolate);
-  }
-  obj->Neuter();
+  i::Runtime::NeuterArrayBuffer(obj);
 }
 
 
index cfaa8d7..d5fd9ad 100644 (file)
@@ -56,6 +56,9 @@ function ArrayBufferSlice(start, end) {
   }
 
   var relativeStart = TO_INTEGER(start);
+  if (!IS_UNDEFINED(end)) {
+    end = TO_INTEGER(end);
+  }
   var first;
   var byte_length = %ArrayBufferGetByteLength(this);
   if (relativeStart < 0) {
@@ -63,7 +66,7 @@ function ArrayBufferSlice(start, end) {
   } else {
     first = MathMin(relativeStart, byte_length);
   }
-  var relativeEnd = IS_UNDEFINED(end) ? byte_length : TO_INTEGER(end);
+  var relativeEnd = IS_UNDEFINED(end) ? byte_length : end;
   var fin;
   if (relativeEnd < 0) {
     fin = MathMax(byte_length + relativeEnd, 0);
index 75b9f2f..6c5ec1c 100644 (file)
@@ -791,6 +791,24 @@ bool Runtime::SetupArrayBufferAllocatingData(
 }
 
 
+void Runtime::NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer) {
+  Isolate* isolate = array_buffer->GetIsolate();
+  for (Handle<Object> view_obj(array_buffer->weak_first_view(), isolate);
+       !view_obj->IsUndefined();) {
+    Handle<JSArrayBufferView> view(JSArrayBufferView::cast(*view_obj));
+    if (view->IsJSTypedArray()) {
+      JSTypedArray::cast(*view)->Neuter();
+    } else if (view->IsJSDataView()) {
+      JSDataView::cast(*view)->Neuter();
+    } else {
+      UNREACHABLE();
+    }
+    view_obj = handle(view->weak_next(), isolate);
+  }
+  array_buffer->Neuter();
+}
+
+
 RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferInitialize) {
   HandleScope scope(isolate);
   ASSERT(args.length() == 2);
@@ -844,7 +862,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferSliceImpl) {
 
   if (target_length == 0) return isolate->heap()->undefined_value();
 
-  ASSERT(NumberToSize(isolate, source->byte_length()) - target_length >= start);
+  size_t source_byte_length = NumberToSize(isolate, source->byte_length());
+  CHECK(start <= source_byte_length);
+  CHECK(source_byte_length - start >= target_length);
   uint8_t* source_data = reinterpret_cast<uint8_t*>(source->backing_store());
   uint8_t* target_data = reinterpret_cast<uint8_t*>(target->backing_store());
   CopyBytes(target_data, source_data + start, target_length);
@@ -862,6 +882,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferIsView) {
 }
 
 
+RUNTIME_FUNCTION(MaybeObject*, Runtime_ArrayBufferNeuter) {
+  HandleScope scope(isolate);
+  CONVERT_ARG_HANDLE_CHECKED(JSArrayBuffer, array_buffer, 0);
+  ASSERT(!array_buffer->is_external());
+  void* backing_store = array_buffer->backing_store();
+  size_t byte_length = NumberToSize(isolate, array_buffer->byte_length());
+  array_buffer->set_is_external(true);
+  Runtime::NeuterArrayBuffer(array_buffer);
+  V8::ArrayBufferAllocator()->Free(backing_store, byte_length);
+  return isolate->heap()->undefined_value();
+}
+
+
 void Runtime::ArrayIdToTypeAndSize(
     int arrayId, ExternalArrayType* array_type, size_t* element_size) {
   switch (arrayId) {
@@ -905,7 +938,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_TypedArrayInitialize) {
 
   size_t byte_offset = NumberToSize(isolate, *byte_offset_object);
   size_t byte_length = NumberToSize(isolate, *byte_length_object);
-  ASSERT(byte_length % element_size == 0);
+  size_t array_buffer_byte_length =
+      NumberToSize(isolate, buffer->byte_length());
+  CHECK(byte_offset <= array_buffer_byte_length);
+  CHECK(array_buffer_byte_length - byte_offset >= byte_length);
+
+  CHECK_EQ(0, static_cast<int>(byte_length % element_size));
   size_t length = byte_length / element_size;
 
   if (length > static_cast<unsigned>(Smi::kMaxValue)) {
index 99c575f..23d6d95 100644 (file)
@@ -365,6 +365,7 @@ namespace internal {
   F(ArrayBufferGetByteLength, 1, 1)\
   F(ArrayBufferSliceImpl, 3, 1) \
   F(ArrayBufferIsView, 1, 1) \
+  F(ArrayBufferNeuter, 1, 1) \
   \
   F(TypedArrayInitialize, 5, 1) \
   F(TypedArrayInitializeFromArrayLike, 4, 1) \
@@ -828,6 +829,8 @@ class Runtime : public AllStatic {
       size_t allocated_length,
       bool initialize = true);
 
+  static void NeuterArrayBuffer(Handle<JSArrayBuffer> array_buffer);
+
   static void FreeArrayBuffer(
       Isolate* isolate,
       JSArrayBuffer* phantom_array_buffer);
index 88fbb34..cba8993 100644 (file)
@@ -49,12 +49,20 @@ endmacro
 
 macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
   function NAMEConstructByArrayBuffer(obj, buffer, byteOffset, length) {
+    if (!IS_UNDEFINED(byteOffset)) {
+        byteOffset =
+            ToPositiveInteger(byteOffset,  "invalid_typed_array_length");
+    }
+    if (!IS_UNDEFINED(length)) {
+        length = ToPositiveInteger(length, "invalid_typed_array_length");
+    }
+
     var bufferByteLength = %ArrayBufferGetByteLength(buffer);
     var offset;
     if (IS_UNDEFINED(byteOffset)) {
       offset = 0;
     } else {
-      offset = ToPositiveInteger(byteOffset, "invalid_typed_array_length");
+      offset = byteOffset;
 
       if (offset % ELEMENT_SIZE !== 0) {
         throw MakeRangeError("invalid_typed_array_alignment",
@@ -75,7 +83,7 @@ macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
       newByteLength = bufferByteLength - offset;
       newLength = newByteLength / ELEMENT_SIZE;
     } else {
-      var newLength = ToPositiveInteger(length, "invalid_typed_array_length");
+      var newLength = length;
       newByteLength = newLength * ELEMENT_SIZE;
     }
     if ((offset + newByteLength > bufferByteLength)
@@ -99,6 +107,7 @@ macro TYPED_ARRAY_CONSTRUCTOR(ARRAY_ID, NAME, ELEMENT_SIZE)
   function NAMEConstructByArrayLike(obj, arrayLike) {
     var length = arrayLike.length;
     var l = ToPositiveInteger(length, "invalid_typed_array_length");
+
     if (l > %MaxSmi()) {
       throw MakeRangeError("invalid_typed_array_length");
     }
@@ -148,15 +157,19 @@ function TypedArrayGetLength() {
 
 function CreateSubArray(elementSize, constructor) {
   return function(begin, end) {
-    var srcLength = %TypedArrayGetLength(this);
     var beginInt = TO_INTEGER(begin);
+    if (!IS_UNDEFINED(end)) {
+      end = TO_INTEGER(end);
+    }
+
+    var srcLength = %TypedArrayGetLength(this);
     if (beginInt < 0) {
       beginInt = MathMax(0, srcLength + beginInt);
     } else {
       beginInt = MathMin(srcLength, beginInt);
     }
 
-    var endInt = IS_UNDEFINED(end) ? srcLength : TO_INTEGER(end);
+    var endInt = IS_UNDEFINED(end) ? srcLength : end;
     if (endInt < 0) {
       endInt = MathMax(0, srcLength + endInt);
     } else {
@@ -317,14 +330,23 @@ function DataViewConstructor(buffer, byteOffset, byteLength) { // length = 3
     if (!IS_ARRAYBUFFER(buffer)) {
       throw MakeTypeError('data_view_not_array_buffer', []);
     }
+    if (!IS_UNDEFINED(byteOffset)) {
+        byteOffset = ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
+    }
+    if (!IS_UNDEFINED(byteLength)) {
+        byteLength = TO_INTEGER(byteLength);
+    }
+
     var bufferByteLength = %ArrayBufferGetByteLength(buffer);
-    var offset = IS_UNDEFINED(byteOffset) ?
-      0 : ToPositiveInteger(byteOffset, 'invalid_data_view_offset');
+
+    var offset = IS_UNDEFINED(byteOffset) ?  0 : byteOffset;
     if (offset > bufferByteLength) {
       throw MakeRangeError('invalid_data_view_offset');
     }
-    var length = IS_UNDEFINED(byteLength) ?
-      bufferByteLength - offset : TO_INTEGER(byteLength);
+
+    var length = IS_UNDEFINED(byteLength)
+        ? bufferByteLength - offset
+        : byteLength;
     if (length < 0 || offset + length > bufferByteLength) {
       throw new MakeRangeError('invalid_data_view_length');
     }
diff --git a/test/mjsunit/regress/regress-353004.js b/test/mjsunit/regress/regress-353004.js
new file mode 100644 (file)
index 0000000..658fd6d
--- /dev/null
@@ -0,0 +1,74 @@
+// Copyright 2014 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
+
+var buffer1 = new ArrayBuffer(100 * 1024);
+
+var array1 = new Uint8Array(buffer1, {valueOf : function() {
+  %ArrayBufferNeuter(buffer1);
+  return 0;
+}});
+
+assertEquals(0, array1.length);
+
+var buffer2 = new ArrayBuffer(100 * 1024);
+
+assertThrows(function() {
+  var array2 = new Uint8Array(buffer2, 0, {valueOf : function() {
+      %ArrayBufferNeuter(buffer2);
+      return 100 * 1024;
+  }});
+}, RangeError);
+
+
+var buffer3 = new ArrayBuffer(100 * 1024 * 1024);
+var dataView1 = new DataView(buffer3, {valueOf : function() {
+  %ArrayBufferNeuter(buffer3);
+  return 0;
+}});
+
+assertEquals(0, dataView1.byteLength);
+
+var buffer4 = new ArrayBuffer(100 * 1024);
+assertThrows(function() {
+  var dataView2 = new DataView(buffer4, 0, {valueOf : function() {
+    %ArrayBufferNeuter(buffer4);
+    return 100 * 1024 * 1024;
+  }});
+}, RangeError);
+
+
+var buffer5 = new ArrayBuffer(100 * 1024);
+var buffer6 = buffer5.slice({valueOf : function() {
+  %ArrayBufferNeuter(buffer5);
+  return 0;
+}}, 100 * 1024 * 1024);
+assertEquals(0, buffer6.byteLength);
+
+
+var buffer7 = new ArrayBuffer(100 * 1024 * 1024);
+var buffer8 = buffer7.slice(0, {valueOf : function() {
+  %ArrayBufferNeuter(buffer7);
+  return 100 * 1024 * 1024;
+}});
+assertEquals(0, buffer8.byteLength);
+
+var buffer9 = new ArrayBuffer(1024);
+var array9 = new Uint8Array(buffer9);
+var array10 = array9.subarray({valueOf : function() {
+    %ArrayBufferNeuter(buffer9);
+    return 0;
+  }}, 1024);
+assertEquals(0, array9.length);
+assertEquals(0, array10.length);
+
+var buffer11 = new ArrayBuffer(1024);
+var array11 = new Uint8Array(buffer11);
+var array12 = array11.subarray(0, {valueOf : function() {
+      %ArrayBufferNeuter(buffer11);
+      return 1024;
+    }});
+assertEquals(0, array11.length);
+assertEquals(0, array12.length);