[JSC] Implement (Shared)ArrayBuffer.prototype.byteLength
authorutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Feb 2017 22:12:36 +0000 (22:12 +0000)
committerutatane.tea@gmail.com <utatane.tea@gmail.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 11 Feb 2017 22:12:36 +0000 (22:12 +0000)
https://bugs.webkit.org/show_bug.cgi?id=166476

Reviewed by Saam Barati.

JSTests:

* ChakraCore/test/typedarray/arraybufferType.baseline-jsc:
* stress/array-buffer-byte-length.js: Added.
(shouldBe):
(shouldThrow):
(Symbol):
* stress/reflect-set.js:

Source/JavaScriptCore:

`byteLength` becomes getter and is set in ArrayBuffer.prototype
and SharedArrayBuffer.prototype. This patch implements the
above getter in native function. We do not have any optimization
path for that for now since ArrayBuffer.prototype.byteLength is
not considered a hot function: while TypedArrays have [] accesses,
ArrayBuffer does not have that. Thus byteLength getter is not so
meaningful for a hot paths like iterations.

* runtime/JSArrayBuffer.cpp:
(JSC::JSArrayBuffer::getOwnPropertySlot): Deleted.
(JSC::JSArrayBuffer::put): Deleted.
(JSC::JSArrayBuffer::defineOwnProperty): Deleted.
(JSC::JSArrayBuffer::deleteProperty): Deleted.
(JSC::JSArrayBuffer::getOwnNonIndexPropertyNames): Deleted.
* runtime/JSArrayBuffer.h:
(JSC::JSArrayBuffer::impl): Deleted.
* runtime/JSArrayBufferPrototype.cpp:
(JSC::arrayBufferProtoGetterFuncByteLength):
(JSC::sharedArrayBufferProtoGetterFuncByteLength):
(JSC::JSArrayBufferPrototype::finishCreation):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@212196 268f45cc-cd09-0410-ab3c-d52691b4dbfc

JSTests/ChakraCore/test/typedarray/arraybufferType.baseline-jsc
JSTests/ChangeLog
JSTests/stress/array-buffer-byte-length.js [new file with mode: 0644]
JSTests/stress/reflect-set.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSArrayBuffer.cpp
Source/JavaScriptCore/runtime/JSArrayBuffer.h
Source/JavaScriptCore/runtime/JSArrayBufferPrototype.cpp

index 654b28d..9732af8 100644 (file)
@@ -7,6 +7,7 @@ foo = 20
 bar = 42
 20
 slice
+byteLength
 constructor
 aaa
 foo
@@ -17,6 +18,7 @@ aaa = 20
 foo = 20
 bar = 42
 slice
+byteLength
 constructor
 aaa
 foo
index 43499ea..f5f3e2b 100644 (file)
@@ -1,3 +1,17 @@
+2017-02-11  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement (Shared)ArrayBuffer.prototype.byteLength
+        https://bugs.webkit.org/show_bug.cgi?id=166476
+
+        Reviewed by Saam Barati.
+
+        * ChakraCore/test/typedarray/arraybufferType.baseline-jsc:
+        * stress/array-buffer-byte-length.js: Added.
+        (shouldBe):
+        (shouldThrow):
+        (Symbol):
+        * stress/reflect-set.js:
+
 2017-02-10  Saam Barati  <sbarati@apple.com>
 
         Object allocation sinking phase doesn't properly handle control flow when emitting a PutHint of a materialized object into a PromotedHeapLocation of a still sunken object
diff --git a/JSTests/stress/array-buffer-byte-length.js b/JSTests/stress/array-buffer-byte-length.js
new file mode 100644 (file)
index 0000000..64335c1
--- /dev/null
@@ -0,0 +1,65 @@
+function shouldBe(actual, expected)
+{
+    if (actual !== expected)
+        throw new Error(`bad value: ${String(actual)}`);
+}
+
+function shouldThrow(func, errorMessage)
+{
+    var errorThrown = false;
+    var error = null;
+    try {
+        func();
+    } catch (e) {
+        errorThrown = true;
+        error = e;
+    }
+    if (!errorThrown)
+        throw new Error('not thrown');
+    if (String(error) !== errorMessage)
+        throw new Error(`bad error: ${String(error)}`);
+}
+
+{
+    let arrayBuffer = new ArrayBuffer(42);
+    let sharedArrayBuffer = new SharedArrayBuffer(500);
+    shouldBe(arrayBuffer.byteLength, 42);
+    shouldBe(sharedArrayBuffer.byteLength, 500);
+    shouldBe(ArrayBuffer.prototype.hasOwnProperty('byteLength'), true);
+    shouldBe(SharedArrayBuffer.prototype.hasOwnProperty('byteLength'), true);
+
+    shouldBe(arrayBuffer.hasOwnProperty('byteLength'), false);
+    shouldBe(sharedArrayBuffer.hasOwnProperty('byteLength'), false);
+
+    shouldBe(!!Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get, true);
+    shouldBe(!!Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get, true);
+
+    shouldBe(!!Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').set, false);
+    shouldBe(!!Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').set, false);
+
+    shouldBe(Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get !== Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get, true);
+
+    shouldThrow(() => {
+        Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get.call(sharedArrayBuffer);
+    }, `TypeError: Receiver should not be a shared array buffer`);
+
+    shouldThrow(() => {
+        Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get.call(arrayBuffer);
+    }, `TypeError: Receiver should be a shared array buffer`);
+
+    for (let value of [ 0, true, "Cocoa", null, undefined, Symbol("Cappuccino") ]) {
+        shouldThrow(() => {
+            Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get.call(value);
+        }, `TypeError: Receiver should be an array buffer but was not an object`);
+        shouldThrow(() => {
+            Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get.call(value);
+        }, `TypeError: Receiver should be an array buffer but was not an object`);
+    }
+
+    shouldThrow(() => {
+        Object.getOwnPropertyDescriptor(ArrayBuffer.prototype, 'byteLength').get.call({});
+    }, `TypeError: Receiver should be an array buffer`);
+    shouldThrow(() => {
+        Object.getOwnPropertyDescriptor(SharedArrayBuffer.prototype, 'byteLength').get.call({});
+    }, `TypeError: Receiver should be an array buffer`);
+}
index d8d8b94..ef315ac 100644 (file)
@@ -490,10 +490,10 @@ var symbol = Symbol();
     var object = new ArrayBuffer(64);
     shouldBe(Reflect.defineProperty(object, 'byteLength', {
         writable: false
-    }), false);
-    shouldBe(Reflect.get(object, 'byteLength'), 64);
+    }), true);
+    shouldBe(Reflect.get(object, 'byteLength'), undefined);
     shouldBe(Reflect.set(object, 'byteLength', 20), false);
-    shouldBe(Reflect.get(object, 'byteLength'), 64);
+    shouldBe(Reflect.get(object, 'byteLength'), undefined);
 
     var object = new ArrayBuffer(64);
     shouldBe(Reflect.defineProperty(object, 'hello', {
index 7c4d3a7..a4ebac4 100644 (file)
@@ -1,3 +1,31 @@
+2017-02-11  Yusuke Suzuki  <utatane.tea@gmail.com>
+
+        [JSC] Implement (Shared)ArrayBuffer.prototype.byteLength
+        https://bugs.webkit.org/show_bug.cgi?id=166476
+
+        Reviewed by Saam Barati.
+
+        `byteLength` becomes getter and is set in ArrayBuffer.prototype
+        and SharedArrayBuffer.prototype. This patch implements the
+        above getter in native function. We do not have any optimization
+        path for that for now since ArrayBuffer.prototype.byteLength is
+        not considered a hot function: while TypedArrays have [] accesses,
+        ArrayBuffer does not have that. Thus byteLength getter is not so
+        meaningful for a hot paths like iterations.
+
+        * runtime/JSArrayBuffer.cpp:
+        (JSC::JSArrayBuffer::getOwnPropertySlot): Deleted.
+        (JSC::JSArrayBuffer::put): Deleted.
+        (JSC::JSArrayBuffer::defineOwnProperty): Deleted.
+        (JSC::JSArrayBuffer::deleteProperty): Deleted.
+        (JSC::JSArrayBuffer::getOwnNonIndexPropertyNames): Deleted.
+        * runtime/JSArrayBuffer.h:
+        (JSC::JSArrayBuffer::impl): Deleted.
+        * runtime/JSArrayBufferPrototype.cpp:
+        (JSC::arrayBufferProtoGetterFuncByteLength):
+        (JSC::sharedArrayBufferProtoGetterFuncByteLength):
+        (JSC::JSArrayBufferPrototype::finishCreation):
+
 2017-02-10  Saam Barati  <sbarati@apple.com>
 
         Object allocation sinking phase doesn't properly handle control flow when emitting a PutHint of a materialized object into a PromotedHeapLocation of a still sunken object
index 23540da..7a7faac 100644 (file)
@@ -85,74 +85,5 @@ size_t JSArrayBuffer::estimatedSize(JSCell* cell)
     return Base::estimatedSize(cell) + bufferEstimatedSize;
 }
 
-bool JSArrayBuffer::getOwnPropertySlot(
-    JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
-{
-    JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object);
-    
-    if (propertyName == exec->propertyNames().byteLength) {
-        slot.setValue(thisObject, DontDelete | ReadOnly, jsNumber(thisObject->impl()->byteLength()));
-        return true;
-    }
-    
-    return Base::getOwnPropertySlot(thisObject, exec, propertyName, slot);
-}
-
-bool JSArrayBuffer::put(
-    JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value,
-    PutPropertySlot& slot)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(cell);
-
-    if (UNLIKELY(isThisValueAltered(slot, thisObject))) {
-        scope.release();
-        return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode());
-    }
-    
-    if (propertyName == vm.propertyNames->byteLength)
-        return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral("Attempting to write to a read-only array buffer property."));
-
-    scope.release();
-    return Base::put(thisObject, exec, propertyName, value, slot);
-}
-
-bool JSArrayBuffer::defineOwnProperty(
-    JSObject* object, ExecState* exec, PropertyName propertyName,
-    const PropertyDescriptor& descriptor, bool shouldThrow)
-{
-    VM& vm = exec->vm();
-    auto scope = DECLARE_THROW_SCOPE(vm);
-    JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object);
-    
-    if (propertyName == vm.propertyNames->byteLength)
-        return typeError(exec, scope, shouldThrow, ASCIILiteral("Attempting to define read-only array buffer property."));
-
-    scope.release();
-    return Base::defineOwnProperty(thisObject, exec, propertyName, descriptor, shouldThrow);
-}
-
-bool JSArrayBuffer::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
-{
-    JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(cell);
-    
-    if (propertyName == exec->propertyNames().byteLength)
-        return false;
-    
-    return Base::deleteProperty(thisObject, exec, propertyName);
-}
-
-void JSArrayBuffer::getOwnNonIndexPropertyNames(
-    JSObject* object, ExecState* exec, PropertyNameArray& array, EnumerationMode mode)
-{
-    JSArrayBuffer* thisObject = jsCast<JSArrayBuffer*>(object);
-    
-    if (mode.includeDontEnumProperties())
-        array.add(exec->propertyNames().byteLength);
-    
-    Base::getOwnNonIndexPropertyNames(thisObject, exec, array, mode);
-}
-
 } // namespace JSC
 
index 52aa6fa..59287e1 100644 (file)
 
 namespace JSC {
 
-class JSArrayBuffer : public JSNonFinalObject {
+class JSArrayBuffer final : public JSNonFinalObject {
 public:
-    typedef JSNonFinalObject Base;
-    static const unsigned StructureFlags = Base::StructureFlags | OverridesGetPropertyNames | OverridesGetOwnPropertySlot;
+    using Base = JSNonFinalObject;
+    static const unsigned StructureFlags = Base::StructureFlags;
     
 protected:
     JSArrayBuffer(VM&, Structure*, PassRefPtr<ArrayBuffer>);
@@ -56,14 +56,7 @@ public:
     static ArrayBuffer* toWrapped(VM&, JSValue);
     
 protected:
-
     static size_t estimatedSize(JSCell*);
-    static bool getOwnPropertySlot(JSObject*, ExecState*, PropertyName, PropertySlot&);
-    static bool put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
-    static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, const PropertyDescriptor&, bool shouldThrow);
-    static bool deleteProperty(JSCell*, ExecState*, PropertyName);
-    
-    static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
 
 private:
     ArrayBuffer* m_impl;
index 5ee5b66..b803d95 100644 (file)
@@ -70,6 +70,46 @@ static EncodedJSValue JSC_HOST_CALL arrayBufferProtoFuncSlice(ExecState* exec)
     return JSValue::encode(result);
 }
 
+// http://tc39.github.io/ecmascript_sharedmem/shmem.html#sec-get-arraybuffer.prototype.bytelength
+static EncodedJSValue JSC_HOST_CALL arrayBufferProtoGetterFuncByteLength(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    JSValue thisValue = exec->thisValue();
+    if (!thisValue.isObject())
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should be an array buffer but was not an object"));
+
+    auto* thisObject = jsDynamicCast<JSArrayBuffer*>(vm, thisValue);
+    if (!thisObject)
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should be an array buffer"));
+    if (thisObject->isShared())
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should not be a shared array buffer"));
+
+    scope.release();
+
+    return JSValue::encode(jsNumber(thisObject->impl()->byteLength()));
+}
+
+// http://tc39.github.io/ecmascript_sharedmem/shmem.html#StructuredData.SharedArrayBuffer.prototype.get_byteLength
+static EncodedJSValue JSC_HOST_CALL sharedArrayBufferProtoGetterFuncByteLength(ExecState* exec)
+{
+    VM& vm = exec->vm();
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    JSValue thisValue = exec->thisValue();
+    if (!thisValue.isObject())
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should be an array buffer but was not an object"));
+
+    auto* thisObject = jsDynamicCast<JSArrayBuffer*>(vm, thisValue);
+    if (!thisObject)
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should be an array buffer"));
+    if (!thisObject->isShared())
+        return throwVMTypeError(exec, scope, ASCIILiteral("Receiver should be a shared array buffer"));
+
+    scope.release();
+
+    return JSValue::encode(jsNumber(thisObject->impl()->byteLength()));
+}
+
 const ClassInfo JSArrayBufferPrototype::s_info = {
     "ArrayBufferPrototype", &Base::s_info, 0, CREATE_METHOD_TABLE(JSArrayBufferPrototype)
 };
@@ -86,6 +126,10 @@ void JSArrayBufferPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject
     
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->slice, arrayBufferProtoFuncSlice, DontEnum, 2);
     putDirectWithoutTransition(vm, vm.propertyNames->toStringTagSymbol, jsString(&vm, arrayBufferSharingModeName(m_sharingMode)), DontEnum | ReadOnly);
+    if (m_sharingMode == ArrayBufferSharingMode::Default)
+        JSC_NATIVE_GETTER(vm.propertyNames->byteLength, arrayBufferProtoGetterFuncByteLength, DontEnum | ReadOnly);
+    else
+        JSC_NATIVE_GETTER(vm.propertyNames->byteLength, sharedArrayBufferProtoGetterFuncByteLength, DontEnum | ReadOnly);
 }
 
 JSArrayBufferPrototype* JSArrayBufferPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, ArrayBufferSharingMode sharingMode)