[crankshaft] Check if the function is callable before generating a tail call via Call builtin.

This is necessary to ensure that "Called non callable" exception will get a proper message and stack trace even for calls at tail position.

BUG=chromium:595615, v8:4698
LOG=N

Review URL: https://codereview.chromium.org/1818003002

Cr-Commit-Position: refs/heads/master@{#34962}
This commit is contained in:
ishell 2016-03-21 12:22:35 -07:00 committed by Commit bot
parent 5dedb164eb
commit e6dca379b6
3 changed files with 51 additions and 1 deletions

View File

@ -7996,9 +7996,37 @@ void HOptimizedGraphBuilder::AddCheckPrototypeMaps(Handle<JSObject> holder,
}
}
void HOptimizedGraphBuilder::BuildEnsureCallable(HValue* object) {
NoObservableSideEffectsScope scope(this);
const Runtime::Function* throw_called_non_callable =
Runtime::FunctionForId(Runtime::kThrowCalledNonCallable);
IfBuilder is_not_function(this);
HValue* smi_check = is_not_function.If<HIsSmiAndBranch>(object);
is_not_function.Or();
HValue* map = AddLoadMap(object, smi_check);
HValue* bit_field =
Add<HLoadNamedField>(map, nullptr, HObjectAccess::ForMapBitField());
HValue* bit_field_masked = AddUncasted<HBitwise>(
Token::BIT_AND, bit_field, Add<HConstant>(1 << Map::kIsCallable));
is_not_function.IfNot<HCompareNumericAndBranch>(
bit_field_masked, Add<HConstant>(1 << Map::kIsCallable), Token::EQ);
is_not_function.Then();
{
Add<HPushArguments>(object);
Add<HCallRuntime>(throw_called_non_callable, 1);
}
is_not_function.End();
}
HInstruction* HOptimizedGraphBuilder::NewCallFunction(
HValue* function, int argument_count, TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, TailCallMode tail_call_mode) {
if (syntactic_tail_call_mode == TailCallMode::kAllow) {
BuildEnsureCallable(function);
} else {
DCHECK_EQ(TailCallMode::kDisallow, tail_call_mode);
}
HValue* arity = Add<HConstant>(argument_count - 1);
HValue* op_vals[] = {context(), function, arity};
@ -8016,6 +8044,11 @@ HInstruction* HOptimizedGraphBuilder::NewCallFunctionViaIC(
HValue* function, int argument_count, TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode, TailCallMode tail_call_mode,
FeedbackVectorSlot slot) {
if (syntactic_tail_call_mode == TailCallMode::kAllow) {
BuildEnsureCallable(function);
} else {
DCHECK_EQ(TailCallMode::kDisallow, tail_call_mode);
}
int arity = argument_count - 1;
Handle<TypeFeedbackVector> vector(current_feedback_vector(), isolate());
HValue* index_val = Add<HConstant>(vector->GetIndex(slot));
@ -8024,7 +8057,7 @@ HInstruction* HOptimizedGraphBuilder::NewCallFunctionViaIC(
HValue* op_vals[] = {context(), function, index_val, vector_val};
Callable callable = CodeFactory::CallICInOptimizedCode(
isolate(), arity, ConvertReceiverMode::kNullOrUndefined, tail_call_mode);
isolate(), arity, convert_mode, tail_call_mode);
HConstant* stub = Add<HConstant>(callable.code());
return New<HCallWithDescriptor>(stub, argument_count, callable.descriptor(),

View File

@ -2835,6 +2835,8 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
void AddCheckPrototypeMaps(Handle<JSObject> holder,
Handle<Map> receiver_map);
void BuildEnsureCallable(HValue* object);
HInstruction* NewCallFunction(HValue* function, int argument_count,
TailCallMode syntactic_tail_call_mode,
ConvertReceiverMode convert_mode,

View File

@ -0,0 +1,15 @@
// 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.
// Flags: --allow-natives-syntax --harmony-tailcalls
"use strict";
function f(o) {
return o.x();
}
try { f({ x: 1 }); } catch(e) {}
try { f({ x: 1 }); } catch(e) {}
%OptimizeFunctionOnNextCall(f);
try { f({ x: 1 }); } catch(e) {}