diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/RegExpObject.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/runtime/RegExpObject.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/RegExpObject.cpp | 345 |
1 files changed, 139 insertions, 206 deletions
diff --git a/Source/JavaScriptCore/runtime/RegExpObject.cpp b/Source/JavaScriptCore/runtime/RegExpObject.cpp index f3b376c0d..63a90df96 100644 --- a/Source/JavaScriptCore/runtime/RegExpObject.cpp +++ b/Source/JavaScriptCore/runtime/RegExpObject.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved. + * Copyright (C) 2003, 2007-2008, 2012, 2016 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 @@ -21,47 +21,21 @@ #include "config.h" #include "RegExpObject.h" -#include "ButterflyInlines.h" -#include "CopiedSpaceInlines.h" #include "Error.h" #include "ExceptionHelpers.h" #include "JSArray.h" #include "JSGlobalObject.h" #include "JSString.h" -#include "Lexer.h" #include "Lookup.h" -#include "Operations.h" +#include "JSCInlines.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" +#include "RegExpObjectInlines.h" namespace JSC { STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(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 -*/ +const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RegExpObject) }; RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) : JSNonFinalObject(vm, structure) @@ -74,19 +48,16 @@ RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) void RegExpObject::finishCreation(VM& vm) { Base::finishCreation(vm); - ASSERT(inherits(info())); + ASSERT(inherits(vm, info())); } 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); + visitor.append(thisObject->m_regExp); + visitor.append(thisObject->m_lastIndex); } bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) @@ -97,7 +68,7 @@ bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, Propert slot.setValue(regExp, attributes, regExp->getLastIndex()); return true; } - return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec->vm()), jsCast<RegExpObject*>(object), propertyName, slot); + return Base::getOwnPropertySlot(object, exec, propertyName, slot); } bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) @@ -109,234 +80,196 @@ 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); } -static bool reject(ExecState* exec, bool throwException, const char* message) +void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - if (throwException) - throwTypeError(exec, ASCIILiteral(message)); - return false; + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getGenericPropertyNames(object, exec, propertyNames, mode); } bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) { - if (propertyName == exec->propertyNames().lastIndex) { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (propertyName == vm.propertyNames->lastIndex) { RegExpObject* regExp = asRegExpObject(object); if (descriptor.configurablePresent() && descriptor.configurable()) - return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property."); + return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeConfigurabilityError)); if (descriptor.enumerablePresent() && descriptor.enumerable()) - return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property."); + return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeEnumerabilityError)); if (descriptor.isAccessorDescriptor()) - return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property."); + return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeAccessMechanismError)); if (!regExp->m_lastIndexIsWritable) { if (descriptor.writablePresent() && descriptor.writable()) - return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property."); + return typeError(exec, scope, shouldThrow, ASCIILiteral(UnconfigurablePropertyChangeWritabilityError)); if (!sameValue(exec, regExp->getLastIndex(), descriptor.value())) - return reject(exec, shouldThrow, "Attempting to change value of a readonly property."); + return typeError(exec, scope, shouldThrow, ASCIILiteral(ReadonlyPropertyChangeError)); return true; } + if (descriptor.value()) { + regExp->setLastIndex(exec, descriptor.value(), false); + RETURN_IF_EXCEPTION(scope, false); + } if (descriptor.writablePresent() && !descriptor.writable()) regExp->m_lastIndexIsWritable = false; - if (descriptor.value()) - regExp->setLastIndex(exec, descriptor.value(), false); return true; } + scope.release(); return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); } -static inline RegExpObject* asRegExpObject(EncodedJSValue value) +static bool regExpObjectSetLastIndexStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) { - return jsCast<RegExpObject*>(JSValue::decode(value)); + return asRegExpObject(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), true); } -EncodedJSValue regExpObjectGlobal(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) +static bool regExpObjectSetLastIndexNonStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) { - return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->global())); + return asRegExpObject(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), false); } -EncodedJSValue regExpObjectIgnoreCase(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) +bool RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase())); -} - -EncodedJSValue regExpObjectMultiline(ExecState*, EncodedJSValue slotBase, EncodedJSValue, PropertyName) -{ - return JSValue::encode(jsBoolean(asRegExpObject(slotBase)->regExp()->multiline())); -} + RegExpObject* thisObject = jsCast<RegExpObject*>(cell); -template <typename CharacterType> -static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType); + if (UNLIKELY(isThisValueAltered(slot, thisObject))) + return ordinarySetSlow(exec, thisObject, propertyName, value, slot.thisValue(), slot.isStrictMode()); -template <> -inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator) -{ - if (lineTerminator == '\n') - builder.append('n'); - else - builder.append('r'); + if (propertyName == exec->propertyNames().lastIndex) { + bool result = asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); + slot.setCustomValue(asRegExpObject(cell), slot.isStrictMode() + ? regExpObjectSetLastIndexStrict + : regExpObjectSetLastIndexNonStrict); + return result; + } + return Base::put(cell, exec, propertyName, value, slot); } -template <> -inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator) +JSValue RegExpObject::exec(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { - if (lineTerminator == '\n') - builder.append('n'); - else if (lineTerminator == '\r') - builder.append('r'); - else if (lineTerminator == 0x2028) - builder.appendLiteral("u2028"); - else - builder.appendLiteral("u2029"); + return execInline(exec, globalObject, string); } -template <typename CharacterType> -static inline JSValue regExpObjectSourceInternal(ExecState* exec, String pattern, const CharacterType* characters, unsigned length) +// Shared implementation used by test and exec. +MatchResult RegExpObject::match(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { - 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; - } + return matchInline(exec, globalObject, string); +} - if (previousCharacterWasBackslash) - previousCharacterWasBackslash = false; - else - previousCharacterWasBackslash = ch == '\\'; - } +template<typename FixEndFunc> +JSValue collectMatches(VM& vm, ExecState* exec, JSString* string, const String& s, RegExpConstructor* constructor, RegExp* regExp, const FixEndFunc& fixEnd) +{ + auto scope = DECLARE_THROW_SCOPE(vm); - 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; - } + MatchResult result = constructor->performMatch(vm, regExp, string, s, 0); + if (!result) + return jsNull(); + + static unsigned maxSizeForDirectPath = 100000; + + JSArray* array = constructEmptyArray(exec, nullptr); + RETURN_IF_EXCEPTION(scope, { }); + + bool hasException = false; + auto iterate = [&] () { + size_t end = result.end; + size_t length = end - result.start; + array->push(exec, JSRopeString::createSubstringOfResolved(vm, string, result.start, length)); + if (UNLIKELY(scope.exception())) { + hasException = true; + return; } - - // 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()); -} - + if (!length) + end = fixEnd(end); + result = constructor->performMatch(vm, regExp, string, s, end); + }; + do { + if (array->length() >= maxSizeForDirectPath) { + // First do a throw-away match to see how many matches we'll get. + unsigned matchCount = 0; + MatchResult savedResult = result; + do { + if (array->length() + matchCount >= MAX_STORAGE_VECTOR_LENGTH) { + throwOutOfMemoryError(exec, scope); + return jsUndefined(); + } + + size_t end = result.end; + matchCount++; + if (result.empty()) + end = fixEnd(end); + + // Using RegExpConstructor::performMatch() instead of calling RegExp::match() + // directly is a surprising but profitable choice: it means that when we do OOM, we + // will leave the cached result in the state it ought to have had just before the + // OOM! On the other hand, if this loop concludes that the result is small enough, + // then the iterate() loop below will overwrite the cached result anyway. + result = constructor->performMatch(vm, regExp, string, s, end); + } while (result); + + // OK, we have a sensible number of matches. Now we can create them for reals. + result = savedResult; + do { + iterate(); + ASSERT(!!scope.exception() == hasException); + if (UNLIKELY(hasException)) + return { }; + } while (result); + + return array; + } + + iterate(); + } while (result); -EncodedJSValue regExpObjectSource(ExecState* exec, EncodedJSValue slotBase, EncodedJSValue, PropertyName) -{ - 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())); + return array; } -void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +JSValue RegExpObject::matchGlobal(ExecState* exec, JSGlobalObject* globalObject, JSString* string) { - if (propertyName == exec->propertyNames().lastIndex) { - asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode()); - return; - } - Base::put(cell, exec, propertyName, value, slot); -} + VM& vm = globalObject->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + RegExp* regExp = this->regExp(); -JSValue RegExpObject::exec(ExecState* exec, JSString* string) -{ - if (MatchResult result = match(exec, string)) - return RegExpMatchesArray::create(exec, string, regExp(), result); - return jsNull(); -} + ASSERT(regExp->global()); -// Shared implementation used by test and exec. -MatchResult RegExpObject::match(ExecState* exec, JSString* string) -{ - RegExp* regExp = this->regExp(); - RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - String input = string->value(exec); - VM& vm = exec->vm(); - if (!regExp->global()) - return regExpConstructor->performMatch(vm, regExp, string, input, 0); - - JSValue jsLastIndex = getLastIndex(); - unsigned lastIndex; - if (LIKELY(jsLastIndex.isUInt32())) { - lastIndex = jsLastIndex.asUInt32(); - if (lastIndex > input.length()) { - setLastIndex(exec, 0); - return MatchResult::failed(); - } - } else { - double doubleLastIndex = jsLastIndex.toInteger(exec); - if (doubleLastIndex < 0 || doubleLastIndex > input.length()) { - setLastIndex(exec, 0); - return MatchResult::failed(); - } - lastIndex = static_cast<unsigned>(doubleLastIndex); + setLastIndex(exec, 0); + RETURN_IF_EXCEPTION(scope, { }); + + String s = string->value(exec); + RegExpConstructor* regExpConstructor = globalObject->regExpConstructor(); + + if (regExp->unicode()) { + unsigned stringLength = s.length(); + scope.release(); + return collectMatches( + vm, exec, string, s, regExpConstructor, regExp, + [&] (size_t end) -> size_t { + return advanceStringUnicode(s, stringLength, end); + }); } - MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); - setLastIndex(exec, result.end); - return result; + scope.release(); + return collectMatches( + vm, exec, string, s, regExpConstructor, regExp, + [&] (size_t end) -> size_t { + return end + 1; + }); } } // namespace JSC |