+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
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..
--- /dev/null
+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
+
--- /dev/null
+<!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>
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."');
--- /dev/null
+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;
+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
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);
};
} // namespace Special
+enum class LinkTimeConstant {
+ DefinePropertyFunction,
+};
+const unsigned LinkTimeConstantCount = 1;
+
inline bool pointerIsFunction(Special::Pointer pointer)
{
ASSERT_UNUSED(pointer, pointer < Special::TableSize);
, m_bytecodeCommentIterator(0)
#endif
{
+ for (auto& constantRegisterIndex : m_linkTimeConstants)
+ constantRegisterIndex = 0;
ASSERT(m_constructorKind == static_cast<unsigned>(info.constructorKind()));
}
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]; }
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;
, m_codeType(GlobalCode)
, m_vm(&vm)
{
+ for (auto& constantRegister : m_linkTimeConstantRegisters)
+ constantRegister = nullptr;
+
m_codeBlock->setNumParameters(1); // Allocate space for "this"
emitOpcode(op_enter);
, m_vm(&vm)
, m_isBuiltinFunction(codeBlock->isBuiltinFunction())
{
+ for (auto& constantRegister : m_linkTimeConstantRegisters)
+ constantRegister = nullptr;
+
if (m_isBuiltinFunction)
m_shouldEmitDebugHooks = false;
, m_codeType(EvalCode)
, m_vm(&vm)
{
+ for (auto& constantRegister : m_linkTimeConstantRegisters)
+ constantRegister = nullptr;
+
m_symbolTable->setUsesNonStrictEval(codeBlock->usesEval() && !codeBlock->isStrictMode());
m_codeBlock->setNumParameters(1);
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);
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()) {
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);
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);
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;
}
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.
}
}
- 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());
}
}
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;
}
RefPtr<RegisterID> constructor;
- RefPtr<RegisterID> prototype;
// FIXME: Make the prototype non-configurable & non-writable.
if (m_constructorExpression)
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();
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);
macro(constructor) \
macro(count) \
macro(counters) \
+ macro(defineProperty) \
macro(description) \
macro(descriptions) \
macro(displayName) \
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());
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());
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);
WriteBarrier<JSFunction> m_evalFunction;
WriteBarrier<JSFunction> m_callFunction;
WriteBarrier<JSFunction> m_applyFunction;
+ WriteBarrier<JSFunction> m_definePropertyFunction;
WriteBarrier<JSFunction> m_arrayProtoValuesFunction;
WriteBarrier<GetterSetter> m_throwTypeErrorGetterSetter;
};
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;
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)
{
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(); }
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);
return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
}
+ JSFunction* addDefineProperty(ExecState*, JSGlobalObject*);
+
protected:
void finishCreation(VM&, JSGlobalObject*, ObjectPrototype*);