diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/RegExpObject.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/RegExpObject.cpp | 272 |
1 files changed, 272 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp new file mode 100644 index 000000000..4553f7ad0 --- /dev/null +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -0,0 +1,272 @@ +/* + * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) + * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "config.h" +#include "RegExpObject.h" + +#include "Error.h" +#include "ExceptionHelpers.h" +#include "JSArray.h" +#include "JSGlobalObject.h" +#include "JSString.h" +#include "Lexer.h" +#include "Lookup.h" +#include "RegExpConstructor.h" +#include "RegExpPrototype.h" +#include "UStringBuilder.h" +#include "UStringConcatenate.h" +#include <wtf/PassOwnPtr.h> + +namespace JSC { + +static JSValue regExpObjectGlobal(ExecState*, JSValue, const Identifier&); +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 + +#include "RegExpObject.lut.h" + +namespace JSC { + +ASSERT_CLASS_FITS_IN_CELL(RegExpObject); + +const ClassInfo RegExpObject::s_info = { "RegExp", &JSNonFinalObject::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 + lastIndex regExpObjectLastIndex DontDelete|DontEnum +@end +*/ + +RegExpObject::RegExpObject(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp) + : JSNonFinalObject(globalObject->globalData(), structure) + , d(adoptPtr(new RegExpObjectData(globalObject->globalData(), this, regExp))) +{ +} + +void RegExpObject::finishCreation(JSGlobalObject* globalObject) +{ + Base::finishCreation(globalObject->globalData()); + ASSERT(inherits(&s_info)); +} + +void RegExpObject::destroy(JSCell* cell) +{ + jsCast<RegExpObject*>(cell)->RegExpObject::~RegExpObject(); +} + +void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + RegExpObject* thisObject = jsCast<RegExpObject*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); + COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag); + ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren()); + Base::visitChildren(thisObject, visitor); + if (thisObject->d->regExp) + visitor.append(&thisObject->d->regExp); + if (UNLIKELY(!thisObject->d->lastIndex.get().isInt32())) + visitor.append(&thisObject->d->lastIndex); +} + +bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +{ + 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) +{ + return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor); +} + +JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->global()); +} + +JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase()); +} + +JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, const Identifier&) +{ + return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline()); +} + +JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&) +{ + UString pattern = asRegExpObject(slotBase)->regExp()->pattern(); + unsigned length = pattern.length(); + const UChar* characters = pattern.characters(); + bool previousCharacterWasBackslash = false; + bool inBrackets = false; + bool shouldEscape = false; + + // early return for strings that don't contain a forwards slash and LineTerminator + for (unsigned i = 0; i < length; ++i) { + UChar ch = characters[i]; + if (!previousCharacterWasBackslash) { + if (inBrackets) { + if (ch == ']') + inBrackets = false; + } else { + if (ch == '/') { + shouldEscape = true; + break; + } + if (ch == '[') + inBrackets = true; + } + } + + if (Lexer<UChar>::isLineTerminator(ch)) { + shouldEscape = true; + break; + } + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + if (!shouldEscape) + return jsString(exec, pattern); + + previousCharacterWasBackslash = false; + inBrackets = false; + UStringBuilder result; + for (unsigned i = 0; i < length; ++i) { + UChar 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<UChar>::isLineTerminator(ch)) { + if (!previousCharacterWasBackslash) + result.append('\\'); + + if (ch == '\n') + result.append('n'); + else if (ch == '\r') + result.append('r'); + else if (ch == 0x2028) + result.append("u2028"); + else + result.append("u2029"); + } else + result.append(ch); + + if (previousCharacterWasBackslash) + previousCharacterWasBackslash = false; + else + previousCharacterWasBackslash = ch == '\\'; + } + + 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) +{ + 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)); +} + +JSValue RegExpObject::exec(ExecState* exec) +{ + if (match(exec)) + return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec); + return jsNull(); +} + +// Shared implementation used by test and exec. +bool RegExpObject::match(ExecState* exec) +{ + RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); + UString input = exec->argument(0).toString(exec); + JSGlobalData* globalData = &exec->globalData(); + if (!regExp()->global()) { + int position; + int length; + regExpConstructor->performMatch(*globalData, d->regExp.get(), input, 0, position, length); + return position >= 0; + } + + JSValue jsLastIndex = getLastIndex(); + unsigned lastIndex; + if (LIKELY(jsLastIndex.isUInt32())) { + lastIndex = jsLastIndex.asUInt32(); + if (lastIndex > input.length()) { + setLastIndex(0); + return false; + } + } else { + double doubleLastIndex = jsLastIndex.toInteger(exec); + if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { + setLastIndex(0); + return false; + } + lastIndex = static_cast<unsigned>(doubleLastIndex); + } + + int position; + int length = 0; + regExpConstructor->performMatch(*globalData, d->regExp.get(), input, lastIndex, position, length); + if (position < 0) { + setLastIndex(0); + return false; + } + + setLastIndex(position + length); + return true; +} + +} // namespace JSC |