| Index: src/hydrogen.cc
|
| diff --git a/src/hydrogen.cc b/src/hydrogen.cc
|
| index a27360a3fd868b428ccbe4d7388f083cb914a18a..2b3812b669922f3b2af4c65beaada4036b5a33f3 100644
|
| --- a/src/hydrogen.cc
|
| +++ b/src/hydrogen.cc
|
| @@ -543,7 +543,7 @@ HConstant* HGraph::GetConstantHole() {
|
| HGraphBuilder::HGraphBuilder(CompilationInfo* info,
|
| TypeFeedbackOracle* oracle)
|
| : function_state_(NULL),
|
| - initial_function_state_(this, info, oracle, false),
|
| + initial_function_state_(this, info, oracle, NORMAL_RETURN),
|
| ast_context_(NULL),
|
| break_scope_(NULL),
|
| graph_(NULL),
|
| @@ -2026,12 +2026,12 @@ void HGraph::ComputeMinusZeroChecks() {
|
| FunctionState::FunctionState(HGraphBuilder* owner,
|
| CompilationInfo* info,
|
| TypeFeedbackOracle* oracle,
|
| - bool drop_extra)
|
| + ReturnHandlingFlag return_handling)
|
| : owner_(owner),
|
| compilation_info_(info),
|
| oracle_(oracle),
|
| call_context_(NULL),
|
| - drop_extra_(drop_extra),
|
| + return_handling_(return_handling),
|
| function_return_(NULL),
|
| test_context_(NULL),
|
| outer_(owner->function_state()) {
|
| @@ -2074,7 +2074,7 @@ AstContext::AstContext(HGraphBuilder* owner, Expression::Context kind)
|
| for_typeof_(false) {
|
| owner->set_ast_context(this); // Push.
|
| #ifdef DEBUG
|
| - ASSERT(!owner->environment()->is_arguments_adaptor());
|
| + ASSERT(owner->environment()->frame_type() == JS_FUNCTION);
|
| original_length_ = owner->environment()->length();
|
| #endif
|
| }
|
| @@ -2089,7 +2089,7 @@ EffectContext::~EffectContext() {
|
| ASSERT(owner()->HasStackOverflow() ||
|
| owner()->current_block() == NULL ||
|
| (owner()->environment()->length() == original_length_ &&
|
| - !owner()->environment()->is_arguments_adaptor()));
|
| + owner()->environment()->frame_type() == JS_FUNCTION));
|
| }
|
|
|
|
|
| @@ -2097,7 +2097,7 @@ ValueContext::~ValueContext() {
|
| ASSERT(owner()->HasStackOverflow() ||
|
| owner()->current_block() == NULL ||
|
| (owner()->environment()->length() == original_length_ + 1 &&
|
| - !owner()->environment()->is_arguments_adaptor()));
|
| + owner()->environment()->frame_type() == JS_FUNCTION));
|
| }
|
|
|
|
|
| @@ -2459,8 +2459,8 @@ void HGraphBuilder::PushAndAdd(HInstruction* instr) {
|
| }
|
|
|
|
|
| -template <int V>
|
| -HInstruction* HGraphBuilder::PreProcessCall(HCall<V>* call) {
|
| +template <class Instruction>
|
| +HInstruction* HGraphBuilder::PreProcessCall(Instruction* call) {
|
| int count = call->argument_count();
|
| ZoneList<HValue*> arguments(count);
|
| for (int i = 0; i < count; ++i) {
|
| @@ -2672,7 +2672,38 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
| CHECK_ALIVE(VisitForValue(stmt->expression()));
|
| HValue* result = environment()->Pop();
|
| current_block()->FinishExit(new(zone()) HReturn(result));
|
| - set_current_block(NULL);
|
| + } else if (function_state()->is_construct()) {
|
| + // Return from an inlined construct call. In a test context the return
|
| + // value will always evaluate to true, in a value context the return value
|
| + // needs to be a JSObject.
|
| + if (context->IsTest()) {
|
| + TestContext* test = TestContext::cast(context);
|
| + CHECK_ALIVE(VisitForEffect(stmt->expression()));
|
| + current_block()->Goto(test->if_true(), function_state()->drop_extra());
|
| + } else if (context->IsEffect()) {
|
| + CHECK_ALIVE(VisitForEffect(stmt->expression()));
|
| + current_block()->Goto(function_return(), function_state()->drop_extra());
|
| + } else {
|
| + ASSERT(context->IsValue());
|
| + CHECK_ALIVE(VisitForValue(stmt->expression()));
|
| + HValue* return_value = Pop();
|
| + HValue* receiver = environment()->Lookup(0);
|
| + HHasInstanceTypeAndBranch* typecheck =
|
| + new(zone()) HHasInstanceTypeAndBranch(return_value,
|
| + FIRST_SPEC_OBJECT_TYPE,
|
| + LAST_SPEC_OBJECT_TYPE);
|
| + HBasicBlock* if_spec_object = graph()->CreateBasicBlock();
|
| + HBasicBlock* not_spec_object = graph()->CreateBasicBlock();
|
| + typecheck->SetSuccessorAt(0, if_spec_object);
|
| + typecheck->SetSuccessorAt(1, not_spec_object);
|
| + current_block()->Finish(typecheck);
|
| + if_spec_object->AddLeaveInlined(return_value,
|
| + function_return(),
|
| + function_state()->drop_extra());
|
| + not_spec_object->AddLeaveInlined(receiver,
|
| + function_return(),
|
| + function_state()->drop_extra());
|
| + }
|
| } else {
|
| // Return from an inlined function, visit the subexpression in the
|
| // expression context of the call.
|
| @@ -2687,13 +2718,13 @@ void HGraphBuilder::VisitReturnStatement(ReturnStatement* stmt) {
|
| } else {
|
| ASSERT(context->IsValue());
|
| CHECK_ALIVE(VisitForValue(stmt->expression()));
|
| - HValue* return_value = environment()->Pop();
|
| + HValue* return_value = Pop();
|
| current_block()->AddLeaveInlined(return_value,
|
| function_return(),
|
| function_state()->drop_extra());
|
| }
|
| - set_current_block(NULL);
|
| }
|
| + set_current_block(NULL);
|
| }
|
|
|
|
|
| @@ -4711,7 +4742,7 @@ void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr,
|
| PrintF("Trying to inline the polymorphic call to %s\n",
|
| *name->ToCString());
|
| }
|
| - if (FLAG_polymorphic_inlining && TryInline(expr)) {
|
| + if (FLAG_polymorphic_inlining && TryInlineCall(expr)) {
|
| // Trying to inline will signal that we should bailout from the
|
| // entire compilation by setting stack overflow on the visitor.
|
| if (HasStackOverflow()) return;
|
| @@ -4781,19 +4812,18 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target,
|
| }
|
|
|
|
|
| -bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| +bool HGraphBuilder::TryInline(CallKind call_kind,
|
| + Handle<JSFunction> target,
|
| + ZoneList<Expression*>* arguments,
|
| + HValue* receiver,
|
| + int ast_id,
|
| + int return_id,
|
| + ReturnHandlingFlag return_handling) {
|
| if (!FLAG_use_inlining) return false;
|
|
|
| - // The function call we are inlining is a method call if the call
|
| - // is a property call.
|
| - CallKind call_kind = (expr->expression()->AsProperty() == NULL)
|
| - ? CALL_AS_FUNCTION
|
| - : CALL_AS_METHOD;
|
| -
|
| // Precondition: call is monomorphic and we have found a target with the
|
| // appropriate arity.
|
| Handle<JSFunction> caller = info()->closure();
|
| - Handle<JSFunction> target = expr->target();
|
| Handle<SharedFunctionInfo> target_shared(target->shared());
|
|
|
| // Do a quick check on source code length to avoid parsing large
|
| @@ -4830,7 +4860,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| TraceInline(target, caller, "inline depth limit reached");
|
| return false;
|
| }
|
| - if (!env->outer()->is_arguments_adaptor()) {
|
| + if (env->outer()->frame_type() == JS_FUNCTION) {
|
| current_level++;
|
| }
|
| env = env->outer();
|
| @@ -4941,16 +4971,17 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| isolate());
|
| // The function state is new-allocated because we need to delete it
|
| // in two different places.
|
| - FunctionState* target_state =
|
| - new FunctionState(this, &target_info, &target_oracle, drop_extra);
|
| + FunctionState* target_state = new FunctionState(
|
| + this, &target_info, &target_oracle, return_handling);
|
|
|
| HConstant* undefined = graph()->GetConstantUndefined();
|
| HEnvironment* inner_env =
|
| environment()->CopyForInlining(target,
|
| - expr->arguments()->length(),
|
| + arguments->length(),
|
| function,
|
| undefined,
|
| - call_kind);
|
| + call_kind,
|
| + function_state()->is_construct());
|
| #ifdef V8_TARGET_ARCH_IA32
|
| // IA32 only, overwrite the caller's context in the deoptimization
|
| // environment with the correct one.
|
| @@ -4964,12 +4995,13 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| #endif
|
| HBasicBlock* body_entry = CreateBasicBlock(inner_env);
|
| current_block()->Goto(body_entry);
|
| - body_entry->SetJoinId(expr->ReturnId());
|
| + body_entry->SetJoinId(return_id);
|
| set_current_block(body_entry);
|
| AddInstruction(new(zone()) HEnterInlined(target,
|
| - expr->arguments()->length(),
|
| + arguments->length(),
|
| function,
|
| - call_kind));
|
| + call_kind,
|
| + function_state()->is_construct()));
|
| VisitDeclarations(target_info.scope()->declarations());
|
| VisitStatements(function->body());
|
| if (HasStackOverflow()) {
|
| @@ -4988,32 +5020,27 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| TraceInline(target, caller, NULL);
|
|
|
| if (current_block() != NULL) {
|
| - // Add a return of undefined if control can fall off the body. In a
|
| - // test context, undefined is false.
|
| - if (inlined_test_context() == NULL) {
|
| + // Add default return value (i.e. undefined for normals calls or the newly
|
| + // allocated receiver for construct calls) if control can fall off the
|
| + // body. In a test context, undefined is false and any JSObject is true.
|
| + if (call_context()->IsValue()) {
|
| ASSERT(function_return() != NULL);
|
| - ASSERT(call_context()->IsEffect() || call_context()->IsValue());
|
| - if (call_context()->IsEffect()) {
|
| - current_block()->Goto(function_return(), drop_extra);
|
| - } else {
|
| - current_block()->AddLeaveInlined(undefined,
|
| - function_return(),
|
| - drop_extra);
|
| - }
|
| + HValue* return_value = function_state()->is_construct()
|
| + ? receiver
|
| + : undefined;
|
| + current_block()->AddLeaveInlined(return_value,
|
| + function_return(),
|
| + function_state()->drop_extra());
|
| + } else if (call_context()->IsEffect()) {
|
| + ASSERT(function_return() != NULL);
|
| + current_block()->Goto(function_return(), function_state()->drop_extra());
|
| } else {
|
| - // The graph builder assumes control can reach both branches of a
|
| - // test, so we materialize the undefined value and test it rather than
|
| - // simply jumping to the false target.
|
| - //
|
| - // TODO(3168478): refactor to avoid this.
|
| ASSERT(call_context()->IsTest());
|
| - HBasicBlock* empty_true = graph()->CreateBasicBlock();
|
| - HBasicBlock* empty_false = graph()->CreateBasicBlock();
|
| - HBranch* test = new(zone()) HBranch(undefined, empty_true, empty_false);
|
| - current_block()->Finish(test);
|
| -
|
| - empty_true->Goto(inlined_test_context()->if_true(), drop_extra);
|
| - empty_false->Goto(inlined_test_context()->if_false(), drop_extra);
|
| + ASSERT(inlined_test_context() != NULL);
|
| + HBasicBlock* target = function_state()->is_construct()
|
| + ? inlined_test_context()->if_true()
|
| + : inlined_test_context()->if_false();
|
| + current_block()->Goto(target, function_state()->drop_extra());
|
| }
|
| }
|
|
|
| @@ -5029,12 +5056,12 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
|
|
| // Forward to the real test context.
|
| if (if_true->HasPredecessor()) {
|
| - if_true->SetJoinId(expr->id());
|
| + if_true->SetJoinId(ast_id);
|
| HBasicBlock* true_target = TestContext::cast(ast_context())->if_true();
|
| if_true->Goto(true_target, function_state()->drop_extra());
|
| }
|
| if (if_false->HasPredecessor()) {
|
| - if_false->SetJoinId(expr->id());
|
| + if_false->SetJoinId(ast_id);
|
| HBasicBlock* false_target = TestContext::cast(ast_context())->if_false();
|
| if_false->Goto(false_target, function_state()->drop_extra());
|
| }
|
| @@ -5042,7 +5069,7 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| return true;
|
|
|
| } else if (function_return()->HasPredecessor()) {
|
| - function_return()->SetJoinId(expr->id());
|
| + function_return()->SetJoinId(ast_id);
|
| set_current_block(function_return());
|
| } else {
|
| set_current_block(NULL);
|
| @@ -5052,6 +5079,34 @@ bool HGraphBuilder::TryInline(Call* expr, bool drop_extra) {
|
| }
|
|
|
|
|
| +bool HGraphBuilder::TryInlineCall(Call* expr, bool drop_extra) {
|
| + // The function call we are inlining is a method call if the call
|
| + // is a property call.
|
| + CallKind call_kind = (expr->expression()->AsProperty() == NULL)
|
| + ? CALL_AS_FUNCTION
|
| + : CALL_AS_METHOD;
|
| +
|
| + return TryInline(call_kind,
|
| + expr->target(),
|
| + expr->arguments(),
|
| + NULL,
|
| + expr->id(),
|
| + expr->ReturnId(),
|
| + drop_extra ? DROP_EXTRA_ON_RETURN : NORMAL_RETURN);
|
| +}
|
| +
|
| +
|
| +bool HGraphBuilder::TryInlineConstruct(CallNew* expr, HValue* receiver) {
|
| + return TryInline(CALL_AS_FUNCTION,
|
| + expr->target(),
|
| + expr->arguments(),
|
| + receiver,
|
| + expr->id(),
|
| + expr->ReturnId(),
|
| + CONSTRUCT_CALL_RETURN);
|
| +}
|
| +
|
| +
|
| bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr,
|
| HValue* receiver,
|
| Handle<Map> receiver_map,
|
| @@ -5335,7 +5390,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
| } else {
|
| AddCheckConstantFunction(expr, receiver, receiver_map, true);
|
|
|
| - if (TryInline(expr)) return;
|
| + if (TryInlineCall(expr)) return;
|
| call = PreProcessCall(
|
| new(zone()) HCallConstantFunction(expr->target(),
|
| argument_count));
|
| @@ -5391,7 +5446,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
| IsGlobalObject());
|
| environment()->SetExpressionStackAt(receiver_index, global_receiver);
|
|
|
| - if (TryInline(expr)) return;
|
| + if (TryInlineCall(expr)) return;
|
| call = PreProcessCall(new(zone()) HCallKnownGlobal(expr->target(),
|
| argument_count));
|
| } else {
|
| @@ -5417,7 +5472,7 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
| PushAndAdd(receiver);
|
| CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| AddInstruction(new(zone()) HCheckFunction(function, expr->target()));
|
| - if (TryInline(expr, true)) { // Drop function from environment.
|
| + if (TryInlineCall(expr, true)) { // Drop function from environment.
|
| return;
|
| } else {
|
| call = PreProcessCall(new(zone()) HInvokeFunction(context,
|
| @@ -5447,25 +5502,64 @@ void HGraphBuilder::VisitCall(Call* expr) {
|
| }
|
|
|
|
|
| +// Checks whether allocation using the given constructor can be inlined.
|
| +static bool IsAllocationInlineable(Handle<JSFunction> constructor) {
|
| + return constructor->has_initial_map() &&
|
| + constructor->initial_map()->instance_type() == JS_OBJECT_TYPE;
|
| +}
|
| +
|
| +
|
| void HGraphBuilder::VisitCallNew(CallNew* expr) {
|
| ASSERT(!HasStackOverflow());
|
| ASSERT(current_block() != NULL);
|
| ASSERT(current_block()->HasPredecessor());
|
| - // The constructor function is also used as the receiver argument to the
|
| - // JS construct call builtin.
|
| - HValue* constructor = NULL;
|
| - CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
|
| - CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| -
|
| + expr->RecordTypeFeedback(oracle());
|
| + int argument_count = expr->arguments()->length() + 1; // Plus constructor.
|
| HValue* context = environment()->LookupContext();
|
|
|
| - // The constructor is both an operand to the instruction and an argument
|
| - // to the construct call.
|
| - int arg_count = expr->arguments()->length() + 1; // Plus constructor.
|
| - HCallNew* call = new(zone()) HCallNew(context, constructor, arg_count);
|
| - call->set_position(expr->position());
|
| - Drop(arg_count);
|
| - return ast_context()->ReturnInstruction(call, expr->id());
|
| + if (FLAG_inline_construct &&
|
| + expr->IsMonomorphic() &&
|
| + IsAllocationInlineable(expr->target())) {
|
| + // The constructor function is on the stack in the unoptimized code
|
| + // during evaluation of the arguments.
|
| + CHECK_ALIVE(VisitForValue(expr->expression()));
|
| + HValue* function = Top();
|
| + CHECK_ALIVE(VisitExpressions(expr->arguments()));
|
| + Handle<JSFunction> constructor = expr->target();
|
| + AddInstruction(new(zone()) HCheckFunction(function, constructor));
|
| +
|
| + // Replace the constructor function with a newly allocated receiver.
|
| + HInstruction* receiver = new(zone()) HAllocateObject(context, constructor);
|
| + // Index of the receiver from the top of the expression stack.
|
| + const int receiver_index = argument_count - 1;
|
| + AddInstruction(receiver);
|
| + ASSERT(environment()->ExpressionStackAt(receiver_index) == function);
|
| + environment()->SetExpressionStackAt(receiver_index, receiver);
|
| +
|
| + if (TryInlineConstruct(expr, receiver)) return;
|
| +
|
| + // TODO(mstarzinger): For now we remove the previous HAllocateObject and
|
| + // add HPushArgument for the arguments in case inlining failed. What we
|
| + // actually should do is emit HInvokeFunction on the constructor instead
|
| + // of using HCallNew as a fallback.
|
| + receiver->DeleteAndReplaceWith(NULL);
|
| + environment()->SetExpressionStackAt(receiver_index, function);
|
| + HInstruction* call = PreProcessCall(
|
| + new(zone()) HCallNew(context, function, argument_count));
|
| + call->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| + } else {
|
| + // The constructor function is both an operand to the instruction and an
|
| + // argument to the construct call.
|
| + HValue* constructor = NULL;
|
| + CHECK_ALIVE(constructor = VisitArgument(expr->expression()));
|
| + CHECK_ALIVE(VisitArgumentList(expr->arguments()));
|
| + HInstruction* call =
|
| + new(zone()) HCallNew(context, constructor, argument_count);
|
| + Drop(argument_count);
|
| + call->set_position(expr->position());
|
| + return ast_context()->ReturnInstruction(call, expr->id());
|
| + }
|
| }
|
|
|
|
|
| @@ -6510,10 +6604,11 @@ void HGraphBuilder::GenerateIsStringWrapperSafeForDefaultValueOf(
|
| void HGraphBuilder::GenerateIsConstructCall(CallRuntime* call) {
|
| ASSERT(call->arguments()->length() == 0);
|
| if (function_state()->outer() != NULL) {
|
| - // We are generating graph for inlined function. Currently
|
| - // constructor inlining is not supported and we can just return
|
| - // false from %_IsConstructCall().
|
| - return ast_context()->ReturnValue(graph()->GetConstantFalse());
|
| + // We are generating graph for inlined function.
|
| + HValue* value = function_state()->is_construct()
|
| + ? graph()->GetConstantTrue()
|
| + : graph()->GetConstantFalse();
|
| + return ast_context()->ReturnValue(value);
|
| } else {
|
| return ast_context()->ReturnControl(new(zone()) HIsConstructCallAndBranch,
|
| call->id());
|
| @@ -6901,14 +6996,14 @@ HEnvironment::HEnvironment(HEnvironment* outer,
|
| : closure_(closure),
|
| values_(0),
|
| assigned_variables_(4),
|
| + frame_type_(JS_FUNCTION),
|
| parameter_count_(0),
|
| specials_count_(1),
|
| local_count_(0),
|
| outer_(outer),
|
| pop_count_(0),
|
| push_count_(0),
|
| - ast_id_(AstNode::kNoNumber),
|
| - arguments_adaptor_(false) {
|
| + ast_id_(AstNode::kNoNumber) {
|
| Initialize(scope->num_parameters() + 1, scope->num_stack_slots(), 0);
|
| }
|
|
|
| @@ -6916,31 +7011,32 @@ HEnvironment::HEnvironment(HEnvironment* outer,
|
| HEnvironment::HEnvironment(const HEnvironment* other)
|
| : values_(0),
|
| assigned_variables_(0),
|
| + frame_type_(JS_FUNCTION),
|
| parameter_count_(0),
|
| specials_count_(1),
|
| local_count_(0),
|
| outer_(NULL),
|
| pop_count_(0),
|
| push_count_(0),
|
| - ast_id_(other->ast_id()),
|
| - arguments_adaptor_(false) {
|
| + ast_id_(other->ast_id()) {
|
| Initialize(other);
|
| }
|
|
|
|
|
| HEnvironment::HEnvironment(HEnvironment* outer,
|
| Handle<JSFunction> closure,
|
| + FrameType frame_type,
|
| int arguments)
|
| : closure_(closure),
|
| values_(arguments),
|
| assigned_variables_(0),
|
| + frame_type_(frame_type),
|
| parameter_count_(arguments),
|
| local_count_(0),
|
| outer_(outer),
|
| pop_count_(0),
|
| push_count_(0),
|
| - ast_id_(AstNode::kNoNumber),
|
| - arguments_adaptor_(true) {
|
| + ast_id_(AstNode::kNoNumber) {
|
| }
|
|
|
|
|
| @@ -6961,13 +7057,13 @@ void HEnvironment::Initialize(const HEnvironment* other) {
|
| closure_ = other->closure();
|
| values_.AddAll(other->values_);
|
| assigned_variables_.AddAll(other->assigned_variables_);
|
| + frame_type_ = other->frame_type_;
|
| parameter_count_ = other->parameter_count_;
|
| local_count_ = other->local_count_;
|
| if (other->outer_ != NULL) outer_ = other->outer_->Copy(); // Deep copy.
|
| pop_count_ = other->pop_count_;
|
| push_count_ = other->push_count_;
|
| ast_id_ = other->ast_id_;
|
| - arguments_adaptor_ = other->arguments_adaptor_;
|
| }
|
|
|
|
|
| @@ -7069,13 +7165,28 @@ HEnvironment* HEnvironment::CopyAsLoopHeader(HBasicBlock* loop_header) const {
|
| }
|
|
|
|
|
| +HEnvironment* HEnvironment::CreateStubEnvironment(HEnvironment* outer,
|
| + Handle<JSFunction> target,
|
| + FrameType frame_type,
|
| + int arguments) const {
|
| + HEnvironment* new_env = new(closure()->GetIsolate()->zone())
|
| + HEnvironment(outer, target, frame_type, arguments + 1);
|
| + for (int i = 0; i <= arguments; ++i) { // Include receiver.
|
| + new_env->Push(ExpressionStackAt(arguments - i));
|
| + }
|
| + new_env->ClearHistory();
|
| + return new_env;
|
| +}
|
| +
|
| +
|
| HEnvironment* HEnvironment::CopyForInlining(
|
| Handle<JSFunction> target,
|
| int arguments,
|
| FunctionLiteral* function,
|
| HConstant* undefined,
|
| - CallKind call_kind) const {
|
| - ASSERT(!is_arguments_adaptor());
|
| + CallKind call_kind,
|
| + bool is_construct) const {
|
| + ASSERT(frame_type() == JS_FUNCTION);
|
|
|
| Zone* zone = closure()->GetIsolate()->zone();
|
|
|
| @@ -7086,13 +7197,16 @@ HEnvironment* HEnvironment::CopyForInlining(
|
| outer->Drop(arguments + 1); // Including receiver.
|
| outer->ClearHistory();
|
|
|
| + if (is_construct) {
|
| + // Create artificial constructor stub environment. The receiver should
|
| + // actually be the constructor function, but we pass the newly allocated
|
| + // object instead, DoComputeConstructStubFrame() relies on that.
|
| + outer = CreateStubEnvironment(outer, target, JS_CONSTRUCT, arguments);
|
| + }
|
| +
|
| if (arity != arguments) {
|
| // Create artificial arguments adaptation environment.
|
| - outer = new(zone) HEnvironment(outer, target, arguments + 1);
|
| - for (int i = 0; i <= arguments; ++i) { // Include receiver.
|
| - outer->Push(ExpressionStackAt(arguments - i));
|
| - }
|
| - outer->ClearHistory();
|
| + outer = CreateStubEnvironment(outer, target, ARGUMENTS_ADAPTOR, arguments);
|
| }
|
|
|
| HEnvironment* inner =
|
|
|