[JSC] Implement Object.getOwnPropertyDescriptors() proposal
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 Feb 2016 03:17:53 +0000 (03:17 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 3 Feb 2016 03:17:53 +0000 (03:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=153799

Patch by Caitlin Potter <caitp@igalia.com> on 2016-02-02
Reviewed by Darin Adler.

Source/JavaScriptCore:

Implements the Object.getOwnPropertyDescriptors() proposal, which
reached Stage 3 in the TC39 process in January 2016.
https://github.com/tc39/proposal-object-getownpropertydescriptors

The method extracts a set of property descriptor objects, which can
be safely used via `Object.create()`.

* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorGetOwnPropertyDescriptors):

LayoutTests:

* js/Object-getOwnPropertyNames-expected.txt:
* js/script-tests/Object-getOwnPropertyNames.js:

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

LayoutTests/ChangeLog
LayoutTests/js/Object-getOwnPropertyNames-expected.txt
LayoutTests/js/script-tests/Object-getOwnPropertyNames.js
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.h
Source/JavaScriptCore/tests/es6.yaml
Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js [new file with mode: 0644]
Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors.js [new file with mode: 0644]

index f4e32d1..2ee3c35 100644 (file)
@@ -1,3 +1,13 @@
+2016-02-02  Caitlin Potter  <caitp@igalia.com>
+
+        [JSC] Implement Object.getOwnPropertyDescriptors() proposal
+        https://bugs.webkit.org/show_bug.cgi?id=153799
+
+        Reviewed by Darin Adler.
+
+        * js/Object-getOwnPropertyNames-expected.txt:
+        * js/script-tests/Object-getOwnPropertyNames.js:
+
 2016-02-02  Brady Eidson  <beidson@apple.com>
 
         Modern IDB: storage/indexeddb/cursor-primary-key-order.html fails with SQLite backend.
index 6802bfe..363ef2c 100644 (file)
@@ -41,7 +41,7 @@ PASS getSortedOwnPropertyNames(decodeURI) is ['length', 'name']
 PASS getSortedOwnPropertyNames(decodeURIComponent) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURI) is ['length', 'name']
 PASS getSortedOwnPropertyNames(encodeURIComponent) is ['length', 'name']
-PASS getSortedOwnPropertyNames(Object) is ['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf']
+PASS getSortedOwnPropertyNames(Object) is ['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf']
 PASS getSortedOwnPropertyNames(Object.prototype) is ['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']
 PASS getSortedOwnPropertyNames(Function) is ['length', 'name', 'prototype']
 PASS getSortedOwnPropertyNames(Function.prototype) is ['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']
index bfa649a..c042637 100644 (file)
@@ -50,7 +50,7 @@ var expectedPropertyNamesSet = {
     "encodeURI": "['length', 'name']",
     "encodeURIComponent": "['length', 'name']",
 // Built-in ECMA objects
-    "Object": "['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf']",
+    "Object": "['assign', 'create', 'defineProperties', 'defineProperty', 'freeze', 'getOwnPropertyDescriptor', 'getOwnPropertyDescriptors', 'getOwnPropertyNames', 'getOwnPropertySymbols', 'getPrototypeOf', 'is', 'isExtensible', 'isFrozen', 'isSealed', 'keys', 'length', 'name', 'preventExtensions', 'prototype', 'seal', 'setPrototypeOf']",
     "Object.prototype": "['__defineGetter__', '__defineSetter__', '__lookupGetter__', '__lookupSetter__', '__proto__', 'constructor', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'toLocaleString', 'toString', 'valueOf']",
     "Function": "['length', 'name', 'prototype']",
     "Function.prototype": "['apply', 'bind', 'call', 'constructor', 'length', 'name', 'toString']",
index 2526abb..557d4b2 100644 (file)
@@ -1,3 +1,20 @@
+2016-02-02  Caitlin Potter  <caitp@igalia.com>
+
+        [JSC] Implement Object.getOwnPropertyDescriptors() proposal
+        https://bugs.webkit.org/show_bug.cgi?id=153799
+
+        Reviewed by Darin Adler.
+
+        Implements the Object.getOwnPropertyDescriptors() proposal, which
+        reached Stage 3 in the TC39 process in January 2016.
+        https://github.com/tc39/proposal-object-getownpropertydescriptors
+
+        The method extracts a set of property descriptor objects, which can
+        be safely used via `Object.create()`.
+
+        * runtime/ObjectConstructor.cpp:
+        (JSC::objectConstructorGetOwnPropertyDescriptors):
+
 2016-02-02  Filip Pizlo  <fpizlo@apple.com>
 
         B3 should be able to compile trivial self-loops
index c5ec0ec..347ae08 100644 (file)
@@ -68,6 +68,7 @@ const ClassInfo ObjectConstructor::s_info = { "Function", &InternalFunction::s_i
   getPrototypeOf            objectConstructorGetPrototypeOf             DontEnum|Function 1
   setPrototypeOf            objectConstructorSetPrototypeOf             DontEnum|Function 2
   getOwnPropertyDescriptor  objectConstructorGetOwnPropertyDescriptor   DontEnum|Function 2
+  getOwnPropertyDescriptors objectConstructorGetOwnPropertyDescriptors  DontEnum|Function 1
   getOwnPropertyNames       objectConstructorGetOwnPropertyNames        DontEnum|Function 1
   keys                      objectConstructorKeys                       DontEnum|Function 1
   defineProperty            objectConstructorDefineProperty             DontEnum|Function 3
@@ -247,6 +248,26 @@ JSValue objectConstructorGetOwnPropertyDescriptor(ExecState* exec, JSObject* obj
     return description;
 }
 
+JSValue objectConstructorGetOwnPropertyDescriptors(ExecState* exec, JSObject* object)
+{
+    PropertyNameArray properties(exec, PropertyNameMode::StringsAndSymbols);
+    object->getOwnPropertyNames(object, exec, properties, EnumerationMode(DontEnumPropertiesMode::Include));
+    if (exec->hadException())
+        return jsUndefined();
+
+    JSObject* descriptors = constructEmptyObject(exec);
+
+    for (auto& propertyName : properties) {
+        JSValue fromDescriptor = objectConstructorGetOwnPropertyDescriptor(exec, object, propertyName);
+        if (exec->hadException())
+            return jsUndefined();
+
+        descriptors->putDirect(exec->vm(), propertyName, fromDescriptor, 0);
+    }
+
+    return descriptors;
+}
+
 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState* exec)
 {
     JSObject* object = exec->argument(0).toObject(exec);
@@ -258,6 +279,14 @@ EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState
     return JSValue::encode(objectConstructorGetOwnPropertyDescriptor(exec, object, propertyName));
 }
 
+EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptors(ExecState* exec)
+{
+    JSObject* object = exec->argument(0).toObject(exec);
+    if (exec->hadException())
+        return JSValue::encode(jsUndefined());
+    return JSValue::encode(objectConstructorGetOwnPropertyDescriptors(exec, object));
+}
+
 // FIXME: Use the enumeration cache.
 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyNames(ExecState* exec)
 {
index 5db78ba..6a12f56 100644 (file)
@@ -28,6 +28,7 @@
 namespace JSC {
 
 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptor(ExecState*);
+EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertyDescriptors(ExecState*);
 EncodedJSValue JSC_HOST_CALL objectConstructorGetOwnPropertySymbols(ExecState*);
 EncodedJSValue JSC_HOST_CALL objectConstructorKeys(ExecState*);
 EncodedJSValue JSC_HOST_CALL ownEnumerablePropertyKeys(ExecState*);
@@ -93,6 +94,7 @@ inline JSObject* constructEmptyObject(ExecState* exec)
 JSObject* objectConstructorFreeze(ExecState*, JSObject*);
 JSValue objectConstructorGetPrototypeOf(ExecState*, JSObject*);
 JSValue objectConstructorGetOwnPropertyDescriptor(ExecState*, JSObject*, const Identifier&);
+JSValue objectConstructorGetOwnPropertyDescriptors(ExecState*, JSObject*);
 JSArray* ownPropertyKeys(ExecState*, JSObject*, PropertyNameMode, DontEnumPropertiesMode);
 bool toPropertyDescriptor(ExecState*, JSValue, PropertyDescriptor&);
 
index b3186c1..064fa5a 100644 (file)
   cmd: runES6 :normal
 - path: es6/well-known_symbols_Symbol.toStringTag_misc._built-ins.js
   cmd: runES6 :normal
+# Late-stage proposals for a future ECMAScript standard
+# FIXME: move these to a new directory?
+- path: es6/Object_static_methods_Object.getOwnPropertyDescriptors.js
+  cmd: runES6 :normal
+- path: es6/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js
+  cmd: runES6 :fail
\ No newline at end of file
diff --git a/Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js b/Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors-proxy.js
new file mode 100644 (file)
index 0000000..952c20f
--- /dev/null
@@ -0,0 +1,97 @@
+function shouldBe(expected, actual, msg) {
+    if (msg === void 0)
+        msg = '';
+    else
+        msg = ' for ' + msg;
+    if (actual !== expected)
+        throw new Error('bad value' + msg + ': ' + actual + '. Expected ' + expected);
+}
+
+function shouldThrow(func, errorType) {
+    try {
+        func();
+        throw new Error('Expected ' + func + '() to throw ' + errorType.name + ', but did not throw.');
+    } catch (e) {
+        if (e instanceof errorType) return;
+        throw new Error('Expected ' + func + '() to throw ' + errorType.name + ', but threw ' + e);
+    }
+}
+
+function shouldBeDataProperty(expected, value, name) {
+    if (name === void 0)
+        name = '<property descriptor>';
+    shouldBe(value, expected.value, name + '.value');
+    shouldBe(true, expected.enumerable, name + '.enumerable');
+    shouldBe(true, expected.configurable, name + '.configurable');
+    shouldBe(true, expected.writable, name + '.writable');
+    shouldBe(undefined, expected.get, name + '.get');
+    shouldBe(undefined, expected.set, name + '.set');
+}
+
+(function testPropertyFilteringAndOrder() {
+    var log = [];
+    var sym = Symbol('test');
+    var O = {
+        0: 0,
+        [sym]: 3,
+        'a': 2,
+        1: 1
+    };
+
+    var P = new Proxy(O, {
+        ownKeys(target) {
+            log.push('ownKeys()');
+            return Reflect.ownKeys(target);
+        },
+        getOwnPropertyDescriptor(target, name) {
+            log.push(`getOwnPropertyDescriptor(${String(name)})`);
+            return Reflect.getOwnPropertyDescriptor(target, name);
+        },
+        get() { throw new Error('[[Get]] trap should be unreachable'); },
+        set() { throw new Error('[[Set]] trap should be unreachable'); },
+        deleteProperty() { throw new Error('[[Delete]] trap should be unreachable'); },
+        defineProperty() { throw new Error('[[DefineOwnProperty]] trap should be unreachable'); }
+    });
+
+    var result = Object.getOwnPropertyDescriptors(P);
+    shouldBe('ownKeys()|getOwnPropertyDescriptor(0)|getOwnPropertyDescriptor(1)|getOwnPropertyDescriptor(a)|getOwnPropertyDescriptor(Symbol(foo))', log.join('|'));
+    shouldBeDataProperty(result[0], 0, 'result[0]');
+    shouldBeDataProperty(result[1], 1, 'result[1]');
+    shouldBeDataProperty(result.a, 1, 'result["a"]');
+    shouldBeDataProperty(result[sym], 1, 'result[Symbol(foo)]');
+
+    var result2 = Object.getOwnPropertyDescriptors(O);
+    shouldBeDataProperty(result2[0], 0, 'result2[0]');
+    shouldBeDataProperty(result2[1], 1, 'result2[1]');
+    shouldBeDataProperty(result2.a, 1, 'result2["a"]');
+    shouldBeDataProperty(result2[sym], 1, 'result2[Symbol(foo)]');
+})();
+
+(function testDuplicatePropertyNames() {
+    var i = 0;
+    var log = [];
+    var P = new Proxy({}, {
+    ownKeys() {
+        log.push(`ownKeys()`);
+        return [ 'A', 'A' ];
+    },
+    getOwnPropertyDescriptor(t, name) {
+        log.push(`getOwnPropertyDescriptor(${name})`);
+        if (i++) return;
+        return {
+            configurable: true,
+            writable: false,
+            value: 'VALUE'
+        };
+    },
+    get() { throw new Error('[[Get]] trap should be unreachable'); },
+    set() { throw new Error('[[Set]] trap should be unreachable'); },
+    deleteProperty() { throw new Error('[[Delete]] trap should be unreachable'); },
+    defineProperty() { throw new Error('[[DefineOwnProperty]] trap should be unreachable'); }
+  });
+
+  var result = Object.getOwnPropertyDescriptors(P);
+  shouldBe(void 0, result.A);
+  shouldBe(true, Object.hasOwnProperty.call(result, 'A'));
+  shouldBe('ownKeys()|getOwnPropertyDescriptor(A)|getOwnPropertyDescriptor(A)', log.join('|'));
+})();
diff --git a/Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors.js b/Source/JavaScriptCore/tests/es6/Object_static_methods_Object.getOwnPropertyDescriptors.js
new file mode 100644 (file)
index 0000000..d0a2d93
--- /dev/null
@@ -0,0 +1,83 @@
+function shouldBe(expected, actual, msg) {
+    if (msg === void 0)
+        msg = '';
+    else
+        msg = ' for ' + msg;
+    if (actual !== expected)
+        throw new Error('bad value' + msg + ': ' + actual + '. Expected ' + expected);
+}
+
+function shouldThrow(func, errorType) {
+    try {
+        func();
+        throw new Error('Expected ' + func + '() to throw ' + errorType.name + ', but did not throw.');
+    } catch (e) {
+        if (e instanceof errorType) return;
+        throw new Error('Expected ' + func + '() to throw ' + errorType.name + ', but threw ' + e);
+    }
+}
+
+function shouldBeDataProperty(expected, value, name) {
+    if (name === void 0)
+        name = '<property descriptor>';
+    shouldBe(value, expected.value, name + '.value');
+    shouldBe(true, expected.enumerable, name + '.enumerable');
+    shouldBe(true, expected.configurable, name + '.configurable');
+    shouldBe(true, expected.writable, name + '.writable');
+    shouldBe(undefined, expected.get, name + '.get');
+    shouldBe(undefined, expected.set, name + '.set');
+}
+
+(function testMeta() {
+    shouldBe(1, Object.getOwnPropertyDescriptors.length);
+
+    shouldBe('getOwnPropertyDescriptors', Object.getOwnPropertyDescriptors.name);
+
+    var propertyDescriptor = Reflect.getOwnPropertyDescriptor(Object, 'getOwnPropertyDescriptors');
+    shouldBe(false, propertyDescriptor.enumerable);
+    shouldBe(true, propertyDescriptor.writable);
+    shouldBe(true, propertyDescriptor.configurable);
+
+    shouldThrow(() => new Object.getOwnPropertyDescriptors({}), TypeError);
+})();
+
+(function testToObject() {
+    shouldThrow(() => Object.getOwnPropertyDescriptors(null), TypeError);
+    shouldThrow(() => Object.getOwnPropertyDescriptors(undefined), TypeError);
+    shouldThrow(() => Object.getOwnPropertyDescriptors(), TypeError);
+})();
+
+(function testPrototypeProperties() {
+    function F() {};
+    F.prototype.a = 'A';
+    F.prototype.b = 'B';
+
+    var F2 = new F();
+    Object.defineProperties(F2, {
+        'b': {
+            enumerable: false,
+            configurable: true,
+            writable: false,
+            value: 'Shadowed "B"'
+        },
+        'c': {
+            enumerable: false,
+            configurable: true,
+            writable: false,
+            value: 'C'
+        }
+    });
+
+    var result = Object.getOwnPropertyDescriptors(F2);
+    shouldBe(undefined, result.a);
+
+    shouldBe(result.b.enumerable, false);
+    shouldBe(result.b.configurable, true);
+    shouldBe(result.b.writable, false);
+    shouldBe(result.b.value, 'Shadowed "B"');
+
+    shouldBe(result.c.enumerable, false);
+    shouldBe(result.c.configurable, true);
+    shouldBe(result.c.writable, false);
+    shouldBe(result.c.value, 'C');
+})();