1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten (at) 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 "ArrayPrototype.h" 26 #include "Error.h" 27 #include "JSArray.h" 28 #include "JSFunction.h" 29 #include "JSString.h" 30 #include "ObjectPrototype.h" 31 #include "RegExpMatchesArray.h" 32 #include "RegExpObject.h" 33 #include "RegExpPrototype.h" 34 #include "RegExp.h" 35 36 namespace JSC { 37 38 static JSValue regExpConstructorInput(ExecState*, const Identifier&, const PropertySlot&); 39 static JSValue regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot&); 40 static JSValue regExpConstructorLastMatch(ExecState*, const Identifier&, const PropertySlot&); 41 static JSValue regExpConstructorLastParen(ExecState*, const Identifier&, const PropertySlot&); 42 static JSValue regExpConstructorLeftContext(ExecState*, const Identifier&, const PropertySlot&); 43 static JSValue regExpConstructorRightContext(ExecState*, const Identifier&, const PropertySlot&); 44 static JSValue regExpConstructorDollar1(ExecState*, const Identifier&, const PropertySlot&); 45 static JSValue regExpConstructorDollar2(ExecState*, const Identifier&, const PropertySlot&); 46 static JSValue regExpConstructorDollar3(ExecState*, const Identifier&, const PropertySlot&); 47 static JSValue regExpConstructorDollar4(ExecState*, const Identifier&, const PropertySlot&); 48 static JSValue regExpConstructorDollar5(ExecState*, const Identifier&, const PropertySlot&); 49 static JSValue regExpConstructorDollar6(ExecState*, const Identifier&, const PropertySlot&); 50 static JSValue regExpConstructorDollar7(ExecState*, const Identifier&, const PropertySlot&); 51 static JSValue regExpConstructorDollar8(ExecState*, const Identifier&, const PropertySlot&); 52 static JSValue regExpConstructorDollar9(ExecState*, const Identifier&, const PropertySlot&); 53 54 static void setRegExpConstructorInput(ExecState*, JSObject*, JSValue); 55 static void setRegExpConstructorMultiline(ExecState*, JSObject*, JSValue); 56 57 } // namespace JSC 58 59 #include "RegExpConstructor.lut.h" 60 61 namespace JSC { 62 63 ASSERT_CLASS_FITS_IN_CELL(RegExpConstructor); 64 65 const ClassInfo RegExpConstructor::info = { "Function", &InternalFunction::info, 0, ExecState::regExpConstructorTable }; 66 67 /* Source for RegExpConstructor.lut.h 68 @begin regExpConstructorTable 69 input regExpConstructorInput None 70 $_ regExpConstructorInput DontEnum 71 multiline regExpConstructorMultiline None 72 $* regExpConstructorMultiline DontEnum 73 lastMatch regExpConstructorLastMatch DontDelete|ReadOnly 74 $& regExpConstructorLastMatch DontDelete|ReadOnly|DontEnum 75 lastParen regExpConstructorLastParen DontDelete|ReadOnly 76 $+ regExpConstructorLastParen DontDelete|ReadOnly|DontEnum 77 leftContext regExpConstructorLeftContext DontDelete|ReadOnly 78 $` regExpConstructorLeftContext DontDelete|ReadOnly|DontEnum 79 rightContext regExpConstructorRightContext DontDelete|ReadOnly 80 $' regExpConstructorRightContext DontDelete|ReadOnly|DontEnum 81 $1 regExpConstructorDollar1 DontDelete|ReadOnly 82 $2 regExpConstructorDollar2 DontDelete|ReadOnly 83 $3 regExpConstructorDollar3 DontDelete|ReadOnly 84 $4 regExpConstructorDollar4 DontDelete|ReadOnly 85 $5 regExpConstructorDollar5 DontDelete|ReadOnly 86 $6 regExpConstructorDollar6 DontDelete|ReadOnly 87 $7 regExpConstructorDollar7 DontDelete|ReadOnly 88 $8 regExpConstructorDollar8 DontDelete|ReadOnly 89 $9 regExpConstructorDollar9 DontDelete|ReadOnly 90 @end 91 */ 92 93 RegExpConstructor::RegExpConstructor(ExecState* exec, NonNullPassRefPtr<Structure> structure, RegExpPrototype* regExpPrototype) 94 : InternalFunction(&exec->globalData(), structure, Identifier(exec, "RegExp")) 95 , d(new RegExpConstructorPrivate) 96 { 97 // ECMA 15.10.5.1 RegExp.prototype 98 putDirectWithoutTransition(exec->propertyNames().prototype, regExpPrototype, DontEnum | DontDelete | ReadOnly); 99 100 // no. of arguments for constructor 101 putDirectWithoutTransition(exec->propertyNames().length, jsNumber(exec, 2), ReadOnly | DontDelete | DontEnum); 102 } 103 104 RegExpMatchesArray::RegExpMatchesArray(ExecState* exec, RegExpConstructorPrivate* data) 105 : JSArray(exec->lexicalGlobalObject()->regExpMatchesArrayStructure(), data->lastNumSubPatterns + 1) 106 { 107 RegExpConstructorPrivate* d = new RegExpConstructorPrivate; 108 d->input = data->lastInput; 109 d->lastInput = data->lastInput; 110 d->lastNumSubPatterns = data->lastNumSubPatterns; 111 unsigned offsetVectorSize = (data->lastNumSubPatterns + 1) * 2; // only copying the result part of the vector 112 d->lastOvector().resize(offsetVectorSize); 113 memcpy(d->lastOvector().data(), data->lastOvector().data(), offsetVectorSize * sizeof(int)); 114 // d->multiline is not needed, and remains uninitialized 115 116 setLazyCreationData(d); 117 } 118 119 RegExpMatchesArray::~RegExpMatchesArray() 120 { 121 delete static_cast<RegExpConstructorPrivate*>(lazyCreationData()); 122 } 123 124 void RegExpMatchesArray::fillArrayInstance(ExecState* exec) 125 { 126 RegExpConstructorPrivate* d = static_cast<RegExpConstructorPrivate*>(lazyCreationData()); 127 ASSERT(d); 128 129 unsigned lastNumSubpatterns = d->lastNumSubPatterns; 130 131 for (unsigned i = 0; i <= lastNumSubpatterns; ++i) { 132 int start = d->lastOvector()[2 * i]; 133 if (start >= 0) 134 JSArray::put(exec, i, jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start)); 135 else 136 JSArray::put(exec, i, jsUndefined()); 137 } 138 139 PutPropertySlot slot; 140 JSArray::put(exec, exec->propertyNames().index, jsNumber(exec, d->lastOvector()[0]), slot); 141 JSArray::put(exec, exec->propertyNames().input, jsString(exec, d->input), slot); 142 143 delete d; 144 setLazyCreationData(0); 145 } 146 147 JSObject* RegExpConstructor::arrayOfMatches(ExecState* exec) const 148 { 149 return new (exec) RegExpMatchesArray(exec, d.get()); 150 } 151 152 JSValue RegExpConstructor::getBackref(ExecState* exec, unsigned i) const 153 { 154 if (!d->lastOvector().isEmpty() && i <= d->lastNumSubPatterns) { 155 int start = d->lastOvector()[2 * i]; 156 if (start >= 0) 157 return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); 158 } 159 return jsEmptyString(exec); 160 } 161 162 JSValue RegExpConstructor::getLastParen(ExecState* exec) const 163 { 164 unsigned i = d->lastNumSubPatterns; 165 if (i > 0) { 166 ASSERT(!d->lastOvector().isEmpty()); 167 int start = d->lastOvector()[2 * i]; 168 if (start >= 0) 169 return jsSubstring(exec, d->lastInput, start, d->lastOvector()[2 * i + 1] - start); 170 } 171 return jsEmptyString(exec); 172 } 173 174 JSValue RegExpConstructor::getLeftContext(ExecState* exec) const 175 { 176 if (!d->lastOvector().isEmpty()) 177 return jsSubstring(exec, d->lastInput, 0, d->lastOvector()[0]); 178 return jsEmptyString(exec); 179 } 180 181 JSValue RegExpConstructor::getRightContext(ExecState* exec) const 182 { 183 if (!d->lastOvector().isEmpty()) 184 return jsSubstring(exec, d->lastInput, d->lastOvector()[1], d->lastInput.size() - d->lastOvector()[1]); 185 return jsEmptyString(exec); 186 } 187 188 bool RegExpConstructor::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) 189 { 190 return getStaticValueSlot<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, slot); 191 } 192 193 bool RegExpConstructor::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) 194 { 195 return getStaticValueDescriptor<RegExpConstructor, InternalFunction>(exec, ExecState::regExpConstructorTable(exec), this, propertyName, descriptor); 196 } 197 198 JSValue regExpConstructorDollar1(ExecState* exec, const Identifier&, const PropertySlot& slot) 199 { 200 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 1); 201 } 202 203 JSValue regExpConstructorDollar2(ExecState* exec, const Identifier&, const PropertySlot& slot) 204 { 205 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 2); 206 } 207 208 JSValue regExpConstructorDollar3(ExecState* exec, const Identifier&, const PropertySlot& slot) 209 { 210 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 3); 211 } 212 213 JSValue regExpConstructorDollar4(ExecState* exec, const Identifier&, const PropertySlot& slot) 214 { 215 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 4); 216 } 217 218 JSValue regExpConstructorDollar5(ExecState* exec, const Identifier&, const PropertySlot& slot) 219 { 220 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 5); 221 } 222 223 JSValue regExpConstructorDollar6(ExecState* exec, const Identifier&, const PropertySlot& slot) 224 { 225 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 6); 226 } 227 228 JSValue regExpConstructorDollar7(ExecState* exec, const Identifier&, const PropertySlot& slot) 229 { 230 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 7); 231 } 232 233 JSValue regExpConstructorDollar8(ExecState* exec, const Identifier&, const PropertySlot& slot) 234 { 235 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 8); 236 } 237 238 JSValue regExpConstructorDollar9(ExecState* exec, const Identifier&, const PropertySlot& slot) 239 { 240 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 9); 241 } 242 243 JSValue regExpConstructorInput(ExecState* exec, const Identifier&, const PropertySlot& slot) 244 { 245 return jsString(exec, asRegExpConstructor(slot.slotBase())->input()); 246 } 247 248 JSValue regExpConstructorMultiline(ExecState*, const Identifier&, const PropertySlot& slot) 249 { 250 return jsBoolean(asRegExpConstructor(slot.slotBase())->multiline()); 251 } 252 253 JSValue regExpConstructorLastMatch(ExecState* exec, const Identifier&, const PropertySlot& slot) 254 { 255 return asRegExpConstructor(slot.slotBase())->getBackref(exec, 0); 256 } 257 258 JSValue regExpConstructorLastParen(ExecState* exec, const Identifier&, const PropertySlot& slot) 259 { 260 return asRegExpConstructor(slot.slotBase())->getLastParen(exec); 261 } 262 263 JSValue regExpConstructorLeftContext(ExecState* exec, const Identifier&, const PropertySlot& slot) 264 { 265 return asRegExpConstructor(slot.slotBase())->getLeftContext(exec); 266 } 267 268 JSValue regExpConstructorRightContext(ExecState* exec, const Identifier&, const PropertySlot& slot) 269 { 270 return asRegExpConstructor(slot.slotBase())->getRightContext(exec); 271 } 272 273 void RegExpConstructor::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) 274 { 275 lookupPut<RegExpConstructor, InternalFunction>(exec, propertyName, value, ExecState::regExpConstructorTable(exec), this, slot); 276 } 277 278 void setRegExpConstructorInput(ExecState* exec, JSObject* baseObject, JSValue value) 279 { 280 asRegExpConstructor(baseObject)->setInput(value.toString(exec)); 281 } 282 283 void setRegExpConstructorMultiline(ExecState* exec, JSObject* baseObject, JSValue value) 284 { 285 asRegExpConstructor(baseObject)->setMultiline(value.toBoolean(exec)); 286 } 287 288 // ECMA 15.10.4 289 JSObject* constructRegExp(ExecState* exec, const ArgList& args) 290 { 291 JSValue arg0 = args.at(0); 292 JSValue arg1 = args.at(1); 293 294 if (arg0.inherits(&RegExpObject::info)) { 295 if (!arg1.isUndefined()) 296 return throwError(exec, TypeError, "Cannot supply flags when constructing one RegExp from another."); 297 return asObject(arg0); 298 } 299 300 UString pattern = arg0.isUndefined() ? UString("") : arg0.toString(exec); 301 UString flags = arg1.isUndefined() ? UString("") : arg1.toString(exec); 302 303 RefPtr<RegExp> regExp = RegExp::create(&exec->globalData(), pattern, flags); 304 if (!regExp->isValid()) 305 return throwError(exec, SyntaxError, makeString("Invalid regular expression: ", regExp->errorMessage())); 306 return new (exec) RegExpObject(exec->lexicalGlobalObject()->regExpStructure(), regExp.release()); 307 } 308 309 static JSObject* constructWithRegExpConstructor(ExecState* exec, JSObject*, const ArgList& args) 310 { 311 return constructRegExp(exec, args); 312 } 313 314 ConstructType RegExpConstructor::getConstructData(ConstructData& constructData) 315 { 316 constructData.native.function = constructWithRegExpConstructor; 317 return ConstructTypeHost; 318 } 319 320 // ECMA 15.10.3 321 static JSValue JSC_HOST_CALL callRegExpConstructor(ExecState* exec, JSObject*, JSValue, const ArgList& args) 322 { 323 return constructRegExp(exec, args); 324 } 325 326 CallType RegExpConstructor::getCallData(CallData& callData) 327 { 328 callData.native.function = callRegExpConstructor; 329 return CallTypeHost; 330 } 331 332 void RegExpConstructor::setInput(const UString& input) 333 { 334 d->input = input; 335 } 336 337 const UString& RegExpConstructor::input() const 338 { 339 // Can detect a distinct initial state that is invisible to JavaScript, by checking for null 340 // state (since jsString turns null strings to empty strings). 341 return d->input; 342 } 343 344 void RegExpConstructor::setMultiline(bool multiline) 345 { 346 d->multiline = multiline; 347 } 348 349 bool RegExpConstructor::multiline() const 350 { 351 return d->multiline; 352 } 353 354 } // namespace JSC 355