Record deferred accesses in kernel world impacts and use it for splitting only

under a flag.

This reapplies commit 70e1517d98, but adds a flag
to gradually migrate users before enabling it by default.

Patchset 1 matches the old CL

Change-Id: Iaf7ee3dec8d4aa658f0b4334549b507e5a610a68
Reviewed-on: https://dart-review.googlesource.com/c/86444
Reviewed-by: Stephen Adams <sra@google.com>
Commit-Queue: Sigmund Cherem <sigmund@google.com>
This commit is contained in:
Sigmund Cherem 2018-12-06 23:20:38 +00:00 committed by commit-bot@chromium.org
parent a84863253c
commit 2a39f63c2a
15 changed files with 362 additions and 104 deletions

View file

@ -21,6 +21,64 @@
### Tool Changes
#### dart2js
* We fixed a bug in how deferred constructor calls were incorrectly not
marked as deferred. The old behavior didn't cause breakages, but was imprecise
and pushed more code to the main output unit.
* A new deferred split algorithm implementation was added.
This implementation fixes a soundness bug and addresses performance issues of
the previous implementation, because of that it can have a visible impact
on apps. In particular,
* We fixed a performance issue which was introduced when we migrated to the
Common front-end. On large apps, the fix can cut down 2/3 of the time
spent on this task.
* We fixed a bug in how inferred types were miscategorized (#35311). The old
behavior was unsound and could produce broken programs. The fix may cause
more code to be pulled into the main output unit.
This shows up frequently when returning deferred values from closures
since the closure's inferred return type is the deferred type.
For example, if you have:
```dart
() async {
await deferred_prefix.loadLibrary();
return new deferred_prefix.Foo();
}
```
The closure's return type is `Future<Foo>`. The old implementation defers
`Foo`, and incorrectly makes the return type `Future<dynamic>`. This may
break in places where the correct type is expected.
The new implementation will not defer `Foo`, and will place it in the main
output unit. If your intent is to defer it, then you need to ensure the
return type is not inferred to be `Foo`. For example, you can do so by
changing the code to a named closure with a declared type, or by ensuring
that the return expression has the type you want, like:
```dart
() async {
await deferred_prefix.loadLibrary();
return new deferred_prefix.Foo() as dynamic;
}
```
* Because the new implementation might require you to inspect and fix
your app, we exposed two temporary flags:
* `--report-invalid-deferred-types`: when provided, we will run
both the old and new algorithm and report where the issue was
detected.
* `--new-deferred-split`: enables the new algorithm.
#### Pub
#### Linter

View file

@ -78,6 +78,10 @@ class Flags {
static const String serverMode = '--server-mode';
static const String newDeferredSplit = '--new-deferred-split';
static const String reportInvalidInferredDeferredTypes =
'--report-invalid-deferred-types';
/// Flag for a combination of flags for 'production' mode.
static const String benchmarkingProduction = '--benchmarking-production';

View file

@ -67,10 +67,9 @@ abstract class Compiler {
/// Options provided from command-line arguments.
final CompilerOptions options;
/**
* If true, stop compilation after type inference is complete. Used for
* debugging and testing purposes only.
*/
// These internal flags are used to stop compilation after a specific phase.
// Used only for debugging and testing purposes only.
bool stopAfterClosedWorld = false;
bool stopAfterTypeInference = false;
/// Output provider from user of Compiler API.
@ -251,6 +250,7 @@ abstract class Compiler {
generateJavaScriptCode(results);
} else {
KernelResult result = await kernelLoader.load(uri);
reporter.log("Kernel load complete");
if (result == null) return;
if (compilationFailed && !options.generateCodeWithCompileTimeErrors) {
return;
@ -390,6 +390,7 @@ abstract class Compiler {
selfTask.measureSubtask("compileFromKernel", () {
JClosedWorld closedWorld = selfTask.measureSubtask("computeClosedWorld",
() => computeClosedWorld(rootLibraryUri, libraries));
if (stopAfterClosedWorld) return;
if (closedWorld != null) {
GlobalTypeInferenceResults globalInferenceResults =
performGlobalTypeInference(closedWorld);

View file

@ -377,6 +377,8 @@ Future<api.CompilationResult> compile(List<String> argv,
new OptionHandler(Flags.disableRtiOptimization, passThrough),
new OptionHandler(Flags.terse, passThrough),
new OptionHandler('--deferred-map=.+', passThrough),
new OptionHandler(Flags.newDeferredSplit, passThrough),
new OptionHandler(Flags.reportInvalidInferredDeferredTypes, passThrough),
new OptionHandler(Flags.dumpInfo, passThrough),
new OptionHandler('--disallow-unsafe-eval', ignoreOption),
new OptionHandler(Option.showPackageWarnings, passThrough),

View file

@ -130,6 +130,9 @@ abstract class DeferredLoadTask extends CompilerTask {
final Compiler compiler;
bool get disableProgramSplit => compiler.options.disableProgramSplit;
bool get newDeferredSplit => compiler.options.newDeferredSplit;
bool get reportInvalidInferredDeferredTypes =>
compiler.options.reportInvalidInferredDeferredTypes;
DeferredLoadTask(this.compiler) : super(compiler.measurer) {
_mainOutputUnit = new OutputUnit(true, 'main', new Set<ImportEntity>());
@ -193,7 +196,7 @@ abstract class DeferredLoadTask extends CompilerTask {
void addLiveInstanceMember(MemberEntity member) {
if (!compiler.resolutionWorldBuilder.isMemberUsed(member)) return;
if (!member.isInstanceMember) return;
dependencies.members.add(member);
dependencies.addMember(member);
_collectDirectMemberDependencies(member, dependencies);
}
@ -202,7 +205,7 @@ abstract class DeferredLoadTask extends CompilerTask {
elementEnvironment.forEachSupertype(cls, (InterfaceType type) {
_collectTypeDependencies(type, dependencies);
});
dependencies.classes.add(cls);
dependencies.addClass(cls);
}
/// Finds all elements and constants that [element] depends directly on.
@ -216,7 +219,7 @@ abstract class DeferredLoadTask extends CompilerTask {
elementEnvironment.getFunctionType(element), dependencies);
}
if (element.isStatic || element.isTopLevel || element.isConstructor) {
dependencies.members.add(element);
dependencies.addMember(element);
_collectDirectMemberDependencies(element, dependencies);
}
if (element is ConstructorEntity && element.isGenerativeConstructor) {
@ -243,21 +246,26 @@ abstract class DeferredLoadTask extends CompilerTask {
/// Recursively collects all the dependencies of [type].
void _collectTypeDependencies(DartType type, Dependencies dependencies) {
// TODO(het): we would like to separate out types that are only needed for
// rti from types that are needed for their members.
if (type is FunctionType) {
_collectFunctionTypeDependencies(type, dependencies);
} else if (type is TypedefType) {
type.typeArguments
.forEach((t) => _collectTypeDependencies(t, dependencies));
_collectTypeArgumentDependencies(type.typeArguments, dependencies);
_collectTypeDependencies(type.unaliased, dependencies);
} else if (type is InterfaceType) {
type.typeArguments
.forEach((t) => _collectTypeDependencies(t, dependencies));
dependencies.classes.add(type.element);
_collectTypeArgumentDependencies(type.typeArguments, dependencies);
// TODO(sigmund): when we are able to split classes from types in our
// runtime-type representation, this should track type.element as a type
// dependency instead.
dependencies.addClass(type.element);
}
}
void _collectTypeArgumentDependencies(
Iterable<DartType> typeArguments, Dependencies dependencies) {
if (typeArguments == null) return;
typeArguments.forEach((t) => _collectTypeDependencies(t, dependencies));
}
void _collectFunctionTypeDependencies(
FunctionType type, Dependencies dependencies) {
for (FunctionTypeVariable typeVariable in type.typeVariables) {
@ -285,7 +293,7 @@ abstract class DeferredLoadTask extends CompilerTask {
new WorldImpactVisitorImpl(visitStaticUse: (StaticUse staticUse) {
Entity usedEntity = staticUse.element;
if (usedEntity is MemberEntity) {
dependencies.members.add(usedEntity);
dependencies.addMember(usedEntity, staticUse.deferredImport);
} else {
assert(usedEntity is KLocalFunction,
failedAt(usedEntity, "Unexpected static use $staticUse."));
@ -298,19 +306,23 @@ abstract class DeferredLoadTask extends CompilerTask {
switch (staticUse.kind) {
case StaticUseKind.CONSTRUCTOR_INVOKE:
case StaticUseKind.CONST_CONSTRUCTOR_INVOKE:
_collectTypeDependencies(staticUse.type, dependencies);
// The receiver type of generative constructors is a dependency of
// the constructor (handled by `addMember` above) and not a
// dependency at the call site.
// Factory methods, on the other hand, are like static methods so
// the target type is not relevant.
// TODO(johnniwinther): Use rti need data to skip unneeded type
// arguments.
_collectTypeArgumentDependencies(
staticUse.type.typeArguments, dependencies);
break;
case StaticUseKind.INVOKE:
case StaticUseKind.CLOSURE_CALL:
case StaticUseKind.DIRECT_INVOKE:
// TODO(johnniwinther): Use rti need data to skip unneeded type
// arguments.
List<DartType> typeArguments = staticUse.typeArguments;
if (typeArguments != null) {
for (DartType typeArgument in typeArguments) {
_collectTypeDependencies(typeArgument, dependencies);
}
}
_collectTypeArgumentDependencies(
staticUse.typeArguments, dependencies);
break;
default:
}
@ -320,7 +332,8 @@ abstract class DeferredLoadTask extends CompilerTask {
case TypeUseKind.TYPE_LITERAL:
if (type.isInterfaceType) {
InterfaceType interface = type;
dependencies.classes.add(interface.element);
dependencies.addClass(
interface.element, typeUse.deferredImport);
}
break;
case TypeUseKind.INSTANTIATION:
@ -352,12 +365,8 @@ abstract class DeferredLoadTask extends CompilerTask {
}, visitDynamicUse: (DynamicUse dynamicUse) {
// TODO(johnniwinther): Use rti need data to skip unneeded type
// arguments.
List<DartType> typeArguments = dynamicUse.typeArguments;
if (typeArguments != null) {
for (DartType typeArgument in typeArguments) {
_collectTypeDependencies(typeArgument, dependencies);
}
}
_collectTypeArgumentDependencies(
dynamicUse.typeArguments, dependencies);
}),
DeferredLoadTask.IMPACT_USE);
}
@ -428,7 +437,8 @@ abstract class DeferredLoadTask extends CompilerTask {
Dependencies dependencies = new Dependencies();
_collectAllElementsAndConstantsResolvedFromClass(element, dependencies);
LibraryEntity library = element.library;
_processDependencies(library, dependencies, oldSet, newSet, queue);
_processDependencies(
library, dependencies, oldSet, newSet, queue, element);
} else {
queue.addClass(element, newSet);
}
@ -455,7 +465,8 @@ abstract class DeferredLoadTask extends CompilerTask {
_collectAllElementsAndConstantsResolvedFromMember(element, dependencies);
LibraryEntity library = element.library;
_processDependencies(library, dependencies, oldSet, newSet, queue);
_processDependencies(
library, dependencies, oldSet, newSet, queue, element);
} else {
queue.addMember(element, newSet);
}
@ -487,60 +498,111 @@ abstract class DeferredLoadTask extends CompilerTask {
/// same nodes we have already seen.
_shouldAddDeferredDependency(ImportSet newSet) => newSet.length <= 1;
void _fixDependencyInfo(DependencyInfo info, List<ImportEntity> imports,
String prefix, String name, Spannable context) {
var isDeferred = _isExplicitlyDeferred(imports);
if (isDeferred) {
if (!newDeferredSplit) {
info.isDeferred = true;
info.imports = imports;
}
if (reportInvalidInferredDeferredTypes) {
reporter.reportErrorMessage(context, MessageKind.GENERIC, {
'text': "$prefix '$name' is deferred but appears to be inferred as"
" a return type or a type parameter (dartbug.com/35311)."
});
}
}
}
// The following 3 methods are used to check whether the new deferred split
// algorithm and the old one match. Because of a soundness bug in the old
// algorithm the new algorithm can pull in a lot of code to the main output
// unit. This logic detects it and will make it easier for us to migrate code
// off it incrementally.
// Note: we only expect discrepancies on class-dependency-info due to how
// inferred types expose deferred types in type-variables and return types
// (Issue #35311). We added the other two methods to test our transition, but
// we don't expect to detect any mismatches there.
//
// TODO(sigmund): delete once the new implementation is on by default.
void _fixClassDependencyInfo(DependencyInfo info, ClassEntity cls,
LibraryEntity library, Spannable context) {
if (info.isDeferred) return;
if (newDeferredSplit && !reportInvalidInferredDeferredTypes) return;
var imports = classImportsTo(cls, library);
_fixDependencyInfo(info, imports, "Class", cls.name, context);
}
void _fixMemberDependencyInfo(DependencyInfo info, MemberEntity member,
LibraryEntity library, Spannable context) {
if (info.isDeferred || compiler.options.newDeferredSplit) return;
var imports = memberImportsTo(member, library);
_fixDependencyInfo(info, imports, "Member", member.name, context);
}
void _fixConstantDependencyInfo(DependencyInfo info, ConstantValue constant,
LibraryEntity library, Spannable context) {
if (info.isDeferred || compiler.options.newDeferredSplit) return;
if (constant is TypeConstantValue) {
var type = constant.representedType;
if (type is InterfaceType) {
var imports = classImportsTo(type.element, library);
_fixDependencyInfo(
info, imports, "Class (in constant) ", type.element.name, context);
} else if (type is TypedefType) {
var imports = typedefImportsTo(type.element, library);
_fixDependencyInfo(
info, imports, "Typedef ", type.element.name, context);
}
}
}
void _processDependencies(LibraryEntity library, Dependencies dependencies,
ImportSet oldSet, ImportSet newSet, WorkQueue queue) {
for (ClassEntity cls in dependencies.classes) {
Iterable<ImportEntity> imports = classImportsTo(cls, library);
if (_isExplicitlyDeferred(imports)) {
ImportSet oldSet, ImportSet newSet, WorkQueue queue, Spannable context) {
dependencies.classes.forEach((ClassEntity cls, DependencyInfo info) {
_fixClassDependencyInfo(info, cls, library, context);
if (info.isDeferred) {
if (_shouldAddDeferredDependency(newSet)) {
for (ImportEntity deferredImport in imports) {
for (ImportEntity deferredImport in info.imports) {
queue.addClass(cls, importSets.singleton(deferredImport));
}
}
} else {
_updateClassRecursive(cls, oldSet, newSet, queue);
}
}
});
for (MemberEntity member in dependencies.members) {
Iterable<ImportEntity> imports = memberImportsTo(member, library);
if (_isExplicitlyDeferred(imports)) {
dependencies.members.forEach((MemberEntity member, DependencyInfo info) {
_fixMemberDependencyInfo(info, member, library, context);
if (info.isDeferred) {
if (_shouldAddDeferredDependency(newSet)) {
for (ImportEntity deferredImport in imports) {
for (ImportEntity deferredImport in info.imports) {
queue.addMember(member, importSets.singleton(deferredImport));
}
}
} else {
_updateMemberRecursive(member, oldSet, newSet, queue);
}
}
});
for (Local localFunction in dependencies.localFunctions) {
_updateLocalFunction(localFunction, oldSet, newSet);
}
for (ConstantValue dependency in dependencies.constants) {
if (dependency is TypeConstantValue) {
var type = dependency.representedType;
var imports = const <ImportEntity>[];
if (type is InterfaceType) {
imports = classImportsTo(type.element, library);
} else if (type is TypedefType) {
imports = typedefImportsTo(type.element, library);
}
if (_isExplicitlyDeferred(imports)) {
if (_shouldAddDeferredDependency(newSet)) {
for (ImportEntity deferredImport in imports) {
queue.addConstant(
dependency, importSets.singleton(deferredImport));
}
dependencies.constants
.forEach((ConstantValue constant, DependencyInfo info) {
_fixConstantDependencyInfo(info, constant, library, context);
if (info.isDeferred) {
if (_shouldAddDeferredDependency(newSet)) {
for (ImportEntity deferredImport in info.imports) {
queue.addConstant(constant, importSets.singleton(deferredImport));
}
continue;
}
} else {
_updateConstantRecursive(constant, oldSet, newSet, queue);
}
_updateConstantRecursive(dependency, oldSet, newSet, queue);
}
});
}
/// Adds extra dependencies coming from mirror usage.
@ -1488,8 +1550,36 @@ String deferredPartFileName(CompilerOptions options, String name,
}
class Dependencies {
final Set<ClassEntity> classes = new Set<ClassEntity>();
final Set<MemberEntity> members = new Set<MemberEntity>();
final Map<ClassEntity, DependencyInfo> classes = {};
final Map<MemberEntity, DependencyInfo> members = {};
final Set<Local> localFunctions = new Set<Local>();
final Set<ConstantValue> constants = new Set<ConstantValue>();
final Map<ConstantValue, DependencyInfo> constants = {};
void addClass(ClassEntity cls, [ImportEntity import]) {
(classes[cls] ??= new DependencyInfo()).registerImport(import);
}
void addMember(MemberEntity m, [ImportEntity import]) {
(members[m] ??= new DependencyInfo()).registerImport(import);
}
void addConstant(ConstantValue c, [ImportEntity import]) {
(constants[c] ??= new DependencyInfo()).registerImport(import);
}
}
class DependencyInfo {
bool isDeferred = true;
List<ImportEntity> imports;
registerImport(ImportEntity import) {
if (!isDeferred) return;
// A null import represents a direct non-deferred dependency.
if (import != null) {
(imports ??= []).add(import);
} else {
imports = null;
isDeferred = false;
}
}
}

View file

@ -111,3 +111,34 @@ NullAwareExpression getNullAwareExpression(ir.TreeNode node) {
}
return null;
}
/// Check whether [node] is immediately guarded by a
/// [ir.CheckLibraryIsLoaded], and hence the node is a deferred access.
ir.LibraryDependency getDeferredImport(ir.TreeNode node) {
// Note: this code relies on the CFE generating the code as we expect it here.
// If one day we optimize away redundant CheckLibraryIsLoaded instructions,
// we'd need to derive this information directly from the CFE (See #35005),
ir.TreeNode parent = node.parent;
// TODO(sigmund): remove when CFE generates the correct tree (#35320). For
// instance, it currently generates
//
// let _ = check(prefix) in (prefix::field.property)
//
// instead of:
//
// (let _ = check(prefix) in prefix::field).property
if (node is ir.StaticGet) {
while (parent is ir.PropertyGet || parent is ir.MethodInvocation) {
parent = parent.parent;
}
}
if (parent is ir.Let) {
var initializer = parent.variable.initializer;
if (initializer is ir.CheckLibraryIsLoaded) {
return initializer.import;
}
}
return null;
}

View file

@ -11,6 +11,7 @@ import '../compiler.dart' show Compiler;
import '../constants/values.dart';
import '../deferred_load.dart';
import '../elements/entities.dart';
import '../ir/util.dart';
import 'element_map.dart';
class KernelDeferredLoadTask extends DeferredLoadTask {
@ -25,6 +26,7 @@ class KernelDeferredLoadTask extends DeferredLoadTask {
return measureSubtask('find-imports', () {
List<ImportEntity> imports = [];
ir.Library source = _elementMap.getLibraryNode(library);
if (!source.dependencies.any((d) => d.isDeferred)) return const [];
for (ir.LibraryDependency dependency in source.dependencies) {
if (dependency.isExport) continue;
if (!_isVisible(dependency.combinators, nodeName)) continue;
@ -55,7 +57,11 @@ class KernelDeferredLoadTask extends DeferredLoadTask {
Iterable<ImportEntity> memberImportsTo(
Entity element, LibraryEntity library) {
ir.Member node = _elementMap.getMemberNode(element);
return _findImportsTo(node, node.name.name, node.enclosingLibrary, library);
return _findImportsTo(
node is ir.Constructor ? node.enclosingClass : node,
node is ir.Constructor ? node.enclosingClass.name : node.name.name,
node.enclosingLibrary,
library);
}
@override
@ -138,7 +144,8 @@ class ConstantCollector extends ir.RecursiveVisitor {
ConstantValue constant =
elementMap.getConstantValue(node, requireConstant: required);
if (constant != null) {
dependencies.constants.add(constant);
dependencies.addConstant(
constant, elementMap.getImport(getDeferredImport(node)));
}
}

View file

@ -707,6 +707,7 @@ class KernelToElementMapImpl implements KernelToElementMap, IrToElementMap {
}
ImportEntity getImport(ir.LibraryDependency node) {
if (node == null) return null;
ir.Library library = node.parent;
KLibraryData data = libraries.getData(getLibraryInternal(library));
return data.imports[node];

View file

@ -252,10 +252,12 @@ class KernelImpactBuilder extends StaticTypeVisitor {
InterfaceType type = elementMap.createInterfaceType(
target.enclosingClass, node.arguments.types);
CallStructure callStructure = elementMap.getCallStructure(node.arguments);
ImportEntity deferredImport = elementMap.getImport(getDeferredImport(node));
impactBuilder.registerStaticUse(isConst
? new StaticUse.constConstructorInvoke(constructor, callStructure, type)
? new StaticUse.constConstructorInvoke(
constructor, callStructure, type, deferredImport)
: new StaticUse.typedConstructorInvoke(
constructor, callStructure, type));
constructor, callStructure, type, deferredImport));
if (type.typeArguments.any((DartType type) => !type.isDynamic)) {
impactBuilder.registerFeature(Feature.TYPE_VARIABLE_BOUNDS_CHECK);
}
@ -321,8 +323,13 @@ class KernelImpactBuilder extends StaticTypeVisitor {
_handleExtractTypeArguments(node, target, typeArguments);
return;
}
ImportEntity deferredImport =
elementMap.getImport(getDeferredImport(node));
impactBuilder.registerStaticUse(new StaticUse.staticInvoke(
target, elementMap.getCallStructure(node.arguments), typeArguments));
target,
elementMap.getCallStructure(node.arguments),
typeArguments,
deferredImport));
}
switch (elementMap.getForeignKind(node)) {
case ForeignKind.JS:
@ -382,17 +389,20 @@ class KernelImpactBuilder extends StaticTypeVisitor {
ir.Member target = node.target;
if (target is ir.Procedure && target.kind == ir.ProcedureKind.Method) {
FunctionEntity method = elementMap.getMethod(target);
impactBuilder.registerStaticUse(new StaticUse.staticTearOff(method));
impactBuilder.registerStaticUse(new StaticUse.staticTearOff(
method, elementMap.getImport(getDeferredImport(node))));
} else {
MemberEntity member = elementMap.getMember(target);
impactBuilder.registerStaticUse(new StaticUse.staticGet(member));
impactBuilder.registerStaticUse(new StaticUse.staticGet(
member, elementMap.getImport(getDeferredImport(node))));
}
}
@override
void handleStaticSet(ir.StaticSet node, ir.DartType valueType) {
MemberEntity member = elementMap.getMember(node.target);
impactBuilder.registerStaticUse(new StaticUse.staticSet(member));
impactBuilder.registerStaticUse(new StaticUse.staticSet(
member, elementMap.getImport(getDeferredImport(node))));
}
void handleSuperInvocation(ir.Name name, ir.Node arguments) {
@ -725,8 +735,9 @@ class KernelImpactBuilder extends StaticTypeVisitor {
@override
void handleTypeLiteral(ir.TypeLiteral node) {
impactBuilder.registerTypeUse(
new TypeUse.typeLiteral(elementMap.getDartType(node.type)));
ImportEntity deferredImport = elementMap.getImport(getDeferredImport(node));
impactBuilder.registerTypeUse(new TypeUse.typeLiteral(
elementMap.getDartType(node.type), deferredImport));
if (node.type is ir.FunctionType) {
ir.FunctionType functionType = node.type;
assert(functionType.typedef != null);

View file

@ -95,6 +95,25 @@ class CompilerOptions implements DiagnosticOptions {
/// libraries are subdivided.
Uri deferredMapUri;
/// Whether to apply the new deferred split fixes. The fixes improve on
/// performance and fix a soundness issue with inferred types. The latter will
/// move more code to the main output unit, because of that we are not
/// enabling the feature by default right away.
///
/// When [reportInvalidInferredDeferredTypes] shows no errors, we expect this
/// flag to produce the same or better results than the current unsound
/// implementation.
bool newDeferredSplit = false;
/// Show errors when a deferred type is inferred as a return type of a closure
/// or in a type parameter. Those cases cause the compiler today to behave
/// unsoundly by putting the code in a deferred output unit. In the future
/// when [newDeferredSplit] is on by default, those cases will be treated
/// soundly and will cause more code to be moved to the main output unit.
///
/// This flag is presented to help developers find and fix the affected code.
bool reportInvalidInferredDeferredTypes = false;
/// Whether to disable inlining during the backend optimizations.
// TODO(sigmund): negate, so all flags are positive
bool disableInlining = false;
@ -282,6 +301,9 @@ class CompilerOptions implements DiagnosticOptions {
_extractStringOption(options, '--build-id=', _UNDETERMINED_BUILD_ID)
..compileForServer = _hasOption(options, Flags.serverMode)
..deferredMapUri = _extractUriOption(options, '--deferred-map=')
..newDeferredSplit = _hasOption(options, Flags.newDeferredSplit)
..reportInvalidInferredDeferredTypes =
_hasOption(options, Flags.reportInvalidInferredDeferredTypes)
..fatalWarnings = _hasOption(options, Flags.fatalWarnings)
..terseDiagnostics = _hasOption(options, Flags.terse)
..suppressWarnings = _hasOption(options, Flags.suppressWarnings)

View file

@ -173,12 +173,22 @@ class StaticUse {
final int hashCode;
final InterfaceType type;
final CallStructure callStructure;
final ImportEntity deferredImport;
StaticUse.internal(Entity element, this.kind,
{this.type, this.callStructure, typeArgumentsHash: 0})
{this.type,
this.callStructure,
this.deferredImport,
typeArgumentsHash: 0})
: this.element = element,
this.hashCode = Hashing.objectsHash(
element, kind, type, typeArgumentsHash, callStructure);
this.hashCode = Hashing.listHash([
element,
kind,
type,
typeArgumentsHash,
callStructure,
deferredImport
]);
/// Short textual representation use for testing.
String get shortText {
@ -232,30 +242,33 @@ class StaticUse {
/// [callStructure].
factory StaticUse.staticInvoke(
FunctionEntity element, CallStructure callStructure,
[List<DartType> typeArguments]) {
[List<DartType> typeArguments, ImportEntity deferredImport]) {
assert(
element.isStatic || element.isTopLevel,
failedAt(
element,
"Static invoke element $element must be a top-level "
"or static method."));
return new GenericStaticUse(
element, StaticUseKind.INVOKE, callStructure, typeArguments);
return new GenericStaticUse(element, StaticUseKind.INVOKE, callStructure,
typeArguments, deferredImport);
}
/// Closurization of a static or top-level function [element].
factory StaticUse.staticTearOff(FunctionEntity element) {
factory StaticUse.staticTearOff(FunctionEntity element,
[ImportEntity deferredImport]) {
assert(
element.isStatic || element.isTopLevel,
failedAt(
element,
"Static tear-off element $element must be a top-level "
"or static method."));
return new StaticUse.internal(element, StaticUseKind.STATIC_TEAR_OFF);
return new StaticUse.internal(element, StaticUseKind.STATIC_TEAR_OFF,
deferredImport: deferredImport);
}
/// Read access of a static or top-level field or getter [element].
factory StaticUse.staticGet(MemberEntity element) {
factory StaticUse.staticGet(MemberEntity element,
[ImportEntity deferredImport]) {
assert(
element.isStatic || element.isTopLevel,
failedAt(
@ -266,11 +279,13 @@ class StaticUse {
element.isField || element.isGetter,
failedAt(element,
"Static get element $element must be a field or a getter."));
return new StaticUse.internal(element, StaticUseKind.GET);
return new StaticUse.internal(element, StaticUseKind.GET,
deferredImport: deferredImport);
}
/// Write access of a static or top-level field or setter [element].
factory StaticUse.staticSet(MemberEntity element) {
factory StaticUse.staticSet(MemberEntity element,
[ImportEntity deferredImport]) {
assert(
element.isStatic || element.isTopLevel,
failedAt(
@ -281,7 +296,8 @@ class StaticUse {
element.isField || element.isSetter,
failedAt(element,
"Static set element $element must be a field or a setter."));
return new StaticUse.internal(element, StaticUseKind.SET);
return new StaticUse.internal(element, StaticUseKind.SET,
deferredImport: deferredImport);
}
/// Invocation of the lazy initializer for a static or top-level field
@ -432,8 +448,11 @@ class StaticUse {
/// Constructor invocation of [element] with the given [callStructure] on
/// [type].
factory StaticUse.typedConstructorInvoke(ConstructorEntity element,
CallStructure callStructure, InterfaceType type) {
factory StaticUse.typedConstructorInvoke(
ConstructorEntity element,
CallStructure callStructure,
InterfaceType type,
ImportEntity deferredImport) {
assert(type != null,
failedAt(element, "No type provided for constructor invocation."));
assert(
@ -443,13 +462,18 @@ class StaticUse {
"Typed constructor invocation element $element "
"must be a constructor."));
return new StaticUse.internal(element, StaticUseKind.CONSTRUCTOR_INVOKE,
type: type, callStructure: callStructure);
type: type,
callStructure: callStructure,
deferredImport: deferredImport);
}
/// Constant constructor invocation of [element] with the given
/// [callStructure] on [type].
factory StaticUse.constConstructorInvoke(ConstructorEntity element,
CallStructure callStructure, InterfaceType type) {
factory StaticUse.constConstructorInvoke(
ConstructorEntity element,
CallStructure callStructure,
InterfaceType type,
ImportEntity deferredImport) {
assert(type != null,
failedAt(element, "No type provided for constructor invocation."));
assert(
@ -460,7 +484,9 @@ class StaticUse {
"must be a constructor."));
return new StaticUse.internal(
element, StaticUseKind.CONST_CONSTRUCTOR_INVOKE,
type: type, callStructure: callStructure);
type: type,
callStructure: callStructure,
deferredImport: deferredImport);
}
/// Constructor redirection to [element] on [type].
@ -563,9 +589,11 @@ class GenericStaticUse extends StaticUse {
final List<DartType> typeArguments;
GenericStaticUse(Entity entity, StaticUseKind kind,
CallStructure callStructure, this.typeArguments)
CallStructure callStructure, this.typeArguments,
[ImportEntity deferredImport])
: super.internal(entity, kind,
callStructure: callStructure,
deferredImport: deferredImport,
typeArgumentsHash: Hashing.listHash(typeArguments)) {
assert(
(callStructure?.typeArgumentCount ?? 0) == (typeArguments?.length ?? 0),
@ -599,11 +627,12 @@ class TypeUse {
final DartType type;
final TypeUseKind kind;
final int hashCode;
final ImportEntity deferredImport;
TypeUse.internal(DartType type, TypeUseKind kind)
TypeUse.internal(DartType type, TypeUseKind kind, [this.deferredImport])
: this.type = type,
this.kind = kind,
this.hashCode = Hashing.objectHash(type, Hashing.objectHash(kind));
this.hashCode = Hashing.objectsHash(type, kind, deferredImport);
/// Short textual representation use for testing.
String get shortText {
@ -678,8 +707,8 @@ class TypeUse {
}
/// [type] used as a type literal, like `foo() => T;`.
factory TypeUse.typeLiteral(DartType type) {
return new TypeUse.internal(type, TypeUseKind.TYPE_LITERAL);
factory TypeUse.typeLiteral(DartType type, ImportEntity deferredImport) {
return new TypeUse.internal(type, TypeUseKind.TYPE_LITERAL, deferredImport);
}
/// [type] used in an instantiation, like `new T();`.

View file

@ -15,7 +15,9 @@ import "../libs/static_separate_lib2.dart" deferred as lib2;
/*element: main:OutputUnit(main, {})*/
void main() {
asyncStart();
Expect.throws(/*OutputUnit(main, {})*/ () => new lib1.C());
Expect.throws(/*OutputUnit(main, {})*/ () {
new lib1.C();
});
lib1.loadLibrary().then(/*OutputUnit(main, {})*/ (_) {
lib2.loadLibrary().then(/*OutputUnit(main, {})*/ (_) {
print("HERE");

View file

@ -4,6 +4,6 @@
import "deferred_overlapping_lib3.dart";
/*class: C1:OutputUnit(1, {lib1})*/
/*element: C1.:OutputUnit(1, {lib1})*/
/*class: C1:OutputUnit(2, {lib1})*/
/*element: C1.:OutputUnit(2, {lib1})*/
class C1 extends C3 {}

View file

@ -2,6 +2,6 @@
// 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.
/*class: C3:OutputUnit(2, {lib1, lib2})*/
/*element: C3.:OutputUnit(2, {lib1, lib2})*/
/*class: C3:OutputUnit(1, {lib1, lib2})*/
/*element: C3.:OutputUnit(1, {lib1, lib2})*/
class C3 {}

View file

@ -177,8 +177,8 @@ main() {}
elementEnvironment.lookupConstructor(cls, '');
InterfaceType type = elementEnvironment.getRawType(cls);
WorldImpact impact = new WorldImpactBuilderImpl()
..registerStaticUse(new StaticUse.typedConstructorInvoke(
constructor, constructor.parameterStructure.callStructure, type));
..registerStaticUse(new StaticUse.typedConstructorInvoke(constructor,
constructor.parameterStructure.callStructure, type, null));
enqueuer.applyImpact(impact);
}