 Chromium Code Reviews
 Chromium Code Reviews Issue 2305573002:
  [regexp] Port RegExp getters and setters  (Closed)
    
  
    Issue 2305573002:
  [regexp] Port RegExp getters and setters  (Closed) 
  | 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): Move to C++. | |
| 
Benedikt Meurer
2016/09/01 10:22:34
This is C++?
 
jgruber
2016/09/01 10:36:39
The comment is about moving the RegExpLastMatchInf
 | |
| 250 static const int kNumberOfCapturesIndex = 0; | |
| 
Benedikt Meurer
2016/09/01 10:22:33
Nit: You don't need static inside anonymous namesp
 
jgruber
2016/09/01 10:36:39
Done.
 | |
| 251 static const int kLastSubjectIndex = 1; | |
| 252 static const int kLastInputIndex = 2; | |
| 253 static const int kFirstCaptureIndex = 3; | |
| 254 | |
| 255 Handle<Object> GetLastMatchField(Isolate* isolate, int index) { | |
| 256 Handle<JSFunction> global_regexp = isolate->regexp_function(); | |
| 257 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | |
| 258 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | |
| 259 | |
| 260 Handle<JSReceiver> last_match_info = | |
| 261 Handle<JSReceiver>::cast(last_match_info_obj); | |
| 262 return JSReceiver::GetElement(isolate, last_match_info, index) | |
| 263 .ToHandleChecked(); | |
| 264 } | |
| 265 | |
| 266 void SetLastMatchField(Isolate* isolate, int index, Handle<Object> value) { | |
| 267 Handle<JSFunction> global_regexp = isolate->regexp_function(); | |
| 268 Handle<Object> last_match_info_obj = JSReceiver::GetDataProperty( | |
| 269 global_regexp, isolate->factory()->regexp_last_match_info_symbol()); | |
| 270 | |
| 271 Handle<JSReceiver> last_match_info = | |
| 272 Handle<JSReceiver>::cast(last_match_info_obj); | |
| 273 JSReceiver::SetElement(isolate, last_match_info, index, value, SLOPPY) | |
| 274 .ToHandleChecked(); | |
| 275 } | |
| 276 | |
| 277 int GetLastMatchNumberOfCaptures(Isolate* isolate) { | |
| 278 Handle<Object> obj = GetLastMatchField(isolate, kNumberOfCapturesIndex); | |
| 279 return Handle<Smi>::cast(obj)->value(); | |
| 280 } | |
| 281 | |
| 282 Handle<String> GetLastMatchSubject(Isolate* isolate) { | |
| 283 return Handle<String>::cast(GetLastMatchField(isolate, kLastSubjectIndex)); | |
| 284 } | |
| 285 | |
| 286 Handle<Object> GetLastMatchInput(Isolate* isolate) { | |
| 287 return GetLastMatchField(isolate, kLastInputIndex); | |
| 288 } | |
| 289 | |
| 290 int GetLastMatchCapture(Isolate* isolate, int i) { | |
| 291 Handle<Object> obj = GetLastMatchField(isolate, kFirstCaptureIndex + i); | |
| 292 return Handle<Smi>::cast(obj)->value(); | |
| 293 } | |
| 294 | |
| 295 Object* GenericCaptureGetter(Isolate* isolate, int capture) { | |
| 296 HandleScope scope(isolate); | |
| 297 const int index = capture * 2; | |
| 298 if (index >= GetLastMatchNumberOfCaptures(isolate)) { | |
| 299 return isolate->heap()->empty_string(); | |
| 300 } | |
| 301 | |
| 302 const int match_start = GetLastMatchCapture(isolate, index); | |
| 303 const int match_end = GetLastMatchCapture(isolate, index + 1); | |
| 304 if (match_start == -1 || match_end == -1) { | |
| 305 return isolate->heap()->empty_string(); | |
| 306 } | |
| 307 | |
| 308 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 309 return *isolate->factory()->NewSubString(last_subject, match_start, | |
| 310 match_end); | |
| 311 } | |
| 312 | |
| 313 } // namespace | |
| 314 | |
| 315 // The properties $1..$9 are the first nine capturing substrings of the last | |
| 316 // successful match, or ''. The function RegExpMakeCaptureGetter will be | |
| 317 // called with indices from 1 to 9. | |
| 318 #define DEFINE_CAPTURE_GETTER(i) \ | |
| 319 BUILTIN(RegExpPrototypeCapture##i##Getter) { \ | |
| 320 HandleScope scope(isolate); \ | |
| 321 return GenericCaptureGetter(isolate, i); \ | |
| 322 } | |
| 323 DEFINE_CAPTURE_GETTER(1) | |
| 324 DEFINE_CAPTURE_GETTER(2) | |
| 325 DEFINE_CAPTURE_GETTER(3) | |
| 326 DEFINE_CAPTURE_GETTER(4) | |
| 327 DEFINE_CAPTURE_GETTER(5) | |
| 328 DEFINE_CAPTURE_GETTER(6) | |
| 329 DEFINE_CAPTURE_GETTER(7) | |
| 330 DEFINE_CAPTURE_GETTER(8) | |
| 331 DEFINE_CAPTURE_GETTER(9) | |
| 332 #undef DEFINE_CAPTURE_GETTER | |
| 333 | |
| 334 // The properties `input` and `$_` are aliases for each other. When this | |
| 335 // value is set the value it is set to is coerced to a string. | |
| 336 // Getter and setter for the input. | |
| 337 | |
| 338 BUILTIN(RegExpPrototypeInputGetter) { | |
| 339 HandleScope scope(isolate); | |
| 340 Handle<Object> obj = GetLastMatchInput(isolate); | |
| 341 return obj->IsUndefined(isolate) ? isolate->heap()->empty_string() | |
| 342 : String::cast(*obj); | |
| 343 } | |
| 344 | |
| 345 BUILTIN(RegExpPrototypeInputSetter) { | |
| 346 HandleScope scope(isolate); | |
| 347 Handle<Object> value = args.atOrUndefined(isolate, 1); | |
| 348 Handle<String> str; | |
| 349 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, str, | |
| 350 Object::ToString(isolate, value)); | |
| 351 SetLastMatchField(isolate, kLastInputIndex, str); | |
| 352 return isolate->heap()->undefined_value(); | |
| 353 } | |
| 354 | |
| 355 // Getters for the static properties lastMatch, lastParen, leftContext, and | |
| 356 // rightContext of the RegExp constructor. The properties are computed based | |
| 357 // on the captures array of the last successful match and the subject string | |
| 358 // of the last successful match. | |
| 359 BUILTIN(RegExpPrototypeLastMatchGetter) { | |
| 360 HandleScope scope(isolate); | |
| 361 return GenericCaptureGetter(isolate, 0); | |
| 362 } | |
| 363 | |
| 364 BUILTIN(RegExpPrototypeLastParenGetter) { | |
| 365 HandleScope scope(isolate); | |
| 366 const int length = GetLastMatchNumberOfCaptures(isolate); | |
| 367 if (length <= 2) return isolate->heap()->empty_string(); // No captures. | |
| 368 | |
| 369 DCHECK_EQ(0, length % 2); | |
| 370 const int last_capture = (length / 2) - 1; | |
| 371 | |
| 372 // We match the SpiderMonkey behavior: return the substring defined by the | |
| 373 // last pair (after the first pair) of elements of the capture array even if | |
| 374 // it is empty. | |
| 375 return GenericCaptureGetter(isolate, last_capture); | |
| 376 } | |
| 377 | |
| 378 BUILTIN(RegExpPrototypeLeftContextGetter) { | |
| 379 HandleScope scope(isolate); | |
| 380 const int start_index = GetLastMatchCapture(isolate, 0); | |
| 381 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 382 return *isolate->factory()->NewSubString(last_subject, 0, start_index); | |
| 383 } | |
| 384 | |
| 385 BUILTIN(RegExpPrototypeRightContextGetter) { | |
| 386 HandleScope scope(isolate); | |
| 387 const int start_index = GetLastMatchCapture(isolate, 1); | |
| 388 Handle<String> last_subject = GetLastMatchSubject(isolate); | |
| 389 const int len = last_subject->length(); | |
| 390 return *isolate->factory()->NewSubString(last_subject, start_index, len); | |
| 391 } | |
| 392 | |
| 151 } // namespace internal | 393 } // namespace internal | 
| 152 } // namespace v8 | 394 } // namespace v8 | 
| OLD | NEW |