| Index: src/code-stubs-hydrogen.cc
|
| diff --git a/src/code-stubs-hydrogen.cc b/src/code-stubs-hydrogen.cc
|
| index 59d3a8b7d1e16742bfd31737d18259bc77fdc9af..9f782225c1614648c95537e68bc08eb4ec7f0598 100644
|
| --- a/src/code-stubs-hydrogen.cc
|
| +++ b/src/code-stubs-hydrogen.cc
|
| @@ -113,7 +113,8 @@ class CodeStubGraphBuilderBase : public HGraphBuilder {
|
| HValue* shared_info,
|
| HValue* native_context);
|
|
|
| - HValue* CheckString(HValue* input, bool convert);
|
| + HValue* BuildToString(HValue* input, bool convert);
|
| + HValue* BuildToPrimitive(HValue* input, HValue* input_map);
|
|
|
| private:
|
| HValue* BuildArraySingleArgumentConstructor(JSArrayBuilder* builder);
|
| @@ -1458,7 +1459,7 @@ Handle<Code> BinaryOpWithAllocationSiteStub::GenerateCode() {
|
| }
|
|
|
|
|
| -HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) {
|
| +HValue* CodeStubGraphBuilderBase::BuildToString(HValue* input, bool convert) {
|
| if (!convert) return BuildCheckString(input);
|
| IfBuilder if_inputissmi(this);
|
| HValue* inputissmi = if_inputissmi.If<HIsSmiAndBranch>(input);
|
| @@ -1496,11 +1497,8 @@ HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) {
|
| }
|
| if_inputisprimitive.Else();
|
| {
|
| - // TODO(bmeurer): Add support for fast ToPrimitive conversion using
|
| - // a dedicated ToPrimitiveStub.
|
| - Add<HPushArguments>(input);
|
| - Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kToPrimitive),
|
| - 1));
|
| + // Convert the input to a primitive.
|
| + Push(BuildToPrimitive(input, input_map));
|
| }
|
| if_inputisprimitive.End();
|
| // Convert the primitive to a string value.
|
| @@ -1518,6 +1516,83 @@ HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) {
|
| }
|
|
|
|
|
| +HValue* CodeStubGraphBuilderBase::BuildToPrimitive(HValue* input,
|
| + HValue* input_map) {
|
| + // Get the native context of the caller.
|
| + HValue* native_context = BuildGetNativeContext();
|
| +
|
| + // Determine the initial map of the %ObjectPrototype%.
|
| + HValue* object_function_prototype_map =
|
| + Add<HLoadNamedField>(native_context, nullptr,
|
| + HObjectAccess::ForContextSlot(
|
| + Context::OBJECT_FUNCTION_PROTOTYPE_MAP_INDEX));
|
| +
|
| + // Determine the initial map of the %StringPrototype%.
|
| + HValue* string_function_prototype_map =
|
| + Add<HLoadNamedField>(native_context, nullptr,
|
| + HObjectAccess::ForContextSlot(
|
| + Context::STRING_FUNCTION_PROTOTYPE_MAP_INDEX));
|
| +
|
| + // Determine the initial map of the String function.
|
| + HValue* string_function = Add<HLoadNamedField>(
|
| + native_context, nullptr,
|
| + HObjectAccess::ForContextSlot(Context::STRING_FUNCTION_INDEX));
|
| + HValue* string_function_initial_map = Add<HLoadNamedField>(
|
| + string_function, nullptr, HObjectAccess::ForPrototypeOrInitialMap());
|
| +
|
| + // Determine the map of the [[Prototype]] of {input}.
|
| + HValue* input_prototype =
|
| + Add<HLoadNamedField>(input_map, nullptr, HObjectAccess::ForPrototype());
|
| + HValue* input_prototype_map =
|
| + Add<HLoadNamedField>(input_prototype, nullptr, HObjectAccess::ForMap());
|
| +
|
| + // For string wrappers (JSValue instances with [[StringData]] internal
|
| + // fields), we can shortcirciut the ToPrimitive if
|
| + //
|
| + // (a) the {input} map matches the initial map of the String function,
|
| + // (b) the {input} [[Prototype]] is the unmodified %StringPrototype% (i.e.
|
| + // no one monkey-patched toString, @@toPrimitive or valueOf), and
|
| + // (c) the %ObjectPrototype% (i.e. the [[Prototype]] of the
|
| + // %StringPrototype%) is also unmodified, that is no one sneaked a
|
| + // @@toPrimitive into the %ObjectPrototype%.
|
| + //
|
| + // If all these assumptions hold, we can just take the [[StringData]] value
|
| + // and return it.
|
| + // TODO(bmeurer): This just repairs a regression introduced by removing the
|
| + // weird (and broken) intrinsic %_IsStringWrapperSafeForDefaultValue, which
|
| + // was intendend to something similar to this, although less efficient and
|
| + // wrong in the presence of @@toPrimitive. Long-term we might want to move
|
| + // into the direction of having a ToPrimitiveStub that can do common cases
|
| + // while staying in JavaScript land (i.e. not going to C++).
|
| + IfBuilder if_inputisstringwrapper(this);
|
| + if_inputisstringwrapper.If<HCompareObjectEqAndBranch>(
|
| + input_map, string_function_initial_map);
|
| + if_inputisstringwrapper.And();
|
| + if_inputisstringwrapper.If<HCompareObjectEqAndBranch>(
|
| + input_prototype_map, string_function_prototype_map);
|
| + if_inputisstringwrapper.And();
|
| + if_inputisstringwrapper.If<HCompareObjectEqAndBranch>(
|
| + Add<HLoadNamedField>(Add<HLoadNamedField>(input_prototype_map, nullptr,
|
| + HObjectAccess::ForPrototype()),
|
| + nullptr, HObjectAccess::ForMap()),
|
| + object_function_prototype_map);
|
| + if_inputisstringwrapper.Then();
|
| + {
|
| + Push(BuildLoadNamedField(
|
| + input, FieldIndex::ForInObjectOffset(JSValue::kValueOffset)));
|
| + }
|
| + if_inputisstringwrapper.Else();
|
| + {
|
| + // TODO(bmeurer): Add support for fast ToPrimitive conversion using
|
| + // a dedicated ToPrimitiveStub.
|
| + Add<HPushArguments>(input);
|
| + Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kToPrimitive), 1));
|
| + }
|
| + if_inputisstringwrapper.End();
|
| + return Pop();
|
| +}
|
| +
|
| +
|
| template <>
|
| HValue* CodeStubGraphBuilder<StringAddStub>::BuildCodeInitializedStub() {
|
| StringAddStub* stub = casted_stub();
|
| @@ -1530,11 +1605,11 @@ HValue* CodeStubGraphBuilder<StringAddStub>::BuildCodeInitializedStub() {
|
| // Make sure that both arguments are strings if not known in advance.
|
| if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
|
| left =
|
| - CheckString(left, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
|
| + BuildToString(left, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
|
| }
|
| if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
|
| - right =
|
| - CheckString(right, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
|
| + right = BuildToString(right,
|
| + (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
|
| }
|
|
|
| return BuildStringAdd(left, right, HAllocationMode(pretenure_flag));
|
|
|