blob: 0595e8d4962d3218522e2eb3a4be36fe778d9865 [file] [log] [blame]
// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
/// This library defines individual world impacts.
///
/// We call these building blocks `uses`. Each `use` is a single impact of the
/// world. Some example uses are:
///
/// * an invocation of a top level function
/// * a call to the `foo()` method on an unknown class.
/// * an instantiation of class T
///
/// The different compiler stages combine these uses into `WorldImpact` objects,
/// which are later used to construct a closed-world understanding of the
/// program.
library dart2js.universe.use;
import '../closure.dart' show BoxFieldElement;
import '../common.dart';
import '../elements/types.dart';
import '../elements/elements.dart'
show
ConstructorElement,
ConstructorBodyElement,
Element,
Entity,
LocalFunctionElement;
import '../elements/entities.dart';
import '../util/util.dart' show Hashing;
import '../world.dart' show World;
import 'call_structure.dart' show CallStructure;
import 'selector.dart' show Selector;
import 'world_builder.dart' show ReceiverConstraint;
enum DynamicUseKind {
INVOKE,
GET,
SET,
}
/// The use of a dynamic property. [selector] defined the name and kind of the
/// property and [mask] defines the known constraint for the object on which
/// the property is accessed.
class DynamicUse {
final Selector selector;
final ReceiverConstraint mask;
DynamicUse(this.selector, this.mask);
bool appliesUnnamed(MemberEntity element, World world) {
return selector.appliesUnnamed(element) &&
(mask == null || mask.canHit(element, selector, world));
}
DynamicUseKind get kind {
if (selector.isGetter) {
return DynamicUseKind.GET;
} else if (selector.isSetter) {
return DynamicUseKind.SET;
} else {
return DynamicUseKind.INVOKE;
}
}
int get hashCode => selector.hashCode * 13 + mask.hashCode * 17;
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! DynamicUse) return false;
return selector == other.selector && mask == other.mask;
}
String toString() => '$selector,$mask';
}
enum StaticUseKind {
GENERAL,
STATIC_TEAR_OFF,
SUPER_TEAR_OFF,
SUPER_FIELD_SET,
FIELD_GET,
FIELD_SET,
CLOSURE,
CONSTRUCTOR_INVOKE,
CONST_CONSTRUCTOR_INVOKE,
REDIRECTION,
DIRECT_INVOKE,
DIRECT_USE,
}
/// Statically known use of an [Element].
// TODO(johnniwinther): Create backend-specific implementations with better
// invariants.
class StaticUse {
final Entity element;
final StaticUseKind kind;
final int hashCode;
final DartType type;
StaticUse.internal(Entity element, StaticUseKind kind, [DartType type = null])
: this.element = element,
this.kind = kind,
this.type = type,
this.hashCode = Hashing.objectsHash(element, kind, type) {
assert(invariant(element, !(element is Element && !element.isDeclaration),
message: "Static use element $element must be "
"the declaration element."));
}
/// Invocation of a static or top-level [element] with the given
/// [callStructure].
factory StaticUse.staticInvoke(
FunctionEntity element, CallStructure callStructure) {
// TODO(johnniwinther): Use the [callStructure].
assert(invariant(element, element.isStatic || element.isTopLevel,
message: "Static invoke element $element must be a top-level "
"or static method."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Closurization of a static or top-level function [element].
factory StaticUse.staticTearOff(FunctionEntity element) {
assert(invariant(element, element.isStatic || element.isTopLevel,
message: "Static tear-off element $element must be a top-level "
"or static method."));
return new StaticUse.internal(element, StaticUseKind.STATIC_TEAR_OFF);
}
/// Read access of a static or top-level field or getter [element].
factory StaticUse.staticGet(MemberEntity element) {
assert(invariant(element, element.isStatic || element.isTopLevel,
message: "Static get element $element must be a top-level "
"or static method."));
assert(invariant(element, element.isField || element.isGetter,
message: "Static get element $element must be a field or a getter."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Write access of a static or top-level field or setter [element].
factory StaticUse.staticSet(MemberEntity element) {
assert(invariant(element, element.isStatic || element.isTopLevel,
message: "Static set element $element must be a top-level "
"or static method."));
assert(invariant(element, element.isField || element.isSetter,
message: "Static set element $element must be a field or a setter."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Invocation of the lazy initializer for a static or top-level field
/// [element].
factory StaticUse.staticInit(FieldEntity element) {
assert(invariant(element, element.isStatic || element.isTopLevel,
message: "Static init element $element must be a top-level "
"or static method."));
assert(invariant(element, element.isField,
message: "Static init element $element must be a field."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Invocation of a super method [element] with the given [callStructure].
factory StaticUse.superInvoke(
FunctionEntity element, CallStructure callStructure) {
// TODO(johnniwinther): Use the [callStructure].
assert(invariant(element, element.isInstanceMember,
message: "Super invoke element $element must be an instance method."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Read access of a super field or getter [element].
factory StaticUse.superGet(MemberEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Super get element $element must be an instance method."));
assert(invariant(element, element.isField || element.isGetter,
message: "Super get element $element must be a field or a getter."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Write access of a super field [element].
factory StaticUse.superFieldSet(FieldEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Super set element $element must be an instance method."));
assert(invariant(element, element.isField,
message: "Super set element $element must be a field."));
return new StaticUse.internal(element, StaticUseKind.SUPER_FIELD_SET);
}
/// Write access of a super setter [element].
factory StaticUse.superSetterSet(FunctionEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Super set element $element must be an instance method."));
assert(invariant(element, element.isSetter,
message: "Super set element $element must be a setter."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Closurization of a super method [element].
factory StaticUse.superTearOff(FunctionEntity element) {
assert(invariant(element, element.isInstanceMember && element.isFunction,
message: "Super invoke element $element must be an instance method."));
return new StaticUse.internal(element, StaticUseKind.SUPER_TEAR_OFF);
}
/// Invocation of a constructor [element] through a this or super
/// constructor call with the given [callStructure].
factory StaticUse.superConstructorInvoke(
ConstructorElement element, CallStructure callStructure) {
// TODO(johnniwinther): Use the [callStructure].
assert(invariant(element, element.isGenerativeConstructor,
message: "Constructor invoke element $element must be a "
"generative constructor."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Invocation of a constructor (body) [element] through a this or super
/// constructor call with the given [callStructure].
factory StaticUse.constructorBodyInvoke(
ConstructorBodyElement element, CallStructure callStructure) {
// TODO(johnniwinther): Use the [callStructure].
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Direct invocation of a method [element] with the given [callStructure].
factory StaticUse.directInvoke(
FunctionEntity element, CallStructure callStructure) {
// TODO(johnniwinther): Use the [callStructure].
assert(invariant(element, element.isInstanceMember,
message: "Direct invoke element $element must be an instance member."));
assert(invariant(element, element.isFunction,
message: "Direct invoke element $element must be a method."));
return new StaticUse.internal(element, StaticUseKind.DIRECT_INVOKE);
}
/// Direct read access of a field or getter [element].
factory StaticUse.directGet(MemberEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Direct get element $element must be an instance member."));
assert(invariant(element, element.isField || element.isGetter,
message: "Direct get element $element must be a field or a getter."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Direct write access of a field [element].
factory StaticUse.directSet(FieldEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Direct set element $element must be an instance member."));
assert(invariant(element, element.isField,
message: "Direct set element $element must be a field."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Constructor invocation of [element] with the given [callStructure].
factory StaticUse.constructorInvoke(
FunctionEntity element, CallStructure callStructure) {
assert(invariant(element, element.isConstructor,
message: "Constructor invocation element $element "
"must be a constructor."));
// TODO(johnniwinther): Use the [callStructure].
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Constructor invocation of [element] with the given [callStructure] on
/// [type].
factory StaticUse.typedConstructorInvoke(
FunctionEntity element, CallStructure callStructure, DartType type) {
assert(invariant(element, type != null,
message: "No type provided for constructor invocation."));
assert(invariant(element, element.isConstructor,
message: "Typed constructor invocation element $element "
"must be a constructor."));
// TODO(johnniwinther): Use the [callStructure].
return new StaticUse.internal(
element, StaticUseKind.CONSTRUCTOR_INVOKE, type);
}
/// Constant constructor invocation of [element] with the given
/// [callStructure] on [type].
factory StaticUse.constConstructorInvoke(
FunctionEntity element, CallStructure callStructure, DartType type) {
assert(invariant(element, type != null,
message: "No type provided for constructor invocation."));
assert(invariant(element, element.isConstructor,
message: "Const constructor invocation element $element "
"must be a constructor."));
// TODO(johnniwinther): Use the [callStructure].
return new StaticUse.internal(
element, StaticUseKind.CONST_CONSTRUCTOR_INVOKE, type);
}
/// Constructor redirection to [element] on [type].
factory StaticUse.constructorRedirect(
FunctionEntity element, InterfaceType type) {
assert(invariant(element, type != null,
message: "No type provided for constructor redirection."));
assert(invariant(element, element.isConstructor,
message: "Constructor redirection element $element "
"must be a constructor."));
return new StaticUse.internal(element, StaticUseKind.REDIRECTION, type);
}
/// Initialization of an instance field [element].
factory StaticUse.fieldInit(FieldEntity element) {
assert(invariant(element, element.isInstanceMember,
message: "Field init element $element must be an instance field."));
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Read access of an instance field or boxed field [element].
factory StaticUse.fieldGet(FieldEntity element) {
assert(invariant(
element, element.isInstanceMember || element is BoxFieldElement,
message: "Field init element $element must be an instance "
"or boxed field."));
return new StaticUse.internal(element, StaticUseKind.FIELD_GET);
}
/// Write access of an instance field or boxed field [element].
factory StaticUse.fieldSet(FieldEntity element) {
assert(invariant(
element, element.isInstanceMember || element is BoxFieldElement,
message: "Field init element $element must be an instance "
"or boxed field."));
return new StaticUse.internal(element, StaticUseKind.FIELD_SET);
}
/// Read of a local function [element].
factory StaticUse.closure(LocalFunctionElement element) {
return new StaticUse.internal(element, StaticUseKind.CLOSURE);
}
/// Unknown use of [element].
///
/// Avoid using this, if possible: Use one of the other constructor which more
/// precisely capture why [element] is used.
@deprecated
factory StaticUse.foreignUse(Entity element) {
return new StaticUse.internal(element, StaticUseKind.GENERAL);
}
/// Direct use of [element] as done with `--analyze-all` and `--analyze-main`.
factory StaticUse.directUse(Entity element) {
return new StaticUse.internal(element, StaticUseKind.DIRECT_USE);
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! StaticUse) return false;
return element == other.element && kind == other.kind && type == other.type;
}
String toString() => 'StaticUse($element,$kind,$type)';
}
enum TypeUseKind {
IS_CHECK,
AS_CAST,
CHECKED_MODE_CHECK,
CATCH_TYPE,
TYPE_LITERAL,
INSTANTIATION,
MIRROR_INSTANTIATION,
NATIVE_INSTANTIATION,
}
/// Use of a [ResolutionDartType].
class TypeUse {
final DartType type;
final TypeUseKind kind;
final int hashCode;
TypeUse.internal(DartType type, TypeUseKind kind)
: this.type = type,
this.kind = kind,
this.hashCode = Hashing.objectHash(type, Hashing.objectHash(kind));
/// [type] used in an is check, like `e is T` or `e is! T`.
factory TypeUse.isCheck(DartType type) {
return new TypeUse.internal(type, TypeUseKind.IS_CHECK);
}
/// [type] used in an as cast, like `e as T`.
factory TypeUse.asCast(DartType type) {
return new TypeUse.internal(type, TypeUseKind.AS_CAST);
}
/// [type] used as a type annotation, like `T foo;`.
factory TypeUse.checkedModeCheck(DartType type) {
return new TypeUse.internal(type, TypeUseKind.CHECKED_MODE_CHECK);
}
/// [type] used in a on type catch clause, like `try {} on T catch (e) {}`.
factory TypeUse.catchType(DartType type) {
return new TypeUse.internal(type, TypeUseKind.CATCH_TYPE);
}
/// [type] used as a type literal, like `foo() => T;`.
factory TypeUse.typeLiteral(DartType type) {
return new TypeUse.internal(type, TypeUseKind.TYPE_LITERAL);
}
/// [type] used in an instantiation, like `new T();`.
factory TypeUse.instantiation(InterfaceType type) {
return new TypeUse.internal(type, TypeUseKind.INSTANTIATION);
}
/// [type] used in an instantiation through mirrors.
factory TypeUse.mirrorInstantiation(InterfaceType type) {
return new TypeUse.internal(type, TypeUseKind.MIRROR_INSTANTIATION);
}
/// [type] used in a native instantiation.
factory TypeUse.nativeInstantiation(InterfaceType type) {
return new TypeUse.internal(type, TypeUseKind.NATIVE_INSTANTIATION);
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! TypeUse) return false;
return type == other.type && kind == other.kind;
}
String toString() => 'TypeUse($type,$kind)';
}