[builtins] Migrate String.prototype.charCodeAt and String.prototype.charAt to TurboFan.
Make these builtins fast by default w/o relying on the %_StringCharCodeAt and %_StringCharAt intrinsics, which we cannot deal with well in TurboFan (and ignition). R=epertoso@chromium.org Review URL: https://codereview.chromium.org/1868963002 Cr-Commit-Position: refs/heads/master@{#35350}
This commit is contained in:
parent
65eb0f6b33
commit
43d3331d87
@ -1255,6 +1255,22 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
attribs);
|
||||
string_map->AppendDescriptor(&d);
|
||||
}
|
||||
|
||||
// Create the %StringPrototype%
|
||||
Handle<JSValue> prototype =
|
||||
Handle<JSValue>::cast(factory->NewJSObject(string_fun, TENURED));
|
||||
prototype->set_value(isolate->heap()->empty_string());
|
||||
Accessors::FunctionSetPrototype(string_fun, prototype).Assert();
|
||||
|
||||
// Install the "constructor" property on the {prototype}.
|
||||
JSObject::AddProperty(prototype, factory->constructor_string(), string_fun,
|
||||
DONT_ENUM);
|
||||
|
||||
// Install the String.prototype methods.
|
||||
SimpleInstallFunction(prototype, "charAt", Builtins::kStringPrototypeCharAt,
|
||||
1, true);
|
||||
SimpleInstallFunction(prototype, "charCodeAt",
|
||||
Builtins::kStringPrototypeCharCodeAt, 1, true);
|
||||
}
|
||||
|
||||
{
|
||||
|
193
src/builtins.cc
193
src/builtins.cc
@ -4187,6 +4187,199 @@ BUILTIN(ObjectProtoToString) {
|
||||
return *result;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// ES6 section 21.1 String Objects
|
||||
|
||||
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
|
||||
void Builtins::Generate_StringPrototypeCharAt(
|
||||
compiler::CodeStubAssembler* assembler) {
|
||||
typedef compiler::CodeStubAssembler::Label Label;
|
||||
typedef compiler::Node Node;
|
||||
typedef compiler::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(
|
||||
compiler::CodeStubAssembler* assembler) {
|
||||
typedef compiler::CodeStubAssembler::Label Label;
|
||||
typedef compiler::Node Node;
|
||||
typedef compiler::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 ArrayBuffer Objects
|
||||
|
||||
// ES6 section 24.1.2.1 ArrayBuffer ( length ) for the [[Call]] case.
|
||||
BUILTIN(ArrayBufferConstructor) {
|
||||
|
@ -310,7 +310,9 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
|
||||
V(MathRound, 2) \
|
||||
V(MathSqrt, 2) \
|
||||
V(MathTrunc, 2) \
|
||||
V(ObjectHasOwnProperty, 2)
|
||||
V(ObjectHasOwnProperty, 2) \
|
||||
V(StringPrototypeCharAt, 2) \
|
||||
V(StringPrototypeCharCodeAt, 2)
|
||||
|
||||
// Define list of builtin handlers implemented in assembly.
|
||||
#define BUILTIN_LIST_H(V) \
|
||||
@ -626,6 +628,13 @@ class Builtins {
|
||||
static void Generate_ObjectHasOwnProperty(
|
||||
compiler::CodeStubAssembler* assembler);
|
||||
|
||||
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
|
||||
static void Generate_StringPrototypeCharAt(
|
||||
compiler::CodeStubAssembler* assembler);
|
||||
// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
|
||||
static void Generate_StringPrototypeCharCodeAt(
|
||||
compiler::CodeStubAssembler* assembler);
|
||||
|
||||
static void Generate_StringConstructor(MacroAssembler* masm);
|
||||
static void Generate_StringConstructor_ConstructStub(MacroAssembler* masm);
|
||||
static void Generate_OnStackReplacement(MacroAssembler* masm);
|
||||
|
@ -114,10 +114,22 @@ Node* CodeStubAssembler::BooleanMapConstant() {
|
||||
return HeapConstant(isolate()->factory()->boolean_map());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::EmptyStringConstant() {
|
||||
return LoadRoot(Heap::kempty_stringRootIndex);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::HeapNumberMapConstant() {
|
||||
return HeapConstant(isolate()->factory()->heap_number_map());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::NaNConstant() {
|
||||
return LoadRoot(Heap::kNanValueRootIndex);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::NoContextConstant() {
|
||||
return SmiConstant(Smi::FromInt(0));
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::NullConstant() {
|
||||
return LoadRoot(Heap::kNullValueRootIndex);
|
||||
}
|
||||
@ -349,6 +361,13 @@ Node* CodeStubAssembler::SmiUntag(Node* value) {
|
||||
return raw_assembler_->WordSar(value, SmiShiftBitsConstant());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiFromWord32(Node* value) {
|
||||
if (raw_assembler_->machine()->Is64()) {
|
||||
value = raw_assembler_->ChangeInt32ToInt64(value);
|
||||
}
|
||||
return raw_assembler_->WordShl(value, SmiShiftBitsConstant());
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiToWord32(Node* value) {
|
||||
Node* result = raw_assembler_->WordSar(value, SmiShiftBitsConstant());
|
||||
if (raw_assembler_->machine()->Is64()) {
|
||||
@ -375,6 +394,10 @@ Node* CodeStubAssembler::SmiSubWithOverflow(Node* a, Node* b) {
|
||||
|
||||
Node* CodeStubAssembler::SmiEqual(Node* a, Node* b) { return WordEqual(a, b); }
|
||||
|
||||
Node* CodeStubAssembler::SmiAboveOrEqual(Node* a, Node* b) {
|
||||
return UintPtrGreaterThanOrEqual(a, b);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::SmiLessThan(Node* a, Node* b) {
|
||||
return IntPtrLessThan(a, b);
|
||||
}
|
||||
@ -489,10 +512,13 @@ Node* CodeStubAssembler::LoadNameHash(Node* name) {
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadFixedArrayElementInt32Index(
|
||||
Node* object, Node* int32_index, int additional_offset) {
|
||||
Node* object, Node* index, int additional_offset) {
|
||||
Node* header_size = IntPtrConstant(additional_offset +
|
||||
FixedArray::kHeaderSize - kHeapObjectTag);
|
||||
Node* scaled_index = WordShl(int32_index, IntPtrConstant(kPointerSizeLog2));
|
||||
if (raw_assembler_->machine()->Is64()) {
|
||||
index = ChangeInt32ToInt64(index);
|
||||
}
|
||||
Node* scaled_index = WordShl(index, IntPtrConstant(kPointerSizeLog2));
|
||||
Node* offset = IntPtrAdd(scaled_index, header_size);
|
||||
return Load(MachineType::AnyTagged(), object, offset);
|
||||
}
|
||||
@ -534,6 +560,18 @@ Node* CodeStubAssembler::StoreFixedArrayElementNoWriteBarrier(Node* object,
|
||||
value);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::StoreFixedArrayElementInt32Index(Node* object,
|
||||
Node* index,
|
||||
Node* value) {
|
||||
if (raw_assembler_->machine()->Is64()) {
|
||||
index = ChangeInt32ToInt64(index);
|
||||
}
|
||||
Node* offset =
|
||||
IntPtrAdd(WordShl(index, IntPtrConstant(kPointerSizeLog2)),
|
||||
IntPtrConstant(FixedArray::kHeaderSize - kHeapObjectTag));
|
||||
return Store(MachineRepresentation::kTagged, object, offset, value);
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::LoadRoot(Heap::RootListIndex root_index) {
|
||||
if (isolate()->heap()->RootCanBeTreatedAsConstant(root_index)) {
|
||||
Handle<Object> root = isolate()->heap()->root_handle(root_index);
|
||||
@ -687,6 +725,26 @@ Node* CodeStubAssembler::AllocateHeapNumberWithValue(Node* value) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateSeqOneByteString(int length) {
|
||||
Node* result = Allocate(SeqOneByteString::SizeFor(length));
|
||||
StoreMapNoWriteBarrier(result, LoadRoot(Heap::kOneByteStringMapRootIndex));
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset,
|
||||
SmiConstant(Smi::FromInt(length)));
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kHashFieldOffset,
|
||||
IntPtrConstant(String::kEmptyHashField));
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length) {
|
||||
Node* result = Allocate(SeqTwoByteString::SizeFor(length));
|
||||
StoreMapNoWriteBarrier(result, LoadRoot(Heap::kStringMapRootIndex));
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset,
|
||||
SmiConstant(Smi::FromInt(length)));
|
||||
StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kHashFieldOffset,
|
||||
IntPtrConstant(String::kEmptyHashField));
|
||||
return result;
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::Load(MachineType rep, Node* base) {
|
||||
return raw_assembler_->Load(rep, base);
|
||||
}
|
||||
@ -936,6 +994,297 @@ Node* CodeStubAssembler::TruncateTaggedToWord32(Node* context, Node* value) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::ToThisString(Node* context, Node* value,
|
||||
char const* method_name) {
|
||||
Variable var_value(this, MachineRepresentation::kTagged);
|
||||
var_value.Bind(value);
|
||||
|
||||
// Check if the {value} is a Smi or a HeapObject.
|
||||
Label if_valueissmi(this, Label::kDeferred), if_valueisnotsmi(this),
|
||||
if_valueisstring(this);
|
||||
Branch(WordIsSmi(value), &if_valueissmi, &if_valueisnotsmi);
|
||||
Bind(&if_valueisnotsmi);
|
||||
{
|
||||
// Load the instance type of the {value}.
|
||||
Node* value_instance_type = LoadInstanceType(value);
|
||||
|
||||
// Check if the {value} is already String.
|
||||
Label if_valueisnotstring(this, Label::kDeferred);
|
||||
Branch(
|
||||
Int32LessThan(value_instance_type, Int32Constant(FIRST_NONSTRING_TYPE)),
|
||||
&if_valueisstring, &if_valueisnotstring);
|
||||
Bind(&if_valueisnotstring);
|
||||
{
|
||||
// Check if the {value} is null.
|
||||
Label if_valueisnullorundefined(this, Label::kDeferred),
|
||||
if_valueisnotnullorundefined(this, Label::kDeferred),
|
||||
if_valueisnotnull(this, Label::kDeferred);
|
||||
Branch(WordEqual(value, NullConstant()), &if_valueisnullorundefined,
|
||||
&if_valueisnotnull);
|
||||
Bind(&if_valueisnotnull);
|
||||
{
|
||||
// Check if the {value} is undefined.
|
||||
Branch(WordEqual(value, UndefinedConstant()),
|
||||
&if_valueisnullorundefined, &if_valueisnotnullorundefined);
|
||||
Bind(&if_valueisnotnullorundefined);
|
||||
{
|
||||
// Convert the {value} to a String.
|
||||
Callable callable = CodeFactory::ToString(isolate());
|
||||
var_value.Bind(CallStub(callable, context, value));
|
||||
Goto(&if_valueisstring);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_valueisnullorundefined);
|
||||
{
|
||||
// The {value} is either null or undefined.
|
||||
CallRuntime(Runtime::kThrowCalledOnNullOrUndefined, context,
|
||||
HeapConstant(factory()->NewStringFromAsciiChecked(
|
||||
"String.prototype.charCodeAt", TENURED)));
|
||||
Goto(&if_valueisstring); // Never reached.
|
||||
}
|
||||
}
|
||||
}
|
||||
Bind(&if_valueissmi);
|
||||
{
|
||||
// The {value} is a Smi, convert it to a String.
|
||||
Callable callable = CodeFactory::NumberToString(isolate());
|
||||
var_value.Bind(CallStub(callable, context, value));
|
||||
Goto(&if_valueisstring);
|
||||
}
|
||||
Bind(&if_valueisstring);
|
||||
return var_value.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::StringCharCodeAt(Node* string, Node* index) {
|
||||
// Translate the {index} into a Word.
|
||||
index = SmiToWord(index);
|
||||
|
||||
// We may need to loop in case of cons or sliced strings.
|
||||
Variable var_index(this, MachineType::PointerRepresentation());
|
||||
Variable var_result(this, MachineRepresentation::kWord32);
|
||||
Variable var_string(this, MachineRepresentation::kTagged);
|
||||
Variable* loop_vars[] = {&var_index, &var_string};
|
||||
Label done_loop(this, &var_result), loop(this, 2, loop_vars);
|
||||
var_string.Bind(string);
|
||||
var_index.Bind(index);
|
||||
Goto(&loop);
|
||||
Bind(&loop);
|
||||
{
|
||||
// Load the current {index}.
|
||||
index = var_index.value();
|
||||
|
||||
// Load the current {string}.
|
||||
string = var_string.value();
|
||||
|
||||
// Load the instance type of the {string}.
|
||||
Node* string_instance_type = LoadInstanceType(string);
|
||||
|
||||
// Check if the {string} is a SeqString.
|
||||
Label if_stringissequential(this), if_stringisnotsequential(this);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kStringRepresentationMask)),
|
||||
Int32Constant(kSeqStringTag)),
|
||||
&if_stringissequential, &if_stringisnotsequential);
|
||||
|
||||
Bind(&if_stringissequential);
|
||||
{
|
||||
// Check if the {string} is a TwoByteSeqString or a OneByteSeqString.
|
||||
Label if_stringistwobyte(this), if_stringisonebyte(this);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(kTwoByteStringTag)),
|
||||
&if_stringistwobyte, &if_stringisonebyte);
|
||||
|
||||
Bind(&if_stringisonebyte);
|
||||
{
|
||||
var_result.Bind(
|
||||
Load(MachineType::Uint8(), string,
|
||||
IntPtrAdd(index, IntPtrConstant(SeqOneByteString::kHeaderSize -
|
||||
kHeapObjectTag))));
|
||||
Goto(&done_loop);
|
||||
}
|
||||
|
||||
Bind(&if_stringistwobyte);
|
||||
{
|
||||
var_result.Bind(
|
||||
Load(MachineType::Uint16(), string,
|
||||
IntPtrAdd(WordShl(index, IntPtrConstant(1)),
|
||||
IntPtrConstant(SeqTwoByteString::kHeaderSize -
|
||||
kHeapObjectTag))));
|
||||
Goto(&done_loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_stringisnotsequential);
|
||||
{
|
||||
// Check if the {string} is a ConsString.
|
||||
Label if_stringiscons(this), if_stringisnotcons(this);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kStringRepresentationMask)),
|
||||
Int32Constant(kConsStringTag)),
|
||||
&if_stringiscons, &if_stringisnotcons);
|
||||
|
||||
Bind(&if_stringiscons);
|
||||
{
|
||||
// Check whether the right hand side is the empty string (i.e. if
|
||||
// this is really a flat string in a cons string). If that is not
|
||||
// the case we flatten the string first.
|
||||
Label if_rhsisempty(this), if_rhsisnotempty(this, Label::kDeferred);
|
||||
Node* rhs = LoadObjectField(string, ConsString::kSecondOffset);
|
||||
Branch(WordEqual(rhs, EmptyStringConstant()), &if_rhsisempty,
|
||||
&if_rhsisnotempty);
|
||||
|
||||
Bind(&if_rhsisempty);
|
||||
{
|
||||
// Just operate on the left hand side of the {string}.
|
||||
var_string.Bind(LoadObjectField(string, ConsString::kFirstOffset));
|
||||
Goto(&loop);
|
||||
}
|
||||
|
||||
Bind(&if_rhsisnotempty);
|
||||
{
|
||||
// Flatten the {string} and lookup in the resulting string.
|
||||
var_string.Bind(CallRuntime(Runtime::kFlattenString,
|
||||
NoContextConstant(), string));
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_stringisnotcons);
|
||||
{
|
||||
// Check if the {string} is an ExternalString.
|
||||
Label if_stringisexternal(this), if_stringisnotexternal(this);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kStringRepresentationMask)),
|
||||
Int32Constant(kExternalStringTag)),
|
||||
&if_stringisexternal, &if_stringisnotexternal);
|
||||
|
||||
Bind(&if_stringisexternal);
|
||||
{
|
||||
// Check if the {string} is a short external string.
|
||||
Label if_stringisshort(this),
|
||||
if_stringisnotshort(this, Label::kDeferred);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kShortExternalStringMask)),
|
||||
Int32Constant(0)),
|
||||
&if_stringisshort, &if_stringisnotshort);
|
||||
|
||||
Bind(&if_stringisshort);
|
||||
{
|
||||
// Load the actual resource data from the {string}.
|
||||
Node* string_resource_data =
|
||||
LoadObjectField(string, ExternalString::kResourceDataOffset,
|
||||
MachineType::Pointer());
|
||||
|
||||
// Check if the {string} is a TwoByteExternalString or a
|
||||
// OneByteExternalString.
|
||||
Label if_stringistwobyte(this), if_stringisonebyte(this);
|
||||
Branch(Word32Equal(Word32And(string_instance_type,
|
||||
Int32Constant(kStringEncodingMask)),
|
||||
Int32Constant(kTwoByteStringTag)),
|
||||
&if_stringistwobyte, &if_stringisonebyte);
|
||||
|
||||
Bind(&if_stringisonebyte);
|
||||
{
|
||||
var_result.Bind(
|
||||
Load(MachineType::Uint8(), string_resource_data, index));
|
||||
Goto(&done_loop);
|
||||
}
|
||||
|
||||
Bind(&if_stringistwobyte);
|
||||
{
|
||||
var_result.Bind(Load(MachineType::Uint16(), string_resource_data,
|
||||
WordShl(index, IntPtrConstant(1))));
|
||||
Goto(&done_loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_stringisnotshort);
|
||||
{
|
||||
// The {string} might be compressed, call the runtime.
|
||||
var_result.Bind(SmiToWord32(
|
||||
CallRuntime(Runtime::kExternalStringGetChar,
|
||||
NoContextConstant(), string, SmiTag(index))));
|
||||
Goto(&done_loop);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_stringisnotexternal);
|
||||
{
|
||||
// The {string} is a SlicedString, continue with its parent.
|
||||
Node* string_offset =
|
||||
SmiToWord(LoadObjectField(string, SlicedString::kOffsetOffset));
|
||||
Node* string_parent =
|
||||
LoadObjectField(string, SlicedString::kParentOffset);
|
||||
var_index.Bind(IntPtrAdd(index, string_offset));
|
||||
var_string.Bind(string_parent);
|
||||
Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&done_loop);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
Node* CodeStubAssembler::StringFromCharCode(Node* code) {
|
||||
Variable var_result(this, MachineRepresentation::kTagged);
|
||||
|
||||
// Check if the {code} is a one-byte char code.
|
||||
Label if_codeisonebyte(this), if_codeistwobyte(this, Label::kDeferred),
|
||||
if_done(this);
|
||||
Branch(Int32LessThanOrEqual(code, Int32Constant(String::kMaxOneByteCharCode)),
|
||||
&if_codeisonebyte, &if_codeistwobyte);
|
||||
Bind(&if_codeisonebyte);
|
||||
{
|
||||
// Load the isolate wide single character string cache.
|
||||
Node* cache = LoadRoot(Heap::kSingleCharacterStringCacheRootIndex);
|
||||
|
||||
// Check if we have an entry for the {code} in the single character string
|
||||
// cache already.
|
||||
Label if_entryisundefined(this, Label::kDeferred),
|
||||
if_entryisnotundefined(this);
|
||||
Node* entry = LoadFixedArrayElementInt32Index(cache, code);
|
||||
Branch(WordEqual(entry, UndefinedConstant()), &if_entryisundefined,
|
||||
&if_entryisnotundefined);
|
||||
|
||||
Bind(&if_entryisundefined);
|
||||
{
|
||||
// Allocate a new SeqOneByteString for {code} and store it in the {cache}.
|
||||
Node* result = AllocateSeqOneByteString(1);
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord8, result,
|
||||
IntPtrConstant(SeqOneByteString::kHeaderSize - kHeapObjectTag), code);
|
||||
StoreFixedArrayElementInt32Index(cache, code, result);
|
||||
var_result.Bind(result);
|
||||
Goto(&if_done);
|
||||
}
|
||||
|
||||
Bind(&if_entryisnotundefined);
|
||||
{
|
||||
// Return the entry from the {cache}.
|
||||
var_result.Bind(entry);
|
||||
Goto(&if_done);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&if_codeistwobyte);
|
||||
{
|
||||
// Allocate a new SeqTwoByteString for {code}.
|
||||
Node* result = AllocateSeqTwoByteString(1);
|
||||
StoreNoWriteBarrier(
|
||||
MachineRepresentation::kWord16, result,
|
||||
IntPtrConstant(SeqTwoByteString::kHeaderSize - kHeapObjectTag), code);
|
||||
var_result.Bind(result);
|
||||
Goto(&if_done);
|
||||
}
|
||||
|
||||
Bind(&if_done);
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
void CodeStubAssembler::BranchIf(Node* condition, Label* if_true,
|
||||
Label* if_false) {
|
||||
Label if_condition_is_true(this), if_condition_is_false(this);
|
||||
|
@ -162,7 +162,10 @@ class CodeStubAssembler {
|
||||
Node* ExternalConstant(ExternalReference address);
|
||||
Node* Float64Constant(double value);
|
||||
Node* BooleanMapConstant();
|
||||
Node* EmptyStringConstant();
|
||||
Node* HeapNumberMapConstant();
|
||||
Node* NaNConstant();
|
||||
Node* NoContextConstant();
|
||||
Node* NullConstant();
|
||||
Node* UndefinedConstant();
|
||||
|
||||
@ -278,9 +281,11 @@ class CodeStubAssembler {
|
||||
Node* SmiTag(Node* value);
|
||||
// Untag a Smi value as a Word.
|
||||
Node* SmiUntag(Node* value);
|
||||
Node* SmiToWord(Node* value) { return SmiUntag(value); }
|
||||
|
||||
// Smi conversions.
|
||||
Node* SmiToFloat64(Node* value);
|
||||
Node* SmiFromWord32(Node* value);
|
||||
Node* SmiToWord32(Node* value);
|
||||
|
||||
// Smi operations.
|
||||
@ -289,6 +294,7 @@ class CodeStubAssembler {
|
||||
Node* SmiSub(Node* a, Node* b);
|
||||
Node* SmiSubWithOverflow(Node* a, Node* b);
|
||||
Node* SmiEqual(Node* a, Node* b);
|
||||
Node* SmiAboveOrEqual(Node* a, Node* b);
|
||||
Node* SmiLessThan(Node* a, Node* b);
|
||||
Node* SmiLessThanOrEqual(Node* a, Node* b);
|
||||
Node* SmiMin(Node* a, Node* b);
|
||||
@ -343,13 +349,19 @@ class CodeStubAssembler {
|
||||
|
||||
// Allocate an object of the given size.
|
||||
Node* Allocate(int size, AllocationFlags flags = kNone);
|
||||
Node* InnerAllocate(Node* previous, int offset);
|
||||
// Allocate a HeapNumber without initializing its value.
|
||||
Node* AllocateHeapNumber();
|
||||
// Allocate a HeapNumber with a specific value.
|
||||
Node* AllocateHeapNumberWithValue(Node* value);
|
||||
Node* InnerAllocate(Node* previous, int offset);
|
||||
// Allocate a SeqOneByteString with the given length.
|
||||
Node* AllocateSeqOneByteString(int length);
|
||||
// Allocate a SeqTwoByteString with the given length.
|
||||
Node* AllocateSeqTwoByteString(int length);
|
||||
|
||||
// Store an array element to a FixedArray.
|
||||
Node* StoreFixedArrayElementInt32Index(Node* object, Node* index,
|
||||
Node* value);
|
||||
Node* StoreFixedArrayElementNoWriteBarrier(Node* object, Node* index,
|
||||
Node* value);
|
||||
// Load the Map of an HeapObject.
|
||||
@ -378,6 +390,17 @@ class CodeStubAssembler {
|
||||
Node* TruncateTaggedToFloat64(Node* context, Node* value);
|
||||
Node* TruncateTaggedToWord32(Node* context, Node* value);
|
||||
|
||||
// Type conversions.
|
||||
// Throws a TypeError for {method_name} if {value} is not coercible to Object,
|
||||
// or returns the {value} converted to a String otherwise.
|
||||
Node* ToThisString(Node* context, Node* value, char const* method_name);
|
||||
|
||||
// String helpers.
|
||||
// Load a character from a String (might flatten a ConsString).
|
||||
Node* StringCharCodeAt(Node* string, Node* smi_index);
|
||||
// Return the single character string with only {code}.
|
||||
Node* StringFromCharCode(Node* code);
|
||||
|
||||
// Branching helpers.
|
||||
// TODO(danno): Can we be more cleverish wrt. edge-split?
|
||||
void BranchIf(Node* condition, Label* if_true, Label* if_false);
|
||||
|
@ -37,7 +37,6 @@ var ObjectHasOwnProperty;
|
||||
var ObjectToString = utils.ImportNow("object_to_string");
|
||||
var Script = utils.ImportNow("Script");
|
||||
var stackTraceSymbol = utils.ImportNow("stack_trace_symbol");
|
||||
var StringCharAt;
|
||||
var StringIndexOf;
|
||||
var StringSubstring;
|
||||
var SymbolToString;
|
||||
@ -57,7 +56,6 @@ utils.Import(function(from) {
|
||||
Int8x16ToString = from.Int8x16ToString;
|
||||
ObjectDefineProperty = from.ObjectDefineProperty;
|
||||
ObjectHasOwnProperty = from.ObjectHasOwnProperty;
|
||||
StringCharAt = from.StringCharAt;
|
||||
StringIndexOf = from.StringIndexOf;
|
||||
StringSubstring = from.StringSubstring;
|
||||
SymbolToString = from.SymbolToString;
|
||||
@ -255,6 +253,7 @@ function ScriptLineFromPosition(position) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get information on a specific source position.
|
||||
* @param {number} position The source position
|
||||
@ -272,7 +271,7 @@ function ScriptLocationFromPosition(position,
|
||||
var line_ends = this.line_ends;
|
||||
var start = line == 0 ? 0 : line_ends[line - 1] + 1;
|
||||
var end = line_ends[line];
|
||||
if (end > 0 && %_Call(StringCharAt, this.source, end - 1) == '\r') {
|
||||
if (end > 0 && %_StringCharAt(this.source, end - 1) === '\r') {
|
||||
end--;
|
||||
}
|
||||
var column = position - start;
|
||||
|
@ -57,30 +57,6 @@ function StringValueOf() {
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, section 15.5.4.4
|
||||
function StringCharAtJS(pos) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.charAt");
|
||||
|
||||
var result = %_StringCharAt(this, pos);
|
||||
if (%_IsSmi(result)) {
|
||||
result = %_StringCharAt(TO_STRING(this), TO_INTEGER(pos));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262 section 15.5.4.5
|
||||
function StringCharCodeAtJS(pos) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "String.prototype.charCodeAt");
|
||||
|
||||
var result = %_StringCharCodeAt(this, pos);
|
||||
if (!%_IsSmi(result)) {
|
||||
result = %_StringCharCodeAt(TO_STRING(this), TO_INTEGER(pos));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// ECMA-262, section 15.5.4.6
|
||||
function StringConcat(other /* and more */) { // length == 1
|
||||
"use strict";
|
||||
@ -857,13 +833,6 @@ function StringRaw(callSite) {
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
// Set the String function and constructor.
|
||||
%FunctionSetPrototype(GlobalString, new GlobalString());
|
||||
|
||||
// Set up the constructor property on the String prototype object.
|
||||
%AddNamedProperty(
|
||||
GlobalString.prototype, "constructor", GlobalString, DONT_ENUM);
|
||||
|
||||
// Set up the non-enumerable functions on the String object.
|
||||
utils.InstallFunctions(GlobalString, DONT_ENUM, [
|
||||
"fromCharCode", StringFromCharCode,
|
||||
@ -875,8 +844,6 @@ utils.InstallFunctions(GlobalString, DONT_ENUM, [
|
||||
utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
|
||||
"valueOf", StringValueOf,
|
||||
"toString", StringToString,
|
||||
"charAt", StringCharAtJS,
|
||||
"charCodeAt", StringCharCodeAtJS,
|
||||
"codePointAt", StringCodePointAt,
|
||||
"concat", StringConcat,
|
||||
"endsWith", StringEndsWith,
|
||||
@ -922,7 +889,6 @@ utils.InstallFunctions(GlobalString.prototype, DONT_ENUM, [
|
||||
|
||||
utils.Export(function(to) {
|
||||
to.ExpandReplacement = ExpandReplacement;
|
||||
to.StringCharAt = StringCharAtJS;
|
||||
to.StringIndexOf = StringIndexOf;
|
||||
to.StringLastIndexOf = StringLastIndexOf;
|
||||
to.StringMatch = StringMatchJS;
|
||||
|
@ -431,6 +431,13 @@ RUNTIME_FUNCTION(Runtime_ThrowCalledNonCallable) {
|
||||
isolate, NewTypeError(MessageTemplate::kCalledNonCallable, callsite));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ThrowCalledOnNullOrUndefined) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK_EQ(1, args.length());
|
||||
CONVERT_ARG_HANDLE_CHECKED(String, name, 0);
|
||||
THROW_NEW_ERROR_RETURN_FAILURE(
|
||||
isolate, NewTypeError(MessageTemplate::kCalledOnNullOrUndefined, name));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ThrowConstructedNonConstructable) {
|
||||
HandleScope scope(isolate);
|
||||
|
@ -1273,6 +1273,13 @@ RUNTIME_FUNCTION(Runtime_StringCharAt) {
|
||||
return __RT_impl_Runtime_StringCharFromCode(Arguments(1, &code), isolate);
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ExternalStringGetChar) {
|
||||
SealHandleScope shs(isolate);
|
||||
DCHECK_EQ(2, args.length());
|
||||
CONVERT_ARG_CHECKED(ExternalString, string, 0);
|
||||
CONVERT_INT32_ARG_CHECKED(index, 1);
|
||||
return Smi::FromInt(string->Get(index));
|
||||
}
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_OneByteSeqStringGetChar) {
|
||||
SealHandleScope shs(isolate);
|
||||
|
@ -312,6 +312,7 @@ namespace internal {
|
||||
F(ThrowConstructedNonConstructable, 1, 1) \
|
||||
F(ThrowDerivedConstructorReturnedNonObject, 0, 1) \
|
||||
F(ThrowCalledNonCallable, 1, 1) \
|
||||
F(ThrowCalledOnNullOrUndefined, 1, 1) \
|
||||
F(CreateListFromArrayLike, 1, 1) \
|
||||
F(IncrementUseCounter, 1, 1) \
|
||||
F(GetOrdinaryHasInstance, 0, 1) \
|
||||
@ -858,6 +859,7 @@ namespace internal {
|
||||
F(FlattenString, 1, 1) \
|
||||
F(StringCharFromCode, 1, 1) \
|
||||
F(StringCharAt, 2, 1) \
|
||||
F(ExternalStringGetChar, 2, 1) \
|
||||
F(OneByteSeqStringGetChar, 2, 1) \
|
||||
F(OneByteSeqStringSetChar, 3, 1) \
|
||||
F(TwoByteSeqStringGetChar, 2, 1) \
|
||||
|
Loading…
Reference in New Issue
Block a user