Index: src/builtins.cc |
diff --git a/src/builtins.cc b/src/builtins.cc |
index c79a5b6728154dc777c6f932363f5715bba5defb..0df2a9d06793f0f755aea1bec1de38f2f5f1ef29 100644 |
--- a/src/builtins.cc |
+++ b/src/builtins.cc |
@@ -4309,67 +4309,191 @@ BUILTIN(ObjectProtoToString) { |
// ----------------------------------------------------------------------------- |
// ES6 section 21.1 String Objects |
-namespace { |
+// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits ) |
+void Builtins::Generate_StringFromCharCode(CodeStubAssembler* assembler) { |
+ typedef CodeStubAssembler::Label Label; |
+ typedef compiler::Node Node; |
+ typedef CodeStubAssembler::Variable Variable; |
-bool ToUint16(Handle<Object> value, uint16_t* result) { |
- if (value->IsNumber() || Object::ToNumber(value).ToHandle(&value)) { |
- *result = DoubleToUint32(value->Number()); |
- return true; |
+ Node* code = assembler->Parameter(1); |
+ Node* context = assembler->Parameter(4); |
+ |
+ // Check if we have exactly one arguments (plus the implicit receiver), i.e. |
+ // if the parent frame is not an arguments adaptor frame. |
+ Label if_oneargument(assembler), if_notoneargument(assembler); |
+ Node* parent_frame_pointer = assembler->LoadParentFramePointer(); |
+ Node* parent_frame_type = |
+ assembler->Load(MachineType::Pointer(), parent_frame_pointer, |
+ assembler->IntPtrConstant( |
+ CommonFrameConstants::kContextOrFrameTypeOffset)); |
+ assembler->Branch( |
+ assembler->WordEqual( |
+ parent_frame_type, |
+ assembler->SmiConstant(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR))), |
+ &if_notoneargument, &if_oneargument); |
+ |
+ assembler->Bind(&if_oneargument); |
+ { |
+ // Single argument case, perform fast single character string cache lookup |
+ // for one-byte code units, or fall back to creating a single character |
+ // string on the fly otherwise. |
+ Node* code32 = assembler->TruncateTaggedToWord32(context, code); |
+ Node* code16 = assembler->Word32And( |
+ code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit)); |
+ Node* result = assembler->StringFromCharCode(code16); |
+ assembler->Return(result); |
} |
- return false; |
-} |
-} // namespace |
+ assembler->Bind(&if_notoneargument); |
+ { |
+ // Determine the resulting string length. |
+ Node* parent_frame_length = |
+ assembler->Load(MachineType::Pointer(), parent_frame_pointer, |
+ assembler->IntPtrConstant( |
+ ArgumentsAdaptorFrameConstants::kLengthOffset)); |
+ Node* length = assembler->SmiToWord(parent_frame_length); |
+ |
+ // Assume that the resulting string contains only one-byte characters. |
+ Node* result = assembler->AllocateSeqOneByteString(context, length); |
+ |
+ // Truncate all input parameters and append them to the resulting string. |
+ Variable var_offset(assembler, MachineType::PointerRepresentation()); |
+ Label loop(assembler, &var_offset), done_loop(assembler); |
+ var_offset.Bind(assembler->IntPtrConstant(0)); |
+ assembler->Goto(&loop); |
+ assembler->Bind(&loop); |
+ { |
+ // Load the current {offset}. |
+ Node* offset = var_offset.value(); |
+ |
+ // Check if we're done with the string. |
+ assembler->GotoIf(assembler->WordEqual(offset, length), &done_loop); |
+ |
+ // Load the next code point and truncate it to a 16-bit value. |
+ Node* code = assembler->Load( |
+ MachineType::AnyTagged(), parent_frame_pointer, |
+ assembler->IntPtrAdd( |
+ assembler->WordShl(assembler->IntPtrSub(length, offset), |
+ assembler->IntPtrConstant(kPointerSizeLog2)), |
+ assembler->IntPtrConstant( |
+ CommonFrameConstants::kFixedFrameSizeAboveFp - |
+ kPointerSize))); |
+ Node* code32 = assembler->TruncateTaggedToWord32(context, code); |
+ Node* code16 = assembler->Word32And( |
+ code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit)); |
+ |
+ // Check if {code16} fits into a one-byte string. |
+ Label if_codeisonebyte(assembler), if_codeistwobyte(assembler); |
+ assembler->Branch( |
+ assembler->Int32LessThanOrEqual( |
+ code16, assembler->Int32Constant(String::kMaxOneByteCharCode)), |
+ &if_codeisonebyte, &if_codeistwobyte); |
-// ES6 21.1.2.1 String.fromCharCode ( ...codeUnits ) |
-BUILTIN(StringFromCharCode) { |
- HandleScope scope(isolate); |
- // Check resulting string length. |
- int index = 0; |
- Handle<String> result; |
- int const length = args.length() - 1; |
- if (length == 0) return isolate->heap()->empty_string(); |
- DCHECK_LT(0, length); |
- // Load the first character code. |
- uint16_t code; |
- if (!ToUint16(args.at<Object>(1), &code)) return isolate->heap()->exception(); |
- // Assume that the resulting String contains only one byte characters. |
- if (code <= String::kMaxOneByteCharCodeU) { |
- // Check for single one-byte character fast case. |
- if (length == 1) { |
- return *isolate->factory()->LookupSingleCharacterStringFromCode(code); |
- } |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
- isolate, result, isolate->factory()->NewRawOneByteString(length)); |
- do { |
- Handle<SeqOneByteString>::cast(result)->Set(index, code); |
- if (++index == length) break; |
- if (!ToUint16(args.at<Object>(1 + index), &code)) { |
- return isolate->heap()->exception(); |
+ assembler->Bind(&if_codeisonebyte); |
+ { |
+ // The {code16} fits into the SeqOneByteString {result}. |
+ assembler->StoreNoWriteBarrier( |
+ MachineRepresentation::kWord8, result, |
+ assembler->IntPtrAdd( |
+ assembler->IntPtrConstant(SeqOneByteString::kHeaderSize - |
+ kHeapObjectTag), |
+ offset), |
+ code16); |
+ var_offset.Bind( |
+ assembler->IntPtrAdd(offset, assembler->IntPtrConstant(1))); |
+ assembler->Goto(&loop); |
} |
- } while (code <= String::kMaxOneByteCharCodeU); |
- } |
- // Check if all characters fit into the one byte range. |
- if (index < length) { |
- // Fallback to two byte string. |
- Handle<String> new_result; |
- ASSIGN_RETURN_FAILURE_ON_EXCEPTION( |
- isolate, new_result, isolate->factory()->NewRawTwoByteString(length)); |
- for (int new_index = 0; new_index < index; ++new_index) { |
- uint16_t new_code = |
- Handle<SeqOneByteString>::cast(result)->Get(new_index); |
- Handle<SeqTwoByteString>::cast(new_result)->Set(new_index, new_code); |
- } |
- while (true) { |
- Handle<SeqTwoByteString>::cast(new_result)->Set(index, code); |
- if (++index == length) break; |
- if (!ToUint16(args.at<Object>(1 + index), &code)) { |
- return isolate->heap()->exception(); |
+ |
+ assembler->Bind(&if_codeistwobyte); |
+ { |
+ // Allocate a SeqTwoByteString to hold the resulting string. |
+ Node* cresult = assembler->AllocateSeqTwoByteString(context, length); |
+ |
+ // Copy all characters that were previously written to the |
+ // SeqOneByteString in {result} over to the new {cresult}. |
+ Variable var_coffset(assembler, MachineType::PointerRepresentation()); |
+ Label cloop(assembler, &var_coffset), done_cloop(assembler); |
+ var_coffset.Bind(assembler->IntPtrConstant(0)); |
+ assembler->Goto(&cloop); |
+ assembler->Bind(&cloop); |
+ { |
+ Node* coffset = var_coffset.value(); |
+ assembler->GotoIf(assembler->WordEqual(coffset, offset), &done_cloop); |
+ Node* ccode = assembler->Load( |
+ MachineType::Uint8(), result, |
+ assembler->IntPtrAdd( |
+ assembler->IntPtrConstant(SeqOneByteString::kHeaderSize - |
+ kHeapObjectTag), |
+ coffset)); |
+ assembler->StoreNoWriteBarrier( |
+ MachineRepresentation::kWord16, cresult, |
+ assembler->IntPtrAdd( |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag), |
+ assembler->WordShl(coffset, 1)), |
+ ccode); |
+ var_coffset.Bind( |
+ assembler->IntPtrAdd(coffset, assembler->IntPtrConstant(1))); |
+ assembler->Goto(&cloop); |
+ } |
+ |
+ // Write the pending {code16} to {offset}. |
+ assembler->Bind(&done_cloop); |
+ assembler->StoreNoWriteBarrier( |
+ MachineRepresentation::kWord16, cresult, |
+ assembler->IntPtrAdd( |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag), |
+ assembler->WordShl(offset, 1)), |
+ code16); |
+ |
+ // Copy the remaining parameters to the SeqTwoByteString {cresult}. |
+ Label floop(assembler, &var_offset), done_floop(assembler); |
+ assembler->Goto(&floop); |
+ assembler->Bind(&floop); |
+ { |
+ // Compute the next {offset}. |
+ Node* offset = assembler->IntPtrAdd(var_offset.value(), |
+ assembler->IntPtrConstant(1)); |
+ |
+ // Check if we're done with the string. |
+ assembler->GotoIf(assembler->WordEqual(offset, length), &done_floop); |
+ |
+ // Load the next code point and truncate it to a 16-bit value. |
+ Node* code = assembler->Load( |
+ MachineType::AnyTagged(), parent_frame_pointer, |
+ assembler->IntPtrAdd( |
+ assembler->WordShl( |
+ assembler->IntPtrSub(length, offset), |
+ assembler->IntPtrConstant(kPointerSizeLog2)), |
+ assembler->IntPtrConstant( |
+ CommonFrameConstants::kFixedFrameSizeAboveFp - |
+ kPointerSize))); |
+ Node* code32 = assembler->TruncateTaggedToWord32(context, code); |
+ Node* code16 = assembler->Word32And( |
+ code32, assembler->Int32Constant(String::kMaxUtf16CodeUnit)); |
+ |
+ // Store the truncated {code} point at the next offset. |
+ assembler->StoreNoWriteBarrier( |
+ MachineRepresentation::kWord16, cresult, |
+ assembler->IntPtrAdd( |
+ assembler->IntPtrConstant(SeqTwoByteString::kHeaderSize - |
+ kHeapObjectTag), |
+ assembler->WordShl(offset, 1)), |
+ code16); |
+ var_offset.Bind(offset); |
+ assembler->Goto(&floop); |
+ } |
+ |
+ // Return the SeqTwoByteString. |
+ assembler->Bind(&done_floop); |
+ assembler->Return(cresult); |
} |
} |
- result = new_result; |
+ |
+ assembler->Bind(&done_loop); |
+ assembler->Return(result); |
} |
- return *result; |
} |
// ES6 section 21.1.3.1 String.prototype.charAt ( pos ) |