blob: 653135f20ff32b6c5bdfc3a0725cacef0b39fcda [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 'package:compiler/src/apiimpl.dart' show
Compiler;
import 'package:compiler/src/constants/values.dart' show
ConstantValue,
TypeConstantValue;
import 'package:compiler/src/elements/elements.dart' show
Element,
Elements;
import 'package:compiler/src/js_backend/js_backend.dart' show
JavaScriptBackend;
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,
options: ['--enable-experimental-mirrors']);
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 = 431;
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.
r'get$field']; // The (getter) name of Foo.field.
// TODO(ahe): Check for the following names, currently they are not being
// recorded correctly, but are being emitted.
[
'Foo_staticMethod', // The name of Foo.staticMethod.
r'instanceMethod$0']; // The name of Foo.instanceMethod.
// We always include the names of some native classes.
List<Element> nativeClasses = [
compiler.intClass, compiler.doubleClass, compiler.numClass,
compiler.stringClass, compiler.boolClass, compiler.nullClass,
compiler.listClass
];
JavaScriptBackend backend = compiler.backend;
Iterable<String> nativeNames = nativeClasses.map(backend.namer.className);
expectedNames.addAll(nativeNames);
Set recordedNames = new Set()
..addAll(backend.emitter.oldEmitter.recordedMangledNames)
..addAll(backend.emitter.oldEmitter.mangledFieldNames.keys)
..addAll(backend.emitter.oldEmitter.mangledGlobalFieldNames.keys);
Expect.setEquals(new Set.from(expectedNames), recordedNames);
for (var library in compiler.libraryLoader.libraries) {
library.forEachLocalMember((member) {
if (library == compiler.mainApp && member.name == 'Foo') {
Expect.isTrue(
compiler.backend.isAccessibleByReflection(member), '$member');
member.forEachLocalMember((classMember) {
Expect.isTrue(
compiler.backend.isAccessibleByReflection(classMember),
'$classMember');
});
} else {
Expect.isFalse(
compiler.backend.isAccessibleByReflection(member), '$member');
}
});
}
// There should at least be one metadata constant:
// 1. The constructed constant for 'MirrorsUsed'.
Expect.isTrue(backend.metadataConstants.length >= 1);
Set<ConstantValue> compiledConstants = backend.constants.compiledConstants;
// Make sure that most of the metadata constants aren't included in the
// generated code.
for (var dependency in backend.metadataConstants) {
ConstantValue constant = dependency.constant;
Expect.isFalse(compiledConstants.contains(constant),
constant.toStructuredString());
}
// 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 (ConstantValue constant in compiledConstants) {
if (constant is TypeConstantValue &&
'${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': """
// The repeated constant value for symbols and targets used to crash dart2js in
// host-checked mode, and could potentially lead to other problems.
@MirrorsUsed(symbols: 'Foo', targets: '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));
}
""",
};