diff --git a/pkg/compiler/lib/src/common/resolution.dart b/pkg/compiler/lib/src/common/resolution.dart index 2f9b8283bc7..7ef8980aadc 100644 --- a/pkg/compiler/lib/src/common/resolution.dart +++ b/pkg/compiler/lib/src/common/resolution.dart @@ -114,4 +114,7 @@ class ResolutionCallbacks { /// Called when resolving the `Symbol` constructor. void onSymbolConstructor(Registry registry) {} + + /// Called when resolving a prefix or postfix expression. + void onIncDecOperation(Registry registry) {} } diff --git a/pkg/compiler/lib/src/compiler.dart b/pkg/compiler/lib/src/compiler.dart index db180aa93f4..249176adc45 100644 --- a/pkg/compiler/lib/src/compiler.dart +++ b/pkg/compiler/lib/src/compiler.dart @@ -848,11 +848,6 @@ abstract class Compiler implements DiagnosticListener { functionClass.ensureResolved(this); functionApplyMethod = functionClass.lookupLocalMember('apply'); - proxyConstant = - constants.getConstantValue( - resolver.constantCompiler.compileConstant( - coreLibrary.find('proxy'))); - if (preserveComments) { return libraryLoader.loadLibrary(Uris.dart_mirrors) .then((LibraryElement libraryElement) { @@ -862,6 +857,18 @@ abstract class Compiler implements DiagnosticListener { }).then((_) => backend.onLibrariesLoaded(loadedLibraries)); } + bool isProxyConstant(ConstantValue value) { + FieldElement field = coreLibrary.find('proxy'); + if (field == null) return false; + if (!enqueuer.resolution.hasBeenResolved(field)) return false; + if (proxyConstant == null) { + proxyConstant = + constants.getConstantValue( + resolver.constantCompiler.compileConstant(field)); + } + return proxyConstant == value; + } + Element findRequiredElement(LibraryElement library, String name) { var element = library.find(name); if (element == null) { diff --git a/pkg/compiler/lib/src/js_backend/backend.dart b/pkg/compiler/lib/src/js_backend/backend.dart index 638a1775d37..c7b70d2dfcd 100644 --- a/pkg/compiler/lib/src/js_backend/backend.dart +++ b/pkg/compiler/lib/src/js_backend/backend.dart @@ -2991,6 +2991,7 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { registerBackendInstantiation(backend.compiler.listClass, registry); registerBackendStaticInvocation(backend.getRuntimeTypeToString(), registry); registerBackendStaticInvocation(backend.getCreateRuntimeType(), registry); + needsInt(registry, 'Needed for accessing a type variable literal on this.'); } // TODO(johnniwinther): Maybe split this into [onAssertType] and [onTestType]. @@ -3051,7 +3052,7 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { registerBackendStaticInvocation( backend.getThrowAbstractClassInstantiationError(), registry); // Also register the types of the arguments passed to this method. - registerBackendInstantiation(backend.compiler.stringClass, registry); + needsString(registry, '// Needed to encode the message.'); } void onFallThroughError(Registry registry) { @@ -3068,8 +3069,10 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { assert(registry.isForResolution); registerBackendStaticInvocation(backend.getThrowNoSuchMethod(), registry); // Also register the types of the arguments passed to this method. - registerBackendInstantiation(backend.compiler.listClass, registry); - registerBackendInstantiation(backend.compiler.stringClass, registry); + needsList(registry, + 'Needed to encode the arguments for throw NoSuchMethodError.'); + needsString(registry, + 'Needed to encode the name for throw NoSuchMethodError.'); } void onThrowRuntimeError(Registry registry) { @@ -3094,8 +3097,12 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { backend.compiler.objectClass.lookupLocalMember( Identifiers.noSuchMethod_), registry); - registerBackendInstantiation(backend.compiler.listClass, registry); - registerBackendInstantiation(backend.compiler.stringClass, registry); + needsInt(registry, + 'Needed to encode the invocation kind of super.noSuchMethod.'); + needsList(registry, + 'Needed to encode the arguments of super.noSuchMethod.'); + needsString(registry, + 'Needed to encode the name of super.noSuchMethod.'); } void onMapLiteral(ResolutionRegistry registry, @@ -3125,6 +3132,29 @@ class JavaScriptResolutionCallbacks extends ResolutionCallbacks { registerBackendStaticInvocation( backend.compiler.symbolValidatedConstructor, registry); } + + /// Called when resolving a prefix or postfix expression. + void onIncDecOperation(Registry registry) { + needsInt(registry, 'Needed for the `+ 1` or `- 1` operation of ++/--.'); + } + + /// Helper for registering that `int` is needed. + void needsInt(Registry registry, String reason) { + // TODO(johnniwinther): Register [reason] for use in dump-info. + registerBackendInstantiation(backend.compiler.intClass, registry); + } + + /// Helper for registering that `List` is needed. + void needsList(Registry registry, String reason) { + // TODO(johnniwinther): Register [reason] for use in dump-info. + registerBackendInstantiation(backend.compiler.listClass, registry); + } + + /// Helper for registering that `String` is needed. + void needsString(Registry registry, String reason) { + // TODO(johnniwinther): Register [reason] for use in dump-info. + registerBackendInstantiation(backend.compiler.stringClass, registry); + } } /// Records that [constant] is used by the element behind [registry]. diff --git a/pkg/compiler/lib/src/resolution/members.dart b/pkg/compiler/lib/src/resolution/members.dart index 46294f78446..db8ae1c51e0 100644 --- a/pkg/compiler/lib/src/resolution/members.dart +++ b/pkg/compiler/lib/src/resolution/members.dart @@ -3422,6 +3422,7 @@ class ResolverVisitor extends MappingVisitor { ? new PrefixStructure(semantics, operator) : new PostfixStructure(semantics, operator); registry.registerSendStructure(node, sendStructure); + registry.registerIncDecOperation(); } else { Node rhs = node.arguments.head; visitExpression(rhs); diff --git a/pkg/compiler/lib/src/resolution/registry.dart b/pkg/compiler/lib/src/resolution/registry.dart index 6a407543753..c222afe129f 100644 --- a/pkg/compiler/lib/src/resolution/registry.dart +++ b/pkg/compiler/lib/src/resolution/registry.dart @@ -610,6 +610,10 @@ class ResolutionRegistry implements Registry { backend.resolutionCallbacks.onAsyncForIn(node, this); } + void registerIncDecOperation() { + backend.resolutionCallbacks.onIncDecOperation(this); + } + void registerTryStatement() { mapping.containsTryStatement = true; } diff --git a/pkg/compiler/lib/src/resolution/resolution.dart b/pkg/compiler/lib/src/resolution/resolution.dart index cf53b0854d6..a137493de37 100644 --- a/pkg/compiler/lib/src/resolution/resolution.dart +++ b/pkg/compiler/lib/src/resolution/resolution.dart @@ -598,7 +598,7 @@ class ResolverTask extends CompilerTask { metadata.ensureResolved(compiler); ConstantValue value = compiler.constants.getConstantValue(metadata.constant); - if (!element.isProxy && value == compiler.proxyConstant) { + if (!element.isProxy && compiler.isProxyConstant(value)) { element.isProxy = true; } } diff --git a/pkg/compiler/lib/src/ssa/codegen.dart b/pkg/compiler/lib/src/ssa/codegen.dart index 3739c838f6f..3e25aa98726 100644 --- a/pkg/compiler/lib/src/ssa/codegen.dart +++ b/pkg/compiler/lib/src/ssa/codegen.dart @@ -2694,7 +2694,7 @@ class SsaCodeGenerator implements HVisitor, HBlockInformationVisitor { checkString(input, '!==', input.sourceInformation); return pop(); } - compiler.internalError(input, 'Unexpected check.'); + compiler.internalError(input, 'Unexpected check: $checkedType.'); return null; } diff --git a/pkg/compiler/lib/src/ssa/nodes.dart b/pkg/compiler/lib/src/ssa/nodes.dart index dfcc4d36758..fdaef017950 100644 --- a/pkg/compiler/lib/src/ssa/nodes.dart +++ b/pkg/compiler/lib/src/ssa/nodes.dart @@ -882,6 +882,32 @@ abstract class HInstruction implements Spannable { return instructionType.isEmpty && !instructionType.isNullable; } + /// Returns `true` if [typeMask] contains [cls]. + static bool containsType( + TypeMask typeMask, + ClassElement cls, + ClassWorld classWorld) { + return classWorld.isInstantiated(cls) && typeMask.contains(cls, classWorld); + } + + /// Returns `true` if [typeMask] contains only [cls]. + static bool containsOnlyType( + TypeMask typeMask, + ClassElement cls, + ClassWorld classWorld) { + return classWorld.isInstantiated(cls) && + typeMask.containsOnly(cls); + } + + /// Returns `true` if [typeMask] is an instance of [cls]. + static bool isInstanceOf( + TypeMask typeMask, + ClassElement cls, + ClassWorld classWorld) { + return classWorld.isInstantiated(cls) && + typeMask.satisfies(cls, classWorld); + } + bool canBePrimitive(Compiler compiler) { return canBePrimitiveNumber(compiler) || canBePrimitiveArray(compiler) @@ -895,27 +921,28 @@ abstract class HInstruction implements Spannable { JavaScriptBackend backend = compiler.backend; // TODO(sra): It should be possible to test only jsDoubleClass and // jsUInt31Class, since all others are superclasses of these two. - return instructionType.contains(backend.jsNumberClass, classWorld) - || instructionType.contains(backend.jsIntClass, classWorld) - || instructionType.contains(backend.jsPositiveIntClass, classWorld) - || instructionType.contains(backend.jsUInt32Class, classWorld) - || instructionType.contains(backend.jsUInt31Class, classWorld) - || instructionType.contains(backend.jsDoubleClass, classWorld); + return containsType(instructionType, backend.jsNumberClass, classWorld) + || containsType(instructionType, backend.jsIntClass, classWorld) + || containsType(instructionType, backend.jsPositiveIntClass, classWorld) + || containsType(instructionType, backend.jsUInt32Class, classWorld) + || containsType(instructionType, backend.jsUInt31Class, classWorld) + || containsType(instructionType, backend.jsDoubleClass, classWorld); } bool canBePrimitiveBoolean(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.contains(backend.jsBoolClass, classWorld); + return containsType(instructionType, backend.jsBoolClass, classWorld); } bool canBePrimitiveArray(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.contains(backend.jsArrayClass, classWorld) - || instructionType.contains(backend.jsFixedArrayClass, classWorld) - || instructionType.contains(backend.jsExtendableArrayClass, classWorld) - || instructionType.contains( + return containsType(instructionType, backend.jsArrayClass, classWorld) + || containsType(instructionType, backend.jsFixedArrayClass, classWorld) + || containsType( + instructionType, backend.jsExtendableArrayClass, classWorld) + || containsType(instructionType, backend.jsUnmodifiableArrayClass, classWorld); } @@ -923,37 +950,43 @@ abstract class HInstruction implements Spannable { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; return instructionType.containsOnlyString(classWorld) - || instructionType.satisfies(backend.jsIndexableClass, classWorld); + || isInstanceOf(instructionType, backend.jsIndexableClass, classWorld); } bool isFixedArray(Compiler compiler) { + ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; // TODO(sra): Recognize the union of these types as well. - return instructionType.containsOnly(backend.jsFixedArrayClass) - || instructionType.containsOnly(backend.jsUnmodifiableArrayClass); + return containsOnlyType( + instructionType, backend.jsFixedArrayClass, classWorld) + || containsOnlyType( + instructionType, backend.jsUnmodifiableArrayClass, classWorld); } bool isExtendableArray(Compiler compiler) { + ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.containsOnly(backend.jsExtendableArrayClass); + return containsOnlyType( + instructionType, backend.jsExtendableArrayClass, classWorld); } bool isMutableArray(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.satisfies(backend.jsMutableArrayClass, classWorld); + return isInstanceOf( + instructionType, backend.jsMutableArrayClass, classWorld); } bool isReadableArray(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.satisfies(backend.jsArrayClass, classWorld); + return isInstanceOf(instructionType, backend.jsArrayClass, classWorld); } bool isMutableIndexable(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.satisfies( + return isInstanceOf(instructionType, backend.jsMutableIndexableClass, classWorld); } @@ -962,7 +995,7 @@ abstract class HInstruction implements Spannable { bool canBePrimitiveString(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.contains(backend.jsStringClass, classWorld); + return containsType(instructionType, backend.jsStringClass, classWorld); } bool isInteger(Compiler compiler) { @@ -975,27 +1008,28 @@ abstract class HInstruction implements Spannable { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; return !instructionType.isNullable - && instructionType.satisfies(backend.jsUInt32Class, classWorld); + && isInstanceOf(instructionType, backend.jsUInt32Class, classWorld); } bool isUInt31(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; return !instructionType.isNullable - && instructionType.satisfies(backend.jsUInt31Class, classWorld); + && isInstanceOf(instructionType, backend.jsUInt31Class, classWorld); } bool isPositiveInteger(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return !instructionType.isNullable - && instructionType.satisfies(backend.jsPositiveIntClass, classWorld); + return !instructionType.isNullable && + isInstanceOf(instructionType, backend.jsPositiveIntClass, classWorld); } bool isPositiveIntegerOrNull(Compiler compiler) { ClassWorld classWorld = compiler.world; JavaScriptBackend backend = compiler.backend; - return instructionType.satisfies(backend.jsPositiveIntClass, classWorld); + return isInstanceOf( + instructionType, backend.jsPositiveIntClass, classWorld); } bool isIntegerOrNull(Compiler compiler) { diff --git a/pkg/compiler/lib/src/ssa/optimize.dart b/pkg/compiler/lib/src/ssa/optimize.dart index 795b510fbc3..eeb12c5e0ad 100644 --- a/pkg/compiler/lib/src/ssa/optimize.dart +++ b/pkg/compiler/lib/src/ssa/optimize.dart @@ -268,9 +268,11 @@ class SsaInstructionSimplifier extends HBaseVisitor ClassWorld classWorld = compiler.world; TypeMask resultType = backend.positiveIntType; // If we already have computed a more specific type, keep that type. - if (actualType.satisfies(backend.jsUInt31Class, classWorld)) { + if (HInstruction.isInstanceOf( + actualType, backend.jsUInt31Class, classWorld)) { resultType = backend.uint31Type; - } else if (actualType.satisfies(backend.jsUInt32Class, classWorld)) { + } else if (HInstruction.isInstanceOf( + actualType, backend.jsUInt32Class, classWorld)) { resultType = backend.uint32Type; } HFieldGet result = new HFieldGet( diff --git a/tests/compiler/dart2js/minimal_resolution_test.dart b/tests/compiler/dart2js/minimal_resolution_test.dart new file mode 100644 index 00000000000..ca61f804884 --- /dev/null +++ b/tests/compiler/dart2js/minimal_resolution_test.dart @@ -0,0 +1,27 @@ +// Copyright (c) 2015, 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. + +// Test that elements are not needlessly required by dart2js. + +import 'package:async_helper/async_helper.dart'; +import 'package:compiler/src/compiler.dart'; +import 'package:expect/expect.dart'; +import 'memory_compiler.dart'; + +main() { + asyncTest(() async { + await analyze('main() {}'); + await analyze('main() => proxy;', proxyConstant: true); + }); +} + +analyze(String code, + {bool proxyConstant: false}) async { + CompilationResult result = await runCompiler( + memorySourceFiles: {'main.dart': code}, + options: ['--analyze-only']); + Expect.isTrue(result.isSuccess); + Compiler compiler = result.compiler; + Expect.equals(proxyConstant, compiler.proxyConstant != null); +}