]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/RegExpObject.cpp
JavaScriptCore-1097.3.3.tar.gz
[apple/javascriptcore.git] / runtime / RegExpObject.cpp
CommitLineData
9dae56ea
A
1/*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
6fe7ccc8 3 * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved.
9dae56ea
A
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 */
20
21#include "config.h"
22#include "RegExpObject.h"
23
ba379fdc 24#include "Error.h"
14957cd0 25#include "ExceptionHelpers.h"
9dae56ea
A
26#include "JSArray.h"
27#include "JSGlobalObject.h"
28#include "JSString.h"
6fe7ccc8 29#include "Lexer.h"
4e4e5a6f 30#include "Lookup.h"
9dae56ea 31#include "RegExpConstructor.h"
6fe7ccc8 32#include "RegExpMatchesArray.h"
9dae56ea 33#include "RegExpPrototype.h"
6fe7ccc8 34#include "UStringBuilder.h"
14957cd0
A
35#include "UStringConcatenate.h"
36#include <wtf/PassOwnPtr.h>
37
38#include <wtf/PassOwnPtr.h>
9dae56ea
A
39
40namespace JSC {
41
4e4e5a6f
A
42static JSValue regExpObjectGlobal(ExecState*, JSValue, const Identifier&);
43static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, const Identifier&);
44static JSValue regExpObjectMultiline(ExecState*, JSValue, const Identifier&);
45static JSValue regExpObjectSource(ExecState*, JSValue, const Identifier&);
9dae56ea
A
46
47} // namespace JSC
48
49#include "RegExpObject.lut.h"
50
51namespace JSC {
52
53ASSERT_CLASS_FITS_IN_CELL(RegExpObject);
54
6fe7ccc8 55const ClassInfo RegExpObject::s_info = { "RegExp", &JSNonFinalObject::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) };
9dae56ea
A
56
57/* Source for RegExpObject.lut.h
58@begin regExpTable
59 global regExpObjectGlobal DontDelete|ReadOnly|DontEnum
60 ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum
61 multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum
62 source regExpObjectSource DontDelete|ReadOnly|DontEnum
9dae56ea
A
63@end
64*/
65
14957cd0 66RegExpObject::RegExpObject(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp)
6fe7ccc8
A
67 : JSNonFinalObject(globalObject->globalData(), structure)
68 , m_regExp(globalObject->globalData(), this, regExp)
69 , m_lastIndexIsWritable(true)
9dae56ea 70{
6fe7ccc8 71 m_lastIndex.setWithoutWriteBarrier(jsNumber(0));
9dae56ea
A
72}
73
6fe7ccc8 74void RegExpObject::finishCreation(JSGlobalObject* globalObject)
9dae56ea 75{
6fe7ccc8
A
76 Base::finishCreation(globalObject->globalData());
77 ASSERT(inherits(&s_info));
9dae56ea
A
78}
79
6fe7ccc8 80void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
14957cd0 81{
6fe7ccc8
A
82 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
83 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
14957cd0 84 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
6fe7ccc8
A
85 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
86 Base::visitChildren(thisObject, visitor);
87 if (thisObject->m_regExp)
88 visitor.append(&thisObject->m_regExp);
89 if (UNLIKELY(!thisObject->m_lastIndex.get().isInt32()))
90 visitor.append(&thisObject->m_lastIndex);
91}
92
93bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
94{
95 if (propertyName == exec->propertyNames().lastIndex) {
96 RegExpObject* regExp = asRegExpObject(cell);
97 slot.setValue(regExp, regExp->getLastIndex());
98 return true;
99 }
100 return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), propertyName, slot);
14957cd0
A
101}
102
6fe7ccc8 103bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
9dae56ea 104{
6fe7ccc8
A
105 if (propertyName == exec->propertyNames().lastIndex) {
106 RegExpObject* regExp = asRegExpObject(object);
107 descriptor.setDescriptor(regExp->getLastIndex(), regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
108 return true;
109 }
110 return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor);
111}
112
113bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
114{
115 if (propertyName == exec->propertyNames().lastIndex)
116 return false;
117 return Base::deleteProperty(cell, exec, propertyName);
9dae56ea
A
118}
119
6fe7ccc8 120void RegExpObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
f9bf01c6 121{
6fe7ccc8
A
122 if (mode == IncludeDontEnumProperties)
123 propertyNames.add(exec->propertyNames().lastIndex);
124 Base::getOwnPropertyNames(object, exec, propertyNames, mode);
125}
126
127void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
128{
129 if (mode == IncludeDontEnumProperties)
130 propertyNames.add(exec->propertyNames().lastIndex);
131 Base::getPropertyNames(object, exec, propertyNames, mode);
132}
133
134static bool reject(ExecState* exec, bool throwException, const char* message)
135{
136 if (throwException)
137 throwTypeError(exec, message);
138 return false;
139}
140
141bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
142{
143 if (propertyName == exec->propertyNames().lastIndex) {
144 RegExpObject* regExp = asRegExpObject(object);
145 if (descriptor.configurablePresent() && descriptor.configurable())
146 return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property.");
147 if (descriptor.enumerablePresent() && descriptor.enumerable())
148 return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property.");
149 if (descriptor.isAccessorDescriptor())
150 return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property.");
151 if (!regExp->m_lastIndexIsWritable) {
152 if (descriptor.writablePresent() && descriptor.writable())
153 return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property.");
154 if (!sameValue(exec, regExp->getLastIndex(), descriptor.value()))
155 return reject(exec, shouldThrow, "Attempting to change value of a readonly property.");
156 return true;
157 }
158 if (descriptor.writablePresent() && !descriptor.writable())
159 regExp->m_lastIndexIsWritable = false;
160 if (descriptor.value())
161 regExp->setLastIndex(exec, descriptor.value(), false);
162 return true;
163 }
164
165 return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
f9bf01c6
A
166}
167
4e4e5a6f 168JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, const Identifier&)
9dae56ea 169{
4e4e5a6f 170 return jsBoolean(asRegExpObject(slotBase)->regExp()->global());
9dae56ea
A
171}
172
4e4e5a6f 173JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, const Identifier&)
9dae56ea 174{
4e4e5a6f 175 return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase());
9dae56ea
A
176}
177
4e4e5a6f 178JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, const Identifier&)
9dae56ea 179{
4e4e5a6f 180 return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline());
9dae56ea
A
181}
182
4e4e5a6f 183JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, const Identifier&)
9dae56ea 184{
6fe7ccc8
A
185 UString pattern = asRegExpObject(slotBase)->regExp()->pattern();
186 unsigned length = pattern.length();
187 const UChar* characters = pattern.characters();
188 bool previousCharacterWasBackslash = false;
189 bool inBrackets = false;
190 bool shouldEscape = false;
9dae56ea 191
6fe7ccc8
A
192 // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
193 // and also states that the result must be a valid RegularExpressionLiteral. '//' is
194 // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
195 // source cannot ever validly be "". If the source is empty, return a different Pattern
196 // that would match the same thing.
197 if (!length)
198 return jsString(exec, "(?:)");
9dae56ea 199
6fe7ccc8
A
200 // early return for strings that don't contain a forwards slash and LineTerminator
201 for (unsigned i = 0; i < length; ++i) {
202 UChar ch = characters[i];
203 if (!previousCharacterWasBackslash) {
204 if (inBrackets) {
205 if (ch == ']')
206 inBrackets = false;
207 } else {
208 if (ch == '/') {
209 shouldEscape = true;
210 break;
211 }
212 if (ch == '[')
213 inBrackets = true;
214 }
215 }
9dae56ea 216
6fe7ccc8
A
217 if (Lexer<UChar>::isLineTerminator(ch)) {
218 shouldEscape = true;
219 break;
220 }
221
222 if (previousCharacterWasBackslash)
223 previousCharacterWasBackslash = false;
224 else
225 previousCharacterWasBackslash = ch == '\\';
226 }
227
228 if (!shouldEscape)
229 return jsString(exec, pattern);
230
231 previousCharacterWasBackslash = false;
232 inBrackets = false;
233 UStringBuilder result;
234 for (unsigned i = 0; i < length; ++i) {
235 UChar ch = characters[i];
236 if (!previousCharacterWasBackslash) {
237 if (inBrackets) {
238 if (ch == ']')
239 inBrackets = false;
240 } else {
241 if (ch == '/')
242 result.append('\\');
243 else if (ch == '[')
244 inBrackets = true;
245 }
246 }
247
248 // escape LineTerminator
249 if (Lexer<UChar>::isLineTerminator(ch)) {
250 if (!previousCharacterWasBackslash)
251 result.append('\\');
252
253 if (ch == '\n')
254 result.append('n');
255 else if (ch == '\r')
256 result.append('r');
257 else if (ch == 0x2028)
258 result.append("u2028");
259 else
260 result.append("u2029");
261 } else
262 result.append(ch);
263
264 if (previousCharacterWasBackslash)
265 previousCharacterWasBackslash = false;
266 else
267 previousCharacterWasBackslash = ch == '\\';
268 }
269
270 return jsString(exec, result.toUString());
9dae56ea
A
271}
272
6fe7ccc8 273void RegExpObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
9dae56ea 274{
6fe7ccc8
A
275 if (propertyName == exec->propertyNames().lastIndex) {
276 asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode());
277 return;
278 }
279 lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot);
9dae56ea
A
280}
281
6fe7ccc8 282JSValue RegExpObject::exec(ExecState* exec, JSString* string)
9dae56ea 283{
6fe7ccc8
A
284 if (MatchResult result = match(exec, string))
285 return RegExpMatchesArray::create(exec, string, regExp(), result);
9dae56ea
A
286 return jsNull();
287}
288
9dae56ea 289// Shared implementation used by test and exec.
6fe7ccc8 290MatchResult RegExpObject::match(ExecState* exec, JSString* string)
9dae56ea 291{
6fe7ccc8 292 RegExp* regExp = this->regExp();
9dae56ea 293 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
6fe7ccc8
A
294 UString input = string->value(exec);
295 JSGlobalData& globalData = exec->globalData();
296 if (!regExp->global())
297 return regExpConstructor->performMatch(globalData, regExp, string, input, 0);
9dae56ea 298
14957cd0
A
299 JSValue jsLastIndex = getLastIndex();
300 unsigned lastIndex;
301 if (LIKELY(jsLastIndex.isUInt32())) {
302 lastIndex = jsLastIndex.asUInt32();
303 if (lastIndex > input.length()) {
6fe7ccc8
A
304 setLastIndex(exec, 0);
305 return MatchResult::failed();
14957cd0
A
306 }
307 } else {
308 double doubleLastIndex = jsLastIndex.toInteger(exec);
309 if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
6fe7ccc8
A
310 setLastIndex(exec, 0);
311 return MatchResult::failed();
14957cd0
A
312 }
313 lastIndex = static_cast<unsigned>(doubleLastIndex);
9dae56ea
A
314 }
315
6fe7ccc8
A
316 MatchResult result = regExpConstructor->performMatch(globalData, regExp, string, input, lastIndex);
317 setLastIndex(exec, result.end);
318 return result;
9dae56ea
A
319}
320
321} // namespace JSC