Commit 4e34894f authored by barraclough@apple.com's avatar barraclough@apple.com

Object.isSealed / Object.isFrozen don't work for native objects

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

Reviewed by Sam Weinig.

Need to inspect all properties, including static ones.
This exposes a couple of bugs in Array & Arguments:
    - getOwnPropertyDescriptor doesn't correctly report the writable attribute of array length.
    - Arguments object's defineOwnProperty does not handle callee/caller/length correctly.

Source/JavaScriptCore: 

* runtime/Arguments.cpp:
(JSC::Arguments::defineOwnProperty):
    - Add handling for callee/caller/length.
* runtime/JSArray.cpp:
(JSC::JSArray::getOwnPropertyDescriptor):
    - report length's writability correctly.
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorSeal):
(JSC::objectConstructorFreeze):
(JSC::objectConstructorIsSealed):
(JSC::objectConstructorIsFrozen):
    - Add spec-based implementation for non-final objects.

LayoutTests: 

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



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@108651 268f45cc-cd09-0410-ab3c-d52691b4dbfc
parent 30ddfb17
2012-02-23 Gavin Barraclough <barraclough@apple.com>
Object.isSealed / Object.isFrozen don't work for native objects
https://bugs.webkit.org/show_bug.cgi?id=79331
Reviewed by Sam Weinig.
Need to inspect all properties, including static ones.
This exposes a couple of bugs in Array & Arguments:
- getOwnPropertyDescriptor doesn't correctly report the writable attribute of array length.
- Arguments object's defineOwnProperty does not handle callee/caller/length correctly.
* fast/js/preventExtensions-expected.txt:
* fast/js/script-tests/preventExtensions.js:
- Added tests.
2012-02-23 Gavin Barraclough <barraclough@apple.com>
pop of array hole should get from the prototype chain
......@@ -18,7 +18,10 @@ 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 Object.getOwnPropertyDescriptor(array, "length").writable is false
PASS args[0] is 0
PASS Object.getOwnPropertyDescriptor(args, "length").writable is false
PASS Object.getOwnPropertyDescriptor(args, "callee").writable is false
PASS successfullyParsed is true
TEST COMPLETE
......
......@@ -91,8 +91,11 @@ shouldBe('obj.foo', '1');
var array = freeze([0,1,2]);
array[0] = 3;
shouldBe('array[0]', '0');
shouldBeFalse('Object.getOwnPropertyDescriptor(array, "length").writable')
// Check that freezing arguments objects works correctly.
var args = freeze((function(){ return arguments; })(0,1,2));
args[0] = 3;
shouldBe('args[0]', '0');
shouldBeFalse('Object.getOwnPropertyDescriptor(args, "length").writable')
shouldBeFalse('Object.getOwnPropertyDescriptor(args, "callee").writable')
2012-02-23 Gavin Barraclough <barraclough@apple.com>
Object.isSealed / Object.isFrozen don't work for native objects
https://bugs.webkit.org/show_bug.cgi?id=79331
Reviewed by Sam Weinig.
Need to inspect all properties, including static ones.
This exposes a couple of bugs in Array & Arguments:
- getOwnPropertyDescriptor doesn't correctly report the writable attribute of array length.
- Arguments object's defineOwnProperty does not handle callee/caller/length correctly.
* runtime/Arguments.cpp:
(JSC::Arguments::defineOwnProperty):
- Add handling for callee/caller/length.
* runtime/JSArray.cpp:
(JSC::JSArray::getOwnPropertyDescriptor):
- report length's writability correctly.
* runtime/ObjectConstructor.cpp:
(JSC::objectConstructorSeal):
(JSC::objectConstructorFreeze):
(JSC::objectConstructorIsSealed):
(JSC::objectConstructorIsFrozen):
- Add spec-based implementation for non-final objects.
2012-02-23 Gavin Barraclough <barraclough@apple.com>
pop of array hole should get from the prototype chain
......@@ -302,13 +302,13 @@ bool Arguments::deleteProperty(JSCell* cell, ExecState* exec, const Identifier&
bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
{
if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
return false;
Arguments* thisObject = jsCast<Arguments*>(object);
bool isArrayIndex;
unsigned i = propertyName.toArrayIndex(isArrayIndex);
if (isArrayIndex && i < thisObject->d->numArguments) {
if (!Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow))
return false;
if (!thisObject->d->deletedArguments) {
thisObject->d->deletedArguments = adoptArrayPtr(new bool[thisObject->d->numArguments]);
memset(thisObject->d->deletedArguments.get(), 0, sizeof(bool) * thisObject->d->numArguments);
......@@ -328,8 +328,38 @@ bool Arguments::defineOwnProperty(JSObject* object, ExecState* exec, const Ident
thisObject->d->deletedArguments[i] = true; // 1. Call the [[Delete]] internal method of map passing P and false as arguments.
}
}
return true;
}
if (propertyName == exec->propertyNames().length && !thisObject->d->overrodeLength) {
thisObject->d->overrodeLength = true;
if (!descriptor.isAccessorDescriptor()) {
if (!descriptor.value())
descriptor.setValue(jsNumber(thisObject->d->numArguments));
if (!descriptor.configurablePresent())
descriptor.setConfigurable(true);
}
if (!descriptor.configurablePresent())
descriptor.setConfigurable(true);
}
if (propertyName == exec->propertyNames().callee && !thisObject->d->overrodeCallee) {
thisObject->d->overrodeCallee = true;
if (!descriptor.isAccessorDescriptor()) {
if (!descriptor.value())
descriptor.setValue(thisObject->d->callee.get());
if (!descriptor.configurablePresent())
descriptor.setConfigurable(true);
}
if (!descriptor.configurablePresent())
descriptor.setConfigurable(true);
}
return true;
if (propertyName == exec->propertyNames().caller && thisObject->d->isStrictMode)
thisObject->createStrictModeCallerIfNecessary(exec);
return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
}
void Arguments::tearOff(CallFrame* callFrame)
......
......@@ -694,7 +694,7 @@ bool JSArray::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const
{
JSArray* thisObject = jsCast<JSArray*>(object);
if (propertyName == exec->propertyNames().length) {
descriptor.setDescriptor(jsNumber(thisObject->length()), DontDelete | DontEnum);
descriptor.setDescriptor(jsNumber(thisObject->length()), thisObject->isLengthWritable() ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
return true;
}
......
......@@ -365,30 +365,31 @@ EncodedJSValue JSC_HOST_CALL objectConstructorSeal(ExecState* exec)
return throwVMError(exec, createTypeError(exec, "Object.seal can only be called on Objects."));
JSObject* object = asObject(obj);
if (isJSFinalObject(obj))
if (isJSFinalObject(object)) {
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);
}
return JSValue::encode(obj);
}
// 3. Set the [[Extensible]] internal property of O to false.
object->preventExtensions(exec->globalData());
// 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);
}
......@@ -401,34 +402,35 @@ EncodedJSValue JSC_HOST_CALL objectConstructorFreeze(ExecState* exec)
return throwVMError(exec, createTypeError(exec, "Object.freeze can only be called on Objects."));
JSObject* object = asObject(obj);
if (isJSFinalObject(obj))
if (isJSFinalObject(object)) {
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);
}
return JSValue::encode(obj);
}
// 3. Set the [[Extensible]] internal property of O to false.
object->preventExtensions(exec->globalData());
// 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);
}
......@@ -444,18 +446,63 @@ EncodedJSValue JSC_HOST_CALL objectConstructorPreventExtensions(ExecState* exec)
EncodedJSValue JSC_HOST_CALL objectConstructorIsSealed(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.isSealed can only be called on Objects."));
return JSValue::encode(jsBoolean(asObject(obj)->isSealed(exec->globalData())));
JSObject* object = asObject(obj);
if (isJSFinalObject(object))
return JSValue::encode(jsBoolean(object->isSealed(exec->globalData())));
// 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, then return false.
if (desc.configurable())
return JSValue::encode(jsBoolean(false));
}
// 3. If the [[Extensible]] internal property of O is false, then return true.
// 4. Otherwise, return false.
return JSValue::encode(jsBoolean(!object->isExtensible()));
}
EncodedJSValue JSC_HOST_CALL objectConstructorIsFrozen(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.isFrozen can only be called on Objects."));
return JSValue::encode(jsBoolean(asObject(obj)->isFrozen(exec->globalData())));
JSObject* object = asObject(obj);
if (isJSFinalObject(object))
return JSValue::encode(jsBoolean(object->isFrozen(exec->globalData())));
// 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, return false. c. If desc.[[Configurable]] is true, then return false.
if ((desc.isDataDescriptor() && desc.writable()) || desc.configurable())
return JSValue::encode(jsBoolean(false));
}
// 3. If the [[Extensible]] internal property of O is false, then return true.
// 4. Otherwise, return false.
return JSValue::encode(jsBoolean(!object->isExtensible()));
}
EncodedJSValue JSC_HOST_CALL objectConstructorIsExtensible(ExecState* exec)
......
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