1/*
2 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 *  Copyright (C) 2003, 2007, 2008 Apple Inc. All Rights Reserved.
4 *  Copyright (C) 2009 Torch Mobile, Inc.
5 *
6 *  This library is free software; you can redistribute it and/or
7 *  modify it under the terms of the GNU Lesser General Public
8 *  License as published by the Free Software Foundation; either
9 *  version 2 of the License, or (at your option) any later version.
10 *
11 *  This library is distributed in the hope that it will be useful,
12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 *  Lesser General Public License for more details.
15 *
16 *  You should have received a copy of the GNU Lesser General Public
17 *  License along with this library; if not, write to the Free Software
18 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19 *
20 */
21
22#include "config.h"
23#include "RegExpConstructor.h"
24
25#include "Error.h"
26#include "JSCInlines.h"
27#include "RegExpMatchesArray.h"
28#include "RegExpPrototype.h"
29
30namespace JSC {
31
32static EncodedJSValue regExpConstructorInput(ExecState*, JSObject*, EncodedJSValue, PropertyName);
33static EncodedJSValue regExpConstructorMultiline(ExecState*, JSObject*, EncodedJSValue, PropertyName);
34static EncodedJSValue regExpConstructorLastMatch(ExecState*, JSObject*, EncodedJSValue, PropertyName);
35static EncodedJSValue regExpConstructorLastParen(ExecState*, JSObject*, EncodedJSValue, PropertyName);
36static EncodedJSValue regExpConstructorLeftContext(ExecState*, JSObject*, EncodedJSValue, PropertyName);
37static EncodedJSValue regExpConstructorRightContext(ExecState*, JSObject*, EncodedJSValue, PropertyName);
38static EncodedJSValue regExpConstructorDollar1(ExecState*, JSObject*, EncodedJSValue, PropertyName);
39static EncodedJSValue regExpConstructorDollar2(ExecState*, JSObject*, EncodedJSValue, PropertyName);
40static EncodedJSValue regExpConstructorDollar3(ExecState*, JSObject*, EncodedJSValue, PropertyName);
41static EncodedJSValue regExpConstructorDollar4(ExecState*, JSObject*, EncodedJSValue, PropertyName);
42static EncodedJSValue regExpConstructorDollar5(ExecState*, JSObject*, EncodedJSValue, PropertyName);
43static EncodedJSValue regExpConstructorDollar6(ExecState*, JSObject*, EncodedJSValue, PropertyName);
44static EncodedJSValue regExpConstructorDollar7(ExecState*, JSObject*, EncodedJSValue, PropertyName);
45static EncodedJSValue regExpConstructorDollar8(ExecState*, JSObject*, EncodedJSValue, PropertyName);
46static EncodedJSValue regExpConstructorDollar9(ExecState*, JSObject*, EncodedJSValue, PropertyName);
47
48static void setRegExpConstructorInput(ExecState*, JSObject*, EncodedJSValue, EncodedJSValue);
49static void setRegExpConstructorMultiline(ExecState*, JSObject*, EncodedJSValue, EncodedJSValue);
50
51} // namespace JSC
52
53#include "RegExpConstructor.lut.h"
54
55namespace JSC {
56
57const ClassInfo RegExpConstructor::s_info = { "Function", &InternalFunction::s_info, 0, ExecState::regExpConstructorTable, CREATE_METHOD_TABLE(RegExpConstructor) };
58
59/* Source for RegExpConstructor.lut.h
60@begin regExpConstructorTable
61    input           regExpConstructorInput          None
62    $_              regExpConstructorInput          DontEnum
63    multiline       regExpConstructorMultiline      None
64    $*              regExpConstructorMultiline      DontEnum
65    lastMatch       regExpConstructorLastMatch      DontDelete|ReadOnly
66    $&              regExpConstructorLastMatch      DontDelete|ReadOnly|DontEnum
67    lastParen       regExpConstructorLastParen      DontDelete|ReadOnly
68    $+              regExpConstructorLastParen      DontDelete|ReadOnly|DontEnum
69    leftContext     regExpConstructorLeftContext    DontDelete|ReadOnly
70    $`              regExpConstructorLeftContext    DontDelete|ReadOnly|DontEnum
71    rightContext    regExpConstructorRightContext   DontDelete|ReadOnly
72    $'              regExpConstructorRightContext   DontDelete|ReadOnly|DontEnum
73    $1              regExpConstructorDollar1        DontDelete|ReadOnly
74    $2              regExpConstructorDollar2        DontDelete|ReadOnly
75    $3              regExpConstructorDollar3        DontDelete|ReadOnly
76    $4              regExpConstructorDollar4        DontDelete|ReadOnly
77    $5              regExpConstructorDollar5        DontDelete|ReadOnly
78    $6              regExpConstructorDollar6        DontDelete|ReadOnly
79    $7              regExpConstructorDollar7        DontDelete|ReadOnly
80    $8              regExpConstructorDollar8        DontDelete|ReadOnly
81    $9              regExpConstructorDollar9        DontDelete|ReadOnly
82@end
83*/
84
85RegExpConstructor::RegExpConstructor(VM& vm, Structure* structure, RegExpPrototype* regExpPrototype)
86    : InternalFunction(vm, structure)
87    , m_cachedResult(vm, this, regExpPrototype->regExp())
88    , m_multiline(false)
89{
90}
91
92void RegExpConstructor::finishCreation(VM& vm, RegExpPrototype* regExpPrototype)
93{
94    Base::finishCreation(vm, Identifier(&vm, "RegExp").string());
95    ASSERT(inherits(info()));
96
97    // ECMA 15.10.5.1 RegExp.prototype
98    putDirectWithoutTransition(vm, vm.propertyNames->prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly);
99
100    // no. of arguments for constructor
101    putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(2), ReadOnly | DontDelete | DontEnum);
102}
103
104void RegExpConstructor::destroy(JSCell* cell)
105{
106    static_cast<RegExpConstructor*>(cell)->RegExpConstructor::~RegExpConstructor();
107}
108
109void RegExpConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor)
110{
111    RegExpConstructor* thisObject = jsCast<RegExpConstructor*>(cell);
112    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
113    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
114    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());
115
116    Base::visitChildren(thisObject, visitor);
117    thisObject->m_cachedResult.visitChildren(visitor);
118}
119
120JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i)
121{
122    RegExpMatchesArray* array = m_cachedResult.lastResult(exec, this);
123
124    if (i < array->length()) {
125        JSValue result = JSValue(array).get(exec, i);
126        ASSERT(result.isString() || result.isUndefined());
127        if (!result.isUndefined())
128            return result;
129    }
130    return jsEmptyString(exec);
131}
132
133JSValue RegExpConstructor::getLastParen(ExecState* exec)
134{
135    RegExpMatchesArray* array = m_cachedResult.lastResult(exec, this);
136    unsigned length = array->length();
137    if (length > 1) {
138        JSValue result = JSValue(array).get(exec, length - 1);
139        ASSERT(result.isString() || result.isUndefined());
140        if (!result.isUndefined())
141            return result;
142    }
143    return jsEmptyString(exec);
144}
145
146JSValue RegExpConstructor::getLeftContext(ExecState* exec)
147{
148    return m_cachedResult.lastResult(exec, this)->leftContext(exec);
149}
150
151JSValue RegExpConstructor::getRightContext(ExecState* exec)
152{
153    return m_cachedResult.lastResult(exec, this)->rightContext(exec);
154}
155
156bool RegExpConstructor::getOwnPropertySlot(JSObject* object, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
157{
158    return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec->vm()), jsCast<RegExpConstructor*>(object), propertyName, slot);
159}
160
161EncodedJSValue regExpConstructorDollar1(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
162{
163    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 1));
164}
165
166EncodedJSValue regExpConstructorDollar2(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
167{
168    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 2));
169}
170
171EncodedJSValue regExpConstructorDollar3(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
172{
173    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 3));
174}
175
176EncodedJSValue regExpConstructorDollar4(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
177{
178    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 4));
179}
180
181EncodedJSValue regExpConstructorDollar5(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
182{
183    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 5));
184}
185
186EncodedJSValue regExpConstructorDollar6(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
187{
188    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 6));
189}
190
191EncodedJSValue regExpConstructorDollar7(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
192{
193    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 7));
194}
195
196EncodedJSValue regExpConstructorDollar8(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
197{
198    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 8));
199}
200
201EncodedJSValue regExpConstructorDollar9(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
202{
203    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 9));
204}
205
206EncodedJSValue regExpConstructorInput(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName)
207{
208    return JSValue::encode(asRegExpConstructor(slotBase)->input());
209}
210
211EncodedJSValue regExpConstructorMultiline(ExecState*, JSObject* slotBase, EncodedJSValue, PropertyName)
212{
213    return JSValue::encode(jsBoolean(asRegExpConstructor(slotBase)->multiline()));
214}
215
216EncodedJSValue regExpConstructorLastMatch(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
217{
218    return JSValue::encode(asRegExpConstructor(slotBase)->getBackref(exec, 0));
219}
220
221EncodedJSValue regExpConstructorLastParen(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
222{
223    return JSValue::encode(asRegExpConstructor(slotBase)->getLastParen(exec));
224}
225
226EncodedJSValue regExpConstructorLeftContext(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
227{
228    return JSValue::encode(asRegExpConstructor(slotBase)->getLeftContext(exec));
229}
230
231EncodedJSValue regExpConstructorRightContext(ExecState* exec, JSObject* slotBase, EncodedJSValue, PropertyName)
232{
233    return JSValue::encode(asRegExpConstructor(slotBase)->getRightContext(exec));
234}
235
236void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, EncodedJSValue, EncodedJSValue value)
237{
238    if (auto constructor = jsDynamicCast<RegExpConstructor*>(baseObject))
239        constructor->setInput(exec, JSValue::decode(value).toString(exec));
240}
241
242void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, EncodedJSValue, EncodedJSValue value)
243{
244    if (auto constructor = jsDynamicCast<RegExpConstructor*>(baseObject))
245        constructor->setMultiline(JSValue::decode(value).toBoolean(exec));
246}
247
248// ECMA 15.10.4
249JSObject* constructRegExp(ExecState* exec, JSGlobalObject* globalObject, const ArgList& args, bool callAsConstructor)
250{
251    JSValue arg0 = args.at(0);
252    JSValue arg1 = args.at(1);
253
254    if (arg0.inherits(RegExpObject::info())) {
255        if (!arg1.isUndefined())
256            return exec->vm().throwException(exec, createTypeError(exec, ASCIILiteral("Cannot supply flags when constructing one RegExp from another.")));
257        // If called as a function, this just returns the first argument (see 15.10.3.1).
258        if (callAsConstructor) {
259            RegExp* regExp = static_cast<RegExpObject*>(asObject(arg0))->regExp();
260            return RegExpObject::create(exec->vm(), globalObject->regExpStructure(), regExp);
261        }
262        return asObject(arg0);
263    }
264
265    String pattern = arg0.isUndefined() ? emptyString() : arg0.toString(exec)->value(exec);
266    if (exec->hadException())
267        return 0;
268
269    RegExpFlags flags = NoFlags;
270    if (!arg1.isUndefined()) {
271        flags = regExpFlags(arg1.toString(exec)->value(exec));
272        if (exec->hadException())
273            return 0;
274        if (flags == InvalidFlags)
275            return exec->vm().throwException(exec, createSyntaxError(exec, ASCIILiteral("Invalid flags supplied to RegExp constructor.")));
276    }
277
278    VM& vm = exec->vm();
279    RegExp* regExp = RegExp::create(vm, pattern, flags);
280    if (!regExp->isValid())
281        return vm.throwException(exec, createSyntaxError(exec, regExp->errorMessage()));
282    return RegExpObject::create(vm, globalObject->regExpStructure(), regExp);
283}
284
285static EncodedJSValue JSC_HOST_CALL constructWithRegExpConstructor(ExecState* exec)
286{
287    ArgList args(exec);
288    return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args, true));
289}
290
291ConstructType RegExpConstructor::getConstructData(JSCell*, ConstructData& constructData)
292{
293    constructData.native.function = constructWithRegExpConstructor;
294    return ConstructTypeHost;
295}
296
297// ECMA 15.10.3
298static EncodedJSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec)
299{
300    ArgList args(exec);
301    return JSValue::encode(constructRegExp(exec, asInternalFunction(exec->callee())->globalObject(), args));
302}
303
304CallType RegExpConstructor::getCallData(JSCell*, CallData& callData)
305{
306    callData.native.function = callRegExpConstructor;
307    return CallTypeHost;
308}
309
310} // namespace JSC
311