Implement ES6 Object.getOwnPropertySymbols
https://bugs.webkit.org/show_bug.cgi?id=141106

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

This patch implements `Object.getOwnPropertySymbols`.
One technical issue is that, since we use private symbols (such as `@Object`) in the
privileged JS code in `builtins/`, they should not be exposed.
To distinguish them from the usual symbols, check the target `StringImpl*` is a not private name
before adding it into PropertyNameArray.

To check the target `StringImpl*` is a private name, we leverage privateToPublic map in `BuiltinNames`
since all private symbols are held in this map.

* builtins/BuiltinExecutables.cpp:
(JSC::BuiltinExecutables::createExecutableInternal):
* builtins/BuiltinNames.h:
(JSC::BuiltinNames::isPrivateName):
* runtime/CommonIdentifiers.cpp:
(JSC::CommonIdentifiers::isPrivateName):
* runtime/CommonIdentifiers.h:
* runtime/EnumerationMode.h:
(JSC::EnumerationMode::EnumerationMode):
(JSC::EnumerationMode::includeSymbolProperties):
* runtime/ExceptionHelpers.cpp:
(JSC::createUndefinedVariableError):
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init):
* runtime/JSLexicalEnvironment.cpp:
(JSC::JSLexicalEnvironment::getOwnNonIndexPropertyNames):
* runtime/JSSymbolTableObject.cpp:
(JSC::JSSymbolTableObject::getOwnNonIndexPropertyNames):
* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructor::finishCreation):
(JSC::objectConstructorGetOwnPropertySymbols):
(JSC::defineProperties):
(JSC::objectConstructorSeal):
(JSC::objectConstructorFreeze):
(JSC::objectConstructorIsSealed):
(JSC::objectConstructorIsFrozen):
* runtime/ObjectConstructor.h:
(JSC::ObjectConstructor::create):
* runtime/Structure.cpp:
(JSC::Structure::getPropertyNamesFromStructure):
* tests/stress/object-get-own-property-symbols-perform-to-object.js: Added.
(compare):
* tests/stress/object-get-own-property-symbols.js: Added.
(forIn):
* tests/stress/symbol-define-property.js: Added.
(testSymbol):
* tests/stress/symbol-seal-and-freeze.js: Added.
* tests/stress/symbol-with-json.js: Added.

LayoutTests:

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

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@182343 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols-perform-to-object.js b/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols-perform-to-object.js
new file mode 100644
index 0000000..031c3fa4
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols-perform-to-object.js
@@ -0,0 +1,39 @@
+var primitives = [
+    ["string", []],
+    [42, []],
+    [Symbol("symbol"), []],
+    [true, []],
+    [false, []]
+];
+
+function compare(ax, bx) {
+    if (ax.length !== bx.length)
+        return false;
+    for (var i = 0, iz = ax.length; i < iz; ++i) {
+        if (ax[i] !== bx[i])
+            return false;
+    }
+    return true;
+}
+
+for (var [primitive, expected] of primitives) {
+    var ret = Object.getOwnPropertySymbols(primitive);
+    if (!compare(ret, expected))
+        throw new Error("bad value for " + String(primitive) + ": " + String(ret));
+}
+
+[
+    [ null, "TypeError: null is not an object (evaluating 'Object.getOwnPropertySymbols(value)')" ],
+    [ undefined, "TypeError: undefined is not an object (evaluating 'Object.getOwnPropertySymbols(value)')" ]
+].forEach(function ([value, message]) {
+    var error = null;
+    try {
+        Object.getOwnPropertySymbols(value);
+    } catch (e) {
+        error = e;
+    }
+    if (!error)
+        throw new Error("error not thrown");
+    if (String(error) !== message)
+        throw new Error("bad error: " + String(error));
+});
diff --git a/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols.js b/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols.js
new file mode 100644
index 0000000..40e93d8
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/object-get-own-property-symbols.js
@@ -0,0 +1,35 @@
+// This tests Object.getOwnPropertySymbols.
+
+var global = (Function("return this")());
+
+// private names for privileged code should not be exposed.
+if (Object.getOwnPropertySymbols(global).length !== 0)
+    throw "Error: bad value " + Object.getOwnPropertySymbols(global).length;
+
+var object = {};
+var symbol = Symbol("Cocoa");
+object[symbol] = "Cappuccino";
+if (Object.getOwnPropertyNames(object).length !== 0)
+    throw "Error: bad value " + Object.getOwnPropertyNames(object).length;
+if (Object.getOwnPropertySymbols(object).length !== 1)
+    throw "Error: bad value " + Object.getOwnPropertySymbols(object).length;
+if (Object.getOwnPropertySymbols(object)[0] !== symbol)
+    throw "Error: bad value " + String(Object.getOwnPropertySymbols(object)[0]);
+
+function forIn(obj) {
+    var array = [];
+    // Symbol should not be enumerated.
+    for (var key in obj) array.push(key);
+    return array;
+}
+
+if (forIn(object).length !== 0)
+    throw "Error: bad value " + forIn(object).length;
+if (Object.keys(object).length !== 0)
+    throw "Error: bad value " + Object.keys(object).length;
+
+delete object[symbol];
+if (Object.getOwnPropertyNames(object).length !== 0)
+    throw "Error: bad value " + Object.getOwnPropertyNames(object).length;
+if (Object.getOwnPropertySymbols(object).length !== 0)
+    throw "Error: bad value " + Object.getOwnPropertySymbols(object).length;
diff --git a/Source/JavaScriptCore/tests/stress/symbol-define-property.js b/Source/JavaScriptCore/tests/stress/symbol-define-property.js
new file mode 100644
index 0000000..bac3ec0
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/symbol-define-property.js
@@ -0,0 +1,33 @@
+// This tests Object.create, Object.defineProperty, Object.defineProperties work with Symbol.
+
+function testSymbol(object) {
+    if (!object.hasOwnProperty(Symbol.iterator))
+        throw "Error: object doesn't have Symbol.iterator";
+    if (object.propertyIsEnumerable(Symbol.iterator))
+        throw "Error: Symbol.iterator is defined as enumerable";
+    if (JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator)) !== '{"value":42,"writable":false,"enumerable":false,"configurable":false}')
+        throw "Error: bad property descriptor " + JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator));
+    if (Object.getOwnPropertySymbols(object).length !== 1)
+    throw "Error: bad value " + Object.getOwnPropertySymbols(object).length;
+    if (Object.getOwnPropertySymbols(object)[0] !== Symbol.iterator)
+        throw "Error: bad value " + String(Object.getOwnPropertySymbols(object)[0]);
+}
+
+var object = Object.create(Object.prototype, {
+    [Symbol.iterator]: {
+        value: 42
+    }
+});
+testSymbol(object);
+
+var object = Object.defineProperties({}, {
+    [Symbol.iterator]: {
+        value: 42
+    }
+});
+testSymbol(object);
+
+var object = Object.defineProperty({}, Symbol.iterator, {
+    value: 42
+});
+testSymbol(object);
diff --git a/Source/JavaScriptCore/tests/stress/symbol-seal-and-freeze.js b/Source/JavaScriptCore/tests/stress/symbol-seal-and-freeze.js
new file mode 100644
index 0000000..4f507cd
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/symbol-seal-and-freeze.js
@@ -0,0 +1,26 @@
+// This tests Object.seal and Object.freeze affect on Symbol properties.
+
+var object = {
+    [Symbol.iterator]: 42
+};
+
+if (!object.hasOwnProperty(Symbol.iterator))
+    throw "Error: object doesn't have Symbol.iterator";
+if (JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator)) !== '{"value":42,"writable":true,"enumerable":true,"configurable":true}')
+    throw "Error: bad property descriptor " + JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator));
+if (Object.getOwnPropertySymbols(object).length !== 1)
+    throw "Error: bad value " + Object.getOwnPropertySymbols(object).length;
+if (Object.getOwnPropertySymbols(object)[0] !== Symbol.iterator)
+    throw "Error: bad value " + String(Object.getOwnPropertySymbols(object)[0]);
+
+Object.seal(object);
+if (!object.hasOwnProperty(Symbol.iterator))
+    throw "Error: object doesn't have Symbol.iterator";
+if (JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator)) !== '{"value":42,"writable":true,"enumerable":true,"configurable":false}')
+    throw "Error: bad property descriptor " + JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator));
+
+Object.freeze(object);
+if (!object.hasOwnProperty(Symbol.iterator))
+    throw "Error: object doesn't have Symbol.iterator";
+if (JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator)) !== '{"value":42,"writable":false,"enumerable":true,"configurable":false}')
+    throw "Error: bad property descriptor " + JSON.stringify(Object.getOwnPropertyDescriptor(object, Symbol.iterator));
diff --git a/Source/JavaScriptCore/tests/stress/symbol-with-json.js b/Source/JavaScriptCore/tests/stress/symbol-with-json.js
new file mode 100644
index 0000000..3c03d5d
--- /dev/null
+++ b/Source/JavaScriptCore/tests/stress/symbol-with-json.js
@@ -0,0 +1,14 @@
+// This tests JSON correctly behaves with Symbol.
+
+if (JSON.stringify(Symbol('Cocoa')) !== undefined)
+    throw "Error: bad value " + JSON.stringify(Symbol('Cocoa'));
+
+var object = {};
+var symbol = Symbol("Cocoa");
+object[symbol] = 42;
+object['Cappuccino'] = 42;
+if (JSON.stringify(object) !== '{"Cappuccino":42}')
+    throw "Error: bad value " + JSON.stringify(object);
+
+if (JSON.stringify(object, [ Symbol('Cocoa') ]) !== "{}")
+    throw "Error: bad value " + JSON.stringify(object, [ Symbol('Cocoa') ]);