TypedArray accessor detection: consider entire prototype chain
authorfedor <fedor@indutny.com>
Thu, 10 Sep 2015 12:21:47 +0000 (05:21 -0700)
committerCommit bot <commit-bot@chromium.org>
Thu, 10 Sep 2015 12:21:55 +0000 (12:21 +0000)
When looking up a special accessor for known TypedArray fields
("length", "byteLength", "byteOffset"), consider the entire prototype
chain, not only the direct prototype.
This allows subclasses of TypedArrays to benefit from fast specialized
accesses.

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

Cr-Commit-Position: refs/heads/master@{#30678}

src/accessors.cc
test/mjsunit/regress/regress-typedarray-length.js

index 6544e71..4421b43 100644 (file)
@@ -99,22 +99,37 @@ bool Accessors::IsJSArrayBufferViewFieldAccessor(Handle<Map> map,
   Isolate* isolate = name->GetIsolate();
 
   switch (map->instance_type()) {
-    case JS_TYPED_ARRAY_TYPE:
-      // %TypedArray%.prototype is non-configurable, and so are the following
-      // named properties on %TypedArray%.prototype, so we can directly inline
-      // the field-load for typed array maps that still use their
-      // %TypedArray%.prototype.
-      if (JSFunction::cast(map->GetConstructor())->prototype() !=
-          map->prototype()) {
+    case JS_TYPED_ARRAY_TYPE: {
+      if (!CheckForName(name, isolate->factory()->length_string(),
+                        JSTypedArray::kLengthOffset, object_offset) &&
+          !CheckForName(name, isolate->factory()->byte_length_string(),
+                        JSTypedArray::kByteLengthOffset, object_offset) &&
+          !CheckForName(name, isolate->factory()->byte_offset_string(),
+                        JSTypedArray::kByteOffsetOffset, object_offset)) {
         return false;
       }
-      return CheckForName(name, isolate->factory()->length_string(),
-                          JSTypedArray::kLengthOffset, object_offset) ||
-             CheckForName(name, isolate->factory()->byte_length_string(),
-                          JSTypedArray::kByteLengthOffset, object_offset) ||
-             CheckForName(name, isolate->factory()->byte_offset_string(),
-                          JSTypedArray::kByteOffsetOffset, object_offset);
 
+      if (map->is_dictionary_map()) return false;
+
+      // Check if the property is overridden on the instance.
+      DescriptorArray* descriptors = map->instance_descriptors();
+      int descriptor = descriptors->SearchWithCache(*name, *map);
+      if (descriptor != DescriptorArray::kNotFound) return false;
+
+      Handle<Object> proto = Handle<Object>(map->prototype(), isolate);
+      if (!proto->IsJSReceiver()) return false;
+
+      // Check if the property is defined in the prototype chain.
+      LookupIterator it(proto, name);
+      if (!it.IsFound()) return false;
+
+      Object* original_proto =
+          JSFunction::cast(map->GetConstructor())->prototype();
+
+      // Property is not configurable. It is enough to verify that
+      // the holder is the same.
+      return *it.GetHolder<Object>() == original_proto;
+    }
     case JS_DATA_VIEW_TYPE:
       return CheckForName(name, isolate->factory()->byte_length_string(),
                           JSDataView::kByteLengthOffset, object_offset) ||
index cae5573..ee85364 100644 (file)
@@ -71,6 +71,43 @@ assertEquals(undefined, get(a));
   assertEquals(undefined, get(a));
 })();
 
+(function() {
+  "use strict";
+
+  class MyTypedArray extends Int32Array {
+    constructor(length) {
+      super(length);
+    }
+  }
+
+  a = new MyTypedArray(1024);
+
+  get = function(a) {
+    return a.length;
+  }
+
+  assertEquals(1024, get(a));
+  assertEquals(1024, get(a));
+  assertEquals(1024, get(a));
+  %OptimizeFunctionOnNextCall(get);
+  assertEquals(1024, get(a));
+})();
+
+(function() {
+  "use strict";
+  var a = new Uint8Array(4);
+  Object.defineProperty(a, "length", {get: function() { return "blah"; }});
+  get = function(a) {
+    return a.length;
+  }
+
+  assertEquals("blah", get(a));
+  assertEquals("blah", get(a));
+  assertEquals("blah", get(a));
+  %OptimizeFunctionOnNextCall(get);
+  assertEquals("blah", get(a));
+})();
+
 // Ensure we cannot delete length, byteOffset, byteLength.
 assertTrue(Int32Array.prototype.hasOwnProperty("length"));
 assertTrue(Int32Array.prototype.hasOwnProperty("byteOffset"));