X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..HEAD:/runtime/RegExpObject.cpp diff --git a/runtime/RegExpObject.cpp b/runtime/RegExpObject.cpp index 687844e..60a26ef 100644 --- a/runtime/RegExpObject.cpp +++ b/runtime/RegExpObject.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved. + * 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 @@ -21,148 +21,181 @@ #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 JSValue regExpObjectGlobal(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectIgnoreCase(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectMultiline(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectSource(ExecState*, const Identifier&, const PropertySlot&); -static JSValue regExpObjectLastIndex(ExecState*, const Identifier&, const PropertySlot&); -static void setRegExpObjectLastIndex(ExecState*, JSObject*, JSValue); +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(RegExpObject); -} // namespace JSC - -#include "RegExpObject.lut.h" - -namespace JSC { - -ASSERT_CLASS_FITS_IN_CELL(RegExpObject); - -const ClassInfo RegExpObject::info = { "RegExp", 0, 0, ExecState::regExpTable }; - -/* 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 -*/ +const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, nullptr, CREATE_METHOD_TABLE(RegExpObject) }; -RegExpObject::RegExpObject(PassRefPtr structure, PassRefPtr regExp) - : JSObject(structure) - , d(new RegExpObjectData(regExp, 0)) +RegExpObject::RegExpObject(VM& vm, Structure* structure, RegExp* regExp) + : JSNonFinalObject(vm, structure) + , m_regExp(vm, this, regExp) + , m_lastIndexIsWritable(true) { + m_lastIndex.setWithoutWriteBarrier(jsNumber(0)); } -RegExpObject::~RegExpObject() +void RegExpObject::finishCreation(VM& vm) { + Base::finishCreation(vm); + ASSERT(inherits(info())); } -bool RegExpObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { - return getStaticValueSlot(exec, ExecState::regExpTable(exec), this, propertyName, slot); + RegExpObject* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(&thisObject->m_regExp); + visitor.append(&thisObject->m_lastIndex); } -JSValue regExpObjectGlobal(ExecState*, const Identifier&, const PropertySlot& slot) +bool RegExpObject::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot) { - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->global()); + 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); } -JSValue regExpObjectIgnoreCase(ExecState*, const Identifier&, const PropertySlot& slot) +bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) { - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->ignoreCase()); + if (propertyName == exec->propertyNames().lastIndex) + return false; + return Base::deleteProperty(cell, exec, propertyName); } - -JSValue regExpObjectMultiline(ExecState*, const Identifier&, const PropertySlot& slot) -{ - return jsBoolean(asRegExpObject(slot.slotBase())->regExp()->multiline()); + +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); } -JSValue regExpObjectSource(ExecState* exec, const Identifier&, const PropertySlot& slot) +void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - return jsString(exec, asRegExpObject(slot.slotBase())->regExp()->pattern()); + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getPropertyNames(object, exec, propertyNames, mode); } -JSValue regExpObjectLastIndex(ExecState* exec, const Identifier&, const PropertySlot& slot) +void RegExpObject::getGenericPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - return jsNumber(exec, asRegExpObject(slot.slotBase())->lastIndex()); + if (mode.includeDontEnumProperties()) + propertyNames.add(exec->propertyNames().lastIndex); + Base::getGenericPropertyNames(object, exec, propertyNames, mode); } -void RegExpObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +static bool reject(ExecState* exec, bool throwException, const char* message) { - lookupPut(exec, propertyName, value, ExecState::regExpTable(exec), this, slot); + if (throwException) + throwTypeError(exec, ASCIILiteral(message)); + return false; } -void setRegExpObjectLastIndex(ExecState* exec, JSObject* baseObject, JSValue value) +bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, const PropertyDescriptor& descriptor, bool shouldThrow) { - asRegExpObject(baseObject)->setLastIndex(value.toInteger(exec)); + 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); } -JSValue RegExpObject::test(ExecState* exec, const ArgList& args) +static void regExpObjectSetLastIndexStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) { - return jsBoolean(match(exec, args)); + asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), true); } -JSValue RegExpObject::exec(ExecState* exec, const ArgList& args) +static void regExpObjectSetLastIndexNonStrict(ExecState* exec, JSObject* slotBase, EncodedJSValue, EncodedJSValue value) { - if (match(exec, args)) - return exec->lexicalGlobalObject()->regExpConstructor()->arrayOfMatches(exec); - return jsNull(); + asRegExpObject(slotBase)->setLastIndex(exec, JSValue::decode(value), false); } -static JSValue JSC_HOST_CALL callRegExpObject(ExecState* exec, JSObject* function, JSValue, const ArgList& args) +void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { - return asRegExpObject(function)->exec(exec, args); + 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); } -CallType RegExpObject::getCallData(CallData& callData) +JSValue RegExpObject::exec(ExecState* exec, JSString* string) { - callData.native.function = callRegExpObject; - return CallTypeHost; + if (MatchResult result = match(exec, string)) + return createRegExpMatchesArray(exec, string, regExp(), result); + return jsNull(); } // Shared implementation used by test and exec. -bool RegExpObject::match(ExecState* exec, const ArgList& args) +MatchResult RegExpObject::match(ExecState* exec, JSString* string) { + RegExp* regExp = this->regExp(); RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor(); - - UString input = args.isEmpty() ? regExpConstructor->input() : args.at(0).toString(exec); - if (input.isNull()) { - throwError(exec, GeneralError, "No input to " + toString(exec) + "."); - return false; - } - - if (!regExp()->global()) { - int position; - int length; - regExpConstructor->performMatch(d->regExp.get(), input, 0, position, length); - return position >= 0; - } - - if (d->lastIndex < 0 || d->lastIndex > input.size()) { - d->lastIndex = 0; - return false; - } - - int position; - int length; - regExpConstructor->performMatch(d->regExp.get(), input, static_cast(d->lastIndex), position, length); - if (position < 0) { - d->lastIndex = 0; - return false; + 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); } - d->lastIndex = position + length; - return true; + MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex); + setLastIndex(exec, result.end); + return result; } } // namespace JSC