Diff
Modified: trunk/LayoutTests/ChangeLog (109007 => 109008)
--- trunk/LayoutTests/ChangeLog 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/LayoutTests/ChangeLog 2012-02-27 18:29:04 UTC (rev 109008)
@@ -1,3 +1,20 @@
+2012-02-27 Gavin Barraclough <barraclo...@apple.com>
+
+ RegExp lastIndex should behave as a regular property
+ https://bugs.webkit.org/show_bug.cgi?id=79446
+
+ Reviewed by Sam Weinig.
+
+ lastIndex should be a regular data descriptor, with the attributes configurable:false,
+ enumerable:false, writable:true. As such, it should be possible to reconfigure writable
+ as false. If the lastIndex property is reconfigured to be read-only, we should respect
+ this correctly.
+
+ * fast/regex/lastIndex-expected.txt: Added.
+ * fast/regex/lastIndex.html: Added.
+ * fast/regex/script-tests/lastIndex.js: Added.
+ - Added test cases for correct handling of lastIndex.
+
2012-02-27 Adrienne Walker <e...@google.com>
[chromium] Unreviewed gardening, mark getPutImageDataPair as slow
Added: trunk/LayoutTests/fast/regex/lastIndex-expected.txt (0 => 109008)
--- trunk/LayoutTests/fast/regex/lastIndex-expected.txt (rev 0)
+++ trunk/LayoutTests/fast/regex/lastIndex-expected.txt 2012-02-27 18:29:04 UTC (rev 109008)
@@ -0,0 +1,30 @@
+This page tests that a RegExp object's lastIndex behaves like a regular property.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS delete /x/.lastIndex is false
+PASS 'use strict'; delete /x/.lastIndex threw exception TypeError: Unable to delete property..
+PASS 'lastIndex' in /x/ is true
+PASS for (property in /x/) if (property === 'lastIndex') throw false; true is true
+PASS var re = /x/; re.lastIndex = re; re.lastIndex === re is true
+PASS Object.defineProperty(/x/, {get:function(){}}) threw exception TypeError: Property description must be an object..
+PASS Object.defineProperty(/x/, 'lastIndex', {enumerable:true}); true threw exception TypeError: Attempting to change enumerable attribute of unconfigurable property..
+PASS Object.defineProperty(/x/, 'lastIndex', {enumerable:false}); true is true
+PASS Object.defineProperty(/x/, 'lastIndex', {configurable:true}); true threw exception TypeError: Attempting to change configurable attribute of unconfigurable property..
+PASS Object.defineProperty(/x/, 'lastIndex', {configurable:false}); true is true
+PASS var re = Object.defineProperty(/x/, 'lastIndex', {writable:true}); re.lastIndex = 42; re.lastIndex is 42
+PASS var re = Object.defineProperty(/x/, 'lastIndex', {writable:false}); re.lastIndex = 42; re.lastIndex is 0
+PASS var re = Object.defineProperty(/x/, 'lastIndex', {value:42}); re.lastIndex is 42
+PASS Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {writable:true}); true threw exception TypeError: Attempting to change writable attribute of unconfigurable property..
+PASS Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {value:42}); true threw exception TypeError: Attempting to change value of a readonly property..
+PASS Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {value:0}); true is true
+PASS Object.defineProperty(/x/, 'lastIndex', {writable:false}).exec('') is null
+PASS Object.defineProperty(/x/, 'lastIndex', {writable:false}).exec('x') is ["x"]
+PASS Object.defineProperty(/x/g, 'lastIndex', {writable:false}).exec('') threw exception TypeError: Attempted to assign to readonly property..
+PASS Object.defineProperty(/x/g, 'lastIndex', {writable:false}).exec('x') threw exception TypeError: Attempted to assign to readonly property..
+PASS var re = /x/; Object.freeze(re); Object.isFrozen(re); is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/fast/regex/lastIndex.html (0 => 109008)
--- trunk/LayoutTests/fast/regex/lastIndex.html (rev 0)
+++ trunk/LayoutTests/fast/regex/lastIndex.html 2012-02-27 18:29:04 UTC (rev 109008)
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script src=""
+<script src=""
+</body>
+</html>
Added: trunk/LayoutTests/fast/regex/script-tests/lastIndex.js (0 => 109008)
--- trunk/LayoutTests/fast/regex/script-tests/lastIndex.js (rev 0)
+++ trunk/LayoutTests/fast/regex/script-tests/lastIndex.js 2012-02-27 18:29:04 UTC (rev 109008)
@@ -0,0 +1,48 @@
+description(
+"This page tests that a RegExp object's lastIndex behaves like a regular property."
+);
+
+// lastIndex is not configurable
+shouldBeFalse("delete /x/.lastIndex");
+shouldThrow("'use strict'; delete /x/.lastIndex");
+
+// lastIndex is not enumerable
+shouldBeTrue("'lastIndex' in /x/");
+shouldBeTrue("for (property in /x/) if (property === 'lastIndex') throw false; true");
+
+// lastIndex is writable
+shouldBeTrue("var re = /x/; re.lastIndex = re; re.lastIndex === re");
+
+// Cannot redefine lastIndex as an accessor
+shouldThrow("Object.defineProperty(/x/, {get:function(){}})");
+
+// Cannot redefine lastIndex as enumerable
+shouldThrow("Object.defineProperty(/x/, 'lastIndex', {enumerable:true}); true");
+shouldBeTrue("Object.defineProperty(/x/, 'lastIndex', {enumerable:false}); true");
+
+// Cannot redefine lastIndex as configurable
+shouldThrow("Object.defineProperty(/x/, 'lastIndex', {configurable:true}); true");
+shouldBeTrue("Object.defineProperty(/x/, 'lastIndex', {configurable:false}); true");
+
+// Can redefine lastIndex as read-only
+shouldBe("var re = Object.defineProperty(/x/, 'lastIndex', {writable:true}); re.lastIndex = 42; re.lastIndex", '42');
+shouldBe("var re = Object.defineProperty(/x/, 'lastIndex', {writable:false}); re.lastIndex = 42; re.lastIndex", '0');
+
+// Can redefine the value
+shouldBe("var re = Object.defineProperty(/x/, 'lastIndex', {value:42}); re.lastIndex", '42');
+
+// Cannot redefine read-only lastIndex as writable
+shouldThrow("Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {writable:true}); true");
+
+// Can only redefine the value of a read-only lastIndex as the current value
+shouldThrow("Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {value:42}); true");
+shouldBeTrue("Object.defineProperty(Object.defineProperty(/x/, 'lastIndex', {writable:false}), 'lastIndex', {value:0}); true");
+
+// Trying to run a global regular _expression_ should throw, if lastIndex is read-only
+shouldBe("Object.defineProperty(/x/, 'lastIndex', {writable:false}).exec('')", 'null');
+shouldBe("Object.defineProperty(/x/, 'lastIndex', {writable:false}).exec('x')", '["x"]');
+shouldThrow("Object.defineProperty(/x/g, 'lastIndex', {writable:false}).exec('')");
+shouldThrow("Object.defineProperty(/x/g, 'lastIndex', {writable:false}).exec('x')");
+
+// Should be able to freeze a regular _expression_ object.
+shouldBeTrue("var re = /x/; Object.freeze(re); Object.isFrozen(re);");
Modified: trunk/Source/_javascript_Core/ChangeLog (109007 => 109008)
--- trunk/Source/_javascript_Core/ChangeLog 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/Source/_javascript_Core/ChangeLog 2012-02-27 18:29:04 UTC (rev 109008)
@@ -1,5 +1,47 @@
2012-02-27 Gavin Barraclough <barraclo...@apple.com>
+ RegExp lastIndex should behave as a regular property
+ https://bugs.webkit.org/show_bug.cgi?id=79446
+
+ Reviewed by Sam Weinig.
+
+ lastIndex should be a regular data descriptor, with the attributes configurable:false,
+ enumerable:false, writable:true. As such, it should be possible to reconfigure writable
+ as false. If the lastIndex property is reconfigured to be read-only, we should respect
+ this correctly.
+
+ * runtime/CommonIdentifiers.h:
+ - Removed some unused identifiers, added lastIndex.
+ * runtime/RegExpObject.cpp:
+ (JSC::RegExpObject::getOwnPropertySlot):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::getOwnPropertyDescriptor):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::deleteProperty):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::getOwnPropertyNames):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::getPropertyNames):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::reject):
+ - helper function for defineOwnProperty.
+ (JSC::RegExpObject::defineOwnProperty):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::put):
+ - lastIndex is no longer a static value, provided specific handling.
+ (JSC::RegExpObject::match):
+ - Pass setLastIndex an ExecState, so it can throw if read-only.
+ * runtime/RegExpObject.h:
+ (JSC::RegExpObject::setLastIndex):
+ - Pass setLastIndex an ExecState, so it can throw if read-only.
+ (RegExpObjectData):
+ - Added lastIndexIsWritable.
+ * runtime/RegExpPrototype.cpp:
+ (JSC::regExpProtoFuncCompile):
+ - Pass setLastIndex an ExecState, so it can throw if read-only.
+
+2012-02-27 Gavin Barraclough <barraclo...@apple.com>
+
Implement support for op_negate and op_bitnot in the DFG JIT
https://bugs.webkit.org/show_bug.cgi?id=79617
Modified: trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h (109007 => 109008)
--- trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/Source/_javascript_Core/runtime/CommonIdentifiers.h 2012-02-27 18:29:04 UTC (rev 109008)
@@ -27,10 +27,6 @@
// MarkedArgumentBuffer of property names, passed to a macro so we can do set them up various
// ways without repeating the list.
#define JSC_COMMON_IDENTIFIERS_EACH_PROPERTY_NAME(macro) \
- macro(__defineGetter__) \
- macro(__defineSetter__) \
- macro(__lookupGetter__) \
- macro(__lookupSetter__) \
macro(apply) \
macro(arguments) \
macro(bind) \
@@ -52,6 +48,7 @@
macro(input) \
macro(isArray) \
macro(isPrototypeOf) \
+ macro(lastIndex) \
macro(length) \
macro(message) \
macro(multiline) \
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.cpp (109007 => 109008)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.cpp 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.cpp 2012-02-27 18:29:04 UTC (rev 109008)
@@ -40,8 +40,6 @@
static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, const Identifier&);
static JSValue regExpObjectMultiline(ExecState*, JSValue, const Identifier&);
static JSValue regExpObjectSource(ExecState*, JSValue, const Identifier&);
-static JSValue regExpObjectLastIndex(ExecState*, JSValue, const Identifier&);
-static void setRegExpObjectLastIndex(ExecState*, JSObject*, JSValue);
} // namespace JSC
@@ -59,7 +57,6 @@
ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum
multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum
source regExpObjectSource DontDelete|ReadOnly|DontEnum
- lastIndex regExpObjectLastIndex DontDelete|DontEnum
@end
*/
@@ -95,14 +92,79 @@
bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
{
+ if (propertyName == exec->propertyNames().lastIndex) {
+ RegExpObject* regExp = asRegExpObject(cell);
+ slot.setValue(regExp, regExp->getLastIndex());
+ return true;
+ }
return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), propertyName, slot);
}
bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
{
+ if (propertyName == exec->propertyNames().lastIndex) {
+ RegExpObject* regExp = asRegExpObject(object);
+ descriptor.setDescriptor(regExp->getLastIndex(), regExp->d->lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
+ return true;
+ }
return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor);
}
+bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
+{
+ if (propertyName == exec->propertyNames().lastIndex)
+ return false;
+ return Base::deleteProperty(cell, exec, propertyName);
+}
+
+void RegExpObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ if (mode == IncludeDontEnumProperties)
+ propertyNames.add(exec->propertyNames().lastIndex);
+ Base::getOwnPropertyNames(object, exec, propertyNames, mode);
+}
+
+void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+{
+ if (mode == IncludeDontEnumProperties)
+ propertyNames.add(exec->propertyNames().lastIndex);
+ Base::getPropertyNames(object, exec, propertyNames, mode);
+}
+
+static bool reject(ExecState* exec, bool throwException, const char* message)
+{
+ if (throwException)
+ throwTypeError(exec, message);
+ return false;
+}
+
+bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
+{
+ if (propertyName == exec->propertyNames().lastIndex) {
+ RegExpObject* regExp = asRegExpObject(object);
+ if (descriptor.configurablePresent() && descriptor.configurable())
+ return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property.");
+ if (descriptor.enumerablePresent() && descriptor.enumerable())
+ return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property.");
+ if (descriptor.isAccessorDescriptor())
+ return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property.");
+ if (!regExp->d->lastIndexIsWritable) {
+ if (descriptor.writablePresent() && descriptor.writable())
+ return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property.");
+ if (!sameValue(exec, regExp->getLastIndex(), descriptor.value()))
+ return reject(exec, shouldThrow, "Attempting to change value of a readonly property.");
+ return true;
+ }
+ if (descriptor.writablePresent() && !descriptor.writable())
+ regExp->d->lastIndexIsWritable = false;
+ if (descriptor.value())
+ regExp->setLastIndex(exec, descriptor.value(), false);
+ return true;
+ }
+
+ return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
+}
+
JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, const Identifier&)
{
return jsBoolean(asRegExpObject(slotBase)->regExp()->global());
@@ -200,21 +262,15 @@
return jsString(exec, result.toUString());
}
-JSValue regExpObjectLastIndex(ExecState*, JSValue slotBase, const Identifier&)
-{
- return asRegExpObject(slotBase)->getLastIndex();
-}
-
void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
{
+ if (propertyName == exec->propertyNames().lastIndex) {
+ asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode());
+ return;
+ }
lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot);
}
-void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value)
-{
- asRegExpObject(baseObject)->setLastIndex(exec->globalData(), value);
-}
-
JSValue RegExpObject::test(ExecState* exec)
{
return jsBoolean(match(exec));
@@ -245,13 +301,13 @@
if (LIKELY(jsLastIndex.isUInt32())) {
lastIndex = jsLastIndex.asUInt32();
if (lastIndex > input.length()) {
- setLastIndex(0);
+ setLastIndex(exec, 0);
return false;
}
} else {
double doubleLastIndex = jsLastIndex.toInteger(exec);
if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
- setLastIndex(0);
+ setLastIndex(exec, 0);
return false;
}
lastIndex = static_cast<unsigned>(doubleLastIndex);
@@ -261,11 +317,11 @@
int length = 0;
regExpConstructor->performMatch(*globalData, d->regExp.get(), input, lastIndex, position, length);
if (position < 0) {
- setLastIndex(0);
+ setLastIndex(exec, 0);
return false;
}
- setLastIndex(position + length);
+ setLastIndex(exec, position + length);
return true;
}
Modified: trunk/Source/_javascript_Core/runtime/RegExpObject.h (109007 => 109008)
--- trunk/Source/_javascript_Core/runtime/RegExpObject.h 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/Source/_javascript_Core/runtime/RegExpObject.h 2012-02-27 18:29:04 UTC (rev 109008)
@@ -47,13 +47,20 @@
void setRegExp(JSGlobalData& globalData, RegExp* r) { d->regExp.set(globalData, this, r); }
RegExp* regExp() const { return d->regExp.get(); }
- void setLastIndex(size_t lastIndex)
+ void setLastIndex(ExecState* exec, size_t lastIndex)
{
d->lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex));
+ if (LIKELY(d->lastIndexIsWritable))
+ d->lastIndex.setWithoutWriteBarrier(jsNumber(lastIndex));
+ else
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
}
- void setLastIndex(JSGlobalData& globalData, JSValue lastIndex)
+ void setLastIndex(ExecState* exec, JSValue lastIndex, bool shouldThrow)
{
- d->lastIndex.set(globalData, this, lastIndex);
+ if (LIKELY(d->lastIndexIsWritable))
+ d->lastIndex.set(exec->globalData(), this, lastIndex);
+ else if (shouldThrow)
+ throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
}
JSValue getLastIndex() const
{
@@ -83,6 +90,11 @@
static void visitChildren(JSCell*, SlotVisitor&);
+ JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName);
+ JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
+ JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
+
private:
bool match(ExecState*);
@@ -91,12 +103,14 @@
public:
RegExpObjectData(JSGlobalData& globalData, RegExpObject* owner, RegExp* regExp)
: regExp(globalData, owner, regExp)
+ , lastIndexIsWritable(true)
{
lastIndex.setWithoutWriteBarrier(jsNumber(0));
}
WriteBarrier<RegExp> regExp;
WriteBarrier<Unknown> lastIndex;
+ bool lastIndexIsWritable;
};
#if COMPILER(MSVC)
friend void WTF::deleteOwnedPtr<RegExpObjectData>(RegExpObjectData*);
Modified: trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp (109007 => 109008)
--- trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp 2012-02-27 18:26:23 UTC (rev 109007)
+++ trunk/Source/_javascript_Core/runtime/RegExpPrototype.cpp 2012-02-27 18:29:04 UTC (rev 109008)
@@ -129,7 +129,7 @@
return throwVMError(exec, createSyntaxError(exec, regExp->errorMessage()));
asRegExpObject(thisValue)->setRegExp(exec->globalData(), regExp);
- asRegExpObject(thisValue)->setLastIndex(0);
+ asRegExpObject(thisValue)->setLastIndex(exec, 0);
return JSValue::encode(jsUndefined());
}