]> git.saurik.com Git - apple/javascriptcore.git/blame_incremental - runtime/RegExpObject.cpp
JavaScriptCore-1218.35.tar.gz
[apple/javascriptcore.git] / runtime / RegExpObject.cpp
... / ...
CommitLineData
1/*
2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2007, 2008, 2012 Apple Inc. All Rights Reserved.
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
24#include "ButterflyInlines.h"
25#include "CopiedSpaceInlines.h"
26#include "Error.h"
27#include "ExceptionHelpers.h"
28#include "JSArray.h"
29#include "JSGlobalObject.h"
30#include "JSString.h"
31#include "Lexer.h"
32#include "Lookup.h"
33#include "Operations.h"
34#include "RegExpConstructor.h"
35#include "RegExpMatchesArray.h"
36#include "RegExpPrototype.h"
37#include <wtf/PassOwnPtr.h>
38#include <wtf/text/StringBuilder.h>
39
40#if PLATFORM(IOS)
41#include <wtf/PassOwnPtr.h>
42#endif
43
44namespace JSC {
45
46static JSValue regExpObjectGlobal(ExecState*, JSValue, PropertyName);
47static JSValue regExpObjectIgnoreCase(ExecState*, JSValue, PropertyName);
48static JSValue regExpObjectMultiline(ExecState*, JSValue, PropertyName);
49static JSValue regExpObjectSource(ExecState*, JSValue, PropertyName);
50
51} // namespace JSC
52
53#include "RegExpObject.lut.h"
54
55namespace JSC {
56
57ASSERT_HAS_TRIVIAL_DESTRUCTOR(RegExpObject);
58
59const ClassInfo RegExpObject::s_info = { "RegExp", &Base::s_info, 0, ExecState::regExpTable, CREATE_METHOD_TABLE(RegExpObject) };
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
67@end
68*/
69
70RegExpObject::RegExpObject(JSGlobalObject* globalObject, Structure* structure, RegExp* regExp)
71 : JSNonFinalObject(globalObject->vm(), structure)
72 , m_regExp(globalObject->vm(), this, regExp)
73 , m_lastIndexIsWritable(true)
74{
75 m_lastIndex.setWithoutWriteBarrier(jsNumber(0));
76}
77
78void RegExpObject::finishCreation(JSGlobalObject* globalObject)
79{
80 Base::finishCreation(globalObject->vm());
81 ASSERT(inherits(&s_info));
82}
83
84void RegExpObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
85{
86 RegExpObject* thisObject = jsCast<RegExpObject*>(cell);
87 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
88 COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
89 ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
90
91 Base::visitChildren(thisObject, visitor);
92 visitor.append(&thisObject->m_regExp);
93 visitor.append(&thisObject->m_lastIndex);
94}
95
96bool RegExpObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
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);
104}
105
106bool RegExpObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor)
107{
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
116bool RegExpObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName)
117{
118 if (propertyName == exec->propertyNames().lastIndex)
119 return false;
120 return Base::deleteProperty(cell, exec, propertyName);
121}
122
123void RegExpObject::getOwnNonIndexPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
124{
125 if (mode == IncludeDontEnumProperties)
126 propertyNames.add(exec->propertyNames().lastIndex);
127 Base::getOwnNonIndexPropertyNames(object, exec, propertyNames, mode);
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)
140 throwTypeError(exec, ASCIILiteral(message));
141 return false;
142}
143
144bool RegExpObject::defineOwnProperty(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor, bool shouldThrow)
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);
169}
170
171JSValue regExpObjectGlobal(ExecState*, JSValue slotBase, PropertyName)
172{
173 return jsBoolean(asRegExpObject(slotBase)->regExp()->global());
174}
175
176JSValue regExpObjectIgnoreCase(ExecState*, JSValue slotBase, PropertyName)
177{
178 return jsBoolean(asRegExpObject(slotBase)->regExp()->ignoreCase());
179}
180
181JSValue regExpObjectMultiline(ExecState*, JSValue slotBase, PropertyName)
182{
183 return jsBoolean(asRegExpObject(slotBase)->regExp()->multiline());
184}
185
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)
213{
214 bool previousCharacterWasBackslash = false;
215 bool inBrackets = false;
216 bool shouldEscape = false;
217
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)
224 return jsNontrivialString(exec, ASCIILiteral("(?:)"));
225
226 // early return for strings that don't contain a forwards slash and LineTerminator
227 for (unsigned i = 0; i < length; ++i) {
228 CharacterType ch = characters[i];
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 }
242
243 if (Lexer<CharacterType>::isLineTerminator(ch)) {
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;
259 StringBuilder result;
260 for (unsigned i = 0; i < length; ++i) {
261 CharacterType ch = characters[i];
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
275 if (Lexer<CharacterType>::isLineTerminator(ch)) {
276 if (!previousCharacterWasBackslash)
277 result.append('\\');
278
279 appendLineTerminatorEscape<CharacterType>(result, ch);
280 } else
281 result.append(ch);
282
283 if (previousCharacterWasBackslash)
284 previousCharacterWasBackslash = false;
285 else
286 previousCharacterWasBackslash = ch == '\\';
287 }
288
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());
298}
299
300void RegExpObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
301{
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);
307}
308
309JSValue RegExpObject::exec(ExecState* exec, JSString* string)
310{
311 if (MatchResult result = match(exec, string))
312 return RegExpMatchesArray::create(exec, string, regExp(), result);
313 return jsNull();
314}
315
316// Shared implementation used by test and exec.
317MatchResult RegExpObject::match(ExecState* exec, JSString* string)
318{
319 RegExp* regExp = this->regExp();
320 RegExpConstructor* regExpConstructor = exec->lexicalGlobalObject()->regExpConstructor();
321 String input = string->value(exec);
322 VM& vm = exec->vm();
323 if (!regExp->global())
324 return regExpConstructor->performMatch(vm, regExp, string, input, 0);
325
326 JSValue jsLastIndex = getLastIndex();
327 unsigned lastIndex;
328 if (LIKELY(jsLastIndex.isUInt32())) {
329 lastIndex = jsLastIndex.asUInt32();
330 if (lastIndex > input.length()) {
331 setLastIndex(exec, 0);
332 return MatchResult::failed();
333 }
334 } else {
335 double doubleLastIndex = jsLastIndex.toInteger(exec);
336 if (doubleLastIndex < 0 || doubleLastIndex > input.length()) {
337 setLastIndex(exec, 0);
338 return MatchResult::failed();
339 }
340 lastIndex = static_cast<unsigned>(doubleLastIndex);
341 }
342
343 MatchResult result = regExpConstructor->performMatch(vm, regExp, string, input, lastIndex);
344 setLastIndex(exec, result.end);
345 return result;
346}
347
348} // namespace JSC