class methods should be non-enumerable
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 25 Apr 2015 22:03:30 +0000 (22:03 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 25 Apr 2015 22:03:30 +0000 (22:03 +0000)
https://bugs.webkit.org/show_bug.cgi?id=143181

Reviewed by Darin Adler.

Source/JavaScriptCore:

Fixed the bug by using Object.defineProperty to define methods.

This patch adds the concept of link time constants and uses it to resolve Object.defineProperty
inside CodeBlock's constructor since bytecode can be linked against multiple global objects.

* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::CodeBlock): Resolve link time constants that are used. Ignore ones with register
index of zero.
* bytecode/SpecialPointer.h: Added a new enum for link time constants. It currently contains
exactly one entry for Object.defineProperty.
* bytecode/UnlinkedCodeBlock.h:
(JSC::UnlinkedCodeBlock::addConstant): Added. Like addConstant that takes JSValue, allocate a new
constant register for the link time constant we're adding.
(JSC::UnlinkedCodeBlock::registerIndexForLinkTimeConstant): Added.
* bytecompiler/BytecodeGenerator.cpp:
(JSC::BytecodeGenerator::emitMoveLinkTimeConstant): Added. Like addConstantValue, allocate a new
register for the specified link time constant and notify UnlinkedCodeBlock about it.
(JSC::BytecodeGenerator::emitCallDefineProperty): Added. Create a new property descriptor and call
Object.defineProperty with it.
* bytecompiler/BytecodeGenerator.h:
* bytecompiler/NodesCodegen.cpp:
(JSC::PropertyListNode::emitBytecode): Make static and non-static getters and setters for classes
non-enumerable by using emitCallDefineProperty to define them.
(JSC::PropertyListNode::emitPutConstantProperty): Ditto for a non-accessor properties.
(JSC::ClassExprNode::emitBytecode): Make prototype.constructor non-enumerable and make prototype
property on the class non-writable, non-configurable, and non-enumerable by using defineProperty.
* runtime/CommonIdentifiers.h:
* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::init): Set m_definePropertyFunction.
(JSC::JSGlobalObject::visitChildren): Visit m_definePropertyFunction.
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::definePropertyFunction): Added.
(JSC::JSGlobalObject::actualPointerFor): Added a variant that takes LinkTimeConstant.
(JSC::JSGlobalObject::jsCellForLinkTimeConstant): Like actualPointerFor, takes LinkTimeConstant and
returns a JSCell; e.g. Object.defineProperty.
* runtime/ObjectConstructor.cpp:
(JSC::ObjectConstructor::addDefineProperty): Added. Returns Object.defineProperty.
* runtime/ObjectConstructor.h:

LayoutTests:

Added a regression test.

Also fixed a test that previously relied on "prototype" property being writable
since this is no longer the case.

* js/class-syntax-extends-expected.txt:
* js/class-syntax-prototype.html: Added.
* js/script-tests/class-syntax-extends.js:
* js/script-tests/class-syntax-prototype.js: Added.

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

19 files changed:
LayoutTests/ChangeLog
LayoutTests/js/class-syntax-extends-expected.txt
LayoutTests/js/class-syntax-prototype-expected.txt [new file with mode: 0644]
LayoutTests/js/class-syntax-prototype.html [new file with mode: 0644]
LayoutTests/js/script-tests/class-syntax-extends.js
LayoutTests/js/script-tests/class-syntax-prototype.js [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/SpecialPointer.h
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.cpp
Source/JavaScriptCore/bytecode/UnlinkedCodeBlock.h
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp
Source/JavaScriptCore/bytecompiler/BytecodeGenerator.h
Source/JavaScriptCore/bytecompiler/NodesCodegen.cpp
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/ObjectConstructor.cpp
Source/JavaScriptCore/runtime/ObjectConstructor.h

index ad1121852c4dc34e0caeeddd75076cd6b200e555..5ea1e2997d01497f69c4de5cb7e507dabe9e2469 100644 (file)
@@ -1,3 +1,20 @@
+2015-04-23  Ryosuke Niwa  <rniwa@webkit.org>
+
+        class methods should be non-enumerable
+        https://bugs.webkit.org/show_bug.cgi?id=143181
+
+        Reviewed by Darin Adler.
+
+        Added a regression test.
+
+        Also fixed a test that previously relied on "prototype" property being writable
+        since this is no longer the case.
+
+        * js/class-syntax-extends-expected.txt:
+        * js/class-syntax-prototype.html: Added.
+        * js/script-tests/class-syntax-extends.js:
+        * js/script-tests/class-syntax-prototype.js: Added.
+
 2015-04-25  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [ES6] Implement String.fromCodePoint
index f802f41d90b94a3543287bb0aa3043e286431bd4..92651e16c7282884ec0860b6865e9b82ae51eea3 100644 (file)
@@ -25,7 +25,7 @@ PASS x = class extends null { constructor() { } }; x.__proto__ is Function.proto
 PASS x.__proto__ is Function.prototype
 PASS x = class extends 3 { constructor() { } }; x.__proto__ threw exception TypeError: The superclass is not an object..
 PASS x = class extends "abc" { constructor() { } }; x.__proto__ threw exception TypeError: The superclass is not an object..
-PASS baseWithBadPrototype = class { constructor() { } }; baseWithBadPrototype.prototype = 3 did not throw exception.
+PASS baseWithBadPrototype = function () {}; baseWithBadPrototype.prototype = 3; new baseWithBadPrototype did not throw exception.
 PASS x = class extends baseWithBadPrototype { constructor() { } } threw exception TypeError: The superclass's prototype is not an object..
 PASS baseWithBadPrototype.prototype = "abc" did not throw exception.
 PASS x = class extends baseWithBadPrototype { constructor() { } } threw exception TypeError: The superclass's prototype is not an object..
diff --git a/LayoutTests/js/class-syntax-prototype-expected.txt b/LayoutTests/js/class-syntax-prototype-expected.txt
new file mode 100644 (file)
index 0000000..15d6ef2
--- /dev/null
@@ -0,0 +1,51 @@
+Tests for the descriptors of the properties implicitly defined by ES6 class syntax
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS class A {}; descriptor(A, "prototype").writable is false
+PASS class A {}; var x = A.prototype; A.prototype = 3; A.prototype is x
+PASS class A {}; descriptor(A, "prototype").enumerable is false
+PASS class A {}; A.foo = "foo"; enumeratedProperties(A).includes("foo") is true
+PASS class A {}; enumeratedProperties(A).includes("prototype") is false
+PASS class A {}; descriptor(A, "prototype").configurable is false
+PASS class A {}; Object.defineProperty(A, "prototype", {value: "foo"}) threw exception TypeError: Attempting to change value of a readonly property..
+PASS class A { static foo() {} }; descriptor(A, "foo").writable is true
+PASS class A { static foo() {} }; A.foo = 3; A.foo is 3
+PASS class A { static foo() {} }; descriptor(A, "foo").enumerable is false
+PASS class A { static foo() {} }; enumeratedProperties(A).includes("foo") is false
+PASS class A { static foo() {} }; descriptor(A, "foo").configurable is true
+PASS class A { static foo() {} }; Object.defineProperty(A, "foo", {value: "bar"}); A.foo is "bar"
+PASS class A { static get foo() {} }; descriptor(A, "foo").writable is undefined
+PASS class A { static get foo() { return 5; } }; A.foo = 3; A.foo is 5
+PASS class A { static get foo() {} }; descriptor(A, "foo").enumerable is false
+PASS class A { static get foo() {} }; enumeratedProperties(A).includes("foo") is false
+PASS class A { static get foo() {} }; enumeratedProperties(new A).includes("foo") is false
+PASS class A { static get foo() {} }; descriptor(A, "foo").configurable is true
+PASS class A { static get foo() {} }; Object.defineProperty(A, "foo", {value: "bar"}); A.foo is "bar"
+PASS class A { foo() {} }; descriptor(A.prototype, "foo").writable is true
+PASS class A { foo() {} }; A.prototype.foo = 3; A.prototype.foo is 3
+PASS class A { foo() {} }; descriptor(A.prototype, "foo").enumerable is false
+PASS class A { foo() {} }; enumeratedProperties(A.prototype).includes("foo") is false
+PASS class A { foo() {} }; enumeratedProperties(new A).includes("foo") is false
+PASS class A { foo() {} }; descriptor(A.prototype, "foo").configurable is true
+PASS class A { foo() {} }; Object.defineProperty(A.prototype, "foo", {value: "bar"}); A.prototype.foo is "bar"
+PASS class A { get foo() {} }; descriptor(A.prototype, "foo").writable is undefined
+PASS class A { get foo() { return 5; } }; A.prototype.foo = 3; A.prototype.foo is 5
+PASS class A { get foo() {} }; descriptor(A.prototype, "foo").enumerable is false
+PASS class A { get foo() {} }; enumeratedProperties(A.prototype).includes("foo") is false
+PASS class A { get foo() {} }; enumeratedProperties(new A).includes("foo") is false
+PASS class A { get foo() {} }; descriptor(A.prototype, "foo").configurable is true
+PASS class A { get foo() {} }; Object.defineProperty(A.prototype, "foo", {value: "bar"}); A.prototype.foo is "bar"
+PASS class A { }; descriptor(A.prototype, "constructor").writable is true
+PASS class A { }; A.prototype.constructor = 3; A.prototype.constructor is 3
+PASS class A { }; x = {}; A.prototype.constructor = function () { return x; }; (new A) instanceof A is true
+PASS class A { }; descriptor(A.prototype, "constructor").enumerable is false
+PASS class A { }; enumeratedProperties(A.prototype).includes("constructor") is false
+PASS class A { }; enumeratedProperties(new A).includes("constructor") is false
+PASS class A { }; descriptor(A.prototype, "constructor").configurable is true
+PASS class A { }; Object.defineProperty(A.prototype, "constructor", {value: "bar"}); A.prototype.constructor is "bar"
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/class-syntax-prototype.html b/LayoutTests/js/class-syntax-prototype.html
new file mode 100644 (file)
index 0000000..e149d56
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../resources/js-test-pre.js"></script>
+<script src="script-tests/class-syntax-prototype.js"></script>
+<script src="../resources/js-test-post.js"></script>
+</body>
+</html>
index 308df2acb228f9f319737962d4ef56597911c743..ecc1177fed401e739f0c53bfa355d95efd44ee2f 100644 (file)
@@ -38,7 +38,7 @@ shouldBe('x = class extends null { constructor() { } }; x.__proto__', 'Function.
 shouldBe('x.__proto__', 'Function.prototype');
 shouldThrow('x = class extends 3 { constructor() { } }; x.__proto__', '"TypeError: The superclass is not an object."');
 shouldThrow('x = class extends "abc" { constructor() { } }; x.__proto__', '"TypeError: The superclass is not an object."');
-shouldNotThrow('baseWithBadPrototype = class { constructor() { } }; baseWithBadPrototype.prototype = 3');
+shouldNotThrow('baseWithBadPrototype = function () {}; baseWithBadPrototype.prototype = 3; new baseWithBadPrototype');
 shouldThrow('x = class extends baseWithBadPrototype { constructor() { } }', '"TypeError: The superclass\'s prototype is not an object."');
 shouldNotThrow('baseWithBadPrototype.prototype = "abc"');
 shouldThrow('x = class extends baseWithBadPrototype { constructor() { } }', '"TypeError: The superclass\'s prototype is not an object."');
diff --git a/LayoutTests/js/script-tests/class-syntax-prototype.js b/LayoutTests/js/script-tests/class-syntax-prototype.js
new file mode 100644 (file)
index 0000000..6d8d6ff
--- /dev/null
@@ -0,0 +1,62 @@
+description('Tests for the descriptors of the properties implicitly defined by ES6 class syntax');
+
+function descriptor(object, propertyName) {
+    return Object.getOwnPropertyDescriptor(object, propertyName);
+}
+
+function enumeratedProperties(object) {
+    var properties = [];
+    for (var propertyName in object)
+        properties.push(propertyName);
+    return properties;
+}
+
+shouldBeFalse('class A {}; descriptor(A, "prototype").writable');
+shouldBe('class A {}; var x = A.prototype; A.prototype = 3; A.prototype', 'x');
+shouldBeFalse('class A {}; descriptor(A, "prototype").enumerable');
+shouldBeTrue('class A {}; A.foo = "foo"; enumeratedProperties(A).includes("foo")');
+shouldBeFalse('class A {}; enumeratedProperties(A).includes("prototype")');
+shouldBeFalse('class A {}; descriptor(A, "prototype").configurable');
+shouldThrow('class A {}; Object.defineProperty(A, "prototype", {value: "foo"})', '"TypeError: Attempting to change value of a readonly property."');
+
+shouldBeTrue('class A { static foo() {} }; descriptor(A, "foo").writable');
+shouldBe('class A { static foo() {} }; A.foo = 3; A.foo', '3');
+shouldBeFalse('class A { static foo() {} }; descriptor(A, "foo").enumerable');
+shouldBeFalse('class A { static foo() {} }; enumeratedProperties(A).includes("foo")');
+shouldBeTrue('class A { static foo() {} }; descriptor(A, "foo").configurable');
+shouldBe('class A { static foo() {} }; Object.defineProperty(A, "foo", {value: "bar"}); A.foo', '"bar"');
+
+shouldBe('class A { static get foo() {} }; descriptor(A, "foo").writable', 'undefined');
+shouldBe('class A { static get foo() { return 5; } }; A.foo = 3; A.foo', '5');
+shouldBeFalse('class A { static get foo() {} }; descriptor(A, "foo").enumerable');
+shouldBeFalse('class A { static get foo() {} }; enumeratedProperties(A).includes("foo")');
+shouldBeFalse('class A { static get foo() {} }; enumeratedProperties(new A).includes("foo")');
+shouldBeTrue('class A { static get foo() {} }; descriptor(A, "foo").configurable');
+shouldBe('class A { static get foo() {} }; Object.defineProperty(A, "foo", {value: "bar"}); A.foo', '"bar"');
+
+shouldBeTrue('class A { foo() {} }; descriptor(A.prototype, "foo").writable');
+shouldBe('class A { foo() {} }; A.prototype.foo = 3; A.prototype.foo', '3');
+shouldBeFalse('class A { foo() {} }; descriptor(A.prototype, "foo").enumerable');
+shouldBeFalse('class A { foo() {} }; enumeratedProperties(A.prototype).includes("foo")');
+shouldBeFalse('class A { foo() {} }; enumeratedProperties(new A).includes("foo")');
+shouldBeTrue('class A { foo() {} }; descriptor(A.prototype, "foo").configurable');
+shouldBe('class A { foo() {} }; Object.defineProperty(A.prototype, "foo", {value: "bar"}); A.prototype.foo', '"bar"');
+
+shouldBe('class A { get foo() {} }; descriptor(A.prototype, "foo").writable', 'undefined');
+shouldBe('class A { get foo() { return 5; } }; A.prototype.foo = 3; A.prototype.foo', '5');
+shouldBeFalse('class A { get foo() {} }; descriptor(A.prototype, "foo").enumerable');
+shouldBeFalse('class A { get foo() {} }; enumeratedProperties(A.prototype).includes("foo")');
+shouldBeFalse('class A { get foo() {} }; enumeratedProperties(new A).includes("foo")');
+shouldBeTrue('class A { get foo() {} }; descriptor(A.prototype, "foo").configurable');
+shouldBe('class A { get foo() {} }; Object.defineProperty(A.prototype, "foo", {value: "bar"}); A.prototype.foo', '"bar"');
+
+shouldBeTrue('class A { }; descriptor(A.prototype, "constructor").writable');
+shouldBe('class A { }; A.prototype.constructor = 3; A.prototype.constructor', '3');
+shouldBeTrue('class A { }; x = {}; A.prototype.constructor = function () { return x; }; (new A) instanceof A');
+shouldBeFalse('class A { }; descriptor(A.prototype, "constructor").enumerable');
+shouldBeFalse('class A { }; enumeratedProperties(A.prototype).includes("constructor")');
+shouldBeFalse('class A { }; enumeratedProperties(new A).includes("constructor")');
+shouldBeTrue('class A { }; descriptor(A.prototype, "constructor").configurable');
+shouldBe('class A { }; Object.defineProperty(A.prototype, "constructor", {value: "bar"}); A.prototype.constructor', '"bar"');
+
+var successfullyParsed = true;
index 0276edbd2528ee59117e0a446ae2ee354bbb99f8..f07cd1d240232bcc2456a76d4ebbf81319c517ef 100644 (file)
@@ -1,3 +1,49 @@
+2015-04-23  Ryosuke Niwa  <rniwa@webkit.org>
+
+        class methods should be non-enumerable
+        https://bugs.webkit.org/show_bug.cgi?id=143181
+
+        Reviewed by Darin Adler.
+
+        Fixed the bug by using Object.defineProperty to define methods.
+
+        This patch adds the concept of link time constants and uses it to resolve Object.defineProperty
+        inside CodeBlock's constructor since bytecode can be linked against multiple global objects.
+
+        * bytecode/CodeBlock.cpp: 
+        (JSC::CodeBlock::CodeBlock): Resolve link time constants that are used. Ignore ones with register
+        index of zero.
+        * bytecode/SpecialPointer.h: Added a new enum for link time constants. It currently contains
+        exactly one entry for Object.defineProperty.
+        * bytecode/UnlinkedCodeBlock.h:
+        (JSC::UnlinkedCodeBlock::addConstant): Added. Like addConstant that takes JSValue, allocate a new
+        constant register for the link time constant we're adding.
+        (JSC::UnlinkedCodeBlock::registerIndexForLinkTimeConstant): Added.
+        * bytecompiler/BytecodeGenerator.cpp:
+        (JSC::BytecodeGenerator::emitMoveLinkTimeConstant): Added. Like addConstantValue, allocate a new
+        register for the specified link time constant and notify UnlinkedCodeBlock about it.
+        (JSC::BytecodeGenerator::emitCallDefineProperty): Added. Create a new property descriptor and call
+        Object.defineProperty with it.
+        * bytecompiler/BytecodeGenerator.h:
+        * bytecompiler/NodesCodegen.cpp:
+        (JSC::PropertyListNode::emitBytecode): Make static and non-static getters and setters for classes
+        non-enumerable by using emitCallDefineProperty to define them.
+        (JSC::PropertyListNode::emitPutConstantProperty): Ditto for a non-accessor properties.
+        (JSC::ClassExprNode::emitBytecode): Make prototype.constructor non-enumerable and make prototype
+        property on the class non-writable, non-configurable, and non-enumerable by using defineProperty.
+        * runtime/CommonIdentifiers.h:
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::init): Set m_definePropertyFunction.
+        (JSC::JSGlobalObject::visitChildren): Visit m_definePropertyFunction.
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::definePropertyFunction): Added.
+        (JSC::JSGlobalObject::actualPointerFor): Added a variant that takes LinkTimeConstant.
+        (JSC::JSGlobalObject::jsCellForLinkTimeConstant): Like actualPointerFor, takes LinkTimeConstant and
+        returns a JSCell; e.g. Object.defineProperty.
+        * runtime/ObjectConstructor.cpp:
+        (JSC::ObjectConstructor::addDefineProperty): Added. Returns Object.defineProperty.
+        * runtime/ObjectConstructor.h:
+
 2015-04-25  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         [ES6] Implement String.fromCodePoint
index 27d33163cfa596b035990b477b409bd727faf7db..22f004ef6c6ef7f3154ccf7a587d94f39eef61b3 100644 (file)
@@ -1761,6 +1761,13 @@ CodeBlock::CodeBlock(ScriptExecutable* ownerExecutable, UnlinkedCodeBlock* unlin
     setConstantRegisters(unlinkedCodeBlock->constantRegisters(), unlinkedCodeBlock->constantsSourceCodeRepresentation());
     if (unlinkedCodeBlock->usesGlobalObject())
         m_constantRegisters[unlinkedCodeBlock->globalObjectRegister().toConstantIndex()].set(*m_vm, ownerExecutable, m_globalObject.get());
+
+    for (unsigned i = 0; i < LinkTimeConstantCount; i++) {
+        LinkTimeConstant type = static_cast<LinkTimeConstant>(i);
+        if (unsigned registerIndex = unlinkedCodeBlock->registerIndexForLinkTimeConstant(type))
+            m_constantRegisters[registerIndex].set(*m_vm, ownerExecutable, m_globalObject->jsCellForLinkTimeConstant(type));
+    }
+
     m_functionDecls.resizeToFit(unlinkedCodeBlock->numberOfFunctionDecls());
     for (size_t count = unlinkedCodeBlock->numberOfFunctionDecls(), i = 0; i < count; ++i) {
         UnlinkedFunctionExecutable* unlinkedExecutable = unlinkedCodeBlock->functionDecl(i);
index c18a6e904d8eebda71a63fe31f36af55f302b075..64fb23fcf27d41250480219e8c8c41b7c41a7ef5 100644 (file)
@@ -41,6 +41,11 @@ enum Pointer {
 };
 } // namespace Special
 
+enum class LinkTimeConstant {
+    DefinePropertyFunction,
+};
+const unsigned LinkTimeConstantCount = 1;
+
 inline bool pointerIsFunction(Special::Pointer pointer)
 {
     ASSERT_UNUSED(pointer, pointer < Special::TableSize);
index 35e0f9082c2bdea1cc4bfe1c3cf567b10ff312a8..efd77cac23814ab324926cc369f7d40c612805d8 100644 (file)
@@ -261,6 +261,8 @@ UnlinkedCodeBlock::UnlinkedCodeBlock(VM* vm, Structure* structure, CodeType code
     , m_bytecodeCommentIterator(0)
 #endif
 {
+    for (auto& constantRegisterIndex : m_linkTimeConstants)
+        constantRegisterIndex = 0;
     ASSERT(m_constructorKind == static_cast<unsigned>(info.constructorKind()));
 }
 
index 1dec5fb780c565eca0cd8b2128965c0c1ab80f75..bedaac6e4542f068e66660b7e412f714de751b58 100644 (file)
@@ -331,6 +331,23 @@ public:
         m_constantsSourceCodeRepresentation.append(sourceCodeRepresentation);
         return result;
     }
+    unsigned addConstant(LinkTimeConstant type)
+    {
+        unsigned result = m_constantRegisters.size();
+        ASSERT(result);
+        unsigned index = static_cast<unsigned>(type);
+        ASSERT(index < LinkTimeConstantCount);
+        m_linkTimeConstants[index] = result;
+        m_constantRegisters.append(WriteBarrier<Unknown>());
+        m_constantsSourceCodeRepresentation.append(SourceCodeRepresentation::Other);
+        return result;
+    }
+    unsigned registerIndexForLinkTimeConstant(LinkTimeConstant type)
+    {
+        unsigned index = static_cast<unsigned>(type);
+        ASSERT(index < LinkTimeConstantCount);
+        return m_linkTimeConstants[index];
+    }
     unsigned addOrFindConstant(JSValue);
     const Vector<WriteBarrier<Unknown>>& constantRegisters() { return m_constantRegisters; }
     const WriteBarrier<Unknown>& constantRegister(int index) const { return m_constantRegisters[index - FirstConstantRegisterIndex]; }
@@ -556,6 +573,7 @@ private:
     Vector<Identifier> m_identifiers;
     Vector<WriteBarrier<Unknown>> m_constantRegisters;
     Vector<SourceCodeRepresentation> m_constantsSourceCodeRepresentation;
+    std::array<unsigned, LinkTimeConstantCount> m_linkTimeConstants;
     typedef Vector<WriteBarrier<UnlinkedFunctionExecutable>> FunctionExpressionVector;
     FunctionExpressionVector m_functionDecls;
     FunctionExpressionVector m_functionExprs;
index 4c7ce2d95e562006c936f77060a8e3ac84929c1c..d472b2ec3144ad4fdf67ab1a4b6023d9c4bd808a 100644 (file)
@@ -149,6 +149,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, ProgramNode* programNode, UnlinkedP
     , m_codeType(GlobalCode)
     , m_vm(&vm)
 {
+    for (auto& constantRegister : m_linkTimeConstantRegisters)
+        constantRegister = nullptr;
+
     m_codeBlock->setNumParameters(1); // Allocate space for "this"
 
     emitOpcode(op_enter);
@@ -179,6 +182,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, FunctionNode* functionNode, Unlinke
     , m_vm(&vm)
     , m_isBuiltinFunction(codeBlock->isBuiltinFunction())
 {
+    for (auto& constantRegister : m_linkTimeConstantRegisters)
+        constantRegister = nullptr;
+
     if (m_isBuiltinFunction)
         m_shouldEmitDebugHooks = false;
     
@@ -494,6 +500,9 @@ BytecodeGenerator::BytecodeGenerator(VM& vm, EvalNode* evalNode, UnlinkedEvalCod
     , m_codeType(EvalCode)
     , m_vm(&vm)
 {
+    for (auto& constantRegister : m_linkTimeConstantRegisters)
+        constantRegister = nullptr;
+
     m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode());
     m_codeBlock->setNumParameters(1);
 
@@ -984,6 +993,24 @@ RegisterID* BytecodeGenerator::addConstantValue(JSValue v, SourceCodeRepresentat
     return &m_constantPoolRegisters[index];
 }
 
+RegisterID* BytecodeGenerator::emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant type)
+{
+    unsigned constantIndex = static_cast<unsigned>(type);
+    if (!m_linkTimeConstantRegisters[constantIndex]) {
+        int index = m_nextConstantOffset;
+        m_constantPoolRegisters.append(FirstConstantRegisterIndex + m_nextConstantOffset);
+        ++m_nextConstantOffset;
+        m_codeBlock->addConstant(type);
+        m_linkTimeConstantRegisters[constantIndex] = &m_constantPoolRegisters[index];
+    }
+
+    emitOpcode(op_mov);
+    instructions().append(dst->index());
+    instructions().append(m_linkTimeConstantRegisters[constantIndex]->index());
+
+    return dst;
+}
+
 unsigned BytecodeGenerator::addRegExp(RegExp* r)
 {
     return m_codeBlock->addRegExp(r);
@@ -1950,6 +1977,41 @@ RegisterID* BytecodeGenerator::emitCallVarargs(OpcodeID opcode, RegisterID* dst,
     return dst;
 }
 
+void BytecodeGenerator::emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
+    RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition& position)
+{
+    RefPtr<RegisterID> descriptorRegister = emitNewObject(newTemporary());
+
+    RefPtr<RegisterID> trueRegister = emitLoad(newTemporary(), true);
+    if (options & PropertyConfigurable)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().configurable, trueRegister.get(), PropertyNode::Unknown);
+    if (options & PropertyWritable)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().writable, trueRegister.get(), PropertyNode::Unknown);
+    else if (valueRegister) {
+        RefPtr<RegisterID> falseRegister = emitLoad(newTemporary(), false);
+        emitDirectPutById(descriptorRegister.get(), propertyNames().writable, falseRegister.get(), PropertyNode::Unknown);
+    }
+    if (options & PropertyEnumerable)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().enumerable, trueRegister.get(), PropertyNode::Unknown);
+
+    if (valueRegister)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().value, valueRegister, PropertyNode::Unknown);
+    if (getterRegister)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().get, getterRegister, PropertyNode::Unknown);
+    if (setterRegister)
+        emitDirectPutById(descriptorRegister.get(), propertyNames().set, setterRegister, PropertyNode::Unknown);
+
+    RefPtr<RegisterID> definePropertyRegister = emitMoveLinkTimeConstant(newTemporary(), LinkTimeConstant::DefinePropertyFunction);
+
+    CallArguments callArguments(*this, nullptr, 3);
+    emitLoad(callArguments.thisRegister(), jsUndefined());
+    emitMove(callArguments.argumentRegister(0), newObj);
+    emitMove(callArguments.argumentRegister(1), propertyNameRegister);
+    emitMove(callArguments.argumentRegister(2), descriptorRegister.get());
+
+    emitCall(newTemporary(), definePropertyRegister.get(), NoExpectedFunction, callArguments, position, position, position);
+}
+
 RegisterID* BytecodeGenerator::emitReturn(RegisterID* src)
 {
     if (isConstructor()) {
index 2e59039d3a484a347281440eef9c1b819f7265db..95002afd7aa3303267917824b80494f7cc981840 100644 (file)
@@ -458,6 +458,7 @@ namespace JSC {
         RegisterID* emitNewDefaultConstructor(RegisterID* dst, ConstructorKind, const Identifier& name);
         RegisterID* emitNewRegExp(RegisterID* dst, RegExp*);
 
+        RegisterID* emitMoveLinkTimeConstant(RegisterID* dst, LinkTimeConstant);
         RegisterID* emitMoveEmptyValue(RegisterID* dst);
         RegisterID* emitMove(RegisterID* dst, RegisterID* src);
 
@@ -489,6 +490,14 @@ namespace JSC {
         RegisterID* emitCallEval(RegisterID* dst, RegisterID* func, CallArguments&, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
         RegisterID* emitCallVarargs(RegisterID* dst, RegisterID* func, RegisterID* thisRegister, RegisterID* arguments, RegisterID* firstFreeRegister, int32_t firstVarArgOffset, RegisterID* profileHookRegister, const JSTextPosition& divot, const JSTextPosition& divotStart, const JSTextPosition& divotEnd);
 
+        enum PropertyDescriptorOption {
+            PropertyConfigurable = 1,
+            PropertyWritable     = 1 << 1,
+            PropertyEnumerable   = 1 << 2,
+        };
+        void emitCallDefineProperty(RegisterID* newObj, RegisterID* propertyNameRegister,
+            RegisterID* valueRegister, RegisterID* getterRegister, RegisterID* setterRegister, unsigned options, const JSTextPosition&);
+
         void emitEnumeration(ThrowableExpressionData* enumerationNode, ExpressionNode* subjectNode, const std::function<void(BytecodeGenerator&, RegisterID*)>& callBack);
         
         RegisterID* emitReturn(RegisterID* src);
@@ -720,6 +729,7 @@ namespace JSC {
         RegisterID* m_emptyValueRegister { nullptr };
         RegisterID* m_globalObjectRegister { nullptr };
         RegisterID* m_newTargetRegister { nullptr };
+        RegisterID* m_linkTimeConstantRegisters[LinkTimeConstantCount];
 
         SegmentedVector<RegisterID, 32> m_constantPoolRegisters;
         SegmentedVector<RegisterID, 32> m_calleeRegisters;
index 99c0ed1068990e0d88ee59c176f0e0494e5ce2c4..64c3e9a87b00be3df935dbbd73bc953aa293167c 100644 (file)
@@ -362,7 +362,8 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
             }
 
             RegisterID* value = generator.emitNode(node->m_assign);
-            if (node->needsSuperBinding())
+            bool isClassProperty = node->needsSuperBinding();
+            if (isClassProperty)
                 emitPutHomeObject(generator, value, dst);
 
             // This is a get/set property, find its entry in the map.
@@ -403,10 +404,16 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
                 }
             }
 
-            if (pair.second && pair.second->needsSuperBinding())
+            ASSERT(!pair.second || isClassProperty == pair.second->needsSuperBinding());
+            if (isClassProperty && pair.second)
                 emitPutHomeObject(generator, secondReg, dst);
 
-            generator.emitPutGetterSetter(dst, *node->name(), getterReg.get(), setterReg.get());
+            if (isClassProperty) {
+                RefPtr<RegisterID> propertyNameRegister = generator.emitLoad(generator.newTemporary(), *node->name());
+                generator.emitCallDefineProperty(dst, propertyNameRegister.get(),
+                    nullptr, getterReg.get(), setterReg.get(), BytecodeGenerator::PropertyConfigurable, m_position);
+            } else
+                generator.emitPutGetterSetter(dst, *node->name(), getterReg.get(), setterReg.get());
         }
     }
 
@@ -416,8 +423,19 @@ RegisterID* PropertyListNode::emitBytecode(BytecodeGenerator& generator, Registe
 void PropertyListNode::emitPutConstantProperty(BytecodeGenerator& generator, RegisterID* newObj, PropertyNode& node)
 {
     RefPtr<RegisterID> value = generator.emitNode(node.m_assign);
-    if (node.needsSuperBinding())
+    if (node.needsSuperBinding()) {
         emitPutHomeObject(generator, value.get(), newObj);
+
+        RefPtr<RegisterID> propertyNameRegister;
+        if (node.name())
+            propertyNameRegister = generator.emitLoad(generator.newTemporary(), *node.name());
+        else
+            propertyNameRegister = generator.emitNode(node.m_expression);
+
+        generator.emitCallDefineProperty(newObj, propertyNameRegister.get(),
+            value.get(), nullptr, nullptr, BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable, m_position);
+        return;
+    }
     if (node.name()) {
         generator.emitDirectPutById(newObj, *node.name(), value.get(), node.putType());
         return;
@@ -2871,7 +2889,6 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
     }
 
     RefPtr<RegisterID> constructor;
-    RefPtr<RegisterID> prototype;
 
     // FIXME: Make the prototype non-configurable & non-writable.
     if (m_constructorExpression)
@@ -2881,7 +2898,8 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
             m_classHeritage ? ConstructorKind::Derived : ConstructorKind::Base, m_name);
     }
 
-    prototype = generator.emitGetById(generator.newTemporary(), constructor.get(), generator.propertyNames().prototype);
+    const auto& propertyNames = generator.propertyNames();
+    RefPtr<RegisterID> prototype = generator.emitNewObject(generator.newTemporary());
 
     if (superclass) {
         RefPtr<RegisterID> protoParent = generator.newTemporary();
@@ -2910,6 +2928,13 @@ RegisterID* ClassExprNode::emitBytecode(BytecodeGenerator& generator, RegisterID
         emitPutHomeObject(generator, constructor.get(), prototype.get());
     }
 
+    RefPtr<RegisterID> constructorNameRegister = generator.emitLoad(generator.newTemporary(), propertyNames.constructor);
+    generator.emitCallDefineProperty(prototype.get(), constructorNameRegister.get(), constructor.get(), nullptr, nullptr,
+        BytecodeGenerator::PropertyConfigurable | BytecodeGenerator::PropertyWritable, m_position);
+
+    RefPtr<RegisterID> prototypeNameRegister = generator.emitLoad(generator.newTemporary(), propertyNames.prototype);
+    generator.emitCallDefineProperty(constructor.get(), prototypeNameRegister.get(), prototype.get(), nullptr, nullptr, 0, m_position);
+
     if (m_staticMethods)
         generator.emitNode(constructor.get(), m_staticMethods);
 
index d16c3f63b358c6da739103d095493c2d3046f5d6..aa02f6da09de4a6e075129b3f5e5b96404bf428e 100644 (file)
@@ -91,6 +91,7 @@
     macro(constructor) \
     macro(count) \
     macro(counters) \
+    macro(defineProperty) \
     macro(description) \
     macro(descriptions) \
     macro(displayName) \
index 0ba0244ba9d74192a24e24661777ad4eb5c5a8ec..9e0f1a6a9d7a586a2afa5fd999504b7ddee7216e 100644 (file)
@@ -332,6 +332,10 @@ m_ ## properName ## Structure.set(vm, this, instanceType::createStructure(vm, th
     
     ObjectConstructor* objectConstructor = ObjectConstructor::create(vm, this, ObjectConstructor::createStructure(vm, this, m_functionPrototype.get()), m_objectPrototype.get());
     m_objectConstructor.set(vm, this, objectConstructor);
+
+    JSFunction* definePropertyFunction = m_objectConstructor->addDefineProperty(exec, this);
+    m_definePropertyFunction.set(vm, this, definePropertyFunction);
+
     JSCell* functionConstructor = FunctionConstructor::create(vm, FunctionConstructor::createStructure(vm, this, m_functionPrototype.get()), m_functionPrototype.get());
     JSCell* arrayConstructor = ArrayConstructor::create(vm, ArrayConstructor::createStructure(vm, this, m_functionPrototype.get()), m_arrayPrototype.get());
     
@@ -458,7 +462,9 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
     m_specialPointers[Special::ApplyFunction] = m_applyFunction.get();
     m_specialPointers[Special::ObjectConstructor] = objectConstructor;
     m_specialPointers[Special::ArrayConstructor] = arrayConstructor;
-    
+
+    m_linkTimeConstants[static_cast<unsigned>(LinkTimeConstant::DefinePropertyFunction)] = m_definePropertyFunction.get();
+
     ConsolePrototype* consolePrototype = ConsolePrototype::create(vm, this, ConsolePrototype::createStructure(vm, this, m_objectPrototype.get()));
     m_consoleStructure.set(vm, this, JSConsole::createStructure(vm, this, consolePrototype));
     JSConsole* consoleObject = JSConsole::create(vm, m_consoleStructure.get());
@@ -717,6 +723,7 @@ void JSGlobalObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
     visitor.append(&thisObject->m_evalFunction);
     visitor.append(&thisObject->m_callFunction);
     visitor.append(&thisObject->m_applyFunction);
+    visitor.append(&thisObject->m_definePropertyFunction);
     visitor.append(&thisObject->m_arrayProtoValuesFunction);
     visitor.append(&thisObject->m_throwTypeErrorGetterSetter);
 
index c4c0bd0017d1871998a850d016cae062120972bc..e5f2e6a8362242bb27cfd92e497469abc21b5d90 100644 (file)
@@ -191,6 +191,7 @@ protected:
     WriteBarrier<JSFunction> m_evalFunction;
     WriteBarrier<JSFunction> m_callFunction;
     WriteBarrier<JSFunction> m_applyFunction;
+    WriteBarrier<JSFunction> m_definePropertyFunction;
     WriteBarrier<JSFunction> m_arrayProtoValuesFunction;
     WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
 
@@ -257,8 +258,9 @@ protected:
     };
     
     std::array<TypedArrayData, NUMBER_OF_TYPED_ARRAY_TYPES> m_typedArrays;
-        
-    void* m_specialPointers[Special::TableSize]; // Special pointers used by the LLInt and JIT.
+
+    JSCell* m_specialPointers[Special::TableSize]; // Special pointers used by the LLInt and JIT.
+    JSCell* m_linkTimeConstants[LinkTimeConstantCount];
 
     String m_name;
 
@@ -400,6 +402,7 @@ public:
     JSFunction* evalFunction() const { return m_evalFunction.get(); }
     JSFunction* callFunction() const { return m_callFunction.get(); }
     JSFunction* applyFunction() const { return m_applyFunction.get(); }
+    JSFunction* definePropertyFunction() const { return m_definePropertyFunction.get(); }
     JSFunction* arrayProtoValuesFunction() const { return m_arrayProtoValuesFunction.get(); }
     GetterSetter* throwTypeErrorGetterSetter(VM& vm)
     {
@@ -523,11 +526,17 @@ public:
         return typedArrayStructure(type) == structure;
     }
 
-    void* actualPointerFor(Special::Pointer pointer)
+    JSCell* actualPointerFor(Special::Pointer pointer)
     {
         ASSERT(pointer < Special::TableSize);
         return m_specialPointers[pointer];
     }
+    JSCell* jsCellForLinkTimeConstant(LinkTimeConstant type)
+    {
+        unsigned index = static_cast<unsigned>(type);
+        ASSERT(index < LinkTimeConstantCount);
+        return m_linkTimeConstants[index];
+    }
 
     WatchpointSet* masqueradesAsUndefinedWatchpoint() { return m_masqueradesAsUndefinedWatchpoint.get(); }
     WatchpointSet* havingABadTimeWatchpoint() { return m_havingABadTimeWatchpoint.get(); }
index 1245e1ca6fd330c7598ab5a7c570eef3009f8c0b..618dac36d2ff753ccd9b477fdf7bb28dfb2d8fe2 100644 (file)
@@ -98,6 +98,14 @@ void ObjectConstructor::finishCreation(VM& vm, JSGlobalObject* globalObject, Obj
         JSC_NATIVE_FUNCTION("getOwnPropertySymbols", objectConstructorGetOwnPropertySymbols, DontEnum, 1);
 }
 
+JSFunction* ObjectConstructor::addDefineProperty(ExecState* exec, JSGlobalObject* globalObject)
+{
+    VM& vm = exec->vm();
+    JSFunction* definePropertyFunction = JSFunction::create(vm, globalObject, 3, vm.propertyNames->defineProperty.string(), objectConstructorDefineProperty);
+    putDirectWithoutTransition(vm, vm.propertyNames->defineProperty, definePropertyFunction, DontEnum);
+    return definePropertyFunction;
+}
+
 bool ObjectConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot &slot)
 {
     return getStaticFunctionSlot<JSObject>(exec, objectConstructorTable, jsCast<ObjectConstructor*>(object), propertyName, slot);
index 1435f0bf85f100631a9fbae62c340806244497b6..53fc0e928760e11faff092a144f549cc1d500da9 100644 (file)
@@ -54,6 +54,8 @@ public:
         return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
     }
 
+    JSFunction* addDefineProperty(ExecState*, JSGlobalObject*);
+
 protected:
     void finishCreation(VM&, JSGlobalObject*, ObjectPrototype*);