]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/RegExpObject.cpp
JavaScriptCore-1218.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
93a37866
A
24#include "ButterflyInlines.h"
25#include "CopiedSpaceInlines.h"
ba379fdc 26#include "Error.h"
14957cd0 27#include "ExceptionHelpers.h"
9dae56ea
A
28#include "JSArray.h"
29#include "JSGlobalObject.h"
30#include "JSString.h"
6fe7ccc8 31#include "Lexer.h"
4e4e5a6f 32#include "Lookup.h"
93a37866 33#include "Operations.h"
9dae56ea 34#include "RegExpConstructor.h"
6fe7ccc8 35#include "RegExpMatchesArray.h"
9dae56ea 36#include "RegExpPrototype.h"
14957cd0 37#include <wtf/PassOwnPtr.h>
93a37866 38#include <wtf/text/StringBuilder.h>
14957cd0 39
93a37866 40#if PLATFORM(IOS)
14957cd0 41#include <wtf/PassOwnPtr.h>
93a37866 42#endif
9dae56ea
A
43
44namespace JSC {
45
93a37866
A
46static JSValue regExpObjectGlobal(ExecState*, JSValue, PropertyName);
47static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, PropertyName);
48static JSValue regExpObjectMultiline(ExecState*, JSValue, PropertyName);
49static JSValue regExpObjectSource(ExecState*, JSValue, PropertyName);
9dae56ea
A
50
51} // namespace JSC
52
53#include "RegExpObject.lut.h"
54
55namespace JSC {
56
93a37866 57ASSERT_HAS_TRIVIAL_DESTRUCTOR(RegExpObject);
9dae56ea 58
93a37866 59const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) };
9dae56ea
A
60
61/* Source for RegExpObject.lut.h
62@begin regExpTable
63 global regExpObjectGlobal DontDelete|ReadOnly|DontEnum
64 ignoreCase regExpObjectIgnoreCase DontDelete|ReadOnly|DontEnum
65 multiline regExpObjectMultiline DontDelete|ReadOnly|DontEnum
66 source regExpObjectSource DontDelete|ReadOnly|DontEnum
9dae56ea
A
67@end
68*/
69
14957cd0 70RegExpObject::RegExpObject(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp)
93a37866
A
71 : JSNonFinalObject(globalObject->vm(), structure)
72 , m_regExp(globalObject->vm(), this, regExp)
6fe7ccc8 73 , m_lastIndexIsWritable(true)
9dae56ea 74{
6fe7ccc8 75 m_lastIndex.setWithoutWriteBarrier(jsNumber(0));
9dae56ea
A
76}
77
6fe7ccc8 78void RegExpObject::finishCreation(JSGlobalObject* globalObject)
9dae56ea 79{
93a37866 80 Base::finishCreation(globalObject->vm());
6fe7ccc8 81 ASSERT(inherits(&s_info));
9dae56ea
A
82}
83
6fe7ccc8 84void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
14957cd0 85{
6fe7ccc8
A
86 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
87 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
14957cd0 88 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
6fe7ccc8 89 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
93a37866 90
6fe7ccc8 91 Base::visitChildren(thisObject, visitor);
93a37866
A
92 visitor.append(&thisObject->m_regExp);
93 visitor.append(&thisObject->m_lastIndex);
6fe7ccc8
A
94}
95
93a37866 96bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
6fe7ccc8
A
97{
98 if (propertyName == exec->propertyNames().lastIndex) {
99 RegExpObject* regExp = asRegExpObject(cell);
100 slot.setValue(regExp, regExp->getLastIndex());
101 return true;
102 }
103 return getStaticValueSlot<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), propertyName, slot);
14957cd0
A
104}
105
93a37866 106bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
9dae56ea 107{
6fe7ccc8
A
108 if (propertyName == exec->propertyNames().lastIndex) {
109 RegExpObject* regExp = asRegExpObject(object);
110 descriptor.setDescriptor(regExp->getLastIndex(), regExp->m_lastIndexIsWritable ? DontDelete | DontEnum : DontDelete | DontEnum | ReadOnly);
111 return true;
112 }
113 return getStaticValueDescriptor<RegExpObject, JSObject>(exec, ExecState::regExpTable(exec), jsCast<RegExpObject*>(object), propertyName, descriptor);
114}
115
93a37866 116bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
6fe7ccc8
A
117{
118 if (propertyName == exec->propertyNames().lastIndex)
119 return false;
120 return Base::deleteProperty(cell, exec, propertyName);
9dae56ea
A
121}
122
93a37866 123void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
f9bf01c6 124{
6fe7ccc8
A
125 if (mode == IncludeDontEnumProperties)
126 propertyNames.add(exec->propertyNames().lastIndex);
93a37866 127 Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
6fe7ccc8
A
128}
129
130void RegExpObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
131{
132 if (mode == IncludeDontEnumProperties)
133 propertyNames.add(exec->propertyNames().lastIndex);
134 Base::getPropertyNames(object, exec, propertyNames, mode);
135}
136
137static bool reject(ExecState* exec, bool throwException, const char* message)
138{
139 if (throwException)
93a37866 140 throwTypeError(exec, ASCIILiteral(message));
6fe7ccc8
A
141 return false;
142}
143
93a37866 144bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
6fe7ccc8
A
145{
146 if (propertyName == exec->propertyNames().lastIndex) {
147 RegExpObject* regExp = asRegExpObject(object);
148 if (descriptor.configurablePresent() && descriptor.configurable())
149 return reject(exec, shouldThrow, "Attempting to change configurable attribute of unconfigurable property.");
150 if (descriptor.enumerablePresent() && descriptor.enumerable())
151 return reject(exec, shouldThrow, "Attempting to change enumerable attribute of unconfigurable property.");
152 if (descriptor.isAccessorDescriptor())
153 return reject(exec, shouldThrow, "Attempting to change access mechanism for an unconfigurable property.");
154 if (!regExp->m_lastIndexIsWritable) {
155 if (descriptor.writablePresent() && descriptor.writable())
156 return reject(exec, shouldThrow, "Attempting to change writable attribute of unconfigurable property.");
157 if (!sameValue(exec, regExp->getLastIndex(), descriptor.value()))
158 return reject(exec, shouldThrow, "Attempting to change value of a readonly property.");
159 return true;
160 }
161 if (descriptor.writablePresent() && !descriptor.writable())
162 regExp->m_lastIndexIsWritable = false;
163 if (descriptor.value())
164 regExp->setLastIndex(exec, descriptor.value(), false);
165 return true;
166 }
167
168 return Base::defineOwnProperty(object, exec, propertyName, descriptor, shouldThrow);
f9bf01c6
A
169}
170
93a37866 171JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, PropertyName)
9dae56ea 172{
4e4e5a6f 173 return jsBoolean(asRegExpObject(slotBase)->regExp()->global());
9dae56ea
A
174}
175
93a37866 176JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, PropertyName)
9dae56ea 177{
4e4e5a6f 178 return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase());
9dae56ea
A
179}
180
93a37866 181JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, PropertyName)
9dae56ea 182{
4e4e5a6f 183 return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline());
9dae56ea
A
184}
185
93a37866
A
186template <typename CharacterType>
187static inline void appendLineTerminatorEscape(StringBuilder&, CharacterType);
188
189template <>
190inline void appendLineTerminatorEscape<LChar>(StringBuilder& builder, LChar lineTerminator)
191{
192 if (lineTerminator == '\n')
193 builder.append('n');
194 else
195 builder.append('r');
196}
197
198template <>
199inline void appendLineTerminatorEscape<UChar>(StringBuilder& builder, UChar lineTerminator)
200{
201 if (lineTerminator == '\n')
202 builder.append('n');
203 else if (lineTerminator == '\r')
204 builder.append('r');
205 else if (lineTerminator == 0x2028)
206 builder.appendLiteral("u2028");
207 else
208 builder.appendLiteral("u2029");
209}
210
211template <typename CharacterType>
212static inline JSValue regExpObjectSourceInternal(ExecState* exec, String pattern, const CharacterType* characters, unsigned length)
9dae56ea 213{
6fe7ccc8
A
214 bool previousCharacterWasBackslash = false;
215 bool inBrackets = false;
216 bool shouldEscape = false;
9dae56ea 217
6fe7ccc8
A
218 // 15.10.6.4 specifies that RegExp.prototype.toString must return '/' + source + '/',
219 // and also states that the result must be a valid RegularExpressionLiteral. '//' is
220 // not a valid RegularExpressionLiteral (since it is a single line comment), and hence
221 // source cannot ever validly be "". If the source is empty, return a different Pattern
222 // that would match the same thing.
223 if (!length)
93a37866 224 return jsNontrivialString(exec, ASCIILiteral("(?:)"));
9dae56ea 225
6fe7ccc8
A
226 // early return for strings that don't contain a forwards slash and LineTerminator
227 for (unsigned i = 0; i < length; ++i) {
93a37866 228 CharacterType ch = characters[i];
6fe7ccc8
A
229 if (!previousCharacterWasBackslash) {
230 if (inBrackets) {
231 if (ch == ']')
232 inBrackets = false;
233 } else {
234 if (ch == '/') {
235 shouldEscape = true;
236 break;
237 }
238 if (ch == '[')
239 inBrackets = true;
240 }
241 }
9dae56ea 242
93a37866 243 if (Lexer<CharacterType>::isLineTerminator(ch)) {
6fe7ccc8
A
244 shouldEscape = true;
245 break;
246 }
247
248 if (previousCharacterWasBackslash)
249 previousCharacterWasBackslash = false;
250 else
251 previousCharacterWasBackslash = ch == '\\';
252 }
253
254 if (!shouldEscape)
255 return jsString(exec, pattern);
256
257 previousCharacterWasBackslash = false;
258 inBrackets = false;
93a37866 259 StringBuilder result;
6fe7ccc8 260 for (unsigned i = 0; i < length; ++i) {
93a37866 261 CharacterType ch = characters[i];
6fe7ccc8
A
262 if (!previousCharacterWasBackslash) {
263 if (inBrackets) {
264 if (ch == ']')
265 inBrackets = false;
266 } else {
267 if (ch == '/')
268 result.append('\\');
269 else if (ch == '[')
270 inBrackets = true;
271 }
272 }
273
274 // escape LineTerminator
93a37866 275 if (Lexer<CharacterType>::isLineTerminator(ch)) {
6fe7ccc8
A
276 if (!previousCharacterWasBackslash)
277 result.append('\\');
278
93a37866 279 appendLineTerminatorEscape<CharacterType>(result, ch);
6fe7ccc8
A
280 } else
281 result.append(ch);
282
283 if (previousCharacterWasBackslash)
284 previousCharacterWasBackslash = false;
285 else
286 previousCharacterWasBackslash = ch == '\\';
287 }
288
93a37866
A
289 return jsString(exec, result.toString());
290}
291
292JSValue regExpObjectSource(ExecState* exec, JSValue slotBase, PropertyName)
293{
294 String pattern = asRegExpObject(slotBase)->regExp()->pattern();
295 if (pattern.is8Bit())
296 return regExpObjectSourceInternal(exec, pattern, pattern.characters8(), pattern.length());
297 return regExpObjectSourceInternal(exec, pattern, pattern.characters16(), pattern.length());
9dae56ea
A
298}
299
93a37866 300void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
9dae56ea 301{
6fe7ccc8
A
302 if (propertyName == exec->propertyNames().lastIndex) {
303 asRegExpObject(cell)->setLastIndex(exec, value, slot.isStrictMode());
304 return;
305 }
306 lookupPut<RegExpObject, JSObject>(exec, propertyName, value, ExecState::regExpTable(exec), jsCast<RegExpObject*>(cell), slot);
9dae56ea
A
307}
308
6fe7ccc8 309JSValue RegExpObject::exec(ExecState* exec, JSString* string)
9dae56ea 310{
6fe7ccc8
A
311 if (MatchResult result = match(exec, string))
312 return RegExpMatchesArray::create(exec, string, regExp(), result);
9dae56ea
A
313 return jsNull();
314}
315
9dae56ea 316// Shared implementation used by test and exec.
6fe7ccc8 317MatchResult RegExpObject::match(ExecState* exec, JSString* string)
9dae56ea 318{
6fe7ccc8 319 RegExp* regExp = this->regExp();
9dae56ea 320 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
93a37866
A
321 String input = string->value(exec);
322 VM& vm = exec->vm();
6fe7ccc8 323 if (!regExp->global())
93a37866 324 return regExpConstructor->performMatch(vm, regExp, string, input, 0);
9dae56ea 325
14957cd0
A
326 JSValue jsLastIndex = getLastIndex();
327 unsigned lastIndex;
328 if (LIKELY(jsLastIndex.isUInt32())) {
329 lastIndex = jsLastIndex.asUInt32();
330 if (lastIndex > input.length()) {
6fe7ccc8
A
331 setLastIndex(exec, 0);
332 return MatchResult::failed();
14957cd0
A
333 }
334 } else {
335 double doubleLastIndex = jsLastIndex.toInteger(exec);
336 if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
6fe7ccc8
A
337 setLastIndex(exec, 0);
338 return MatchResult::failed();
14957cd0
A
339 }
340 lastIndex = static_cast<unsigned>(doubleLastIndex);
9dae56ea
A
341 }
342
93a37866 343 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex);
6fe7ccc8
A
344 setLastIndex(exec, result.end);
345 return result;
9dae56ea
A
346}
347
348} // namespace JSC