Index: src/runtime/runtime-strings.cc |
diff --git a/src/runtime/runtime-strings.cc b/src/runtime/runtime-strings.cc |
index 71f27a03d6edaf487efc206f11a8fca9eb76959a..c35be0115e9c8827df4a4aa6dbc21950b57927ee 100644 |
--- a/src/runtime/runtime-strings.cc |
+++ b/src/runtime/runtime-strings.cc |
@@ -2,11 +2,11 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
-#include "src/runtime/runtime-utils.h" |
- |
#include "src/arguments.h" |
#include "src/regexp/jsregexp-inl.h" |
+#include "src/runtime/runtime-utils.h" |
#include "src/string-builder.h" |
+ |
#include "src/string-search.h" |
namespace v8 { |
@@ -16,7 +16,7 @@ namespace internal { |
// Perform string match of pattern on subject, starting at start index. |
// Caller must ensure that 0 <= start_index <= sub->length(), |
// and should check that pat->length() + start_index <= sub->length(). |
-int StringMatch(Isolate* isolate, Handle<String> sub, Handle<String> pat, |
+int StringMatch(Isolate *isolate, Handle<String> sub, Handle<String> pat, |
int start_index) { |
DCHECK(0 <= start_index); |
DCHECK(start_index <= sub->length()); |
@@ -57,15 +57,15 @@ int StringMatch(Isolate* isolate, Handle<String> sub, Handle<String> pat, |
// This may return an empty MaybeHandle if an exception is thrown or |
// we abort due to reaching the recursion limit. |
MaybeHandle<String> StringReplaceOneCharWithString( |
- Isolate* isolate, Handle<String> subject, Handle<String> search, |
- Handle<String> replace, bool* found, int recursion_limit) { |
+ Isolate *isolate, Handle<String> subject, Handle<String> search, |
Yang
2016/05/12 07:39:21
V8's convention is to have the asterisk at the typ
Franzi
2016/05/13 09:42:02
Done.
|
+ Handle<String> replace, bool *found, int recursion_limit) { |
StackLimitCheck stackLimitCheck(isolate); |
if (stackLimitCheck.HasOverflowed() || (recursion_limit == 0)) { |
return MaybeHandle<String>(); |
} |
recursion_limit--; |
if (subject->IsConsString()) { |
- ConsString* cons = ConsString::cast(*subject); |
+ ConsString *cons = ConsString::cast(*subject); |
Handle<String> first = Handle<String>(cons->first()); |
Handle<String> second = Handle<String>(cons->second()); |
Handle<String> new_first; |
@@ -145,7 +145,6 @@ RUNTIME_FUNCTION(Runtime_StringIndexOf) { |
return Smi::FromInt(position); |
} |
- |
template <typename schar, typename pchar> |
static int StringMatchBackwards(Vector<const schar> subject, |
Vector<const pchar> pattern, int idx) { |
@@ -347,7 +346,7 @@ RUNTIME_FUNCTION(Runtime_StringMatch) { |
ZoneList<int> offsets(8, zone_scope.zone()); |
while (true) { |
- int32_t* match = global_cache.FetchNext(); |
+ int32_t *match = global_cache.FetchNext(); |
if (match == NULL) break; |
offsets.Add(match[0], zone_scope.zone()); // start |
offsets.Add(match[1], zone_scope.zone()); // end |
@@ -454,7 +453,7 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { |
{ |
DisallowHeapAllocation no_gc; |
- FixedArray* fixed_array = FixedArray::cast(array->elements()); |
+ FixedArray *fixed_array = FixedArray::cast(array->elements()); |
if (fixed_array->length() < array_length) { |
array_length = fixed_array->length(); |
} |
@@ -462,7 +461,7 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { |
if (array_length == 0) { |
return isolate->heap()->empty_string(); |
} else if (array_length == 1) { |
- Object* first = fixed_array->get(0); |
+ Object *first = fixed_array->get(0); |
if (first->IsString()) return first; |
} |
length = StringBuilderConcatLength(special_length, fixed_array, |
@@ -513,7 +512,7 @@ RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { |
if (array_length == 0) { |
return isolate->heap()->empty_string(); |
} else if (array_length == 1) { |
- Object* first = fixed_array->get(0); |
+ Object *first = fixed_array->get(0); |
RUNTIME_ASSERT(first->IsString()); |
return first; |
} |
@@ -527,9 +526,9 @@ RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { |
} |
int length = (array_length - 1) * separator_length; |
for (int i = 0; i < array_length; i++) { |
- Object* element_obj = fixed_array->get(i); |
+ Object *element_obj = fixed_array->get(i); |
RUNTIME_ASSERT(element_obj->IsString()); |
- String* element = String::cast(element_obj); |
+ String *element = String::cast(element_obj); |
int increment = element->length(); |
if (increment > String::kMaxLength - length) { |
STATIC_ASSERT(String::kMaxLength < kMaxInt); |
@@ -545,14 +544,14 @@ RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { |
DisallowHeapAllocation no_gc; |
- uc16* sink = answer->GetChars(); |
+ uc16 *sink = answer->GetChars(); |
#ifdef DEBUG |
uc16* end = sink + length; |
#endif |
RUNTIME_ASSERT(fixed_array->get(0)->IsString()); |
- String* first = String::cast(fixed_array->get(0)); |
- String* separator_raw = *separator; |
+ String *first = String::cast(fixed_array->get(0)); |
+ String *separator_raw = *separator; |
int first_length = first->length(); |
String::WriteToFlat(first, sink, 0, first_length); |
@@ -564,7 +563,7 @@ RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { |
sink += separator_length; |
RUNTIME_ASSERT(fixed_array->get(i)->IsString()); |
- String* element = String::cast(fixed_array->get(i)); |
+ String *element = String::cast(fixed_array->get(i)); |
int element_length = element->length(); |
DCHECK(sink + element_length <= end); |
String::WriteToFlat(element, sink, 0, element_length); |
@@ -578,15 +577,15 @@ RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { |
} |
template <typename sinkchar> |
-static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor, |
+static void WriteRepeatToFlat(String *src, Vector<sinkchar> buffer, int cursor, |
int repeat, int length) { |
if (repeat == 0) return; |
- sinkchar* start = &buffer[cursor]; |
+ sinkchar *start = &buffer[cursor]; |
String::WriteToFlat<sinkchar>(src, start, 0, length); |
int done = 1; |
- sinkchar* next = start + length; |
+ sinkchar *next = start + length; |
while (done < repeat) { |
int block = Min(done, repeat - done); |
@@ -598,10 +597,10 @@ static void WriteRepeatToFlat(String* src, Vector<sinkchar> buffer, int cursor, |
} |
template <typename Char> |
-static void JoinSparseArrayWithSeparator(FixedArray* elements, |
+static void JoinSparseArrayWithSeparator(FixedArray *elements, |
int elements_length, |
uint32_t array_length, |
- String* separator, |
+ String *separator, |
Vector<Char> buffer) { |
DisallowHeapAllocation no_gc; |
int previous_separator_position = 0; |
@@ -610,7 +609,7 @@ static void JoinSparseArrayWithSeparator(FixedArray* elements, |
int cursor = 0; |
for (int i = 0; i < elements_length; i += 2) { |
int position = NumberToInt32(elements->get(i)); |
- String* string = String::cast(elements->get(i + 1)); |
+ String *string = String::cast(elements->get(i + 1)); |
int string_length = string->length(); |
if (string->length() > 0) { |
int repeat = position - previous_separator_position; |
@@ -654,11 +653,11 @@ RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { |
CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length()); |
RUNTIME_ASSERT(elements_length <= elements_array->elements()->length()); |
RUNTIME_ASSERT((elements_length & 1) == 0); // Even length. |
- FixedArray* elements = FixedArray::cast(elements_array->elements()); |
+ FixedArray *elements = FixedArray::cast(elements_array->elements()); |
{ |
DisallowHeapAllocation no_gc; |
for (int i = 0; i < elements_length; i += 2) { |
- String* string = String::cast(elements->get(i + 1)); |
+ String *string = String::cast(elements->get(i + 1)); |
int length = string->length(); |
if (is_one_byte && !string->IsOneByteRepresentation()) { |
is_one_byte = false; |
@@ -723,15 +722,15 @@ RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { |
// one-char strings in the cache. Gives up on the first char that is |
// not in the cache and fills the remainder with smi zeros. Returns |
// the length of the successfully copied prefix. |
-static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars, |
- FixedArray* elements, int length) { |
+static int CopyCachedOneByteCharsToArray(Heap *heap, const uint8_t *chars, |
+ FixedArray *elements, int length) { |
DisallowHeapAllocation no_gc; |
- FixedArray* one_byte_cache = heap->single_character_string_cache(); |
- Object* undefined = heap->undefined_value(); |
+ FixedArray *one_byte_cache = heap->single_character_string_cache(); |
+ Object *undefined = heap->undefined_value(); |
int i; |
WriteBarrierMode mode = elements->GetWriteBarrierMode(no_gc); |
for (i = 0; i < length; ++i) { |
- Object* value = one_byte_cache->get(chars[i]); |
+ Object *value = one_byte_cache->get(chars[i]); |
if (value == undefined) break; |
elements->set(i, value, mode); |
} |
@@ -806,11 +805,10 @@ static inline bool ToUpperOverflows(uc32 character) { |
return (character == yuml_code || character == micro_code); |
} |
- |
template <class Converter> |
-MUST_USE_RESULT static Object* ConvertCaseHelper( |
- Isolate* isolate, String* string, SeqString* result, int result_length, |
- unibrow::Mapping<Converter, 128>* mapping) { |
+MUST_USE_RESULT static Object *ConvertCaseHelper( |
+ Isolate *isolate, String *string, SeqString *result, int result_length, |
+ unibrow::Mapping<Converter, 128> *mapping) { |
DisallowHeapAllocation no_gc; |
// We try this twice, once with the assumption that the result is no longer |
// than the input and, if that assumption breaks, again with the exact |
@@ -946,10 +944,9 @@ static bool CheckFastAsciiConvert(char* dst, const char* src, int length, |
} |
#endif |
- |
template <class Converter> |
-static bool FastAsciiConvert(char* dst, const char* src, int length, |
- bool* changed_out) { |
+static bool FastAsciiConvert(char *dst, const char *src, int length, |
+ bool *changed_out) { |
#ifdef DEBUG |
char* saved_dst = dst; |
const char* saved_src = src; |
@@ -963,7 +960,7 @@ static bool FastAsciiConvert(char* dst, const char* src, int length, |
static const char hi = Converter::kIsToLower ? 'Z' + 1 : 'z' + 1; |
bool changed = false; |
uintptr_t or_acc = 0; |
- const char* const limit = src + length; |
+ const char *const limit = src + length; |
// dst is newly allocated and always aligned. |
DCHECK(IsAligned(reinterpret_cast<intptr_t>(dst), sizeof(uintptr_t))); |
@@ -972,26 +969,26 @@ static bool FastAsciiConvert(char* dst, const char* src, int length, |
// Process the prefix of the input that requires no conversion one aligned |
// (machine) word at a time. |
while (src <= limit - sizeof(uintptr_t)) { |
- const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); |
+ const uintptr_t w = *reinterpret_cast<const uintptr_t *>(src); |
or_acc |= w; |
if (AsciiRangeMask(w, lo, hi) != 0) { |
changed = true; |
break; |
} |
- *reinterpret_cast<uintptr_t*>(dst) = w; |
+ *reinterpret_cast<uintptr_t *>(dst) = w; |
src += sizeof(uintptr_t); |
dst += sizeof(uintptr_t); |
} |
// Process the remainder of the input performing conversion when |
// required one word at a time. |
while (src <= limit - sizeof(uintptr_t)) { |
- const uintptr_t w = *reinterpret_cast<const uintptr_t*>(src); |
+ const uintptr_t w = *reinterpret_cast<const uintptr_t *>(src); |
or_acc |= w; |
uintptr_t m = AsciiRangeMask(w, lo, hi); |
// The mask has high (7th) bit set in every byte that needs |
// conversion and we know that the distance between cases is |
// 1 << 5. |
- *reinterpret_cast<uintptr_t*>(dst) = w ^ (m >> 2); |
+ *reinterpret_cast<uintptr_t *>(dst) = w ^ (m >> 2); |
src += sizeof(uintptr_t); |
dst += sizeof(uintptr_t); |
} |
@@ -1019,11 +1016,10 @@ static bool FastAsciiConvert(char* dst, const char* src, int length, |
return true; |
} |
- |
template <class Converter> |
-MUST_USE_RESULT static Object* ConvertCase( |
- Handle<String> s, Isolate* isolate, |
- unibrow::Mapping<Converter, 128>* mapping) { |
+MUST_USE_RESULT static Object *ConvertCase( |
Yang
2016/05/12 07:39:21
You probably did some auto format on your IDE. Ple
Franzi
2016/05/13 09:42:03
Acknowledged.
|
+ Handle<String> s, Isolate *isolate, |
+ unibrow::Mapping<Converter, 128> *mapping) { |
s = String::Flatten(s); |
int length = s->length(); |
// Assume that the string is not empty; we need this assumption later |
@@ -1044,8 +1040,8 @@ MUST_USE_RESULT static Object* ConvertCase( |
DCHECK(flat_content.IsFlat()); |
bool has_changed_character = false; |
bool is_ascii = FastAsciiConvert<Converter>( |
- reinterpret_cast<char*>(result->GetChars()), |
- reinterpret_cast<const char*>(flat_content.ToOneByteVector().start()), |
+ reinterpret_cast<char *>(result->GetChars()), |
+ reinterpret_cast<const char *>(flat_content.ToOneByteVector().start()), |
length, &has_changed_character); |
// If not ASCII, we discard the result and take the 2 byte path. |
if (is_ascii) return has_changed_character ? *result : *s; |
@@ -1058,7 +1054,7 @@ MUST_USE_RESULT static Object* ConvertCase( |
result = isolate->factory()->NewRawTwoByteString(length).ToHandleChecked(); |
} |
- Object* answer = ConvertCaseHelper(isolate, *s, *result, length, mapping); |
+ Object *answer = ConvertCaseHelper(isolate, *s, *result, length, mapping); |
if (answer->IsException() || answer->IsString()) return answer; |
DCHECK(answer->IsSmi()); |
@@ -1103,7 +1099,7 @@ RUNTIME_FUNCTION(Runtime_StringTrim) { |
int length = string->length(); |
int left = 0; |
- UnicodeCache* unicode_cache = isolate->unicode_cache(); |
+ UnicodeCache *unicode_cache = isolate->unicode_cache(); |
if (trimLeft) { |
while (left < length && |
unicode_cache->IsWhiteSpaceOrLineTerminator(string->Get(left))) { |
@@ -1151,6 +1147,140 @@ RUNTIME_FUNCTION(Runtime_NewString) { |
return *result; |
} |
+bool unescapePredicateInComponent(uint16_t c) { |
Yang
2016/05/12 07:39:21
CamelCase with capital first letter please.
Also
Franzi
2016/05/13 09:42:03
Done.
|
+ // do not escape alphanumeric or !'()*-._~ |
+ if (c < 256 && isalnum(c)) { |
Yang
2016/05/12 07:39:21
Let's use IsAlphaNumeric from char-predicates.h. T
Franzi
2016/05/13 09:42:03
Done.
|
+ return true; |
+ } |
+ if (c == '!' || c == '\'' || c == '(' || c == ')' || c == '*' || c == '-' || |
+ c == '.' || c == '_' || c == '~') { |
Yang
2016/05/12 07:39:21
A switch would be a lot nicer to read.
Franzi
2016/05/13 09:42:02
Done.
|
+ return true; |
+ } |
+ |
+ return false; |
+} |
+ |
+bool uriSeparator(uint16_t c) { |
+ // separators are #:;/?$&+,@= |
+ if (c == '#' || c == ':' || c == ';' || c == '/' || c == '?' || c == '$' || |
Yang
2016/05/12 07:39:21
Same here, a switch would be nice.
Franzi
2016/05/13 09:42:02
Done.
|
+ c == '&' || c == '+' || c == ',' || c == '@' || c == '=') { |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+bool unescape(uint16_t c, bool is_url) { |
Yang
2016/05/12 07:39:21
This is only used once. Let's inline it at the cal
Franzi
2016/05/13 09:42:03
Done.
|
+ if (unescapePredicateInComponent(c)) { |
+ return true; |
+ } |
+ return is_url && uriSeparator(c); |
+} |
+ |
+void uriAddEncodedOctetToBuffer(uint16_t octet, List<uint16_t> *result) { |
+ // Do I need lazy initialization? |
Yang
2016/05/12 07:39:21
No. Just declare them as static const. And using a
Franzi
2016/05/13 09:42:02
Done.
|
+ int hexCharCodeArray[16] = {48, 49, 50, 51, 52, 53, 54, 55, 56, 57, // 0..9 |
Yang
2016/05/12 07:39:21
We have a HexCharOfValue in bignum.cc. You could m
|
+ 65, 66, 67, 68, 69, 70}; // A..F |
+ |
+ result->Add(37); // Char code of '%'. |
Yang
2016/05/12 07:39:21
How about result->Add('%')
Franzi
2016/05/13 09:42:02
Done.
|
+ uint16_t firstHex = octet >> 4; |
+ uint16_t secondHex = octet & 0x0F; |
+ |
+ DCHECK(firstHex < 16); |
+ DCHECK(secondHex < 16); |
+ |
+ result->Add(hexCharCodeArray[firstHex]); |
+ result->Add(hexCharCodeArray[secondHex]); |
+} |
+ |
+void uriEncodeOctets(uint16_t *octets, List<uint16_t> *result) { |
+ uriAddEncodedOctetToBuffer(octets[0], result); |
+ if (octets[1]) uriAddEncodedOctetToBuffer(octets[1], result); |
+ if (octets[2]) uriAddEncodedOctetToBuffer(octets[2], result); |
+ if (octets[3]) uriAddEncodedOctetToBuffer(octets[3], result); |
+} |
+ |
+void uriEncodeSingle(uint16_t cc, List<uint16_t> *result) { |
+ // 16 bits total, cut it in 4,6, and 6 bits |
Yang
2016/05/12 07:39:21
whitespace after comma.
Franzi
2016/05/13 09:42:02
Done.
|
+ uint16_t x = (cc >> 12) & 0xF; |
Yang
2016/05/12 07:39:21
uint8_t should suffice.
Franzi
2016/05/13 09:42:02
Done.
|
+ uint16_t y = (cc >> 6) & 63; |
+ uint16_t z = cc & 63; // get last 6 bits |
+ uint16_t octets[4] = {0, 0, 0, 0}; // TODO(franzih) 3 is enough here |
+ if (cc <= 0x007F) { // smaller than 8 bits, i.e., 128 |
+ octets[0] = cc; // ascii same as UTF-8 |
Yang
2016/05/12 07:39:21
Instead of collecting octets, let's just call uriA
Franzi
2016/05/13 09:42:03
Done.
|
+ } else if (cc <= 0x07FF) { |
+ octets[0] = y + 192; // Leading byte: 110xxxxx |
+ octets[1] = z + 128; // Continuation byte: 10xxxxxx |
+ } else { |
+ octets[0] = x + 224; // Leading byte: 1110xxxx |
+ octets[1] = y + 128; // Continuation byte: 10xxxxxx |
+ octets[2] = z + 128; // Continuation byte: 10xxxxxx |
+ } |
+ |
+ uriEncodeOctets(octets, result); |
+} |
+ |
+void uriEncodePair(uint16_t cc1, uint16_t cc2, List<uint16_t> *result) { |
+ uint16_t u = ((cc1 >> 6) & 0xF) + 1; |
+ uint16_t w = (cc1 >> 2) & 0xF; |
+ uint16_t x = cc1 & 3; |
+ uint16_t y = (cc2 >> 6) & 0xF; |
+ uint16_t z = cc2 & 63; |
+ uint16_t octets[4] = {0, 0, 0, 0}; |
+ octets[0] = (u >> 2) + 240; |
+ octets[1] = (((u & 3) << 4) | w) + 128; |
+ octets[2] = ((x << 4) | y) + 128; |
+ octets[3] = z + 128; |
+ uriEncodeOctets(octets, result); |
+} |
+ |
+RUNTIME_FUNCTION(Runtime_Encode) { |
+ HandleScope scope(isolate); |
+ DCHECK(args.length() == 2); |
+ CONVERT_ARG_HANDLE_CHECKED(String, uri, 0); |
+ CONVERT_BOOLEAN_ARG_CHECKED(is_uri, 1); |
+ |
+ size_t uriLength = uri->length(); |
Yang
2016/05/12 07:39:20
Let's use int here.
Franzi
2016/05/13 09:42:03
Done.
|
+ List<uint16_t> buffer; |
Yang
2016/05/12 07:39:21
The result can only be ASCII characters, so please
Franzi
2016/05/13 09:42:03
Done.
|
+ |
+ for (size_t k = 0; k < uriLength; k++) { |
Yang
2016/05/12 07:39:21
And int here.
Franzi
2016/05/13 09:42:02
Done.
|
+ uint16_t cc1 = uri->Get(static_cast<int>(k)); |
Yang
2016/05/12 07:39:21
Calling Get is fairly inefficient. We should flat
Franzi
2016/05/13 09:42:02
Done. Using Get() on FlatContent.
|
+ |
+ if (unescape(cc1, is_uri)) { |
+ DCHECK(cc1 <= String::kMaxOneByteCharCode); |
+ buffer.Add(cc1); |
+ } else { |
+ if (cc1 >= 0xDC00 && cc1 <= 0xDFFF) { |
Yang
2016/05/12 07:39:21
please do not use magic numbers here. Use unibrow:
Franzi
2016/05/13 09:42:02
Done.
|
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewMakeURIError()); |
+ } |
+ if (cc1 < 0xD800 || cc1 > 0xDBFF) { |
Yang
2016/05/12 07:39:21
!unibrow::Utf16::IsLeadSurrogate
Franzi
2016/05/13 09:42:02
Done.
|
+ uriEncodeSingle(cc1, &buffer); |
+ } else { |
+ k++; |
+ if (k == uriLength) { |
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewMakeURIError()); |
+ } |
+ uint16_t cc2 = uri->Get(static_cast<int>(k)); |
+ if (cc2 < 0xDC00 || cc2 > 0xDFFF) { |
Yang
2016/05/12 07:39:21
Here as well.
Franzi
2016/05/13 09:42:03
Done.
|
+ THROW_NEW_ERROR_RETURN_FAILURE(isolate, NewMakeURIError()); |
+ } |
+ uriEncodePair(cc1, cc2, &buffer); |
+ } |
+ } |
+ } |
+ |
+ Handle<String> result; |
+ int totalLength = buffer.length(); |
+ |
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
+ isolate, result, isolate->factory()->NewRawOneByteString(totalLength)); |
Yang
2016/05/12 07:39:21
NewStringFromAscii(buffer.ToConstVector()) would w
Franzi
2016/05/13 09:42:03
Done. I'm using NewStringFromOneByte() because buf
|
+ |
+ for (int i = 0; i < totalLength; i++) { |
+ uint16_t value = buffer.at(i); |
+ result->Set(i, value); |
+ } |
+ return *result; |
+} |
+ |
RUNTIME_FUNCTION(Runtime_StringLessThan) { |
HandleScope handle_scope(isolate); |
DCHECK_EQ(2, args.length()); |
@@ -1265,7 +1395,7 @@ RUNTIME_FUNCTION(Runtime_StringCharAt) { |
if (!args[0]->IsString()) return Smi::FromInt(0); |
if (!args[1]->IsNumber()) return Smi::FromInt(0); |
if (std::isinf(args.number_at(1))) return isolate->heap()->empty_string(); |
- Object* code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); |
+ Object *code = __RT_impl_Runtime_StringCharCodeAtRT(args, isolate); |
if (code->IsNaN()) return isolate->heap()->empty_string(); |
return __RT_impl_Runtime_StringCharFromCode(Arguments(1, &code), isolate); |
} |