blob: 25ac037891a260802cce735d594fc4b32aa3d864 [file] [log] [blame]
// Copyright (c) 2013, 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 the @MirrorsUsed annotation suppress hints and that only
/// requested elements are retained for reflection.
library dart2js.test.mirrors_used_test;
import 'package:expect/expect.dart';
import "package:async_helper/async_helper.dart";
import 'memory_compiler.dart' show
compilerFor;
import '../../../sdk/lib/_internal/compiler/implementation/apiimpl.dart' show
Compiler;
import '../../../sdk/lib/_internal/compiler/implementation/dart2jslib.dart' show
Constant,
SourceString,
TypeConstant;
import
'../../../sdk/lib/_internal/compiler/implementation/elements/elements.dart'
show
Elements;
void expectOnlyVerboseInfo(Uri uri, int begin, int end, String message, kind) {
if (kind.name == 'verbose info') {
print(message);
return;
}
if (message.contains('methods retained for use by dart:mirrors out of')) {
print(message);
return;
}
if (kind.name == 'info') return;
// TODO(aprelev@gmail.com): Remove once dartbug.com/13907 is fixed.
if (message.contains("Warning: 'typedef' not allowed here")) return;
throw '$uri:$begin:$end: $kind: $message';
}
void main() {
Compiler compiler = compilerFor(
MEMORY_SOURCE_FILES, diagnosticHandler: expectOnlyVerboseInfo);
asyncTest(() => compiler.runCompiler(Uri.parse('memory:main.dart')).then((_) {
print('');
List generatedCode =
Elements.sortedByPosition(compiler.enqueuer.codegen.generatedCode.keys);
for (var element in generatedCode) {
print(element);
}
print('');
// This assertion can fail for two reasons:
// 1. Too many elements retained for reflection.
// 2. Some code was refactored, and there are more methods.
// Either situation could be problematic, but in situation 2, it is often
// acceptable to increase [expectedMethodCount] a little.
int expectedMethodCount = 326;
Expect.isTrue(
generatedCode.length <= expectedMethodCount,
'Too many compiled methods: '
'${generatedCode.length} > $expectedMethodCount');
// The following names should be retained:
List expectedNames = [
'Foo', // The name of class Foo.
r'Foo$', // The name of class Foo's constructor.
'Foo_staticMethod', // The name of Foo.staticMethod.
r'get$field', // The (getter) name of Foo.field.
r'instanceMethod$0']; // The name of Foo.instanceMethod.
Set recordedNames = new Set()
..addAll(compiler.backend.emitter.recordedMangledNames)
..addAll(compiler.backend.emitter.mangledFieldNames.keys)
..addAll(compiler.backend.emitter.mangledGlobalFieldNames.keys);
Expect.setEquals(new Set.from(expectedNames), recordedNames);
for (var library in compiler.libraries.values) {
library.forEachLocalMember((member) {
if (library == compiler.mainApp
&& member.name == const SourceString('Foo')) {
Expect.isTrue(
compiler.backend.isNeededForReflection(member), '$member');
member.forEachLocalMember((classMember) {
Expect.isTrue(
compiler.backend.isNeededForReflection(classMember),
'$classMember');
});
} else {
Expect.isFalse(
compiler.backend.isNeededForReflection(member), '$member');
}
});
}
// There should at least be one metadata constant:
// 1. The constructed constant for 'MirrorsUsed'.
Expect.isTrue(compiler.backend.metadataConstants.length >= 1);
// Make sure that most of the metadata constants aren't included in the
// generated code.
for (var dependency in compiler.backend.metadataConstants) {
Constant constant = dependency.constant;
Expect.isFalse(
compiler.constantHandler.compiledConstants.contains(constant),
'$constant');
}
// The type literal 'Foo' is both used as metadata, and as a plain value in
// the program. Make sure that it isn't duplicated.
int fooConstantCount = 0;
for (Constant constant in compiler.constantHandler.compiledConstants) {
if (constant is TypeConstant && '${constant.representedType}' == 'Foo') {
fooConstantCount++;
}
}
Expect.equals(
1, fooConstantCount,
"The type literal 'Foo' is duplicated or missing.");
}));
}
const MEMORY_SOURCE_FILES = const <String, String> {
'main.dart': """
@MirrorsUsed(targets: const [Foo], override: '*')
import 'dart:mirrors';
import 'library.dart';
class Foo {
int field;
instanceMethod() {}
static staticMethod() {}
}
unusedFunction() {
}
main() {
useReflect(Foo);
}
""",
'library.dart': """
library lib;
import 'dart:mirrors';
useReflect(type) {
print(new Symbol('Foo'));
print(MirrorSystem.getName(reflectClass(type).owner.qualifiedName));
}
""",
};