| /* |
| * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) |
| * Copyright (C) 2003, 2007, 2008, 2012 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 "ButterflyInlines.h" |
| #include "CopiedSpaceInlines.h" |
| #include "Error.h" |
| #include "ExceptionHelpers.h" |
| #include "JSArray.h" |
| #include "JSGlobalObject.h" |
| #include "JSString.h" |
| #include "Lookup.h" |
| #include "JSCInlines.h" |
| #include "RegExpConstructor.h" |
| #include "RegExpMatchesArray.h" |
| #include "RegExpPrototype.h" |
| #include <wtf/text/StringBuilder.h> |
| |
| namespace JSC { |
| |
| STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject); |
| |
| 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) |
| , m_regExp(vm, this, regExp) |
| , m_lastIndexIsWritable(true) |
| { |
| m_lastIndex.setWithoutWriteBarrier(jsNumber(0)); |
| } |
| |
| void RegExpObject::finishCreation(VM& vm) |
| { |
| Base::finishCreation(vm); |
| ASSERT(inherits(info())); |
| } |
| |
| void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) |
| { |
| RegExpObject* thisObject = jsCast<RegExpObject*>(cell); |
| ASSERT_GC_OBJECT_INHERITS(thisObject, info()); |
| Base::visitChildren(thisObject, visitor); |
| visitor.append(&thisObject->m_regExp); |
| visitor.append(&thisObject->m_lastIndex); |
| } |
| |
| bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) |
| { |
| if (propertyName == exec->propertyNames().lastIndex) { |
| RegExpObject* regExp = asRegExpObject(object); |
| unsigned attributes = regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly; |
| slot.setValue(regExp, attributes, regExp->getLastIndex()); |
| return true; |
| } |
| return Base::getOwnPropertySlot(object, exec, propertyName, slot); |
| } |
| |
| bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) |
| { |
| if (propertyName == exec->propertyNames().lastIndex) |
| return false; |
| return Base::deleteProperty(cell, exec, propertyName); |
| } |
| |
| void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) |
| { |
| 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()) |
| 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) |
| throwTypeError(exec, ASCIILiteral(message)); |
| return false; |
| } |
| |
| bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const 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->m_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->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, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) |
| { |
| asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), true); |
| } |
| |
| static void regExpObjectSetLastIndexNonStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) |
| { |
| asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), false); |
| } |
| |
| 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.setCustomProperty(asRegExpObject(cell), slot.isStrictMode() |
| ? regExpObjectSetLastIndexStrict |
| : regExpObjectSetLastIndexNonStrict); |
| return; |
| } |
| Base::put(cell, exec, propertyName, value, slot); |
| } |
| |
| JSValue RegExpObject::exec(ExecState* exec, JSString* string) |
| { |
| if (MatchResult result = match(exec, string)) |
| return createRegExpMatchesArray(exec, string, regExp(), result); |
| return jsNull(); |
| } |
| |
| // 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); |
| } |
| |
| MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); |
| setLastIndex(exec, result.end); |
| return result; |
| } |
| |
| } // namespace JSC |