diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/RegExpObject.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/RegExpObject.cpp | 185 |
1 files changed, 163 insertions, 22 deletions
diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index d61ec74e9..f3b376c0d 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -28,18 +28,40 @@ #include "JSArray.h" #include "JSGlobalObject.h" #include "JSString.h" +#include "Lexer.h" #include "Lookup.h" -#include "JSCInlines.h" +#include "Operations.h" #include "RegExpConstructor.h" #include "RegExpMatchesArray.h" #include "RegExpPrototype.h" +#include <wtf/PassOwnPtr.h> #include <wtf/text/StringBuilder.h> namespace JSC { +static EncodedJSValue regExpObjectGlobal(ExecState*, EncodedJSValue, EncodedJSValue, PropertyName); +static EncodedJSValue regExpObjectIgnoreCase(ExecState*, EncodedJSValue, EncodedJSValue, PropertyName); +static EncodedJSValue regExpObjectMultiline(ExecState*, EncodedJSValue, EncodedJSValue, PropertyName); +static EncodedJSValue regExpObjectSource(ExecState*, EncodedJSValue, EncodedJSValue, PropertyName); + +} // namespace JSC + +#include "RegExpObject.lut.h" + +namespace JSC { + STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject); -const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RegExpObject) }; +const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) }; + +/* Source for RegExpObject.lut.h +@begin regExpTable + global regExpObjectGlobal DontDelete|ReadOnly|DontEnum + ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum + multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum + source regExpObjectSource DontDelete|ReadOnly|DontEnum +@end +*/ RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) : JSNonFinalObject(vm, structure) @@ -59,6 +81,9 @@ void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { RegExpObject* thisObject = jsCast<RegExpObject*>(cell); ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); visitor.append(&thisObject->m_regExp); visitor.append(&thisObject->m_lastIndex); @@ -72,7 +97,7 @@ bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, Propert slot.setValue(regExp, attributes, regExp->getLastIndex()); return true; } - return Base::getOwnPropertySlot(object, exec, propertyName, slot); + return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec->vm()), jsCast<RegExpObject*>(object), propertyName, slot); } bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) @@ -84,25 +109,18 @@ bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName pr void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - if (mode.includeDontEnumProperties()) + if (mode == IncludeDontEnumProperties) propertyNames.add(exec->propertyNames().lastIndex); Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode); } void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - if (mode.includeDontEnumProperties()) + if (mode == IncludeDontEnumProperties) propertyNames.add(exec->propertyNames().lastIndex); Base::getPropertyNames(object, exec, propertyNames, mode); } -void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) -{ - if (mode.includeDontEnumProperties()) - propertyNames.add(exec->propertyNames().lastIndex); - Base::getGenericPropertyNames(object, exec, propertyNames, mode); -} - static bool reject(ExecState* exec, bool throwException, const char* message) { if (throwException) @@ -127,33 +145,156 @@ bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, Property return reject(exec, shouldThrow, "Attempting to change value of a readonly property."); return true; } - if (descriptor.value()) - regExp->setLastIndex(exec, descriptor.value(), false); if (descriptor.writablePresent() && !descriptor.writable()) regExp->m_lastIndexIsWritable = false; + if (descriptor.value()) + regExp->setLastIndex(exec, descriptor.value(), false); return true; } return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); } -static void regExpObjectSetLastIndexStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) +static inline RegExpObject* asRegExpObject(EncodedJSValue value) +{ + return jsCast<RegExpObject*>(JSValue::decode(value)); +} + +EncodedJSValue regExpObjectGlobal(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->global())); +} + +EncodedJSValue regExpObjectIgnoreCase(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase())); +} + +EncodedJSValue regExpObjectMultiline(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) +{ + return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->multiline())); +} + +template <typename CharacterType> +static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType); + +template <> +inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator) +{ + if (lineTerminator == '\n') + builder.append('n'); + else + builder.append('r'); +} + +template <> +inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator) { - asRegExpObject(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), true); + if (lineTerminator == '\n') + builder.append('n'); + else if (lineTerminator == '\r') + builder.append('r'); + else if (lineTerminator == 0x2028) + builder.appendLiteral("u2028"); + else + builder.appendLiteral("u2029"); +} + +template <typename CharacterType> +static inline JSValue regExpObjectSourceInternal(ExecState* exec, String pattern, const CharacterType* characters, unsigned length) +{ + bool previousCharacterWasBackslash = false; + bool inBrackets = false; + bool shouldEscape = false; + + // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/', + // and also states that the result must be a valid RegularExpressionLiteral. '//' is + // not a valid RegularExpressionLiteral (since it is a single line comment), and hence + // source cannot ever validly be "". If the source is empty, return a different Pattern + // that would match the same thing. + if (!length) + return jsNontrivialString(exec, ASCIILiteral("(?:)")); + + // early return for strings that don't contain a forwards slash and LineTerminator + for (unsigned i = 0; i < length; ++i) { + CharacterType ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') { + shouldEscape = true; + break; + } + if (ch == '[') + inBrackets = true; + } + } + + if (Lexer<CharacterType>::isLineTerminator(ch)) { + shouldEscape = true; + break; + } + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + if (!shouldEscape) + return jsString(exec, pattern); + + previousCharacterWasBackslash = false; + inBrackets = false; + StringBuilder result; + for (unsigned i = 0; i < length; ++i) { + CharacterType ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') + result.append('\\'); + else if (ch == '[') + inBrackets = true; + } + } + + // escape LineTerminator + if (Lexer<CharacterType>::isLineTerminator(ch)) { + if (!previousCharacterWasBackslash) + result.append('\\'); + + appendLineTerminatorEscape<CharacterType>(result, ch); + } else + result.append(ch); + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + return jsString(exec, result.toString()); } -static void regExpObjectSetLastIndexNonStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) + + +EncodedJSValue regExpObjectSource(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, PropertyName) { - asRegExpObject(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), false); + String pattern = asRegExpObject(slotBase)->regExp()->pattern(); + if (pattern.is8Bit()) + return JSValue::encode(regExpObjectSourceInternal(exec, pattern, pattern.characters8(), pattern.length())); + return JSValue::encode(regExpObjectSourceInternal(exec, pattern, pattern.characters16(), pattern.length())); } void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { if (propertyName == exec->propertyNames().lastIndex) { asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); - slot.setCustomValue(asRegExpObject(cell), slot.isStrictMode() - ? regExpObjectSetLastIndexStrict - : regExpObjectSetLastIndexNonStrict); return; } Base::put(cell, exec, propertyName, value, slot); @@ -162,7 +303,7 @@ void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue RegExpObject::exec(ExecState* exec, JSString* string) { if (MatchResult result = match(exec, string)) - return createRegExpMatchesArray(exec, string, regExp(), result); + return RegExpMatchesArray::create(exec, string, regExp(), result); return jsNull(); } |