| OLD | NEW |
| 1 // Copyright 2016 the V8 project authors. All rights reserved. | 1 // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/builtins/builtins.h" | 5 #include "src/builtins/builtins.h" |
| 6 #include "src/builtins/builtins-utils.h" | 6 #include "src/builtins/builtins-utils.h" |
| 7 | 7 |
| 8 #include "src/string-builder.h" | 8 #include "src/string-builder.h" |
| 9 | 9 |
| 10 namespace v8 { | 10 namespace v8 { |
| (...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 141 | 141 |
| 142 Handle<JSObject> object; | 142 Handle<JSObject> object; |
| 143 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | 143 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
| 144 isolate, object, JSObject::New(target, new_target_receiver)); | 144 isolate, object, JSObject::New(target, new_target_receiver)); |
| 145 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); | 145 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(object); |
| 146 | 146 |
| 147 RETURN_RESULT_OR_FAILURE(isolate, | 147 RETURN_RESULT_OR_FAILURE(isolate, |
| 148 RegExpInitialize(isolate, regexp, pattern, flags)); | 148 RegExpInitialize(isolate, regexp, pattern, flags)); |
| 149 } | 149 } |
| 150 | 150 |
| 151 #define APPEND_CHAR_FOR_FLAG(flag, c) \ | |
| 152 do { \ | |
| 153 Handle<Object> property; \ | |
| 154 Handle<Name> name = isolate->factory()->flag##_string(); \ | |
| 155 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, property, \ | |
| 156 JSReceiver::GetProperty(recv, name)); \ | |
| 157 if (property->BooleanValue()) { \ | |
| 158 builder.AppendCharacter(c); \ | |
| 159 } \ | |
| 160 } while (false); | |
| 161 | |
| 162 // ES6 21.2.5.3. | |
| 163 BUILTIN(RegExpPrototypeFlagsGetter) { | |
| 164 HandleScope scope(isolate); | |
| 165 CHECK_RECEIVER(JSReceiver, recv, "get RegExp.prototype.flags"); | |
| 166 | |
| 167 IncrementalStringBuilder builder(isolate); | |
| 168 | |
| 169 APPEND_CHAR_FOR_FLAG(global, 'g'); | |
| 170 APPEND_CHAR_FOR_FLAG(ignoreCase, 'i'); | |
| 171 APPEND_CHAR_FOR_FLAG(multiline, 'm'); | |
| 172 APPEND_CHAR_FOR_FLAG(unicode, 'u'); | |
| 173 APPEND_CHAR_FOR_FLAG(sticky, 'y'); | |
| 174 | |
| 175 RETURN_RESULT_OR_FAILURE(isolate, builder.Finish()); | |
| 176 } | |
| 177 | |
| 178 #undef APPEND_CHAR_FOR_FLAG | |
| 179 | |
| 180 // ES6 21.2.5.10. | |
| 181 BUILTIN(RegExpPrototypeSourceGetter) { | |
| 182 HandleScope scope(isolate); | |
| 183 | |
| 184 Handle<Object> recv = args.receiver(); | |
| 185 if (!recv->IsJSRegExp()) { | |
| 186 // TODO(littledan): Remove this RegExp compat workaround | |
| 187 Handle<JSFunction> regexp_fun = isolate->regexp_function(); | |
| 188 if (*recv == regexp_fun->prototype()) { | |
| 189 isolate->CountUsage(v8::Isolate::kRegExpPrototypeSourceGetter); | |
| 190 return *isolate->factory()->NewStringFromAsciiChecked("(?:)"); | |
| 191 } | |
| 192 THROW_NEW_ERROR_RETURN_FAILURE( | |
| 193 isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, | |
| 194 isolate->factory()->NewStringFromAsciiChecked( | |
| 195 "RegExp.prototype.source"))); | |
| 196 } | |
| 197 | |
| 198 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); | |
| 199 return regexp->source(); | |
| 200 } | |
| 201 | |
| 202 // ES6 21.2.4.2. | |
| 203 BUILTIN(RegExpPrototypeSpeciesGetter) { | |
| 204 HandleScope scope(isolate); | |
| 205 return *args.receiver(); | |
| 206 } | |
| 207 | |
| 208 #define REGEXP_FLAG_GETTER(name, counter, getter) \ | |
| 209 BUILTIN(RegExpPrototype##name##Getter) { \ | |
| 210 HandleScope scope(isolate); \ | |
| 211 Handle<Object> recv = args.receiver(); \ | |
| 212 if (!recv->IsJSRegExp()) { \ | |
| 213 /* TODO(littledan): Remove this RegExp compat workaround */ \ | |
| 214 Handle<JSFunction> regexp_fun = isolate->regexp_function(); \ | |
| 215 if (*recv == regexp_fun->prototype()) { \ | |
| 216 isolate->CountUsage(v8::Isolate::kRegExpPrototype##counter##Getter); \ | |
| 217 return isolate->heap()->undefined_value(); \ | |
| 218 } \ | |
| 219 THROW_NEW_ERROR_RETURN_FAILURE( \ | |
| 220 isolate, NewTypeError(MessageTemplate::kRegExpNonRegExp, \ | |
| 221 isolate->factory()->NewStringFromAsciiChecked( \ | |
| 222 getter))); \ | |
| 223 } \ | |
| 224 Handle<JSRegExp> regexp = Handle<JSRegExp>::cast(recv); \ | |
| 225 const bool ret = (regexp->GetFlags() & JSRegExp::k##name) != 0; \ | |
| 226 return *isolate->factory()->ToBoolean(ret); \ | |
| 227 } | |
| 228 | |
| 229 // ES6 21.2.5.4. | |
| 230 REGEXP_FLAG_GETTER(Global, OldFlag, "RegExp.prototype.global") | |
| 231 | |
| 232 // ES6 21.2.5.5. | |
| 233 REGEXP_FLAG_GETTER(IgnoreCase, OldFlag, "RegExp.prototype.ignoreCase") | |
| 234 | |
| 235 // ES6 21.2.5.7. | |
| 236 REGEXP_FLAG_GETTER(Multiline, OldFlag, "RegExp.prototype.multiline") | |
| 237 | |
| 238 // ES6 21.2.5.12. | |
| 239 REGEXP_FLAG_GETTER(Sticky, Sticky, "RegExp.prototype.sticky") | |
| 240 | |
| 241 // ES6 21.2.5.15. | |
| 242 REGEXP_FLAG_GETTER(Unicode, Unicode, "RegExp.prototype.unicode") | |
| 243 | |
| 244 #undef REGEXP_FLAG_GETTER | |
| 245 | |
| 246 namespace { | |
| 247 | |
| 248 // Constants for accessing RegExpLastMatchInfo. | |
| 249 // TODO(jgruber): Currently, RegExpLastMatchInfo is still a JSObject maintained | |
| 250 // and accessed from JS. This is a crutch until all RegExp logic is ported, then | |
| 251 // we can take care of RegExpLastMatchInfo. | |
| 252 const int kNumberOfCapturesIndex = 0; | |
| 253 const int kLastSubjectIndex = 1; | |
| 254 const int kLastInputIndex = 2; | |
| 255 const int kFirstCaptureIndex = 3; | |
| 256 | |
| 257 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { | |
| 258 Handle<JSFunction> global_regexp = isolate->regexp_function(); | |
| 259 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | |
| 260 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | |
| 261 | |
| 262 Handle<JSReceiver> last_match_info = | |
| 263 Handle<JSReceiver>::cast(last_match_info_obj); | |
| 264 return JSReceiver::GetElement(isolate, last_match_info, index) | |
| 265 .ToHandleChecked(); | |
| 266 } | |
| 267 | |
| 268 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { | |
| 269 Handle<JSFunction> global_regexp = isolate->regexp_function(); | |
| 270 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | |
| 271 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | |
| 272 | |
| 273 Handle<JSReceiver> last_match_info = | |
| 274 Handle<JSReceiver>::cast(last_match_info_obj); | |
| 275 JSReceiver::SetElement(isolate, last_match_info, index, value, SLOPPY) | |
| 276 .ToHandleChecked(); | |
| 277 } | |
| 278 | |
| 279 int GetLastMatchNumberOfCaptures(Isolate* isolate) { | |
| 280 Handle<Object> obj = GetLastMatchField(isolate, kNumberOfCapturesIndex); | |
| 281 return Handle<Smi>::cast(obj)->value(); | |
| 282 } | |
| 283 | |
| 284 Handle<String> GetLastMatchSubject(Isolate* isolate) { | |
| 285 return Handle<String>::cast(GetLastMatchField(isolate, kLastSubjectIndex)); | |
| 286 } | |
| 287 | |
| 288 Handle<Object> GetLastMatchInput(Isolate* isolate) { | |
| 289 return GetLastMatchField(isolate, kLastInputIndex); | |
| 290 } | |
| 291 | |
| 292 int GetLastMatchCapture(Isolate* isolate, int i) { | |
| 293 Handle<Object> obj = GetLastMatchField(isolate, kFirstCaptureIndex + i); | |
| 294 return Handle<Smi>::cast(obj)->value(); | |
| 295 } | |
| 296 | |
| 297 Object* GenericCaptureGetter(Isolate* isolate, int capture) { | |
| 298 HandleScope scope(isolate); | |
| 299 const int index = capture * 2; | |
| 300 if (index >= GetLastMatchNumberOfCaptures(isolate)) { | |
| 301 return isolate->heap()->empty_string(); | |
| 302 } | |
| 303 | |
| 304 const int match_start = GetLastMatchCapture(isolate, index); | |
| 305 const int match_end = GetLastMatchCapture(isolate, index + 1); | |
| 306 if (match_start == -1 || match_end == -1) { | |
| 307 return isolate->heap()->empty_string(); | |
| 308 } | |
| 309 | |
| 310 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 311 return *isolate->factory()->NewSubString(last_subject, match_start, | |
| 312 match_end); | |
| 313 } | |
| 314 | |
| 315 } // namespace | |
| 316 | |
| 317 // The properties $1..$9 are the first nine capturing substrings of the last | |
| 318 // successful match, or ''. The function RegExpMakeCaptureGetter will be | |
| 319 // called with indices from 1 to 9. | |
| 320 #define DEFINE_CAPTURE_GETTER(i) \ | |
| 321 BUILTIN(RegExpPrototypeCapture##i##Getter) { \ | |
| 322 HandleScope scope(isolate); \ | |
| 323 return GenericCaptureGetter(isolate, i); \ | |
| 324 } | |
| 325 DEFINE_CAPTURE_GETTER(1) | |
| 326 DEFINE_CAPTURE_GETTER(2) | |
| 327 DEFINE_CAPTURE_GETTER(3) | |
| 328 DEFINE_CAPTURE_GETTER(4) | |
| 329 DEFINE_CAPTURE_GETTER(5) | |
| 330 DEFINE_CAPTURE_GETTER(6) | |
| 331 DEFINE_CAPTURE_GETTER(7) | |
| 332 DEFINE_CAPTURE_GETTER(8) | |
| 333 DEFINE_CAPTURE_GETTER(9) | |
| 334 #undef DEFINE_CAPTURE_GETTER | |
| 335 | |
| 336 // The properties `input` and `$_` are aliases for each other. When this | |
| 337 // value is set the value it is set to is coerced to a string. | |
| 338 // Getter and setter for the input. | |
| 339 | |
| 340 BUILTIN(RegExpPrototypeInputGetter) { | |
| 341 HandleScope scope(isolate); | |
| 342 Handle<Object> obj = GetLastMatchInput(isolate); | |
| 343 return obj->IsUndefined(isolate) ? isolate->heap()->empty_string() | |
| 344 : String::cast(*obj); | |
| 345 } | |
| 346 | |
| 347 BUILTIN(RegExpPrototypeInputSetter) { | |
| 348 HandleScope scope(isolate); | |
| 349 Handle<Object> value = args.atOrUndefined(isolate, 1); | |
| 350 Handle<String> str; | |
| 351 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str, | |
| 352 Object::ToString(isolate, value)); | |
| 353 SetLastMatchField(isolate, kLastInputIndex, str); | |
| 354 return isolate->heap()->undefined_value(); | |
| 355 } | |
| 356 | |
| 357 // Getters for the static properties lastMatch, lastParen, leftContext, and | |
| 358 // rightContext of the RegExp constructor. The properties are computed based | |
| 359 // on the captures array of the last successful match and the subject string | |
| 360 // of the last successful match. | |
| 361 BUILTIN(RegExpPrototypeLastMatchGetter) { | |
| 362 HandleScope scope(isolate); | |
| 363 return GenericCaptureGetter(isolate, 0); | |
| 364 } | |
| 365 | |
| 366 BUILTIN(RegExpPrototypeLastParenGetter) { | |
| 367 HandleScope scope(isolate); | |
| 368 const int length = GetLastMatchNumberOfCaptures(isolate); | |
| 369 if (length <= 2) return isolate->heap()->empty_string(); // No captures. | |
| 370 | |
| 371 DCHECK_EQ(0, length % 2); | |
| 372 const int last_capture = (length / 2) - 1; | |
| 373 | |
| 374 // We match the SpiderMonkey behavior: return the substring defined by the | |
| 375 // last pair (after the first pair) of elements of the capture array even if | |
| 376 // it is empty. | |
| 377 return GenericCaptureGetter(isolate, last_capture); | |
| 378 } | |
| 379 | |
| 380 BUILTIN(RegExpPrototypeLeftContextGetter) { | |
| 381 HandleScope scope(isolate); | |
| 382 const int start_index = GetLastMatchCapture(isolate, 0); | |
| 383 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 384 return *isolate->factory()->NewSubString(last_subject, 0, start_index); | |
| 385 } | |
| 386 | |
| 387 BUILTIN(RegExpPrototypeRightContextGetter) { | |
| 388 HandleScope scope(isolate); | |
| 389 const int start_index = GetLastMatchCapture(isolate, 1); | |
| 390 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 391 const int len = last_subject->length(); | |
| 392 return *isolate->factory()->NewSubString(last_subject, start_index, len); | |
| 393 } | |
| 394 | |
| 395 } // namespace internal | 151 } // namespace internal |
| 396 } // namespace v8 | 152 } // namespace v8 |
| OLD | NEW |