一、string.js 内 StringConcat 方法
// ECMA-262, section 15.5.4.6
function StringConcat() {
if (IS_NULL_OR_UNDEFINED(this) && !IS_UNDETECTABLE(this)) {
throw MakeTypeError("called_on_null_or_undefined", ["String.prototype.concat"]);
}
var len = %_ArgumentsLength();
var this_as_string = TO_STRING_INLINE(this);
if (len === 1) {
return this_as_string + %_Arguments(0);
}
var parts = new InternalArray(len + 1);
parts[0] = this_as_string;
for (var i = 0; i < len; i++) {
var part = %_Arguments(i);
parts[i + 1] = TO_STRING_INLINE(part);
}
return %StringBuilderConcat(parts, len + 1, "");
}
二、调用 %StringBuilderConcat 内置方法,它在 runtime.cc 内。
RUNTIME_FUNCTION(MaybeObject*, Runtime_StringBuilderConcat) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
CONVERT_CHECKED(JSArray, array, args[0]);
if (!args[1]->IsSmi()) {
isolate->context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
int array_length = args.smi_at(1);
CONVERT_CHECKED(String, special, args[2]);
// This assumption is used by the slice encoding in one or two smis.
ASSERT(Smi::kMaxValue >= String::kMaxLength);
int special_length = special->length();
if (!array->HasFastElements()) {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
FixedArray* fixed_array = FixedArray::cast(array->elements());
if (fixed_array->length() < array_length) {
array_length = fixed_array->length();
}
if (array_length == 0) {
return isolate->heap()->empty_string();
} else if (array_length == 1) {
Object* first = fixed_array->get(0);
if (first->IsString()) return first;
}
bool ascii = special->HasOnlyAsciiChars();
int position = 0;
for (int i = 0; i < array_length; i++) {
int increment = 0;
Object* elt = fixed_array->get(i);
if (elt->IsSmi()) {
// Smi encoding of position and length.
int smi_value = Smi::cast(elt)->value();
int pos;
int len;
if (smi_value > 0) {
// Position and length encoded in one smi.
pos = StringBuilderSubstringPosition::decode(smi_value);
len = StringBuilderSubstringLength::decode(smi_value);
} else {
// Position and length encoded in two smis.
len = -smi_value;
// Get the position and check that it is a positive smi.
i++;
if (i >= array_length) {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
Object* next_smi = fixed_array->get(i);
if (!next_smi->IsSmi()) {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
pos = Smi::cast(next_smi)->value();
if (pos < 0) {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
}
ASSERT(pos >= 0);
ASSERT(len >= 0);
if (pos > special_length || len > special_length - pos) {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
increment = len;
} else if (elt->IsString()) {
String* element = String::cast(elt);
int element_length = element->length();
increment = element_length;
if (ascii && !element->HasOnlyAsciiChars()) {
ascii = false;
}
} else {
return isolate->Throw(isolate->heap()->illegal_argument_symbol());
}
if (increment > String::kMaxLength - position) {
isolate->context()->mark_out_of_memory();
return Failure::OutOfMemoryException();
}
position += increment;
}
int length = position;
Object* object;
if (ascii) {
{ MaybeObject* maybe_object =
isolate->heap()->AllocateRawAsciiString(length);
if (!maybe_object->ToObject(&object)) return maybe_object;
}
SeqAsciiString* answer = SeqAsciiString::cast(object);
StringBuilderConcatHelper(special,
answer->GetChars(),
fixed_array,
array_length);
return answer;
} else {
{ MaybeObject* maybe_object =
isolate->heap()->AllocateRawTwoByteString(length);
if (!maybe_object->ToObject(&object)) return maybe_object;
}
SeqTwoByteString* answer = SeqTwoByteString::cast(object);
StringBuilderConcatHelper(special,
answer->GetChars(),
fixed_array,
array_length);
return answer;
}
}
【注】:RUNTIME_FUNCTION 是个宏,定义在 argument.h 文件内
#define RUNTIME_FUNCTION(Type, Name) \
Type Name(Arguments args, Isolate* isolate)
三、调用 StringBuilderConcatHelper ,它同在 runtime.cc 内
template <typename sinkchar>
static inline void StringBuilderConcatHelper(String* special,
sinkchar* sink,
FixedArray* fixed_array,
int array_length) {
int position = 0;
for (int i = 0; i < array_length; i++) {
Object* element = fixed_array->get(i);
if (element->IsSmi()) {
// Smi encoding of position and length.
int encoded_slice = Smi::cast(element)->value();
int pos;
int len;
if (encoded_slice > 0) {
// Position and length encoded in one smi.
pos = StringBuilderSubstringPosition::decode(encoded_slice);
len = StringBuilderSubstringLength::decode(encoded_slice);
} else {
// Position and length encoded in two smis.
Object* obj = fixed_array->get(++i);
ASSERT(obj->IsSmi());
pos = Smi::cast(obj)->value();
len = -encoded_slice;
}
String::WriteToFlat(special,
sink + position,
pos,
pos + len);
position += len;
} else {
String* string = String::cast(element);
int element_length = string->length();
String::WriteToFlat(string, sink + position, 0, element_length);
position += element_length;
}
}
}
四、调用 String::WriteToFlat
此后流程与 String + 调用 String::WriteToFlat 流程一致。