OLD | NEW |
1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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 <stdlib.h> | 5 #include <stdlib.h> |
6 #include <limits> | 6 #include <limits> |
7 | 7 |
8 #include "src/v8.h" | 8 #include "src/v8.h" |
9 | 9 |
10 #include "src/accessors.h" | 10 #include "src/accessors.h" |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "src/conversions.h" | 21 #include "src/conversions.h" |
22 #include "src/cpu-profiler.h" | 22 #include "src/cpu-profiler.h" |
23 #include "src/date.h" | 23 #include "src/date.h" |
24 #include "src/dateparser-inl.h" | 24 #include "src/dateparser-inl.h" |
25 #include "src/debug.h" | 25 #include "src/debug.h" |
26 #include "src/deoptimizer.h" | 26 #include "src/deoptimizer.h" |
27 #include "src/execution.h" | 27 #include "src/execution.h" |
28 #include "src/full-codegen.h" | 28 #include "src/full-codegen.h" |
29 #include "src/global-handles.h" | 29 #include "src/global-handles.h" |
30 #include "src/isolate-inl.h" | 30 #include "src/isolate-inl.h" |
31 #include "src/json-parser.h" | |
32 #include "src/json-stringifier.h" | |
33 #include "src/jsregexp-inl.h" | |
34 #include "src/jsregexp.h" | |
35 #include "src/liveedit.h" | 31 #include "src/liveedit.h" |
36 #include "src/misc-intrinsics.h" | 32 #include "src/misc-intrinsics.h" |
37 #include "src/parser.h" | 33 #include "src/parser.h" |
38 #include "src/prototype.h" | 34 #include "src/prototype.h" |
39 #include "src/runtime/runtime.h" | 35 #include "src/runtime/runtime.h" |
40 #include "src/runtime/runtime-utils.h" | 36 #include "src/runtime/runtime-utils.h" |
41 #include "src/runtime-profiler.h" | 37 #include "src/runtime-profiler.h" |
42 #include "src/scopeinfo.h" | 38 #include "src/scopeinfo.h" |
43 #include "src/smart-pointers.h" | 39 #include "src/smart-pointers.h" |
44 #include "src/string-search.h" | |
45 #include "src/uri.h" | |
46 #include "src/utils.h" | 40 #include "src/utils.h" |
47 #include "src/v8threads.h" | 41 #include "src/v8threads.h" |
48 #include "src/vm-state-inl.h" | 42 #include "src/vm-state-inl.h" |
49 #include "third_party/fdlibm/fdlibm.h" | 43 #include "third_party/fdlibm/fdlibm.h" |
50 | 44 |
51 | 45 |
52 #ifndef _STLP_VENDOR_CSTD | 46 #ifndef _STLP_VENDOR_CSTD |
53 // STLPort doesn't import fpclassify and isless into the std namespace. | 47 // STLPort doesn't import fpclassify and isless into the std namespace. |
54 using std::fpclassify; | 48 using std::fpclassify; |
55 using std::isless; | 49 using std::isless; |
(...skipping 1926 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1982 if (obj->IsJSGlobalProxy()) { | 1976 if (obj->IsJSGlobalProxy()) { |
1983 PrototypeIterator iter(isolate, obj); | 1977 PrototypeIterator iter(isolate, obj); |
1984 if (iter.IsAtEnd()) return isolate->heap()->false_value(); | 1978 if (iter.IsAtEnd()) return isolate->heap()->false_value(); |
1985 DCHECK(iter.GetCurrent()->IsJSGlobalObject()); | 1979 DCHECK(iter.GetCurrent()->IsJSGlobalObject()); |
1986 obj = JSObject::cast(iter.GetCurrent()); | 1980 obj = JSObject::cast(iter.GetCurrent()); |
1987 } | 1981 } |
1988 return isolate->heap()->ToBoolean(obj->map()->is_extensible()); | 1982 return isolate->heap()->ToBoolean(obj->map()->is_extensible()); |
1989 } | 1983 } |
1990 | 1984 |
1991 | 1985 |
1992 RUNTIME_FUNCTION(Runtime_RegExpCompile) { | |
1993 HandleScope scope(isolate); | |
1994 DCHECK(args.length() == 3); | |
1995 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, re, 0); | |
1996 CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1); | |
1997 CONVERT_ARG_HANDLE_CHECKED(String, flags, 2); | |
1998 Handle<Object> result; | |
1999 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, | |
2000 RegExpImpl::Compile(re, pattern, flags)); | |
2001 return *result; | |
2002 } | |
2003 | |
2004 | |
2005 RUNTIME_FUNCTION(Runtime_CreateApiFunction) { | 1986 RUNTIME_FUNCTION(Runtime_CreateApiFunction) { |
2006 HandleScope scope(isolate); | 1987 HandleScope scope(isolate); |
2007 DCHECK(args.length() == 2); | 1988 DCHECK(args.length() == 2); |
2008 CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0); | 1989 CONVERT_ARG_HANDLE_CHECKED(FunctionTemplateInfo, data, 0); |
2009 CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); | 1990 CONVERT_ARG_HANDLE_CHECKED(Object, prototype, 1); |
2010 return *isolate->factory()->CreateApiFunction(data, prototype); | 1991 return *isolate->factory()->CreateApiFunction(data, prototype); |
2011 } | 1992 } |
2012 | 1993 |
2013 | 1994 |
2014 RUNTIME_FUNCTION(Runtime_IsTemplate) { | 1995 RUNTIME_FUNCTION(Runtime_IsTemplate) { |
(...skipping 378 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2393 CONVERT_SMI_ARG_CHECKED(properties, 1); | 2374 CONVERT_SMI_ARG_CHECKED(properties, 1); |
2394 // Conservative upper limit to prevent fuzz tests from going OOM. | 2375 // Conservative upper limit to prevent fuzz tests from going OOM. |
2395 RUNTIME_ASSERT(properties <= 100000); | 2376 RUNTIME_ASSERT(properties <= 100000); |
2396 if (object->HasFastProperties() && !object->IsJSGlobalProxy()) { | 2377 if (object->HasFastProperties() && !object->IsJSGlobalProxy()) { |
2397 JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); | 2378 JSObject::NormalizeProperties(object, KEEP_INOBJECT_PROPERTIES, properties); |
2398 } | 2379 } |
2399 return *object; | 2380 return *object; |
2400 } | 2381 } |
2401 | 2382 |
2402 | 2383 |
2403 RUNTIME_FUNCTION(Runtime_RegExpExecRT) { | |
2404 HandleScope scope(isolate); | |
2405 DCHECK(args.length() == 4); | |
2406 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); | |
2407 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); | |
2408 CONVERT_INT32_ARG_CHECKED(index, 2); | |
2409 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); | |
2410 // Due to the way the JS calls are constructed this must be less than the | |
2411 // length of a string, i.e. it is always a Smi. We check anyway for security. | |
2412 RUNTIME_ASSERT(index >= 0); | |
2413 RUNTIME_ASSERT(index <= subject->length()); | |
2414 isolate->counters()->regexp_entry_runtime()->Increment(); | |
2415 Handle<Object> result; | |
2416 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
2417 isolate, result, | |
2418 RegExpImpl::Exec(regexp, subject, index, last_match_info)); | |
2419 return *result; | |
2420 } | |
2421 | |
2422 | |
2423 RUNTIME_FUNCTION(Runtime_RegExpConstructResult) { | |
2424 HandleScope handle_scope(isolate); | |
2425 DCHECK(args.length() == 3); | |
2426 CONVERT_SMI_ARG_CHECKED(size, 0); | |
2427 RUNTIME_ASSERT(size >= 0 && size <= FixedArray::kMaxLength); | |
2428 CONVERT_ARG_HANDLE_CHECKED(Object, index, 1); | |
2429 CONVERT_ARG_HANDLE_CHECKED(Object, input, 2); | |
2430 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(size); | |
2431 Handle<Map> regexp_map(isolate->native_context()->regexp_result_map()); | |
2432 Handle<JSObject> object = | |
2433 isolate->factory()->NewJSObjectFromMap(regexp_map, NOT_TENURED, false); | |
2434 Handle<JSArray> array = Handle<JSArray>::cast(object); | |
2435 array->set_elements(*elements); | |
2436 array->set_length(Smi::FromInt(size)); | |
2437 // Write in-object properties after the length of the array. | |
2438 array->InObjectPropertyAtPut(JSRegExpResult::kIndexIndex, *index); | |
2439 array->InObjectPropertyAtPut(JSRegExpResult::kInputIndex, *input); | |
2440 return *array; | |
2441 } | |
2442 | |
2443 | |
2444 RUNTIME_FUNCTION(Runtime_RegExpInitializeObject) { | |
2445 HandleScope scope(isolate); | |
2446 DCHECK(args.length() == 6); | |
2447 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); | |
2448 CONVERT_ARG_HANDLE_CHECKED(String, source, 1); | |
2449 // If source is the empty string we set it to "(?:)" instead as | |
2450 // suggested by ECMA-262, 5th, section 15.10.4.1. | |
2451 if (source->length() == 0) source = isolate->factory()->query_colon_string(); | |
2452 | |
2453 CONVERT_ARG_HANDLE_CHECKED(Object, global, 2); | |
2454 if (!global->IsTrue()) global = isolate->factory()->false_value(); | |
2455 | |
2456 CONVERT_ARG_HANDLE_CHECKED(Object, ignoreCase, 3); | |
2457 if (!ignoreCase->IsTrue()) ignoreCase = isolate->factory()->false_value(); | |
2458 | |
2459 CONVERT_ARG_HANDLE_CHECKED(Object, multiline, 4); | |
2460 if (!multiline->IsTrue()) multiline = isolate->factory()->false_value(); | |
2461 | |
2462 CONVERT_ARG_HANDLE_CHECKED(Object, sticky, 5); | |
2463 if (!sticky->IsTrue()) sticky = isolate->factory()->false_value(); | |
2464 | |
2465 Map* map = regexp->map(); | |
2466 Object* constructor = map->constructor(); | |
2467 if (!FLAG_harmony_regexps && constructor->IsJSFunction() && | |
2468 JSFunction::cast(constructor)->initial_map() == map) { | |
2469 // If we still have the original map, set in-object properties directly. | |
2470 regexp->InObjectPropertyAtPut(JSRegExp::kSourceFieldIndex, *source); | |
2471 // Both true and false are immovable immortal objects so no need for write | |
2472 // barrier. | |
2473 regexp->InObjectPropertyAtPut(JSRegExp::kGlobalFieldIndex, *global, | |
2474 SKIP_WRITE_BARRIER); | |
2475 regexp->InObjectPropertyAtPut(JSRegExp::kIgnoreCaseFieldIndex, *ignoreCase, | |
2476 SKIP_WRITE_BARRIER); | |
2477 regexp->InObjectPropertyAtPut(JSRegExp::kMultilineFieldIndex, *multiline, | |
2478 SKIP_WRITE_BARRIER); | |
2479 regexp->InObjectPropertyAtPut(JSRegExp::kLastIndexFieldIndex, | |
2480 Smi::FromInt(0), SKIP_WRITE_BARRIER); | |
2481 return *regexp; | |
2482 } | |
2483 | |
2484 // Map has changed, so use generic, but slower, method. We also end here if | |
2485 // the --harmony-regexp flag is set, because the initial map does not have | |
2486 // space for the 'sticky' flag, since it is from the snapshot, but must work | |
2487 // both with and without --harmony-regexp. When sticky comes out from under | |
2488 // the flag, we will be able to use the fast initial map. | |
2489 PropertyAttributes final = | |
2490 static_cast<PropertyAttributes>(READ_ONLY | DONT_ENUM | DONT_DELETE); | |
2491 PropertyAttributes writable = | |
2492 static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE); | |
2493 Handle<Object> zero(Smi::FromInt(0), isolate); | |
2494 Factory* factory = isolate->factory(); | |
2495 JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->source_string(), | |
2496 source, final).Check(); | |
2497 JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->global_string(), | |
2498 global, final).Check(); | |
2499 JSObject::SetOwnPropertyIgnoreAttributes( | |
2500 regexp, factory->ignore_case_string(), ignoreCase, final).Check(); | |
2501 JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->multiline_string(), | |
2502 multiline, final).Check(); | |
2503 if (FLAG_harmony_regexps) { | |
2504 JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->sticky_string(), | |
2505 sticky, final).Check(); | |
2506 } | |
2507 JSObject::SetOwnPropertyIgnoreAttributes(regexp, factory->last_index_string(), | |
2508 zero, writable).Check(); | |
2509 return *regexp; | |
2510 } | |
2511 | |
2512 | |
2513 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) { | 2384 RUNTIME_FUNCTION(Runtime_FinishArrayPrototypeSetup) { |
2514 HandleScope scope(isolate); | 2385 HandleScope scope(isolate); |
2515 DCHECK(args.length() == 1); | 2386 DCHECK(args.length() == 1); |
2516 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0); | 2387 CONVERT_ARG_HANDLE_CHECKED(JSArray, prototype, 0); |
2517 Object* length = prototype->length(); | 2388 Object* length = prototype->length(); |
2518 RUNTIME_ASSERT(length->IsSmi() && Smi::cast(length)->value() == 0); | 2389 RUNTIME_ASSERT(length->IsSmi() && Smi::cast(length)->value() == 0); |
2519 RUNTIME_ASSERT(prototype->HasFastSmiOrObjectElements()); | 2390 RUNTIME_ASSERT(prototype->HasFastSmiOrObjectElements()); |
2520 // This is necessary to enable fast checks for absence of elements | 2391 // This is necessary to enable fast checks for absence of elements |
2521 // on Array.prototype and below. | 2392 // on Array.prototype and below. |
2522 prototype->set_elements(isolate->heap()->empty_fixed_array()); | 2393 prototype->set_elements(isolate->heap()->empty_fixed_array()); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2590 if (shared->native() || shared->strict_mode() == STRICT) { | 2461 if (shared->native() || shared->strict_mode() == STRICT) { |
2591 return isolate->heap()->undefined_value(); | 2462 return isolate->heap()->undefined_value(); |
2592 } | 2463 } |
2593 // Returns undefined for strict or native functions, or | 2464 // Returns undefined for strict or native functions, or |
2594 // the associated global receiver for "normal" functions. | 2465 // the associated global receiver for "normal" functions. |
2595 | 2466 |
2596 return function->global_proxy(); | 2467 return function->global_proxy(); |
2597 } | 2468 } |
2598 | 2469 |
2599 | 2470 |
2600 RUNTIME_FUNCTION(Runtime_MaterializeRegExpLiteral) { | |
2601 HandleScope scope(isolate); | |
2602 DCHECK(args.length() == 4); | |
2603 CONVERT_ARG_HANDLE_CHECKED(FixedArray, literals, 0); | |
2604 CONVERT_SMI_ARG_CHECKED(index, 1); | |
2605 CONVERT_ARG_HANDLE_CHECKED(String, pattern, 2); | |
2606 CONVERT_ARG_HANDLE_CHECKED(String, flags, 3); | |
2607 | |
2608 // Get the RegExp function from the context in the literals array. | |
2609 // This is the RegExp function from the context in which the | |
2610 // function was created. We do not use the RegExp function from the | |
2611 // current native context because this might be the RegExp function | |
2612 // from another context which we should not have access to. | |
2613 Handle<JSFunction> constructor = Handle<JSFunction>( | |
2614 JSFunction::NativeContextFromLiterals(*literals)->regexp_function()); | |
2615 // Compute the regular expression literal. | |
2616 Handle<Object> regexp; | |
2617 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
2618 isolate, regexp, | |
2619 RegExpImpl::CreateRegExpLiteral(constructor, pattern, flags)); | |
2620 literals->set(index, *regexp); | |
2621 return *regexp; | |
2622 } | |
2623 | |
2624 | |
2625 RUNTIME_FUNCTION(Runtime_FunctionGetName) { | 2471 RUNTIME_FUNCTION(Runtime_FunctionGetName) { |
2626 SealHandleScope shs(isolate); | 2472 SealHandleScope shs(isolate); |
2627 DCHECK(args.length() == 1); | 2473 DCHECK(args.length() == 1); |
2628 | 2474 |
2629 CONVERT_ARG_CHECKED(JSFunction, f, 0); | 2475 CONVERT_ARG_CHECKED(JSFunction, f, 0); |
2630 return f->shared()->name(); | 2476 return f->shared()->name(); |
2631 } | 2477 } |
2632 | 2478 |
2633 | 2479 |
2634 RUNTIME_FUNCTION(Runtime_FunctionSetName) { | 2480 RUNTIME_FUNCTION(Runtime_FunctionSetName) { |
(...skipping 371 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3006 // %ObjectFreeze is a fast path and these cases are handled elsewhere. | 2852 // %ObjectFreeze is a fast path and these cases are handled elsewhere. |
3007 RUNTIME_ASSERT(!object->HasSloppyArgumentsElements() && | 2853 RUNTIME_ASSERT(!object->HasSloppyArgumentsElements() && |
3008 !object->map()->is_observed() && !object->IsJSProxy()); | 2854 !object->map()->is_observed() && !object->IsJSProxy()); |
3009 | 2855 |
3010 Handle<Object> result; | 2856 Handle<Object> result; |
3011 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, JSObject::Freeze(object)); | 2857 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, JSObject::Freeze(object)); |
3012 return *result; | 2858 return *result; |
3013 } | 2859 } |
3014 | 2860 |
3015 | 2861 |
3016 RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) { | |
3017 HandleScope handle_scope(isolate); | |
3018 DCHECK(args.length() == 2); | |
3019 | |
3020 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | |
3021 CONVERT_NUMBER_CHECKED(uint32_t, i, Uint32, args[1]); | |
3022 | |
3023 // Flatten the string. If someone wants to get a char at an index | |
3024 // in a cons string, it is likely that more indices will be | |
3025 // accessed. | |
3026 subject = String::Flatten(subject); | |
3027 | |
3028 if (i >= static_cast<uint32_t>(subject->length())) { | |
3029 return isolate->heap()->nan_value(); | |
3030 } | |
3031 | |
3032 return Smi::FromInt(subject->Get(i)); | |
3033 } | |
3034 | |
3035 | |
3036 RUNTIME_FUNCTION(Runtime_CharFromCode) { | |
3037 HandleScope handlescope(isolate); | |
3038 DCHECK(args.length() == 1); | |
3039 if (args[0]->IsNumber()) { | |
3040 CONVERT_NUMBER_CHECKED(uint32_t, code, Uint32, args[0]); | |
3041 code &= 0xffff; | |
3042 return *isolate->factory()->LookupSingleCharacterStringFromCode(code); | |
3043 } | |
3044 return isolate->heap()->empty_string(); | |
3045 } | |
3046 | |
3047 | |
3048 class FixedArrayBuilder { | |
3049 public: | |
3050 explicit FixedArrayBuilder(Isolate* isolate, int initial_capacity) | |
3051 : array_(isolate->factory()->NewFixedArrayWithHoles(initial_capacity)), | |
3052 length_(0), | |
3053 has_non_smi_elements_(false) { | |
3054 // Require a non-zero initial size. Ensures that doubling the size to | |
3055 // extend the array will work. | |
3056 DCHECK(initial_capacity > 0); | |
3057 } | |
3058 | |
3059 explicit FixedArrayBuilder(Handle<FixedArray> backing_store) | |
3060 : array_(backing_store), length_(0), has_non_smi_elements_(false) { | |
3061 // Require a non-zero initial size. Ensures that doubling the size to | |
3062 // extend the array will work. | |
3063 DCHECK(backing_store->length() > 0); | |
3064 } | |
3065 | |
3066 bool HasCapacity(int elements) { | |
3067 int length = array_->length(); | |
3068 int required_length = length_ + elements; | |
3069 return (length >= required_length); | |
3070 } | |
3071 | |
3072 void EnsureCapacity(int elements) { | |
3073 int length = array_->length(); | |
3074 int required_length = length_ + elements; | |
3075 if (length < required_length) { | |
3076 int new_length = length; | |
3077 do { | |
3078 new_length *= 2; | |
3079 } while (new_length < required_length); | |
3080 Handle<FixedArray> extended_array = | |
3081 array_->GetIsolate()->factory()->NewFixedArrayWithHoles(new_length); | |
3082 array_->CopyTo(0, *extended_array, 0, length_); | |
3083 array_ = extended_array; | |
3084 } | |
3085 } | |
3086 | |
3087 void Add(Object* value) { | |
3088 DCHECK(!value->IsSmi()); | |
3089 DCHECK(length_ < capacity()); | |
3090 array_->set(length_, value); | |
3091 length_++; | |
3092 has_non_smi_elements_ = true; | |
3093 } | |
3094 | |
3095 void Add(Smi* value) { | |
3096 DCHECK(value->IsSmi()); | |
3097 DCHECK(length_ < capacity()); | |
3098 array_->set(length_, value); | |
3099 length_++; | |
3100 } | |
3101 | |
3102 Handle<FixedArray> array() { return array_; } | |
3103 | |
3104 int length() { return length_; } | |
3105 | |
3106 int capacity() { return array_->length(); } | |
3107 | |
3108 Handle<JSArray> ToJSArray(Handle<JSArray> target_array) { | |
3109 JSArray::SetContent(target_array, array_); | |
3110 target_array->set_length(Smi::FromInt(length_)); | |
3111 return target_array; | |
3112 } | |
3113 | |
3114 | |
3115 private: | |
3116 Handle<FixedArray> array_; | |
3117 int length_; | |
3118 bool has_non_smi_elements_; | |
3119 }; | |
3120 | |
3121 | |
3122 // Forward declarations. | |
3123 const int kStringBuilderConcatHelperLengthBits = 11; | |
3124 const int kStringBuilderConcatHelperPositionBits = 19; | |
3125 | |
3126 template <typename schar> | |
3127 static inline void StringBuilderConcatHelper(String*, schar*, FixedArray*, int); | |
3128 | |
3129 typedef BitField<int, 0, kStringBuilderConcatHelperLengthBits> | |
3130 StringBuilderSubstringLength; | |
3131 typedef BitField<int, kStringBuilderConcatHelperLengthBits, | |
3132 kStringBuilderConcatHelperPositionBits> | |
3133 StringBuilderSubstringPosition; | |
3134 | |
3135 | |
3136 class ReplacementStringBuilder { | |
3137 public: | |
3138 ReplacementStringBuilder(Heap* heap, Handle<String> subject, | |
3139 int estimated_part_count) | |
3140 : heap_(heap), | |
3141 array_builder_(heap->isolate(), estimated_part_count), | |
3142 subject_(subject), | |
3143 character_count_(0), | |
3144 is_one_byte_(subject->IsOneByteRepresentation()) { | |
3145 // Require a non-zero initial size. Ensures that doubling the size to | |
3146 // extend the array will work. | |
3147 DCHECK(estimated_part_count > 0); | |
3148 } | |
3149 | |
3150 static inline void AddSubjectSlice(FixedArrayBuilder* builder, int from, | |
3151 int to) { | |
3152 DCHECK(from >= 0); | |
3153 int length = to - from; | |
3154 DCHECK(length > 0); | |
3155 if (StringBuilderSubstringLength::is_valid(length) && | |
3156 StringBuilderSubstringPosition::is_valid(from)) { | |
3157 int encoded_slice = StringBuilderSubstringLength::encode(length) | | |
3158 StringBuilderSubstringPosition::encode(from); | |
3159 builder->Add(Smi::FromInt(encoded_slice)); | |
3160 } else { | |
3161 // Otherwise encode as two smis. | |
3162 builder->Add(Smi::FromInt(-length)); | |
3163 builder->Add(Smi::FromInt(from)); | |
3164 } | |
3165 } | |
3166 | |
3167 | |
3168 void EnsureCapacity(int elements) { array_builder_.EnsureCapacity(elements); } | |
3169 | |
3170 | |
3171 void AddSubjectSlice(int from, int to) { | |
3172 AddSubjectSlice(&array_builder_, from, to); | |
3173 IncrementCharacterCount(to - from); | |
3174 } | |
3175 | |
3176 | |
3177 void AddString(Handle<String> string) { | |
3178 int length = string->length(); | |
3179 DCHECK(length > 0); | |
3180 AddElement(*string); | |
3181 if (!string->IsOneByteRepresentation()) { | |
3182 is_one_byte_ = false; | |
3183 } | |
3184 IncrementCharacterCount(length); | |
3185 } | |
3186 | |
3187 | |
3188 MaybeHandle<String> ToString() { | |
3189 Isolate* isolate = heap_->isolate(); | |
3190 if (array_builder_.length() == 0) { | |
3191 return isolate->factory()->empty_string(); | |
3192 } | |
3193 | |
3194 Handle<String> joined_string; | |
3195 if (is_one_byte_) { | |
3196 Handle<SeqOneByteString> seq; | |
3197 ASSIGN_RETURN_ON_EXCEPTION( | |
3198 isolate, seq, | |
3199 isolate->factory()->NewRawOneByteString(character_count_), String); | |
3200 | |
3201 DisallowHeapAllocation no_gc; | |
3202 uint8_t* char_buffer = seq->GetChars(); | |
3203 StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(), | |
3204 array_builder_.length()); | |
3205 joined_string = Handle<String>::cast(seq); | |
3206 } else { | |
3207 // Two-byte. | |
3208 Handle<SeqTwoByteString> seq; | |
3209 ASSIGN_RETURN_ON_EXCEPTION( | |
3210 isolate, seq, | |
3211 isolate->factory()->NewRawTwoByteString(character_count_), String); | |
3212 | |
3213 DisallowHeapAllocation no_gc; | |
3214 uc16* char_buffer = seq->GetChars(); | |
3215 StringBuilderConcatHelper(*subject_, char_buffer, *array_builder_.array(), | |
3216 array_builder_.length()); | |
3217 joined_string = Handle<String>::cast(seq); | |
3218 } | |
3219 return joined_string; | |
3220 } | |
3221 | |
3222 | |
3223 void IncrementCharacterCount(int by) { | |
3224 if (character_count_ > String::kMaxLength - by) { | |
3225 STATIC_ASSERT(String::kMaxLength < kMaxInt); | |
3226 character_count_ = kMaxInt; | |
3227 } else { | |
3228 character_count_ += by; | |
3229 } | |
3230 } | |
3231 | |
3232 private: | |
3233 void AddElement(Object* element) { | |
3234 DCHECK(element->IsSmi() || element->IsString()); | |
3235 DCHECK(array_builder_.capacity() > array_builder_.length()); | |
3236 array_builder_.Add(element); | |
3237 } | |
3238 | |
3239 Heap* heap_; | |
3240 FixedArrayBuilder array_builder_; | |
3241 Handle<String> subject_; | |
3242 int character_count_; | |
3243 bool is_one_byte_; | |
3244 }; | |
3245 | |
3246 | |
3247 class CompiledReplacement { | |
3248 public: | |
3249 explicit CompiledReplacement(Zone* zone) | |
3250 : parts_(1, zone), replacement_substrings_(0, zone), zone_(zone) {} | |
3251 | |
3252 // Return whether the replacement is simple. | |
3253 bool Compile(Handle<String> replacement, int capture_count, | |
3254 int subject_length); | |
3255 | |
3256 // Use Apply only if Compile returned false. | |
3257 void Apply(ReplacementStringBuilder* builder, int match_from, int match_to, | |
3258 int32_t* match); | |
3259 | |
3260 // Number of distinct parts of the replacement pattern. | |
3261 int parts() { return parts_.length(); } | |
3262 | |
3263 Zone* zone() const { return zone_; } | |
3264 | |
3265 private: | |
3266 enum PartType { | |
3267 SUBJECT_PREFIX = 1, | |
3268 SUBJECT_SUFFIX, | |
3269 SUBJECT_CAPTURE, | |
3270 REPLACEMENT_SUBSTRING, | |
3271 REPLACEMENT_STRING, | |
3272 NUMBER_OF_PART_TYPES | |
3273 }; | |
3274 | |
3275 struct ReplacementPart { | |
3276 static inline ReplacementPart SubjectMatch() { | |
3277 return ReplacementPart(SUBJECT_CAPTURE, 0); | |
3278 } | |
3279 static inline ReplacementPart SubjectCapture(int capture_index) { | |
3280 return ReplacementPart(SUBJECT_CAPTURE, capture_index); | |
3281 } | |
3282 static inline ReplacementPart SubjectPrefix() { | |
3283 return ReplacementPart(SUBJECT_PREFIX, 0); | |
3284 } | |
3285 static inline ReplacementPart SubjectSuffix(int subject_length) { | |
3286 return ReplacementPart(SUBJECT_SUFFIX, subject_length); | |
3287 } | |
3288 static inline ReplacementPart ReplacementString() { | |
3289 return ReplacementPart(REPLACEMENT_STRING, 0); | |
3290 } | |
3291 static inline ReplacementPart ReplacementSubString(int from, int to) { | |
3292 DCHECK(from >= 0); | |
3293 DCHECK(to > from); | |
3294 return ReplacementPart(-from, to); | |
3295 } | |
3296 | |
3297 // If tag <= 0 then it is the negation of a start index of a substring of | |
3298 // the replacement pattern, otherwise it's a value from PartType. | |
3299 ReplacementPart(int tag, int data) : tag(tag), data(data) { | |
3300 // Must be non-positive or a PartType value. | |
3301 DCHECK(tag < NUMBER_OF_PART_TYPES); | |
3302 } | |
3303 // Either a value of PartType or a non-positive number that is | |
3304 // the negation of an index into the replacement string. | |
3305 int tag; | |
3306 // The data value's interpretation depends on the value of tag: | |
3307 // tag == SUBJECT_PREFIX || | |
3308 // tag == SUBJECT_SUFFIX: data is unused. | |
3309 // tag == SUBJECT_CAPTURE: data is the number of the capture. | |
3310 // tag == REPLACEMENT_SUBSTRING || | |
3311 // tag == REPLACEMENT_STRING: data is index into array of substrings | |
3312 // of the replacement string. | |
3313 // tag <= 0: Temporary representation of the substring of the replacement | |
3314 // string ranging over -tag .. data. | |
3315 // Is replaced by REPLACEMENT_{SUB,}STRING when we create the | |
3316 // substring objects. | |
3317 int data; | |
3318 }; | |
3319 | |
3320 template <typename Char> | |
3321 bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts, | |
3322 Vector<Char> characters, int capture_count, | |
3323 int subject_length, Zone* zone) { | |
3324 int length = characters.length(); | |
3325 int last = 0; | |
3326 for (int i = 0; i < length; i++) { | |
3327 Char c = characters[i]; | |
3328 if (c == '$') { | |
3329 int next_index = i + 1; | |
3330 if (next_index == length) { // No next character! | |
3331 break; | |
3332 } | |
3333 Char c2 = characters[next_index]; | |
3334 switch (c2) { | |
3335 case '$': | |
3336 if (i > last) { | |
3337 // There is a substring before. Include the first "$". | |
3338 parts->Add( | |
3339 ReplacementPart::ReplacementSubString(last, next_index), | |
3340 zone); | |
3341 last = next_index + 1; // Continue after the second "$". | |
3342 } else { | |
3343 // Let the next substring start with the second "$". | |
3344 last = next_index; | |
3345 } | |
3346 i = next_index; | |
3347 break; | |
3348 case '`': | |
3349 if (i > last) { | |
3350 parts->Add(ReplacementPart::ReplacementSubString(last, i), zone); | |
3351 } | |
3352 parts->Add(ReplacementPart::SubjectPrefix(), zone); | |
3353 i = next_index; | |
3354 last = i + 1; | |
3355 break; | |
3356 case '\'': | |
3357 if (i > last) { | |
3358 parts->Add(ReplacementPart::ReplacementSubString(last, i), zone); | |
3359 } | |
3360 parts->Add(ReplacementPart::SubjectSuffix(subject_length), zone); | |
3361 i = next_index; | |
3362 last = i + 1; | |
3363 break; | |
3364 case '&': | |
3365 if (i > last) { | |
3366 parts->Add(ReplacementPart::ReplacementSubString(last, i), zone); | |
3367 } | |
3368 parts->Add(ReplacementPart::SubjectMatch(), zone); | |
3369 i = next_index; | |
3370 last = i + 1; | |
3371 break; | |
3372 case '0': | |
3373 case '1': | |
3374 case '2': | |
3375 case '3': | |
3376 case '4': | |
3377 case '5': | |
3378 case '6': | |
3379 case '7': | |
3380 case '8': | |
3381 case '9': { | |
3382 int capture_ref = c2 - '0'; | |
3383 if (capture_ref > capture_count) { | |
3384 i = next_index; | |
3385 continue; | |
3386 } | |
3387 int second_digit_index = next_index + 1; | |
3388 if (second_digit_index < length) { | |
3389 // Peek ahead to see if we have two digits. | |
3390 Char c3 = characters[second_digit_index]; | |
3391 if ('0' <= c3 && c3 <= '9') { // Double digits. | |
3392 int double_digit_ref = capture_ref * 10 + c3 - '0'; | |
3393 if (double_digit_ref <= capture_count) { | |
3394 next_index = second_digit_index; | |
3395 capture_ref = double_digit_ref; | |
3396 } | |
3397 } | |
3398 } | |
3399 if (capture_ref > 0) { | |
3400 if (i > last) { | |
3401 parts->Add(ReplacementPart::ReplacementSubString(last, i), | |
3402 zone); | |
3403 } | |
3404 DCHECK(capture_ref <= capture_count); | |
3405 parts->Add(ReplacementPart::SubjectCapture(capture_ref), zone); | |
3406 last = next_index + 1; | |
3407 } | |
3408 i = next_index; | |
3409 break; | |
3410 } | |
3411 default: | |
3412 i = next_index; | |
3413 break; | |
3414 } | |
3415 } | |
3416 } | |
3417 if (length > last) { | |
3418 if (last == 0) { | |
3419 // Replacement is simple. Do not use Apply to do the replacement. | |
3420 return true; | |
3421 } else { | |
3422 parts->Add(ReplacementPart::ReplacementSubString(last, length), zone); | |
3423 } | |
3424 } | |
3425 return false; | |
3426 } | |
3427 | |
3428 ZoneList<ReplacementPart> parts_; | |
3429 ZoneList<Handle<String> > replacement_substrings_; | |
3430 Zone* zone_; | |
3431 }; | |
3432 | |
3433 | |
3434 bool CompiledReplacement::Compile(Handle<String> replacement, int capture_count, | |
3435 int subject_length) { | |
3436 { | |
3437 DisallowHeapAllocation no_gc; | |
3438 String::FlatContent content = replacement->GetFlatContent(); | |
3439 DCHECK(content.IsFlat()); | |
3440 bool simple = false; | |
3441 if (content.IsOneByte()) { | |
3442 simple = ParseReplacementPattern(&parts_, content.ToOneByteVector(), | |
3443 capture_count, subject_length, zone()); | |
3444 } else { | |
3445 DCHECK(content.IsTwoByte()); | |
3446 simple = ParseReplacementPattern(&parts_, content.ToUC16Vector(), | |
3447 capture_count, subject_length, zone()); | |
3448 } | |
3449 if (simple) return true; | |
3450 } | |
3451 | |
3452 Isolate* isolate = replacement->GetIsolate(); | |
3453 // Find substrings of replacement string and create them as String objects. | |
3454 int substring_index = 0; | |
3455 for (int i = 0, n = parts_.length(); i < n; i++) { | |
3456 int tag = parts_[i].tag; | |
3457 if (tag <= 0) { // A replacement string slice. | |
3458 int from = -tag; | |
3459 int to = parts_[i].data; | |
3460 replacement_substrings_.Add( | |
3461 isolate->factory()->NewSubString(replacement, from, to), zone()); | |
3462 parts_[i].tag = REPLACEMENT_SUBSTRING; | |
3463 parts_[i].data = substring_index; | |
3464 substring_index++; | |
3465 } else if (tag == REPLACEMENT_STRING) { | |
3466 replacement_substrings_.Add(replacement, zone()); | |
3467 parts_[i].data = substring_index; | |
3468 substring_index++; | |
3469 } | |
3470 } | |
3471 return false; | |
3472 } | |
3473 | |
3474 | |
3475 void CompiledReplacement::Apply(ReplacementStringBuilder* builder, | |
3476 int match_from, int match_to, int32_t* match) { | |
3477 DCHECK_LT(0, parts_.length()); | |
3478 for (int i = 0, n = parts_.length(); i < n; i++) { | |
3479 ReplacementPart part = parts_[i]; | |
3480 switch (part.tag) { | |
3481 case SUBJECT_PREFIX: | |
3482 if (match_from > 0) builder->AddSubjectSlice(0, match_from); | |
3483 break; | |
3484 case SUBJECT_SUFFIX: { | |
3485 int subject_length = part.data; | |
3486 if (match_to < subject_length) { | |
3487 builder->AddSubjectSlice(match_to, subject_length); | |
3488 } | |
3489 break; | |
3490 } | |
3491 case SUBJECT_CAPTURE: { | |
3492 int capture = part.data; | |
3493 int from = match[capture * 2]; | |
3494 int to = match[capture * 2 + 1]; | |
3495 if (from >= 0 && to > from) { | |
3496 builder->AddSubjectSlice(from, to); | |
3497 } | |
3498 break; | |
3499 } | |
3500 case REPLACEMENT_SUBSTRING: | |
3501 case REPLACEMENT_STRING: | |
3502 builder->AddString(replacement_substrings_[part.data]); | |
3503 break; | |
3504 default: | |
3505 UNREACHABLE(); | |
3506 } | |
3507 } | |
3508 } | |
3509 | |
3510 | |
3511 void FindOneByteStringIndices(Vector<const uint8_t> subject, char pattern, | |
3512 ZoneList<int>* indices, unsigned int limit, | |
3513 Zone* zone) { | |
3514 DCHECK(limit > 0); | |
3515 // Collect indices of pattern in subject using memchr. | |
3516 // Stop after finding at most limit values. | |
3517 const uint8_t* subject_start = subject.start(); | |
3518 const uint8_t* subject_end = subject_start + subject.length(); | |
3519 const uint8_t* pos = subject_start; | |
3520 while (limit > 0) { | |
3521 pos = reinterpret_cast<const uint8_t*>( | |
3522 memchr(pos, pattern, subject_end - pos)); | |
3523 if (pos == NULL) return; | |
3524 indices->Add(static_cast<int>(pos - subject_start), zone); | |
3525 pos++; | |
3526 limit--; | |
3527 } | |
3528 } | |
3529 | |
3530 | |
3531 void FindTwoByteStringIndices(const Vector<const uc16> subject, uc16 pattern, | |
3532 ZoneList<int>* indices, unsigned int limit, | |
3533 Zone* zone) { | |
3534 DCHECK(limit > 0); | |
3535 const uc16* subject_start = subject.start(); | |
3536 const uc16* subject_end = subject_start + subject.length(); | |
3537 for (const uc16* pos = subject_start; pos < subject_end && limit > 0; pos++) { | |
3538 if (*pos == pattern) { | |
3539 indices->Add(static_cast<int>(pos - subject_start), zone); | |
3540 limit--; | |
3541 } | |
3542 } | |
3543 } | |
3544 | |
3545 | |
3546 template <typename SubjectChar, typename PatternChar> | |
3547 void FindStringIndices(Isolate* isolate, Vector<const SubjectChar> subject, | |
3548 Vector<const PatternChar> pattern, | |
3549 ZoneList<int>* indices, unsigned int limit, Zone* zone) { | |
3550 DCHECK(limit > 0); | |
3551 // Collect indices of pattern in subject. | |
3552 // Stop after finding at most limit values. | |
3553 int pattern_length = pattern.length(); | |
3554 int index = 0; | |
3555 StringSearch<PatternChar, SubjectChar> search(isolate, pattern); | |
3556 while (limit > 0) { | |
3557 index = search.Search(subject, index); | |
3558 if (index < 0) return; | |
3559 indices->Add(index, zone); | |
3560 index += pattern_length; | |
3561 limit--; | |
3562 } | |
3563 } | |
3564 | |
3565 | |
3566 void FindStringIndicesDispatch(Isolate* isolate, String* subject, | |
3567 String* pattern, ZoneList<int>* indices, | |
3568 unsigned int limit, Zone* zone) { | |
3569 { | |
3570 DisallowHeapAllocation no_gc; | |
3571 String::FlatContent subject_content = subject->GetFlatContent(); | |
3572 String::FlatContent pattern_content = pattern->GetFlatContent(); | |
3573 DCHECK(subject_content.IsFlat()); | |
3574 DCHECK(pattern_content.IsFlat()); | |
3575 if (subject_content.IsOneByte()) { | |
3576 Vector<const uint8_t> subject_vector = subject_content.ToOneByteVector(); | |
3577 if (pattern_content.IsOneByte()) { | |
3578 Vector<const uint8_t> pattern_vector = | |
3579 pattern_content.ToOneByteVector(); | |
3580 if (pattern_vector.length() == 1) { | |
3581 FindOneByteStringIndices(subject_vector, pattern_vector[0], indices, | |
3582 limit, zone); | |
3583 } else { | |
3584 FindStringIndices(isolate, subject_vector, pattern_vector, indices, | |
3585 limit, zone); | |
3586 } | |
3587 } else { | |
3588 FindStringIndices(isolate, subject_vector, | |
3589 pattern_content.ToUC16Vector(), indices, limit, zone); | |
3590 } | |
3591 } else { | |
3592 Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); | |
3593 if (pattern_content.IsOneByte()) { | |
3594 Vector<const uint8_t> pattern_vector = | |
3595 pattern_content.ToOneByteVector(); | |
3596 if (pattern_vector.length() == 1) { | |
3597 FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices, | |
3598 limit, zone); | |
3599 } else { | |
3600 FindStringIndices(isolate, subject_vector, pattern_vector, indices, | |
3601 limit, zone); | |
3602 } | |
3603 } else { | |
3604 Vector<const uc16> pattern_vector = pattern_content.ToUC16Vector(); | |
3605 if (pattern_vector.length() == 1) { | |
3606 FindTwoByteStringIndices(subject_vector, pattern_vector[0], indices, | |
3607 limit, zone); | |
3608 } else { | |
3609 FindStringIndices(isolate, subject_vector, pattern_vector, indices, | |
3610 limit, zone); | |
3611 } | |
3612 } | |
3613 } | |
3614 } | |
3615 } | |
3616 | |
3617 | |
3618 template <typename ResultSeqString> | |
3619 MUST_USE_RESULT static Object* StringReplaceGlobalAtomRegExpWithString( | |
3620 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> pattern_regexp, | |
3621 Handle<String> replacement, Handle<JSArray> last_match_info) { | |
3622 DCHECK(subject->IsFlat()); | |
3623 DCHECK(replacement->IsFlat()); | |
3624 | |
3625 ZoneScope zone_scope(isolate->runtime_zone()); | |
3626 ZoneList<int> indices(8, zone_scope.zone()); | |
3627 DCHECK_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag()); | |
3628 String* pattern = | |
3629 String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex)); | |
3630 int subject_len = subject->length(); | |
3631 int pattern_len = pattern->length(); | |
3632 int replacement_len = replacement->length(); | |
3633 | |
3634 FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff, | |
3635 zone_scope.zone()); | |
3636 | |
3637 int matches = indices.length(); | |
3638 if (matches == 0) return *subject; | |
3639 | |
3640 // Detect integer overflow. | |
3641 int64_t result_len_64 = (static_cast<int64_t>(replacement_len) - | |
3642 static_cast<int64_t>(pattern_len)) * | |
3643 static_cast<int64_t>(matches) + | |
3644 static_cast<int64_t>(subject_len); | |
3645 int result_len; | |
3646 if (result_len_64 > static_cast<int64_t>(String::kMaxLength)) { | |
3647 STATIC_ASSERT(String::kMaxLength < kMaxInt); | |
3648 result_len = kMaxInt; // Provoke exception. | |
3649 } else { | |
3650 result_len = static_cast<int>(result_len_64); | |
3651 } | |
3652 | |
3653 int subject_pos = 0; | |
3654 int result_pos = 0; | |
3655 | |
3656 MaybeHandle<SeqString> maybe_res; | |
3657 if (ResultSeqString::kHasOneByteEncoding) { | |
3658 maybe_res = isolate->factory()->NewRawOneByteString(result_len); | |
3659 } else { | |
3660 maybe_res = isolate->factory()->NewRawTwoByteString(result_len); | |
3661 } | |
3662 Handle<SeqString> untyped_res; | |
3663 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, untyped_res, maybe_res); | |
3664 Handle<ResultSeqString> result = Handle<ResultSeqString>::cast(untyped_res); | |
3665 | |
3666 for (int i = 0; i < matches; i++) { | |
3667 // Copy non-matched subject content. | |
3668 if (subject_pos < indices.at(i)) { | |
3669 String::WriteToFlat(*subject, result->GetChars() + result_pos, | |
3670 subject_pos, indices.at(i)); | |
3671 result_pos += indices.at(i) - subject_pos; | |
3672 } | |
3673 | |
3674 // Replace match. | |
3675 if (replacement_len > 0) { | |
3676 String::WriteToFlat(*replacement, result->GetChars() + result_pos, 0, | |
3677 replacement_len); | |
3678 result_pos += replacement_len; | |
3679 } | |
3680 | |
3681 subject_pos = indices.at(i) + pattern_len; | |
3682 } | |
3683 // Add remaining subject content at the end. | |
3684 if (subject_pos < subject_len) { | |
3685 String::WriteToFlat(*subject, result->GetChars() + result_pos, subject_pos, | |
3686 subject_len); | |
3687 } | |
3688 | |
3689 int32_t match_indices[] = {indices.at(matches - 1), | |
3690 indices.at(matches - 1) + pattern_len}; | |
3691 RegExpImpl::SetLastMatchInfo(last_match_info, subject, 0, match_indices); | |
3692 | |
3693 return *result; | |
3694 } | |
3695 | |
3696 | |
3697 MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithString( | |
3698 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | |
3699 Handle<String> replacement, Handle<JSArray> last_match_info) { | |
3700 DCHECK(subject->IsFlat()); | |
3701 DCHECK(replacement->IsFlat()); | |
3702 | |
3703 int capture_count = regexp->CaptureCount(); | |
3704 int subject_length = subject->length(); | |
3705 | |
3706 // CompiledReplacement uses zone allocation. | |
3707 ZoneScope zone_scope(isolate->runtime_zone()); | |
3708 CompiledReplacement compiled_replacement(zone_scope.zone()); | |
3709 bool simple_replace = | |
3710 compiled_replacement.Compile(replacement, capture_count, subject_length); | |
3711 | |
3712 // Shortcut for simple non-regexp global replacements | |
3713 if (regexp->TypeTag() == JSRegExp::ATOM && simple_replace) { | |
3714 if (subject->HasOnlyOneByteChars() && replacement->HasOnlyOneByteChars()) { | |
3715 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( | |
3716 isolate, subject, regexp, replacement, last_match_info); | |
3717 } else { | |
3718 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( | |
3719 isolate, subject, regexp, replacement, last_match_info); | |
3720 } | |
3721 } | |
3722 | |
3723 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); | |
3724 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3725 | |
3726 int32_t* current_match = global_cache.FetchNext(); | |
3727 if (current_match == NULL) { | |
3728 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3729 return *subject; | |
3730 } | |
3731 | |
3732 // Guessing the number of parts that the final result string is built | |
3733 // from. Global regexps can match any number of times, so we guess | |
3734 // conservatively. | |
3735 int expected_parts = (compiled_replacement.parts() + 1) * 4 + 1; | |
3736 ReplacementStringBuilder builder(isolate->heap(), subject, expected_parts); | |
3737 | |
3738 // Number of parts added by compiled replacement plus preceeding | |
3739 // string and possibly suffix after last match. It is possible for | |
3740 // all components to use two elements when encoded as two smis. | |
3741 const int parts_added_per_loop = 2 * (compiled_replacement.parts() + 2); | |
3742 | |
3743 int prev = 0; | |
3744 | |
3745 do { | |
3746 builder.EnsureCapacity(parts_added_per_loop); | |
3747 | |
3748 int start = current_match[0]; | |
3749 int end = current_match[1]; | |
3750 | |
3751 if (prev < start) { | |
3752 builder.AddSubjectSlice(prev, start); | |
3753 } | |
3754 | |
3755 if (simple_replace) { | |
3756 builder.AddString(replacement); | |
3757 } else { | |
3758 compiled_replacement.Apply(&builder, start, end, current_match); | |
3759 } | |
3760 prev = end; | |
3761 | |
3762 current_match = global_cache.FetchNext(); | |
3763 } while (current_match != NULL); | |
3764 | |
3765 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3766 | |
3767 if (prev < subject_length) { | |
3768 builder.EnsureCapacity(2); | |
3769 builder.AddSubjectSlice(prev, subject_length); | |
3770 } | |
3771 | |
3772 RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count, | |
3773 global_cache.LastSuccessfulMatch()); | |
3774 | |
3775 Handle<String> result; | |
3776 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, builder.ToString()); | |
3777 return *result; | |
3778 } | |
3779 | |
3780 | |
3781 template <typename ResultSeqString> | |
3782 MUST_USE_RESULT static Object* StringReplaceGlobalRegExpWithEmptyString( | |
3783 Isolate* isolate, Handle<String> subject, Handle<JSRegExp> regexp, | |
3784 Handle<JSArray> last_match_info) { | |
3785 DCHECK(subject->IsFlat()); | |
3786 | |
3787 // Shortcut for simple non-regexp global replacements | |
3788 if (regexp->TypeTag() == JSRegExp::ATOM) { | |
3789 Handle<String> empty_string = isolate->factory()->empty_string(); | |
3790 if (subject->IsOneByteRepresentation()) { | |
3791 return StringReplaceGlobalAtomRegExpWithString<SeqOneByteString>( | |
3792 isolate, subject, regexp, empty_string, last_match_info); | |
3793 } else { | |
3794 return StringReplaceGlobalAtomRegExpWithString<SeqTwoByteString>( | |
3795 isolate, subject, regexp, empty_string, last_match_info); | |
3796 } | |
3797 } | |
3798 | |
3799 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); | |
3800 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3801 | |
3802 int32_t* current_match = global_cache.FetchNext(); | |
3803 if (current_match == NULL) { | |
3804 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3805 return *subject; | |
3806 } | |
3807 | |
3808 int start = current_match[0]; | |
3809 int end = current_match[1]; | |
3810 int capture_count = regexp->CaptureCount(); | |
3811 int subject_length = subject->length(); | |
3812 | |
3813 int new_length = subject_length - (end - start); | |
3814 if (new_length == 0) return isolate->heap()->empty_string(); | |
3815 | |
3816 Handle<ResultSeqString> answer; | |
3817 if (ResultSeqString::kHasOneByteEncoding) { | |
3818 answer = Handle<ResultSeqString>::cast( | |
3819 isolate->factory()->NewRawOneByteString(new_length).ToHandleChecked()); | |
3820 } else { | |
3821 answer = Handle<ResultSeqString>::cast( | |
3822 isolate->factory()->NewRawTwoByteString(new_length).ToHandleChecked()); | |
3823 } | |
3824 | |
3825 int prev = 0; | |
3826 int position = 0; | |
3827 | |
3828 do { | |
3829 start = current_match[0]; | |
3830 end = current_match[1]; | |
3831 if (prev < start) { | |
3832 // Add substring subject[prev;start] to answer string. | |
3833 String::WriteToFlat(*subject, answer->GetChars() + position, prev, start); | |
3834 position += start - prev; | |
3835 } | |
3836 prev = end; | |
3837 | |
3838 current_match = global_cache.FetchNext(); | |
3839 } while (current_match != NULL); | |
3840 | |
3841 if (global_cache.HasException()) return isolate->heap()->exception(); | |
3842 | |
3843 RegExpImpl::SetLastMatchInfo(last_match_info, subject, capture_count, | |
3844 global_cache.LastSuccessfulMatch()); | |
3845 | |
3846 if (prev < subject_length) { | |
3847 // Add substring subject[prev;length] to answer string. | |
3848 String::WriteToFlat(*subject, answer->GetChars() + position, prev, | |
3849 subject_length); | |
3850 position += subject_length - prev; | |
3851 } | |
3852 | |
3853 if (position == 0) return isolate->heap()->empty_string(); | |
3854 | |
3855 // Shorten string and fill | |
3856 int string_size = ResultSeqString::SizeFor(position); | |
3857 int allocated_string_size = ResultSeqString::SizeFor(new_length); | |
3858 int delta = allocated_string_size - string_size; | |
3859 | |
3860 answer->set_length(position); | |
3861 if (delta == 0) return *answer; | |
3862 | |
3863 Address end_of_string = answer->address() + string_size; | |
3864 Heap* heap = isolate->heap(); | |
3865 | |
3866 // The trimming is performed on a newly allocated object, which is on a | |
3867 // fresly allocated page or on an already swept page. Hence, the sweeper | |
3868 // thread can not get confused with the filler creation. No synchronization | |
3869 // needed. | |
3870 heap->CreateFillerObjectAt(end_of_string, delta); | |
3871 heap->AdjustLiveBytes(answer->address(), -delta, Heap::FROM_MUTATOR); | |
3872 return *answer; | |
3873 } | |
3874 | |
3875 | |
3876 RUNTIME_FUNCTION(Runtime_StringReplaceGlobalRegExpWithString) { | |
3877 HandleScope scope(isolate); | |
3878 DCHECK(args.length() == 4); | |
3879 | |
3880 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | |
3881 CONVERT_ARG_HANDLE_CHECKED(String, replacement, 2); | |
3882 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); | |
3883 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 3); | |
3884 | |
3885 RUNTIME_ASSERT(regexp->GetFlags().is_global()); | |
3886 RUNTIME_ASSERT(last_match_info->HasFastObjectElements()); | |
3887 | |
3888 subject = String::Flatten(subject); | |
3889 | |
3890 if (replacement->length() == 0) { | |
3891 if (subject->HasOnlyOneByteChars()) { | |
3892 return StringReplaceGlobalRegExpWithEmptyString<SeqOneByteString>( | |
3893 isolate, subject, regexp, last_match_info); | |
3894 } else { | |
3895 return StringReplaceGlobalRegExpWithEmptyString<SeqTwoByteString>( | |
3896 isolate, subject, regexp, last_match_info); | |
3897 } | |
3898 } | |
3899 | |
3900 replacement = String::Flatten(replacement); | |
3901 | |
3902 return StringReplaceGlobalRegExpWithString(isolate, subject, regexp, | |
3903 replacement, last_match_info); | |
3904 } | |
3905 | |
3906 | |
3907 // This may return an empty MaybeHandle if an exception is thrown or | |
3908 // we abort due to reaching the recursion limit. | |
3909 MaybeHandle<String> StringReplaceOneCharWithString( | |
3910 Isolate* isolate, Handle<String> subject, Handle<String> search, | |
3911 Handle<String> replace, bool* found, int recursion_limit) { | |
3912 StackLimitCheck stackLimitCheck(isolate); | |
3913 if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) { | |
3914 return MaybeHandle<String>(); | |
3915 } | |
3916 recursion_limit--; | |
3917 if (subject->IsConsString()) { | |
3918 ConsString* cons = ConsString::cast(*subject); | |
3919 Handle<String> first = Handle<String>(cons->first()); | |
3920 Handle<String> second = Handle<String>(cons->second()); | |
3921 Handle<String> new_first; | |
3922 if (!StringReplaceOneCharWithString(isolate, first, search, replace, found, | |
3923 recursion_limit).ToHandle(&new_first)) { | |
3924 return MaybeHandle<String>(); | |
3925 } | |
3926 if (*found) return isolate->factory()->NewConsString(new_first, second); | |
3927 | |
3928 Handle<String> new_second; | |
3929 if (!StringReplaceOneCharWithString(isolate, second, search, replace, found, | |
3930 recursion_limit) | |
3931 .ToHandle(&new_second)) { | |
3932 return MaybeHandle<String>(); | |
3933 } | |
3934 if (*found) return isolate->factory()->NewConsString(first, new_second); | |
3935 | |
3936 return subject; | |
3937 } else { | |
3938 int index = Runtime::StringMatch(isolate, subject, search, 0); | |
3939 if (index == -1) return subject; | |
3940 *found = true; | |
3941 Handle<String> first = isolate->factory()->NewSubString(subject, 0, index); | |
3942 Handle<String> cons1; | |
3943 ASSIGN_RETURN_ON_EXCEPTION( | |
3944 isolate, cons1, isolate->factory()->NewConsString(first, replace), | |
3945 String); | |
3946 Handle<String> second = | |
3947 isolate->factory()->NewSubString(subject, index + 1, subject->length()); | |
3948 return isolate->factory()->NewConsString(cons1, second); | |
3949 } | |
3950 } | |
3951 | |
3952 | |
3953 RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) { | |
3954 HandleScope scope(isolate); | |
3955 DCHECK(args.length() == 3); | |
3956 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | |
3957 CONVERT_ARG_HANDLE_CHECKED(String, search, 1); | |
3958 CONVERT_ARG_HANDLE_CHECKED(String, replace, 2); | |
3959 | |
3960 // If the cons string tree is too deep, we simply abort the recursion and | |
3961 // retry with a flattened subject string. | |
3962 const int kRecursionLimit = 0x1000; | |
3963 bool found = false; | |
3964 Handle<String> result; | |
3965 if (StringReplaceOneCharWithString(isolate, subject, search, replace, &found, | |
3966 kRecursionLimit).ToHandle(&result)) { | |
3967 return *result; | |
3968 } | |
3969 if (isolate->has_pending_exception()) return isolate->heap()->exception(); | |
3970 | |
3971 subject = String::Flatten(subject); | |
3972 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
3973 isolate, result, | |
3974 StringReplaceOneCharWithString(isolate, subject, search, replace, &found, | |
3975 kRecursionLimit)); | |
3976 return *result; | |
3977 } | |
3978 | |
3979 | |
3980 // Perform string match of pattern on subject, starting at start index. | |
3981 // Caller must ensure that 0 <= start_index <= sub->length(), | |
3982 // and should check that pat->length() + start_index <= sub->length(). | |
3983 int Runtime::StringMatch(Isolate* isolate, Handle<String> sub, | |
3984 Handle<String> pat, int start_index) { | |
3985 DCHECK(0 <= start_index); | |
3986 DCHECK(start_index <= sub->length()); | |
3987 | |
3988 int pattern_length = pat->length(); | |
3989 if (pattern_length == 0) return start_index; | |
3990 | |
3991 int subject_length = sub->length(); | |
3992 if (start_index + pattern_length > subject_length) return -1; | |
3993 | |
3994 sub = String::Flatten(sub); | |
3995 pat = String::Flatten(pat); | |
3996 | |
3997 DisallowHeapAllocation no_gc; // ensure vectors stay valid | |
3998 // Extract flattened substrings of cons strings before getting encoding. | |
3999 String::FlatContent seq_sub = sub->GetFlatContent(); | |
4000 String::FlatContent seq_pat = pat->GetFlatContent(); | |
4001 | |
4002 // dispatch on type of strings | |
4003 if (seq_pat.IsOneByte()) { | |
4004 Vector<const uint8_t> pat_vector = seq_pat.ToOneByteVector(); | |
4005 if (seq_sub.IsOneByte()) { | |
4006 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, | |
4007 start_index); | |
4008 } | |
4009 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, | |
4010 start_index); | |
4011 } | |
4012 Vector<const uc16> pat_vector = seq_pat.ToUC16Vector(); | |
4013 if (seq_sub.IsOneByte()) { | |
4014 return SearchString(isolate, seq_sub.ToOneByteVector(), pat_vector, | |
4015 start_index); | |
4016 } | |
4017 return SearchString(isolate, seq_sub.ToUC16Vector(), pat_vector, start_index); | |
4018 } | |
4019 | |
4020 | |
4021 RUNTIME_FUNCTION(Runtime_StringIndexOf) { | |
4022 HandleScope scope(isolate); | |
4023 DCHECK(args.length() == 3); | |
4024 | |
4025 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); | |
4026 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); | |
4027 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2); | |
4028 | |
4029 uint32_t start_index; | |
4030 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); | |
4031 | |
4032 RUNTIME_ASSERT(start_index <= static_cast<uint32_t>(sub->length())); | |
4033 int position = Runtime::StringMatch(isolate, sub, pat, start_index); | |
4034 return Smi::FromInt(position); | |
4035 } | |
4036 | |
4037 | |
4038 template <typename schar, typename pchar> | |
4039 static int StringMatchBackwards(Vector<const schar> subject, | |
4040 Vector<const pchar> pattern, int idx) { | |
4041 int pattern_length = pattern.length(); | |
4042 DCHECK(pattern_length >= 1); | |
4043 DCHECK(idx + pattern_length <= subject.length()); | |
4044 | |
4045 if (sizeof(schar) == 1 && sizeof(pchar) > 1) { | |
4046 for (int i = 0; i < pattern_length; i++) { | |
4047 uc16 c = pattern[i]; | |
4048 if (c > String::kMaxOneByteCharCode) { | |
4049 return -1; | |
4050 } | |
4051 } | |
4052 } | |
4053 | |
4054 pchar pattern_first_char = pattern[0]; | |
4055 for (int i = idx; i >= 0; i--) { | |
4056 if (subject[i] != pattern_first_char) continue; | |
4057 int j = 1; | |
4058 while (j < pattern_length) { | |
4059 if (pattern[j] != subject[i + j]) { | |
4060 break; | |
4061 } | |
4062 j++; | |
4063 } | |
4064 if (j == pattern_length) { | |
4065 return i; | |
4066 } | |
4067 } | |
4068 return -1; | |
4069 } | |
4070 | |
4071 | |
4072 RUNTIME_FUNCTION(Runtime_StringLastIndexOf) { | |
4073 HandleScope scope(isolate); | |
4074 DCHECK(args.length() == 3); | |
4075 | |
4076 CONVERT_ARG_HANDLE_CHECKED(String, sub, 0); | |
4077 CONVERT_ARG_HANDLE_CHECKED(String, pat, 1); | |
4078 CONVERT_ARG_HANDLE_CHECKED(Object, index, 2); | |
4079 | |
4080 uint32_t start_index; | |
4081 if (!index->ToArrayIndex(&start_index)) return Smi::FromInt(-1); | |
4082 | |
4083 uint32_t pat_length = pat->length(); | |
4084 uint32_t sub_length = sub->length(); | |
4085 | |
4086 if (start_index + pat_length > sub_length) { | |
4087 start_index = sub_length - pat_length; | |
4088 } | |
4089 | |
4090 if (pat_length == 0) { | |
4091 return Smi::FromInt(start_index); | |
4092 } | |
4093 | |
4094 sub = String::Flatten(sub); | |
4095 pat = String::Flatten(pat); | |
4096 | |
4097 int position = -1; | |
4098 DisallowHeapAllocation no_gc; // ensure vectors stay valid | |
4099 | |
4100 String::FlatContent sub_content = sub->GetFlatContent(); | |
4101 String::FlatContent pat_content = pat->GetFlatContent(); | |
4102 | |
4103 if (pat_content.IsOneByte()) { | |
4104 Vector<const uint8_t> pat_vector = pat_content.ToOneByteVector(); | |
4105 if (sub_content.IsOneByte()) { | |
4106 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector, | |
4107 start_index); | |
4108 } else { | |
4109 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector, | |
4110 start_index); | |
4111 } | |
4112 } else { | |
4113 Vector<const uc16> pat_vector = pat_content.ToUC16Vector(); | |
4114 if (sub_content.IsOneByte()) { | |
4115 position = StringMatchBackwards(sub_content.ToOneByteVector(), pat_vector, | |
4116 start_index); | |
4117 } else { | |
4118 position = StringMatchBackwards(sub_content.ToUC16Vector(), pat_vector, | |
4119 start_index); | |
4120 } | |
4121 } | |
4122 | |
4123 return Smi::FromInt(position); | |
4124 } | |
4125 | |
4126 | |
4127 RUNTIME_FUNCTION(Runtime_StringLocaleCompare) { | |
4128 HandleScope handle_scope(isolate); | |
4129 DCHECK(args.length() == 2); | |
4130 | |
4131 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); | |
4132 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); | |
4133 | |
4134 if (str1.is_identical_to(str2)) return Smi::FromInt(0); // Equal. | |
4135 int str1_length = str1->length(); | |
4136 int str2_length = str2->length(); | |
4137 | |
4138 // Decide trivial cases without flattening. | |
4139 if (str1_length == 0) { | |
4140 if (str2_length == 0) return Smi::FromInt(0); // Equal. | |
4141 return Smi::FromInt(-str2_length); | |
4142 } else { | |
4143 if (str2_length == 0) return Smi::FromInt(str1_length); | |
4144 } | |
4145 | |
4146 int end = str1_length < str2_length ? str1_length : str2_length; | |
4147 | |
4148 // No need to flatten if we are going to find the answer on the first | |
4149 // character. At this point we know there is at least one character | |
4150 // in each string, due to the trivial case handling above. | |
4151 int d = str1->Get(0) - str2->Get(0); | |
4152 if (d != 0) return Smi::FromInt(d); | |
4153 | |
4154 str1 = String::Flatten(str1); | |
4155 str2 = String::Flatten(str2); | |
4156 | |
4157 DisallowHeapAllocation no_gc; | |
4158 String::FlatContent flat1 = str1->GetFlatContent(); | |
4159 String::FlatContent flat2 = str2->GetFlatContent(); | |
4160 | |
4161 for (int i = 0; i < end; i++) { | |
4162 if (flat1.Get(i) != flat2.Get(i)) { | |
4163 return Smi::FromInt(flat1.Get(i) - flat2.Get(i)); | |
4164 } | |
4165 } | |
4166 | |
4167 return Smi::FromInt(str1_length - str2_length); | |
4168 } | |
4169 | |
4170 | |
4171 RUNTIME_FUNCTION(Runtime_SubString) { | |
4172 HandleScope scope(isolate); | |
4173 DCHECK(args.length() == 3); | |
4174 | |
4175 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); | |
4176 int start, end; | |
4177 // We have a fast integer-only case here to avoid a conversion to double in | |
4178 // the common case where from and to are Smis. | |
4179 if (args[1]->IsSmi() && args[2]->IsSmi()) { | |
4180 CONVERT_SMI_ARG_CHECKED(from_number, 1); | |
4181 CONVERT_SMI_ARG_CHECKED(to_number, 2); | |
4182 start = from_number; | |
4183 end = to_number; | |
4184 } else { | |
4185 CONVERT_DOUBLE_ARG_CHECKED(from_number, 1); | |
4186 CONVERT_DOUBLE_ARG_CHECKED(to_number, 2); | |
4187 start = FastD2IChecked(from_number); | |
4188 end = FastD2IChecked(to_number); | |
4189 } | |
4190 RUNTIME_ASSERT(end >= start); | |
4191 RUNTIME_ASSERT(start >= 0); | |
4192 RUNTIME_ASSERT(end <= string->length()); | |
4193 isolate->counters()->sub_string_runtime()->Increment(); | |
4194 | |
4195 return *isolate->factory()->NewSubString(string, start, end); | |
4196 } | |
4197 | |
4198 | |
4199 RUNTIME_FUNCTION(Runtime_InternalizeString) { | |
4200 HandleScope handles(isolate); | |
4201 RUNTIME_ASSERT(args.length() == 1); | |
4202 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); | |
4203 return *isolate->factory()->InternalizeString(string); | |
4204 } | |
4205 | |
4206 | |
4207 RUNTIME_FUNCTION(Runtime_StringMatch) { | |
4208 HandleScope handles(isolate); | |
4209 DCHECK(args.length() == 3); | |
4210 | |
4211 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | |
4212 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 1); | |
4213 CONVERT_ARG_HANDLE_CHECKED(JSArray, regexp_info, 2); | |
4214 | |
4215 RUNTIME_ASSERT(regexp_info->HasFastObjectElements()); | |
4216 | |
4217 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); | |
4218 if (global_cache.HasException()) return isolate->heap()->exception(); | |
4219 | |
4220 int capture_count = regexp->CaptureCount(); | |
4221 | |
4222 ZoneScope zone_scope(isolate->runtime_zone()); | |
4223 ZoneList<int> offsets(8, zone_scope.zone()); | |
4224 | |
4225 while (true) { | |
4226 int32_t* match = global_cache.FetchNext(); | |
4227 if (match == NULL) break; | |
4228 offsets.Add(match[0], zone_scope.zone()); // start | |
4229 offsets.Add(match[1], zone_scope.zone()); // end | |
4230 } | |
4231 | |
4232 if (global_cache.HasException()) return isolate->heap()->exception(); | |
4233 | |
4234 if (offsets.length() == 0) { | |
4235 // Not a single match. | |
4236 return isolate->heap()->null_value(); | |
4237 } | |
4238 | |
4239 RegExpImpl::SetLastMatchInfo(regexp_info, subject, capture_count, | |
4240 global_cache.LastSuccessfulMatch()); | |
4241 | |
4242 int matches = offsets.length() / 2; | |
4243 Handle<FixedArray> elements = isolate->factory()->NewFixedArray(matches); | |
4244 Handle<String> substring = | |
4245 isolate->factory()->NewSubString(subject, offsets.at(0), offsets.at(1)); | |
4246 elements->set(0, *substring); | |
4247 for (int i = 1; i < matches; i++) { | |
4248 HandleScope temp_scope(isolate); | |
4249 int from = offsets.at(i * 2); | |
4250 int to = offsets.at(i * 2 + 1); | |
4251 Handle<String> substring = | |
4252 isolate->factory()->NewProperSubString(subject, from, to); | |
4253 elements->set(i, *substring); | |
4254 } | |
4255 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements(elements); | |
4256 result->set_length(Smi::FromInt(matches)); | |
4257 return *result; | |
4258 } | |
4259 | |
4260 | |
4261 // Only called from Runtime_RegExpExecMultiple so it doesn't need to maintain | |
4262 // separate last match info. See comment on that function. | |
4263 template <bool has_capture> | |
4264 static Object* SearchRegExpMultiple(Isolate* isolate, Handle<String> subject, | |
4265 Handle<JSRegExp> regexp, | |
4266 Handle<JSArray> last_match_array, | |
4267 Handle<JSArray> result_array) { | |
4268 DCHECK(subject->IsFlat()); | |
4269 DCHECK_NE(has_capture, regexp->CaptureCount() == 0); | |
4270 | |
4271 int capture_count = regexp->CaptureCount(); | |
4272 int subject_length = subject->length(); | |
4273 | |
4274 static const int kMinLengthToCache = 0x1000; | |
4275 | |
4276 if (subject_length > kMinLengthToCache) { | |
4277 Handle<Object> cached_answer( | |
4278 RegExpResultsCache::Lookup(isolate->heap(), *subject, regexp->data(), | |
4279 RegExpResultsCache::REGEXP_MULTIPLE_INDICES), | |
4280 isolate); | |
4281 if (*cached_answer != Smi::FromInt(0)) { | |
4282 Handle<FixedArray> cached_fixed_array = | |
4283 Handle<FixedArray>(FixedArray::cast(*cached_answer)); | |
4284 // The cache FixedArray is a COW-array and can therefore be reused. | |
4285 JSArray::SetContent(result_array, cached_fixed_array); | |
4286 // The actual length of the result array is stored in the last element of | |
4287 // the backing store (the backing FixedArray may have a larger capacity). | |
4288 Object* cached_fixed_array_last_element = | |
4289 cached_fixed_array->get(cached_fixed_array->length() - 1); | |
4290 Smi* js_array_length = Smi::cast(cached_fixed_array_last_element); | |
4291 result_array->set_length(js_array_length); | |
4292 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | |
4293 NULL); | |
4294 return *result_array; | |
4295 } | |
4296 } | |
4297 | |
4298 RegExpImpl::GlobalCache global_cache(regexp, subject, true, isolate); | |
4299 if (global_cache.HasException()) return isolate->heap()->exception(); | |
4300 | |
4301 // Ensured in Runtime_RegExpExecMultiple. | |
4302 DCHECK(result_array->HasFastObjectElements()); | |
4303 Handle<FixedArray> result_elements( | |
4304 FixedArray::cast(result_array->elements())); | |
4305 if (result_elements->length() < 16) { | |
4306 result_elements = isolate->factory()->NewFixedArrayWithHoles(16); | |
4307 } | |
4308 | |
4309 FixedArrayBuilder builder(result_elements); | |
4310 | |
4311 // Position to search from. | |
4312 int match_start = -1; | |
4313 int match_end = 0; | |
4314 bool first = true; | |
4315 | |
4316 // Two smis before and after the match, for very long strings. | |
4317 static const int kMaxBuilderEntriesPerRegExpMatch = 5; | |
4318 | |
4319 while (true) { | |
4320 int32_t* current_match = global_cache.FetchNext(); | |
4321 if (current_match == NULL) break; | |
4322 match_start = current_match[0]; | |
4323 builder.EnsureCapacity(kMaxBuilderEntriesPerRegExpMatch); | |
4324 if (match_end < match_start) { | |
4325 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, | |
4326 match_start); | |
4327 } | |
4328 match_end = current_match[1]; | |
4329 { | |
4330 // Avoid accumulating new handles inside loop. | |
4331 HandleScope temp_scope(isolate); | |
4332 Handle<String> match; | |
4333 if (!first) { | |
4334 match = isolate->factory()->NewProperSubString(subject, match_start, | |
4335 match_end); | |
4336 } else { | |
4337 match = | |
4338 isolate->factory()->NewSubString(subject, match_start, match_end); | |
4339 first = false; | |
4340 } | |
4341 | |
4342 if (has_capture) { | |
4343 // Arguments array to replace function is match, captures, index and | |
4344 // subject, i.e., 3 + capture count in total. | |
4345 Handle<FixedArray> elements = | |
4346 isolate->factory()->NewFixedArray(3 + capture_count); | |
4347 | |
4348 elements->set(0, *match); | |
4349 for (int i = 1; i <= capture_count; i++) { | |
4350 int start = current_match[i * 2]; | |
4351 if (start >= 0) { | |
4352 int end = current_match[i * 2 + 1]; | |
4353 DCHECK(start <= end); | |
4354 Handle<String> substring = | |
4355 isolate->factory()->NewSubString(subject, start, end); | |
4356 elements->set(i, *substring); | |
4357 } else { | |
4358 DCHECK(current_match[i * 2 + 1] < 0); | |
4359 elements->set(i, isolate->heap()->undefined_value()); | |
4360 } | |
4361 } | |
4362 elements->set(capture_count + 1, Smi::FromInt(match_start)); | |
4363 elements->set(capture_count + 2, *subject); | |
4364 builder.Add(*isolate->factory()->NewJSArrayWithElements(elements)); | |
4365 } else { | |
4366 builder.Add(*match); | |
4367 } | |
4368 } | |
4369 } | |
4370 | |
4371 if (global_cache.HasException()) return isolate->heap()->exception(); | |
4372 | |
4373 if (match_start >= 0) { | |
4374 // Finished matching, with at least one match. | |
4375 if (match_end < subject_length) { | |
4376 ReplacementStringBuilder::AddSubjectSlice(&builder, match_end, | |
4377 subject_length); | |
4378 } | |
4379 | |
4380 RegExpImpl::SetLastMatchInfo(last_match_array, subject, capture_count, | |
4381 NULL); | |
4382 | |
4383 if (subject_length > kMinLengthToCache) { | |
4384 // Store the length of the result array into the last element of the | |
4385 // backing FixedArray. | |
4386 builder.EnsureCapacity(1); | |
4387 Handle<FixedArray> fixed_array = builder.array(); | |
4388 fixed_array->set(fixed_array->length() - 1, | |
4389 Smi::FromInt(builder.length())); | |
4390 // Cache the result and turn the FixedArray into a COW array. | |
4391 RegExpResultsCache::Enter(isolate, subject, | |
4392 handle(regexp->data(), isolate), fixed_array, | |
4393 RegExpResultsCache::REGEXP_MULTIPLE_INDICES); | |
4394 } | |
4395 return *builder.ToJSArray(result_array); | |
4396 } else { | |
4397 return isolate->heap()->null_value(); // No matches at all. | |
4398 } | |
4399 } | |
4400 | |
4401 | |
4402 // This is only called for StringReplaceGlobalRegExpWithFunction. This sets | |
4403 // lastMatchInfoOverride to maintain the last match info, so we don't need to | |
4404 // set any other last match array info. | |
4405 RUNTIME_FUNCTION(Runtime_RegExpExecMultiple) { | |
4406 HandleScope handles(isolate); | |
4407 DCHECK(args.length() == 4); | |
4408 | |
4409 CONVERT_ARG_HANDLE_CHECKED(String, subject, 1); | |
4410 CONVERT_ARG_HANDLE_CHECKED(JSRegExp, regexp, 0); | |
4411 CONVERT_ARG_HANDLE_CHECKED(JSArray, last_match_info, 2); | |
4412 CONVERT_ARG_HANDLE_CHECKED(JSArray, result_array, 3); | |
4413 RUNTIME_ASSERT(last_match_info->HasFastObjectElements()); | |
4414 RUNTIME_ASSERT(result_array->HasFastObjectElements()); | |
4415 | |
4416 subject = String::Flatten(subject); | |
4417 RUNTIME_ASSERT(regexp->GetFlags().is_global()); | |
4418 | |
4419 if (regexp->CaptureCount() == 0) { | |
4420 return SearchRegExpMultiple<false>(isolate, subject, regexp, | |
4421 last_match_info, result_array); | |
4422 } else { | |
4423 return SearchRegExpMultiple<true>(isolate, subject, regexp, last_match_info, | |
4424 result_array); | |
4425 } | |
4426 } | |
4427 | |
4428 | |
4429 RUNTIME_FUNCTION(Runtime_NumberToRadixString) { | 2862 RUNTIME_FUNCTION(Runtime_NumberToRadixString) { |
4430 HandleScope scope(isolate); | 2863 HandleScope scope(isolate); |
4431 DCHECK(args.length() == 2); | 2864 DCHECK(args.length() == 2); |
4432 CONVERT_SMI_ARG_CHECKED(radix, 1); | 2865 CONVERT_SMI_ARG_CHECKED(radix, 1); |
4433 RUNTIME_ASSERT(2 <= radix && radix <= 36); | 2866 RUNTIME_ASSERT(2 <= radix && radix <= 36); |
4434 | 2867 |
4435 // Fast case where the result is a one character string. | 2868 // Fast case where the result is a one character string. |
4436 if (args[0]->IsSmi()) { | 2869 if (args[0]->IsSmi()) { |
4437 int value = args.smi_at(0); | 2870 int value = args.smi_at(0); |
4438 if (value >= 0 && value < radix) { | 2871 if (value >= 0 && value < radix) { |
(...skipping 1580 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6019 // The current spec draft has not updated "ToNumber Applied to the String | 4452 // The current spec draft has not updated "ToNumber Applied to the String |
6020 // Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584 | 4453 // Type", https://bugs.ecmascript.org/show_bug.cgi?id=1584 |
6021 flags |= ALLOW_OCTAL | ALLOW_BINARY; | 4454 flags |= ALLOW_OCTAL | ALLOW_BINARY; |
6022 } | 4455 } |
6023 | 4456 |
6024 return *isolate->factory()->NewNumber( | 4457 return *isolate->factory()->NewNumber( |
6025 StringToDouble(isolate->unicode_cache(), *subject, flags)); | 4458 StringToDouble(isolate->unicode_cache(), *subject, flags)); |
6026 } | 4459 } |
6027 | 4460 |
6028 | 4461 |
6029 RUNTIME_FUNCTION(Runtime_NewString) { | |
6030 HandleScope scope(isolate); | |
6031 DCHECK(args.length() == 2); | |
6032 CONVERT_INT32_ARG_CHECKED(length, 0); | |
6033 CONVERT_BOOLEAN_ARG_CHECKED(is_one_byte, 1); | |
6034 if (length == 0) return isolate->heap()->empty_string(); | |
6035 Handle<String> result; | |
6036 if (is_one_byte) { | |
6037 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6038 isolate, result, isolate->factory()->NewRawOneByteString(length)); | |
6039 } else { | |
6040 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6041 isolate, result, isolate->factory()->NewRawTwoByteString(length)); | |
6042 } | |
6043 return *result; | |
6044 } | |
6045 | |
6046 | |
6047 RUNTIME_FUNCTION(Runtime_TruncateString) { | |
6048 HandleScope scope(isolate); | |
6049 DCHECK(args.length() == 2); | |
6050 CONVERT_ARG_HANDLE_CHECKED(SeqString, string, 0); | |
6051 CONVERT_INT32_ARG_CHECKED(new_length, 1); | |
6052 RUNTIME_ASSERT(new_length >= 0); | |
6053 return *SeqString::Truncate(string, new_length); | |
6054 } | |
6055 | |
6056 | |
6057 RUNTIME_FUNCTION(Runtime_URIEscape) { | |
6058 HandleScope scope(isolate); | |
6059 DCHECK(args.length() == 1); | |
6060 CONVERT_ARG_HANDLE_CHECKED(String, source, 0); | |
6061 Handle<String> string = String::Flatten(source); | |
6062 DCHECK(string->IsFlat()); | |
6063 Handle<String> result; | |
6064 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6065 isolate, result, string->IsOneByteRepresentationUnderneath() | |
6066 ? URIEscape::Escape<uint8_t>(isolate, source) | |
6067 : URIEscape::Escape<uc16>(isolate, source)); | |
6068 return *result; | |
6069 } | |
6070 | |
6071 | |
6072 RUNTIME_FUNCTION(Runtime_URIUnescape) { | |
6073 HandleScope scope(isolate); | |
6074 DCHECK(args.length() == 1); | |
6075 CONVERT_ARG_HANDLE_CHECKED(String, source, 0); | |
6076 Handle<String> string = String::Flatten(source); | |
6077 DCHECK(string->IsFlat()); | |
6078 Handle<String> result; | |
6079 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6080 isolate, result, string->IsOneByteRepresentationUnderneath() | |
6081 ? URIUnescape::Unescape<uint8_t>(isolate, source) | |
6082 : URIUnescape::Unescape<uc16>(isolate, source)); | |
6083 return *result; | |
6084 } | |
6085 | |
6086 | |
6087 RUNTIME_FUNCTION(Runtime_QuoteJSONString) { | |
6088 HandleScope scope(isolate); | |
6089 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); | |
6090 DCHECK(args.length() == 1); | |
6091 Handle<Object> result; | |
6092 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6093 isolate, result, BasicJsonStringifier::StringifyString(isolate, string)); | |
6094 return *result; | |
6095 } | |
6096 | |
6097 | |
6098 RUNTIME_FUNCTION(Runtime_BasicJSONStringify) { | |
6099 HandleScope scope(isolate); | |
6100 DCHECK(args.length() == 1); | |
6101 CONVERT_ARG_HANDLE_CHECKED(Object, object, 0); | |
6102 BasicJsonStringifier stringifier(isolate); | |
6103 Handle<Object> result; | |
6104 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, | |
6105 stringifier.Stringify(object)); | |
6106 return *result; | |
6107 } | |
6108 | |
6109 | |
6110 RUNTIME_FUNCTION(Runtime_StringParseInt) { | 4462 RUNTIME_FUNCTION(Runtime_StringParseInt) { |
6111 HandleScope handle_scope(isolate); | 4463 HandleScope handle_scope(isolate); |
6112 DCHECK(args.length() == 2); | 4464 DCHECK(args.length() == 2); |
6113 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | 4465 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
6114 CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]); | 4466 CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]); |
6115 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36)); | 4467 RUNTIME_ASSERT(radix == 0 || (2 <= radix && radix <= 36)); |
6116 | 4468 |
6117 subject = String::Flatten(subject); | 4469 subject = String::Flatten(subject); |
6118 double value; | 4470 double value; |
6119 | 4471 |
(...skipping 20 matching lines...) Expand all Loading... |
6140 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | 4492 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); |
6141 | 4493 |
6142 subject = String::Flatten(subject); | 4494 subject = String::Flatten(subject); |
6143 double value = StringToDouble(isolate->unicode_cache(), *subject, | 4495 double value = StringToDouble(isolate->unicode_cache(), *subject, |
6144 ALLOW_TRAILING_JUNK, base::OS::nan_value()); | 4496 ALLOW_TRAILING_JUNK, base::OS::nan_value()); |
6145 | 4497 |
6146 return *isolate->factory()->NewNumber(value); | 4498 return *isolate->factory()->NewNumber(value); |
6147 } | 4499 } |
6148 | 4500 |
6149 | 4501 |
6150 static inline bool ToUpperOverflows(uc32 character) { | |
6151 // y with umlauts and the micro sign are the only characters that stop | |
6152 // fitting into one-byte when converting to uppercase. | |
6153 static const uc32 yuml_code = 0xff; | |
6154 static const uc32 micro_code = 0xb5; | |
6155 return (character == yuml_code || character == micro_code); | |
6156 } | |
6157 | |
6158 | |
6159 template <class Converter> | |
6160 MUST_USE_RESULT static Object* ConvertCaseHelper( | |
6161 Isolate* isolate, String* string, SeqString* result, int result_length, | |
6162 unibrow::Mapping<Converter, 128>* mapping) { | |
6163 DisallowHeapAllocation no_gc; | |
6164 // We try this twice, once with the assumption that the result is no longer | |
6165 // than the input and, if that assumption breaks, again with the exact | |
6166 // length. This may not be pretty, but it is nicer than what was here before | |
6167 // and I hereby claim my vaffel-is. | |
6168 // | |
6169 // NOTE: This assumes that the upper/lower case of an ASCII | |
6170 // character is also ASCII. This is currently the case, but it | |
6171 // might break in the future if we implement more context and locale | |
6172 // dependent upper/lower conversions. | |
6173 bool has_changed_character = false; | |
6174 | |
6175 // Convert all characters to upper case, assuming that they will fit | |
6176 // in the buffer | |
6177 Access<ConsStringIteratorOp> op(isolate->runtime_state()->string_iterator()); | |
6178 StringCharacterStream stream(string, op.value()); | |
6179 unibrow::uchar chars[Converter::kMaxWidth]; | |
6180 // We can assume that the string is not empty | |
6181 uc32 current = stream.GetNext(); | |
6182 bool ignore_overflow = Converter::kIsToLower || result->IsSeqTwoByteString(); | |
6183 for (int i = 0; i < result_length;) { | |
6184 bool has_next = stream.HasMore(); | |
6185 uc32 next = has_next ? stream.GetNext() : 0; | |
6186 int char_length = mapping->get(current, next, chars); | |
6187 if (char_length == 0) { | |
6188 // The case conversion of this character is the character itself. | |
6189 result->Set(i, current); | |
6190 i++; | |
6191 } else if (char_length == 1 && | |
6192 (ignore_overflow || !ToUpperOverflows(current))) { | |
6193 // Common case: converting the letter resulted in one character. | |
6194 DCHECK(static_cast<uc32>(chars[0]) != current); | |
6195 result->Set(i, chars[0]); | |
6196 has_changed_character = true; | |
6197 i++; | |
6198 } else if (result_length == string->length()) { | |
6199 bool overflows = ToUpperOverflows(current); | |
6200 // We've assumed that the result would be as long as the | |
6201 // input but here is a character that converts to several | |
6202 // characters. No matter, we calculate the exact length | |
6203 // of the result and try the whole thing again. | |
6204 // | |
6205 // Note that this leaves room for optimization. We could just | |
6206 // memcpy what we already have to the result string. Also, | |
6207 // the result string is the last object allocated we could | |
6208 // "realloc" it and probably, in the vast majority of cases, | |
6209 // extend the existing string to be able to hold the full | |
6210 // result. | |
6211 int next_length = 0; | |
6212 if (has_next) { | |
6213 next_length = mapping->get(next, 0, chars); | |
6214 if (next_length == 0) next_length = 1; | |
6215 } | |
6216 int current_length = i + char_length + next_length; | |
6217 while (stream.HasMore()) { | |
6218 current = stream.GetNext(); | |
6219 overflows |= ToUpperOverflows(current); | |
6220 // NOTE: we use 0 as the next character here because, while | |
6221 // the next character may affect what a character converts to, | |
6222 // it does not in any case affect the length of what it convert | |
6223 // to. | |
6224 int char_length = mapping->get(current, 0, chars); | |
6225 if (char_length == 0) char_length = 1; | |
6226 current_length += char_length; | |
6227 if (current_length > String::kMaxLength) { | |
6228 AllowHeapAllocation allocate_error_and_return; | |
6229 THROW_NEW_ERROR_RETURN_FAILURE(isolate, | |
6230 NewInvalidStringLengthError()); | |
6231 } | |
6232 } | |
6233 // Try again with the real length. Return signed if we need | |
6234 // to allocate a two-byte string for to uppercase. | |
6235 return (overflows && !ignore_overflow) ? Smi::FromInt(-current_length) | |
6236 : Smi::FromInt(current_length); | |
6237 } else { | |
6238 for (int j = 0; j < char_length; j++) { | |
6239 result->Set(i, chars[j]); | |
6240 i++; | |
6241 } | |
6242 has_changed_character = true; | |
6243 } | |
6244 current = next; | |
6245 } | |
6246 if (has_changed_character) { | |
6247 return result; | |
6248 } else { | |
6249 // If we didn't actually change anything in doing the conversion | |
6250 // we simple return the result and let the converted string | |
6251 // become garbage; there is no reason to keep two identical strings | |
6252 // alive. | |
6253 return string; | |
6254 } | |
6255 } | |
6256 | |
6257 | |
6258 namespace { | |
6259 | |
6260 static const uintptr_t kOneInEveryByte = kUintptrAllBitsSet / 0xFF; | |
6261 static const uintptr_t kAsciiMask = kOneInEveryByte << 7; | |
6262 | |
6263 // Given a word and two range boundaries returns a word with high bit | |
6264 // set in every byte iff the corresponding input byte was strictly in | |
6265 // the range (m, n). All the other bits in the result are cleared. | |
6266 // This function is only useful when it can be inlined and the | |
6267 // boundaries are statically known. | |
6268 // Requires: all bytes in the input word and the boundaries must be | |
6269 // ASCII (less than 0x7F). | |
6270 static inline uintptr_t AsciiRangeMask(uintptr_t w, char m, char n) { | |
6271 // Use strict inequalities since in edge cases the function could be | |
6272 // further simplified. | |
6273 DCHECK(0 < m && m < n); | |
6274 // Has high bit set in every w byte less than n. | |
6275 uintptr_t tmp1 = kOneInEveryByte * (0x7F + n) - w; | |
6276 // Has high bit set in every w byte greater than m. | |
6277 uintptr_t tmp2 = w + kOneInEveryByte * (0x7F - m); | |
6278 return (tmp1 & tmp2 & (kOneInEveryByte * 0x80)); | |
6279 } | |
6280 | |
6281 | |
6282 #ifdef DEBUG | |
6283 static bool CheckFastAsciiConvert(char* dst, const char* src, int length, | |
6284 bool changed, bool is_to_lower) { | |
6285 bool expected_changed = false; | |
6286 for (int i = 0; i < length; i++) { | |
6287 if (dst[i] == src[i]) continue; | |
6288 expected_changed = true; | |
6289 if (is_to_lower) { | |
6290 DCHECK('A' <= src[i] && src[i] <= 'Z'); | |
6291 DCHECK(dst[i] == src[i] + ('a' - 'A')); | |
6292 } else { | |
6293 DCHECK('a' <= src[i] && src[i] <= 'z'); | |
6294 DCHECK(dst[i] == src[i] - ('a' - 'A')); | |
6295 } | |
6296 } | |
6297 return (expected_changed == changed); | |
6298 } | |
6299 #endif | |
6300 | |
6301 | |
6302 template <class Converter> | |
6303 static bool FastAsciiConvert(char* dst, const char* src, int length, | |
6304 bool* changed_out) { | |
6305 #ifdef DEBUG | |
6306 char* saved_dst = dst; | |
6307 const char* saved_src = src; | |
6308 #endif | |
6309 DisallowHeapAllocation no_gc; | |
6310 // We rely on the distance between upper and lower case letters | |
6311 // being a known power of 2. | |
6312 DCHECK('a' - 'A' == (1 << 5)); | |
6313 // Boundaries for the range of input characters than require conversion. | |
6314 static const char lo = Converter::kIsToLower ? 'A' - 1 : 'a' - 1; | |
6315 static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1; | |
6316 bool changed = false; | |
6317 uintptr_t or_acc = 0; | |
6318 const char* const limit = src + length; | |
6319 | |
6320 // dst is newly allocated and always aligned. | |
6321 DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t))); | |
6322 // Only attempt processing one word at a time if src is also aligned. | |
6323 if (IsAligned(reinterpret_cast<intptr_t>(src), sizeof(uintptr_t))) { | |
6324 // Process the prefix of the input that requires no conversion one aligned | |
6325 // (machine) word at a time. | |
6326 while (src <= limit - sizeof(uintptr_t)) { | |
6327 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); | |
6328 or_acc |= w; | |
6329 if (AsciiRangeMask(w, lo, hi) != 0) { | |
6330 changed = true; | |
6331 break; | |
6332 } | |
6333 *reinterpret_cast<uintptr_t*>(dst) = w; | |
6334 src += sizeof(uintptr_t); | |
6335 dst += sizeof(uintptr_t); | |
6336 } | |
6337 // Process the remainder of the input performing conversion when | |
6338 // required one word at a time. | |
6339 while (src <= limit - sizeof(uintptr_t)) { | |
6340 const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); | |
6341 or_acc |= w; | |
6342 uintptr_t m = AsciiRangeMask(w, lo, hi); | |
6343 // The mask has high (7th) bit set in every byte that needs | |
6344 // conversion and we know that the distance between cases is | |
6345 // 1 << 5. | |
6346 *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2); | |
6347 src += sizeof(uintptr_t); | |
6348 dst += sizeof(uintptr_t); | |
6349 } | |
6350 } | |
6351 // Process the last few bytes of the input (or the whole input if | |
6352 // unaligned access is not supported). | |
6353 while (src < limit) { | |
6354 char c = *src; | |
6355 or_acc |= c; | |
6356 if (lo < c && c < hi) { | |
6357 c ^= (1 << 5); | |
6358 changed = true; | |
6359 } | |
6360 *dst = c; | |
6361 ++src; | |
6362 ++dst; | |
6363 } | |
6364 | |
6365 if ((or_acc & kAsciiMask) != 0) return false; | |
6366 | |
6367 DCHECK(CheckFastAsciiConvert(saved_dst, saved_src, length, changed, | |
6368 Converter::kIsToLower)); | |
6369 | |
6370 *changed_out = changed; | |
6371 return true; | |
6372 } | |
6373 | |
6374 } // namespace | |
6375 | |
6376 | |
6377 template <class Converter> | |
6378 MUST_USE_RESULT static Object* ConvertCase( | |
6379 Handle<String> s, Isolate* isolate, | |
6380 unibrow::Mapping<Converter, 128>* mapping) { | |
6381 s = String::Flatten(s); | |
6382 int length = s->length(); | |
6383 // Assume that the string is not empty; we need this assumption later | |
6384 if (length == 0) return *s; | |
6385 | |
6386 // Simpler handling of ASCII strings. | |
6387 // | |
6388 // NOTE: This assumes that the upper/lower case of an ASCII | |
6389 // character is also ASCII. This is currently the case, but it | |
6390 // might break in the future if we implement more context and locale | |
6391 // dependent upper/lower conversions. | |
6392 if (s->IsOneByteRepresentationUnderneath()) { | |
6393 // Same length as input. | |
6394 Handle<SeqOneByteString> result = | |
6395 isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); | |
6396 DisallowHeapAllocation no_gc; | |
6397 String::FlatContent flat_content = s->GetFlatContent(); | |
6398 DCHECK(flat_content.IsFlat()); | |
6399 bool has_changed_character = false; | |
6400 bool is_ascii = FastAsciiConvert<Converter>( | |
6401 reinterpret_cast<char*>(result->GetChars()), | |
6402 reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()), | |
6403 length, &has_changed_character); | |
6404 // If not ASCII, we discard the result and take the 2 byte path. | |
6405 if (is_ascii) return has_changed_character ? *result : *s; | |
6406 } | |
6407 | |
6408 Handle<SeqString> result; // Same length as input. | |
6409 if (s->IsOneByteRepresentation()) { | |
6410 result = isolate->factory()->NewRawOneByteString(length).ToHandleChecked(); | |
6411 } else { | |
6412 result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked(); | |
6413 } | |
6414 | |
6415 Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping); | |
6416 if (answer->IsException() || answer->IsString()) return answer; | |
6417 | |
6418 DCHECK(answer->IsSmi()); | |
6419 length = Smi::cast(answer)->value(); | |
6420 if (s->IsOneByteRepresentation() && length > 0) { | |
6421 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6422 isolate, result, isolate->factory()->NewRawOneByteString(length)); | |
6423 } else { | |
6424 if (length < 0) length = -length; | |
6425 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6426 isolate, result, isolate->factory()->NewRawTwoByteString(length)); | |
6427 } | |
6428 return ConvertCaseHelper(isolate, *s, *result, length, mapping); | |
6429 } | |
6430 | |
6431 | |
6432 RUNTIME_FUNCTION(Runtime_StringToLowerCase) { | |
6433 HandleScope scope(isolate); | |
6434 DCHECK(args.length() == 1); | |
6435 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); | |
6436 return ConvertCase(s, isolate, isolate->runtime_state()->to_lower_mapping()); | |
6437 } | |
6438 | |
6439 | |
6440 RUNTIME_FUNCTION(Runtime_StringToUpperCase) { | |
6441 HandleScope scope(isolate); | |
6442 DCHECK(args.length() == 1); | |
6443 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); | |
6444 return ConvertCase(s, isolate, isolate->runtime_state()->to_upper_mapping()); | |
6445 } | |
6446 | |
6447 | |
6448 RUNTIME_FUNCTION(Runtime_StringTrim) { | |
6449 HandleScope scope(isolate); | |
6450 DCHECK(args.length() == 3); | |
6451 | |
6452 CONVERT_ARG_HANDLE_CHECKED(String, string, 0); | |
6453 CONVERT_BOOLEAN_ARG_CHECKED(trimLeft, 1); | |
6454 CONVERT_BOOLEAN_ARG_CHECKED(trimRight, 2); | |
6455 | |
6456 string = String::Flatten(string); | |
6457 int length = string->length(); | |
6458 | |
6459 int left = 0; | |
6460 UnicodeCache* unicode_cache = isolate->unicode_cache(); | |
6461 if (trimLeft) { | |
6462 while (left < length && | |
6463 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) { | |
6464 left++; | |
6465 } | |
6466 } | |
6467 | |
6468 int right = length; | |
6469 if (trimRight) { | |
6470 while ( | |
6471 right > left && | |
6472 unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(right - 1))) { | |
6473 right--; | |
6474 } | |
6475 } | |
6476 | |
6477 return *isolate->factory()->NewSubString(string, left, right); | |
6478 } | |
6479 | |
6480 | |
6481 RUNTIME_FUNCTION(Runtime_StringSplit) { | |
6482 HandleScope handle_scope(isolate); | |
6483 DCHECK(args.length() == 3); | |
6484 CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); | |
6485 CONVERT_ARG_HANDLE_CHECKED(String, pattern, 1); | |
6486 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[2]); | |
6487 RUNTIME_ASSERT(limit > 0); | |
6488 | |
6489 int subject_length = subject->length(); | |
6490 int pattern_length = pattern->length(); | |
6491 RUNTIME_ASSERT(pattern_length > 0); | |
6492 | |
6493 if (limit == 0xffffffffu) { | |
6494 Handle<Object> cached_answer( | |
6495 RegExpResultsCache::Lookup(isolate->heap(), *subject, *pattern, | |
6496 RegExpResultsCache::STRING_SPLIT_SUBSTRINGS), | |
6497 isolate); | |
6498 if (*cached_answer != Smi::FromInt(0)) { | |
6499 // The cache FixedArray is a COW-array and can therefore be reused. | |
6500 Handle<JSArray> result = isolate->factory()->NewJSArrayWithElements( | |
6501 Handle<FixedArray>::cast(cached_answer)); | |
6502 return *result; | |
6503 } | |
6504 } | |
6505 | |
6506 // The limit can be very large (0xffffffffu), but since the pattern | |
6507 // isn't empty, we can never create more parts than ~half the length | |
6508 // of the subject. | |
6509 | |
6510 subject = String::Flatten(subject); | |
6511 pattern = String::Flatten(pattern); | |
6512 | |
6513 static const int kMaxInitialListCapacity = 16; | |
6514 | |
6515 ZoneScope zone_scope(isolate->runtime_zone()); | |
6516 | |
6517 // Find (up to limit) indices of separator and end-of-string in subject | |
6518 int initial_capacity = Min<uint32_t>(kMaxInitialListCapacity, limit); | |
6519 ZoneList<int> indices(initial_capacity, zone_scope.zone()); | |
6520 | |
6521 FindStringIndicesDispatch(isolate, *subject, *pattern, &indices, limit, | |
6522 zone_scope.zone()); | |
6523 | |
6524 if (static_cast<uint32_t>(indices.length()) < limit) { | |
6525 indices.Add(subject_length, zone_scope.zone()); | |
6526 } | |
6527 | |
6528 // The list indices now contains the end of each part to create. | |
6529 | |
6530 // Create JSArray of substrings separated by separator. | |
6531 int part_count = indices.length(); | |
6532 | |
6533 Handle<JSArray> result = isolate->factory()->NewJSArray(part_count); | |
6534 JSObject::EnsureCanContainHeapObjectElements(result); | |
6535 result->set_length(Smi::FromInt(part_count)); | |
6536 | |
6537 DCHECK(result->HasFastObjectElements()); | |
6538 | |
6539 if (part_count == 1 && indices.at(0) == subject_length) { | |
6540 FixedArray::cast(result->elements())->set(0, *subject); | |
6541 return *result; | |
6542 } | |
6543 | |
6544 Handle<FixedArray> elements(FixedArray::cast(result->elements())); | |
6545 int part_start = 0; | |
6546 for (int i = 0; i < part_count; i++) { | |
6547 HandleScope local_loop_handle(isolate); | |
6548 int part_end = indices.at(i); | |
6549 Handle<String> substring = | |
6550 isolate->factory()->NewProperSubString(subject, part_start, part_end); | |
6551 elements->set(i, *substring); | |
6552 part_start = part_end + pattern_length; | |
6553 } | |
6554 | |
6555 if (limit == 0xffffffffu) { | |
6556 if (result->HasFastObjectElements()) { | |
6557 RegExpResultsCache::Enter(isolate, subject, pattern, elements, | |
6558 RegExpResultsCache::STRING_SPLIT_SUBSTRINGS); | |
6559 } | |
6560 } | |
6561 | |
6562 return *result; | |
6563 } | |
6564 | |
6565 | |
6566 // Copies Latin1 characters to the given fixed array looking up | |
6567 // one-char strings in the cache. Gives up on the first char that is | |
6568 // not in the cache and fills the remainder with smi zeros. Returns | |
6569 // the length of the successfully copied prefix. | |
6570 static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars, | |
6571 FixedArray* elements, int length) { | |
6572 DisallowHeapAllocation no_gc; | |
6573 FixedArray* one_byte_cache = heap->single_character_string_cache(); | |
6574 Object* undefined = heap->undefined_value(); | |
6575 int i; | |
6576 WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); | |
6577 for (i = 0; i < length; ++i) { | |
6578 Object* value = one_byte_cache->get(chars[i]); | |
6579 if (value == undefined) break; | |
6580 elements->set(i, value, mode); | |
6581 } | |
6582 if (i < length) { | |
6583 DCHECK(Smi::FromInt(0) == 0); | |
6584 memset(elements->data_start() + i, 0, kPointerSize * (length - i)); | |
6585 } | |
6586 #ifdef DEBUG | |
6587 for (int j = 0; j < length; ++j) { | |
6588 Object* element = elements->get(j); | |
6589 DCHECK(element == Smi::FromInt(0) || | |
6590 (element->IsString() && String::cast(element)->LooksValid())); | |
6591 } | |
6592 #endif | |
6593 return i; | |
6594 } | |
6595 | |
6596 | |
6597 // Converts a String to JSArray. | |
6598 // For example, "foo" => ["f", "o", "o"]. | |
6599 RUNTIME_FUNCTION(Runtime_StringToArray) { | |
6600 HandleScope scope(isolate); | |
6601 DCHECK(args.length() == 2); | |
6602 CONVERT_ARG_HANDLE_CHECKED(String, s, 0); | |
6603 CONVERT_NUMBER_CHECKED(uint32_t, limit, Uint32, args[1]); | |
6604 | |
6605 s = String::Flatten(s); | |
6606 const int length = static_cast<int>(Min<uint32_t>(s->length(), limit)); | |
6607 | |
6608 Handle<FixedArray> elements; | |
6609 int position = 0; | |
6610 if (s->IsFlat() && s->IsOneByteRepresentation()) { | |
6611 // Try using cached chars where possible. | |
6612 elements = isolate->factory()->NewUninitializedFixedArray(length); | |
6613 | |
6614 DisallowHeapAllocation no_gc; | |
6615 String::FlatContent content = s->GetFlatContent(); | |
6616 if (content.IsOneByte()) { | |
6617 Vector<const uint8_t> chars = content.ToOneByteVector(); | |
6618 // Note, this will initialize all elements (not only the prefix) | |
6619 // to prevent GC from seeing partially initialized array. | |
6620 position = CopyCachedOneByteCharsToArray(isolate->heap(), chars.start(), | |
6621 *elements, length); | |
6622 } else { | |
6623 MemsetPointer(elements->data_start(), isolate->heap()->undefined_value(), | |
6624 length); | |
6625 } | |
6626 } else { | |
6627 elements = isolate->factory()->NewFixedArray(length); | |
6628 } | |
6629 for (int i = position; i < length; ++i) { | |
6630 Handle<Object> str = | |
6631 isolate->factory()->LookupSingleCharacterStringFromCode(s->Get(i)); | |
6632 elements->set(i, *str); | |
6633 } | |
6634 | |
6635 #ifdef DEBUG | |
6636 for (int i = 0; i < length; ++i) { | |
6637 DCHECK(String::cast(elements->get(i))->length() == 1); | |
6638 } | |
6639 #endif | |
6640 | |
6641 return *isolate->factory()->NewJSArrayWithElements(elements); | |
6642 } | |
6643 | |
6644 | |
6645 RUNTIME_FUNCTION(Runtime_NewStringWrapper) { | 4502 RUNTIME_FUNCTION(Runtime_NewStringWrapper) { |
6646 HandleScope scope(isolate); | 4503 HandleScope scope(isolate); |
6647 DCHECK(args.length() == 1); | 4504 DCHECK(args.length() == 1); |
6648 CONVERT_ARG_HANDLE_CHECKED(String, value, 0); | 4505 CONVERT_ARG_HANDLE_CHECKED(String, value, 0); |
6649 return *Object::ToObject(isolate, value).ToHandleChecked(); | 4506 return *Object::ToObject(isolate, value).ToHandleChecked(); |
6650 } | 4507 } |
6651 | 4508 |
6652 | 4509 |
6653 bool Runtime::IsUpperCaseChar(RuntimeState* runtime_state, uint16_t ch) { | |
6654 unibrow::uchar chars[unibrow::ToUppercase::kMaxWidth]; | |
6655 int char_length = runtime_state->to_upper_mapping()->get(ch, 0, chars); | |
6656 return char_length == 0; | |
6657 } | |
6658 | |
6659 | |
6660 RUNTIME_FUNCTION(Runtime_NumberToStringRT) { | 4510 RUNTIME_FUNCTION(Runtime_NumberToStringRT) { |
6661 HandleScope scope(isolate); | 4511 HandleScope scope(isolate); |
6662 DCHECK(args.length() == 1); | 4512 DCHECK(args.length() == 1); |
6663 CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0); | 4513 CONVERT_NUMBER_ARG_HANDLE_CHECKED(number, 0); |
6664 | 4514 |
6665 return *isolate->factory()->NumberToString(number); | 4515 return *isolate->factory()->NumberToString(number); |
6666 } | 4516 } |
6667 | 4517 |
6668 | 4518 |
6669 RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) { | 4519 RUNTIME_FUNCTION(Runtime_NumberToStringSkipCache) { |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6807 | 4657 |
6808 // We rely on implementation-defined behavior below, but at least not on | 4658 // We rely on implementation-defined behavior below, but at least not on |
6809 // undefined behavior. | 4659 // undefined behavior. |
6810 CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]); | 4660 CONVERT_NUMBER_CHECKED(uint32_t, x, Int32, args[0]); |
6811 CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]); | 4661 CONVERT_NUMBER_CHECKED(uint32_t, y, Int32, args[1]); |
6812 int32_t product = static_cast<int32_t>(x * y); | 4662 int32_t product = static_cast<int32_t>(x * y); |
6813 return *isolate->factory()->NewNumberFromInt(product); | 4663 return *isolate->factory()->NewNumberFromInt(product); |
6814 } | 4664 } |
6815 | 4665 |
6816 | 4666 |
6817 RUNTIME_FUNCTION(Runtime_StringAdd) { | |
6818 HandleScope scope(isolate); | |
6819 DCHECK(args.length() == 2); | |
6820 CONVERT_ARG_HANDLE_CHECKED(String, str1, 0); | |
6821 CONVERT_ARG_HANDLE_CHECKED(String, str2, 1); | |
6822 isolate->counters()->string_add_runtime()->Increment(); | |
6823 Handle<String> result; | |
6824 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6825 isolate, result, isolate->factory()->NewConsString(str1, str2)); | |
6826 return *result; | |
6827 } | |
6828 | |
6829 | |
6830 template <typename sinkchar> | |
6831 static inline void StringBuilderConcatHelper(String* special, sinkchar* sink, | |
6832 FixedArray* fixed_array, | |
6833 int array_length) { | |
6834 DisallowHeapAllocation no_gc; | |
6835 int position = 0; | |
6836 for (int i = 0; i < array_length; i++) { | |
6837 Object* element = fixed_array->get(i); | |
6838 if (element->IsSmi()) { | |
6839 // Smi encoding of position and length. | |
6840 int encoded_slice = Smi::cast(element)->value(); | |
6841 int pos; | |
6842 int len; | |
6843 if (encoded_slice > 0) { | |
6844 // Position and length encoded in one smi. | |
6845 pos = StringBuilderSubstringPosition::decode(encoded_slice); | |
6846 len = StringBuilderSubstringLength::decode(encoded_slice); | |
6847 } else { | |
6848 // Position and length encoded in two smis. | |
6849 Object* obj = fixed_array->get(++i); | |
6850 DCHECK(obj->IsSmi()); | |
6851 pos = Smi::cast(obj)->value(); | |
6852 len = -encoded_slice; | |
6853 } | |
6854 String::WriteToFlat(special, sink + position, pos, pos + len); | |
6855 position += len; | |
6856 } else { | |
6857 String* string = String::cast(element); | |
6858 int element_length = string->length(); | |
6859 String::WriteToFlat(string, sink + position, 0, element_length); | |
6860 position += element_length; | |
6861 } | |
6862 } | |
6863 } | |
6864 | |
6865 | |
6866 // Returns the result length of the concatenation. | |
6867 // On illegal argument, -1 is returned. | |
6868 static inline int StringBuilderConcatLength(int special_length, | |
6869 FixedArray* fixed_array, | |
6870 int array_length, bool* one_byte) { | |
6871 DisallowHeapAllocation no_gc; | |
6872 int position = 0; | |
6873 for (int i = 0; i < array_length; i++) { | |
6874 int increment = 0; | |
6875 Object* elt = fixed_array->get(i); | |
6876 if (elt->IsSmi()) { | |
6877 // Smi encoding of position and length. | |
6878 int smi_value = Smi::cast(elt)->value(); | |
6879 int pos; | |
6880 int len; | |
6881 if (smi_value > 0) { | |
6882 // Position and length encoded in one smi. | |
6883 pos = StringBuilderSubstringPosition::decode(smi_value); | |
6884 len = StringBuilderSubstringLength::decode(smi_value); | |
6885 } else { | |
6886 // Position and length encoded in two smis. | |
6887 len = -smi_value; | |
6888 // Get the position and check that it is a positive smi. | |
6889 i++; | |
6890 if (i >= array_length) return -1; | |
6891 Object* next_smi = fixed_array->get(i); | |
6892 if (!next_smi->IsSmi()) return -1; | |
6893 pos = Smi::cast(next_smi)->value(); | |
6894 if (pos < 0) return -1; | |
6895 } | |
6896 DCHECK(pos >= 0); | |
6897 DCHECK(len >= 0); | |
6898 if (pos > special_length || len > special_length - pos) return -1; | |
6899 increment = len; | |
6900 } else if (elt->IsString()) { | |
6901 String* element = String::cast(elt); | |
6902 int element_length = element->length(); | |
6903 increment = element_length; | |
6904 if (*one_byte && !element->HasOnlyOneByteChars()) { | |
6905 *one_byte = false; | |
6906 } | |
6907 } else { | |
6908 return -1; | |
6909 } | |
6910 if (increment > String::kMaxLength - position) { | |
6911 return kMaxInt; // Provoke throw on allocation. | |
6912 } | |
6913 position += increment; | |
6914 } | |
6915 return position; | |
6916 } | |
6917 | |
6918 | |
6919 RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { | |
6920 HandleScope scope(isolate); | |
6921 DCHECK(args.length() == 3); | |
6922 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); | |
6923 int32_t array_length; | |
6924 if (!args[1]->ToInt32(&array_length)) { | |
6925 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); | |
6926 } | |
6927 CONVERT_ARG_HANDLE_CHECKED(String, special, 2); | |
6928 | |
6929 size_t actual_array_length = 0; | |
6930 RUNTIME_ASSERT( | |
6931 TryNumberToSize(isolate, array->length(), &actual_array_length)); | |
6932 RUNTIME_ASSERT(array_length >= 0); | |
6933 RUNTIME_ASSERT(static_cast<size_t>(array_length) <= actual_array_length); | |
6934 | |
6935 // This assumption is used by the slice encoding in one or two smis. | |
6936 DCHECK(Smi::kMaxValue >= String::kMaxLength); | |
6937 | |
6938 RUNTIME_ASSERT(array->HasFastElements()); | |
6939 JSObject::EnsureCanContainHeapObjectElements(array); | |
6940 | |
6941 int special_length = special->length(); | |
6942 if (!array->HasFastObjectElements()) { | |
6943 return isolate->Throw(isolate->heap()->illegal_argument_string()); | |
6944 } | |
6945 | |
6946 int length; | |
6947 bool one_byte = special->HasOnlyOneByteChars(); | |
6948 | |
6949 { | |
6950 DisallowHeapAllocation no_gc; | |
6951 FixedArray* fixed_array = FixedArray::cast(array->elements()); | |
6952 if (fixed_array->length() < array_length) { | |
6953 array_length = fixed_array->length(); | |
6954 } | |
6955 | |
6956 if (array_length == 0) { | |
6957 return isolate->heap()->empty_string(); | |
6958 } else if (array_length == 1) { | |
6959 Object* first = fixed_array->get(0); | |
6960 if (first->IsString()) return first; | |
6961 } | |
6962 length = StringBuilderConcatLength(special_length, fixed_array, | |
6963 array_length, &one_byte); | |
6964 } | |
6965 | |
6966 if (length == -1) { | |
6967 return isolate->Throw(isolate->heap()->illegal_argument_string()); | |
6968 } | |
6969 | |
6970 if (one_byte) { | |
6971 Handle<SeqOneByteString> answer; | |
6972 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6973 isolate, answer, isolate->factory()->NewRawOneByteString(length)); | |
6974 StringBuilderConcatHelper(*special, answer->GetChars(), | |
6975 FixedArray::cast(array->elements()), | |
6976 array_length); | |
6977 return *answer; | |
6978 } else { | |
6979 Handle<SeqTwoByteString> answer; | |
6980 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
6981 isolate, answer, isolate->factory()->NewRawTwoByteString(length)); | |
6982 StringBuilderConcatHelper(*special, answer->GetChars(), | |
6983 FixedArray::cast(array->elements()), | |
6984 array_length); | |
6985 return *answer; | |
6986 } | |
6987 } | |
6988 | |
6989 | |
6990 RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { | |
6991 HandleScope scope(isolate); | |
6992 DCHECK(args.length() == 3); | |
6993 CONVERT_ARG_HANDLE_CHECKED(JSArray, array, 0); | |
6994 int32_t array_length; | |
6995 if (!args[1]->ToInt32(&array_length)) { | |
6996 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); | |
6997 } | |
6998 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2); | |
6999 RUNTIME_ASSERT(array->HasFastObjectElements()); | |
7000 RUNTIME_ASSERT(array_length >= 0); | |
7001 | |
7002 Handle<FixedArray> fixed_array(FixedArray::cast(array->elements())); | |
7003 if (fixed_array->length() < array_length) { | |
7004 array_length = fixed_array->length(); | |
7005 } | |
7006 | |
7007 if (array_length == 0) { | |
7008 return isolate->heap()->empty_string(); | |
7009 } else if (array_length == 1) { | |
7010 Object* first = fixed_array->get(0); | |
7011 RUNTIME_ASSERT(first->IsString()); | |
7012 return first; | |
7013 } | |
7014 | |
7015 int separator_length = separator->length(); | |
7016 RUNTIME_ASSERT(separator_length > 0); | |
7017 int max_nof_separators = | |
7018 (String::kMaxLength + separator_length - 1) / separator_length; | |
7019 if (max_nof_separators < (array_length - 1)) { | |
7020 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); | |
7021 } | |
7022 int length = (array_length - 1) * separator_length; | |
7023 for (int i = 0; i < array_length; i++) { | |
7024 Object* element_obj = fixed_array->get(i); | |
7025 RUNTIME_ASSERT(element_obj->IsString()); | |
7026 String* element = String::cast(element_obj); | |
7027 int increment = element->length(); | |
7028 if (increment > String::kMaxLength - length) { | |
7029 STATIC_ASSERT(String::kMaxLength < kMaxInt); | |
7030 length = kMaxInt; // Provoke exception; | |
7031 break; | |
7032 } | |
7033 length += increment; | |
7034 } | |
7035 | |
7036 Handle<SeqTwoByteString> answer; | |
7037 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( | |
7038 isolate, answer, isolate->factory()->NewRawTwoByteString(length)); | |
7039 | |
7040 DisallowHeapAllocation no_gc; | |
7041 | |
7042 uc16* sink = answer->GetChars(); | |
7043 #ifdef DEBUG | |
7044 uc16* end = sink + length; | |
7045 #endif | |
7046 | |
7047 RUNTIME_ASSERT(fixed_array->get(0)->IsString()); | |
7048 String* first = String::cast(fixed_array->get(0)); | |
7049 String* separator_raw = *separator; | |
7050 int first_length = first->length(); | |
7051 String::WriteToFlat(first, sink, 0, first_length); | |
7052 sink += first_length; | |
7053 | |
7054 for (int i = 1; i < array_length; i++) { | |
7055 DCHECK(sink + separator_length <= end); | |
7056 String::WriteToFlat(separator_raw, sink, 0, separator_length); | |
7057 sink += separator_length; | |
7058 | |
7059 RUNTIME_ASSERT(fixed_array->get(i)->IsString()); | |
7060 String* element = String::cast(fixed_array->get(i)); | |
7061 int element_length = element->length(); | |
7062 DCHECK(sink + element_length <= end); | |
7063 String::WriteToFlat(element, sink, 0, element_length); | |
7064 sink += element_length; | |
7065 } | |
7066 DCHECK(sink == end); | |
7067 | |
7068 // Use %_FastOneByteArrayJoin instead. | |
7069 DCHECK(!answer->IsOneByteRepresentation()); | |
7070 return *answer; | |
7071 } | |
7072 | |
7073 template <typename Char> | |
7074 static void JoinSparseArrayWithSeparator(FixedArray* elements, | |
7075 int elements_length, | |
7076 uint32_t array_length, | |
7077 String* separator, | |
7078 Vector<Char> buffer) { | |
7079 DisallowHeapAllocation no_gc; | |
7080 int previous_separator_position = 0; | |
7081 int separator_length = separator->length(); | |
7082 int cursor = 0; | |
7083 for (int i = 0; i < elements_length; i += 2) { | |
7084 int position = NumberToInt32(elements->get(i)); | |
7085 String* string = String::cast(elements->get(i + 1)); | |
7086 int string_length = string->length(); | |
7087 if (string->length() > 0) { | |
7088 while (previous_separator_position < position) { | |
7089 String::WriteToFlat<Char>(separator, &buffer[cursor], 0, | |
7090 separator_length); | |
7091 cursor += separator_length; | |
7092 previous_separator_position++; | |
7093 } | |
7094 String::WriteToFlat<Char>(string, &buffer[cursor], 0, string_length); | |
7095 cursor += string->length(); | |
7096 } | |
7097 } | |
7098 if (separator_length > 0) { | |
7099 // Array length must be representable as a signed 32-bit number, | |
7100 // otherwise the total string length would have been too large. | |
7101 DCHECK(array_length <= 0x7fffffff); // Is int32_t. | |
7102 int last_array_index = static_cast<int>(array_length - 1); | |
7103 while (previous_separator_position < last_array_index) { | |
7104 String::WriteToFlat<Char>(separator, &buffer[cursor], 0, | |
7105 separator_length); | |
7106 cursor += separator_length; | |
7107 previous_separator_position++; | |
7108 } | |
7109 } | |
7110 DCHECK(cursor <= buffer.length()); | |
7111 } | |
7112 | |
7113 | |
7114 RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { | |
7115 HandleScope scope(isolate); | |
7116 DCHECK(args.length() == 3); | |
7117 CONVERT_ARG_HANDLE_CHECKED(JSArray, elements_array, 0); | |
7118 CONVERT_NUMBER_CHECKED(uint32_t, array_length, Uint32, args[1]); | |
7119 CONVERT_ARG_HANDLE_CHECKED(String, separator, 2); | |
7120 // elements_array is fast-mode JSarray of alternating positions | |
7121 // (increasing order) and strings. | |
7122 RUNTIME_ASSERT(elements_array->HasFastSmiOrObjectElements()); | |
7123 // array_length is length of original array (used to add separators); | |
7124 // separator is string to put between elements. Assumed to be non-empty. | |
7125 RUNTIME_ASSERT(array_length > 0); | |
7126 | |
7127 // Find total length of join result. | |
7128 int string_length = 0; | |
7129 bool is_one_byte = separator->IsOneByteRepresentation(); | |
7130 bool overflow = false; | |
7131 CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length()); | |
7132 RUNTIME_ASSERT(elements_length <= elements_array->elements()->length()); | |
7133 RUNTIME_ASSERT((elements_length & 1) == 0); // Even length. | |
7134 FixedArray* elements = FixedArray::cast(elements_array->elements()); | |
7135 for (int i = 0; i < elements_length; i += 2) { | |
7136 RUNTIME_ASSERT(elements->get(i)->IsNumber()); | |
7137 CONVERT_NUMBER_CHECKED(uint32_t, position, Uint32, elements->get(i)); | |
7138 RUNTIME_ASSERT(position < array_length); | |
7139 RUNTIME_ASSERT(elements->get(i + 1)->IsString()); | |
7140 } | |
7141 | |
7142 { | |
7143 DisallowHeapAllocation no_gc; | |
7144 for (int i = 0; i < elements_length; i += 2) { | |
7145 String* string = String::cast(elements->get(i + 1)); | |
7146 int length = string->length(); | |
7147 if (is_one_byte && !string->IsOneByteRepresentation()) { | |
7148 is_one_byte = false; | |
7149 } | |
7150 if (length > String::kMaxLength || | |
7151 String::kMaxLength - length < string_length) { | |
7152 overflow = true; | |
7153 break; | |
7154 } | |
7155 string_length += length; | |
7156 } | |
7157 } | |
7158 | |
7159 int separator_length = separator->length(); | |
7160 if (!overflow && separator_length > 0) { | |
7161 if (array_length <= 0x7fffffffu) { | |
7162 int separator_count = static_cast<int>(array_length) - 1; | |
7163 int remaining_length = String::kMaxLength - string_length; | |
7164 if ((remaining_length / separator_length) >= separator_count) { | |
7165 string_length += separator_length * (array_length - 1); | |
7166 } else { | |
7167 // Not room for the separators within the maximal string length. | |
7168 overflow = true; | |
7169 } | |
7170 } else { | |
7171 // Nonempty separator and at least 2^31-1 separators necessary | |
7172 // means that the string is too large to create. | |
7173 STATIC_ASSERT(String::kMaxLength < 0x7fffffff); | |
7174 overflow = true; | |
7175 } | |
7176 } | |
7177 if (overflow) { | |
7178 // Throw an exception if the resulting string is too large. See | |
7179 // https://code.google.com/p/chromium/issues/detail?id=336820 | |
7180 // for details. | |
7181 THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewInvalidStringLengthError()); | |
7182 } | |
7183 | |
7184 if (is_one_byte) { | |
7185 Handle<SeqOneByteString> result = isolate->factory() | |
7186 ->NewRawOneByteString(string_length) | |
7187 .ToHandleChecked(); | |
7188 JoinSparseArrayWithSeparator<uint8_t>( | |
7189 FixedArray::cast(elements_array->elements()), elements_length, | |
7190 array_length, *separator, | |
7191 Vector<uint8_t>(result->GetChars(), string_length)); | |
7192 return *result; | |
7193 } else { | |
7194 Handle<SeqTwoByteString> result = isolate->factory() | |
7195 ->NewRawTwoByteString(string_length) | |
7196 .ToHandleChecked(); | |
7197 JoinSparseArrayWithSeparator<uc16>( | |
7198 FixedArray::cast(elements_array->elements()), elements_length, | |
7199 array_length, *separator, | |
7200 Vector<uc16>(result->GetChars(), string_length)); | |
7201 return *result; | |
7202 } | |
7203 } | |
7204 | |
7205 | |
7206 RUNTIME_FUNCTION(Runtime_NumberOr) { | 4667 RUNTIME_FUNCTION(Runtime_NumberOr) { |
7207 HandleScope scope(isolate); | 4668 HandleScope scope(isolate); |
7208 DCHECK(args.length() == 2); | 4669 DCHECK(args.length() == 2); |
7209 | 4670 |
7210 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]); | 4671 CONVERT_NUMBER_CHECKED(int32_t, x, Int32, args[0]); |
7211 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]); | 4672 CONVERT_NUMBER_CHECKED(int32_t, y, Int32, args[1]); |
7212 return *isolate->factory()->NewNumberFromInt(x | y); | 4673 return *isolate->factory()->NewNumberFromInt(x | y); |
7213 } | 4674 } |
7214 | 4675 |
7215 | 4676 |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7276 Object* result; | 4737 Object* result; |
7277 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) { | 4738 if ((fpclassify(x) == FP_ZERO) && (fpclassify(y) == FP_ZERO)) { |
7278 result = Smi::FromInt(EQUAL); | 4739 result = Smi::FromInt(EQUAL); |
7279 } else { | 4740 } else { |
7280 result = Smi::FromInt(NOT_EQUAL); | 4741 result = Smi::FromInt(NOT_EQUAL); |
7281 } | 4742 } |
7282 return result; | 4743 return result; |
7283 } | 4744 } |
7284 | 4745 |
7285 | 4746 |
7286 RUNTIME_FUNCTION(Runtime_StringEquals) { | |
7287 HandleScope handle_scope(isolate); | |
7288 DCHECK(args.length() == 2); | |
7289 | |
7290 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); | |
7291 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); | |
7292 | |
7293 bool not_equal = !String::Equals(x, y); | |
7294 // This is slightly convoluted because the value that signifies | |
7295 // equality is 0 and inequality is 1 so we have to negate the result | |
7296 // from String::Equals. | |
7297 DCHECK(not_equal == 0 || not_equal == 1); | |
7298 STATIC_ASSERT(EQUAL == 0); | |
7299 STATIC_ASSERT(NOT_EQUAL == 1); | |
7300 return Smi::FromInt(not_equal); | |
7301 } | |
7302 | |
7303 | |
7304 RUNTIME_FUNCTION(Runtime_NumberCompare) { | 4747 RUNTIME_FUNCTION(Runtime_NumberCompare) { |
7305 SealHandleScope shs(isolate); | 4748 SealHandleScope shs(isolate); |
7306 DCHECK(args.length() == 3); | 4749 DCHECK(args.length() == 3); |
7307 | 4750 |
7308 CONVERT_DOUBLE_ARG_CHECKED(x, 0); | 4751 CONVERT_DOUBLE_ARG_CHECKED(x, 0); |
7309 CONVERT_DOUBLE_ARG_CHECKED(y, 1); | 4752 CONVERT_DOUBLE_ARG_CHECKED(y, 1); |
7310 CONVERT_ARG_HANDLE_CHECKED(Object, uncomparable_result, 2) | 4753 CONVERT_ARG_HANDLE_CHECKED(Object, uncomparable_result, 2) |
7311 if (std::isnan(x) || std::isnan(y)) return *uncomparable_result; | 4754 if (std::isnan(x) || std::isnan(y)) return *uncomparable_result; |
7312 if (x == y) return Smi::FromInt(EQUAL); | 4755 if (x == y) return Smi::FromInt(EQUAL); |
7313 if (isless(x, y)) return Smi::FromInt(LESS); | 4756 if (isless(x, y)) return Smi::FromInt(LESS); |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
7384 x_scaled /= 10; | 4827 x_scaled /= 10; |
7385 tie = GREATER; | 4828 tie = GREATER; |
7386 } | 4829 } |
7387 | 4830 |
7388 if (x_scaled < y_scaled) return Smi::FromInt(LESS); | 4831 if (x_scaled < y_scaled) return Smi::FromInt(LESS); |
7389 if (x_scaled > y_scaled) return Smi::FromInt(GREATER); | 4832 if (x_scaled > y_scaled) return Smi::FromInt(GREATER); |
7390 return Smi::FromInt(tie); | 4833 return Smi::FromInt(tie); |
7391 } | 4834 } |
7392 | 4835 |
7393 | 4836 |
7394 RUNTIME_FUNCTION(Runtime_StringCompare) { | |
7395 HandleScope handle_scope(isolate); | |
7396 DCHECK(args.length() == 2); | |
7397 | 4837 |
7398 CONVERT_ARG_HANDLE_CHECKED(String, x, 0); | |
7399 CONVERT_ARG_HANDLE_CHECKED(String, y, 1); | |
7400 | |
7401 isolate->counters()->string_compare_runtime()->Increment(); | |
7402 | |
7403 // A few fast case tests before we flatten. | |
7404 if (x.is_identical_to(y)) return Smi::FromInt(EQUAL); | |
7405 if (y->length() == 0) { | |
7406 if (x->length() == 0) return Smi::FromInt(EQUAL); | |
7407 return Smi::FromInt(GREATER); | |
7408 } else if (x->length() == 0) { | |
7409 return Smi::FromInt(LESS); | |
7410 } | |
7411 | |
7412 int d = x->Get(0) - y->Get(0); | |
7413 if (d < 0) | |
7414 return Smi::FromInt(LESS); | |
7415 else if (d > 0) | |
7416 return Smi::FromInt(GREATER); | |
7417 | |
7418 // Slow case. | |
7419 x = String::Flatten(x); | |
7420 y = String::Flatten(y); | |
7421 | |
7422 DisallowHeapAllocation no_gc; | |
7423 Object* equal_prefix_result = Smi::FromInt(EQUAL); | |
7424 int prefix_length = x->length(); | |
7425 if (y->length() < prefix_length) { | |
7426 prefix_length = y->length(); | |
7427 equal_prefix_result = Smi::FromInt(GREATER); | |
7428 } else if (y->length() > prefix_length) { | |
7429 equal_prefix_result = Smi::FromInt(LESS); | |
7430 } | |
7431 int r; | |
7432 String::FlatContent x_content = x->GetFlatContent(); | |
7433 String::FlatContent y_content = y->GetFlatContent(); | |
7434 if (x_content.IsOneByte()) { | |
7435 Vector<const uint8_t> x_chars = x_content.ToOneByteVector(); | |
7436 if (y_content.IsOneByte()) { | |
7437 Vector<const uint8_t> y_chars = y_content.ToOneByteVector(); | |
7438 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); | |
7439 } else { | |
7440 Vector<const uc16> y_chars = y_content.ToUC16Vector(); | |
7441 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); | |
7442 } | |
7443 } else { | |
7444 Vector<const uc16> x_chars = x_content.ToUC16Vector(); | |
7445 if (y_content.IsOneByte()) { | |
7446 Vector<const uint8_t> y_chars = y_content.ToOneByteVector(); | |
7447 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); | |
7448 } else { | |
7449 Vector<const uc16> y_chars = y_content.ToUC16Vector(); | |
7450 r = CompareChars(x_chars.start(), y_chars.start(), prefix_length); | |
7451 } | |
7452 } | |
7453 Object* result; | |
7454 if (r == 0) { | |
7455 result = equal_prefix_result; | |
7456 } else { | |
7457 result = (r < 0) ? Smi::FromInt(LESS) : Smi::FromInt(GREATER); | |
7458 } | |
7459 return result; | |
7460 } | |
7461 | 4838 |
7462 | 4839 |
7463 #define RUNTIME_UNARY_MATH(Name, name) \ | 4840 #define RUNTIME_UNARY_MATH(Name, name) \ |
7464 RUNTIME_FUNCTION(Runtime_Math##Name) { \ | 4841 RUNTIME_FUNCTION(Runtime_Math##Name) { \ |
7465 HandleScope scope(isolate); \ | 4842 HandleScope scope(isolate); \ |
7466 DCHECK(args.length() == 1); \ | 4843 DCHECK(args.length() == 1); \ |
7467 isolate->counters()->math_##name()->Increment(); \ | 4844 isolate->counters()->math_##name()->Increment(); \ |
7468 CONVERT_DOUBLE_ARG_CHECKED(x, 0); \ | 4845 CONVERT_DOUBLE_ARG_CHECKED(x, 0); \ |
7469 return *isolate->factory()->NewHeapNumber(std::name(x)); \ | 4846 return *isolate->factory()->NewHeapNumber(std::name(x)); \ |
7470 } | 4847 } |
(...skipping 1983 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
9454 RUNTIME_FUNCTION(Runtime_IsAttachedGlobal) { | 6831 RUNTIME_FUNCTION(Runtime_IsAttachedGlobal) { |
9455 SealHandleScope shs(isolate); | 6832 SealHandleScope shs(isolate); |
9456 DCHECK(args.length() == 1); | 6833 DCHECK(args.length() == 1); |
9457 CONVERT_ARG_CHECKED(Object, global, 0); | 6834 CONVERT_ARG_CHECKED(Object, global, 0); |
9458 if (!global->IsJSGlobalObject()) return isolate->heap()->false_value(); | 6835 if (!global->IsJSGlobalObject()) return isolate->heap()->false_value(); |
9459 return isolate->heap()->ToBoolean( | 6836 return isolate->heap()->ToBoolean( |
9460 !JSGlobalObject::cast(global)->IsDetached()); | 6837 !JSGlobalObject::cast(global)->IsDetached()); |
9461 } | 6838 } |
9462 | 6839 |
9463 | 6840 |
9464 RUNTIME_FUNCTION(Runtime_ParseJson) { | |
9465 HandleScope scope(isolate); | |
9466 DCHECK(args.length() == 1); | |
9467 CONVERT_ARG_HANDLE_CHECKED(String, source, 0); | |
9468 | |
9469 source = String::Flatten(source); | |
9470 // Optimized fast case where we only have Latin1 characters. | |
9471 Handle<Object> result; | |
9472 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, | |
9473 source->IsSeqOneByteString() | |
9474 ? JsonParser<true>::Parse(source) | |
9475 : JsonParser<false>::Parse(source)); | |
9476 return *result; | |
9477 } | |
9478 | |
9479 | |
9480 bool CodeGenerationFromStringsAllowed(Isolate* isolate, | 6841 bool CodeGenerationFromStringsAllowed(Isolate* isolate, |
9481 Handle<Context> context) { | 6842 Handle<Context> context) { |
9482 DCHECK(context->allow_code_gen_from_strings()->IsFalse()); | 6843 DCHECK(context->allow_code_gen_from_strings()->IsFalse()); |
9483 // Check with callback if set. | 6844 // Check with callback if set. |
9484 AllowCodeGenerationFromStringsCallback callback = | 6845 AllowCodeGenerationFromStringsCallback callback = |
9485 isolate->allow_code_gen_callback(); | 6846 isolate->allow_code_gen_callback(); |
9486 if (callback == NULL) { | 6847 if (callback == NULL) { |
9487 // No callback set and code generation disallowed. | 6848 // No callback set and code generation disallowed. |
9488 return false; | 6849 return false; |
9489 } else { | 6850 } else { |
(...skipping 4760 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
14250 THROW_NEW_ERROR_RETURN_FAILURE( | 11611 THROW_NEW_ERROR_RETURN_FAILURE( |
14251 isolate, | 11612 isolate, |
14252 NewTypeError("not_date_object", HandleVector<Object>(NULL, 0))); | 11613 NewTypeError("not_date_object", HandleVector<Object>(NULL, 0))); |
14253 } | 11614 } |
14254 JSDate* date = JSDate::cast(obj); | 11615 JSDate* date = JSDate::cast(obj); |
14255 if (index == 0) return date->value(); | 11616 if (index == 0) return date->value(); |
14256 return JSDate::GetField(date, Smi::FromInt(index)); | 11617 return JSDate::GetField(date, Smi::FromInt(index)); |
14257 } | 11618 } |
14258 | 11619 |
14259 | 11620 |
14260 RUNTIME_FUNCTION(RuntimeReference_StringCharFromCode) { | |
14261 SealHandleScope shs(isolate); | |
14262 return __RT_impl_Runtime_CharFromCode(args, isolate); | |
14263 } | |
14264 | |
14265 | |
14266 RUNTIME_FUNCTION(RuntimeReference_StringCharAt) { | |
14267 SealHandleScope shs(isolate); | |
14268 DCHECK(args.length() == 2); | |
14269 if (!args[0]->IsString()) return Smi::FromInt(0); | |
14270 if (!args[1]->IsNumber()) return Smi::FromInt(0); | |
14271 if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string(); | |
14272 Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); | |
14273 if (code->IsNaN()) return isolate->heap()->empty_string(); | |
14274 return __RT_impl_Runtime_CharFromCode(Arguments(1, &code), isolate); | |
14275 } | |
14276 | |
14277 | |
14278 RUNTIME_FUNCTION(RuntimeReference_OneByteSeqStringSetChar) { | |
14279 SealHandleScope shs(isolate); | |
14280 DCHECK(args.length() == 3); | |
14281 CONVERT_INT32_ARG_CHECKED(index, 0); | |
14282 CONVERT_INT32_ARG_CHECKED(value, 1); | |
14283 CONVERT_ARG_CHECKED(SeqOneByteString, string, 2); | |
14284 string->SeqOneByteStringSet(index, value); | |
14285 return string; | |
14286 } | |
14287 | |
14288 | |
14289 RUNTIME_FUNCTION(RuntimeReference_TwoByteSeqStringSetChar) { | |
14290 SealHandleScope shs(isolate); | |
14291 DCHECK(args.length() == 3); | |
14292 CONVERT_INT32_ARG_CHECKED(index, 0); | |
14293 CONVERT_INT32_ARG_CHECKED(value, 1); | |
14294 CONVERT_ARG_CHECKED(SeqTwoByteString, string, 2); | |
14295 string->SeqTwoByteStringSet(index, value); | |
14296 return string; | |
14297 } | |
14298 | |
14299 | |
14300 RUNTIME_FUNCTION(RuntimeReference_ObjectEquals) { | 11621 RUNTIME_FUNCTION(RuntimeReference_ObjectEquals) { |
14301 SealHandleScope shs(isolate); | 11622 SealHandleScope shs(isolate); |
14302 DCHECK(args.length() == 2); | 11623 DCHECK(args.length() == 2); |
14303 CONVERT_ARG_CHECKED(Object, obj1, 0); | 11624 CONVERT_ARG_CHECKED(Object, obj1, 0); |
14304 CONVERT_ARG_CHECKED(Object, obj2, 1); | 11625 CONVERT_ARG_CHECKED(Object, obj2, 1); |
14305 return isolate->heap()->ToBoolean(obj1 == obj2); | 11626 return isolate->heap()->ToBoolean(obj1 == obj2); |
14306 } | 11627 } |
14307 | 11628 |
14308 | 11629 |
14309 RUNTIME_FUNCTION(RuntimeReference_IsObject) { | 11630 RUNTIME_FUNCTION(RuntimeReference_IsObject) { |
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
14396 | 11717 |
14397 RUNTIME_FUNCTION(RuntimeReference_ClassOf) { | 11718 RUNTIME_FUNCTION(RuntimeReference_ClassOf) { |
14398 SealHandleScope shs(isolate); | 11719 SealHandleScope shs(isolate); |
14399 DCHECK(args.length() == 1); | 11720 DCHECK(args.length() == 1); |
14400 CONVERT_ARG_CHECKED(Object, obj, 0); | 11721 CONVERT_ARG_CHECKED(Object, obj, 0); |
14401 if (!obj->IsJSReceiver()) return isolate->heap()->null_value(); | 11722 if (!obj->IsJSReceiver()) return isolate->heap()->null_value(); |
14402 return JSReceiver::cast(obj)->class_name(); | 11723 return JSReceiver::cast(obj)->class_name(); |
14403 } | 11724 } |
14404 | 11725 |
14405 | 11726 |
14406 RUNTIME_FUNCTION(RuntimeReference_StringCharCodeAt) { | |
14407 SealHandleScope shs(isolate); | |
14408 DCHECK(args.length() == 2); | |
14409 if (!args[0]->IsString()) return isolate->heap()->undefined_value(); | |
14410 if (!args[1]->IsNumber()) return isolate->heap()->undefined_value(); | |
14411 if (std::isinf(args.number_at(1))) return isolate->heap()->nan_value(); | |
14412 return __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); | |
14413 } | |
14414 | |
14415 | |
14416 RUNTIME_FUNCTION(RuntimeReference_StringAdd) { | |
14417 SealHandleScope shs(isolate); | |
14418 return __RT_impl_Runtime_StringAdd(args, isolate); | |
14419 } | |
14420 | |
14421 | |
14422 RUNTIME_FUNCTION(RuntimeReference_SubString) { | |
14423 SealHandleScope shs(isolate); | |
14424 return __RT_impl_Runtime_SubString(args, isolate); | |
14425 } | |
14426 | |
14427 | |
14428 RUNTIME_FUNCTION(RuntimeReference_StringCompare) { | |
14429 SealHandleScope shs(isolate); | |
14430 return __RT_impl_Runtime_StringCompare(args, isolate); | |
14431 } | |
14432 | |
14433 | |
14434 RUNTIME_FUNCTION(RuntimeReference_RegExpExec) { | |
14435 SealHandleScope shs(isolate); | |
14436 return __RT_impl_Runtime_RegExpExecRT(args, isolate); | |
14437 } | |
14438 | |
14439 | |
14440 RUNTIME_FUNCTION(RuntimeReference_RegExpConstructResult) { | |
14441 SealHandleScope shs(isolate); | |
14442 return __RT_impl_Runtime_RegExpConstructResult(args, isolate); | |
14443 } | |
14444 | |
14445 | |
14446 RUNTIME_FUNCTION(RuntimeReference_GetFromCache) { | 11727 RUNTIME_FUNCTION(RuntimeReference_GetFromCache) { |
14447 HandleScope scope(isolate); | 11728 HandleScope scope(isolate); |
14448 DCHECK(args.length() == 2); | 11729 DCHECK(args.length() == 2); |
14449 CONVERT_SMI_ARG_CHECKED(id, 0); | 11730 CONVERT_SMI_ARG_CHECKED(id, 0); |
14450 args[0] = isolate->native_context()->jsfunction_result_caches()->get(id); | 11731 args[0] = isolate->native_context()->jsfunction_result_caches()->get(id); |
14451 return __RT_impl_Runtime_GetFromCache(args, isolate); | 11732 return __RT_impl_Runtime_GetFromCache(args, isolate); |
14452 } | 11733 } |
14453 | 11734 |
14454 | 11735 |
14455 RUNTIME_FUNCTION(RuntimeReference_NumberToString) { | 11736 RUNTIME_FUNCTION(RuntimeReference_NumberToString) { |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
14537 } | 11818 } |
14538 return NULL; | 11819 return NULL; |
14539 } | 11820 } |
14540 | 11821 |
14541 | 11822 |
14542 const Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) { | 11823 const Runtime::Function* Runtime::FunctionForId(Runtime::FunctionId id) { |
14543 return &(kIntrinsicFunctions[static_cast<int>(id)]); | 11824 return &(kIntrinsicFunctions[static_cast<int>(id)]); |
14544 } | 11825 } |
14545 } | 11826 } |
14546 } // namespace v8::internal | 11827 } // namespace v8::internal |
OLD | NEW |