/* * 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 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(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.value()) regExp->setLastIndex(exec, descriptor.value(), false); if (descriptor.writablePresent() && !descriptor.writable()) regExp->m_lastIndexIsWritable = false; return true; } return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow); } static void regExpObjectSetLastIndexStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) { asRegExpObject(JSValue::decode(thisValue))->setLastIndex(exec, JSValue::decode(value), true); } static void regExpObjectSetLastIndexNonStrict(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value) { asRegExpObject(JSValue::decode(thisValue))->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.setCustomValue(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(doubleLastIndex); } MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); setLastIndex(exec, result.end); return result; } } // namespace JSC