Commit 43d3331d authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent 65eb0f6b
......@@ -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);
}
{
......
......@@ -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);
......
This diff is collapsed.
......@@ -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) \
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment