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 |