[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:
parent
5dedb164eb
commit
e6dca379b6
@ -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(),
|
||||
|
@ -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,
|
||||
|
15
test/mjsunit/regress/regress-crbug-595615.js
Normal file
15
test/mjsunit/regress/regress-crbug-595615.js
Normal 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) {}
|
Loading…
Reference in New Issue
Block a user