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 |
151 } // namespace internal | 395 } // namespace internal |
152 } // namespace v8 | 396 } // namespace v8 |
OLD | NEW |