blob: 97b5b2d188d727612f24560101d7a85b577708ce [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.
library dart2js.common.resolution;
import '../common.dart';
import '../compiler.dart' show Compiler;
import '../constants/expressions.dart' show ConstantExpression;
import '../core_types.dart' show CoreClasses, CoreTypes;
import '../dart_types.dart' show DartType, InterfaceType, Types;
import '../elements/elements.dart'
show
AstElement,
ClassElement,
Element,
ExecutableElement,
FunctionElement,
FunctionSignature,
LibraryElement,
MetadataAnnotation,
ResolvedAst,
TypedefElement;
import '../enqueue.dart' show ResolutionEnqueuer;
import '../options.dart' show ParserOptions;
import '../parser/element_listener.dart' show ScannerOptions;
import '../parser/parser_task.dart';
import '../patch_parser.dart';
import '../tree/tree.dart' show TypeAnnotation;
import '../universe/world_impact.dart' show WorldImpact;
import 'backend_api.dart';
import 'work.dart' show ItemCompilationContext, WorkItem;
/// [WorkItem] used exclusively by the [ResolutionEnqueuer].
class ResolutionWorkItem extends WorkItem {
bool _isAnalyzed = false;
ResolutionWorkItem(
AstElement element, ItemCompilationContext compilationContext)
: super(element, compilationContext);
WorldImpact run(Compiler compiler, ResolutionEnqueuer world) {
WorldImpact impact = compiler.analyze(this, world);
_isAnalyzed = true;
return impact;
}
bool get isAnalyzed => _isAnalyzed;
}
class ResolutionImpact extends WorldImpact {
const ResolutionImpact();
Iterable<Feature> get features => const <Feature>[];
Iterable<MapLiteralUse> get mapLiterals => const <MapLiteralUse>[];
Iterable<ListLiteralUse> get listLiterals => const <ListLiteralUse>[];
Iterable<String> get constSymbolNames => const <String>[];
Iterable<ConstantExpression> get constantLiterals {
return const <ConstantExpression>[];
}
Iterable<dynamic> get nativeData => const <dynamic>[];
}
/// A language feature seen during resolution.
// TODO(johnniwinther): Should mirror usage be part of this?
enum Feature {
/// Invocation of a generative construction on an abstract class.
ABSTRACT_CLASS_INSTANTIATION,
/// An assert statement with no message.
ASSERT,
/// An assert statement with a message.
ASSERT_WITH_MESSAGE,
/// A method with an `async` body modifier.
ASYNC,
/// An asynchronous for in statement like `await for (var e in i) {}`.
ASYNC_FOR_IN,
/// A method with an `async*` body modifier.
ASYNC_STAR,
/// A catch statement.
CATCH_STATEMENT,
/// A compile time error.
COMPILE_TIME_ERROR,
/// A fall through in a switch case.
FALL_THROUGH_ERROR,
/// A ++/-- operation.
INC_DEC_OPERATION,
/// A field whose initialization is not a constant.
LAZY_FIELD,
/// A catch clause with a variable for the stack trace.
STACK_TRACE_IN_CATCH,
/// String interpolation.
STRING_INTERPOLATION,
/// String juxtaposition.
STRING_JUXTAPOSITION,
/// An implicit call to `super.noSuchMethod`, like calling an unresolved
/// super method.
SUPER_NO_SUCH_METHOD,
/// A redirection to the `Symbol` constructor.
SYMBOL_CONSTRUCTOR,
/// An synchronous for in statement, like `for (var e in i) {}`.
SYNC_FOR_IN,
/// A method with a `sync*` body modifier.
SYNC_STAR,
/// A throw expression.
THROW_EXPRESSION,
/// An implicit throw of a `NoSuchMethodError`, like calling an unresolved
/// static method.
THROW_NO_SUCH_METHOD,
/// An implicit throw of a runtime error, like
THROW_RUNTIME_ERROR,
/// The need for a type variable bound check, like instantiation of a generic
/// type whose type variable have non-trivial bounds.
TYPE_VARIABLE_BOUNDS_CHECK,
}
/// A use of a map literal seen during resolution.
class MapLiteralUse {
final InterfaceType type;
final bool isConstant;
final bool isEmpty;
MapLiteralUse(this.type, {this.isConstant: false, this.isEmpty: false});
int get hashCode {
return type.hashCode * 13 +
isConstant.hashCode * 17 +
isEmpty.hashCode * 19;
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! MapLiteralUse) return false;
return type == other.type &&
isConstant == other.isConstant &&
isEmpty == other.isEmpty;
}
String toString() {
return 'MapLiteralUse($type,isConstant:$isConstant,isEmpty:$isEmpty)';
}
}
/// A use of a list literal seen during resolution.
class ListLiteralUse {
final InterfaceType type;
final bool isConstant;
final bool isEmpty;
ListLiteralUse(this.type, {this.isConstant: false, this.isEmpty: false});
int get hashCode {
return type.hashCode * 13 +
isConstant.hashCode * 17 +
isEmpty.hashCode * 19;
}
bool operator ==(other) {
if (identical(this, other)) return true;
if (other is! ListLiteralUse) return false;
return type == other.type &&
isConstant == other.isConstant &&
isEmpty == other.isEmpty;
}
String toString() {
return 'ListLiteralUse($type,isConstant:$isConstant,isEmpty:$isEmpty)';
}
}
/// Interface for the accessing the front-end analysis.
// TODO(johnniwinther): Find a better name for this.
abstract class Frontend {
/// Returns the [ResolutionImpact] for [element].
ResolutionImpact getResolutionImpact(Element element);
}
/// Interface defining target-specific behavior for resolution.
abstract class Target {
/// Returns `true` if [library] is a target specific library whose members
/// have special treatment, such as being allowed to extends blacklisted
/// classes or members being eagerly resolved.
bool isTargetSpecificLibrary(LibraryElement element);
}
// TODO(johnniwinther): Rename to `Resolver` or `ResolverContext`.
abstract class Resolution implements Frontend {
ParsingContext get parsingContext;
DiagnosticReporter get reporter;
CoreClasses get coreClasses;
CoreTypes get coreTypes;
Types get types;
Target get target;
/// If set to `true` resolution caches will not be cleared. Use this only for
/// testing.
bool retainCachesForTesting;
void resolveTypedef(TypedefElement typdef);
void resolveClass(ClassElement cls);
void registerClass(ClassElement cls);
void resolveMetadataAnnotation(MetadataAnnotation metadataAnnotation);
FunctionSignature resolveSignature(FunctionElement function);
DartType resolveTypeAnnotation(Element element, TypeAnnotation node);
/// Returns `true` if [element] has been resolved.
// TODO(johnniwinther): Normalize semantics between normal and deserialized
// elements; deserialized elements are always resolved but the method will
// return `false`.
bool hasBeenResolved(Element element);
/// Resolve [element] if it has not already been resolved.
void ensureResolved(Element element);
ResolutionWorkItem createWorkItem(
Element element, ItemCompilationContext compilationContext);
/// Returns `true` if [element] as a fully computed [ResolvedAst].
bool hasResolvedAst(ExecutableElement element);
/// Returns the `ResolvedAst` for the [element].
ResolvedAst getResolvedAst(ExecutableElement element);
/// Returns `true` if the [ResolutionImpact] for [element] is cached.
bool hasResolutionImpact(Element element);
/// Returns the precomputed [ResolutionImpact] for [element].
ResolutionImpact getResolutionImpact(Element element);
/// Returns the [ResolvedAst] for [element], computing it if necessary.
ResolvedAst computeResolvedAst(Element element);
/// Returns the precomputed [WorldImpact] for [element].
WorldImpact getWorldImpact(Element element);
/// Computes the [WorldImpact] for [element].
WorldImpact computeWorldImpact(Element element);
WorldImpact transformResolutionImpact(
Element element, ResolutionImpact resolutionImpact);
/// Removes the [WorldImpact] for [element] from the resolution cache. Later
/// calls to [getWorldImpact] or [computeWorldImpact] returns an empty impact.
void uncacheWorldImpact(Element element);
/// Removes the [WorldImpact]s for all [Element]s in the resolution cache. ,
/// Later calls to [getWorldImpact] or [computeWorldImpact] returns an empty
/// impact.
void emptyCache();
void forgetElement(Element element);
}
/// A container of commonly used dependencies for tasks that involve parsing.
abstract class ParsingContext {
factory ParsingContext(
DiagnosticReporter reporter,
ParserOptions parserOptions,
ParserTask parser,
PatchParserTask patchParser,
Backend backend) = _ParsingContext;
DiagnosticReporter get reporter;
ParserOptions get parserOptions;
ParserTask get parser;
PatchParserTask get patchParser;
/// Use [patchParser] directly instead.
@deprecated
void parsePatchClass(ClassElement cls);
/// Use [parser] and measure directly instead.
@deprecated
measure(f());
/// Get the [ScannerOptions] to scan the given [element].
ScannerOptions getScannerOptionsFor(Element element);
}
class _ParsingContext implements ParsingContext {
final DiagnosticReporter reporter;
final ParserOptions parserOptions;
final ParserTask parser;
final PatchParserTask patchParser;
final Backend backend;
_ParsingContext(this.reporter, this.parserOptions, this.parser,
this.patchParser, this.backend);
@override
measure(f()) => parser.measure(f);
@override
void parsePatchClass(ClassElement cls) {
patchParser.measure(() {
if (cls.isPatch) {
patchParser.parsePatchClassNode(cls);
}
});
}
@override
ScannerOptions getScannerOptionsFor(Element element) => new ScannerOptions(
canUseNative: backend.canLibraryUseNative(element.library));
}