Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(417)

Unified Diff: src/builtins/builtins-string.cc

Issue 2165593002: [builtins] Move builtins into own files (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Remove builtins-error.cc from BUILD.gn Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer.cc ('k') | src/builtins/builtins-symbol.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/builtins/builtins-string.cc
diff --git a/src/builtins/builtins-string.cc b/src/builtins/builtins-string.cc
new file mode 100644
index 0000000000000000000000000000000000000000..7faf22da2f07e22ad9ca2677020d39150d8fa9f4
--- /dev/null
+++ b/src/builtins/builtins-string.cc
@@ -0,0 +1,529 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/builtins/builtins.h"
+#include "src/builtins/builtins-utils.h"
+
+#include "src/code-factory.h"
+
+namespace v8 {
+namespace internal {
+
+// -----------------------------------------------------------------------------
+// ES6 section 21.1 String Objects
+
+// 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;
+
+ Node* code = assembler->Parameter(1);
+ Node* context = assembler->Parameter(4);
+
+ // Check if we have exactly one argument (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);
+ }
+
+ 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);
+
+ 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);
+ }
+
+ 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);
+ }
+ }
+
+ assembler->Bind(&done_loop);
+ assembler->Return(result);
+ }
+}
+
+namespace { // for String.fromCodePoint
+
+bool IsValidCodePoint(Isolate* isolate, Handle<Object> value) {
+ if (!value->IsNumber() && !Object::ToNumber(value).ToHandle(&value)) {
+ return false;
+ }
+
+ if (Object::ToInteger(isolate, value).ToHandleChecked()->Number() !=
+ value->Number()) {
+ return false;
+ }
+
+ if (value->Number() < 0 || value->Number() > 0x10FFFF) {
+ return false;
+ }
+
+ return true;
+}
+
+uc32 NextCodePoint(Isolate* isolate, BuiltinArguments args, int index) {
+ Handle<Object> value = args.at<Object>(1 + index);
+ ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate, value, Object::ToNumber(value), -1);
+ if (!IsValidCodePoint(isolate, value)) {
+ isolate->Throw(*isolate->factory()->NewRangeError(
+ MessageTemplate::kInvalidCodePoint, value));
+ return -1;
+ }
+ return DoubleToUint32(value->Number());
+}
+
+} // namespace
+
+// ES6 section 21.1.2.2 String.fromCodePoint ( ...codePoints )
+BUILTIN(StringFromCodePoint) {
+ HandleScope scope(isolate);
+ int const length = args.length() - 1;
+ if (length == 0) return isolate->heap()->empty_string();
+ DCHECK_LT(0, length);
+
+ // Optimistically assume that the resulting String contains only one byte
+ // characters.
+ List<uint8_t> one_byte_buffer(length);
+ uc32 code = 0;
+ int index;
+ for (index = 0; index < length; index++) {
+ code = NextCodePoint(isolate, args, index);
+ if (code < 0) {
+ return isolate->heap()->exception();
+ }
+ if (code > String::kMaxOneByteCharCode) {
+ break;
+ }
+ one_byte_buffer.Add(code);
+ }
+
+ if (index == length) {
+ RETURN_RESULT_OR_FAILURE(isolate, isolate->factory()->NewStringFromOneByte(
+ one_byte_buffer.ToConstVector()));
+ }
+
+ List<uc16> two_byte_buffer(length - index);
+
+ while (true) {
+ if (code <= unibrow::Utf16::kMaxNonSurrogateCharCode) {
+ two_byte_buffer.Add(code);
+ } else {
+ two_byte_buffer.Add(unibrow::Utf16::LeadSurrogate(code));
+ two_byte_buffer.Add(unibrow::Utf16::TrailSurrogate(code));
+ }
+
+ if (++index == length) {
+ break;
+ }
+ code = NextCodePoint(isolate, args, index);
+ if (code < 0) {
+ return isolate->heap()->exception();
+ }
+ }
+
+ Handle<SeqTwoByteString> result;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, result,
+ isolate->factory()->NewRawTwoByteString(one_byte_buffer.length() +
+ two_byte_buffer.length()));
+
+ CopyChars(result->GetChars(), one_byte_buffer.ToConstVector().start(),
+ one_byte_buffer.length());
+ CopyChars(result->GetChars() + one_byte_buffer.length(),
+ two_byte_buffer.ToConstVector().start(), two_byte_buffer.length());
+
+ return *result;
+}
+
+// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
+void Builtins::Generate_StringPrototypeCharAt(CodeStubAssembler* assembler) {
+ typedef CodeStubAssembler::Label Label;
+ typedef compiler::Node Node;
+ typedef CodeStubAssembler::Variable Variable;
+
+ Node* receiver = assembler->Parameter(0);
+ Node* position = assembler->Parameter(1);
+ Node* context = assembler->Parameter(4);
+
+ // Check that {receiver} is coercible to Object and convert it to a String.
+ receiver =
+ assembler->ToThisString(context, receiver, "String.prototype.charAt");
+
+ // Convert the {position} to a Smi and check that it's in bounds of the
+ // {receiver}.
+ // TODO(bmeurer): Find an abstraction for this!
+ {
+ // Check if the {position} is already a Smi.
+ Variable var_position(assembler, MachineRepresentation::kTagged);
+ var_position.Bind(position);
+ Label if_positionissmi(assembler),
+ if_positionisnotsmi(assembler, Label::kDeferred);
+ assembler->Branch(assembler->WordIsSmi(position), &if_positionissmi,
+ &if_positionisnotsmi);
+ assembler->Bind(&if_positionisnotsmi);
+ {
+ // Convert the {position} to an Integer via the ToIntegerStub.
+ Callable callable = CodeFactory::ToInteger(assembler->isolate());
+ Node* index = assembler->CallStub(callable, context, position);
+
+ // Check if the resulting {index} is now a Smi.
+ Label if_indexissmi(assembler, Label::kDeferred),
+ if_indexisnotsmi(assembler, Label::kDeferred);
+ assembler->Branch(assembler->WordIsSmi(index), &if_indexissmi,
+ &if_indexisnotsmi);
+
+ assembler->Bind(&if_indexissmi);
+ {
+ var_position.Bind(index);
+ assembler->Goto(&if_positionissmi);
+ }
+
+ assembler->Bind(&if_indexisnotsmi);
+ {
+ // The ToIntegerStub canonicalizes everything in Smi range to Smi
+ // representation, so any HeapNumber returned is not in Smi range.
+ // The only exception here is -0.0, which we treat as 0.
+ Node* index_value = assembler->LoadHeapNumberValue(index);
+ Label if_indexiszero(assembler, Label::kDeferred),
+ if_indexisnotzero(assembler, Label::kDeferred);
+ assembler->Branch(assembler->Float64Equal(
+ index_value, assembler->Float64Constant(0.0)),
+ &if_indexiszero, &if_indexisnotzero);
+
+ assembler->Bind(&if_indexiszero);
+ {
+ var_position.Bind(assembler->SmiConstant(Smi::FromInt(0)));
+ assembler->Goto(&if_positionissmi);
+ }
+
+ assembler->Bind(&if_indexisnotzero);
+ {
+ // The {index} is some other integral Number, that is definitely
+ // neither -0.0 nor in Smi range.
+ assembler->Return(assembler->EmptyStringConstant());
+ }
+ }
+ }
+ assembler->Bind(&if_positionissmi);
+ position = var_position.value();
+
+ // Determine the actual length of the {receiver} String.
+ Node* receiver_length =
+ assembler->LoadObjectField(receiver, String::kLengthOffset);
+
+ // Return "" if the Smi {position} is outside the bounds of the {receiver}.
+ Label if_positioninbounds(assembler),
+ if_positionnotinbounds(assembler, Label::kDeferred);
+ assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
+ &if_positionnotinbounds, &if_positioninbounds);
+ assembler->Bind(&if_positionnotinbounds);
+ assembler->Return(assembler->EmptyStringConstant());
+ assembler->Bind(&if_positioninbounds);
+ }
+
+ // Load the character code at the {position} from the {receiver}.
+ Node* code = assembler->StringCharCodeAt(receiver, position);
+
+ // And return the single character string with only that {code}.
+ Node* result = assembler->StringFromCharCode(code);
+ assembler->Return(result);
+}
+
+// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
+void Builtins::Generate_StringPrototypeCharCodeAt(
+ CodeStubAssembler* assembler) {
+ typedef CodeStubAssembler::Label Label;
+ typedef compiler::Node Node;
+ typedef CodeStubAssembler::Variable Variable;
+
+ Node* receiver = assembler->Parameter(0);
+ Node* position = assembler->Parameter(1);
+ Node* context = assembler->Parameter(4);
+
+ // Check that {receiver} is coercible to Object and convert it to a String.
+ receiver =
+ assembler->ToThisString(context, receiver, "String.prototype.charCodeAt");
+
+ // Convert the {position} to a Smi and check that it's in bounds of the
+ // {receiver}.
+ // TODO(bmeurer): Find an abstraction for this!
+ {
+ // Check if the {position} is already a Smi.
+ Variable var_position(assembler, MachineRepresentation::kTagged);
+ var_position.Bind(position);
+ Label if_positionissmi(assembler),
+ if_positionisnotsmi(assembler, Label::kDeferred);
+ assembler->Branch(assembler->WordIsSmi(position), &if_positionissmi,
+ &if_positionisnotsmi);
+ assembler->Bind(&if_positionisnotsmi);
+ {
+ // Convert the {position} to an Integer via the ToIntegerStub.
+ Callable callable = CodeFactory::ToInteger(assembler->isolate());
+ Node* index = assembler->CallStub(callable, context, position);
+
+ // Check if the resulting {index} is now a Smi.
+ Label if_indexissmi(assembler, Label::kDeferred),
+ if_indexisnotsmi(assembler, Label::kDeferred);
+ assembler->Branch(assembler->WordIsSmi(index), &if_indexissmi,
+ &if_indexisnotsmi);
+
+ assembler->Bind(&if_indexissmi);
+ {
+ var_position.Bind(index);
+ assembler->Goto(&if_positionissmi);
+ }
+
+ assembler->Bind(&if_indexisnotsmi);
+ {
+ // The ToIntegerStub canonicalizes everything in Smi range to Smi
+ // representation, so any HeapNumber returned is not in Smi range.
+ // The only exception here is -0.0, which we treat as 0.
+ Node* index_value = assembler->LoadHeapNumberValue(index);
+ Label if_indexiszero(assembler, Label::kDeferred),
+ if_indexisnotzero(assembler, Label::kDeferred);
+ assembler->Branch(assembler->Float64Equal(
+ index_value, assembler->Float64Constant(0.0)),
+ &if_indexiszero, &if_indexisnotzero);
+
+ assembler->Bind(&if_indexiszero);
+ {
+ var_position.Bind(assembler->SmiConstant(Smi::FromInt(0)));
+ assembler->Goto(&if_positionissmi);
+ }
+
+ assembler->Bind(&if_indexisnotzero);
+ {
+ // The {index} is some other integral Number, that is definitely
+ // neither -0.0 nor in Smi range.
+ assembler->Return(assembler->NaNConstant());
+ }
+ }
+ }
+ assembler->Bind(&if_positionissmi);
+ position = var_position.value();
+
+ // Determine the actual length of the {receiver} String.
+ Node* receiver_length =
+ assembler->LoadObjectField(receiver, String::kLengthOffset);
+
+ // Return NaN if the Smi {position} is outside the bounds of the {receiver}.
+ Label if_positioninbounds(assembler),
+ if_positionnotinbounds(assembler, Label::kDeferred);
+ assembler->Branch(assembler->SmiAboveOrEqual(position, receiver_length),
+ &if_positionnotinbounds, &if_positioninbounds);
+ assembler->Bind(&if_positionnotinbounds);
+ assembler->Return(assembler->NaNConstant());
+ assembler->Bind(&if_positioninbounds);
+ }
+
+ // Load the character at the {position} from the {receiver}.
+ Node* value = assembler->StringCharCodeAt(receiver, position);
+ Node* result = assembler->SmiFromWord32(value);
+ assembler->Return(result);
+}
+
+// ES6 section 21.1.3.25 String.prototype.toString ()
+void Builtins::Generate_StringPrototypeToString(CodeStubAssembler* assembler) {
+ typedef compiler::Node Node;
+
+ Node* receiver = assembler->Parameter(0);
+ Node* context = assembler->Parameter(3);
+
+ Node* result = assembler->ToThisValue(
+ context, receiver, PrimitiveType::kString, "String.prototype.toString");
+ assembler->Return(result);
+}
+
+// ES6 section 21.1.3.27 String.prototype.trim ()
+BUILTIN(StringPrototypeTrim) {
+ HandleScope scope(isolate);
+ TO_THIS_STRING(string, "String.prototype.trim");
+ return *String::Trim(string, String::kTrim);
+}
+
+// Non-standard WebKit extension
+BUILTIN(StringPrototypeTrimLeft) {
+ HandleScope scope(isolate);
+ TO_THIS_STRING(string, "String.prototype.trimLeft");
+ return *String::Trim(string, String::kTrimLeft);
+}
+
+// Non-standard WebKit extension
+BUILTIN(StringPrototypeTrimRight) {
+ HandleScope scope(isolate);
+ TO_THIS_STRING(string, "String.prototype.trimRight");
+ return *String::Trim(string, String::kTrimRight);
+}
+
+// ES6 section 21.1.3.28 String.prototype.valueOf ( )
+void Builtins::Generate_StringPrototypeValueOf(CodeStubAssembler* assembler) {
+ typedef compiler::Node Node;
+
+ Node* receiver = assembler->Parameter(0);
+ Node* context = assembler->Parameter(3);
+
+ Node* result = assembler->ToThisValue(
+ context, receiver, PrimitiveType::kString, "String.prototype.valueOf");
+ assembler->Return(result);
+}
+
+} // namespace internal
+} // namespace v8
« no previous file with comments | « src/builtins/builtins-sharedarraybuffer.cc ('k') | src/builtins/builtins-symbol.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698