Commit 2668db93 authored by barraclough@apple.com's avatar barraclough@apple.com

Fix Object.freeze for non-final objects.

https://bugs.webkit.org/show_bug.cgi?id=79286

Reviewed by Oliver Hunt.

For vanilla objects we implement this with a single transition, for objects
with special properties we should just follow the spec defined algorithm.

Source/JavaScriptCore: 

* runtime/JSArray.cpp:
(JSC::SparseArrayValueMap::put):
    - this does need to handle inextensible objects.
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorSeal):
(JSC::objectConstructorFreeze):
    - Implement spec defined algorithm for non-final objects.
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::freezeTransition):
    - freeze should set m_hasReadOnlyOrGetterSetterPropertiesExcludingProto.
* runtime/Structure.h:
(JSC::Structure::hasReadOnlyOrGetterSetterPropertiesExcludingProto):
(JSC::Structure::setHasGetterSetterProperties):
(JSC::Structure::setContainsReadOnlyProperties):
(Structure):
    - renamed m_hasReadOnlyOrGetterSetterPropertiesExcludingProto.

LayoutTests: 

* fast/js/preventExtensions-expected.txt:
* fast/js/script-tests/preventExtensions.js:
    - added new tests.



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108568 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 8a63aeac
2012-02-22 Gavin Barraclough <barraclough@apple.com>
Fix Object.freeze for non-final objects.
https://bugs.webkit.org/show_bug.cgi?id=79286
Reviewed by Oliver Hunt.
For vanilla objects we implement this with a single transition, for objects
with special properties we should just follow the spec defined algorithm.
* fast/js/preventExtensions-expected.txt:
* fast/js/script-tests/preventExtensions.js:
- added new tests.
2012-02-22 Max Vujovic <mvujovic@adobe.com>
Paddings and borders on root SVG element with viewbox causes child SVG elements to be rendered with the incorrect size
......@@ -16,6 +16,9 @@ PASS "use strict"; var o = {}; Object.preventExtensions(o); o.__proto__ = { newP
PASS Object.preventExtensions(Math); Math.sqrt(4) is 2
PASS var arr = Object.preventExtensions([]); arr[0] = 42; arr[0] is undefined.
PASS var arr = Object.preventExtensions([]); arr[0] = 42; arr.length is 0
PASS obj.foo is 1
PASS array[0] is 0
PASS args[0] is 0
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -78,3 +78,21 @@ shouldBe('Object.preventExtensions(Math); Math.sqrt(4)', '2');
// Should not be able to add properties to a preventExtensions array.
shouldBeUndefined('var arr = Object.preventExtensions([]); arr[0] = 42; arr[0]');
shouldBe('var arr = Object.preventExtensions([]); arr[0] = 42; arr.length', '0');
// A read-only property on the prototype should prevent a [[Put]] .
function Constructor() {}
Constructor.prototype.foo = 1;
Object.freeze(Constructor.prototype);
var obj = new Constructor();
obj.foo = 2;
shouldBe('obj.foo', '1');
// Check that freezing array objects works correctly.
var array = freeze([0,1,2]);
array[0] = 3;
shouldBe('array[0]', '0');
// Check that freezing arguments objects works correctly.
var args = freeze((function(){ return arguments; })(0,1,2));
args[0] = 3;
shouldBe('args[0]', '0');
2012-02-22 Gavin Barraclough <barraclough@apple.com>
Fix Object.freeze for non-final objects.
https://bugs.webkit.org/show_bug.cgi?id=79286
Reviewed by Oliver Hunt.
For vanilla objects we implement this with a single transition, for objects
with special properties we should just follow the spec defined algorithm.
* runtime/JSArray.cpp:
(JSC::SparseArrayValueMap::put):
- this does need to handle inextensible objects.
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorSeal):
(JSC::objectConstructorFreeze):
- Implement spec defined algorithm for non-final objects.
* runtime/Structure.cpp:
(JSC::Structure::Structure):
(JSC::Structure::freezeTransition):
- freeze should set m_hasReadOnlyOrGetterSetterPropertiesExcludingProto.
* runtime/Structure.h:
(JSC::Structure::hasReadOnlyOrGetterSetterPropertiesExcludingProto):
(JSC::Structure::setHasGetterSetterProperties):
(JSC::Structure::setContainsReadOnlyProperties):
(Structure):
- renamed m_hasReadOnlyOrGetterSetterPropertiesExcludingProto.
2012-02-22 Mark Hahnenberg <mhahnenberg@apple.com>
Allocations from CopiedBlocks should always be 8-byte aligned
......@@ -223,10 +223,17 @@ inline std::pair<SparseArrayValueMap::iterator, bool> SparseArrayValueMap::add(J
inline void SparseArrayValueMap::put(ExecState* exec, JSArray* array, unsigned i, JSValue value)
{
// If the array is not extensible, we shouldn't get here!
ASSERT(array->isExtensible());
SparseArrayEntry& entry = add(array, i).first->second;
std::pair<SparseArrayValueMap::iterator, bool> result = add(array, i);
SparseArrayEntry& entry = result.first->second;
// To save a separate find & add, we first always add to the sparse map.
// In the uncommon case that this is a new property, and the array is not
// extensible, this is not the right thing to have done - so remove again.
if (result.second && !array->isExtensible()) {
remove(result.first);
// FIXME: should throw in strict mode.
return;
}
if (!(entry.attributes & Accessor)) {
if (entry.attributes & ReadOnly) {
......
......@@ -359,19 +359,77 @@ EncodedJSValue JSC_HOST_CALL objectConstructorCreate(ExecState* exec)
EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
{
// 1. If Type(O) is not Object throw a TypeError exception.
JSValue obj = exec->argument(0);
if (!obj.isObject())
return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects."));
asObject(obj)->seal(exec->globalData());
JSObject* object = asObject(obj);
if (isJSFinalObject(obj))
object->seal(exec->globalData());
else {
// 2. For each named own property name P of O,
PropertyNameArray properties(exec);
object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties);
PropertyNameArray::const_iterator end = properties.end();
for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
// a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P.
PropertyDescriptor desc;
if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, *iter, desc))
continue;
// b. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false.
desc.setConfigurable(false);
// c. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments.
object->methodTable()->defineOwnProperty(object, exec, *iter, desc, true);
if (exec->hadException())
return JSValue::encode(obj);
}
// 3. Set the [[Extensible]] internal property of O to false.
object->preventExtensions(exec->globalData());
}
// 4. Return O.
return JSValue::encode(obj);
}
EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
{
// 1. If Type(O) is not Object throw a TypeError exception.
JSValue obj = exec->argument(0);
if (!obj.isObject())
return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects."));
asObject(obj)->freeze(exec->globalData());
JSObject* object = asObject(obj);
if (isJSFinalObject(obj))
object->freeze(exec->globalData());
else {
// 2. For each named own property name P of O,
PropertyNameArray properties(exec);
object->methodTable()->getOwnPropertyNames(object, exec, properties, IncludeDontEnumProperties);
PropertyNameArray::const_iterator end = properties.end();
for (PropertyNameArray::const_iterator iter = properties.begin(); iter != end; ++iter) {
// a. Let desc be the result of calling the [[GetOwnProperty]] internal method of O with P.
PropertyDescriptor desc;
if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, *iter, desc))
continue;
// b. If IsDataDescriptor(desc) is true, then
// i. If desc.[[Writable]] is true, set desc.[[Writable]] to false.
if (desc.isDataDescriptor())
desc.setWritable(false);
// c. If desc.[[Configurable]] is true, set desc.[[Configurable]] to false.
desc.setConfigurable(false);
// d. Call the [[DefineOwnProperty]] internal method of O with P, desc, and true as arguments.
object->methodTable()->defineOwnProperty(object, exec, *iter, desc, true);
if (exec->hadException())
return JSValue::encode(obj);
}
// 3. Set the [[Extensible]] internal property of O to false.
object->preventExtensions(exec->globalData());
}
// 4. Return O.
return JSValue::encode(obj);
}
......
......@@ -167,7 +167,7 @@ Structure::Structure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSV
, m_dictionaryKind(NoneDictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(false)
, m_hasReadOnlyGetterSetterPropertiesExcludingProto(false)
, m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
, m_hasNonEnumerableProperties(false)
, m_attributesInPrevious(0)
, m_specificFunctionThrashCount(0)
......@@ -189,7 +189,7 @@ Structure::Structure(JSGlobalData& globalData)
, m_dictionaryKind(NoneDictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(false)
, m_hasReadOnlyGetterSetterPropertiesExcludingProto(false)
, m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(false)
, m_hasNonEnumerableProperties(false)
, m_attributesInPrevious(0)
, m_specificFunctionThrashCount(0)
......@@ -209,7 +209,7 @@ Structure::Structure(JSGlobalData& globalData, const Structure* previous)
, m_dictionaryKind(previous->m_dictionaryKind)
, m_isPinnedPropertyTable(false)
, m_hasGetterSetterProperties(previous->m_hasGetterSetterProperties)
, m_hasReadOnlyGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyGetterSetterPropertiesExcludingProto)
, m_hasReadOnlyOrGetterSetterPropertiesExcludingProto(previous->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto)
, m_hasNonEnumerableProperties(previous->m_hasNonEnumerableProperties)
, m_attributesInPrevious(0)
, m_specificFunctionThrashCount(previous->m_specificFunctionThrashCount)
......@@ -470,8 +470,11 @@ Structure* Structure::freezeTransition(JSGlobalData& globalData, Structure* stru
Structure* transition = preventExtensionsTransition(globalData, structure);
if (transition->m_propertyTable) {
PropertyTable::iterator iter = transition->m_propertyTable->begin();
PropertyTable::iterator end = transition->m_propertyTable->end();
for (PropertyTable::iterator iter = transition->m_propertyTable->begin(); iter != end; ++iter)
if (iter != end)
transition->m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
for (; iter != end; ++iter)
iter->attributes |= iter->attributes & Accessor ? DontDelete : (DontDelete | ReadOnly);
}
......
......@@ -146,16 +146,16 @@ namespace JSC {
}
bool hasGetterSetterProperties() const { return m_hasGetterSetterProperties; }
bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyGetterSetterPropertiesExcludingProto; }
bool hasReadOnlyOrGetterSetterPropertiesExcludingProto() const { return m_hasReadOnlyOrGetterSetterPropertiesExcludingProto; }
void setHasGetterSetterProperties(bool is__proto__)
{
m_hasGetterSetterProperties = true;
if (!is__proto__)
m_hasReadOnlyGetterSetterPropertiesExcludingProto = true;
m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
}
void setContainsReadOnlyProperties()
{
m_hasReadOnlyGetterSetterPropertiesExcludingProto = true;
m_hasReadOnlyOrGetterSetterPropertiesExcludingProto = true;
}
bool hasNonEnumerableProperties() const { return m_hasNonEnumerableProperties; }
......@@ -295,7 +295,7 @@ namespace JSC {
unsigned m_dictionaryKind : 2;
bool m_isPinnedPropertyTable : 1;
bool m_hasGetterSetterProperties : 1;
bool m_hasReadOnlyGetterSetterPropertiesExcludingProto : 1;
bool m_hasReadOnlyOrGetterSetterPropertiesExcludingProto : 1;
bool m_hasNonEnumerableProperties : 1;
unsigned m_attributesInPrevious : 7;
unsigned m_specificFunctionThrashCount : 2;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment