Commit 777e142c authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add appropriate types to express Callable.

This introduces three new types OtherCallable, CallableProxy (and OtherProxy),
and BoundFunction to make it possible to express Callable in the Type system.
It also forces all undetectable receivers to be Callable, which matches the
use case for undetectable, namely document.all (guarded by proper checks and
tests).

It also uses these new types to properly optimize instanceof (indirectly via
OrdinaryHasInstance) based on the type of the constructor and the object. So
we are able to constant-fold certain instanceof expressions based on types
and completely avoid the builtin call.

R=jarin@chromium.org
BUG=v8:5267

Review-Url: https://codereview.chromium.org/2535753004
Cr-Commit-Position: refs/heads/master@{#41345}
parent 18523009
...@@ -675,6 +675,12 @@ Handle<JSFunction> ApiNatives::CreateApiFunction( ...@@ -675,6 +675,12 @@ Handle<JSFunction> ApiNatives::CreateApiFunction(
// Mark as undetectable if needed. // Mark as undetectable if needed.
if (obj->undetectable()) { if (obj->undetectable()) {
// We only allow callable undetectable receivers here, since this whole
// undetectable business is only to support document.all, which is both
// undetectable and callable. If we ever see the need to have an object
// that is undetectable but not callable, we need to update the types.h
// to allow encoding this.
CHECK(!obj->instance_call_handler()->IsUndefined(isolate));
map->set_is_undetectable(); map->set_is_undetectable();
} }
......
...@@ -1331,11 +1331,30 @@ Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) { ...@@ -1331,11 +1331,30 @@ Reduction JSTypedLowering::ReduceJSOrdinaryHasInstance(Node* node) {
Node* constructor = NodeProperties::GetValueInput(node, 0); Node* constructor = NodeProperties::GetValueInput(node, 0);
Type* constructor_type = NodeProperties::GetType(constructor); Type* constructor_type = NodeProperties::GetType(constructor);
Node* object = NodeProperties::GetValueInput(node, 1); Node* object = NodeProperties::GetValueInput(node, 1);
Type* object_type = NodeProperties::GetType(object);
Node* context = NodeProperties::GetContextInput(node); Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node); Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
// Check if the {constructor} cannot be callable.
// See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 1.
if (!constructor_type->Maybe(Type::Callable())) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// If the {constructor} cannot be a JSBoundFunction and then {object}
// cannot be a JSReceiver, then this can be constant-folded to false.
// See ES6 section 7.3.19 OrdinaryHasInstance ( C, O ) step 2 and 3.
if (!object_type->Maybe(Type::Receiver()) &&
!constructor_type->Maybe(Type::BoundFunction())) {
Node* value = jsgraph()->FalseConstant();
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
// Check if the {constructor} is a (known) JSFunction. // Check if the {constructor} is a (known) JSFunction.
if (!constructor_type->IsHeapConstant() || if (!constructor_type->IsHeapConstant() ||
!constructor_type->AsHeapConstant()->Value()->IsJSFunction()) { !constructor_type->AsHeapConstant()->Value()->IsJSFunction()) {
......
...@@ -104,7 +104,9 @@ ...@@ -104,7 +104,9 @@
#define JS_SIMPLE_BINOP_LIST(V) \ #define JS_SIMPLE_BINOP_LIST(V) \
JS_COMPARE_BINOP_LIST(V) \ JS_COMPARE_BINOP_LIST(V) \
JS_BITWISE_BINOP_LIST(V) \ JS_BITWISE_BINOP_LIST(V) \
JS_ARITH_BINOP_LIST(V) JS_ARITH_BINOP_LIST(V) \
V(JSInstanceOf) \
V(JSOrdinaryHasInstance)
#define JS_CONVERSION_UNOP_LIST(V) \ #define JS_CONVERSION_UNOP_LIST(V) \
V(JSToBoolean) \ V(JSToBoolean) \
...@@ -140,9 +142,7 @@ ...@@ -140,9 +142,7 @@
V(JSStoreGlobal) \ V(JSStoreGlobal) \
V(JSStoreDataPropertyInLiteral) \ V(JSStoreDataPropertyInLiteral) \
V(JSDeleteProperty) \ V(JSDeleteProperty) \
V(JSHasProperty) \ V(JSHasProperty)
V(JSInstanceOf) \
V(JSOrdinaryHasInstance)
#define JS_CONTEXT_OP_LIST(V) \ #define JS_CONTEXT_OP_LIST(V) \
V(JSLoadContext) \ V(JSLoadContext) \
......
...@@ -1244,9 +1244,14 @@ Type* Typer::Visitor::TypeJSDeleteProperty(Node* node) { ...@@ -1244,9 +1244,14 @@ Type* Typer::Visitor::TypeJSDeleteProperty(Node* node) {
Type* Typer::Visitor::TypeJSHasProperty(Node* node) { return Type::Boolean(); } Type* Typer::Visitor::TypeJSHasProperty(Node* node) { return Type::Boolean(); }
Type* Typer::Visitor::TypeJSInstanceOf(Node* node) { return Type::Boolean(); } // JS instanceof operator.
Type* Typer::Visitor::TypeJSOrdinaryHasInstance(Node* node) { Type* Typer::Visitor::JSInstanceOfTyper(Type* lhs, Type* rhs, Typer* t) {
return Type::Boolean();
}
Type* Typer::Visitor::JSOrdinaryHasInstanceTyper(Type* lhs, Type* rhs,
Typer* t) {
return Type::Boolean(); return Type::Boolean();
} }
......
...@@ -196,7 +196,17 @@ Type::bitset BitsetType::Lub(i::Map* map) { ...@@ -196,7 +196,17 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case JS_GLOBAL_PROXY_TYPE: case JS_GLOBAL_PROXY_TYPE:
case JS_API_OBJECT_TYPE: case JS_API_OBJECT_TYPE:
case JS_SPECIAL_API_OBJECT_TYPE: case JS_SPECIAL_API_OBJECT_TYPE:
if (map->is_undetectable()) return kOtherUndetectable; if (map->is_undetectable()) {
// Currently we assume that every undetectable receiver is also
// callable, which is what we need to support document.all. We
// could add another Type bit to support other use cases in the
// future if necessary.
DCHECK(map->is_callable());
return kOtherUndetectable;
}
if (map->is_callable()) {
return kOtherCallable;
}
return kOtherObject; return kOtherObject;
case JS_VALUE_TYPE: case JS_VALUE_TYPE:
case JS_MESSAGE_OBJECT_TYPE: case JS_MESSAGE_OBJECT_TYPE:
...@@ -255,15 +265,19 @@ Type::bitset BitsetType::Lub(i::Map* map) { ...@@ -255,15 +265,19 @@ Type::bitset BitsetType::Lub(i::Map* map) {
case JS_WEAK_MAP_TYPE: case JS_WEAK_MAP_TYPE:
case JS_WEAK_SET_TYPE: case JS_WEAK_SET_TYPE:
case JS_PROMISE_TYPE: case JS_PROMISE_TYPE:
case JS_BOUND_FUNCTION_TYPE: DCHECK(!map->is_callable());
DCHECK(!map->is_undetectable()); DCHECK(!map->is_undetectable());
return kOtherObject; return kOtherObject;
case JS_BOUND_FUNCTION_TYPE:
DCHECK(!map->is_undetectable());
return kBoundFunction;
case JS_FUNCTION_TYPE: case JS_FUNCTION_TYPE:
DCHECK(!map->is_undetectable()); DCHECK(!map->is_undetectable());
return kFunction; return kFunction;
case JS_PROXY_TYPE: case JS_PROXY_TYPE:
DCHECK(!map->is_undetectable()); DCHECK(!map->is_undetectable());
return kProxy; if (map->is_callable()) return kCallableProxy;
return kOtherProxy;
case MAP_TYPE: case MAP_TYPE:
case ALLOCATION_SITE_TYPE: case ALLOCATION_SITE_TYPE:
case ACCESSOR_INFO_TYPE: case ACCESSOR_INFO_TYPE:
......
...@@ -117,13 +117,16 @@ namespace compiler { ...@@ -117,13 +117,16 @@ namespace compiler {
V(InternalizedString, 1u << 13) \ V(InternalizedString, 1u << 13) \
V(OtherString, 1u << 14) \ V(OtherString, 1u << 14) \
V(Simd, 1u << 15) \ V(Simd, 1u << 15) \
V(OtherCallable, 1u << 16) \
V(OtherObject, 1u << 17) \ V(OtherObject, 1u << 17) \
V(OtherUndetectable, 1u << 16) \ V(OtherUndetectable, 1u << 18) \
V(Proxy, 1u << 18) \ V(CallableProxy, 1u << 19) \
V(Function, 1u << 19) \ V(OtherProxy, 1u << 20) \
V(Hole, 1u << 20) \ V(Function, 1u << 21) \
V(OtherInternal, 1u << 21) \ V(BoundFunction, 1u << 22) \
V(ExternalPointer, 1u << 22) \ V(Hole, 1u << 23) \
V(OtherInternal, 1u << 24) \
V(ExternalPointer, 1u << 25) \
\ \
V(Signed31, kUnsigned30 | kNegative31) \ V(Signed31, kUnsigned30 | kNegative31) \
V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \ V(Signed32, kSigned31 | kOtherUnsigned31 | kOtherSigned32) \
...@@ -155,9 +158,14 @@ namespace compiler { ...@@ -155,9 +158,14 @@ namespace compiler {
V(NumberOrUndefined, kNumber | kUndefined) \ V(NumberOrUndefined, kNumber | kUndefined) \
V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \ V(PlainPrimitive, kNumberOrString | kBoolean | kNullOrUndefined) \
V(Primitive, kSymbol | kSimd | kPlainPrimitive) \ V(Primitive, kSymbol | kSimd | kPlainPrimitive) \
V(DetectableReceiver, kFunction | kOtherObject | kProxy) \ V(Proxy, kCallableProxy | kOtherProxy) \
V(Callable, kFunction | kBoundFunction | kOtherCallable | \
kCallableProxy | kOtherUndetectable) \
V(DetectableObject, kFunction | kBoundFunction | kOtherCallable | \
kOtherObject) \
V(DetectableReceiver, kDetectableObject | kProxy) \
V(DetectableReceiverOrNull, kDetectableReceiver | kNull) \ V(DetectableReceiverOrNull, kDetectableReceiver | kNull) \
V(Object, kFunction | kOtherObject | kOtherUndetectable) \ V(Object, kDetectableObject | kOtherUndetectable) \
V(Receiver, kObject | kProxy) \ V(Receiver, kObject | kProxy) \
V(ReceiverOrUndefined, kReceiver | kUndefined) \ V(ReceiverOrUndefined, kReceiver | kUndefined) \
V(ReceiverOrNullOrUndefined, kReceiver | kNull | kUndefined) \ V(ReceiverOrNullOrUndefined, kReceiver | kNull | kUndefined) \
......
...@@ -299,6 +299,9 @@ RUNTIME_FUNCTION(Runtime_GetOptimizationCount) { ...@@ -299,6 +299,9 @@ RUNTIME_FUNCTION(Runtime_GetOptimizationCount) {
return Smi::FromInt(function->shared()->opt_count()); return Smi::FromInt(function->shared()->opt_count());
} }
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
RUNTIME_FUNCTION(Runtime_GetUndetectable) { RUNTIME_FUNCTION(Runtime_GetUndetectable) {
HandleScope scope(isolate); HandleScope scope(isolate);
...@@ -307,6 +310,7 @@ RUNTIME_FUNCTION(Runtime_GetUndetectable) { ...@@ -307,6 +310,7 @@ RUNTIME_FUNCTION(Runtime_GetUndetectable) {
Local<v8::ObjectTemplate> desc = v8::ObjectTemplate::New(v8_isolate); Local<v8::ObjectTemplate> desc = v8::ObjectTemplate::New(v8_isolate);
desc->MarkAsUndetectable(); desc->MarkAsUndetectable();
desc->SetCallAsFunctionHandler(ReturnThis);
Local<v8::Object> obj; Local<v8::Object> obj;
if (!desc->NewInstance(v8_isolate->GetCurrentContext()).ToLocal(&obj)) { if (!desc->NewInstance(v8_isolate->GetCurrentContext()).ToLocal(&obj)) {
return nullptr; return nullptr;
......
...@@ -7085,6 +7085,9 @@ THREADED_TEST(Regress892105) { ...@@ -7085,6 +7085,9 @@ THREADED_TEST(Regress892105) {
.FromJust()); .FromJust());
} }
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
THREADED_TEST(UndetectableObject) { THREADED_TEST(UndetectableObject) {
LocalContext env; LocalContext env;
...@@ -7093,6 +7096,7 @@ THREADED_TEST(UndetectableObject) { ...@@ -7093,6 +7096,7 @@ THREADED_TEST(UndetectableObject) {
Local<v8::FunctionTemplate> desc = Local<v8::FunctionTemplate> desc =
v8::FunctionTemplate::New(env->GetIsolate()); v8::FunctionTemplate::New(env->GetIsolate());
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local()) Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked() .ToLocalChecked()
...@@ -7141,6 +7145,7 @@ THREADED_TEST(VoidLiteral) { ...@@ -7141,6 +7145,7 @@ THREADED_TEST(VoidLiteral) {
Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local()) Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked() .ToLocalChecked()
...@@ -7191,6 +7196,7 @@ THREADED_TEST(ExtensibleOnUndetectable) { ...@@ -7191,6 +7196,7 @@ THREADED_TEST(ExtensibleOnUndetectable) {
Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate); Local<v8::FunctionTemplate> desc = v8::FunctionTemplate::New(isolate);
desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
desc->InstanceTemplate()->SetCallAsFunctionHandler(ReturnThis); // callable
Local<v8::Object> obj = desc->GetFunction(env.local()) Local<v8::Object> obj = desc->GetFunction(env.local())
.ToLocalChecked() .ToLocalChecked()
...@@ -11775,11 +11781,6 @@ static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) { ...@@ -11775,11 +11781,6 @@ static void call_as_function(const v8::FunctionCallbackInfo<v8::Value>& args) {
} }
static void ReturnThis(const v8::FunctionCallbackInfo<v8::Value>& args) {
args.GetReturnValue().Set(args.This());
}
// Test that a call handler can be set for objects which will allow // Test that a call handler can be set for objects which will allow
// non-function objects created through the API to be called as // non-function objects created through the API to be called as
// functions. // functions.
......
// 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
var Foo = {
[Symbol.hasInstance]: Function.prototype[Symbol.hasInstance]
};
// TurboFan will optimize this to false via constant-folding the
// OrdinaryHasInstance call inside Function.prototype[@@hasInstance].
function foo() { return 1 instanceof Foo; }
assertEquals(false, foo());
assertEquals(false, foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals(false, foo());
// 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
function Foo() {}
// TurboFan will optimize this to false via constant-folding the
// OrdinaryHasInstance call inside Function.prototype[@@hasInstance].
function foo() { return 1 instanceof Foo; }
assertEquals(false, foo());
assertEquals(false, foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals(false, foo());
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