mirror of
https://github.com/dart-lang/sdk
synced 2024-10-04 16:14:50 +00:00
Delete dart_backend from compiler.
R=sra@google.com Review URL: https://codereview.chromium.org/2213673002 .
This commit is contained in:
parent
3c38f96a0a
commit
0eee4027a6
|
@ -46,6 +46,9 @@
|
|||
* Eliminate some false negatives when determining whether global executables
|
||||
are on the user's executable path.
|
||||
|
||||
* dart2dart (aka `dart2js --output-type=dart`) has been removed (this was
|
||||
deprecated in Dart 1.11)
|
||||
|
||||
[Flutter]: https://flutter.io/
|
||||
|
||||
## 1.18.1 - 2016-08-02
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
'<!@(["python", "tools/list_files.py",'
|
||||
'"dart$",'
|
||||
'"sdk/lib"])',
|
||||
'sdk/lib/dart2dart.platform',
|
||||
'sdk/lib/dart_client.platform',
|
||||
'sdk/lib/dart_server.platform',
|
||||
'sdk/lib/dart_shared.platform',
|
||||
|
|
|
@ -27,7 +27,6 @@ import 'common.dart';
|
|||
import 'compile_time_constants.dart';
|
||||
import 'constants/values.dart';
|
||||
import 'core_types.dart' show CoreClasses, CoreTypes;
|
||||
import 'dart_backend/dart_backend.dart' as dart_backend;
|
||||
import 'dart_types.dart' show DartType, DynamicType, InterfaceType, Types;
|
||||
import 'deferred_load.dart' show DeferredLoadTask;
|
||||
import 'diagnostics/code_location.dart';
|
||||
|
@ -302,19 +301,13 @@ abstract class Compiler implements LibraryLoaderListener {
|
|||
|
||||
if (makeBackend != null) {
|
||||
backend = makeBackend(this);
|
||||
} else if (options.emitJavaScript) {
|
||||
} else {
|
||||
js_backend.JavaScriptBackend jsBackend = new js_backend.JavaScriptBackend(
|
||||
this,
|
||||
generateSourceMap: options.generateSourceMap,
|
||||
useStartupEmitter: options.useStartupEmitter,
|
||||
useNewSourceInfo: options.useNewSourceInfo);
|
||||
backend = jsBackend;
|
||||
} else {
|
||||
backend = new dart_backend.DartBackend(this, options.strips,
|
||||
multiFile: options.dart2dartMultiFile);
|
||||
if (options.dumpInfo) {
|
||||
throw new ArgumentError('--dump-info is not supported for dart2dart.');
|
||||
}
|
||||
}
|
||||
|
||||
if (options.dumpInfo && options.useStartupEmitter) {
|
||||
|
|
|
@ -115,8 +115,6 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
List<String> explicitOutputArguments = <String>[];
|
||||
bool wantHelp = false;
|
||||
bool wantVersion = false;
|
||||
String outputLanguage = 'JavaScript';
|
||||
bool stripArgumentSet = false;
|
||||
bool analyzeOnly = false;
|
||||
bool analyzeAll = false;
|
||||
bool resolveOnly = false;
|
||||
|
@ -173,20 +171,10 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
optionsImplyCompilation.add(argument);
|
||||
if (argument == '--output-type=dart' ||
|
||||
argument == '--output-type=dart-multi') {
|
||||
outputLanguage = OUTPUT_LANGUAGE_DART;
|
||||
if (explicitOutputArguments.isNotEmpty) {
|
||||
out = currentDirectory.resolve('out.dart');
|
||||
sourceMapOut = currentDirectory.resolve('out.dart.map');
|
||||
}
|
||||
diagnosticHandler(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
"--output-type=dart is deprecated. It will remain available "
|
||||
"in Dart 1.11, but will be removed in Dart 1.12.",
|
||||
api.Diagnostic.WARNING);
|
||||
helpAndFail(
|
||||
"--output-type=dart is no longer supported. It was deprecated "
|
||||
"since Dart 1.11 and removed in Dart 1.19.");
|
||||
}
|
||||
passThrough(argument);
|
||||
}
|
||||
|
||||
void setResolutionInput(String argument) {
|
||||
|
@ -208,14 +196,14 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
return filenames.join("\n");
|
||||
}
|
||||
|
||||
void implyCompilation(String argument) {
|
||||
implyCompilation(String argument) {
|
||||
optionsImplyCompilation.add(argument);
|
||||
passThrough(argument);
|
||||
}
|
||||
|
||||
void setStrip(String argument) {
|
||||
stripArgumentSet = true;
|
||||
implyCompilation(argument);
|
||||
setStrip(String argument) {
|
||||
helpAndFail("Option '--force-strip' is not in use now that"
|
||||
"--output-type=dart is no longer supported.");
|
||||
}
|
||||
|
||||
void setAnalyzeOnly(String argument) {
|
||||
|
@ -326,6 +314,7 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
new OptionHandler(Flags.fatalWarnings, passThrough),
|
||||
new OptionHandler(
|
||||
Flags.suppressHints, (_) => diagnosticHandler.showHints = false),
|
||||
// TODO(sigmund): remove entirely after Dart 1.20
|
||||
new OptionHandler(
|
||||
'--output-type=dart|--output-type=dart-multi|--output-type=js',
|
||||
setOutputType),
|
||||
|
@ -370,7 +359,7 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
new OptionHandler(Flags.disableTypeInference, implyCompilation),
|
||||
new OptionHandler(Flags.terse, passThrough),
|
||||
new OptionHandler('--deferred-map=.+', implyCompilation),
|
||||
new OptionHandler(Flags.dumpInfo, setDumpInfo),
|
||||
new OptionHandler(Flags.dumpInfo, implyCompilation),
|
||||
new OptionHandler(
|
||||
'--disallow-unsafe-eval', (_) => hasDisallowUnsafeEval = true),
|
||||
new OptionHandler(Option.showPackageWarnings, passThrough),
|
||||
|
@ -429,10 +418,6 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
" '$precompiledName'.");
|
||||
}
|
||||
|
||||
if (outputLanguage != OUTPUT_LANGUAGE_DART && stripArgumentSet) {
|
||||
helpAndFail("Option '--force-strip' may only be used with "
|
||||
"'--output-type=dart'.");
|
||||
}
|
||||
if (arguments.isEmpty) {
|
||||
helpAndFail('No Dart file specified.');
|
||||
}
|
||||
|
@ -487,10 +472,6 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
"in combination with the '${Flags.analyzeOnly}' option.");
|
||||
}
|
||||
}
|
||||
if (dumpInfo && outputLanguage == OUTPUT_LANGUAGE_DART) {
|
||||
helpAndFail("Option '${Flags.dumpInfo}' is not supported in "
|
||||
"combination with the '--output-type=dart' option.");
|
||||
}
|
||||
|
||||
options.add('--out=$out');
|
||||
options.add('--source-map=$sourceMapOut');
|
||||
|
@ -511,11 +492,11 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
diagnosticHandler
|
||||
.info('Compiled ${inputProvider.dartCharactersRead} characters Dart '
|
||||
'-> ${outputProvider.totalCharactersWritten} characters '
|
||||
'$outputLanguage in '
|
||||
'JavaScript in '
|
||||
'${relativize(currentDirectory, out, Platform.isWindows)}');
|
||||
if (diagnosticHandler.verbose) {
|
||||
String input = uriPathToNative(arguments[0]);
|
||||
print('Dart file ($input) compiled to $outputLanguage.');
|
||||
print('Dart file ($input) compiled to JavaScript.');
|
||||
print('Wrote the following files:');
|
||||
for (String filename in outputProvider.allOutputFiles) {
|
||||
print(" $filename");
|
||||
|
@ -523,7 +504,7 @@ Future<api.CompilationResult> compile(List<String> argv) {
|
|||
} else if (explicitOutputArguments.isNotEmpty) {
|
||||
String input = uriPathToNative(arguments[0]);
|
||||
String output = relativize(currentDirectory, out, Platform.isWindows);
|
||||
print('Dart file ($input) compiled to $outputLanguage: $output');
|
||||
print('Dart file ($input) compiled to JavaScript: $output');
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -672,9 +653,6 @@ Supported options:
|
|||
The following options are only used for compiler development and may
|
||||
be removed in a future version:
|
||||
|
||||
--output-type=dart
|
||||
Output Dart code instead of JavaScript.
|
||||
|
||||
--throw-on-error
|
||||
Throw an exception if a compile-time error is detected.
|
||||
|
||||
|
@ -703,8 +681,6 @@ be removed in a future version:
|
|||
Generates an out.info.json file with information about the generated code.
|
||||
You can inspect the generated file with the viewer at:
|
||||
https://dart-lang.github.io/dump-info-visualizer/
|
||||
This feature is currently not supported in combination with the
|
||||
'--output-type=dart' option.
|
||||
|
||||
--generate-code-with-compile-time-errors
|
||||
Generates output even if the program contains compile-time errors. Use the
|
||||
|
|
|
@ -1,546 +0,0 @@
|
|||
// 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.
|
||||
|
||||
part of dart_backend;
|
||||
|
||||
// TODO(ahe): This class is simply wrong. This backend should use
|
||||
// elements when it can, not AST nodes. Perhaps a [Map<Element,
|
||||
// TreeElements>] is what is needed.
|
||||
class ElementAst {
|
||||
final Node ast;
|
||||
final TreeElements treeElements;
|
||||
|
||||
ElementAst(this.ast, this.treeElements);
|
||||
}
|
||||
|
||||
class DartBackend extends Backend {
|
||||
final List<CompilerTask> tasks;
|
||||
final bool stripAsserts;
|
||||
|
||||
bool get supportsReflection => true;
|
||||
|
||||
// TODO(zarah) Maybe change this to a command-line option.
|
||||
// Right now, it is set by the tests.
|
||||
bool useMirrorHelperLibrary = false;
|
||||
|
||||
/// Updated to a [MirrorRenamerImpl] instance if the [useMirrorHelperLibrary]
|
||||
/// field is set and mirror are needed.
|
||||
MirrorRenamer mirrorRenamer = const MirrorRenamer();
|
||||
|
||||
final DartOutputter outputter;
|
||||
|
||||
// Used in test.
|
||||
PlaceholderRenamer get placeholderRenamer => outputter.renamer;
|
||||
Map<ClassNode, List<Node>> get memberNodes => outputter.output.memberNodes;
|
||||
|
||||
ConstantSystem get constantSystem {
|
||||
return constantCompilerTask.constantCompiler.constantSystem;
|
||||
}
|
||||
|
||||
BackendConstantEnvironment get constants => constantCompilerTask;
|
||||
|
||||
DartConstantTask constantCompilerTask;
|
||||
|
||||
DartImpactTransformer impactTransformer;
|
||||
|
||||
final Set<ClassElement> usedTypeLiterals = new Set<ClassElement>();
|
||||
|
||||
/// The set of visible platform classes that are implemented by instantiated
|
||||
/// user classes.
|
||||
final Set<ClassElement> _userImplementedPlatformClasses =
|
||||
new Set<ClassElement>();
|
||||
|
||||
bool enableCodegenWithErrorsIfSupported(Spannable node) {
|
||||
reporter.reportHintMessage(node, MessageKind.GENERIC, {
|
||||
'text': "Generation of code with compile time errors is not "
|
||||
"supported for dart2dart."
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether it is safe to remove type declarations from variables,
|
||||
* functions parameters. It becomes not safe if:
|
||||
* 1) TypeError is used somewhere in the code,
|
||||
* 2) The code has typedefs in right hand side of IS checks,
|
||||
* 3) The code has classes which extend typedefs, have type arguments typedefs
|
||||
* or type variable bounds typedefs.
|
||||
* These restrictions can be less strict.
|
||||
*/
|
||||
bool isSafeToRemoveTypeDeclarations(
|
||||
Map<ClassElement, Iterable<Element>> classMembers) {
|
||||
ClassElement typeErrorElement = compiler.coreLibrary.find('TypeError');
|
||||
if (classMembers.containsKey(typeErrorElement) ||
|
||||
compiler.resolverWorld.isChecks
|
||||
.any((DartType type) => type.element == typeErrorElement)) {
|
||||
return false;
|
||||
}
|
||||
Set<DartType> processedTypes = new Set<DartType>();
|
||||
List<DartType> workQueue = new List<DartType>();
|
||||
workQueue
|
||||
.addAll(classMembers.keys.map((classElement) => classElement.thisType));
|
||||
workQueue.addAll(compiler.resolverWorld.isChecks);
|
||||
|
||||
while (!workQueue.isEmpty) {
|
||||
DartType type = workQueue.removeLast();
|
||||
if (processedTypes.contains(type)) continue;
|
||||
processedTypes.add(type);
|
||||
if (type is FunctionType) return false;
|
||||
if (type is TypedefType) return false;
|
||||
if (type is InterfaceType) {
|
||||
InterfaceType interfaceType = type;
|
||||
// Check all type arguments.
|
||||
interfaceType.typeArguments.forEach(workQueue.add);
|
||||
ClassElement element = type.element;
|
||||
// Check all supertypes.
|
||||
if (element.allSupertypes != null) {
|
||||
element.allSupertypes.forEach(workQueue.add);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
DartBackend(Compiler compiler, List<String> strips, {bool multiFile})
|
||||
: tasks = <CompilerTask>[],
|
||||
stripAsserts = strips.indexOf('asserts') != -1,
|
||||
constantCompilerTask = new DartConstantTask(compiler),
|
||||
outputter = new DartOutputter(
|
||||
compiler.reporter, compiler.outputProvider,
|
||||
forceStripTypes: strips.indexOf('types') != -1,
|
||||
multiFile: multiFile,
|
||||
enableMinification: compiler.options.enableMinification),
|
||||
super(compiler) {
|
||||
impactTransformer = new DartImpactTransformer(this);
|
||||
}
|
||||
|
||||
DiagnosticReporter get reporter => compiler.reporter;
|
||||
|
||||
Resolution get resolution => compiler.resolution;
|
||||
|
||||
bool classNeedsRti(ClassElement cls) => false;
|
||||
bool methodNeedsRti(FunctionElement function) => false;
|
||||
|
||||
void enqueueHelpers(ResolutionEnqueuer world, Registry registry) {
|
||||
// Right now resolver doesn't always resolve interfaces needed
|
||||
// for literals, so force them. TODO(antonm): fix in the resolver.
|
||||
final LITERAL_TYPE_NAMES = const [
|
||||
'Map',
|
||||
'List',
|
||||
'num',
|
||||
'int',
|
||||
'double',
|
||||
'bool'
|
||||
];
|
||||
final coreLibrary = compiler.coreLibrary;
|
||||
for (final name in LITERAL_TYPE_NAMES) {
|
||||
ClassElement classElement = coreLibrary.findLocal(name);
|
||||
classElement.ensureResolved(resolution);
|
||||
}
|
||||
// Enqueue the methods that the VM might invoke on user objects because
|
||||
// we don't trust the resolution to always get these included.
|
||||
world.registerDynamicUse(new DynamicUse(Selectors.toString_, null));
|
||||
world.registerDynamicUse(new DynamicUse(Selectors.hashCode_, null));
|
||||
world.registerDynamicUse(
|
||||
new DynamicUse(new Selector.binaryOperator('=='), null));
|
||||
world.registerDynamicUse(new DynamicUse(Selectors.compareTo, null));
|
||||
}
|
||||
|
||||
WorldImpact codegen(CodegenWorkItem work) {
|
||||
return const WorldImpact();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether we should output given element. Corelib classes like
|
||||
* Object should not be in the resulting code.
|
||||
*/
|
||||
@override
|
||||
bool shouldOutput(Element element) {
|
||||
return (!element.library.isPlatformLibrary &&
|
||||
!element.isSynthesized &&
|
||||
element is! AbstractFieldElement) ||
|
||||
mirrorRenamer.isMirrorHelperLibrary(element.library);
|
||||
}
|
||||
|
||||
int assembleProgram() {
|
||||
ElementAst computeElementAst(AstElement element) {
|
||||
return new ElementAst(
|
||||
element.resolvedAst.node, element.resolvedAst.elements);
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Remove the need for this method.
|
||||
void postProcessElementAst(AstElement element, ElementAst elementAst,
|
||||
newTypedefElementCallback, newClassElementCallback) {
|
||||
ReferencedElementCollector collector = new ReferencedElementCollector(
|
||||
reporter,
|
||||
element,
|
||||
elementAst,
|
||||
newTypedefElementCallback,
|
||||
newClassElementCallback);
|
||||
collector.collect();
|
||||
}
|
||||
|
||||
int totalSize = outputter.assembleProgram(
|
||||
libraries: compiler.libraryLoader.libraries,
|
||||
instantiatedClasses: compiler.resolverWorld.directlyInstantiatedClasses,
|
||||
resolvedElements: compiler.enqueuer.resolution.processedElements,
|
||||
usedTypeLiterals: usedTypeLiterals,
|
||||
postProcessElementAst: postProcessElementAst,
|
||||
computeElementAst: computeElementAst,
|
||||
shouldOutput: shouldOutput,
|
||||
isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations,
|
||||
sortElements: Elements.sortedByPosition,
|
||||
mirrorRenamer: mirrorRenamer,
|
||||
mainFunction: compiler.mainFunction,
|
||||
outputUri: compiler.options.outputUri);
|
||||
|
||||
// Output verbose info about size ratio of resulting bundle to all
|
||||
// referenced non-platform sources.
|
||||
logResultBundleSizeInfo(outputter.libraryInfo.userLibraries,
|
||||
outputter.elementInfo.topLevelElements, totalSize);
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
void logResultBundleSizeInfo(Iterable<LibraryElement> userLibraries,
|
||||
Iterable<Element> topLevelElements, int totalOutputSize) {
|
||||
// Sum total size of scripts in each referenced library.
|
||||
int nonPlatformSize = 0;
|
||||
for (LibraryElement lib in userLibraries) {
|
||||
for (CompilationUnitElement compilationUnit in lib.compilationUnits) {
|
||||
nonPlatformSize += compilationUnit.script.file.length;
|
||||
}
|
||||
}
|
||||
int percentage = totalOutputSize * 100 ~/ nonPlatformSize;
|
||||
log('Total used non-platform files size: ${nonPlatformSize} bytes, '
|
||||
'Output total size: $totalOutputSize bytes (${percentage}%)');
|
||||
}
|
||||
|
||||
log(String message) => reporter.log('[DartBackend] $message');
|
||||
|
||||
@override
|
||||
Future onLibrariesLoaded(LoadedLibraries loadedLibraries) {
|
||||
// All platform classes must be resolved to ensure that their member names
|
||||
// are preserved.
|
||||
loadedLibraries.forEachLibrary((LibraryElement library) {
|
||||
if (library.isPlatformLibrary) {
|
||||
library.forEachLocalMember((Element element) {
|
||||
if (element.isClass) {
|
||||
ClassElement classElement = element;
|
||||
classElement.ensureResolved(resolution);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (useMirrorHelperLibrary &&
|
||||
loadedLibraries.containsLibrary(Uris.dart_mirrors)) {
|
||||
return compiler.libraryLoader
|
||||
.loadLibrary(compiler.resolvedUriTranslator.translate(
|
||||
loadedLibraries.getLibrary(Uris.dart_mirrors),
|
||||
MirrorRenamerImpl.DART_MIRROR_HELPER,
|
||||
null))
|
||||
.then((LibraryElement library) {
|
||||
mirrorRenamer = new MirrorRenamerImpl(compiler, this, library);
|
||||
});
|
||||
}
|
||||
return new Future.value();
|
||||
}
|
||||
|
||||
@override
|
||||
void registerStaticUse(Element element, Enqueuer enqueuer) {
|
||||
if (element == compiler.mirrorSystemGetNameFunction) {
|
||||
FunctionElement getNameFunction = mirrorRenamer.getNameFunction;
|
||||
if (getNameFunction != null) {
|
||||
enqueuer.addToWorkList(getNameFunction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void registerInstantiatedType(
|
||||
InterfaceType type, Enqueuer enqueuer, Registry registry,
|
||||
{bool mirrorUsage: false}) {
|
||||
registerPlatformMembers(type, registerUse: registry.registerDynamicUse);
|
||||
super.registerInstantiatedType(type, enqueuer, registry,
|
||||
mirrorUsage: mirrorUsage);
|
||||
}
|
||||
|
||||
/// Register dynamic access of members of [type] that implement members
|
||||
/// of types defined in the platform libraries.
|
||||
void registerPlatformMembers(InterfaceType type,
|
||||
{void registerUse(DynamicUse dynamicUse)}) {
|
||||
// Without patching, dart2dart has no way of performing sound tree-shaking
|
||||
// in face external functions. Therefore we employ another scheme:
|
||||
//
|
||||
// Based on the assumption that the platform code only relies on the
|
||||
// interfaces of it's own classes, we can approximate the semantics of
|
||||
// external functions by eagerly registering dynamic invocation of instance
|
||||
// members defined the platform interfaces.
|
||||
//
|
||||
// Since we only need to generate code for non-platform classes we can
|
||||
// restrict this registration to platform interfaces implemented by
|
||||
// instantiated non-platform classes.
|
||||
//
|
||||
// Consider for instance this program:
|
||||
//
|
||||
// import 'dart:math' show Random;
|
||||
//
|
||||
// class MyRandom implements Random {
|
||||
// int nextInt() => 0;
|
||||
// }
|
||||
//
|
||||
// main() {
|
||||
// print([0, 1, 2].shuffle(new MyRandom()));
|
||||
// }
|
||||
//
|
||||
// Here `MyRandom` is a subtype if `Random` defined in 'dart:math'. By the
|
||||
// assumption, all methods defined `Random` are potentially called, and
|
||||
// therefore, though there are no visible call sites from the user node,
|
||||
// dynamic invocation of for instance `nextInt` should be registered. In
|
||||
// this case, `nextInt` is actually called by the standard implementation of
|
||||
// `shuffle`.
|
||||
|
||||
ClassElement cls = type.element;
|
||||
if (!cls.library.isPlatformLibrary) {
|
||||
for (Link<DartType> link = cls.allSupertypes;
|
||||
!link.isEmpty;
|
||||
link = link.tail) {
|
||||
InterfaceType supertype = link.head;
|
||||
ClassElement superclass = supertype.element;
|
||||
LibraryElement library = superclass.library;
|
||||
if (library.isPlatformLibrary) {
|
||||
if (_userImplementedPlatformClasses.add(superclass)) {
|
||||
// Register selectors for all instance methods since these might
|
||||
// be called on user classes from within the platform
|
||||
// implementation.
|
||||
superclass.forEachLocalMember((MemberElement element) {
|
||||
if (element.isConstructor || element.isStatic) return;
|
||||
|
||||
element.computeType(resolution);
|
||||
Selector selector = new Selector.fromElement(element);
|
||||
registerUse(new DynamicUse(selector, null));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool enableDeferredLoadingIfSupported(Spannable node, Registry registry) {
|
||||
// TODO(sigurdm): Implement deferred loading for dart2dart.
|
||||
reporter.reportWarningMessage(
|
||||
node, MessageKind.DEFERRED_LIBRARY_DART_2_DART);
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Uri resolvePatchUri(String libraryName, Uri) {
|
||||
// Dart2dart does not use patches.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
class DartImpactTransformer extends ImpactTransformer {
|
||||
final DartBackend backend;
|
||||
|
||||
DartImpactTransformer(this.backend);
|
||||
|
||||
@override
|
||||
WorldImpact transformResolutionImpact(ResolutionImpact worldImpact) {
|
||||
TransformedWorldImpact transformed =
|
||||
new TransformedWorldImpact(worldImpact);
|
||||
for (TypeUse typeUse in worldImpact.typeUses) {
|
||||
if (typeUse.kind == TypeUseKind.TYPE_LITERAL &&
|
||||
typeUse.type.isInterfaceType) {
|
||||
backend.usedTypeLiterals.add(typeUse.type.element);
|
||||
}
|
||||
if (typeUse.kind == TypeUseKind.INSTANTIATION) {
|
||||
backend.registerPlatformMembers(typeUse.type,
|
||||
registerUse: transformed.registerDynamicUse);
|
||||
}
|
||||
}
|
||||
return transformed;
|
||||
}
|
||||
}
|
||||
|
||||
class EmitterUnparser extends Unparser {
|
||||
final Map<Node, String> renames;
|
||||
|
||||
EmitterUnparser(this.renames, {bool minify, bool stripTypes})
|
||||
: super(minify: minify, stripTypes: stripTypes);
|
||||
|
||||
visit(Node node) {
|
||||
if (node != null && renames.containsKey(node)) {
|
||||
write(renames[node]);
|
||||
} else {
|
||||
super.visit(node);
|
||||
}
|
||||
}
|
||||
|
||||
unparseSendReceiver(Send node, {bool spacesNeeded: false}) {
|
||||
// TODO(smok): Remove ugly hack for library prefices.
|
||||
if (node.receiver != null && renames[node.receiver] == '') return;
|
||||
super.unparseSendReceiver(node, spacesNeeded: spacesNeeded);
|
||||
}
|
||||
|
||||
unparseFunctionName(Node name) {
|
||||
if (name != null && renames.containsKey(name)) {
|
||||
write(renames[name]);
|
||||
} else {
|
||||
super.unparseFunctionName(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some elements are not recorded by resolver now,
|
||||
* for example, typedefs or classes which are only
|
||||
* used in signatures, as/is operators or in super clauses
|
||||
* (just to name a few). Retraverse AST to pick those up.
|
||||
*/
|
||||
class ReferencedElementCollector extends Visitor {
|
||||
final DiagnosticReporter reporter;
|
||||
final Element element;
|
||||
final ElementAst elementAst;
|
||||
final newTypedefElementCallback;
|
||||
final newClassElementCallback;
|
||||
|
||||
ReferencedElementCollector(this.reporter, this.element, this.elementAst,
|
||||
this.newTypedefElementCallback, this.newClassElementCallback);
|
||||
|
||||
visitNode(Node node) {
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation typeAnnotation) {
|
||||
TreeElements treeElements = elementAst.treeElements;
|
||||
final DartType type = treeElements.getType(typeAnnotation);
|
||||
assert(invariant(typeAnnotation, type != null,
|
||||
message: "Missing type for type annotation: $treeElements."));
|
||||
if (type.isTypedef) newTypedefElementCallback(type.element);
|
||||
if (type.isInterfaceType) newClassElementCallback(type.element);
|
||||
typeAnnotation.visitChildren(this);
|
||||
}
|
||||
|
||||
void collect() {
|
||||
reporter.withCurrentElement(element, () {
|
||||
elementAst.ast.accept(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Comparator compareBy(f) => (x, y) => f(x).compareTo(f(y));
|
||||
|
||||
List sorted(Iterable l, comparison) {
|
||||
final result = new List.from(l);
|
||||
result.sort(comparison);
|
||||
return result;
|
||||
}
|
||||
|
||||
compareElements(e0, e1) {
|
||||
int result = compareBy((e) => e.library.canonicalUri.toString())(e0, e1);
|
||||
if (result != 0) return result;
|
||||
return compareBy((e) => e.position.charOffset)(e0, e1);
|
||||
}
|
||||
|
||||
/// [ConstantCompilerTask] for compilation of constants for the Dart backend.
|
||||
///
|
||||
/// Since this task needs no distinction between frontend and backend constants
|
||||
/// it also serves as the [BackendConstantEnvironment].
|
||||
class DartConstantTask extends ConstantCompilerTask
|
||||
implements BackendConstantEnvironment {
|
||||
final DartConstantCompiler constantCompiler;
|
||||
|
||||
DartConstantTask(Compiler compiler)
|
||||
: this.constantCompiler = new DartConstantCompiler(compiler),
|
||||
super(compiler.measurer);
|
||||
|
||||
String get name => 'ConstantHandler';
|
||||
|
||||
@override
|
||||
ConstantSystem get constantSystem => constantCompiler.constantSystem;
|
||||
|
||||
@override
|
||||
bool hasConstantValue(ConstantExpression expression) {
|
||||
return constantCompiler.hasConstantValue(expression);
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantValue getConstantValue(ConstantExpression expression) {
|
||||
return constantCompiler.getConstantValue(expression);
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantValue getConstantValueForVariable(VariableElement element) {
|
||||
return constantCompiler.getConstantValueForVariable(element);
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantExpression getConstantForNode(Node node, TreeElements elements) {
|
||||
return constantCompiler.getConstantForNode(node, elements);
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantValue getConstantValueForNode(Node node, TreeElements elements) {
|
||||
return getConstantValue(
|
||||
constantCompiler.getConstantForNode(node, elements));
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantValue getConstantValueForMetadata(MetadataAnnotation metadata) {
|
||||
return getConstantValue(metadata.constant);
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantExpression compileConstant(VariableElement element) {
|
||||
return measure(() {
|
||||
return constantCompiler.compileConstant(element);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void evaluate(ConstantExpression constant) {
|
||||
return measure(() {
|
||||
return constantCompiler.evaluate(constant);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantExpression compileVariable(VariableElement element) {
|
||||
return measure(() {
|
||||
return constantCompiler.compileVariable(element);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantExpression compileNode(Node node, TreeElements elements,
|
||||
{bool enforceConst: true}) {
|
||||
return measure(() {
|
||||
return constantCompiler.compileNodeWithDefinitions(node, elements,
|
||||
isConst: enforceConst);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
ConstantExpression compileMetadata(
|
||||
MetadataAnnotation metadata, Node node, TreeElements elements) {
|
||||
return measure(() {
|
||||
return constantCompiler.compileMetadata(metadata, node, elements);
|
||||
});
|
||||
}
|
||||
|
||||
// TODO(johnniwinther): Remove this when values are computed from the
|
||||
// expressions.
|
||||
@override
|
||||
void copyConstantValues(DartConstantTask task) {
|
||||
constantCompiler.constantValueMap
|
||||
.addAll(task.constantCompiler.constantValueMap);
|
||||
}
|
||||
|
||||
@override
|
||||
void registerLazyStatic(FieldElement element) {
|
||||
// Do nothing.
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
// Copyright (c) 2014, 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 backend_ast_emitter;
|
||||
|
||||
import 'backend_ast_nodes.dart';
|
||||
import '../dart_types.dart';
|
||||
import '../elements/elements.dart';
|
||||
|
||||
class TypeGenerator {
|
||||
/// TODO(johnniwinther): Remove this when issue 21283 has been resolved.
|
||||
static int pseudoNameCounter = 0;
|
||||
|
||||
static Parameter emitParameter(DartType type,
|
||||
{String name, Element element}) {
|
||||
if (name == null && element != null) {
|
||||
name = element.name;
|
||||
}
|
||||
if (name == null) {
|
||||
name = '_${pseudoNameCounter++}';
|
||||
}
|
||||
Parameter parameter;
|
||||
if (type.isFunctionType) {
|
||||
FunctionType functionType = type;
|
||||
TypeAnnotation returnType = createOptionalType(functionType.returnType);
|
||||
Parameters innerParameters = createParametersFromType(functionType);
|
||||
parameter = new Parameter.function(name, returnType, innerParameters);
|
||||
} else {
|
||||
TypeAnnotation typeAnnotation = createOptionalType(type);
|
||||
parameter = new Parameter(name, type: typeAnnotation);
|
||||
}
|
||||
parameter.element = element;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
static Parameters createParametersFromType(FunctionType functionType) {
|
||||
pseudoNameCounter = 0;
|
||||
if (functionType.namedParameters.isEmpty) {
|
||||
return new Parameters(createParameters(functionType.parameterTypes),
|
||||
createParameters(functionType.optionalParameterTypes), false);
|
||||
} else {
|
||||
return new Parameters(
|
||||
createParameters(functionType.parameterTypes),
|
||||
createParameters(functionType.namedParameterTypes,
|
||||
names: functionType.namedParameters),
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
static List<Parameter> createParameters(Iterable<DartType> parameterTypes,
|
||||
{Iterable<String> names: const <String>[],
|
||||
Iterable<Element> elements: const <Element>[]}) {
|
||||
Iterator<String> name = names.iterator;
|
||||
Iterator<Element> element = elements.iterator;
|
||||
return parameterTypes.map((DartType type) {
|
||||
name.moveNext();
|
||||
element.moveNext();
|
||||
return emitParameter(type, name: name.current, element: element.current);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
/// Like [createTypeAnnotation] except the dynamic type is converted to null.
|
||||
static TypeAnnotation createOptionalType(DartType type) {
|
||||
if (type.treatAsDynamic) {
|
||||
return null;
|
||||
} else {
|
||||
return createType(type);
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates the [TypeAnnotation] for a [type] that is not function type.
|
||||
static TypeAnnotation createType(DartType type) {
|
||||
if (type is GenericType) {
|
||||
if (type.treatAsRaw) {
|
||||
return new TypeAnnotation(type.element.name)..dartType = type;
|
||||
}
|
||||
return new TypeAnnotation(type.element.name,
|
||||
type.typeArguments.map(createType).toList(growable: false))
|
||||
..dartType = type;
|
||||
} else if (type is VoidType) {
|
||||
return new TypeAnnotation('void')..dartType = type;
|
||||
} else if (type is TypeVariableType) {
|
||||
return new TypeAnnotation(type.name)..dartType = type;
|
||||
} else if (type is DynamicType) {
|
||||
return new TypeAnnotation("dynamic")..dartType = type;
|
||||
} else if (type is MalformedType) {
|
||||
return new TypeAnnotation(type.name)..dartType = type;
|
||||
} else {
|
||||
throw "Unsupported type annotation: $type";
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,40 +0,0 @@
|
|||
// 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 dart_backend;
|
||||
|
||||
import 'dart:async' show Future;
|
||||
import 'dart:math' show max;
|
||||
|
||||
import '../../compiler.dart' show CompilerOutputProvider;
|
||||
import '../common.dart';
|
||||
import '../common/backend_api.dart' show Backend, ImpactTransformer;
|
||||
import '../common/codegen.dart' show CodegenWorkItem;
|
||||
import '../common/names.dart' show Selectors, Uris;
|
||||
import '../common/registry.dart' show Registry;
|
||||
import '../common/resolution.dart' show Resolution, ResolutionImpact;
|
||||
import '../common/tasks.dart' show CompilerTask;
|
||||
import '../compiler.dart' show Compiler;
|
||||
import '../compile_time_constants.dart';
|
||||
import '../constants/constant_system.dart';
|
||||
import '../constants/expressions.dart';
|
||||
import '../constants/values.dart';
|
||||
import '../dart_types.dart';
|
||||
import '../elements/elements.dart';
|
||||
import '../enqueue.dart' show Enqueuer, ResolutionEnqueuer;
|
||||
import '../library_loader.dart' show LoadedLibraries;
|
||||
import '../mirror_renamer/mirror_renamer.dart';
|
||||
import '../resolution/tree_elements.dart' show TreeElements, TreeElementMapping;
|
||||
import '../tokens/keyword.dart' show Keyword;
|
||||
import '../tree/tree.dart';
|
||||
import '../universe/selector.dart' show Selector;
|
||||
import '../universe/use.dart' show DynamicUse, TypeUse, TypeUseKind;
|
||||
import '../universe/world_impact.dart' show WorldImpact, TransformedWorldImpact;
|
||||
import '../util/util.dart';
|
||||
import 'backend_ast_to_frontend_ast.dart' as backend2frontend;
|
||||
|
||||
part 'backend.dart';
|
||||
part 'renamer.dart';
|
||||
part 'placeholder_collector.dart';
|
||||
part 'outputter.dart';
|
|
@ -1,576 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
part of dart_backend;
|
||||
|
||||
typedef bool IsSafeToRemoveTypeDeclarations(
|
||||
Map<ClassElement, Iterable<Element>> classMembers);
|
||||
typedef void ElementCallback<E>(E element);
|
||||
typedef void ElementPostProcessFunction(
|
||||
AstElement element,
|
||||
ElementAst elementAst,
|
||||
ElementCallback<TypedefElement> typedefCallback,
|
||||
ElementCallback<ClassElement> classCallback);
|
||||
typedef ElementAst ComputeElementAstFunction(AstElement element);
|
||||
typedef bool ElementFilter(Element element);
|
||||
typedef List<Element> ElementSorter(Iterable<Element> elements);
|
||||
|
||||
/// Output engine for dart2dart that is shared between the dart2js and the
|
||||
/// analyzer implementations of dart2dart.
|
||||
class DartOutputter {
|
||||
final DiagnosticReporter reporter;
|
||||
final CompilerOutputProvider outputProvider;
|
||||
final bool forceStripTypes;
|
||||
|
||||
// TODO(antonm): make available from command-line options.
|
||||
final bool outputAst = false;
|
||||
final bool enableMinification;
|
||||
|
||||
/// If `true`, libraries are generated into separate files.
|
||||
final bool multiFile;
|
||||
|
||||
/// Internal structures accessible for tests and logging.
|
||||
// TODO(johnniwinther): Clean this up.
|
||||
PlaceholderRenamer renamer;
|
||||
MainOutputGenerator output;
|
||||
LibraryInfo libraryInfo;
|
||||
ElementInfo elementInfo;
|
||||
|
||||
// TODO(johnniwinther): Support recompilation.
|
||||
DartOutputter(this.reporter, this.outputProvider,
|
||||
{bool this.forceStripTypes: false,
|
||||
bool this.enableMinification: false,
|
||||
bool this.multiFile: false});
|
||||
|
||||
/// Generate Dart code for the program starting at [mainFunction].
|
||||
///
|
||||
/// [libraries] is the set of all libraries (user/package/sdk) that are
|
||||
/// referenced in the program.
|
||||
///
|
||||
/// [instantiatedClasses] is the set of classes that are potentially
|
||||
/// instantiated in the program.
|
||||
///
|
||||
/// [resolvedElements] is the set of methods, constructors, and fields that
|
||||
/// are potentially accessed/called in the program.
|
||||
///
|
||||
/// The [sortElements] function is used to sort [instantiatedClasses] and
|
||||
/// [resolvedElements] in the generated output.
|
||||
///
|
||||
/// Returns the total size of the generated code.
|
||||
int assembleProgram(
|
||||
{MirrorRenamer mirrorRenamer: const MirrorRenamer(),
|
||||
Iterable<LibraryElement> libraries,
|
||||
Iterable<Element> instantiatedClasses,
|
||||
Iterable<Element> resolvedElements,
|
||||
Iterable<ClassElement> usedTypeLiterals: const <ClassElement>[],
|
||||
FunctionElement mainFunction,
|
||||
Uri outputUri,
|
||||
ElementPostProcessFunction postProcessElementAst,
|
||||
ComputeElementAstFunction computeElementAst,
|
||||
ElementFilter shouldOutput,
|
||||
IsSafeToRemoveTypeDeclarations isSafeToRemoveTypeDeclarations,
|
||||
ElementSorter sortElements}) {
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, libraries != null,
|
||||
message: "'libraries' must be non-null."));
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, instantiatedClasses != null,
|
||||
message: "'instantiatedClasses' must be non-null."));
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, resolvedElements != null,
|
||||
message: "'resolvedElements' must be non-null."));
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, mainFunction != null,
|
||||
message: "'mainFunction' must be non-null."));
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, computeElementAst != null,
|
||||
message: "'computeElementAst' must be non-null."));
|
||||
assert(invariant(NO_LOCATION_SPANNABLE, shouldOutput != null,
|
||||
message: "'shouldOutput' must be non-null."));
|
||||
assert(invariant(
|
||||
NO_LOCATION_SPANNABLE, isSafeToRemoveTypeDeclarations != null,
|
||||
message: "'isSafeToRemoveTypeDeclarations' must be non-null."));
|
||||
|
||||
if (sortElements == null) {
|
||||
// Ensure deterministic output order.
|
||||
sortElements = (Iterable<Element> elements) {
|
||||
List<Element> list = elements.toList();
|
||||
list.sort((Element a, Element b) => a.name.compareTo(b.name));
|
||||
return list;
|
||||
};
|
||||
}
|
||||
|
||||
libraryInfo =
|
||||
LibraryInfo.processLibraries(reporter, libraries, resolvedElements);
|
||||
|
||||
elementInfo = ElementInfoProcessor.createElementInfo(
|
||||
instantiatedClasses, resolvedElements, usedTypeLiterals,
|
||||
postProcessElementAst: postProcessElementAst,
|
||||
parseElementAst: computeElementAst,
|
||||
shouldOutput: shouldOutput,
|
||||
sortElements: sortElements);
|
||||
|
||||
PlaceholderCollector collector = collectPlaceholders(
|
||||
reporter, mirrorRenamer, mainFunction, libraryInfo, elementInfo);
|
||||
|
||||
renamer = createRenamer(collector, libraryInfo, elementInfo,
|
||||
enableMinification: enableMinification,
|
||||
forceStripTypes: forceStripTypes,
|
||||
isSafeToRemoveTypeDeclarations: isSafeToRemoveTypeDeclarations);
|
||||
|
||||
if (outputAst) {
|
||||
String code = astOutput(reporter, elementInfo);
|
||||
outputProvider("", "dart")
|
||||
..add(code)
|
||||
..close();
|
||||
return code.length;
|
||||
} else {
|
||||
output = new MainOutputGenerator();
|
||||
return output.generateCode(libraryInfo, elementInfo, collector, renamer,
|
||||
mainFunction, outputUri, outputProvider, mirrorRenamer,
|
||||
multiFile: multiFile,
|
||||
forceStripTypes: forceStripTypes,
|
||||
enableMinification: enableMinification);
|
||||
}
|
||||
}
|
||||
|
||||
static PlaceholderCollector collectPlaceholders(
|
||||
DiagnosticReporter reporter,
|
||||
MirrorRenamer mirrorRenamer,
|
||||
FunctionElement mainFunction,
|
||||
LibraryInfo libraryInfo,
|
||||
ElementInfo elementInfo) {
|
||||
// Create all necessary placeholders.
|
||||
PlaceholderCollector collector = new PlaceholderCollector(
|
||||
reporter,
|
||||
mirrorRenamer,
|
||||
libraryInfo.fixedDynamicNames,
|
||||
elementInfo.elementAsts,
|
||||
mainFunction);
|
||||
|
||||
makePlaceholders(element) {
|
||||
collector.collect(element);
|
||||
|
||||
if (element.isClass && !element.isEnumClass) {
|
||||
elementInfo.classMembers[element].forEach(makePlaceholders);
|
||||
}
|
||||
}
|
||||
elementInfo.topLevelElements.forEach(makePlaceholders);
|
||||
return collector;
|
||||
}
|
||||
|
||||
static PlaceholderRenamer createRenamer(PlaceholderCollector collector,
|
||||
LibraryInfo libraryInfo, ElementInfo elementInfo,
|
||||
{bool enableMinification: false,
|
||||
bool forceStripTypes: false,
|
||||
isSafeToRemoveTypeDeclarations}) {
|
||||
// Create renames.
|
||||
bool shouldCutDeclarationTypes = forceStripTypes ||
|
||||
(enableMinification &&
|
||||
isSafeToRemoveTypeDeclarations(elementInfo.classMembers));
|
||||
|
||||
PlaceholderRenamer placeholderRenamer = new PlaceholderRenamer(
|
||||
libraryInfo.fixedDynamicNames,
|
||||
libraryInfo.fixedStaticNames,
|
||||
libraryInfo.reexportingLibraries,
|
||||
cutDeclarationTypes: shouldCutDeclarationTypes,
|
||||
enableMinification: enableMinification);
|
||||
|
||||
placeholderRenamer.computeRenames(collector);
|
||||
return placeholderRenamer;
|
||||
}
|
||||
|
||||
static String astOutput(
|
||||
DiagnosticReporter reporter, ElementInfo elementInfo) {
|
||||
// TODO(antonm): Ideally XML should be a separate backend.
|
||||
// TODO(antonm): obey renames and minification, at least as an option.
|
||||
StringBuffer sb = new StringBuffer();
|
||||
outputElement(element) {
|
||||
sb.write(element.parseNode(reporter).toDebugString());
|
||||
}
|
||||
|
||||
// Emit XML for AST instead of the program.
|
||||
for (Element topLevel in elementInfo.topLevelElements) {
|
||||
if (topLevel.isClass &&
|
||||
!elementInfo.emitNoMembersFor.contains(topLevel)) {
|
||||
// TODO(antonm): add some class info.
|
||||
elementInfo.classMembers[topLevel].forEach(outputElement);
|
||||
} else {
|
||||
outputElement(topLevel);
|
||||
}
|
||||
}
|
||||
return '<Program>\n$sb</Program>\n';
|
||||
}
|
||||
}
|
||||
|
||||
class LibraryInfo {
|
||||
final Set<String> fixedStaticNames;
|
||||
final Set<String> fixedDynamicNames;
|
||||
final Map<Element, LibraryElement> reexportingLibraries;
|
||||
final List<LibraryElement> userLibraries;
|
||||
|
||||
LibraryInfo(this.fixedStaticNames, this.fixedDynamicNames,
|
||||
this.reexportingLibraries, this.userLibraries);
|
||||
|
||||
static LibraryInfo processLibraries(
|
||||
DiagnosticReporter reporter,
|
||||
Iterable<LibraryElement> libraries,
|
||||
Iterable<AstElement> resolvedElements) {
|
||||
Set<String> fixedStaticNames = new Set<String>();
|
||||
Set<String> fixedDynamicNames = new Set<String>();
|
||||
Map<Element, LibraryElement> reexportingLibraries =
|
||||
<Element, LibraryElement>{};
|
||||
List<LibraryElement> userLibraries = <LibraryElement>[];
|
||||
// Conservatively traverse all platform libraries and collect member names.
|
||||
// TODO(antonm): ideally we should only collect names of used members,
|
||||
// however as of today there are problems with names of some core library
|
||||
// interfaces, most probably for interfaces of literals.
|
||||
|
||||
for (LibraryElement library in libraries) {
|
||||
if (!library.isPlatformLibrary) {
|
||||
userLibraries.add(library);
|
||||
continue;
|
||||
}
|
||||
library.forEachLocalMember((Element element) {
|
||||
if (element.isClass) {
|
||||
ClassElement classElement = element;
|
||||
assert(invariant(classElement, classElement.isResolved,
|
||||
message: "Unresolved platform class."));
|
||||
classElement.forEachLocalMember((member) {
|
||||
if (member.isInstanceMember) {
|
||||
fixedDynamicNames.add(member.name);
|
||||
} else {
|
||||
fixedStaticNames.add(member.name);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Even class names are added due to a delicate problem we have:
|
||||
// if one imports dart:core with a prefix, we cannot tell prefix.name
|
||||
// from dynamic invocation (alas!). So we'd better err on preserving
|
||||
// those names.
|
||||
fixedStaticNames.add(element.name);
|
||||
});
|
||||
|
||||
library.forEachExport((Element export) {
|
||||
if (!library.isInternalLibrary && export.library.isInternalLibrary) {
|
||||
// If an element of an internal library is reexported by a platform
|
||||
// library, we have to import the reexporting library instead of the
|
||||
// internal library, because the internal library is an
|
||||
// implementation detail of dart2js.
|
||||
reexportingLibraries[export] = library;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Map to keep track of names of enum classes. Since these cannot be renamed
|
||||
// we ensure that they are unique.
|
||||
Map<String, ClassElement> enumClassMap = <String, ClassElement>{};
|
||||
|
||||
// As of now names of named optionals are not renamed. Therefore add all
|
||||
// field names used as named optionals into [fixedMemberNames].
|
||||
for (final element in resolvedElements) {
|
||||
if (!element.isConstructor) continue;
|
||||
for (ParameterElement parameter in element.parameters) {
|
||||
if (parameter.isInitializingFormal && parameter.isNamed) {
|
||||
fixedDynamicNames.add(parameter.name);
|
||||
}
|
||||
}
|
||||
ClassElement cls = element.enclosingClass;
|
||||
if (cls != null && cls.isEnumClass) {
|
||||
fixedDynamicNames.add('index');
|
||||
|
||||
ClassElement existingEnumClass =
|
||||
enumClassMap.putIfAbsent(cls.name, () => cls);
|
||||
if (existingEnumClass != cls) {
|
||||
reporter.reportError(
|
||||
reporter.createMessage(cls, MessageKind.GENERIC, {
|
||||
'text': "Duplicate enum names are not supported "
|
||||
"in dart2dart."
|
||||
}),
|
||||
<DiagnosticMessage>[
|
||||
reporter.createMessage(existingEnumClass, MessageKind.GENERIC, {
|
||||
'text': "This is the other declaration of '${cls.name}'."
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fixedStaticNames.addAll(enumClassMap.keys);
|
||||
|
||||
// The VM will automatically invoke the call method of objects
|
||||
// that are invoked as functions. Make sure to not rename that.
|
||||
fixedDynamicNames.add('call');
|
||||
|
||||
return new LibraryInfo(fixedStaticNames, fixedDynamicNames,
|
||||
reexportingLibraries, userLibraries);
|
||||
}
|
||||
}
|
||||
|
||||
class ElementInfo {
|
||||
final Map<Element, ElementAst> elementAsts;
|
||||
final Iterable<Element> topLevelElements;
|
||||
final Map<ClassElement, Iterable<Element>> classMembers;
|
||||
final Iterable<ClassElement> emitNoMembersFor;
|
||||
|
||||
ElementInfo(this.elementAsts, this.topLevelElements, this.classMembers,
|
||||
this.emitNoMembersFor);
|
||||
}
|
||||
|
||||
class ElementInfoProcessor implements ElementInfo {
|
||||
final Map<Element, ElementAst> elementAsts = new Map<Element, ElementAst>();
|
||||
final Set<Element> topLevelElements = new Set<Element>();
|
||||
final Map<ClassElement, Set<Element>> classMembers =
|
||||
new Map<ClassElement, Set<Element>>();
|
||||
final Set<ClassElement> emitNoMembersFor = new Set<ClassElement>();
|
||||
final ElementPostProcessFunction postProcessElementAst;
|
||||
final ComputeElementAstFunction parseElementAst;
|
||||
final ElementFilter shouldOutput;
|
||||
|
||||
ElementInfoProcessor(
|
||||
{this.postProcessElementAst, this.parseElementAst, this.shouldOutput});
|
||||
|
||||
static ElementInfo createElementInfo(
|
||||
Iterable<ClassElement> instantiatedClasses,
|
||||
Iterable<AstElement> resolvedElements,
|
||||
Iterable<ClassElement> usedTypeLiterals,
|
||||
{ElementPostProcessFunction postProcessElementAst,
|
||||
ComputeElementAstFunction parseElementAst,
|
||||
ElementFilter shouldOutput,
|
||||
ElementSorter sortElements}) {
|
||||
ElementInfoProcessor processor = new ElementInfoProcessor(
|
||||
postProcessElementAst: postProcessElementAst,
|
||||
parseElementAst: parseElementAst,
|
||||
shouldOutput: shouldOutput);
|
||||
return processor.process(
|
||||
instantiatedClasses, resolvedElements, usedTypeLiterals,
|
||||
sortElements: sortElements);
|
||||
}
|
||||
|
||||
ElementInfo process(
|
||||
Iterable<ClassElement> instantiatedClasses,
|
||||
Iterable<AstElement> resolvedElements,
|
||||
Iterable<ClassElement> usedTypeLiterals,
|
||||
{ElementSorter sortElements}) {
|
||||
// Build all top level elements to emit and necessary class members.
|
||||
instantiatedClasses.where(shouldOutput).forEach(addClass);
|
||||
resolvedElements.where(shouldOutput).forEach(addMember);
|
||||
usedTypeLiterals.forEach((ClassElement element) {
|
||||
if (shouldOutput(element)) {
|
||||
if (!topLevelElements.contains(element)) {
|
||||
// The class is only referenced by type literals.
|
||||
emitNoMembersFor.add(element);
|
||||
}
|
||||
addClass(element);
|
||||
}
|
||||
});
|
||||
|
||||
// Sort elements.
|
||||
List<Element> sortedTopLevels = sortElements(topLevelElements);
|
||||
Map<ClassElement, List<Element>> sortedClassMembers =
|
||||
new Map<ClassElement, List<Element>>();
|
||||
classMembers.forEach((classElement, members) {
|
||||
sortedClassMembers[classElement] = sortElements(members);
|
||||
});
|
||||
|
||||
return new ElementInfo(
|
||||
elementAsts, sortedTopLevels, sortedClassMembers, emitNoMembersFor);
|
||||
}
|
||||
|
||||
void processElement(Element element, ElementAst elementAst) {
|
||||
if (postProcessElementAst != null) {
|
||||
postProcessElementAst(element, elementAst, newTypedefElementCallback,
|
||||
newClassElementCallback);
|
||||
}
|
||||
elementAsts[element] = elementAst;
|
||||
}
|
||||
|
||||
void addTopLevel(AstElement element, ElementAst elementAst) {
|
||||
if (topLevelElements.contains(element)) return;
|
||||
topLevelElements.add(element);
|
||||
processElement(element, elementAst);
|
||||
}
|
||||
|
||||
void addClass(ClassElement classElement) {
|
||||
TreeElements treeElements = new TreeElementMapping(classElement);
|
||||
backend2frontend.TreePrinter treePrinter =
|
||||
new backend2frontend.TreePrinter(treeElements);
|
||||
Node node = treePrinter.makeNodeForClassElement(classElement);
|
||||
addTopLevel(classElement, new ElementAst(node, treeElements));
|
||||
classMembers.putIfAbsent(classElement, () => new Set());
|
||||
}
|
||||
|
||||
void newTypedefElementCallback(TypedefElement element) {
|
||||
if (!shouldOutput(element)) return;
|
||||
TreeElements treeElements = new TreeElementMapping(element);
|
||||
backend2frontend.TreePrinter treePrinter =
|
||||
new backend2frontend.TreePrinter(treeElements);
|
||||
Node node = treePrinter.makeTypedef(element);
|
||||
addTopLevel(element, new ElementAst(node, treeElements));
|
||||
}
|
||||
|
||||
void newClassElementCallback(ClassElement classElement) {
|
||||
if (!shouldOutput(classElement)) return;
|
||||
addClass(classElement);
|
||||
}
|
||||
|
||||
void addMember(element) {
|
||||
if (element.isClassMember) {
|
||||
ClassElement enclosingClass = element.enclosingClass;
|
||||
assert(enclosingClass.isClass);
|
||||
assert(shouldOutput(enclosingClass));
|
||||
addClass(enclosingClass);
|
||||
classMembers[enclosingClass].add(element);
|
||||
if (enclosingClass.isEnumClass) return;
|
||||
processElement(element, parseElementAst(element));
|
||||
} else {
|
||||
if (element.isTopLevel) {
|
||||
addTopLevel(element, parseElementAst(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Main output generator for [DartOutputter] that emits dart code through a
|
||||
/// [CompilerOutputProvider].
|
||||
class MainOutputGenerator {
|
||||
final Map<ClassNode, List<Node>> memberNodes =
|
||||
new Map<ClassNode, List<Node>>();
|
||||
final List<Node> topLevelNodes = <Node>[];
|
||||
|
||||
/// Generates the code and returns the total size.
|
||||
int generateCode(
|
||||
LibraryInfo libraryInfo,
|
||||
ElementInfo elementInfo,
|
||||
PlaceholderCollector collector,
|
||||
PlaceholderRenamer placeholderRenamer,
|
||||
FunctionElement mainFunction,
|
||||
Uri outputUri,
|
||||
CompilerOutputProvider outputProvider,
|
||||
MirrorRenamer mirrorRenamer,
|
||||
{bool multiFile: false,
|
||||
bool forceStripTypes: false,
|
||||
bool enableMinification: false}) {
|
||||
for (Element element in elementInfo.topLevelElements) {
|
||||
topLevelNodes.add(elementInfo.elementAsts[element].ast);
|
||||
if (element.isClass) {
|
||||
ClassElement cls = element;
|
||||
if (cls.isMixinApplication || cls.isEnumClass) {
|
||||
continue;
|
||||
}
|
||||
final members = <Node>[];
|
||||
for (Element member in elementInfo.classMembers[cls]) {
|
||||
members.add(elementInfo.elementAsts[member].ast);
|
||||
}
|
||||
memberNodes[elementInfo.elementAsts[cls].ast] = members;
|
||||
}
|
||||
}
|
||||
|
||||
mirrorRenamer.addRenames(
|
||||
placeholderRenamer.renames, topLevelNodes, collector);
|
||||
|
||||
Map<LibraryElement, String> outputPaths = new Map<LibraryElement, String>();
|
||||
Map<LibraryElement, EmitterUnparser> unparsers =
|
||||
new Map<LibraryElement, EmitterUnparser>();
|
||||
|
||||
// The single unparser used if we collect all the output in one file.
|
||||
EmitterUnparser mainUnparser = multiFile
|
||||
? null
|
||||
: new EmitterUnparser(placeholderRenamer.renames,
|
||||
stripTypes: forceStripTypes, minify: enableMinification);
|
||||
|
||||
if (multiFile) {
|
||||
// TODO(sigurdm): Factor handling of library-paths out from emitting.
|
||||
String mainName = outputUri.pathSegments.last;
|
||||
String mainBaseName = mainName.endsWith(".dart")
|
||||
? mainName.substring(0, mainName.length - 5)
|
||||
: mainName;
|
||||
// Map each library to a path based on the uri of the original
|
||||
// library and [compiler.options.outputUri].
|
||||
Set<String> usedLibraryPaths = new Set<String>();
|
||||
for (LibraryElement library in libraryInfo.userLibraries) {
|
||||
if (library == mainFunction.library) {
|
||||
outputPaths[library] = mainBaseName;
|
||||
} else {
|
||||
List<String> names =
|
||||
library.canonicalUri.pathSegments.last.split(".");
|
||||
if (names.last == "dart") {
|
||||
names = names.sublist(0, names.length - 1);
|
||||
}
|
||||
outputPaths[library] =
|
||||
"$mainBaseName.${makeUnique(names.join("."), usedLibraryPaths)}";
|
||||
}
|
||||
}
|
||||
|
||||
/// Rewrites imports/exports to refer to the paths given in [outputPaths].
|
||||
for (LibraryElement outputLibrary in libraryInfo.userLibraries) {
|
||||
EmitterUnparser unparser = new EmitterUnparser(
|
||||
placeholderRenamer.renames,
|
||||
stripTypes: forceStripTypes,
|
||||
minify: enableMinification);
|
||||
unparsers[outputLibrary] = unparser;
|
||||
if (outputLibrary.hasLibraryName) {
|
||||
unparser.unparseLibraryName(outputLibrary.libraryName);
|
||||
}
|
||||
for (ImportElement import in outputLibrary.imports) {
|
||||
LibraryElement libraryElement = import.importedLibrary;
|
||||
String uri = outputPaths.containsKey(libraryElement)
|
||||
? "${outputPaths[libraryElement]}.dart"
|
||||
: libraryElement.canonicalUri.toString();
|
||||
unparser.unparseImportTag(uri);
|
||||
}
|
||||
for (ExportElement export in outputLibrary.exports) {
|
||||
LibraryElement libraryElement = export.exportedLibrary;
|
||||
String uri = outputPaths.containsKey(libraryElement)
|
||||
? "${outputPaths[libraryElement]}.dart"
|
||||
: libraryElement.canonicalUri.toString();
|
||||
unparser.unparseExportTag(uri);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
placeholderRenamer.platformImports
|
||||
.forEach((LibraryElement library, String prefix) {
|
||||
assert(library.isPlatformLibrary && !library.isInternalLibrary);
|
||||
mainUnparser.unparseImportTag(library.canonicalUri.toString());
|
||||
if (prefix != null) {
|
||||
// Adding a prefixed import because (some) top-level access need
|
||||
// it to avoid shadowing.
|
||||
// TODO(johnniwinther): Avoid prefix-less import if not needed.
|
||||
mainUnparser.unparseImportTag(library.canonicalUri.toString(),
|
||||
prefix: prefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < elementInfo.topLevelElements.length; i++) {
|
||||
Element element = elementInfo.topLevelElements.elementAt(i);
|
||||
Node node = topLevelNodes[i];
|
||||
Unparser unparser = multiFile ? unparsers[element.library] : mainUnparser;
|
||||
if (node is ClassNode) {
|
||||
// TODO(smok): Filter out default constructors here.
|
||||
unparser.unparseClassWithBody(node, memberNodes[node]);
|
||||
} else {
|
||||
unparser.unparse(node);
|
||||
}
|
||||
unparser.newline();
|
||||
}
|
||||
|
||||
int totalSize = 0;
|
||||
if (multiFile) {
|
||||
for (LibraryElement outputLibrary in libraryInfo.userLibraries) {
|
||||
// TODO(sigurdm): Make the unparser output directly into the buffer
|
||||
// instead of caching in `.result`.
|
||||
String code = unparsers[outputLibrary].result;
|
||||
totalSize += code.length;
|
||||
outputProvider(outputPaths[outputLibrary], "dart")
|
||||
..add(code)
|
||||
..close();
|
||||
}
|
||||
} else {
|
||||
String code = mainUnparser.result;
|
||||
outputProvider("", "dart")
|
||||
..add(code)
|
||||
..close();
|
||||
|
||||
totalSize = code.length;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
}
|
|
@ -1,724 +0,0 @@
|
|||
// 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.
|
||||
|
||||
part of dart_backend;
|
||||
|
||||
class LocalPlaceholder {
|
||||
final String identifier;
|
||||
final Set<Node> nodes;
|
||||
LocalPlaceholder(this.identifier) : nodes = new Set<Node>();
|
||||
int get hashCode => identifier.hashCode;
|
||||
String toString() => 'local_placeholder[id($identifier), nodes($nodes)]';
|
||||
}
|
||||
|
||||
class FunctionScope {
|
||||
final Set<String> parameterIdentifiers;
|
||||
final Set<LocalPlaceholder> localPlaceholders;
|
||||
FunctionScope()
|
||||
: parameterIdentifiers = new Set<String>(),
|
||||
localPlaceholders = new Set<LocalPlaceholder>();
|
||||
void registerParameter(Identifier node) {
|
||||
parameterIdentifiers.add(node.source);
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructorPlaceholder {
|
||||
final Identifier node;
|
||||
final ConstructorElement element;
|
||||
|
||||
ConstructorPlaceholder(this.node, this.element);
|
||||
}
|
||||
|
||||
class DeclarationTypePlaceholder {
|
||||
final TypeAnnotation typeNode;
|
||||
final bool requiresVar;
|
||||
DeclarationTypePlaceholder(this.typeNode, this.requiresVar);
|
||||
}
|
||||
|
||||
class SendVisitor extends Visitor {
|
||||
final TreeElements elements;
|
||||
final PlaceholderCollector collector;
|
||||
|
||||
SendVisitor(this.collector, this.elements);
|
||||
|
||||
visitSend(Send node) {
|
||||
Element element = elements[node];
|
||||
if (elements.isTypeLiteral(node)) {
|
||||
DartType type = elements.getTypeLiteralType(node);
|
||||
if (!type.isDynamic) {
|
||||
if (type is TypeVariableType) {
|
||||
collector.makeTypeVariablePlaceholder(node.selector, type);
|
||||
} else {
|
||||
collector.makeTypePlaceholder(node.selector, type);
|
||||
}
|
||||
}
|
||||
} else if (node.isSuperCall) {
|
||||
if (element != null && element.isConstructor) {
|
||||
collector.tryMakeConstructorPlaceholder(node, element);
|
||||
} else {
|
||||
collector.tryMakeMemberPlaceholder(node.selector);
|
||||
}
|
||||
} else if (node.isOperator) {
|
||||
return;
|
||||
} else if (node.isPropertyAccess) {
|
||||
if (!Elements.isUnresolved(element) && element.impliesType) {
|
||||
collector.makeElementPlaceholder(node, element);
|
||||
} else {
|
||||
visitGetterSend(node);
|
||||
}
|
||||
} else if (element != null && Initializers.isConstructorRedirect(node)) {
|
||||
visitStaticSend(node);
|
||||
} else if (Elements.isClosureSend(node, element)) {
|
||||
if (element != null) {
|
||||
collector.tryMakeLocalPlaceholder(element, node.selector);
|
||||
}
|
||||
} else {
|
||||
if (Elements.isUnresolved(element)) {
|
||||
if (element == null) {
|
||||
// Example: f() with 'f' unbound.
|
||||
// This can only happen inside an instance method.
|
||||
visitDynamicSend(node);
|
||||
} else {
|
||||
visitStaticSend(node);
|
||||
}
|
||||
} else if (element.isInstanceMember) {
|
||||
// Example: f() with 'f' bound to instance method.
|
||||
visitDynamicSend(node);
|
||||
} else if (!element.isInstanceMember) {
|
||||
// Example: A.f() or f() with 'f' bound to a static function.
|
||||
// Also includes new A() or new A.named() which is treated like a
|
||||
// static call to a factory.
|
||||
visitStaticSend(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitDynamicSend(Send node) {
|
||||
final element = elements[node];
|
||||
if (element == null || !element.isMalformed) {
|
||||
collector.tryMakeMemberPlaceholder(node.selector);
|
||||
}
|
||||
}
|
||||
|
||||
visitGetterSend(Send node) {
|
||||
final element = elements[node];
|
||||
// element == null means dynamic property access.
|
||||
if (element == null) {
|
||||
collector.tryMakeMemberPlaceholder(node.selector);
|
||||
} else if (element.isMalformed) {
|
||||
collector.makeUnresolvedPlaceholder(node);
|
||||
return;
|
||||
} else if (element.isPrefix) {
|
||||
// Node is prefix part in case of source 'lib.somesetter = 5;'
|
||||
collector.makeErasePrefixPlaceholder(node);
|
||||
} else if (Elements.isStaticOrTopLevel(element)) {
|
||||
// Unqualified or prefixed top level or static.
|
||||
collector.makeElementPlaceholder(node.selector, element);
|
||||
} else if (!element.isTopLevel) {
|
||||
if (element.isInstanceMember) {
|
||||
collector.tryMakeMemberPlaceholder(node.selector);
|
||||
} else {
|
||||
// May get FunctionExpression here in selector
|
||||
// in case of A(int this.f());
|
||||
if (node.selector is Identifier) {
|
||||
collector.tryMakeLocalPlaceholder(element, node.selector);
|
||||
} else {
|
||||
assert(node.selector is FunctionExpression);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitStaticSend(Send node) {
|
||||
Element element = elements[node];
|
||||
collector.mirrorRenamer
|
||||
.registerStaticSend(collector.currentElement, element, node);
|
||||
|
||||
if (Elements.isUnresolved(element) || element.isDeferredLoaderGetter) {
|
||||
return;
|
||||
}
|
||||
if (element.isConstructor || element.isFactoryConstructor) {
|
||||
// Rename named constructor in redirection position:
|
||||
// class C { C.named(); C.redirecting() : this.named(); }
|
||||
if (node.receiver is Identifier &&
|
||||
node.receiver.asIdentifier().isThis()) {
|
||||
assert(node.selector is Identifier);
|
||||
collector.tryMakeConstructorPlaceholder(node, element);
|
||||
}
|
||||
return;
|
||||
}
|
||||
collector.makeElementPlaceholder(node.selector, element);
|
||||
// Another ugly case: <lib prefix>.<top level> is represented as
|
||||
// receiver: lib prefix, selector: top level.
|
||||
if (element.isTopLevel && node.receiver != null) {
|
||||
assert(elements[node.receiver].isPrefix);
|
||||
// Hack: putting null into map overrides receiver of original node.
|
||||
collector.makeErasePrefixPlaceholder(node.receiver);
|
||||
}
|
||||
}
|
||||
|
||||
internalError(Spannable node, String reason) {
|
||||
collector.internalError(reason, node: node);
|
||||
}
|
||||
|
||||
visitNode(Node node) {
|
||||
internalError(node, "Unhandled node");
|
||||
}
|
||||
}
|
||||
|
||||
class PlaceholderCollector extends Visitor {
|
||||
final DiagnosticReporter reporter;
|
||||
final MirrorRenamer mirrorRenamer;
|
||||
final FunctionElement mainFunction;
|
||||
final Set<String> fixedMemberNames; // member names which cannot be renamed.
|
||||
final Map<Element, ElementAst> elementAsts;
|
||||
final Set<Node> prefixNodesToErase = new Set<Node>();
|
||||
final Set<Node> unresolvedNodes = new Set<Node>();
|
||||
final Map<Element, Set<Node>> elementNodes = new Map<Element, Set<Node>>();
|
||||
final Map<FunctionElement, FunctionScope> functionScopes =
|
||||
new Map<FunctionElement, FunctionScope>();
|
||||
final Map<LibraryElement, Set<Identifier>> privateNodes =
|
||||
new Map<LibraryElement, Set<Identifier>>();
|
||||
final List<DeclarationTypePlaceholder> declarationTypePlaceholders =
|
||||
new List<DeclarationTypePlaceholder>();
|
||||
final Map<String, Set<Identifier>> memberPlaceholders =
|
||||
new Map<String, Set<Identifier>>();
|
||||
final List<ConstructorPlaceholder> constructorPlaceholders =
|
||||
new List<ConstructorPlaceholder>();
|
||||
Map<String, LocalPlaceholder> currentLocalPlaceholders;
|
||||
Element currentElement;
|
||||
FunctionElement topmostEnclosingFunction;
|
||||
TreeElements treeElements;
|
||||
|
||||
get currentFunctionScope => functionScopes.putIfAbsent(
|
||||
topmostEnclosingFunction, () => new FunctionScope());
|
||||
|
||||
PlaceholderCollector(this.reporter, this.mirrorRenamer, this.fixedMemberNames,
|
||||
this.elementAsts, this.mainFunction);
|
||||
|
||||
void collectFunctionDeclarationPlaceholders(
|
||||
FunctionElement element, FunctionExpression node) {
|
||||
if (element.isConstructor) {
|
||||
ConstructorElement constructor = element;
|
||||
tryMakeConstructorPlaceholder(node.name, element);
|
||||
RedirectingFactoryBody bodyAsRedirectingFactoryBody =
|
||||
node.body.asRedirectingFactoryBody();
|
||||
if (bodyAsRedirectingFactoryBody != null) {
|
||||
// Factory redirection.
|
||||
FunctionElement redirectTarget = constructor.immediateRedirectionTarget;
|
||||
assert(redirectTarget != null && redirectTarget != element);
|
||||
tryMakeConstructorPlaceholder(
|
||||
bodyAsRedirectingFactoryBody.constructorReference, redirectTarget);
|
||||
}
|
||||
} else if (Elements.isStaticOrTopLevel(element)) {
|
||||
// Note: this code should only rename private identifiers for class'
|
||||
// fields/getters/setters/methods. Top-level identifiers are renamed
|
||||
// just to escape conflicts and that should be enough as we shouldn't
|
||||
// be able to resolve private identifiers for other libraries.
|
||||
makeElementPlaceholder(node.name, element);
|
||||
} else if (element.isClassMember) {
|
||||
if (node.name is Identifier) {
|
||||
tryMakeMemberPlaceholder(node.name);
|
||||
} else {
|
||||
assert(node.name.asSend().isOperator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collectFieldDeclarationPlaceholders(Element element, Node node) {
|
||||
Identifier name = node is Identifier ? node : node.asSend().selector;
|
||||
if (Elements.isStaticOrTopLevel(element)) {
|
||||
makeElementPlaceholder(name, element);
|
||||
} else if (Elements.isInstanceField(element)) {
|
||||
tryMakeMemberPlaceholder(name);
|
||||
}
|
||||
}
|
||||
|
||||
void collect(Element element) {
|
||||
this.currentElement = element;
|
||||
this.topmostEnclosingFunction = null;
|
||||
final ElementAst elementAst = elementAsts[element];
|
||||
this.treeElements = elementAst.treeElements;
|
||||
Node elementNode = elementAst.ast;
|
||||
if (element is FunctionElement) {
|
||||
collectFunctionDeclarationPlaceholders(element, elementNode);
|
||||
} else if (element is VariableElement) {
|
||||
VariableDefinitions definitions = elementNode;
|
||||
Node definition = definitions.definitions.nodes.head;
|
||||
collectFieldDeclarationPlaceholders(element, definition);
|
||||
makeVarDeclarationTypePlaceholder(definitions);
|
||||
} else {
|
||||
assert(element is ClassElement || element is TypedefElement);
|
||||
}
|
||||
currentLocalPlaceholders = new Map<String, LocalPlaceholder>();
|
||||
if (!(element is ConstructorElement && element.isRedirectingFactory)) {
|
||||
// Do not visit the body of redirecting factories.
|
||||
reporter.withCurrentElement(element, () {
|
||||
elementNode.accept(this);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(karlklose): should we create placeholders for these?
|
||||
bool isTypedefParameter(Element element) {
|
||||
return element != null &&
|
||||
element.enclosingElement != null &&
|
||||
element.enclosingElement.isTypedef;
|
||||
}
|
||||
|
||||
void tryMakeLocalPlaceholder(Element element, Identifier node) {
|
||||
bool isNamedOptionalParameter() {
|
||||
FunctionTypedElement function = element.enclosingElement;
|
||||
FunctionSignature signature = function.functionSignature;
|
||||
if (!signature.optionalParametersAreNamed) return false;
|
||||
for (Element parameter in signature.optionalParameters) {
|
||||
if (identical(parameter, element)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (element.isRegularParameter &&
|
||||
!isTypedefParameter(element) &&
|
||||
isNamedOptionalParameter()) {
|
||||
currentFunctionScope.registerParameter(node);
|
||||
} else if (Elements.isLocal(element) && !isTypedefParameter(element)) {
|
||||
makeLocalPlaceholder(node);
|
||||
}
|
||||
}
|
||||
|
||||
void tryMakeMemberPlaceholder(Identifier node) {
|
||||
assert(node != null);
|
||||
if (node is Operator) return;
|
||||
String identifier = node.source;
|
||||
if (fixedMemberNames.contains(identifier)) return;
|
||||
memberPlaceholders
|
||||
.putIfAbsent(identifier, () => new Set<Identifier>())
|
||||
.add(node);
|
||||
}
|
||||
|
||||
void makeTypePlaceholder(Node node, DartType type) {
|
||||
Send send = node.asSend();
|
||||
if (send != null) {
|
||||
// Prefix.
|
||||
assert(send.receiver is Identifier);
|
||||
assert(send.selector is Identifier);
|
||||
makeErasePrefixPlaceholder(send.receiver);
|
||||
node = send.selector;
|
||||
}
|
||||
makeElementPlaceholder(node, type.element);
|
||||
}
|
||||
|
||||
void makeTypeVariablePlaceholder(Node node, TypeVariableType type) {
|
||||
Send send = node.asSend();
|
||||
if (send != null) {
|
||||
// Prefix.
|
||||
assert(send.receiver is Identifier);
|
||||
assert(send.selector is Identifier);
|
||||
makeErasePrefixPlaceholder(send.receiver);
|
||||
node = send.selector;
|
||||
}
|
||||
tryMakeMemberPlaceholder(node);
|
||||
}
|
||||
|
||||
void makeOmitDeclarationTypePlaceholder(TypeAnnotation type) {
|
||||
if (type == null) return;
|
||||
declarationTypePlaceholders
|
||||
.add(new DeclarationTypePlaceholder(type, false));
|
||||
}
|
||||
|
||||
void makeVarDeclarationTypePlaceholder(VariableDefinitions node) {
|
||||
// TODO(smok): Maybe instead of calling this method and
|
||||
// makeDeclaratioTypePlaceholder have type declaration placeholder
|
||||
// collector logic in visitVariableDefinitions when resolver becomes better
|
||||
// and/or catch syntax changes.
|
||||
if (node.type == null) return;
|
||||
bool requiresVar = !node.modifiers.isFinalOrConst;
|
||||
declarationTypePlaceholders
|
||||
.add(new DeclarationTypePlaceholder(node.type, requiresVar));
|
||||
}
|
||||
|
||||
/// Marks [node] to be erased in the output.
|
||||
/// This is done for library prefixes because they are not used in the output
|
||||
/// because all imports are flattened and conflicts are renamed away.
|
||||
void makeErasePrefixPlaceholder(Node node) {
|
||||
assert(node is Identifier || node is Send);
|
||||
prefixNodesToErase.add(node);
|
||||
}
|
||||
|
||||
void makeElementPlaceholder(Node node, Element element) {
|
||||
assert(node != null);
|
||||
assert(element != null);
|
||||
LibraryElement library = element.library;
|
||||
if (identical(element, mainFunction)) return;
|
||||
if (library.isDartCore) return;
|
||||
|
||||
if (library.isPlatformLibrary && !element.isTopLevel) {
|
||||
return;
|
||||
}
|
||||
|
||||
ClassElement cls = element.enclosingClass;
|
||||
if (cls != null && cls.isEnumClass) {
|
||||
// Enums and enum values cannot be changed, since the semantics of
|
||||
// `toString` is defined by the names of the declarations.
|
||||
return;
|
||||
}
|
||||
|
||||
if (element.isAccessor) {
|
||||
element = (element as AccessorElement).abstractField;
|
||||
}
|
||||
elementNodes.putIfAbsent(element, () => new Set<Node>()).add(node);
|
||||
}
|
||||
|
||||
/// Marks [node] to be renamed per-library if it names an instance member
|
||||
/// and has a private name.
|
||||
void tryMakePrivateIdentifier(Node node, Element element) {
|
||||
if (node is Identifier &&
|
||||
!Elements.isStaticOrTopLevel(element) &&
|
||||
!Elements.isLocal(element) &&
|
||||
Name.isPrivateName(node.source)) {
|
||||
privateNodes
|
||||
.putIfAbsent(currentElement.library, () => new Set<Identifier>())
|
||||
.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
void makeUnresolvedPlaceholder(Node node) {
|
||||
unresolvedNodes.add(node);
|
||||
}
|
||||
|
||||
void makeLocalPlaceholder(Identifier identifier) {
|
||||
LocalPlaceholder getLocalPlaceholder() {
|
||||
String name = identifier.source;
|
||||
return currentLocalPlaceholders.putIfAbsent(name, () {
|
||||
LocalPlaceholder localPlaceholder = new LocalPlaceholder(name);
|
||||
currentFunctionScope.localPlaceholders.add(localPlaceholder);
|
||||
return localPlaceholder;
|
||||
});
|
||||
}
|
||||
getLocalPlaceholder().nodes.add(identifier);
|
||||
}
|
||||
|
||||
/// Finds the first constructor on the chain of definingConstructor from
|
||||
/// [element] that is not in a synthetic class.
|
||||
Element findDefiningConstructor(ConstructorElement element) {
|
||||
while (element.definingConstructor != null) {
|
||||
element = element.definingConstructor;
|
||||
}
|
||||
return element;
|
||||
}
|
||||
|
||||
void tryMakeConstructorPlaceholder(Node node, ConstructorElement element) {
|
||||
if (Elements.isUnresolved(element)) {
|
||||
makeUnresolvedPlaceholder(node);
|
||||
return;
|
||||
}
|
||||
// A library prefix.
|
||||
Node prefix;
|
||||
// The name of the class with the constructor.
|
||||
Node className;
|
||||
// Will be null for unnamed constructors.
|
||||
Identifier constructorName;
|
||||
// First deconstruct the constructor, there are 4 possibilities:
|
||||
// ClassName()
|
||||
// prefix.ClassName()
|
||||
// ClassName.constructorName()
|
||||
// prefix.ClassName.constructorName()
|
||||
if (node is Send) {
|
||||
if (node.receiver is Send) {
|
||||
Send receiver = node.receiver;
|
||||
// prefix.ClassName.constructorName()
|
||||
assert(treeElements[receiver.receiver] != null &&
|
||||
treeElements[receiver.receiver].isPrefix);
|
||||
prefix = receiver.receiver;
|
||||
className = receiver.selector;
|
||||
constructorName = node.selector;
|
||||
} else {
|
||||
Element receiverElement = treeElements[node.receiver];
|
||||
if (receiverElement != null && receiverElement.isPrefix) {
|
||||
// prefix.ClassName()
|
||||
prefix = node.receiver;
|
||||
className = node.selector;
|
||||
} else {
|
||||
// ClassName.constructorName()
|
||||
className = node.receiver;
|
||||
constructorName = node.selector;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ClassName()
|
||||
className = node;
|
||||
}
|
||||
|
||||
if (prefix != null) {
|
||||
makeErasePrefixPlaceholder(prefix);
|
||||
}
|
||||
|
||||
if (className is TypeAnnotation) {
|
||||
visitTypeAnnotation(className);
|
||||
} else if (Elements.isUnresolved(element)) {
|
||||
// We handle unresolved nodes elsewhere.
|
||||
} else if (className.isThis() || className.isSuper()) {
|
||||
// Do not rename super and this.
|
||||
} else if (className is Identifier) {
|
||||
makeElementPlaceholder(className, element.contextClass);
|
||||
} else {
|
||||
throw "Bad type of constructor name $className";
|
||||
}
|
||||
|
||||
if (constructorName != null) {
|
||||
Element definingConstructor = findDefiningConstructor(element);
|
||||
constructorPlaceholders.add(
|
||||
new ConstructorPlaceholder(constructorName, definingConstructor));
|
||||
tryMakePrivateIdentifier(constructorName, element);
|
||||
}
|
||||
}
|
||||
|
||||
void internalError(String reason, {Node node}) {
|
||||
reporter.internalError(node, reason);
|
||||
}
|
||||
|
||||
visit(Node node) => (node == null) ? null : node.accept(this);
|
||||
|
||||
visitNode(Node node) {
|
||||
node.visitChildren(this);
|
||||
} // We must go deeper.
|
||||
|
||||
visitNewExpression(NewExpression node) {
|
||||
Send send = node.send;
|
||||
DartType type = treeElements.getType(node);
|
||||
assert(type != null);
|
||||
Element constructor = treeElements[send];
|
||||
assert(constructor != null);
|
||||
assert(send.receiver == null);
|
||||
if (!Elements.isMalformed(constructor)) {
|
||||
tryMakeConstructorPlaceholder(node.send.selector, constructor);
|
||||
// TODO(smok): Should this be in visitNamedArgument?
|
||||
// Field names can be exposed as names of optional arguments, e.g.
|
||||
// class C {
|
||||
// final field;
|
||||
// C([this.field]);
|
||||
// }
|
||||
// Do not forget to rename them as well.
|
||||
FunctionElement constructorFunction = constructor;
|
||||
List<Element> optionalParameters =
|
||||
constructorFunction.functionSignature.optionalParameters;
|
||||
for (final argument in send.argumentsNode) {
|
||||
NamedArgument named = argument.asNamedArgument();
|
||||
if (named == null) continue;
|
||||
Identifier name = named.name;
|
||||
String nameAsString = name.source;
|
||||
for (final parameter in optionalParameters) {
|
||||
if (parameter.isInitializingFormal) {
|
||||
if (parameter.name == nameAsString) {
|
||||
tryMakeMemberPlaceholder(name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
makeUnresolvedPlaceholder(node.send.selector);
|
||||
}
|
||||
visit(node.send.argumentsNode);
|
||||
}
|
||||
|
||||
visitSend(Send send) {
|
||||
Element element = treeElements[send];
|
||||
tryMakePrivateIdentifier(send.selector, element);
|
||||
new SendVisitor(this, treeElements).visitSend(send);
|
||||
send.visitChildren(this);
|
||||
}
|
||||
|
||||
visitSendSet(SendSet send) {
|
||||
Element element = treeElements[send];
|
||||
if (Elements.isMalformed(element)) {
|
||||
// Complicated case: constructs like receiver.selector++ can resolve
|
||||
// to ErroneousElement. Fortunately, receiver.selector still
|
||||
// can be resoved via treeElements[send.selector], that's all
|
||||
// that is needed to rename the construct properly.
|
||||
element = treeElements[send.selector];
|
||||
}
|
||||
tryMakePrivateIdentifier(send.selector, element);
|
||||
if (element == null) {
|
||||
if (send.receiver != null) tryMakeMemberPlaceholder(send.selector);
|
||||
} else if (!element.isMalformed) {
|
||||
if (Elements.isStaticOrTopLevel(element)) {
|
||||
// TODO(smok): Worth investigating why sometimes we get getter/setter
|
||||
// here and sometimes abstract field.
|
||||
assert(element.isClass ||
|
||||
element is VariableElement ||
|
||||
element.isAccessor ||
|
||||
element.isAbstractField ||
|
||||
element.isFunction ||
|
||||
element.isTypedef ||
|
||||
element is TypeVariableElement);
|
||||
makeElementPlaceholder(send.selector, element);
|
||||
} else {
|
||||
Identifier identifier = send.selector.asIdentifier();
|
||||
if (identifier == null) {
|
||||
// Handle optional function expression parameters with default values.
|
||||
identifier = send.selector.asFunctionExpression().name;
|
||||
}
|
||||
if (Elements.isInstanceField(element)) {
|
||||
tryMakeMemberPlaceholder(identifier);
|
||||
} else {
|
||||
tryMakeLocalPlaceholder(element, identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
send.visitChildren(this);
|
||||
}
|
||||
|
||||
visitTypeAnnotation(TypeAnnotation node) {
|
||||
final type = treeElements.getType(node);
|
||||
assert(invariant(node, type != null,
|
||||
message: "Missing type for type annotation: $treeElements"));
|
||||
if (!type.isVoid) {
|
||||
if (!type.treatAsDynamic) {
|
||||
if (type is TypeVariableType) {
|
||||
makeTypeVariablePlaceholder(node.typeName, type);
|
||||
} else {
|
||||
makeTypePlaceholder(node.typeName, type);
|
||||
}
|
||||
} else if (!type.isDynamic) {
|
||||
makeUnresolvedPlaceholder(node.typeName);
|
||||
}
|
||||
}
|
||||
// Visit only type arguments, otherwise in case of lib.Class type
|
||||
// annotation typeName is Send and we go to visitGetterSend, as a result
|
||||
// "Class" is added to member placeholders.
|
||||
visit(node.typeArguments);
|
||||
}
|
||||
|
||||
visitVariableDefinitions(VariableDefinitions node) {
|
||||
// Collect only local placeholders.
|
||||
for (Node definition in node.definitions.nodes) {
|
||||
Element definitionElement = treeElements[definition];
|
||||
// definitionElement may be null if we're inside variable definitions
|
||||
// of a function that is a parameter of another function.
|
||||
// TODO(smok): Fix this when resolver correctly deals with
|
||||
// such cases.
|
||||
if (definitionElement == null) continue;
|
||||
|
||||
Send send = definition.asSend();
|
||||
Identifier identifier = definition is Identifier
|
||||
? definition
|
||||
: definition is Send
|
||||
? (send.selector is Identifier ? send.selector : null)
|
||||
: null;
|
||||
|
||||
tryMakePrivateIdentifier(identifier, definitionElement);
|
||||
|
||||
if (send != null) {
|
||||
// May get FunctionExpression here in definition.selector
|
||||
// in case of A(int this.f());
|
||||
if (send.selector is Identifier) {
|
||||
if (definitionElement.isInitializingFormal) {
|
||||
tryMakeMemberPlaceholder(send.selector);
|
||||
} else {
|
||||
tryMakeLocalPlaceholder(definitionElement, send.selector);
|
||||
}
|
||||
} else {
|
||||
assert(send.selector is FunctionExpression);
|
||||
if (definitionElement.isInitializingFormal) {
|
||||
tryMakeMemberPlaceholder(send.selector.asFunctionExpression().name);
|
||||
}
|
||||
}
|
||||
} else if (definition is Identifier) {
|
||||
tryMakeLocalPlaceholder(definitionElement, definition);
|
||||
} else if (definition is FunctionExpression) {
|
||||
// Skip, it will be processed in visitFunctionExpression.
|
||||
} else {
|
||||
internalError('Unexpected definition structure $definition');
|
||||
}
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitFunctionExpression(FunctionExpression node) {
|
||||
bool isKeyword(Identifier id) =>
|
||||
id != null && Keyword.keywords[id.source] != null;
|
||||
|
||||
Element element = treeElements[node];
|
||||
// May get null;
|
||||
if (element != null) {
|
||||
tryMakePrivateIdentifier(node.name, element);
|
||||
|
||||
// Rename only local functions.
|
||||
if (topmostEnclosingFunction == null &&
|
||||
element is! LocalParameterElement &&
|
||||
element is! InitializingFormalElement) {
|
||||
topmostEnclosingFunction = element;
|
||||
}
|
||||
if (!identical(element, currentElement)) {
|
||||
if (node.name != null) {
|
||||
assert(node.name is Identifier);
|
||||
tryMakeLocalPlaceholder(element, node.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.visitChildren(this);
|
||||
|
||||
// Make sure we don't omit return type of methods which names are
|
||||
// identifiers, because the following works fine:
|
||||
// int interface() => 1;
|
||||
// But omitting 'int' makes VM unhappy.
|
||||
// TODO(smok): Remove it when http://dartbug.com/5278 is fixed.
|
||||
if (node.name == null || !isKeyword(node.name.asIdentifier())) {
|
||||
makeOmitDeclarationTypePlaceholder(node.returnType);
|
||||
}
|
||||
collectFunctionParameters(node.parameters);
|
||||
}
|
||||
|
||||
void collectFunctionParameters(NodeList parameters) {
|
||||
if (parameters == null) return;
|
||||
for (Node parameter in parameters.nodes) {
|
||||
if (parameter is NodeList) {
|
||||
// Optional parameter list.
|
||||
collectFunctionParameters(parameter);
|
||||
} else {
|
||||
assert(parameter is VariableDefinitions);
|
||||
makeOmitDeclarationTypePlaceholder(
|
||||
parameter.asVariableDefinitions().type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
visitClassNode(ClassNode node) {
|
||||
ClassElement classElement = currentElement;
|
||||
makeElementPlaceholder(node.name, classElement);
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitNamedMixinApplication(NamedMixinApplication node) {
|
||||
ClassElement classElement = currentElement;
|
||||
makeElementPlaceholder(node.name, classElement);
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitTypeVariable(TypeVariable node) {
|
||||
DartType type = treeElements.getType(node);
|
||||
assert(invariant(node, type != null,
|
||||
message: "Missing type for type variable: $treeElements"));
|
||||
makeTypeVariablePlaceholder(node.name, type);
|
||||
node.visitChildren(this);
|
||||
}
|
||||
|
||||
visitTypedef(Typedef node) {
|
||||
assert(currentElement is TypedefElement);
|
||||
makeElementPlaceholder(node.name, currentElement);
|
||||
node.visitChildren(this);
|
||||
makeOmitDeclarationTypePlaceholder(node.returnType);
|
||||
collectFunctionParameters(node.formals);
|
||||
}
|
||||
|
||||
visitBlock(Block node) {
|
||||
for (Node statement in node.statements.nodes) {
|
||||
if (statement is VariableDefinitions) {
|
||||
makeVarDeclarationTypePlaceholder(statement);
|
||||
}
|
||||
}
|
||||
node.visitChildren(this);
|
||||
}
|
||||
}
|
|
@ -1,372 +0,0 @@
|
|||
// 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.
|
||||
|
||||
part of dart_backend;
|
||||
|
||||
Comparator get _compareNodes => compareBy((n) => n.getBeginToken().charOffset);
|
||||
|
||||
abstract class Renamable implements Comparable {
|
||||
final int RENAMABLE_TYPE_ELEMENT = 1;
|
||||
final int RENAMABLE_TYPE_MEMBER = 2;
|
||||
final int RENAMABLE_TYPE_LOCAL = 3;
|
||||
|
||||
final Set<Node> nodes;
|
||||
|
||||
Renamable(this.nodes);
|
||||
int compareTo(Renamable other) {
|
||||
int nodesDiff = other.nodes.length.compareTo(this.nodes.length);
|
||||
if (nodesDiff != 0) return nodesDiff;
|
||||
int typeDiff = this.kind.compareTo(other.kind);
|
||||
return typeDiff != 0 ? typeDiff : compareInternals(other);
|
||||
}
|
||||
|
||||
int compareInternals(Renamable other);
|
||||
int get kind;
|
||||
|
||||
String createNewName(PlaceholderRenamer placeholderRenamer);
|
||||
}
|
||||
|
||||
class GlobalRenamable extends Renamable {
|
||||
final Entity entity;
|
||||
|
||||
GlobalRenamable(this.entity, Set<Node> nodes) : super(nodes);
|
||||
|
||||
int compareInternals(GlobalRenamable other) =>
|
||||
compareElements(this.entity, other.entity);
|
||||
int get kind => RENAMABLE_TYPE_ELEMENT;
|
||||
String createNewName(PlaceholderRenamer placeholderRenamer) {
|
||||
return placeholderRenamer._renameGlobal(entity);
|
||||
}
|
||||
}
|
||||
|
||||
class MemberRenamable extends Renamable {
|
||||
final String identifier;
|
||||
MemberRenamable(this.identifier, Set<Node> nodes) : super(nodes);
|
||||
int compareInternals(MemberRenamable other) =>
|
||||
this.identifier.compareTo(other.identifier);
|
||||
int get kind => RENAMABLE_TYPE_MEMBER;
|
||||
String createNewName(PlaceholderRenamer placeholderRenamer) {
|
||||
return placeholderRenamer._generateMemberName(identifier);
|
||||
}
|
||||
}
|
||||
|
||||
class LocalRenamable extends Renamable {
|
||||
LocalRenamable(Set<Node> nodes) : super(nodes);
|
||||
int compareInternals(LocalRenamable other) => _compareNodes(
|
||||
sorted(this.nodes, _compareNodes)[0],
|
||||
sorted(other.nodes, _compareNodes)[0]);
|
||||
int get kind => RENAMABLE_TYPE_LOCAL;
|
||||
String createNewName(PlaceholderRenamer placeholderRenamer) {
|
||||
return placeholderRenamer._generateUniqueTopLevelName("");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames only top-level elements that would lead to ambiguity if not renamed.
|
||||
*/
|
||||
class PlaceholderRenamer {
|
||||
/// After running [computeRenames] this will contain the computed renames.
|
||||
final Map<Node, String> renames = new Map<Node, String>();
|
||||
|
||||
/// After running [computeRenames] this will map the used platform
|
||||
/// libraries to their respective prefixes.
|
||||
final Map<LibraryElement, String> platformImports =
|
||||
<LibraryElement, String>{};
|
||||
|
||||
final bool enableMinification;
|
||||
final Set<String> fixedDynamicNames;
|
||||
final Set<String> fixedStaticNames;
|
||||
final Map<Element, LibraryElement> reexportingLibraries;
|
||||
final bool cutDeclarationTypes;
|
||||
|
||||
final Map<Entity, String> _renamedCache = new Map<Entity, String>();
|
||||
final Map<Entity, Map<String, String>> _privateCache =
|
||||
new Map<Entity, Map<String, String>>();
|
||||
|
||||
// Identifiers that has already been used, or are reserved by the
|
||||
// language/platform.
|
||||
Set<String> _forbiddenIdentifiers;
|
||||
Set<String> _allNamedParameterIdentifiers;
|
||||
|
||||
Generator _generator;
|
||||
|
||||
PlaceholderRenamer(
|
||||
this.fixedDynamicNames, this.fixedStaticNames, this.reexportingLibraries,
|
||||
{this.enableMinification, this.cutDeclarationTypes});
|
||||
|
||||
void _renameNodes(Iterable<Node> nodes, String renamer(Node node)) {
|
||||
for (Node node in sorted(nodes, _compareNodes)) {
|
||||
renames[node] = renamer(node);
|
||||
}
|
||||
}
|
||||
|
||||
String _generateUniqueTopLevelName(String originalName) {
|
||||
String newName = _generator.generate(originalName, (name) {
|
||||
return _forbiddenIdentifiers.contains(name) ||
|
||||
_allNamedParameterIdentifiers.contains(name);
|
||||
});
|
||||
_forbiddenIdentifiers.add(newName);
|
||||
return newName;
|
||||
}
|
||||
|
||||
String _generateMemberName(String original) {
|
||||
return _generator.generate(original, _forbiddenIdentifiers.contains);
|
||||
}
|
||||
|
||||
/// Looks up [originalName] in the [_privateCache] cache of [library].
|
||||
/// If [originalName] was not renamed before, generate a new name.
|
||||
String _getPrivateName(LibraryElement library, String originalName) {
|
||||
return _privateCache
|
||||
.putIfAbsent(library, () => new Map<String, String>())
|
||||
.putIfAbsent(
|
||||
originalName, () => _generateUniqueTopLevelName(originalName));
|
||||
}
|
||||
|
||||
String _renameConstructor(ConstructorPlaceholder placeholder) {
|
||||
String name = placeholder.element.name;
|
||||
if (name == '') return "";
|
||||
String result = _renameGlobal(placeholder.element);
|
||||
return result;
|
||||
}
|
||||
|
||||
String _renameGlobal(Entity entity) {
|
||||
assert(entity is! Element ||
|
||||
Elements.isMalformed(entity) ||
|
||||
Elements.isStaticOrTopLevel(entity) ||
|
||||
entity is TypeVariableElement);
|
||||
// TODO(smok): We may want to reuse class static field and method names.
|
||||
if (entity is Element) {
|
||||
LibraryElement library = entity.library;
|
||||
if (reexportingLibraries.containsKey(entity)) {
|
||||
library = reexportingLibraries[entity];
|
||||
}
|
||||
if (library.isPlatformLibrary) {
|
||||
// TODO(johnniwinther): Handle prefixes for dart:core.
|
||||
if (library.canonicalUri == Uris.dart_core) return entity.name;
|
||||
if (library.isInternalLibrary) {
|
||||
throw new SpannableAssertionFailure(
|
||||
entity,
|
||||
"Internal library $library should never have been imported from "
|
||||
"the code compiled by dart2dart.");
|
||||
}
|
||||
|
||||
String prefix = platformImports.putIfAbsent(library, () => null);
|
||||
if (entity.isTopLevel && fixedDynamicNames.contains(entity.name)) {
|
||||
if (prefix == null) {
|
||||
prefix = _generateUniqueTopLevelName('');
|
||||
platformImports[library] = prefix;
|
||||
}
|
||||
return '$prefix.${entity.name}';
|
||||
}
|
||||
return entity.name;
|
||||
}
|
||||
}
|
||||
String name = _renamedCache.putIfAbsent(
|
||||
entity, () => _generateUniqueTopLevelName(entity.name));
|
||||
// Look up in [_renamedCache] for a name for [entity] .
|
||||
// If it was not renamed before, generate a new name.
|
||||
return name;
|
||||
}
|
||||
|
||||
void _computeMinifiedRenames(PlaceholderCollector placeholderCollector) {
|
||||
_generator = new MinifyingGenerator();
|
||||
|
||||
// Build a list sorted by usage of local nodes that will be renamed to
|
||||
// the same identifier. So the top-used local variables in all functions
|
||||
// will be renamed first and will all share the same new identifier.
|
||||
int maxLength = placeholderCollector.functionScopes.values
|
||||
.fold(0, (a, b) => max(a, b.localPlaceholders.length));
|
||||
|
||||
List<Set<Node>> allLocals =
|
||||
new List<Set<Node>>.generate(maxLength, (_) => new Set<Node>());
|
||||
|
||||
for (FunctionScope functionScope
|
||||
in placeholderCollector.functionScopes.values) {
|
||||
// Add current sorted local identifiers to the whole sorted list
|
||||
// of all local identifiers for all functions.
|
||||
List<LocalPlaceholder> currentSortedPlaceholders = sorted(
|
||||
functionScope.localPlaceholders,
|
||||
compareBy((LocalPlaceholder ph) => -ph.nodes.length));
|
||||
|
||||
List<Set<Node>> currentSortedNodes = currentSortedPlaceholders
|
||||
.map((LocalPlaceholder ph) => ph.nodes)
|
||||
.toList();
|
||||
|
||||
for (int i = 0; i < currentSortedNodes.length; i++) {
|
||||
allLocals[i].addAll(currentSortedNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Rename elements, members and locals together based on their usage
|
||||
// count, otherwise when we rename elements first there will be no good
|
||||
// identifiers left for members even if they are used often.
|
||||
List<Renamable> renamables = new List<Renamable>();
|
||||
placeholderCollector.elementNodes
|
||||
.forEach((Element element, Set<Node> nodes) {
|
||||
renamables.add(new GlobalRenamable(element, nodes));
|
||||
});
|
||||
placeholderCollector.memberPlaceholders
|
||||
.forEach((String memberName, Set<Identifier> identifiers) {
|
||||
renamables.add(new MemberRenamable(memberName, identifiers));
|
||||
});
|
||||
for (Set<Node> localIdentifiers in allLocals) {
|
||||
renamables.add(new LocalRenamable(localIdentifiers));
|
||||
}
|
||||
renamables.sort();
|
||||
for (Renamable renamable in renamables) {
|
||||
String newName = renamable.createNewName(this);
|
||||
_renameNodes(renamable.nodes, (_) => newName);
|
||||
}
|
||||
}
|
||||
|
||||
void _computeNonMinifiedRenames(PlaceholderCollector placeholderCollector) {
|
||||
_generator = new ConservativeGenerator();
|
||||
// Rename elements.
|
||||
placeholderCollector.elementNodes
|
||||
.forEach((Element element, Set<Node> nodes) {
|
||||
_renameNodes(nodes, (_) => _renameGlobal(element));
|
||||
});
|
||||
|
||||
// Rename locals.
|
||||
placeholderCollector.functionScopes
|
||||
.forEach((functionElement, functionScope) {
|
||||
Set<String> memberIdentifiers = new Set<String>();
|
||||
Set<LocalPlaceholder> placeholders = functionScope.localPlaceholders;
|
||||
if (functionElement != null && functionElement.enclosingClass != null) {
|
||||
functionElement.enclosingClass.forEachMember((enclosingClass, member) {
|
||||
memberIdentifiers.add(member.name);
|
||||
});
|
||||
}
|
||||
Set<String> usedLocalIdentifiers = new Set<String>();
|
||||
for (LocalPlaceholder placeholder in placeholders) {
|
||||
String nextId = _generator.generate(placeholder.identifier, (name) {
|
||||
return functionScope.parameterIdentifiers.contains(name) ||
|
||||
_forbiddenIdentifiers.contains(name) ||
|
||||
usedLocalIdentifiers.contains(name) ||
|
||||
memberIdentifiers.contains(name);
|
||||
});
|
||||
usedLocalIdentifiers.add(nextId);
|
||||
_renameNodes(placeholder.nodes, (_) => nextId);
|
||||
}
|
||||
});
|
||||
|
||||
// Do not rename members to top-levels, that allows to avoid renaming
|
||||
// members to constructors.
|
||||
placeholderCollector.memberPlaceholders.forEach((identifier, nodes) {
|
||||
String newIdentifier = _generateMemberName(identifier);
|
||||
_renameNodes(nodes, (_) => newIdentifier);
|
||||
});
|
||||
}
|
||||
|
||||
/// Finds renamings for all the placeholders in [placeholderCollector] and
|
||||
/// stores them in [renames].
|
||||
/// Also adds to [platformImports] all the platform-libraries that are used.
|
||||
void computeRenames(PlaceholderCollector placeholderCollector) {
|
||||
_allNamedParameterIdentifiers = new Set<String>();
|
||||
for (FunctionScope functionScope
|
||||
in placeholderCollector.functionScopes.values) {
|
||||
_allNamedParameterIdentifiers.addAll(functionScope.parameterIdentifiers);
|
||||
}
|
||||
|
||||
_forbiddenIdentifiers = new Set<String>.from(fixedDynamicNames);
|
||||
_forbiddenIdentifiers.addAll(fixedStaticNames);
|
||||
_forbiddenIdentifiers.addAll(Keyword.keywords.keys);
|
||||
_forbiddenIdentifiers.add('main');
|
||||
|
||||
if (enableMinification) {
|
||||
_computeMinifiedRenames(placeholderCollector);
|
||||
} else {
|
||||
_computeNonMinifiedRenames(placeholderCollector);
|
||||
}
|
||||
|
||||
// Rename constructors.
|
||||
for (ConstructorPlaceholder placeholder
|
||||
in placeholderCollector.constructorPlaceholders) {
|
||||
renames[placeholder.node] = _renameConstructor(placeholder);
|
||||
}
|
||||
;
|
||||
|
||||
// Rename private identifiers uniquely for each library.
|
||||
placeholderCollector.privateNodes
|
||||
.forEach((LibraryElement library, Set<Identifier> identifiers) {
|
||||
for (Identifier identifier in identifiers) {
|
||||
renames[identifier] = _getPrivateName(library, identifier.source);
|
||||
}
|
||||
});
|
||||
|
||||
// Rename unresolved nodes, to make sure they still do not resolve.
|
||||
for (Node node in placeholderCollector.unresolvedNodes) {
|
||||
renames[node] = _generateUniqueTopLevelName('Unresolved');
|
||||
}
|
||||
|
||||
// Erase prefixes that are now not needed.
|
||||
for (Node node in placeholderCollector.prefixNodesToErase) {
|
||||
renames[node] = '';
|
||||
}
|
||||
|
||||
if (cutDeclarationTypes) {
|
||||
for (DeclarationTypePlaceholder placeholder
|
||||
in placeholderCollector.declarationTypePlaceholders) {
|
||||
renames[placeholder.typeNode] = placeholder.requiresVar ? 'var' : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates mini ID based on index.
|
||||
* In other words, it converts index to visual representation
|
||||
* as if digits are given characters.
|
||||
*/
|
||||
String generateMiniId(int index) {
|
||||
const String firstCharAlphabet =
|
||||
r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
|
||||
const String otherCharsAlphabet =
|
||||
r'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$';
|
||||
// It's like converting index in decimal to [chars] radix.
|
||||
if (index < firstCharAlphabet.length) return firstCharAlphabet[index];
|
||||
StringBuffer resultBuilder = new StringBuffer();
|
||||
resultBuilder.writeCharCode(
|
||||
firstCharAlphabet.codeUnitAt(index % firstCharAlphabet.length));
|
||||
index ~/= firstCharAlphabet.length;
|
||||
int length = otherCharsAlphabet.length;
|
||||
while (index >= length) {
|
||||
resultBuilder.writeCharCode(otherCharsAlphabet.codeUnitAt(index % length));
|
||||
index ~/= length;
|
||||
}
|
||||
resultBuilder.write(otherCharsAlphabet[index]);
|
||||
return resultBuilder.toString();
|
||||
}
|
||||
|
||||
abstract class Generator {
|
||||
String generate(String originalName, bool isForbidden(String name));
|
||||
}
|
||||
|
||||
/// Always tries to return original identifier name unless it is forbidden.
|
||||
class ConservativeGenerator implements Generator {
|
||||
String generate(String originalName, bool isForbidden(String name)) {
|
||||
String result = originalName;
|
||||
int index = 0;
|
||||
while (isForbidden(result)) {
|
||||
//|| result == originalName) {
|
||||
result = '${originalName}_${generateMiniId(index++)}';
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// Always tries to generate the most compact identifier.
|
||||
class MinifyingGenerator implements Generator {
|
||||
int index = 0;
|
||||
|
||||
MinifyingGenerator();
|
||||
|
||||
String generate(String originalName, bool isForbidden(String name)) {
|
||||
String result;
|
||||
do {
|
||||
result = generateMiniId(index++);
|
||||
} while (isForbidden(result));
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -3626,15 +3626,18 @@ part of test.main;
|
|||
MessageKind.EXTERNAL_WITH_BODY: const MessageTemplate(
|
||||
MessageKind.EXTERNAL_WITH_BODY,
|
||||
"External function '#{functionName}' cannot have a function body.",
|
||||
options: const ["--output-type=dart"],
|
||||
howToFix:
|
||||
"Try removing the 'external' modifier or the function body.",
|
||||
examples: const [
|
||||
"""
|
||||
import 'package:js/js.dart';
|
||||
@JS()
|
||||
external foo() => 0;
|
||||
main() => foo();
|
||||
""",
|
||||
"""
|
||||
import 'package:js/js.dart';
|
||||
@JS()
|
||||
external foo() {}
|
||||
main() => foo();
|
||||
"""
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// 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.
|
||||
|
||||
library mirror_renamer;
|
||||
|
||||
import '../compiler.dart' show Compiler;
|
||||
import '../dart_backend/dart_backend.dart'
|
||||
show DartBackend, PlaceholderCollector;
|
||||
import '../elements/elements.dart';
|
||||
import '../tokens/token.dart' show Token;
|
||||
import '../tree/tree.dart';
|
||||
|
||||
part 'renamer.dart';
|
||||
|
||||
class MirrorRenamer {
|
||||
const MirrorRenamer();
|
||||
|
||||
LibraryElement get helperLibrary => null;
|
||||
|
||||
FunctionElement get getNameFunction => null;
|
||||
|
||||
bool isMirrorHelperLibrary(LibraryElement element) => false;
|
||||
|
||||
void registerStaticSend(Element currentElement, Element target, Node node) {}
|
||||
|
||||
void addRenames(Map<Node, String> renames, List<Node> topLevelNodes,
|
||||
PlaceholderCollector placeholderCollector) {}
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
// 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.
|
||||
|
||||
part of mirror_renamer;
|
||||
|
||||
class MirrorRenamerImpl implements MirrorRenamer {
|
||||
static const String MIRROR_HELPER_GET_NAME_FUNCTION = 'helperGetName';
|
||||
static final Uri DART_MIRROR_HELPER =
|
||||
new Uri(scheme: 'dart', path: '_mirror_helper');
|
||||
static const String MIRROR_HELPER_SYMBOLS_MAP_NAME = '_SYMBOLS';
|
||||
|
||||
/// Initialized when dart:mirrors is loaded if the useMirrorHelperLibrary
|
||||
/// field is set.
|
||||
final LibraryElement helperLibrary;
|
||||
|
||||
/// Initialized when dart:mirrors is loaded if the useMirrorHelperLibrary
|
||||
/// field is set.
|
||||
final FunctionElement getNameFunction;
|
||||
|
||||
/// Initialized when dart:mirrors is loaded if the useMirrorHelperLibrary
|
||||
/// field is set.
|
||||
final FieldElement symbolsMapVariable;
|
||||
|
||||
/// Maps mangled name to original name.
|
||||
Map<String, String> symbols = new Map<String, String>();
|
||||
|
||||
/// Contains all occurrencs of MirrorSystem.getName() calls in the user code.
|
||||
List<Node> mirrorSystemGetNameNodes = <Node>[];
|
||||
|
||||
/**
|
||||
* Initialized when the placeholderCollector collects the FunctionElement
|
||||
* backend.mirrorHelperGetNameFunction which represents the helperGetName
|
||||
* function in _mirror_helper.
|
||||
*/
|
||||
FunctionExpression get getNameFunctionNode => getNameFunction.node;
|
||||
VariableDefinitions get symbolsMapNode => symbolsMapVariable.node;
|
||||
Compiler compiler;
|
||||
DartBackend backend;
|
||||
|
||||
MirrorRenamerImpl(this.compiler, this.backend, LibraryElement library)
|
||||
: this.helperLibrary = library,
|
||||
getNameFunction =
|
||||
library.find(MirrorRenamerImpl.MIRROR_HELPER_GET_NAME_FUNCTION),
|
||||
symbolsMapVariable =
|
||||
library.find(MirrorRenamerImpl.MIRROR_HELPER_SYMBOLS_MAP_NAME);
|
||||
|
||||
bool isMirrorHelperLibrary(LibraryElement element) {
|
||||
return element == helperLibrary;
|
||||
}
|
||||
|
||||
void registerStaticSend(Element currentElement, Element target, Send node) {
|
||||
if (target == compiler.mirrorSystemGetNameFunction &&
|
||||
currentElement.library != helperLibrary) {
|
||||
// Access to `MirrorSystem.getName` that needs to be redirected to the
|
||||
// [getNameFunction].
|
||||
mirrorSystemGetNameNodes.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a toplevel node to the output containing a map from the mangled
|
||||
* to the unmangled names and replaces calls to MirrorSystem.getName()
|
||||
* with calls to the corresponding wrapper from _mirror_helper which has
|
||||
* been added during resolution. [renames] is assumed to map nodes in user
|
||||
* code to mangled names appearing in output code, and [topLevelNodes] should
|
||||
* contain all the toplevel ast nodes that will be emitted in the output.
|
||||
*/
|
||||
void addRenames(Map<Node, String> renames, List<Node> topLevelNodes,
|
||||
PlaceholderCollector placeholderCollector) {
|
||||
// Right now we only support instances of MirrorSystem.getName,
|
||||
// hence if there are no occurence of these we don't do anything.
|
||||
if (mirrorSystemGetNameNodes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
Node parse(String text) {
|
||||
Token tokens = compiler.scanner.tokenize(text);
|
||||
return compiler.parser.parseCompilationUnit(tokens);
|
||||
}
|
||||
|
||||
// Add toplevel map containing all renames of members.
|
||||
symbols = new Map<String, String>();
|
||||
for (Set<Identifier> s in placeholderCollector.memberPlaceholders.values) {
|
||||
// All members in a set have the same name so we only need to look at one.
|
||||
Identifier sampleNode = s.first;
|
||||
symbols.putIfAbsent(renames[sampleNode], () => sampleNode.source);
|
||||
}
|
||||
|
||||
Identifier symbolsMapIdentifier =
|
||||
symbolsMapNode.definitions.nodes.head.asSend().selector;
|
||||
assert(symbolsMapIdentifier != null);
|
||||
topLevelNodes.remove(symbolsMapNode);
|
||||
|
||||
StringBuffer sb = new StringBuffer(
|
||||
'const ${renames[symbolsMapIdentifier]} = const<String,String>{');
|
||||
bool first = true;
|
||||
for (String mangledName in symbols.keys) {
|
||||
if (!first) {
|
||||
sb.write(',');
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
sb.write("'$mangledName' : '");
|
||||
sb.write(symbols[mangledName]);
|
||||
sb.write("'");
|
||||
}
|
||||
sb.write('};');
|
||||
sb.writeCharCode(0); // Terminate the string with '0', see [StringScanner].
|
||||
topLevelNodes.add(parse(sb.toString()));
|
||||
|
||||
// Replace calls to Mirrorsystem.getName with calls to helper function.
|
||||
mirrorSystemGetNameNodes.forEach((node) {
|
||||
renames[node.selector] = renames[getNameFunctionNode.name];
|
||||
renames[node.receiver] = '';
|
||||
});
|
||||
}
|
||||
}
|
|
@ -245,12 +245,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
/// Whether to preserve comments while scanning (only use for dart:mirrors).
|
||||
final bool preserveComments;
|
||||
|
||||
/// Whether to emit JavaScript (false enables dart2dart).
|
||||
final bool emitJavaScript;
|
||||
|
||||
/// When using dart2dart, whether to use the multi file format.
|
||||
final bool dart2dartMultiFile;
|
||||
|
||||
/// Strip option used by dart2dart.
|
||||
final List<String> strips;
|
||||
|
||||
|
@ -280,7 +274,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
analyzeSignaturesOnly: _hasOption(options, Flags.analyzeSignaturesOnly),
|
||||
buildId: _extractStringOption(
|
||||
options, '--build-id=', _UNDETERMINED_BUILD_ID),
|
||||
dart2dartMultiFile: _hasOption(options, '--output-type=dart-multi'),
|
||||
deferredMapUri: _extractUriOption(options, '--deferred-map='),
|
||||
fatalWarnings: _hasOption(options, Flags.fatalWarnings),
|
||||
terseDiagnostics: _hasOption(options, Flags.terse),
|
||||
|
@ -291,8 +284,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
disableInlining: _hasOption(options, Flags.disableInlining),
|
||||
disableTypeInference: _hasOption(options, Flags.disableTypeInference),
|
||||
dumpInfo: _hasOption(options, Flags.dumpInfo),
|
||||
emitJavaScript: !(_hasOption(options, '--output-type=dart') ||
|
||||
_hasOption(options, '--output-type=dart-multi')),
|
||||
enableAssertMessage: _hasOption(options, Flags.enableAssertMessage),
|
||||
enableGenericMethodSyntax:
|
||||
_hasOption(options, Flags.genericMethodSyntax),
|
||||
|
@ -354,7 +345,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
bool analyzeOnly: false,
|
||||
bool analyzeSignaturesOnly: false,
|
||||
String buildId: _UNDETERMINED_BUILD_ID,
|
||||
bool dart2dartMultiFile: false,
|
||||
Uri deferredMapUri: null,
|
||||
bool fatalWarnings: false,
|
||||
bool terseDiagnostics: false,
|
||||
|
@ -364,7 +354,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
bool disableInlining: false,
|
||||
bool disableTypeInference: false,
|
||||
bool dumpInfo: false,
|
||||
bool emitJavaScript: true,
|
||||
bool enableAssertMessage: false,
|
||||
bool enableGenericMethodSyntax: false,
|
||||
bool enableInitializingFormalAccess: false,
|
||||
|
@ -428,7 +417,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
analyzeOnly || analyzeSignaturesOnly || analyzeAll || resolveOnly,
|
||||
analyzeSignaturesOnly: analyzeSignaturesOnly,
|
||||
buildId: buildId,
|
||||
dart2dartMultiFile: dart2dartMultiFile,
|
||||
deferredMapUri: deferredMapUri,
|
||||
fatalWarnings: fatalWarnings,
|
||||
terseDiagnostics: terseDiagnostics,
|
||||
|
@ -436,9 +424,8 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
suppressHints: suppressHints,
|
||||
shownPackageWarnings: shownPackageWarnings,
|
||||
disableInlining: disableInlining || hasIncrementalSupport,
|
||||
disableTypeInference: disableTypeInference || !emitJavaScript,
|
||||
disableTypeInference: disableTypeInference,
|
||||
dumpInfo: dumpInfo,
|
||||
emitJavaScript: emitJavaScript,
|
||||
enableAssertMessage: enableAssertMessage,
|
||||
enableGenericMethodSyntax: enableGenericMethodSyntax,
|
||||
enableInitializingFormalAccess: enableInitializingFormalAccess,
|
||||
|
@ -452,8 +439,7 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
hasIncrementalSupport: hasIncrementalSupport,
|
||||
outputUri: outputUri,
|
||||
platformConfigUri: platformConfigUri ??
|
||||
_resolvePlatformConfig(
|
||||
libraryRoot, null, !emitJavaScript, const []),
|
||||
_resolvePlatformConfig(libraryRoot, null, const []),
|
||||
preserveComments: preserveComments,
|
||||
preserveUris: preserveUris,
|
||||
resolutionInputs: resolutionInputs,
|
||||
|
@ -482,7 +468,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
this.analyzeOnly: false,
|
||||
this.analyzeSignaturesOnly: false,
|
||||
this.buildId: _UNDETERMINED_BUILD_ID,
|
||||
this.dart2dartMultiFile: false,
|
||||
this.deferredMapUri: null,
|
||||
this.fatalWarnings: false,
|
||||
this.terseDiagnostics: false,
|
||||
|
@ -492,7 +477,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
this.disableInlining: false,
|
||||
this.disableTypeInference: false,
|
||||
this.dumpInfo: false,
|
||||
this.emitJavaScript: true,
|
||||
this.enableAssertMessage: false,
|
||||
this.enableGenericMethodSyntax: false,
|
||||
this.enableInitializingFormalAccess: false,
|
||||
|
@ -541,7 +525,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
analyzeOnly,
|
||||
analyzeSignaturesOnly,
|
||||
buildId,
|
||||
dart2dartMultiFile,
|
||||
deferredMapUri,
|
||||
fatalWarnings,
|
||||
terseDiagnostics,
|
||||
|
@ -551,7 +534,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
disableInlining,
|
||||
disableTypeInference,
|
||||
dumpInfo,
|
||||
emitJavaScript,
|
||||
enableAssertMessage,
|
||||
enableGenericMethodSyntax,
|
||||
enableInitializingFormalAccess,
|
||||
|
@ -599,7 +581,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
analyzeSignaturesOnly:
|
||||
analyzeSignaturesOnly ?? options.analyzeSignaturesOnly,
|
||||
buildId: buildId ?? options.buildId,
|
||||
dart2dartMultiFile: dart2dartMultiFile ?? options.dart2dartMultiFile,
|
||||
deferredMapUri: deferredMapUri ?? options.deferredMapUri,
|
||||
fatalWarnings: fatalWarnings ?? options.fatalWarnings,
|
||||
terseDiagnostics: terseDiagnostics ?? options.terseDiagnostics,
|
||||
|
@ -611,7 +592,6 @@ class CompilerOptions implements DiagnosticOptions, ParserOptions {
|
|||
disableTypeInference:
|
||||
disableTypeInference ?? options.disableTypeInference,
|
||||
dumpInfo: dumpInfo ?? options.dumpInfo,
|
||||
emitJavaScript: emitJavaScript ?? options.emitJavaScript,
|
||||
enableAssertMessage: enableAssertMessage ?? options.enableAssertMessage,
|
||||
enableGenericMethodSyntax:
|
||||
enableGenericMethodSyntax ?? options.enableGenericMethodSyntax,
|
||||
|
@ -722,11 +702,9 @@ List<String> _extractOptionalCsvOption(List<String> options, String flag) {
|
|||
}
|
||||
|
||||
Uri _resolvePlatformConfig(Uri libraryRoot, String platformConfigPath,
|
||||
bool isDart2Dart, Iterable<String> categories) {
|
||||
Iterable<String> categories) {
|
||||
if (platformConfigPath != null) {
|
||||
return libraryRoot.resolve(platformConfigPath);
|
||||
} else if (isDart2Dart) {
|
||||
return libraryRoot.resolve(_dart2dartPlatform);
|
||||
} else {
|
||||
if (categories.length == 0) {
|
||||
return libraryRoot.resolve(_clientPlatform);
|
||||
|
@ -747,7 +725,6 @@ Uri _resolvePlatformConfigFromOptions(Uri libraryRoot, List<String> options) {
|
|||
return _resolvePlatformConfig(
|
||||
libraryRoot,
|
||||
_extractStringOption(options, "--platform-config=", null),
|
||||
_hasOption(options, '--output-type=dart'),
|
||||
_extractCsvOption(options, '--categories='));
|
||||
}
|
||||
|
||||
|
@ -755,7 +732,6 @@ Uri _resolvePlatformConfigFromOptions(Uri libraryRoot, List<String> options) {
|
|||
const String _clientPlatform = "lib/dart_client.platform";
|
||||
const String _serverPlatform = "lib/dart_server.platform";
|
||||
const String _sharedPlatform = "lib/dart_shared.platform";
|
||||
const String _dart2dartPlatform = "lib/dart2dart.platform";
|
||||
|
||||
const String _UNDETERMINED_BUILD_ID = "build number could not be determined";
|
||||
const bool _forceIncrementalSupport =
|
||||
|
|
|
@ -18,10 +18,6 @@ sample_extension: Crash # Unable to compile UnsupportedError.message.
|
|||
[ $compiler == dart2analyzer ]
|
||||
build_dart: Skip
|
||||
|
||||
[ $compiler == dart2dart ]
|
||||
# Skip until we stabilize language tests.
|
||||
*: Skip
|
||||
|
||||
[ $arch == arm ]
|
||||
sample_extension/test/sample_extension_test: Skip # Issue 14705
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
// 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.
|
||||
/**
|
||||
* Helps dealing with reflection in the case that the source code has been
|
||||
* changed as a result of compiling with dart2dart.
|
||||
*/
|
||||
library _mirror_helper;
|
||||
|
||||
import 'dart:mirrors';
|
||||
|
||||
/// The compiler will replace this variable with a map containing all the
|
||||
/// renames made in dart2dart.
|
||||
const Map<String, String> _SYMBOLS = null;
|
||||
|
||||
/// This method is a wrapper for MirrorSystem.getName() and will be inlined and
|
||||
/// called in the generated output Dart code.
|
||||
String helperGetName(Symbol sym) {
|
||||
var name = MirrorSystem.getName(sym);
|
||||
if (_SYMBOLS.containsKey(name)) {
|
||||
return _SYMBOLS[name];
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -227,14 +227,6 @@ const Map<String, LibraryInfo> libraries = const {
|
|||
documented: false,
|
||||
platforms: DART2JS_PLATFORM),
|
||||
|
||||
// TODO(ahe): This library is only for dart2dart, perhaps it should use a
|
||||
// different platform.
|
||||
"_mirror_helper": const LibraryInfo(
|
||||
"_internal/js_runtime/lib/mirror_helper.dart",
|
||||
categories: "",
|
||||
documented: false,
|
||||
platforms: DART2JS_PLATFORM),
|
||||
|
||||
"_js_embedded_names": const LibraryInfo(
|
||||
"_internal/js_runtime/lib/shared/embedded_names.dart",
|
||||
categories: "",
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
# 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.
|
||||
|
||||
# The platform when compiling with dart2js for dart2dart.
|
||||
#
|
||||
# Includes the _mirror_helpers private libraries
|
||||
|
||||
[dart-spec]
|
||||
spec:3rd edition.
|
||||
|
||||
[features]
|
||||
# No extra features.
|
||||
|
||||
[libraries]
|
||||
async: async/async.dart
|
||||
_chrome: _chrome/dart2js/chrome_dart2js.dart
|
||||
collection: collection/collection.dart
|
||||
convert: convert/convert.dart
|
||||
core: core/core.dart
|
||||
developer: developer/developer.dart
|
||||
html: html/dart2js/html_dart2js.dart
|
||||
html_common: html/html_common/html_common_dart2js.dart
|
||||
indexed_db: indexed_db/dart2js/indexed_db_dart2js.dart
|
||||
io: io/io.dart
|
||||
isolate: isolate/isolate.dart
|
||||
js: js/dart2js/js_dart2js.dart
|
||||
js_util: js_util/dart2js/js_util_dart2js.dart
|
||||
math: math/math.dart
|
||||
mirrors: mirrors/mirrors.dart
|
||||
nativewrappers: html/dart2js/nativewrappers.dart
|
||||
typed_data: typed_data/typed_data.dart
|
||||
_native_typed_data: _internal/js_runtime/lib/native_typed_data.dart
|
||||
svg: svg/dart2js/svg_dart2js.dart
|
||||
web_audio: web_audio/dart2js/web_audio_dart2js.dart
|
||||
web_gl: web_gl/dart2js/web_gl_dart2js.dart
|
||||
web_sql: web_sql/dart2js/web_sql_dart2js.dart
|
||||
_internal: internal/internal.dart
|
||||
_js_helper: _internal/js_runtime/lib/js_helper.dart
|
||||
_interceptors: _internal/js_runtime/lib/interceptors.dart
|
||||
_foreign_helper: _internal/js_runtime/lib/foreign_helper.dart
|
||||
_isolate_helper: _internal/js_runtime/lib/isolate_helper.dart
|
||||
_js_mirrors: _internal/js_runtime/lib/js_mirrors.dart
|
||||
_js_names: _internal/js_runtime/lib/js_names.dart
|
||||
_js_primitives: _internal/js_runtime/lib/js_primitives.dart
|
||||
_mirror_helper: _internal/js_runtime/lib/mirror_helper.dart
|
||||
_js_embedded_names: _internal/js_runtime/lib/shared/embedded_names.dart
|
||||
_async_await_error_codes: _internal/js_runtime/lib/shared/async_await_error_codes.dart
|
||||
_metadata: html/html_common/metadata.dart
|
|
@ -23,9 +23,6 @@ const Map<String, List<String>> WHITE_LIST = const {
|
|||
"The method 'asAssert' is never called.",
|
||||
"The method 'asLiteralBool' is never called."],
|
||||
|
||||
// Some things in dart_printer are not yet used
|
||||
"lib/src/dart_backend/backend_ast_nodes.dart": const [" is never "],
|
||||
|
||||
// Uncalled methods in SemanticSendVisitor and subclasses.
|
||||
"lib/src/resolution/semantic_visitor.dart": const [
|
||||
"The method 'error"],
|
||||
|
@ -56,10 +53,6 @@ const Map<String, List<String>> WHITE_LIST = const {
|
|||
"accept", "CreateFunction",
|
||||
],
|
||||
|
||||
"lib/src/dart_backend/backend_ast_to_frontend_ast.dart": const [
|
||||
" is never "
|
||||
],
|
||||
|
||||
// Useful utility functions that are not currently used.
|
||||
"lib/src/cps_ir/cps_fragment.dart": const [
|
||||
"The method 'beginLoop' is never called.",
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,990 +0,0 @@
|
|||
// Copyright (c) 2014, 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 dart_printer_test;
|
||||
|
||||
import 'dart:mirrors';
|
||||
import 'package:expect/expect.dart';
|
||||
import 'package:compiler/src/constants/values.dart';
|
||||
import 'package:compiler/src/dart_backend/backend_ast_nodes.dart';
|
||||
import 'package:compiler/src/dart_backend/backend_ast_to_frontend_ast.dart'
|
||||
show TreePrinter;
|
||||
import 'package:compiler/src/diagnostics/diagnostic_listener.dart';
|
||||
import 'package:compiler/src/diagnostics/messages.dart';
|
||||
import 'package:compiler/src/diagnostics/spannable.dart' show Spannable;
|
||||
import 'package:compiler/src/parser/listener.dart';
|
||||
import 'package:compiler/src/parser/parser.dart';
|
||||
import 'package:compiler/src/scanner/scanner.dart';
|
||||
import 'package:compiler/src/tokens/token.dart';
|
||||
import 'package:compiler/src/tokens/token_constants.dart';
|
||||
import 'package:compiler/src/io/source_file.dart';
|
||||
import 'package:compiler/src/string_validator.dart';
|
||||
import 'package:compiler/src/tree/tree.dart' show DartString;
|
||||
import 'package:compiler/src/tree/tree.dart' as tree;
|
||||
import '../options_helper.dart';
|
||||
|
||||
/// For debugging the [AstBuilder] stack. Prints information about [x].
|
||||
void show(x) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
Unparser unparser = new Unparser(buf);
|
||||
void unparse(x) {
|
||||
if (x is Expression)
|
||||
unparser.writeExpression(x);
|
||||
else if (x is TypeAnnotation)
|
||||
unparser.writeType(x);
|
||||
else if (x is Statement)
|
||||
unparser.writeStatement(x);
|
||||
else if (x is List) {
|
||||
buf.write('[');
|
||||
bool first = true;
|
||||
for (var y in x) {
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
buf.write(', ');
|
||||
unparse(y);
|
||||
}
|
||||
buf.write(']');
|
||||
}
|
||||
}
|
||||
unparse(x);
|
||||
print("${x.runtimeType}: ${buf.toString()}");
|
||||
}
|
||||
|
||||
class PrintDiagnosticListener implements DiagnosticReporter {
|
||||
void log(message) {
|
||||
print(message);
|
||||
}
|
||||
|
||||
void internalError(Spannable spannable, message) {
|
||||
print(message);
|
||||
}
|
||||
|
||||
SourceSpan spanFromSpannable(Spannable node) {
|
||||
return new SourceSpan(null, 0, 0);
|
||||
}
|
||||
|
||||
void reportFatalError(Spannable node, MessageKind errorCode,
|
||||
[Map arguments = const {}]) {
|
||||
print(errorCode);
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
void reportError(Spannable node, MessageKind errorCode,
|
||||
[Map arguments = const {}]) {
|
||||
print(errorCode);
|
||||
}
|
||||
|
||||
void reportWarning(Spannable node, MessageKind errorCode,
|
||||
[Map arguments = const {}]) {
|
||||
print(errorCode);
|
||||
}
|
||||
|
||||
void reportHint(Spannable node, MessageKind errorCode,
|
||||
[Map arguments = const {}]) {
|
||||
print(errorCode);
|
||||
}
|
||||
|
||||
void reportInfo(Spannable node, MessageKind errorCode,
|
||||
[Map arguments = const {}]) {
|
||||
print(errorCode);
|
||||
}
|
||||
|
||||
withCurrentElement(element, f()) {
|
||||
f();
|
||||
}
|
||||
}
|
||||
|
||||
class AstBuilder extends Listener {
|
||||
final List stack = [];
|
||||
final StringValidator stringValidator
|
||||
= new StringValidator(new PrintDiagnosticListener());
|
||||
|
||||
String asName(e) {
|
||||
if (e is Identifier)
|
||||
return e.name;
|
||||
else if (e == null)
|
||||
return null;
|
||||
else
|
||||
throw 'Expression is not a name: ${e.runtimeType}';
|
||||
}
|
||||
|
||||
TypeAnnotation asType(x) {
|
||||
if (x is TypeAnnotation)
|
||||
return x;
|
||||
if (x is Identifier)
|
||||
return new TypeAnnotation(x.name);
|
||||
if (x == null)
|
||||
return null;
|
||||
else
|
||||
throw "Not a type: ${x.runtimeType}";
|
||||
}
|
||||
|
||||
Parameter asParameter(x) {
|
||||
if (x is Parameter)
|
||||
return x;
|
||||
if (x is Identifier)
|
||||
return new Parameter(x.name);
|
||||
else
|
||||
throw "Not a parameter: ${x.runtimeType}";
|
||||
}
|
||||
|
||||
void push(node) {
|
||||
stack.add(node);
|
||||
}
|
||||
dynamic peek() {
|
||||
return stack.last;
|
||||
}
|
||||
dynamic pop([coerce(x) = null]) {
|
||||
var x = stack.removeLast();
|
||||
if (coerce != null)
|
||||
return coerce(x);
|
||||
else
|
||||
return x;
|
||||
}
|
||||
List popList(int count, [List result, coerce(x) = null]) {
|
||||
if (result == null)
|
||||
result = <Node>[];
|
||||
for (int i=0; i<count; i++) {
|
||||
var x = stack[stack.length-count+i];
|
||||
if (coerce != null) {
|
||||
x = coerce(x);
|
||||
}
|
||||
result.add(x);
|
||||
}
|
||||
stack.removeRange(stack.length-count, stack.length);
|
||||
return result;
|
||||
}
|
||||
popTypeAnnotation() {
|
||||
List<TypeAnnotation> args = pop();
|
||||
if (args == null)
|
||||
return null;
|
||||
String name = pop(asName);
|
||||
return new TypeAnnotation(name, args);
|
||||
}
|
||||
|
||||
// EXPRESSIONS
|
||||
endCascade() {
|
||||
throw "Cascade not supported yet";
|
||||
}
|
||||
endIdentifierList(int count) {
|
||||
push(popList(count, <Identifier>[]));
|
||||
}
|
||||
endTypeList(int count) {
|
||||
push(popList(count, <TypeAnnotation>[], asType));
|
||||
}
|
||||
beginLiteralString(Token token) {
|
||||
String source = token.value;
|
||||
tree.StringQuoting quoting = StringValidator.quotingFromString(source);
|
||||
push(quoting);
|
||||
push(token); // collect token at the end
|
||||
}
|
||||
handleStringPart(Token token) {
|
||||
push(token); // collect token at the end
|
||||
}
|
||||
endLiteralString(int interpCount) {
|
||||
List parts = popList(2 * interpCount + 1, []);
|
||||
tree.StringQuoting quoting = pop();
|
||||
List<Expression> members = <Expression>[];
|
||||
for (var i=0; i<parts.length; i++) {
|
||||
var part = parts[i];
|
||||
if (part is Expression) {
|
||||
members.add(part);
|
||||
} else {
|
||||
assert(part is Token);
|
||||
DartString str = stringValidator.validateInterpolationPart(
|
||||
part as Token,
|
||||
quoting,
|
||||
isFirst: i == 0,
|
||||
isLast: i == parts.length - 1);
|
||||
members.add(new Literal(new StringConstantValue(str)));
|
||||
}
|
||||
}
|
||||
push(new StringConcat(members));
|
||||
}
|
||||
handleStringJuxtaposition(int litCount) {
|
||||
push(new StringConcat(popList(litCount, <Expression>[])));
|
||||
}
|
||||
endArguments(int count, begin, end) {
|
||||
push(popList(count, <Argument>[]));
|
||||
}
|
||||
handleNoArguments(token) {
|
||||
push(null);
|
||||
}
|
||||
handleNoTypeArguments(token) {
|
||||
push(<TypeAnnotation>[]);
|
||||
}
|
||||
endTypeArguments(int count, t, y) {
|
||||
List<TypeAnnotation> args = <TypeAnnotation>[];
|
||||
for (var i=0; i<count; i++) {
|
||||
args.add(popTypeAnnotation());
|
||||
}
|
||||
push(args.reversed.toList(growable:false));
|
||||
}
|
||||
handleVoidKeyword(token) {
|
||||
push(new Identifier("void"));
|
||||
push(<TypeAnnotation>[]); // prepare for popTypeAnnotation
|
||||
}
|
||||
handleQualified(Token period) {
|
||||
String last = pop(asName);
|
||||
String first = pop(asName);
|
||||
push(new Identifier('$first.$last'));
|
||||
}
|
||||
endSend(t) {
|
||||
List<Argument> arguments = pop();
|
||||
pop(); // typeArguments
|
||||
if (arguments == null)
|
||||
return; // not a function call
|
||||
Expression selector = pop();
|
||||
push(new CallFunction(selector, arguments));
|
||||
}
|
||||
endThrowExpression(t, tt) {
|
||||
push(new Throw(pop()));
|
||||
}
|
||||
handleAssignmentExpression(Token token) {
|
||||
Expression right = pop();
|
||||
Expression left = pop();
|
||||
push(new Assignment(left, token.value, right));
|
||||
}
|
||||
handleBinaryExpression(Token token) {
|
||||
Expression right = pop();
|
||||
Receiver left = pop();
|
||||
String tokenString = token.stringValue;
|
||||
if (tokenString == '.') {
|
||||
if (right is CallFunction) {
|
||||
String name = (right.callee as Identifier).name;
|
||||
push(new CallMethod(left, name, right.arguments));
|
||||
} else {
|
||||
push(new FieldExpression(left, (right as Identifier).name));
|
||||
}
|
||||
} else {
|
||||
push(new BinaryOperator(left, tokenString, right));
|
||||
}
|
||||
}
|
||||
handleConditionalExpression(question, colon) {
|
||||
Expression elseExpression = pop();
|
||||
Expression thenExpression = pop();
|
||||
Expression condition = pop();
|
||||
push(new Conditional(condition, thenExpression, elseExpression));
|
||||
}
|
||||
handleIdentifier(Token t) {
|
||||
push(new Identifier(t.value));
|
||||
}
|
||||
handleOperator(t) {
|
||||
push(new Identifier(t.value));
|
||||
}
|
||||
handleIndexedExpression(open, close) {
|
||||
Expression index = pop();
|
||||
Receiver object = pop();
|
||||
push(new IndexExpression(object, index));
|
||||
}
|
||||
handleIsOperator(operathor, not, endToken) {
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
Expression exp = pop();
|
||||
TypeOperator r = new TypeOperator(exp, 'is', type);
|
||||
if (not != null) {
|
||||
push(new UnaryOperator('!', r));
|
||||
} else {
|
||||
push(r);
|
||||
}
|
||||
}
|
||||
handleAsOperator(operathor, endToken) {
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
Expression exp = pop();
|
||||
push(new TypeOperator(exp, 'as', type));
|
||||
}
|
||||
handleLiteralBool(Token t) {
|
||||
bool value = t.value == 'true';
|
||||
push(new Literal(
|
||||
value ? new TrueConstantValue() : new FalseConstantValue()));
|
||||
}
|
||||
handleLiteralDouble(t) {
|
||||
push(new Literal(new DoubleConstantValue(double.parse(t.value))));
|
||||
}
|
||||
handleLiteralInt(Token t) {
|
||||
push(new Literal(new IntConstantValue(int.parse(t.value))));
|
||||
}
|
||||
handleLiteralNull(t) {
|
||||
push(new Literal(new NullConstantValue()));
|
||||
}
|
||||
endLiteralSymbol(Token hash, int idCount) {
|
||||
List<Identifier> ids = popList(idCount, <Identifier>[]);
|
||||
push(new LiteralSymbol(ids.map((id) => id.name).join('.')));
|
||||
}
|
||||
handleLiteralList(int count, begin, constKeyword, end) {
|
||||
List<Expression> exps = popList(count, <Expression>[]);
|
||||
List<TypeAnnotation> types = pop();
|
||||
assert(types.length <= 1);
|
||||
push(new LiteralList(exps,
|
||||
isConst: constKeyword != null,
|
||||
typeArgument: types.length == 0 ? null : types[0]
|
||||
));
|
||||
}
|
||||
handleLiteralMap(int count, begin, constKeyword, end) {
|
||||
List<LiteralMapEntry> entries = popList(count, <LiteralMapEntry>[]);
|
||||
List<TypeAnnotation> types = pop();
|
||||
assert(types.length == 0 || types.length == 2);
|
||||
push(new LiteralMap(entries,
|
||||
isConst: constKeyword != null,
|
||||
typeArguments: types
|
||||
));
|
||||
}
|
||||
endLiteralMapEntry(colon, endToken) {
|
||||
Expression value = pop();
|
||||
Expression key = pop();
|
||||
push(new LiteralMapEntry(key,value));
|
||||
}
|
||||
handleNamedArgument(colon) {
|
||||
Expression exp = pop();
|
||||
Identifier name = pop();
|
||||
push(new NamedArgument(name.name, exp));
|
||||
}
|
||||
endConstructorReference(Token start, Token period, Token end) {
|
||||
if (period == null) {
|
||||
push(null); // indicate missing constructor name
|
||||
}
|
||||
}
|
||||
handleNewExpression(t) {
|
||||
List<Argument> args = pop();
|
||||
String constructorName = pop(asName);
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
push(new CallNew(type, args, constructorName: constructorName));
|
||||
}
|
||||
handleConstExpression(t) {
|
||||
List<Argument> args = pop();
|
||||
String constructorName = pop(asName);
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
push(new CallNew(type, args, constructorName: constructorName,
|
||||
isConst:true));
|
||||
}
|
||||
handleParenthesizedExpression(t) {
|
||||
// do nothing, just leave expression on top of stack
|
||||
}
|
||||
handleSuperExpression(t) {
|
||||
push(new SuperReceiver());
|
||||
}
|
||||
handleThisExpression(t) {
|
||||
push(new This());
|
||||
}
|
||||
handleUnaryPostfixAssignmentExpression(Token t) {
|
||||
push(new Increment.postfix(pop(), t.value));
|
||||
}
|
||||
handleUnaryPrefixAssignmentExpression(Token t) {
|
||||
push(new Increment.prefix(pop(), t.value));
|
||||
}
|
||||
handleUnaryPrefixExpression(Token t) {
|
||||
push(new UnaryOperator(t.value, pop()));
|
||||
}
|
||||
|
||||
handleFunctionTypedFormalParameter(tok) {
|
||||
// handled in endFormalParameter
|
||||
}
|
||||
endFormalParameter(thisKeyword) {
|
||||
Expression defaultValue = null;
|
||||
var x = pop();
|
||||
if (x is DefaultValue) {
|
||||
defaultValue = x.expression;
|
||||
x = pop();
|
||||
}
|
||||
if (x is Parameters) {
|
||||
String name = pop(asName);
|
||||
TypeAnnotation returnType = popTypeAnnotation();
|
||||
push(new Parameter.function(name, returnType, x, defaultValue));
|
||||
} else {
|
||||
String name = asName(x);
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
push(new Parameter(name, type:type, defaultValue:defaultValue));
|
||||
}
|
||||
}
|
||||
handleValuedFormalParameter(eq, tok) {
|
||||
push(new DefaultValue(pop()));
|
||||
}
|
||||
endOptionalFormalParameters(int count, begin, end) {
|
||||
bool isNamed = end.value == '}';
|
||||
push(popList(count, <Parameter>[], asParameter));
|
||||
push(isNamed); // Indicate optional parameters to endFormalParameters.
|
||||
}
|
||||
endFormalParameters(count, begin, end) {
|
||||
if (count == 0) {
|
||||
push(new Parameters([]));
|
||||
return;
|
||||
}
|
||||
var last = pop(); // Detect if optional parameters are present.
|
||||
if (last is bool) { // See endOptionalFormalParameters.
|
||||
List<Parameter> optional = pop();
|
||||
List<Parameter> required = popList(count-1, <Parameter>[], asParameter);
|
||||
push(new Parameters(required, optional, last));
|
||||
} else {
|
||||
// No optional parameters.
|
||||
List<Parameter> required = popList(count-1, <Parameter>[], asParameter);
|
||||
required.add(last);
|
||||
push(new Parameters(required));
|
||||
}
|
||||
}
|
||||
handleNoFormalParameters(tok) {
|
||||
push(new Parameters([]));
|
||||
}
|
||||
|
||||
endUnnamedFunction(t) {
|
||||
Statement body = pop();
|
||||
Parameters parameters = pop();
|
||||
push(new FunctionExpression(parameters, body));
|
||||
}
|
||||
|
||||
handleNoType(Token token) {
|
||||
push(null);
|
||||
}
|
||||
|
||||
endReturnStatement(bool hasExpression, begin, end) {
|
||||
// This is also called for functions whose body is "=> expression"
|
||||
if (hasExpression) {
|
||||
push(new Return(pop()));
|
||||
} else {
|
||||
push(new Return());
|
||||
}
|
||||
}
|
||||
|
||||
endExpressionStatement(Token token) {
|
||||
push(new ExpressionStatement(pop()));
|
||||
}
|
||||
|
||||
endDoWhileStatement(Token doKeyword, Token whileKeyword, Token end) {
|
||||
Expression condition = pop();
|
||||
Statement body = pop();
|
||||
push(new DoWhile(body, condition));
|
||||
}
|
||||
|
||||
endWhileStatement(Token whileKeyword, Token end) {
|
||||
Statement body = pop();
|
||||
Expression condition = pop();
|
||||
push(new While(condition, body));
|
||||
}
|
||||
|
||||
endBlock(int count, Token begin, Token end) {
|
||||
push(new Block(popList(count, <Statement>[])));
|
||||
}
|
||||
|
||||
endRethrowStatement(Token throwToken, Token endToken) {
|
||||
push(new Rethrow());
|
||||
}
|
||||
|
||||
endTryStatement(int catchCount, Token tryKeyword, Token finallyKeyword) {
|
||||
Statement finallyBlock = null;
|
||||
if (finallyKeyword != null) {
|
||||
finallyBlock = pop();
|
||||
}
|
||||
List<CatchBlock> catchBlocks = popList(catchCount, <CatchBlock>[]);
|
||||
Statement tryBlock = pop();
|
||||
push(new Try(tryBlock, catchBlocks, finallyBlock));
|
||||
}
|
||||
|
||||
void handleCatchBlock(Token onKeyword, Token catchKeyword) {
|
||||
Statement block = pop();
|
||||
String exceptionVar = null;
|
||||
String stackVar = null;
|
||||
if (catchKeyword != null) {
|
||||
Parameters params = pop();
|
||||
exceptionVar = params.requiredParameters[0].name;
|
||||
if (params.requiredParameters.length > 1) {
|
||||
stackVar = params.requiredParameters[1].name;
|
||||
}
|
||||
}
|
||||
TypeAnnotation type = onKeyword == null ? null : pop();
|
||||
push(new CatchBlock(block,
|
||||
onType: type,
|
||||
exceptionVar: exceptionVar,
|
||||
stackVar: stackVar
|
||||
));
|
||||
}
|
||||
|
||||
endSwitchStatement(Token switchKeyword, Token end) {
|
||||
List<SwitchCase> cases = pop();
|
||||
Expression expression = pop();
|
||||
push(new Switch(expression, cases));
|
||||
}
|
||||
|
||||
endSwitchBlock(int caseCount, Token begin, Token end) {
|
||||
push(popList(caseCount, <SwitchCase>[]));
|
||||
}
|
||||
|
||||
handleSwitchCase(int labelCount, int caseCount, Token defaultKeyword,
|
||||
int statementCount, Token first, Token end) {
|
||||
List<Statement> statements = popList(statementCount, <Statement>[]);
|
||||
List<Expression> cases = popList(caseCount, <Expression>[]);
|
||||
if (defaultKeyword != null) {
|
||||
cases = null;
|
||||
}
|
||||
push(new SwitchCase(cases, statements));
|
||||
}
|
||||
|
||||
handleCaseMatch(Token caseKeyword, Token colon) {
|
||||
// do nothing, leave case expression on stack
|
||||
}
|
||||
|
||||
handleBreakStatement(bool hasTarget, Token breakKeyword, Token end) {
|
||||
String target = hasTarget ? pop(asName) : null;
|
||||
push(new Break(target));
|
||||
}
|
||||
|
||||
handleContinueStatement(bool hasTarget, Token continueKeyword, Token end) {
|
||||
String target = hasTarget ? pop(asName) : null;
|
||||
push(new Continue(target));
|
||||
}
|
||||
|
||||
handleEmptyStatement(Token token) {
|
||||
push(new EmptyStatement());
|
||||
}
|
||||
|
||||
|
||||
VariableDeclaration asVariableDeclaration(x) {
|
||||
if (x is VariableDeclaration)
|
||||
return x;
|
||||
if (x is Identifier)
|
||||
return new VariableDeclaration(x.name);
|
||||
throw "Not a variable definition: ${x.runtimeType}";
|
||||
}
|
||||
|
||||
endVariablesDeclaration(int count, Token end) {
|
||||
List<VariableDeclaration> variables =
|
||||
popList(count, <VariableDeclaration>[], asVariableDeclaration);
|
||||
TypeAnnotation type = popTypeAnnotation();
|
||||
push(new VariableDeclarations(variables,
|
||||
type: type,
|
||||
isFinal: false, // TODO(asgerf): Parse modifiers.
|
||||
isConst: false
|
||||
));
|
||||
}
|
||||
|
||||
endInitializer(Token assign) {
|
||||
Expression init = pop();
|
||||
String name = pop(asName);
|
||||
push(new VariableDeclaration(name, init));
|
||||
}
|
||||
|
||||
endIfStatement(Token ifToken, Token elseToken) {
|
||||
Statement elsePart = (elseToken == null) ? null : pop();
|
||||
Statement thenPart = pop();
|
||||
Expression condition = pop();
|
||||
push(new If(condition, thenPart, elsePart));
|
||||
}
|
||||
|
||||
endForStatement(int updateCount, Token begin, Token end) {
|
||||
Statement body = pop();
|
||||
List<Expression> updates = popList(updateCount, <Expression>[]);
|
||||
ExpressionStatement condition = pop(); // parsed as expression statement
|
||||
Expression exp = condition == null ? null : condition.expression;
|
||||
Node initializer = pop();
|
||||
push(new For(initializer, exp, updates, body));
|
||||
}
|
||||
|
||||
handleNoExpression(Token token) {
|
||||
push(null);
|
||||
}
|
||||
|
||||
endForIn(Token await, Token begin, Token inKeyword, Token end) {
|
||||
Statement body = pop();
|
||||
Expression exp = pop();
|
||||
Node declaredIdentifier = pop();
|
||||
push(new ForIn(declaredIdentifier, exp, body));
|
||||
}
|
||||
|
||||
handleAssertStatement(Token assertKeyword,
|
||||
Token commaToken, Token semicolonToken) {
|
||||
Expression message;
|
||||
if (commaToken != null) message = pop();
|
||||
Expression exp = pop();
|
||||
var arguments = [exp];
|
||||
if (message != null) arguments.add(message);
|
||||
Expression call = new CallFunction(new Identifier("assert"), arguments);
|
||||
push(new ExpressionStatement(call));
|
||||
}
|
||||
|
||||
endLabeledStatement(int labelCount) {
|
||||
Statement statement = pop();
|
||||
for (int i=0; i<labelCount; i++) {
|
||||
String label = pop(asName);
|
||||
statement = new LabeledStatement(label, statement);
|
||||
}
|
||||
push(statement);
|
||||
}
|
||||
|
||||
endFunctionDeclaration(Token end) {
|
||||
Statement body = pop();
|
||||
Parameters parameters = pop();
|
||||
String name = pop(asName);
|
||||
TypeAnnotation returnType = popTypeAnnotation();
|
||||
push(new FunctionDeclaration(new FunctionExpression(parameters, body,
|
||||
name: name,
|
||||
returnType: returnType)));
|
||||
}
|
||||
|
||||
endFunctionBody(int count, Token begin, Token end) {
|
||||
push(new Block(popList(count, <Statement>[])));
|
||||
}
|
||||
}
|
||||
|
||||
class DefaultValue {
|
||||
final Expression expression;
|
||||
DefaultValue(this.expression);
|
||||
}
|
||||
|
||||
/// Compares ASTs for structural equality.
|
||||
void checkDeepEqual(x, y) {
|
||||
if (x is List && y is List) {
|
||||
if (x.length != y.length)
|
||||
return;
|
||||
for (var i=0; i<x.length; i++) {
|
||||
checkDeepEqual(x[i], y[i]);
|
||||
}
|
||||
}
|
||||
else if (x is Node && y is Node) {
|
||||
if (x.runtimeType != y.runtimeType)
|
||||
throw new Error();
|
||||
InstanceMirror xm = reflect(x);
|
||||
InstanceMirror ym = reflect(y);
|
||||
for (Symbol name in xm.type.instanceMembers.keys) {
|
||||
if (reflectClass(Object).declarations.containsKey(name)) {
|
||||
continue; // do not check things from Object, such as hashCode
|
||||
}
|
||||
MethodMirror mm = xm.type.instanceMembers[name];
|
||||
if (mm.isGetter) {
|
||||
var xv = xm.getField(name).reflectee;
|
||||
var yv = ym.getField(name).reflectee;
|
||||
checkDeepEqual(xv,yv);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (x is PrimitiveConstantValue && y is PrimitiveConstantValue) {
|
||||
checkDeepEqual(x.primitiveValue, y.primitiveValue);
|
||||
}
|
||||
else if (x is DartString && y is DartString) {
|
||||
if (x.slowToString() != y.slowToString()) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (x != y) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expression parseExpression(String code) {
|
||||
SourceFile file = new StringSourceFile.fromName('', code);
|
||||
Scanner scan = new Scanner(file);
|
||||
Token tok = scan.tokenize();
|
||||
AstBuilder builder = new AstBuilder();
|
||||
Parser parser = new Parser(builder, new MockParserOptions());
|
||||
tok = parser.parseExpression(tok);
|
||||
if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) {
|
||||
throw "Parse error in $code";
|
||||
}
|
||||
return builder.pop();
|
||||
}
|
||||
Statement parseStatement(String code) {
|
||||
SourceFile file = new StringSourceFile.fromName('', code);
|
||||
Scanner scan = new Scanner(file);
|
||||
Token tok = scan.tokenize();
|
||||
AstBuilder builder = new AstBuilder();
|
||||
Parser parser = new Parser(builder, new MockParserOptions());
|
||||
tok = parser.parseStatement(tok);
|
||||
if (builder.stack.length != 1 || tok.kind != EOF_TOKEN) {
|
||||
throw "Parse error in $code";
|
||||
}
|
||||
return builder.pop();
|
||||
}
|
||||
|
||||
String unparseExpression(Expression exp) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
new Unparser(buf).writeExpression(exp);
|
||||
return buf.toString();
|
||||
}
|
||||
String unparseStatement(Statement stmt) {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
new Unparser(buf).writeStatement(stmt);
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
/// Converts [exp] to an instance of the frontend AST and unparses that.
|
||||
String frontUnparseExpression(Expression exp) {
|
||||
tree.Node node = new TreePrinter().makeExpression(exp);
|
||||
return tree.unparse(node);
|
||||
}
|
||||
/// Converts [stmt] to an instance of the frontend AST and unparses that.
|
||||
String frontUnparseStatement(Statement stmt) {
|
||||
tree.Node node = new TreePrinter().makeStatement(stmt);
|
||||
return tree.unparse(node);
|
||||
}
|
||||
|
||||
/// Parses [code], unparses the resulting AST, then parses the unparsed text.
|
||||
/// The ASTs from the first and second parse are then compared for structural
|
||||
/// equality. Alternatively, if [expected] is not an empty string, the second
|
||||
/// parse must match the AST of parsing [expected].
|
||||
void checkFn(String code, String expected, Function parse, Function unparse) {
|
||||
var firstParse = parse(code);
|
||||
String unparsed = unparse(firstParse);
|
||||
try {
|
||||
var secondParse = parse(unparsed);
|
||||
var baseline = expected == "" ? firstParse : parse(expected);
|
||||
checkDeepEqual(baseline, secondParse);
|
||||
} catch (e, stack) {
|
||||
Expect.fail('"$code" was unparsed as "$unparsed"');
|
||||
}
|
||||
}
|
||||
|
||||
void checkExpression(String code, [String expected="", String expected2=""]) {
|
||||
checkFn(code, expected, parseExpression, unparseExpression);
|
||||
checkFn(code, expected2, parseExpression, frontUnparseExpression);
|
||||
}
|
||||
void checkStatement(String code, [String expected="", String expected2=""]) {
|
||||
checkFn(code, expected, parseStatement, unparseStatement);
|
||||
checkFn(code, expected2, parseStatement, frontUnparseStatement);
|
||||
}
|
||||
|
||||
void debugTokens(String code) {
|
||||
SourceFile file = new StringSourceFile.fromName('', code);
|
||||
Scanner scan = new Scanner(file);
|
||||
Token tok = scan.tokenize();
|
||||
while (tok.next != tok) {
|
||||
print(tok.toString());
|
||||
tok = tok.next;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
// To check if these tests are effective, one should manually alter
|
||||
// something in [Unparser] and see if a test fails.
|
||||
|
||||
checkExpression(" a + b + c");
|
||||
checkExpression("(a + b) + c");
|
||||
checkExpression(" a + (b + c)");
|
||||
|
||||
checkExpression(" a + b - c");
|
||||
checkExpression("(a + b) - c");
|
||||
checkExpression(" a + (b - c)");
|
||||
|
||||
checkExpression(" a - b + c");
|
||||
checkExpression("(a - b) + c");
|
||||
checkExpression(" a - (b + c)");
|
||||
|
||||
checkExpression(" a * b + c");
|
||||
checkExpression("(a * b) + c");
|
||||
checkExpression(" a * (b + c)");
|
||||
|
||||
checkExpression(" a + b * c");
|
||||
checkExpression("(a + b) * c");
|
||||
checkExpression(" a + (b * c)");
|
||||
|
||||
checkExpression(" a * b * c");
|
||||
checkExpression("(a * b) * c");
|
||||
checkExpression(" a * (b * c)");
|
||||
|
||||
checkExpression("a is T");
|
||||
checkExpression("a is! T");
|
||||
checkExpression("!(a is T)");
|
||||
|
||||
checkExpression("a is T.x");
|
||||
checkExpression("a is! T.x");
|
||||
checkExpression("!(a is T.x)");
|
||||
checkExpression("!(a is T).x");
|
||||
|
||||
checkExpression("a as T.x");
|
||||
checkExpression("(a as T).x");
|
||||
|
||||
checkExpression("a == b");
|
||||
checkExpression("a != b");
|
||||
checkExpression("!(a == b)", "a != b");
|
||||
|
||||
checkExpression("a && b ? c : d");
|
||||
checkExpression("(a && b) ? c : d");
|
||||
checkExpression("a && (b ? c : d)");
|
||||
|
||||
checkExpression("a || b ? c : d");
|
||||
checkExpression("(a || b) ? c : d");
|
||||
checkExpression("a || (b ? c : d)");
|
||||
|
||||
checkExpression(" a ? b : c && d");
|
||||
checkExpression(" a ? b : (c && d)");
|
||||
checkExpression("(a ? b : c) && d");
|
||||
|
||||
checkExpression(" a ? b : c = d");
|
||||
checkExpression(" a ? b : (c = d)");
|
||||
|
||||
checkExpression("(a == b) == c");
|
||||
checkExpression("a == (b == c)");
|
||||
|
||||
checkExpression(" a < b == c");
|
||||
checkExpression("(a < b) == c");
|
||||
checkExpression(" a < (b == c)");
|
||||
|
||||
checkExpression(" a == b < c");
|
||||
checkExpression("(a == b) < c");
|
||||
checkExpression(" a == (b < c)");
|
||||
|
||||
checkExpression("x.f()");
|
||||
checkExpression("(x.f)()");
|
||||
|
||||
checkExpression("x.f()()");
|
||||
checkExpression("(x.f)()()");
|
||||
|
||||
checkExpression("x.f().g()");
|
||||
checkExpression("(x.f)().g()");
|
||||
|
||||
checkExpression("x.f()");
|
||||
checkExpression("x.f(1 + 2)");
|
||||
checkExpression("x.f(1 + 2, 3 + 4)");
|
||||
checkExpression("x.f(1 + 2, foo:3 + 4)");
|
||||
checkExpression("x.f(1 + 2, foo:3 + 4, bar: 5)");
|
||||
checkExpression("x.f(foo:3 + 4)");
|
||||
checkExpression("x.f(foo:3 + 4, bar: 5)");
|
||||
|
||||
checkExpression("x.f.g.h");
|
||||
checkExpression("(x.f).g.h");
|
||||
checkExpression("(x.f.g).h");
|
||||
|
||||
checkExpression(" a = b + c");
|
||||
checkExpression(" a = (b + c)");
|
||||
checkExpression("(a = b) + c");
|
||||
|
||||
checkExpression("a + (b = c)");
|
||||
|
||||
checkExpression("dx * dx + dy * dy < r * r",
|
||||
"((dx * dx) + (dy * dy)) < (r * r)");
|
||||
checkExpression("mid = left + right << 1",
|
||||
"mid = ((left + right) << 1)");
|
||||
checkExpression("a + b % c * -d ^ e - f ~/ x & ++y / z++ | w > a ? b : c");
|
||||
checkExpression("a + b % c * -d ^ (e - f) ~/ x & ++y / z++ | w > a ? b : c");
|
||||
|
||||
checkExpression("'foo'");
|
||||
checkExpression("'foo' 'bar'", "'foobar'");
|
||||
|
||||
checkExpression("{}.length");
|
||||
checkExpression("{x: 1+2}.length");
|
||||
checkExpression("<String,int>{}.length");
|
||||
checkExpression("<String,int>{x: 1+2}.length");
|
||||
|
||||
checkExpression("[].length");
|
||||
checkExpression("[1+2].length");
|
||||
checkExpression("<num>[].length");
|
||||
checkExpression("<num>[1+2].length");
|
||||
|
||||
checkExpression("x + -y");
|
||||
checkExpression("x + --y");
|
||||
checkExpression("x++ + y");
|
||||
checkExpression("x + ++y");
|
||||
checkExpression("x-- - y");
|
||||
checkExpression("x-- - -y");
|
||||
checkExpression("x - --y");
|
||||
|
||||
checkExpression("x && !y");
|
||||
checkExpression("!x && y");
|
||||
checkExpression("!(x && y)");
|
||||
|
||||
checkExpression(" super + 1 * 2");
|
||||
checkExpression("(super + 1) * 2");
|
||||
checkExpression(" super + (1 * 2)");
|
||||
checkExpression("x + -super");
|
||||
checkExpression("x-- - -super");
|
||||
checkExpression("x - -super");
|
||||
checkExpression("x && !super");
|
||||
|
||||
checkExpression("super.f(1, 2) + 3");
|
||||
checkExpression("super.f + 3");
|
||||
|
||||
checkExpression(r"'foo\nbar'");
|
||||
checkExpression(r"'foo\r\nbar'");
|
||||
checkExpression(r"'foo\rbar'");
|
||||
checkExpression(r"'foo\'bar'");
|
||||
checkExpression(r"""'foo"bar'""");
|
||||
checkExpression(r"r'foo\nbar'");
|
||||
checkExpression("''");
|
||||
checkExpression("r''");
|
||||
|
||||
var sq = "'";
|
||||
var dq = '"';
|
||||
checkExpression("'$dq$dq' \"$sq$sq\"");
|
||||
checkExpression("'$dq$dq$dq$dq' \"$sq$sq$sq$sq\"");
|
||||
checkExpression(r"'\$\$\$\$\$\$\$\$\$'");
|
||||
checkExpression("'$dq$dq$dq' '\\n\\n\\n\\n\\n\\n\\n\\n\\n\\n' \"$sq$sq$sq\"");
|
||||
checkExpression("'$dq$dq$dq' '\\r\\r\\r\\r\\r\\r\\r\\r\\r\\r' \"$sq$sq$sq\"");
|
||||
checkExpression("'$dq$dq$dq' '\\r\\n\\r\\n\\r\\n\\r\\n\\r\\n' \"$sq$sq$sq\"");
|
||||
|
||||
checkExpression(r"'$foo'");
|
||||
checkExpression(r"'${foo}x'");
|
||||
checkExpression(r"'${foo}x\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\'");
|
||||
checkExpression(r"'abc' '${foo}' r'\\\\\\\'");
|
||||
|
||||
checkExpression(r"'${$x}'");
|
||||
checkExpression(r"'${$x}y'");
|
||||
checkExpression("null + null");
|
||||
|
||||
checkExpression("(x) => x",
|
||||
'',
|
||||
'(x){return x;}');
|
||||
checkStatement("fn(x) => x;",
|
||||
'',
|
||||
'fn(x){return x;}');
|
||||
|
||||
checkExpression("throw x");
|
||||
checkStatement("throw x;");
|
||||
|
||||
checkStatement("var x, y, z;");
|
||||
checkStatement("final x, y, z;");
|
||||
checkStatement("dynamic x, y, z;");
|
||||
checkStatement("String x, y, z;");
|
||||
checkStatement("List<int> x, y, z;");
|
||||
checkStatement("final dynamic x, y, z;");
|
||||
checkStatement("final String x, y, z;");
|
||||
checkStatement("final List<int> x, y, z;");
|
||||
|
||||
checkStatement("var x = y, z;");
|
||||
checkStatement("var x, y = z;");
|
||||
checkStatement("var x = y = z;");
|
||||
|
||||
// Note: We sometimes have to pass an expected string to account for
|
||||
// block flattening which does not preserve structural AST equality
|
||||
checkStatement("if (x) if (y) foo(); else bar(); ");
|
||||
checkStatement("if (x) { if (y) foo(); } else bar(); ");
|
||||
checkStatement("if (x) { if (y) foo(); else bar(); }",
|
||||
"if (x) if (y) foo(); else bar(); ");
|
||||
|
||||
checkStatement("if (x) while (y) if (z) foo(); else bar(); ");
|
||||
checkStatement("if (x) while (y) { if (z) foo(); } else bar(); ");
|
||||
checkStatement("if (x) while (y) { if (z) foo(); else bar(); }",
|
||||
"if (x) while (y) if (z) foo(); else bar(); ");
|
||||
|
||||
checkStatement("{var x = 1; {var x = 2;} return x;}");
|
||||
checkStatement("{var x = 1; {x = 2;} return x;}",
|
||||
"{var x = 1; x = 2; return x;}",
|
||||
"{var x = 1; x = 2; return x;}");
|
||||
|
||||
checkStatement("if (x) {var x = 1;}");
|
||||
|
||||
checkStatement("({'foo': 1}).bar();");
|
||||
checkStatement("({'foo': 1}).length;");
|
||||
checkStatement("({'foo': 1}).length + 1;");
|
||||
checkStatement("({'foo': 1})['foo'].toString();");
|
||||
checkStatement("({'foo': 1})['foo'] = 3;");
|
||||
checkStatement("({'foo': 1}['foo']());");
|
||||
checkStatement("({'foo': 1}['foo'])();");
|
||||
checkStatement("({'foo': 1})['foo'].x++;");
|
||||
checkStatement("({'foo': 1}) is Map;");
|
||||
checkStatement("({'foo': 1}) as Map;");
|
||||
checkStatement("({'foo': 1}) is util.Map;");
|
||||
checkStatement("({'foo': 1}) + 1;");
|
||||
|
||||
checkStatement("[1].bar();");
|
||||
checkStatement("1.bar();");
|
||||
checkStatement("'foo'.bar();");
|
||||
|
||||
checkStatement("do while(x); while (y);");
|
||||
checkStatement("{do; while(x); while (y);}");
|
||||
|
||||
checkStatement('switch(x) { case 1: case 2: return y; }');
|
||||
checkStatement('switch(x) { default: return y; }');
|
||||
checkStatement('switch(x) { case 1: x=y; default: return y; }');
|
||||
checkStatement('switch(x) { case 1: x=y; y=z; break; default: return y; }');
|
||||
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
/// End-to-end test of the dart2dart compiler.
|
||||
library dart_backend.end2end_test;
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import '../../../../pkg/analyzer2dart/test/test_helper.dart' hide TestSpec;
|
||||
import '../../../../pkg/analyzer2dart/test/end2end_data.dart';
|
||||
|
||||
import 'test_helper.dart';
|
||||
import '../output_collector.dart';
|
||||
|
||||
main(List<String> args) {
|
||||
performTests(TEST_DATA, asyncTester, runTest, args);
|
||||
}
|
||||
|
||||
runTest(TestSpec result) {
|
||||
OutputCollector outputCollector = new OutputCollector();
|
||||
asyncTest(() => compilerFor(result.input, outputProvider: outputCollector)
|
||||
.then((Compiler compiler) {
|
||||
String expectedOutput = result.output.trim();
|
||||
compiler.phase = Compiler.PHASE_COMPILING;
|
||||
DartBackend backend = compiler.backend;
|
||||
backend.assembleProgram();
|
||||
String output = outputCollector.getOutput('', 'dart').trim();
|
||||
Expect.equals(expectedOutput, output,
|
||||
'\nInput:\n${result.input}\n'
|
||||
'Expected:\n$expectedOutput\n'
|
||||
'Actual:\n$output\n');
|
||||
}));
|
||||
}
|
|
@ -1,528 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
import 'dart:async';
|
||||
import '../mock_compiler.dart';
|
||||
import 'sexpr_unstringifier.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
|
||||
import 'package:compiler/src/cps_ir/optimizers.dart';
|
||||
import 'package:compiler/src/constant_system_dart.dart';
|
||||
|
||||
// The tests in this file that ensure that sparse constant propagation on the
|
||||
// CPS IR works as expected.
|
||||
|
||||
// CP1 represents the following incoming dart code:
|
||||
//
|
||||
// int main() {
|
||||
// int i = 1;
|
||||
// int j;
|
||||
// if (i == 1) {
|
||||
// j = 2;
|
||||
// } else {
|
||||
// j = 3;
|
||||
// }
|
||||
// return j;
|
||||
// }
|
||||
|
||||
String CP1_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v3 (Constant (Int 2)))
|
||||
(InvokeContinuation return (v3))))
|
||||
(k2 ()
|
||||
(LetPrim (v4 (Constant (Int 3)))
|
||||
(InvokeContinuation return (v4)))))
|
||||
(Branch (IsTrue v2) k1 k2))))
|
||||
(InvokeMethod v0 == (v1) k0)))))
|
||||
""";
|
||||
String CP1_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v3 (Constant (Int 2)))
|
||||
(InvokeContinuation return (v3))))
|
||||
(k2 ()
|
||||
(LetPrim (v4 (Constant (Int 3)))
|
||||
(InvokeContinuation return (v4)))))
|
||||
(InvokeContinuation k1 ()))))
|
||||
(LetPrim (v5 (Constant (Bool true)))
|
||||
(InvokeContinuation k0 (v5)))))))
|
||||
""";
|
||||
|
||||
// CP2 represents the following incoming dart code:
|
||||
//
|
||||
// int main() {
|
||||
// int i = 1;
|
||||
// while (true) {
|
||||
// if (false || false) {
|
||||
// return i;
|
||||
// }
|
||||
// if (true && i == 1) {
|
||||
// return i;
|
||||
// }
|
||||
// }
|
||||
// return 42;
|
||||
// }
|
||||
|
||||
String CP2_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((rec k0 ()
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v1 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v1))))
|
||||
(k2 ()
|
||||
(LetPrim (v2 (Constant (Bool false)))
|
||||
(LetCont
|
||||
((k3 (v3)
|
||||
(LetCont
|
||||
((k4 ()
|
||||
(InvokeContinuation return (v0)))
|
||||
(k5 ()
|
||||
(LetPrim (v4 (Constant (Bool true)))
|
||||
(LetCont
|
||||
((k6 (v5)
|
||||
(LetCont
|
||||
((k7 ()
|
||||
(InvokeContinuation return (v0)))
|
||||
(k8 ()
|
||||
(InvokeContinuation rec k0 ())))
|
||||
(Branch (IsTrue v5) k7 k8))))
|
||||
(LetCont
|
||||
((k9 ()
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k10 (v7)
|
||||
(LetCont
|
||||
((k11 ()
|
||||
(LetPrim (v8 (Constant (Bool true)))
|
||||
(InvokeContinuation k6 (v8))))
|
||||
(k12 ()
|
||||
(LetPrim (v9 (Constant (Bool false)))
|
||||
(InvokeContinuation k6 (v9)))))
|
||||
(Branch (IsTrue v7) k11 k12))))
|
||||
(InvokeMethod v0 == (v6) k10))))
|
||||
(k13 ()
|
||||
(LetPrim (v10 (Constant (Bool false)))
|
||||
(InvokeContinuation k6 (v10)))))
|
||||
(Branch (IsTrue v4) k9 k13))))))
|
||||
(Branch (IsTrue v3) k4 k5))))
|
||||
(LetCont
|
||||
((k14 ()
|
||||
(LetPrim (v11 (Constant (Bool true)))
|
||||
(InvokeContinuation k3 (v11))))
|
||||
(k15 ()
|
||||
(LetPrim (v12 (Constant (Bool false)))
|
||||
(LetCont
|
||||
((k16 ()
|
||||
(LetPrim (v13 (Constant (Bool true)))
|
||||
(InvokeContinuation k3 (v13))))
|
||||
(k17 ()
|
||||
(LetPrim (v14 (Constant (Bool false)))
|
||||
(InvokeContinuation k3 (v14)))))
|
||||
(Branch (IsTrue v12) k16 k17)))))
|
||||
(Branch (IsTrue v2) k14 k15))))))
|
||||
(LetPrim (v15 (Constant (Bool true)))
|
||||
(Branch (IsTrue v15) k2 k1)))))
|
||||
(InvokeContinuation k0 ()))))
|
||||
""";
|
||||
String CP2_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((rec k0 ()
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v1 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v1))))
|
||||
(k2 ()
|
||||
(LetPrim (v2 (Constant (Bool false)))
|
||||
(LetCont
|
||||
((k3 (v3)
|
||||
(LetCont
|
||||
((k4 ()
|
||||
(InvokeContinuation return (v0)))
|
||||
(k5 ()
|
||||
(LetPrim (v4 (Constant (Bool true)))
|
||||
(LetCont
|
||||
((k6 (v5)
|
||||
(LetCont
|
||||
((k7 ()
|
||||
(InvokeContinuation return (v0)))
|
||||
(k8 ()
|
||||
(InvokeContinuation rec k0 ())))
|
||||
(InvokeContinuation k7 ()))))
|
||||
(LetCont
|
||||
((k9 ()
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k10 (v7)
|
||||
(LetCont
|
||||
((k11 ()
|
||||
(LetPrim (v8 (Constant (Bool true)))
|
||||
(InvokeContinuation k6 (v8))))
|
||||
(k12 ()
|
||||
(LetPrim (v9 (Constant (Bool false)))
|
||||
(InvokeContinuation k6 (v9)))))
|
||||
(InvokeContinuation k11 ()))))
|
||||
(LetPrim (v10 (Constant (Bool true)))
|
||||
(InvokeContinuation k10 (v10))))))
|
||||
(k13 ()
|
||||
(LetPrim (v11 (Constant (Bool false)))
|
||||
(InvokeContinuation k6 (v11)))))
|
||||
(InvokeContinuation k9 ()))))))
|
||||
(InvokeContinuation k5 ()))))
|
||||
(LetCont
|
||||
((k14 ()
|
||||
(LetPrim (v12 (Constant (Bool true)))
|
||||
(InvokeContinuation k3 (v12))))
|
||||
(k15 ()
|
||||
(LetPrim (v13 (Constant (Bool false)))
|
||||
(LetCont
|
||||
((k16 ()
|
||||
(LetPrim (v14 (Constant (Bool true)))
|
||||
(InvokeContinuation k3 (v14))))
|
||||
(k17 ()
|
||||
(LetPrim (v15 (Constant (Bool false)))
|
||||
(InvokeContinuation k3 (v15)))))
|
||||
(InvokeContinuation k17 ())))))
|
||||
(InvokeContinuation k15 ()))))))
|
||||
(LetPrim (v16 (Constant (Bool true)))
|
||||
(InvokeContinuation k2 ())))))
|
||||
(InvokeContinuation k0 ()))))
|
||||
""";
|
||||
|
||||
// CP3 represents the following incoming dart code:
|
||||
//
|
||||
// int main() {
|
||||
// int i = 1;
|
||||
// i = f();
|
||||
// if (i == 1) {
|
||||
// return 42;
|
||||
// }
|
||||
// return i;
|
||||
// }
|
||||
|
||||
String CP3_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k0 (v1)
|
||||
(LetPrim (v2 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k1 (v3)
|
||||
(LetCont
|
||||
((k2 ()
|
||||
(LetPrim (v4 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v4))))
|
||||
(k3 ()
|
||||
(InvokeContinuation return (v1))))
|
||||
(Branch (IsTrue v3) k2 k3))))
|
||||
(InvokeMethod v1 == (v2) k1)))))
|
||||
(InvokeStatic f () k0))))
|
||||
""";
|
||||
String CP3_OUT = CP3_IN;
|
||||
|
||||
// Addition.
|
||||
|
||||
String CP4_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(InvokeContinuation return (v2))))
|
||||
(InvokeMethod v0 + (v1) k0)))))
|
||||
""";
|
||||
String CP4_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(InvokeContinuation return (v2))))
|
||||
(LetPrim (v3 (Constant (Int 3)))
|
||||
(InvokeContinuation k0 (v3)))))))
|
||||
""";
|
||||
|
||||
// Array access operator (no optimization).
|
||||
|
||||
String CP5_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(InvokeContinuation return (v2))))
|
||||
(InvokeMethod v0 [] (v1) k0)))))
|
||||
""";
|
||||
String CP5_OUT = CP5_IN;
|
||||
|
||||
// Division by 0.
|
||||
|
||||
String CP6_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(InvokeContinuation return (v2))))
|
||||
(InvokeMethod v0 / (v1) k0)))))
|
||||
""";
|
||||
String CP6_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(InvokeContinuation return (v2))))
|
||||
(LetPrim (v3 (Constant (Double Infinity)))
|
||||
(InvokeContinuation k0 (v3)))))))
|
||||
""";
|
||||
|
||||
// Concatenate strings.
|
||||
|
||||
String CP7_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (String "b")))
|
||||
(LetPrim (v1 (Constant (String "d")))
|
||||
(LetPrim (v2 (Constant (String "a")))
|
||||
(LetPrim (v3 (Constant (String "c")))
|
||||
(LetPrim (v4 (Constant (String "")))
|
||||
(LetCont
|
||||
((k0 (v5)
|
||||
(LetCont
|
||||
((k1 (v6)
|
||||
(InvokeContinuation return (v6))))
|
||||
(InvokeMethod v5 length () k1))))
|
||||
(ConcatenateStrings (v2 v0 v3 v1 v4) k0))))))))
|
||||
""";
|
||||
String CP7_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (String "b")))
|
||||
(LetPrim (v1 (Constant (String "d")))
|
||||
(LetPrim (v2 (Constant (String "a")))
|
||||
(LetPrim (v3 (Constant (String "c")))
|
||||
(LetPrim (v4 (Constant (String "")))
|
||||
(LetCont
|
||||
((k0 (v5)
|
||||
(LetCont
|
||||
((k1 (v6)
|
||||
(InvokeContinuation return (v6))))
|
||||
(InvokeMethod v5 length () k1))))
|
||||
(LetPrim (v7 (Constant (String "abcd")))
|
||||
(InvokeContinuation k0 (v7))))))))))
|
||||
""";
|
||||
|
||||
// TODO(jgruber): We can't test is-check optimization because the unstringifier
|
||||
// does not recreate accurate types for the TypeOperator node.
|
||||
|
||||
// Simple branch removal.
|
||||
|
||||
String CP8_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v3 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v3))))
|
||||
(k2 ()
|
||||
(InvokeContinuation return (v0))))
|
||||
(Branch (IsTrue v2) k1 k2))))
|
||||
(InvokeMethod v0 == (v1) k0)))))
|
||||
""";
|
||||
String CP8_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v3 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v3))))
|
||||
(k2 ()
|
||||
(InvokeContinuation return (v0))))
|
||||
(InvokeContinuation k1 ()))))
|
||||
(LetPrim (v4 (Constant (Bool true)))
|
||||
(InvokeContinuation k0 (v4)))))))
|
||||
""";
|
||||
|
||||
// While loop.
|
||||
|
||||
String CP9_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((rec k0 (v1)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(InvokeContinuation return (v1)))
|
||||
(k2 ()
|
||||
(LetPrim (v2 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k3 (v3)
|
||||
(LetCont
|
||||
((k4 (v4)
|
||||
(LetCont
|
||||
((k5 ()
|
||||
(LetPrim (v5 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v5))))
|
||||
(k6 ()
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k7 (v7)
|
||||
(InvokeContinuation rec k0 (v7))))
|
||||
(InvokeMethod v1 + (v6) k7)))))
|
||||
(Branch (IsTrue v4) k5 k6))))
|
||||
(LetCont
|
||||
((k8 ()
|
||||
(LetPrim (v8 (Constant (Bool false)))
|
||||
(InvokeContinuation k4 (v8))))
|
||||
(k9 ()
|
||||
(LetPrim (v9 (Constant (Bool true)))
|
||||
(InvokeContinuation k4 (v9)))))
|
||||
(Branch (IsTrue v3) k8 k9)))))
|
||||
(InvokeMethod v1 == (v2) k3)))))
|
||||
(LetPrim (v10 (Constant (Bool true)))
|
||||
(Branch (IsTrue v10) k2 k1)))))
|
||||
(InvokeContinuation k0 (v0)))))
|
||||
""";
|
||||
String CP9_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((rec k0 (v1)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(InvokeContinuation return (v1)))
|
||||
(k2 ()
|
||||
(LetPrim (v2 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k3 (v3)
|
||||
(LetCont
|
||||
((k4 (v4)
|
||||
(LetCont
|
||||
((k5 ()
|
||||
(LetPrim (v5 (Constant (Int 42)))
|
||||
(InvokeContinuation return (v5))))
|
||||
(k6 ()
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k7 (v7)
|
||||
(InvokeContinuation rec k0 (v7))))
|
||||
(InvokeMethod v1 + (v6) k7)))))
|
||||
(Branch (IsTrue v4) k5 k6))))
|
||||
(LetCont
|
||||
((k8 ()
|
||||
(LetPrim (v8 (Constant (Bool false)))
|
||||
(InvokeContinuation k4 (v8))))
|
||||
(k9 ()
|
||||
(LetPrim (v9 (Constant (Bool true)))
|
||||
(InvokeContinuation k4 (v9)))))
|
||||
(Branch (IsTrue v3) k8 k9)))))
|
||||
(InvokeMethod v1 == (v2) k3)))))
|
||||
(LetPrim (v10 (Constant (Bool true)))
|
||||
(InvokeContinuation k2 ())))))
|
||||
(InvokeContinuation k0 (v0)))))
|
||||
""";
|
||||
|
||||
// While loop, from:
|
||||
//
|
||||
// int main() {
|
||||
// for (int i = 0; i < 2; i++) {
|
||||
// print(42 + i);
|
||||
// }
|
||||
// }
|
||||
|
||||
String CP10_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v1)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v2 (Constant (Null)))
|
||||
(InvokeContinuation return (v2))))
|
||||
(k2 ()
|
||||
(LetPrim (v3 (Constant (Int 42)))
|
||||
(LetCont
|
||||
((k3 (v4)
|
||||
(LetCont
|
||||
((k4 (v5)
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k5 (v7)
|
||||
(InvokeContinuation rec k0 (v7))))
|
||||
(InvokeMethod v1 + (v6) k5)))))
|
||||
(InvokeStatic print (v4) k4))))
|
||||
(InvokeMethod v3 + (v1) k3)))))
|
||||
(LetPrim (v8 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k6 (v9)
|
||||
(Branch (IsTrue v9) k2 k1)))
|
||||
(InvokeMethod v1 < (v8) k6))))))
|
||||
(InvokeContinuation k0 (v0)))))
|
||||
""";
|
||||
String CP10_OUT = CP10_IN;
|
||||
|
||||
/// Normalizes whitespace by replacing all whitespace sequences by a single
|
||||
/// space and trimming leading and trailing whitespace.
|
||||
String normalizeSExpr(String input) {
|
||||
return input.replaceAll(new RegExp(r'[ \n\t]+'), ' ').trim();
|
||||
}
|
||||
|
||||
/// Parses the given input IR, runs an optimization pass over it, and compares
|
||||
/// the stringification of the result against the expected output.
|
||||
Future testConstantPropagator(String input, String expectedOutput) {
|
||||
final compiler = new MockCompiler.internal(
|
||||
emitJavaScript: false,
|
||||
enableMinification: false);
|
||||
return compiler.init().then((_) {
|
||||
final unstringifier = new SExpressionUnstringifier();
|
||||
final stringifier = new SExpressionStringifier();
|
||||
final optimizer = new TypePropagator(
|
||||
compiler.types,
|
||||
DART_CONSTANT_SYSTEM,
|
||||
new UnitTypeSystem(),
|
||||
compiler.internalError);
|
||||
|
||||
final f = unstringifier.unstringify(input);
|
||||
optimizer.rewrite(f);
|
||||
|
||||
String expected = normalizeSExpr(expectedOutput);
|
||||
String actual = normalizeSExpr(stringifier.visit(f));
|
||||
|
||||
Expect.equals(expected, actual);
|
||||
});
|
||||
}
|
||||
|
||||
void main() {
|
||||
asyncTest(() => testConstantPropagator(CP1_IN, CP1_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP2_IN, CP2_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP3_IN, CP3_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP4_IN, CP4_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP5_IN, CP5_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP6_IN, CP6_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP7_IN, CP7_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP8_IN, CP8_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP9_IN, CP9_OUT));
|
||||
asyncTest(() => testConstantPropagator(CP10_IN, CP10_OUT));
|
||||
}
|
|
@ -1,97 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
import 'opt_redundant_phi_test.dart';
|
||||
|
||||
// The 'cyclic deps' IR tests removal of redundant phis with cyclic
|
||||
// dependencies.
|
||||
//
|
||||
// void main() {
|
||||
// var x = 0;
|
||||
// var y = x;
|
||||
// for (int i = 0; i < 10; i++) {
|
||||
// if (i == -1) x = y;
|
||||
// if (i == -1) y = x;
|
||||
// }
|
||||
// print(x);
|
||||
// print(y);
|
||||
// }
|
||||
|
||||
String CYCLIC_DEPS_IN = """
|
||||
(FunctionDefinition main (return) (LetPrim v0 (Constant 0))
|
||||
(LetPrim v1 (Constant 0))
|
||||
(LetCont* (k0 v2 v3 v4)
|
||||
(LetCont (k1)
|
||||
(LetCont (k2 v5)
|
||||
(LetCont (k3 v6) (LetPrim v7 (Constant null))
|
||||
(InvokeContinuation return v7))
|
||||
(InvokeStatic print v3 k3))
|
||||
(InvokeStatic print v2 k2))
|
||||
(LetCont (k4) (LetPrim v8 (Constant 1))
|
||||
(LetCont (k5 v9)
|
||||
(LetCont (k6 v10)
|
||||
(LetCont (k7 v11) (LetPrim v12 (Constant 1))
|
||||
(LetCont (k8 v13)
|
||||
(LetCont (k9 v14)
|
||||
(LetCont (k10 v15)
|
||||
(LetPrim v16 (Constant 1))
|
||||
(LetCont (k11 v17)
|
||||
(InvokeContinuation* k0 v11 v15 v17))
|
||||
(InvokeMethod v4 + v16 k11))
|
||||
(LetCont (k12) (InvokeContinuation k10 v11))
|
||||
(LetCont (k13) (InvokeContinuation k10 v3))
|
||||
(Branch (IsTrue v14) k12 k13))
|
||||
(InvokeMethod v4 == v13 k9))
|
||||
(InvokeMethod v12 unary- k8))
|
||||
(LetCont (k14) (InvokeContinuation k7 v3))
|
||||
(LetCont (k15) (InvokeContinuation k7 v2))
|
||||
(Branch (IsTrue v10) k14 k15))
|
||||
(InvokeMethod v4 == v9 k6))
|
||||
(InvokeMethod v8 unary- k5))
|
||||
(LetPrim v18 (Constant 10))
|
||||
(LetCont (k16 v19) (Branch (IsTrue v19) k4 k1))
|
||||
(InvokeMethod v4 < v18 k16))
|
||||
(InvokeContinuation k0 v0 v0 v1))
|
||||
""";
|
||||
|
||||
String CYCLIC_DEPS_OUT = """
|
||||
(FunctionDefinition main (return) (LetPrim v0 (Constant 0))
|
||||
(LetPrim v1 (Constant 0))
|
||||
(LetCont* (k0 v2)
|
||||
(LetCont (k1)
|
||||
(LetCont (k2 v3)
|
||||
(LetCont (k3 v4) (LetPrim v5 (Constant null))
|
||||
(InvokeContinuation return v5))
|
||||
(InvokeStatic print v0 k3))
|
||||
(InvokeStatic print v0 k2))
|
||||
(LetCont (k4) (LetPrim v6 (Constant 1))
|
||||
(LetCont (k5 v7)
|
||||
(LetCont (k6 v8)
|
||||
(LetCont (k7) (LetPrim v9 (Constant 1))
|
||||
(LetCont (k8 v10)
|
||||
(LetCont (k9 v11)
|
||||
(LetCont (k10)
|
||||
(LetPrim v12 (Constant 1))
|
||||
(LetCont (k11 v13)
|
||||
(InvokeContinuation* k0 v13))
|
||||
(InvokeMethod v2 + v12 k11))
|
||||
(LetCont (k12) (InvokeContinuation k10))
|
||||
(LetCont (k13) (InvokeContinuation k10))
|
||||
(Branch (IsTrue v11) k12 k13))
|
||||
(InvokeMethod v2 == v10 k9))
|
||||
(InvokeMethod v9 unary- k8))
|
||||
(LetCont (k14) (InvokeContinuation k7))
|
||||
(LetCont (k15) (InvokeContinuation k7))
|
||||
(Branch (IsTrue v8) k14 k15))
|
||||
(InvokeMethod v2 == v7 k6))
|
||||
(InvokeMethod v6 unary- k5))
|
||||
(LetPrim v14 (Constant 10))
|
||||
(LetCont (k16 v15) (Branch (IsTrue v15) k4 k1))
|
||||
(InvokeMethod v2 < v14 k16))
|
||||
(InvokeContinuation k0 v1))
|
||||
""";
|
||||
|
||||
void main() {
|
||||
testRedundantPhi(CYCLIC_DEPS_IN, CYCLIC_DEPS_OUT);
|
||||
}
|
|
@ -1,309 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
import 'sexpr_unstringifier.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
|
||||
import 'package:compiler/src/cps_ir/optimizers.dart';
|
||||
|
||||
// The 'read in loop' IR tests the most basic case of redundant phi removal
|
||||
// and represents the following source code:
|
||||
//
|
||||
// void main() {
|
||||
// int j = 42;
|
||||
// for (int i = 0; i < 2; i++) {
|
||||
// print(j.toString());
|
||||
// }
|
||||
// }
|
||||
|
||||
String READ_IN_LOOP_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v2 v3)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v4 (Constant (Null)))
|
||||
(InvokeContinuation return (v4))))
|
||||
(k2 ()
|
||||
(LetCont
|
||||
((k3 (v5)
|
||||
(LetCont
|
||||
((k4 (v6)
|
||||
(LetPrim (v7 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k5 (v8)
|
||||
(InvokeContinuation rec k0 (v2 v8))))
|
||||
(InvokeMethod v3 + (v7) k5)))))
|
||||
(InvokeStatic print (v5) k4))))
|
||||
(InvokeMethod v2 toString () k3))))
|
||||
(LetPrim (v9 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k6 (v10)
|
||||
(Branch (IsTrue v10) k2 k1)))
|
||||
(InvokeMethod v3 < (v9) k6))))))
|
||||
(InvokeContinuation k0 (v0 v1))))))
|
||||
""";
|
||||
|
||||
String READ_IN_LOOP_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v3 (Constant (Null)))
|
||||
(InvokeContinuation return (v3))))
|
||||
(k2 ()
|
||||
(LetCont
|
||||
((k3 (v4)
|
||||
(LetCont
|
||||
((k4 (v5)
|
||||
(LetPrim (v6 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k5 (v7)
|
||||
(InvokeContinuation rec k0 (v7))))
|
||||
(InvokeMethod v2 + (v6) k5)))))
|
||||
(InvokeStatic print (v4) k4))))
|
||||
(InvokeMethod v0 toString () k3))))
|
||||
(LetPrim (v8 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k6 (v9)
|
||||
(Branch (IsTrue v9) k2 k1)))
|
||||
(InvokeMethod v2 < (v8) k6))))))
|
||||
(InvokeContinuation k0 (v1))))))
|
||||
""";
|
||||
|
||||
// The 'inner loop' IR represents the following source code:
|
||||
//
|
||||
// void main() {
|
||||
// int j = 42;
|
||||
// for (int i = 0; i < 2; i++) {
|
||||
// for (int k = 0; k < 2; k++) {
|
||||
// print(i.toString());
|
||||
// }
|
||||
// }
|
||||
// print(j.toString());
|
||||
// }
|
||||
//
|
||||
// This test case ensures that iterative optimization works: first, v8 and v9
|
||||
// are removed from k5, and only then can k0 be optimized as well.
|
||||
|
||||
const String INNER_LOOP_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v2 v3)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetCont
|
||||
((k2 (v4)
|
||||
(LetCont
|
||||
((k3 (v5)
|
||||
(LetPrim (v6 (Constant (Null)))
|
||||
(InvokeContinuation return (v6)))))
|
||||
(InvokeStatic print (v4) k3))))
|
||||
(InvokeMethod v2 toString () k2)))
|
||||
(k4 ()
|
||||
(LetPrim (v7 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k5 (v8 v9 v10)
|
||||
(LetCont
|
||||
((k6 ()
|
||||
(LetPrim (v11 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k7 (v12)
|
||||
(InvokeContinuation rec k0 (v8 v12))))
|
||||
(InvokeMethod v9 + (v11) k7))))
|
||||
(k8 ()
|
||||
(LetCont
|
||||
((k9 (v13)
|
||||
(LetCont
|
||||
((k10 (v14)
|
||||
(LetPrim (v15 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k11 (v16)
|
||||
(InvokeContinuation rec k5 (v8 v9 v16))))
|
||||
(InvokeMethod v10 + (v15) k11)))))
|
||||
(InvokeStatic print (v13) k10))))
|
||||
(InvokeMethod v9 toString () k9))))
|
||||
(LetPrim (v17 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k12 (v18)
|
||||
(Branch (IsTrue v18) k8 k6)))
|
||||
(InvokeMethod v10 < (v17) k12))))))
|
||||
(InvokeContinuation k5 (v2 v3 v7))))))
|
||||
(LetPrim (v19 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k13 (v20)
|
||||
(Branch (IsTrue v20) k4 k1)))
|
||||
(InvokeMethod v3 < (v19) k13))))))
|
||||
(InvokeContinuation k0 (v0 v1))))))
|
||||
""";
|
||||
|
||||
const String INNER_LOOP_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v2)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetCont
|
||||
((k2 (v3)
|
||||
(LetCont
|
||||
((k3 (v4)
|
||||
(LetPrim (v5 (Constant (Null)))
|
||||
(InvokeContinuation return (v5)))))
|
||||
(InvokeStatic print (v3) k3))))
|
||||
(InvokeMethod v0 toString () k2)))
|
||||
(k4 ()
|
||||
(LetPrim (v6 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k5 (v7)
|
||||
(LetCont
|
||||
((k6 ()
|
||||
(LetPrim (v8 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k7 (v9)
|
||||
(InvokeContinuation rec k0 (v9))))
|
||||
(InvokeMethod v2 + (v8) k7))))
|
||||
(k8 ()
|
||||
(LetCont
|
||||
((k9 (v10)
|
||||
(LetCont
|
||||
((k10 (v11)
|
||||
(LetPrim (v12 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k11 (v13)
|
||||
(InvokeContinuation rec k5 (v13))))
|
||||
(InvokeMethod v7 + (v12) k11)))))
|
||||
(InvokeStatic print (v10) k10))))
|
||||
(InvokeMethod v2 toString () k9))))
|
||||
(LetPrim (v14 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k12 (v15)
|
||||
(Branch (IsTrue v15) k8 k6)))
|
||||
(InvokeMethod v7 < (v14) k12))))))
|
||||
(InvokeContinuation k5 (v6))))))
|
||||
(LetPrim (v16 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k13 (v17)
|
||||
(Branch (IsTrue v17) k4 k1)))
|
||||
(InvokeMethod v2 < (v16) k13))))))
|
||||
(InvokeContinuation k0 (v1))))))
|
||||
""";
|
||||
|
||||
// There are no redundant phis in the 'basic loop' IR, and this test ensures
|
||||
// simply that the optimization does not alter the IR. It represents the
|
||||
// following program:
|
||||
//
|
||||
// void main() {
|
||||
// for (int i = 0; i < 2; i++) {
|
||||
// print(i.toString());
|
||||
// }
|
||||
// }
|
||||
|
||||
String BASIC_LOOP_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((rec k0 (v1)
|
||||
(LetCont
|
||||
((k1 ()
|
||||
(LetPrim (v2 (Constant (Null)))
|
||||
(InvokeContinuation return (v2))))
|
||||
(k2 ()
|
||||
(LetCont
|
||||
((k3 (v3)
|
||||
(LetCont
|
||||
((k4 (v4)
|
||||
(LetPrim (v5 (Constant (Int 1)))
|
||||
(LetCont
|
||||
((k5 (v6)
|
||||
(InvokeContinuation rec k0 (v6))))
|
||||
(InvokeMethod v1 + (v5) k5)))))
|
||||
(InvokeStatic print (v3) k4))))
|
||||
(InvokeMethod v1 toString () k3))))
|
||||
(LetPrim (v7 (Constant (Int 2)))
|
||||
(LetCont
|
||||
((k6 (v8)
|
||||
(Branch (IsTrue v8) k2 k1)))
|
||||
(InvokeMethod v1 < (v7) k6))))))
|
||||
(InvokeContinuation k0 (v0)))))
|
||||
""";
|
||||
|
||||
String BASIC_LOOP_OUT = BASIC_LOOP_IN;
|
||||
|
||||
// Ensures that proper scoping is preserved, i.e. that the optimized
|
||||
// continuation body does reference out of scope primitives.
|
||||
// IR written by hand since this case is currently not being generated.
|
||||
|
||||
String SCOPING_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetCont
|
||||
((k0 (v1)
|
||||
(InvokeStatic print (v1) return)))
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetPrim (v2 (Constant (Null)))
|
||||
(InvokeContinuation k0 (v0))))))
|
||||
""";
|
||||
|
||||
String SCOPING_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((k0 ()
|
||||
(InvokeStatic print (v0) return)))
|
||||
(LetPrim (v1 (Constant (Null)))
|
||||
(InvokeContinuation k0 ())))))
|
||||
""";
|
||||
|
||||
// Ensures that continuations which are never invoked are not optimized.
|
||||
// IR written by hand.
|
||||
|
||||
String NEVER_INVOKED_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont
|
||||
((k0 (v1)
|
||||
(InvokeStatic print (v1) return)))
|
||||
(InvokeContinuation return (v0)))))
|
||||
""";
|
||||
|
||||
String NEVER_INVOKED_OUT = NEVER_INVOKED_IN;
|
||||
|
||||
/// Normalizes whitespace by replacing all whitespace sequences by a single
|
||||
/// space and trimming leading and trailing whitespace.
|
||||
String normalizeSExpr(String input) {
|
||||
return input.replaceAll(new RegExp(r'[ \n\t]+'), ' ').trim();
|
||||
}
|
||||
|
||||
/// Parses the given input IR, runs a redundant phi pass over it, and compares
|
||||
/// the stringification of the result against the expected output.
|
||||
void testRedundantPhi(String input, String expectedOutput) {
|
||||
final unstringifier = new SExpressionUnstringifier();
|
||||
final stringifier = new SExpressionStringifier();
|
||||
final optimizer = new RedundantPhiEliminator();
|
||||
|
||||
FunctionDefinition f = unstringifier.unstringify(input);
|
||||
optimizer.rewrite(f);
|
||||
|
||||
String expected = normalizeSExpr(expectedOutput);
|
||||
String actual = normalizeSExpr(stringifier.visit(f));
|
||||
|
||||
Expect.equals(expected, actual, "Actual:\n$actual");
|
||||
}
|
||||
|
||||
void main() {
|
||||
testRedundantPhi(READ_IN_LOOP_IN, READ_IN_LOOP_OUT);
|
||||
testRedundantPhi(INNER_LOOP_IN, INNER_LOOP_OUT);
|
||||
testRedundantPhi(BASIC_LOOP_IN, BASIC_LOOP_OUT);
|
||||
testRedundantPhi(SCOPING_IN, SCOPING_OUT);
|
||||
testRedundantPhi(NEVER_INVOKED_IN, NEVER_INVOKED_OUT);
|
||||
}
|
|
@ -1,331 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
import 'sexpr_unstringifier.dart';
|
||||
import "package:expect/expect.dart";
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
|
||||
import 'package:compiler/src/cps_ir/optimizers.dart';
|
||||
|
||||
// The tests in this file that ensure shrinking reductions work as expected.
|
||||
// Reductions and their corresponding names are taken from
|
||||
// 'Compiling with Continuations, Continued' by Andrew Kennedy.
|
||||
|
||||
// Basic dead-val: letprim x = V in K -> K (x not free in K).
|
||||
//
|
||||
// int main() {
|
||||
// int i = 42;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
String DEAD_VAL_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v1)))))
|
||||
""";
|
||||
String DEAD_VAL_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v0))))
|
||||
""";
|
||||
|
||||
// Iterative dead-val. No optimizations possible since the continuation to
|
||||
// InvokeMethod must have one argument, even if it is unused.
|
||||
//
|
||||
// int main() {
|
||||
// int i = 42;
|
||||
// int j = i + 1;
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
String ITERATIVE_DEAD_VAL1_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(LetCont ((k0 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeMethod v0 + (v1) k0)))))
|
||||
""";
|
||||
String ITERATIVE_DEAD_VAL1_OUT = ITERATIVE_DEAD_VAL1_IN;
|
||||
|
||||
// Iterative dead-val. IR written by hand.
|
||||
|
||||
String ITERATIVE_DEAD_VAL2_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 42)))
|
||||
(LetPrim (v1
|
||||
(CreateFunction
|
||||
(FunctionDefinition f () (i) return
|
||||
(InvokeContinuation return (v0)))))
|
||||
(LetPrim (v2 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v2))))))
|
||||
""";
|
||||
String ITERATIVE_DEAD_VAL2_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v0))))
|
||||
""";
|
||||
|
||||
// Basic dead-cont: letcont k x = L in K -> K (k not free in K).
|
||||
// IR written by hand.
|
||||
|
||||
String DEAD_CONT_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v4 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v0)
|
||||
(InvokeConstructor List () return)))
|
||||
(LetCont ((k1 (v1)
|
||||
(LetCont ((k2 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v4) k2))))
|
||||
(InvokeStatic print (v4) k1)))))
|
||||
""";
|
||||
String DEAD_CONT_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v1)
|
||||
(LetCont ((k1 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v0) k1))))
|
||||
(InvokeStatic print (v0) k0))))
|
||||
""";
|
||||
|
||||
// Iterative dead-cont. IR written by hand.
|
||||
|
||||
String ITERATIVE_DEAD_CONT_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v4 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v0)
|
||||
(InvokeConstructor List () return)))
|
||||
(LetCont ((k3 (v5)
|
||||
(InvokeContinuation k0 (v5))))
|
||||
(LetCont ((k1 (v1)
|
||||
(LetCont ((k2 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v4) k2))))
|
||||
(InvokeStatic print (v4) k1))))))
|
||||
""";
|
||||
String ITERATIVE_DEAD_CONT_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v1)
|
||||
(LetCont ((k1 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v0) k1))))
|
||||
(InvokeStatic print (v0) k0))))
|
||||
""";
|
||||
|
||||
// Beta-cont-lin: letcont k x = K in C[k y] -> C[K[y/x]] (k not free in C).
|
||||
// IR written by hand.
|
||||
|
||||
String BETA_CONT_LIN_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetCont ((k0 (v0)
|
||||
(LetCont ((k1 (v1)
|
||||
(LetCont ((k2 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v0) k2))))
|
||||
(InvokeStatic print (v0) k1))))
|
||||
(LetPrim (v4 (Constant (Int 0)))
|
||||
(InvokeContinuation k0 (v4)))))
|
||||
""";
|
||||
String BETA_CONT_LIN_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v1)
|
||||
(LetCont ((k1 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v0) k1))))
|
||||
(InvokeStatic print (v0) k0))))
|
||||
""";
|
||||
|
||||
// Beta-cont-lin with recursive continuation. IR written by hand.
|
||||
|
||||
String RECURSIVE_BETA_CONT_LIN_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetCont ((rec k0 (v0)
|
||||
(InvokeContinuation rec k0 (v0))))
|
||||
(LetPrim (v1 (Constant (Int 0)))
|
||||
(InvokeContinuation k0 (v1)))))
|
||||
""";
|
||||
String RECURSIVE_BETA_CONT_LIN_OUT = RECURSIVE_BETA_CONT_LIN_IN;
|
||||
|
||||
// Beta-cont-lin used inside body. IR written by hand.
|
||||
|
||||
String USED_BETA_CONT_LIN_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v1)
|
||||
(LetCont ((k1 (v2)
|
||||
(LetCont ((k2 (v3)
|
||||
(LetPrim (v4 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v4)))))
|
||||
(InvokeStatic print (v1) k2))))
|
||||
(InvokeStatic print (v1) k1))))
|
||||
(LetPrim (v5
|
||||
(CreateFunction
|
||||
(FunctionDefinition f () () return
|
||||
(InvokeContinuation return (v1)))))
|
||||
(InvokeContinuation k0 (v0))))))
|
||||
""";
|
||||
String USED_BETA_CONT_LIN_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((k0 (v1)
|
||||
(LetCont ((k1 (v2)
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v3)))))
|
||||
(InvokeStatic print (v0) k1))))
|
||||
(InvokeStatic print (v0) k0))))
|
||||
""";
|
||||
|
||||
// Eta-cont: letcont k x = j x in K -> K[j/k].
|
||||
// IR written by hand.
|
||||
//
|
||||
// This test is incorrectly named: with the current implementation, there is no
|
||||
// eta reduction. Instead, dead-parameter, beta-cont-lin, and dead-val
|
||||
// reductions are performed, which in turn creates a second beta-cont-lin
|
||||
// reduction.
|
||||
//
|
||||
// TODO(kmillikin): To test continuation eta reduction, use eta redexes that are
|
||||
// not overlapping beta redexes.
|
||||
String ETA_CONT_IN = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(LetCont ((rec k0 (v1)
|
||||
(InvokeContinuation return (v0))))
|
||||
(LetCont ((k1 (v2)
|
||||
(InvokeContinuation k0 (v2))))
|
||||
(LetPrim (v3
|
||||
(CreateFunction
|
||||
(FunctionDefinition f () () return
|
||||
(InvokeContinuation k0 (v0)))))
|
||||
(InvokeContinuation k1 (v0)))))))
|
||||
""";
|
||||
String ETA_CONT_OUT = """
|
||||
(FunctionDefinition main () () return
|
||||
(LetPrim (v0 (Constant (Int 0)))
|
||||
(InvokeContinuation return (v0))))
|
||||
""";
|
||||
|
||||
// Dead-parameter:
|
||||
// letcont k x = E0 in E1 -> letcont k () = E0 in E1,
|
||||
// if x does not occur free in E0.
|
||||
|
||||
// Parameter v1 is unused in k0.
|
||||
String DEAD_PARAMETER_IN = """
|
||||
(FunctionDefinition main () (x) return
|
||||
(LetCont ((k0 (v0 v1 v2)
|
||||
(InvokeStatic foo (v0 v2) return)))
|
||||
(LetCont ((k1 ()
|
||||
(LetPrim (v3 (Constant (Int 0)))
|
||||
(LetPrim (v4 (Constant (Int 1)))
|
||||
(LetPrim (v5 (Constant (Int 2)))
|
||||
(InvokeContinuation k0 (v3 v4 v5))))))
|
||||
(k2 ()
|
||||
(LetPrim (v6 (Constant (Int 3)))
|
||||
(LetPrim (v7 (Constant (Int 4)))
|
||||
(LetPrim (v8 (Constant (Int 5)))
|
||||
(InvokeContinuation k0 (v6 v7 v8)))))))
|
||||
(Branch (IsTrue x) k1 k2))))
|
||||
""";
|
||||
String DEAD_PARAMETER_OUT = """
|
||||
(FunctionDefinition main () (x) return
|
||||
(LetCont ((k0 (v0 v1)
|
||||
(InvokeStatic foo (v0 v1) return)))
|
||||
(LetCont ((k1 ()
|
||||
(LetPrim (v2 (Constant (Int 0)))
|
||||
(LetPrim (v3 (Constant (Int 2)))
|
||||
(InvokeContinuation k0 (v2 v3)))))
|
||||
(k2 ()
|
||||
(LetPrim (v4 (Constant (Int 3)))
|
||||
(LetPrim (v5 (Constant (Int 5)))
|
||||
(InvokeContinuation k0 (v4 v5))))))
|
||||
(Branch (IsTrue x) k1 k2))))
|
||||
""";
|
||||
|
||||
// Create an eta-cont redex:
|
||||
// Dead parameter reductions can create an eta-cont redex by removing unused
|
||||
// continuation parameters and thus creating the eta redex.
|
||||
String CREATE_ETA_CONT_IN = """
|
||||
(FunctionDefinition main () (x) return
|
||||
(LetCont ((rec loop (v0)
|
||||
(InvokeContinuation rec loop (v0))))
|
||||
(LetCont ((created (v1 v2 v3)
|
||||
(InvokeContinuation loop (v2))))
|
||||
(LetCont ((then ()
|
||||
(LetPrim (v4 (Constant (Int 0)))
|
||||
(LetPrim (v5 (Constant (Int 1)))
|
||||
(LetPrim (v6 (Constant (Int 2)))
|
||||
(InvokeContinuation created (v4 v5 v6))))))
|
||||
(else ()
|
||||
(LetPrim (v6 (Constant (Int 3)))
|
||||
(LetPrim (v7 (Constant (Int 4)))
|
||||
(LetPrim (v8 (Constant (Int 5)))
|
||||
(InvokeContinuation created (v6 v7 v8)))))))
|
||||
(Branch (IsTrue x) then else)))))
|
||||
""";
|
||||
String CREATE_ETA_CONT_OUT = """
|
||||
(FunctionDefinition main () (x) return
|
||||
(LetCont ((rec k0 (v0)
|
||||
(InvokeContinuation rec k0 (v0))))
|
||||
(LetCont ((k1 ()
|
||||
(LetPrim (v1 (Constant (Int 1)))
|
||||
(InvokeContinuation k0 (v1))))
|
||||
(k2 ()
|
||||
(LetPrim (v2 (Constant (Int 4)))
|
||||
(InvokeContinuation k0 (v2)))))
|
||||
(Branch (IsTrue x) k1 k2))))
|
||||
""";
|
||||
|
||||
|
||||
|
||||
// Beta-fun-lin and eta-fun might not apply to us, since
|
||||
// a. in (InvokeMethod v0 call k0), v0 might carry state, and
|
||||
// b. there is no way to generate static nested functions that we could
|
||||
// use InvokeStatic on.
|
||||
|
||||
/// Normalizes whitespace by replacing all whitespace sequences by a single
|
||||
/// space and trimming leading and trailing whitespace.
|
||||
String normalizeSExpr(String input) {
|
||||
return input.replaceAll(new RegExp(r'[ \n\t]+'), ' ').trim();
|
||||
}
|
||||
|
||||
/// Parses the given input IR, runs an optimization pass over it, and compares
|
||||
/// the stringification of the result against the expected output.
|
||||
void testShrinkingReducer(String input, String expectedOutput) {
|
||||
final unstringifier = new SExpressionUnstringifier();
|
||||
final stringifier = new SExpressionStringifier();
|
||||
final optimizer = new ShrinkingReducer();
|
||||
|
||||
FunctionDefinition f = unstringifier.unstringify(input);
|
||||
optimizer.rewrite(f);
|
||||
|
||||
String expected = normalizeSExpr(expectedOutput);
|
||||
String actual = normalizeSExpr(stringifier.visit(f));
|
||||
|
||||
Expect.equals(expected, actual);
|
||||
}
|
||||
|
||||
void main() {
|
||||
testShrinkingReducer(DEAD_VAL_IN, DEAD_VAL_OUT);
|
||||
testShrinkingReducer(ITERATIVE_DEAD_VAL1_IN, ITERATIVE_DEAD_VAL1_OUT);
|
||||
testShrinkingReducer(ITERATIVE_DEAD_VAL2_IN, ITERATIVE_DEAD_VAL2_OUT);
|
||||
testShrinkingReducer(DEAD_CONT_IN, DEAD_CONT_OUT);
|
||||
testShrinkingReducer(ITERATIVE_DEAD_CONT_IN, ITERATIVE_DEAD_CONT_OUT);
|
||||
testShrinkingReducer(BETA_CONT_LIN_IN, BETA_CONT_LIN_OUT);
|
||||
testShrinkingReducer(RECURSIVE_BETA_CONT_LIN_IN, RECURSIVE_BETA_CONT_LIN_OUT);
|
||||
testShrinkingReducer(USED_BETA_CONT_LIN_IN, USED_BETA_CONT_LIN_OUT);
|
||||
testShrinkingReducer(ETA_CONT_IN, ETA_CONT_OUT);
|
||||
testShrinkingReducer(DEAD_PARAMETER_IN, DEAD_PARAMETER_OUT);
|
||||
testShrinkingReducer(CREATE_ETA_CONT_IN, CREATE_ETA_CONT_OUT);
|
||||
}
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
/// Unittest test of the CPS ir generated by the dart2dart compiler.
|
||||
library dart_backend.sexpr2_test;
|
||||
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
|
||||
import 'package:compiler/src/elements/elements.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import '../../../../pkg/analyzer2dart/test/test_helper.dart';
|
||||
import '../../../../pkg/analyzer2dart/test/sexpr_data.dart';
|
||||
|
||||
import 'test_helper.dart';
|
||||
|
||||
main(List<String> args) {
|
||||
performTests(TEST_DATA, asyncTester, runTest, args);
|
||||
}
|
||||
|
||||
runTest(TestSpec result) {
|
||||
return compilerFor(result.input).then((Compiler compiler) {
|
||||
void checkOutput(String elementName,
|
||||
Element element,
|
||||
String expectedOutput) {
|
||||
FunctionDefinition ir = compiler.irBuilder.getIr(element);
|
||||
if (expectedOutput == null) {
|
||||
Expect.isNull(ir, "\nInput:\n${result.input}\n"
|
||||
"No CPS IR expected for $element");
|
||||
} else {
|
||||
Expect.isNotNull(ir, "\nInput:\n${result.input}\n"
|
||||
"No CPS IR for $element");
|
||||
expectedOutput = expectedOutput.trim();
|
||||
String output = ir.accept(new SExpressionStringifier()).trim();
|
||||
Expect.equals(expectedOutput, output,
|
||||
"\nInput:\n${result.input}\n"
|
||||
"Expected for '$elementName':\n$expectedOutput\n"
|
||||
"Actual for '$elementName':\n$output\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (result.output is String) {
|
||||
checkOutput('main', compiler.mainFunction, result.output);
|
||||
} else {
|
||||
assert(result.output is Map<String, String>);
|
||||
result.output.forEach((String elementName, String output) {
|
||||
Element element;
|
||||
if (elementName.contains('.')) {
|
||||
ClassElement cls = compiler.mainApp.localLookup(
|
||||
elementName.substring(0, elementName.indexOf('.')));
|
||||
element = cls.localLookup(
|
||||
elementName.substring(elementName.indexOf('.') + 1));
|
||||
} else {
|
||||
element = compiler.mainApp.localLookup(elementName);
|
||||
}
|
||||
checkOutput(elementName, element, output);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,177 +0,0 @@
|
|||
// Copyright (c) 2014, 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 dart_backend.sexpr_test;
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes_sexpr.dart';
|
||||
import 'package:expect/expect.dart';
|
||||
|
||||
import '../compiler_helper.dart' hide compilerFor;
|
||||
import 'sexpr_unstringifier.dart';
|
||||
import 'test_helper.dart';
|
||||
|
||||
const String CODE = """
|
||||
class Foo {
|
||||
static int x = 1;
|
||||
foo() => 42;
|
||||
}
|
||||
|
||||
class Bar extends Foo {
|
||||
int y = 2;
|
||||
Bar() {
|
||||
Foo.x = 2;
|
||||
}
|
||||
foo() => this.y + super.foo();
|
||||
}
|
||||
|
||||
class FooBar<T> {
|
||||
bool foobar() => T is int;
|
||||
}
|
||||
|
||||
main() {
|
||||
Foo foo;
|
||||
if (foo is Foo) {
|
||||
print("Surprise");
|
||||
}
|
||||
|
||||
String inner() => "Inner function";
|
||||
print(inner());
|
||||
|
||||
int j = 42;
|
||||
for (int i = 0; i < 2; i++) {
|
||||
print(i.toString());
|
||||
}
|
||||
|
||||
print(Foo.x);
|
||||
|
||||
String recursive(int h) {
|
||||
if (h < 1) {
|
||||
return j.toString();
|
||||
}
|
||||
j++;
|
||||
return "h\$\{recursive(h - 1)\}";
|
||||
}
|
||||
print(recursive(5));
|
||||
|
||||
Bar bar = new Bar();
|
||||
print(bar.foo().toString());
|
||||
|
||||
bar = foo as Bar;
|
||||
if (bar == null) {
|
||||
var list = [1, 2, 3, 4];
|
||||
var map = { 1: "one"
|
||||
, 2: "two"
|
||||
, 3: "three"
|
||||
};
|
||||
var double = 3.14;
|
||||
list.forEach((i) => print(i.toString()));
|
||||
print("\$list \$map \$double");
|
||||
}
|
||||
|
||||
print(new FooBar<int>().foobar());
|
||||
}
|
||||
|
||||
""";
|
||||
|
||||
bool shouldOutput(Element element) {
|
||||
return (!element.library.isPlatformLibrary &&
|
||||
!element.isSynthesized &&
|
||||
element.kind == ElementKind.FUNCTION);
|
||||
}
|
||||
|
||||
/// Compiles the given dart code (which must include a 'main' function) and
|
||||
/// returns a list of all generated CPS IR definitions.
|
||||
Future<List<FunctionDefinition>> compile(String code) {
|
||||
return compilerFor(code).then((Compiler compiler) {
|
||||
return compiler.enqueuer.resolution.resolvedElements
|
||||
.where(shouldOutput)
|
||||
.map(compiler.irBuilder.getIr)
|
||||
.toList();
|
||||
});
|
||||
}
|
||||
|
||||
/// Returns an S-expression string for each compiled function.
|
||||
List<String> stringifyAll(Iterable<FunctionDefinition> functions) {
|
||||
final stringifier = new SExpressionStringifier();
|
||||
return functions.map((f) => stringifier.visitFunctionDefinition(f)).toList();
|
||||
}
|
||||
|
||||
Future<List<String>> testStringifier(String code,
|
||||
Iterable<String> expectedTokens) {
|
||||
return compile(code)
|
||||
.then((List<FunctionDefinition> functions) {
|
||||
List<String> sexprs = stringifyAll(functions);
|
||||
String combined = sexprs.join();
|
||||
String withoutNullConstants = combined.replaceAll("Constant null", "");
|
||||
Expect.isFalse(withoutNullConstants.contains("null"));
|
||||
for (String token in expectedTokens) {
|
||||
Expect.isTrue(combined.contains(token),
|
||||
"'$combined' doesn't contain '$token' in test:\n$code");
|
||||
}
|
||||
|
||||
return sexprs;
|
||||
});
|
||||
}
|
||||
|
||||
/// Checks if the generated S-expressions can be processed by the unstringifier,
|
||||
/// returns the resulting definitions.
|
||||
List<FunctionDefinition> testUnstringifier(List<String> sexprs) {
|
||||
return sexprs.map((String sexpr) {
|
||||
try {
|
||||
final function = new SExpressionUnstringifier().unstringify(sexpr);
|
||||
Expect.isNotNull(function, "Unstringification failed:\n\n$sexpr");
|
||||
return function;
|
||||
} catch (e, s) {
|
||||
print('$e\n$s');
|
||||
Expect.fail('Error unstringifying "$sexpr": $e');
|
||||
}
|
||||
}).toList();
|
||||
}
|
||||
|
||||
void main() {
|
||||
final tokens =
|
||||
[ "FunctionDefinition"
|
||||
, "IsTrue"
|
||||
|
||||
// Expressions
|
||||
, "Branch"
|
||||
, "ConcatenateStrings"
|
||||
, "DeclareFunction"
|
||||
, "InvokeConstructor"
|
||||
, "InvokeContinuation"
|
||||
, "InvokeMethod"
|
||||
, "InvokeStatic"
|
||||
, "InvokeMethodDirectly"
|
||||
, "LetCont"
|
||||
, "LetPrim"
|
||||
, "SetMutableVariable"
|
||||
, "TypeOperator"
|
||||
|
||||
// Primitives
|
||||
, "Constant"
|
||||
, "CreateFunction"
|
||||
, "GetMutableVariable"
|
||||
, "LiteralList"
|
||||
, "LiteralMap"
|
||||
// Parameters are encoded by name only and hence are not in this list.
|
||||
, "ReifyTypeVar"
|
||||
|
||||
, "(this)" // 'this' Parameter declarations
|
||||
, "this" // 'this' Parameter uses
|
||||
|
||||
];
|
||||
|
||||
asyncTest(() => testStringifier(CODE, tokens).then((List<String> sexprs) {
|
||||
final functions = testUnstringifier(sexprs);
|
||||
|
||||
// Ensure that
|
||||
// stringified(CODE) == stringified(unstringified(stringified(CODE)))
|
||||
Expect.listEquals(sexprs, stringifyAll(functions));
|
||||
}));
|
||||
}
|
|
@ -1,885 +0,0 @@
|
|||
// Copyright (c) 2014, 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.
|
||||
|
||||
// SExpressionUnstringifier implements the inverse operation to
|
||||
// [SExpressionStringifier].
|
||||
|
||||
library sexpr_unstringifier;
|
||||
|
||||
import 'package:compiler/src/constants/expressions.dart';
|
||||
import 'package:compiler/src/constants/values.dart';
|
||||
import 'package:compiler/src/dart_types.dart' as dart_types
|
||||
show DartType;
|
||||
import 'package:compiler/src/diagnostics/messages.dart'
|
||||
show MessageKind;
|
||||
import 'package:compiler/src/elements/elements.dart';
|
||||
import 'package:compiler/src/elements/modelx.dart'
|
||||
show ErroneousElementX, TypeVariableElementX;
|
||||
import 'package:compiler/src/tree/tree.dart' show LiteralDartString;
|
||||
import 'package:compiler/src/universe/call_structure.dart'
|
||||
show CallStructure;
|
||||
import 'package:compiler/src/universe/selector.dart'
|
||||
show Selector, SelectorKind;
|
||||
import 'package:compiler/src/cps_ir/cps_ir_nodes.dart';
|
||||
|
||||
/// Used whenever a node constructed by [SExpressionUnstringifier] needs a
|
||||
/// named entity.
|
||||
class DummyEntity extends Entity {
|
||||
final String name;
|
||||
DummyEntity(this.name);
|
||||
}
|
||||
|
||||
/// Used whenever a node constructed by [SExpressionUnstringifier] needs a
|
||||
/// local.
|
||||
class DummyLocal extends DummyEntity implements Local {
|
||||
DummyLocal(String name) : super(name);
|
||||
|
||||
ExecutableElement get executableContext => null;
|
||||
}
|
||||
|
||||
// TODO(karlklose): we should remove all references to [ErroneousElement] from
|
||||
// the CPS IR. Instead, the builder must construct appropriate terms for ASTs
|
||||
// that could not be resolved correctly. Perhaps the IR should not rely on
|
||||
// elements at all for naming.
|
||||
/// Used whenever a node constructed by [SExpressionUnstringifier] requires
|
||||
/// an [Element] or [FunctionElement]. Extends [ErroneousElementX] since there
|
||||
/// is currently a large amount of overhead when extending the base abstract
|
||||
/// classes, and erroneous elements conveniently also skip several assertion
|
||||
/// checks in CPS IR nodes that are irrelevant to us.
|
||||
class DummyElement extends ErroneousElementX
|
||||
implements TypeVariableElement, FieldElement {
|
||||
DummyElement(String name)
|
||||
: super(MessageKind.GENERIC, {}, name, null);
|
||||
|
||||
final dart_types.DartType bound = null;
|
||||
final TypeDeclarationElement typeDeclaration = null;
|
||||
|
||||
noSuchMethod(inv) => super.noSuchMethod(inv);
|
||||
}
|
||||
|
||||
/// Used whenever a node constructed by [SExpressionUnstringifier] requires
|
||||
/// a named type.
|
||||
class DummyNamedType extends dart_types.DartType {
|
||||
final String name;
|
||||
|
||||
final kind = null;
|
||||
final element = null;
|
||||
|
||||
DummyNamedType(this.name);
|
||||
|
||||
subst(arguments, parameters) => null;
|
||||
unalias(compiler) => null;
|
||||
accept(visitor, argument) => null;
|
||||
|
||||
String toString() => name;
|
||||
}
|
||||
|
||||
/// Represents a list of tokens, but is basically a partial view into a list
|
||||
/// with appropriate convenience methods.
|
||||
class Tokens {
|
||||
final List<String> _list;
|
||||
int _index; // Current index into the list.
|
||||
|
||||
Tokens(List<String> this._list) : _index = 0;
|
||||
|
||||
String get current => _list[_index];
|
||||
String get next => _list[_index + 1];
|
||||
|
||||
String read([String expected]) {
|
||||
if (expected != null) {
|
||||
if (current != expected) {
|
||||
print('expected "$expected", found "$current"');
|
||||
int start = _index - 15;
|
||||
String dotdotdot = '... ';
|
||||
if (start < 0) {
|
||||
start = 0;
|
||||
dotdotdot = '';
|
||||
}
|
||||
print('${dotdotdot}${_list.sublist(start, _index + 1).join(' ')}');
|
||||
assert(current == expected);
|
||||
}
|
||||
}
|
||||
return _list[_index++];
|
||||
}
|
||||
|
||||
/// Consumes the preamble to a new node, consisting of an opening parenthesis
|
||||
/// and a tag.
|
||||
void consumeStart([String tag]) {
|
||||
read("(");
|
||||
if (tag != null) {
|
||||
read(tag);
|
||||
}
|
||||
}
|
||||
|
||||
void consumeEnd() {
|
||||
read(")");
|
||||
}
|
||||
|
||||
bool get hasNext => _index < _list.length;
|
||||
String toString() => _list.sublist(_index).toString();
|
||||
}
|
||||
|
||||
/// Constructs a minimal in-memory representation of the IR represented
|
||||
/// by the given string. Many fields are currently simply set to null.
|
||||
class SExpressionUnstringifier {
|
||||
|
||||
// Expressions
|
||||
static const String BRANCH = "Branch";
|
||||
static const String CONCATENATE_STRINGS = "ConcatenateStrings";
|
||||
static const String DECLARE_FUNCTION = "DeclareFunction";
|
||||
static const String INVOKE_CONSTRUCTOR = "InvokeConstructor";
|
||||
static const String INVOKE_CONTINUATION = "InvokeContinuation";
|
||||
static const String INVOKE_STATIC = "InvokeStatic";
|
||||
static const String INVOKE_METHOD_DIRECTLY = "InvokeMethodDirectly";
|
||||
static const String INVOKE_METHOD = "InvokeMethod";
|
||||
static const String LET_PRIM = "LetPrim";
|
||||
static const String LET_CONT = "LetCont";
|
||||
static const String LET_MUTABLE = "LetMutable";
|
||||
static const String TYPE_CAST = "TypeCast";
|
||||
static const String GET_LAZY_STATIC = "GetLazyStatic";
|
||||
static const String UNREACHABLE = "Unreachable";
|
||||
|
||||
// Primitives
|
||||
static const String CONSTANT = "Constant";
|
||||
static const String CREATE_FUNCTION = "CreateFunction";
|
||||
static const String GET_MUTABLE = "GetMutable";
|
||||
static const String SET_MUTABLE = "SetMutable";
|
||||
static const String LITERAL_LIST = "LiteralList";
|
||||
static const String LITERAL_MAP = "LiteralMap";
|
||||
static const String REIFY_TYPE_VAR = "ReifyTypeVar";
|
||||
static const String GET_STATIC = "GetStatic";
|
||||
static const String SET_STATIC = "SetStatic";
|
||||
static const String TYPE_TEST = "TypeTest";
|
||||
static const String APPLY_BUILTIN_OPERATOR = "ApplyBuiltinOperator";
|
||||
static const String GET_LENGTH = "GetLength";
|
||||
static const String GET_INDEX = "GetIndex";
|
||||
static const String SET_INDEX = "SetIndex";
|
||||
static const String GET_FIELD = "GetField";
|
||||
static const String SET_FIELD = "SetField";
|
||||
|
||||
// Other
|
||||
static const String FUNCTION_DEFINITION = "FunctionDefinition";
|
||||
static const String IS_TRUE = "IsTrue";
|
||||
|
||||
// Constants
|
||||
static const String BOOL = "Bool";
|
||||
static const String DOUBLE = "Double";
|
||||
static const String INT = "Int";
|
||||
static const String NULL = "Null";
|
||||
static const String STRING = "String";
|
||||
|
||||
final Map<String, Definition> name2variable =
|
||||
<String, Definition>{ "return": new Continuation.retrn() };
|
||||
|
||||
// Operator names used for canonicalization. In theory, we could simply use
|
||||
// Elements.isOperatorName() on the parsed tokens; however, comparisons are
|
||||
// done using identical() for performance reasons, which are reliable only for
|
||||
// compile-time literal strings.
|
||||
static Set<String> OPERATORS = new Set<String>.from(
|
||||
[ '~', '==', '[]', '*', '/', '%', '~/', '+', '<<', 'unary-'
|
||||
, '>>', '>=', '>', '<=', '<', '&', '^', '|', '[]=', '-'
|
||||
]);
|
||||
|
||||
// The tokens currently being parsed.
|
||||
Tokens tokens;
|
||||
|
||||
FunctionDefinition unstringify(String s) {
|
||||
tokens = tokenize(s);
|
||||
FunctionDefinition def = parseFunctionDefinition();
|
||||
assert(!tokens.hasNext);
|
||||
return def;
|
||||
}
|
||||
|
||||
/// Returns a new named dummy selector with a roughly appropriate kind.
|
||||
Selector dummySelector(String name, int argumentCount) {
|
||||
SelectorKind kind;
|
||||
if (name == "[]") {
|
||||
kind = SelectorKind.INDEX;
|
||||
} else if (Elements.isOperatorName(name)) {
|
||||
kind = SelectorKind.OPERATOR;
|
||||
} else {
|
||||
kind = SelectorKind.CALL;
|
||||
}
|
||||
return new Selector(kind, new PublicName(name),
|
||||
new CallStructure.unnamed(argumentCount));
|
||||
}
|
||||
|
||||
/// Returns the tokens in s. Note that string literals are not necessarily
|
||||
/// preserved; for instance, "(literalString)" is transformed to
|
||||
/// " ( literalString ) ".
|
||||
Tokens tokenize(String s) =>
|
||||
new Tokens(
|
||||
s.replaceAll("(", " ( ")
|
||||
.replaceAll(")", " ) ")
|
||||
.replaceAll("{", " { ")
|
||||
.replaceAll("}", " } ")
|
||||
.replaceAll(new RegExp(r"[ \t\n]+"), " ")
|
||||
.trim()
|
||||
.split(" ")
|
||||
.map(canonicalizeOperators)
|
||||
.toList());
|
||||
|
||||
/// Canonicalizes strings containing operator names.
|
||||
String canonicalizeOperators(String token) {
|
||||
String opname = OPERATORS.lookup(token);
|
||||
if (opname != null) {
|
||||
return opname;
|
||||
}
|
||||
return token;
|
||||
}
|
||||
|
||||
Expression parseExpression() {
|
||||
assert(tokens.current == "(");
|
||||
|
||||
switch (tokens.next) {
|
||||
case BRANCH:
|
||||
return parseBranch();
|
||||
case CONCATENATE_STRINGS:
|
||||
return parseConcatenateStrings();
|
||||
case DECLARE_FUNCTION:
|
||||
return parseDeclareFunction();
|
||||
case INVOKE_CONSTRUCTOR:
|
||||
return parseInvokeConstructor();
|
||||
case INVOKE_CONTINUATION:
|
||||
return parseInvokeContinuation();
|
||||
case INVOKE_METHOD:
|
||||
return parseInvokeMethod();
|
||||
case INVOKE_STATIC:
|
||||
return parseInvokeStatic();
|
||||
case INVOKE_METHOD_DIRECTLY:
|
||||
return parseInvokeMethodDirectly();
|
||||
case LET_PRIM:
|
||||
return parseLetPrim();
|
||||
case LET_CONT:
|
||||
return parseLetCont();
|
||||
case LET_MUTABLE:
|
||||
return parseLetMutable();
|
||||
case TYPE_CAST:
|
||||
return parseTypeCast();
|
||||
case GET_LAZY_STATIC:
|
||||
return parseGetLazyStatic();
|
||||
case UNREACHABLE:
|
||||
return parseUnreachable();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// (prim1 prim2 ... primn)
|
||||
List<Primitive> parsePrimitiveList() {
|
||||
tokens.consumeStart();
|
||||
List<Primitive> prims = <Primitive>[];
|
||||
while (tokens.current != ")") {
|
||||
Primitive prim = name2variable[tokens.read()];
|
||||
assert(prim != null);
|
||||
prims.add(prim);
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
return prims;
|
||||
}
|
||||
|
||||
/// (FunctionDefinition name (parameters) continuation body)
|
||||
FunctionDefinition parseFunctionDefinition() {
|
||||
tokens.consumeStart(FUNCTION_DEFINITION);
|
||||
|
||||
// name
|
||||
Element element = new DummyElement("");
|
||||
if (tokens.current != '(') {
|
||||
// This is a named function.
|
||||
element = new DummyElement(tokens.read());
|
||||
}
|
||||
|
||||
// (this) or ()
|
||||
Definition thisParameter = null;
|
||||
tokens.consumeStart();
|
||||
if (tokens.current != ')') {
|
||||
String thisName = tokens.read();
|
||||
if (name2variable.containsKey(thisName)) {
|
||||
thisParameter = name2variable[thisName];
|
||||
} else {
|
||||
thisParameter = new Parameter(new DummyElement(thisName));
|
||||
name2variable[thisName] = thisParameter;
|
||||
}
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
|
||||
// (parameters)
|
||||
List<Definition> parameters = <Definition>[];
|
||||
tokens.consumeStart();
|
||||
while (tokens.current != ")") {
|
||||
String paramName = tokens.read();
|
||||
if (name2variable.containsKey(paramName)) {
|
||||
parameters.add(name2variable[paramName]);
|
||||
} else {
|
||||
Parameter param = new Parameter(new DummyElement(paramName));
|
||||
name2variable[paramName] = param;
|
||||
parameters.add(param);
|
||||
}
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
|
||||
// continuation
|
||||
String contName = tokens.read("return");
|
||||
Continuation cont = name2variable[contName];
|
||||
assert(cont != null);
|
||||
|
||||
// body
|
||||
Expression body = parseExpression();
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new FunctionDefinition(element, thisParameter, parameters,
|
||||
new Body(body, cont), null, null);
|
||||
}
|
||||
|
||||
/// (IsTrue arg)
|
||||
Condition parseCondition() {
|
||||
// Handles IsTrue only for now.
|
||||
tokens.consumeStart(IS_TRUE);
|
||||
|
||||
Definition value = name2variable[tokens.read()];
|
||||
assert(value != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new IsTrue(value);
|
||||
}
|
||||
|
||||
/// (Branch condition cont cont)
|
||||
Branch parseBranch() {
|
||||
tokens.consumeStart(BRANCH);
|
||||
|
||||
Condition cond = parseCondition();
|
||||
Continuation trueCont = name2variable[tokens.read()];
|
||||
Continuation falseCont = name2variable[tokens.read()];
|
||||
assert(trueCont != null && falseCont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new Branch(cond, trueCont, falseCont);
|
||||
}
|
||||
|
||||
/// (ConcatenateStrings (args) cont)
|
||||
ConcatenateStrings parseConcatenateStrings() {
|
||||
tokens.consumeStart(CONCATENATE_STRINGS);
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new ConcatenateStrings(args, cont);
|
||||
}
|
||||
|
||||
/// (DeclareFunction name = function in body)
|
||||
DeclareFunction parseDeclareFunction() {
|
||||
tokens.consumeStart(DECLARE_FUNCTION);
|
||||
|
||||
// name =
|
||||
MutableVariable local = addMutableVariable(tokens.read());
|
||||
tokens.read("=");
|
||||
|
||||
// function in
|
||||
FunctionDefinition def = parseFunctionDefinition();
|
||||
tokens.read("in");
|
||||
|
||||
// body
|
||||
Expression body = parseExpression();
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new DeclareFunction(local, def)..plug(body);
|
||||
}
|
||||
|
||||
/// (InvokeConstructor name (args) cont)
|
||||
InvokeConstructor parseInvokeConstructor() {
|
||||
tokens.consumeStart(INVOKE_CONSTRUCTOR);
|
||||
|
||||
String constructorName = tokens.read();
|
||||
List<String> split = constructorName.split(".");
|
||||
assert(split.length < 3);
|
||||
|
||||
dart_types.DartType type = new DummyNamedType(split[0]);
|
||||
Element element = new DummyElement((split.length == 1) ? "" : split[1]);
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
Selector selector = dummySelector(constructorName, args.length);
|
||||
return new InvokeConstructor(type, element, selector, args, cont);
|
||||
}
|
||||
|
||||
/// (InvokeContinuation rec? name (args))
|
||||
InvokeContinuation parseInvokeContinuation() {
|
||||
tokens.consumeStart(INVOKE_CONTINUATION);
|
||||
String name = tokens.read();
|
||||
bool isRecursive = name == "rec";
|
||||
if (isRecursive) name = tokens.read();
|
||||
|
||||
Continuation cont = name2variable[name];
|
||||
assert(cont != null);
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new InvokeContinuation(cont, args, isRecursive: isRecursive);
|
||||
}
|
||||
|
||||
/// (InvokeMethod receiver method (args) cont)
|
||||
InvokeMethod parseInvokeMethod() {
|
||||
tokens.consumeStart(INVOKE_METHOD);
|
||||
|
||||
Definition receiver = name2variable[tokens.read()];
|
||||
assert(receiver != null);
|
||||
|
||||
String methodName = tokens.read();
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
Selector selector = dummySelector(methodName, args.length);
|
||||
return new InvokeMethod(receiver, selector, args, cont);
|
||||
}
|
||||
|
||||
/// (InvokeStatic method (args) cont)
|
||||
InvokeStatic parseInvokeStatic() {
|
||||
tokens.consumeStart(INVOKE_STATIC);
|
||||
|
||||
String methodName = tokens.read();
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
Entity entity = new DummyEntity(methodName);
|
||||
Selector selector = dummySelector(methodName, args.length);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new InvokeStatic(entity, selector, args, cont, null);
|
||||
}
|
||||
|
||||
/// (InvokeMethodDirectly receiver method (args) cont)
|
||||
InvokeMethodDirectly parseInvokeMethodDirectly() {
|
||||
tokens.consumeStart(INVOKE_METHOD_DIRECTLY);
|
||||
|
||||
Definition receiver = name2variable[tokens.read()];
|
||||
assert(receiver != null);
|
||||
|
||||
String methodName = tokens.read();
|
||||
|
||||
List<Primitive> args = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
Element element = new DummyElement(methodName);
|
||||
Selector selector = dummySelector(methodName, args.length);
|
||||
return new InvokeMethodDirectly(receiver, element, selector, args, cont);
|
||||
}
|
||||
|
||||
// (rec? name (args) body)
|
||||
Continuation parseContinuation() {
|
||||
// (rec? name
|
||||
tokens.consumeStart();
|
||||
String name = tokens.read();
|
||||
bool isRecursive = name == "rec";
|
||||
if (isRecursive) name = tokens.read();
|
||||
|
||||
// (args)
|
||||
tokens.consumeStart();
|
||||
List<Parameter> params = <Parameter>[];
|
||||
while (tokens.current != ")") {
|
||||
String paramName = tokens.read();
|
||||
Parameter param = new Parameter(new DummyElement(paramName));
|
||||
name2variable[paramName] = param;
|
||||
params.add(param);
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
|
||||
Continuation cont = new Continuation(params);
|
||||
name2variable[name] = cont;
|
||||
|
||||
cont.isRecursive = isRecursive;
|
||||
// cont_body
|
||||
cont.body = parseExpression();
|
||||
tokens.consumeEnd();
|
||||
return cont;
|
||||
}
|
||||
|
||||
/// (LetCont (continuations) body)
|
||||
LetCont parseLetCont() {
|
||||
tokens.consumeStart(LET_CONT);
|
||||
tokens.consumeStart();
|
||||
List<Continuation> continuations = <Continuation>[];
|
||||
while (tokens.current != ")") {
|
||||
continuations.add(parseContinuation());
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
|
||||
// body)
|
||||
Expression body = parseExpression();
|
||||
tokens.consumeEnd();
|
||||
|
||||
return new LetCont.many(continuations, body);
|
||||
}
|
||||
|
||||
/// (LetMutable (name value) body)
|
||||
LetMutable parseLetMutable() {
|
||||
tokens.consumeStart(LET_MUTABLE);
|
||||
|
||||
tokens.consumeStart();
|
||||
String name = tokens.read();
|
||||
MutableVariable local = addMutableVariable(name);
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
tokens.consumeEnd();
|
||||
|
||||
Expression body = parseExpression();
|
||||
tokens.consumeEnd();
|
||||
return new LetMutable(local, value)..plug(body);
|
||||
}
|
||||
|
||||
/// (SetMutable name value)
|
||||
SetMutable parseSetMutable() {
|
||||
tokens.consumeStart(SET_MUTABLE);
|
||||
|
||||
MutableVariable local = name2variable[tokens.read()];
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
assert(value != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new SetMutable(local, value);
|
||||
}
|
||||
|
||||
/// (TypeCast value type args cont)
|
||||
TypeCast parseTypeCast() {
|
||||
tokens.consumeStart(TYPE_CAST);
|
||||
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
assert(value != null);
|
||||
|
||||
dart_types.DartType type = new DummyNamedType(tokens.read());
|
||||
|
||||
List<ir.Primitive> typeArguments = parsePrimitiveList();
|
||||
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new TypeCast(value, type, typeArguments, cont);
|
||||
}
|
||||
|
||||
/// (TypeTest value type args)
|
||||
TypeTest parseTypeTest() {
|
||||
tokens.consumeStart(TYPE_TEST);
|
||||
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
assert(value != null);
|
||||
|
||||
dart_types.DartType type = new DummyNamedType(tokens.read());
|
||||
|
||||
List<ir.Primitive> typeArguments = parsePrimitiveList();
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new TypeTest(value, type, typeArguments);
|
||||
}
|
||||
|
||||
/// (ApplyBuiltinOperator operator args)
|
||||
ApplyBuiltinOperator parseApplyBuiltinOperator() {
|
||||
tokens.consumeStart(APPLY_BUILTIN_OPERATOR);
|
||||
|
||||
String operatorName = tokens.read();
|
||||
BuiltinOperator operator;
|
||||
for (BuiltinOperator op in BuiltinOperator.values) {
|
||||
if (op.toString() == operatorName) {
|
||||
operator = op;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(operator != null);
|
||||
List<ir.Primitive> arguments = parsePrimitiveList();
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new ApplyBuiltinOperator(operator, arguments);
|
||||
}
|
||||
|
||||
/// (GetLength object)
|
||||
GetLength parseGetLength() {
|
||||
tokens.consumeStart(GET_LENGTH);
|
||||
Primitive object = name2variable[tokens.read()];
|
||||
tokens.consumeEnd();
|
||||
return new GetLength(object);
|
||||
}
|
||||
|
||||
/// (GetIndex object index)
|
||||
GetIndex parseGetIndex() {
|
||||
tokens.consumeStart(GET_INDEX);
|
||||
Primitive object = name2variable[tokens.read()];
|
||||
Primitive index = name2variable[tokens.read()];
|
||||
tokens.consumeEnd();
|
||||
return new GetIndex(object, index);
|
||||
}
|
||||
|
||||
/// (SetIndex object index value)
|
||||
SetIndex parseSetIndex() {
|
||||
tokens.consumeStart(SET_INDEX);
|
||||
Primitive object = name2variable[tokens.read()];
|
||||
Primitive index = name2variable[tokens.read()];
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
tokens.consumeEnd();
|
||||
return new SetIndex(object, index, value);
|
||||
}
|
||||
|
||||
/// (SetStatic field value)
|
||||
SetStatic parseSetStatic() {
|
||||
tokens.consumeStart(SET_STATIC);
|
||||
|
||||
Element fieldElement = new DummyElement(tokens.read());
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
assert(value != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new SetStatic(fieldElement, value, null);
|
||||
}
|
||||
|
||||
/// (GetLazyStatic field cont)
|
||||
GetLazyStatic parseGetLazyStatic() {
|
||||
tokens.consumeStart(GET_LAZY_STATIC);
|
||||
|
||||
Element fieldElement = new DummyElement(tokens.read());
|
||||
Continuation cont = name2variable[tokens.read()];
|
||||
assert(cont != null);
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new GetLazyStatic(fieldElement, cont, null);
|
||||
}
|
||||
|
||||
/// (Unreachable)
|
||||
Unreachable parseUnreachable() {
|
||||
tokens.consumeStart(UNREACHABLE);
|
||||
tokens.consumeEnd();
|
||||
return new Unreachable();
|
||||
}
|
||||
|
||||
/// (LetPrim (name primitive) body)
|
||||
LetPrim parseLetPrim() {
|
||||
tokens.consumeStart(LET_PRIM);
|
||||
|
||||
// (name
|
||||
tokens.consumeStart();
|
||||
String name = tokens.read();
|
||||
|
||||
// primitive)
|
||||
Primitive primitive = parsePrimitive();
|
||||
name2variable[name] = primitive;
|
||||
tokens.consumeEnd();
|
||||
|
||||
// body)
|
||||
Expression body = parseExpression();
|
||||
tokens.consumeEnd();
|
||||
|
||||
return new LetPrim(primitive)..plug(body);
|
||||
}
|
||||
|
||||
Primitive parsePrimitive() {
|
||||
assert(tokens.current == "(");
|
||||
|
||||
switch (tokens.next) {
|
||||
case CONSTANT:
|
||||
return parseConstant();
|
||||
case CREATE_FUNCTION:
|
||||
return parseCreateFunction();
|
||||
case GET_MUTABLE:
|
||||
return parseGetMutable();
|
||||
case SET_MUTABLE:
|
||||
return parseSetMutable();
|
||||
case LITERAL_LIST:
|
||||
return parseLiteralList();
|
||||
case LITERAL_MAP:
|
||||
return parseLiteralMap();
|
||||
case REIFY_TYPE_VAR:
|
||||
return parseReifyTypeVar();
|
||||
case GET_STATIC:
|
||||
return parseGetStatic();
|
||||
case SET_STATIC:
|
||||
return parseSetStatic();
|
||||
case TYPE_TEST:
|
||||
return parseTypeTest();
|
||||
case APPLY_BUILTIN_OPERATOR:
|
||||
return parseApplyBuiltinOperator();
|
||||
case GET_LENGTH:
|
||||
return parseGetLength();
|
||||
case GET_INDEX:
|
||||
return parseGetIndex();
|
||||
case SET_INDEX:
|
||||
return parseSetIndex();
|
||||
case GET_FIELD:
|
||||
return parseGetField();
|
||||
case SET_FIELD:
|
||||
return parseSetField();
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// (Constant (constant))
|
||||
Constant parseConstant() {
|
||||
tokens.consumeStart(CONSTANT);
|
||||
tokens.consumeStart();
|
||||
Constant result;
|
||||
String tag = tokens.read();
|
||||
switch (tag) {
|
||||
case NULL:
|
||||
result = new Constant(
|
||||
new NullConstantExpression(new NullConstantValue()));
|
||||
break;
|
||||
case BOOL:
|
||||
String value = tokens.read();
|
||||
if (value == "true") {
|
||||
result = new Constant(
|
||||
new BoolConstantExpression(true, new TrueConstantValue()));
|
||||
} else if (value == "false") {
|
||||
result = new Constant(
|
||||
new BoolConstantExpression(false, new FalseConstantValue()));
|
||||
} else {
|
||||
throw "Invalid Boolean value '$value'.";
|
||||
}
|
||||
break;
|
||||
case STRING:
|
||||
List<String> strings = <String>[];
|
||||
do {
|
||||
strings.add(tokens.read());
|
||||
} while (tokens.current != ")");
|
||||
String string = strings.join(" ");
|
||||
assert(string.startsWith('"') && string.endsWith('"'));
|
||||
String text = string.substring(1, string.length - 1);
|
||||
StringConstantValue value = new StringConstantValue(
|
||||
new LiteralDartString(text));
|
||||
result = new Constant(new StringConstantExpression(text, value));
|
||||
break;
|
||||
case INT:
|
||||
String value = tokens.read();
|
||||
int intValue = int.parse(value, onError: (_) => null);
|
||||
if (intValue == null) {
|
||||
throw "Invalid int value 'value'.";
|
||||
}
|
||||
result = new Constant(new IntConstantExpression(
|
||||
intValue, new IntConstantValue(intValue)));
|
||||
break;
|
||||
case DOUBLE:
|
||||
String value = tokens.read();
|
||||
double doubleValue = double.parse(value, (_) => null);
|
||||
if (doubleValue == null) {
|
||||
throw "Invalid double value '$value'.";
|
||||
}
|
||||
result = new Constant(new DoubleConstantExpression(
|
||||
doubleValue, new DoubleConstantValue(doubleValue)));
|
||||
break;
|
||||
default:
|
||||
throw "Unexpected constant tag '$tag'.";
|
||||
}
|
||||
tokens.consumeEnd();
|
||||
tokens.consumeEnd();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// (CreateFunction (definition))
|
||||
CreateFunction parseCreateFunction() {
|
||||
tokens.consumeStart(CREATE_FUNCTION);
|
||||
FunctionDefinition def = parseFunctionDefinition();
|
||||
tokens.consumeEnd();
|
||||
return new CreateFunction(def);
|
||||
}
|
||||
|
||||
MutableVariable addMutableVariable(String name) {
|
||||
assert(!name2variable.containsKey(name));
|
||||
MutableVariable variable = new MutableVariable(new DummyElement(name));
|
||||
name2variable[name] = variable;
|
||||
return variable;
|
||||
}
|
||||
|
||||
/// (GetMutable name)
|
||||
GetMutable parseGetMutable() {
|
||||
tokens.consumeStart(GET_MUTABLE);
|
||||
MutableVariable local = name2variable[tokens.read()];
|
||||
tokens.consumeEnd();
|
||||
|
||||
return new GetMutable(local);
|
||||
}
|
||||
|
||||
/// (LiteralList (values))
|
||||
LiteralList parseLiteralList() {
|
||||
tokens.consumeStart(LITERAL_LIST);
|
||||
List<Primitive> values = parsePrimitiveList();
|
||||
tokens.consumeEnd();
|
||||
return new LiteralList(null, values);
|
||||
}
|
||||
|
||||
/// (LiteralMap (keys) (values))
|
||||
LiteralMap parseLiteralMap() {
|
||||
tokens.consumeStart(LITERAL_MAP);
|
||||
|
||||
List<Primitive> keys = parsePrimitiveList();
|
||||
List<Primitive> values = parsePrimitiveList();
|
||||
|
||||
List<LiteralMapEntry> entries = <LiteralMapEntry>[];
|
||||
for (int i = 0; i < keys.length; i++) {
|
||||
entries.add(new LiteralMapEntry(keys[i], values[i]));
|
||||
}
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new LiteralMap(null, entries);
|
||||
}
|
||||
|
||||
/// (ReifyTypeVar type)
|
||||
ReifyTypeVar parseReifyTypeVar() {
|
||||
tokens.consumeStart(REIFY_TYPE_VAR);
|
||||
|
||||
TypeVariableElement type = new DummyElement(tokens.read());
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new ReifyTypeVar(type);
|
||||
}
|
||||
|
||||
/// (GetStatic field)
|
||||
GetStatic parseGetStatic() {
|
||||
tokens.consumeStart(GET_STATIC);
|
||||
|
||||
Element field = new DummyElement(tokens.read());
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new GetStatic(field, null);
|
||||
}
|
||||
|
||||
/// (GetField object field)
|
||||
GetField parseGetField() {
|
||||
tokens.consumeStart(GET_FIELD);
|
||||
|
||||
Primitive object = name2variable[tokens.read()];
|
||||
Element field = new DummyElement(tokens.read());
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new GetField(object, field);
|
||||
}
|
||||
|
||||
/// (SetField object field value)
|
||||
SetField parseSetField() {
|
||||
tokens.consumeStart(SET_FIELD);
|
||||
|
||||
Primitive object = name2variable[tokens.read()];
|
||||
Element field = new DummyElement(tokens.read());
|
||||
Primitive value = name2variable[tokens.read()];
|
||||
|
||||
tokens.consumeEnd();
|
||||
return new SetField(object, field, value);
|
||||
}
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright (c) 2014, 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 dart_backend.test_helper;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/compiler.dart' as api;
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import '../../../../pkg/analyzer2dart/test/test_helper.dart';
|
||||
import '../compiler_helper.dart';
|
||||
|
||||
/// Compiles the given dart code (which must include a 'main' function) and
|
||||
/// returns the compiler.
|
||||
Future<Compiler> compilerFor(String code,
|
||||
{api.CompilerOutputProvider outputProvider}) {
|
||||
MockCompiler compiler = new MockCompiler.internal(
|
||||
emitJavaScript: false,
|
||||
enableMinification: false,
|
||||
outputProvider: outputProvider);
|
||||
compiler.diagnosticHandler = createHandler(compiler, code);
|
||||
return compiler.init().then((_) {
|
||||
compiler.parseScript(code);
|
||||
|
||||
Element element = compiler.mainApp.find('main');
|
||||
if (element == null) return null;
|
||||
|
||||
compiler.mainFunction = element;
|
||||
compiler.phase = Compiler.PHASE_RESOLVING;
|
||||
compiler.backend.enqueueHelpers(compiler.enqueuer.resolution,
|
||||
compiler.globalDependencies);
|
||||
compiler.processQueue(compiler.enqueuer.resolution, element);
|
||||
compiler.world.populate();
|
||||
compiler.backend.onResolutionComplete();
|
||||
|
||||
compiler.irBuilder.buildNodes();
|
||||
|
||||
return compiler;
|
||||
});
|
||||
}
|
||||
|
||||
/// Test group using async_helper.
|
||||
asyncTester(Group group, RunTest runTest) {
|
||||
asyncTest(() => Future.forEach(group.results, runTest));
|
||||
}
|
|
@ -10,7 +10,6 @@ library dart2js.test.generate_code_with_compile_time_errors;
|
|||
import 'package:expect/expect.dart';
|
||||
import 'package:async_helper/async_helper.dart';
|
||||
import 'package:compiler/src/compiler.dart';
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart';
|
||||
import 'package:compiler/src/js_backend/js_backend.dart';
|
||||
import 'memory_compiler.dart';
|
||||
import 'output_collector.dart';
|
||||
|
@ -53,14 +52,8 @@ test(List<String> options,
|
|||
collector.hints.isNotEmpty,
|
||||
"Unexpected hints: ${collector.warnings}");
|
||||
|
||||
bool isCodeGenerated;
|
||||
if (options.contains('--output-type=dart')) {
|
||||
DartBackend backend = compiler.backend;
|
||||
isCodeGenerated = backend.outputter.libraryInfo != null;
|
||||
} else {
|
||||
JavaScriptBackend backend = compiler.backend;
|
||||
isCodeGenerated = backend.generatedCode.isNotEmpty;
|
||||
}
|
||||
JavaScriptBackend backend = compiler.backend;
|
||||
bool isCodeGenerated = backend.generatedCode.isNotEmpty;
|
||||
Expect.equals(
|
||||
expectedCodeGenerated,
|
||||
isCodeGenerated,
|
||||
|
@ -114,26 +107,5 @@ void main() {
|
|||
expectedCodeGenerated: false,
|
||||
expectedOutput: false,
|
||||
expectHint: true);
|
||||
|
||||
await test(
|
||||
['--output-type=dart'],
|
||||
expectedCodeGenerated: false,
|
||||
expectedOutput: false);
|
||||
await test(
|
||||
['--output-type=dart', '--test-mode'],
|
||||
expectedCodeGenerated: false,
|
||||
expectedOutput: false);
|
||||
await test(
|
||||
['--output-type=dart', '--generate-code-with-compile-time-errors'],
|
||||
expectedCodeGenerated: false,
|
||||
expectedOutput: false,
|
||||
expectHint: true);
|
||||
await test(
|
||||
['--output-type=dart',
|
||||
'--generate-code-with-compile-time-errors',
|
||||
'--test-mode'],
|
||||
expectedCodeGenerated: false,
|
||||
expectedOutput: false,
|
||||
expectHint: true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import 'dart:async';
|
|||
import 'package:compiler/src/commandline_options.dart';
|
||||
import 'package:compiler/src/compiler.dart' show
|
||||
Compiler;
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart' show
|
||||
DartBackend;
|
||||
import 'package:compiler/src/diagnostics/messages.dart' show
|
||||
MessageKind,
|
||||
MessageTemplate;
|
||||
|
@ -71,21 +69,12 @@ Future<Compiler> check(MessageTemplate template, Compiler cachedCompiler) {
|
|||
}
|
||||
DiagnosticCollector collector = new DiagnosticCollector();
|
||||
|
||||
bool oldBackendIsDart;
|
||||
if (cachedCompiler != null) {
|
||||
oldBackendIsDart = cachedCompiler.backend is DartBackend;
|
||||
}
|
||||
bool newBackendIsDart = template.options.contains('--output-type=dart');
|
||||
|
||||
Compiler compiler = compilerFor(
|
||||
memorySourceFiles: example,
|
||||
diagnosticHandler: collector,
|
||||
options: [Flags.analyzeOnly,
|
||||
Flags.enableExperimentalMirrors]..addAll(template.options),
|
||||
cachedCompiler:
|
||||
// TODO(johnniwinther): Remove this restriction when constant
|
||||
// values can be computed directly from the expressions.
|
||||
oldBackendIsDart == newBackendIsDart ? cachedCompiler : null);
|
||||
cachedCompiler: cachedCompiler);
|
||||
|
||||
return compiler.run(Uri.parse('memory:main.dart')).then((_) {
|
||||
Iterable<CollectedMessage> messages = collector.filterMessagesByKinds(
|
||||
|
|
|
@ -1,108 +0,0 @@
|
|||
// 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.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'memory_compiler.dart' show runCompiler, OutputCollector;
|
||||
import 'package:compiler/src/apiimpl.dart' show
|
||||
CompilerImpl;
|
||||
import 'package:compiler/src/tree/tree.dart' show
|
||||
Node;
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart';
|
||||
import 'package:compiler/src/mirror_renamer/mirror_renamer.dart';
|
||||
|
||||
main() {
|
||||
asyncTest(() async {
|
||||
await testWithMirrorHelperLibrary(minify: true);
|
||||
await testWithMirrorHelperLibrary(minify: false);
|
||||
await testWithoutMirrorHelperLibrary(minify: true);
|
||||
await testWithoutMirrorHelperLibrary(minify: false);
|
||||
});
|
||||
}
|
||||
|
||||
Future<CompilerImpl> run({OutputCollector outputCollector,
|
||||
bool useMirrorHelperLibrary: false,
|
||||
bool minify: false}) async {
|
||||
List<String> options = ['--output-type=dart'];
|
||||
if (minify) {
|
||||
options.add('--minify');
|
||||
}
|
||||
var result = await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES,
|
||||
outputProvider: outputCollector,
|
||||
options: options,
|
||||
beforeRun: (CompilerImpl compiler) {
|
||||
DartBackend backend = compiler.backend;
|
||||
backend.useMirrorHelperLibrary = useMirrorHelperLibrary;
|
||||
});
|
||||
return result.compiler;
|
||||
}
|
||||
|
||||
Future testWithMirrorHelperLibrary({bool minify}) async {
|
||||
OutputCollector outputCollector = new OutputCollector();
|
||||
CompilerImpl compiler = await run(
|
||||
outputCollector: outputCollector,
|
||||
useMirrorHelperLibrary: true,
|
||||
minify: minify);
|
||||
DartBackend backend = compiler.backend;
|
||||
MirrorRenamerImpl mirrorRenamer = backend.mirrorRenamer;
|
||||
Map<Node, String> renames = backend.placeholderRenamer.renames;
|
||||
Map<String, String> symbols = mirrorRenamer.symbols;
|
||||
|
||||
Expect.isFalse(null == mirrorRenamer.helperLibrary);
|
||||
Expect.isFalse(null == mirrorRenamer.getNameFunction);
|
||||
|
||||
for (Node n in renames.keys) {
|
||||
if (symbols.containsKey(renames[n])) {
|
||||
if(n.toString() == 'getName') {
|
||||
Expect.equals(
|
||||
MirrorRenamerImpl.MIRROR_HELPER_GET_NAME_FUNCTION,
|
||||
symbols[renames[n]]);
|
||||
} else {
|
||||
Expect.equals(n.toString(), symbols[renames[n]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String output = outputCollector.getOutput('', 'dart');
|
||||
String getNameMatch = MirrorRenamerImpl.MIRROR_HELPER_GET_NAME_FUNCTION;
|
||||
Iterable i = getNameMatch.allMatches(output);
|
||||
print(output);
|
||||
if (minify) {
|
||||
Expect.equals(0, i.length);
|
||||
} else {
|
||||
// Appears twice in code (defined & called).
|
||||
Expect.equals(2, i.length);
|
||||
}
|
||||
|
||||
RegExp mapMatch = new RegExp('const<String,( )?String>');
|
||||
i = mapMatch.allMatches(output);
|
||||
Expect.equals(1, i.length);
|
||||
}
|
||||
|
||||
Future testWithoutMirrorHelperLibrary({bool minify}) async {
|
||||
CompilerImpl compiler =
|
||||
await run(useMirrorHelperLibrary: false, minify: minify);
|
||||
DartBackend backend = compiler.backend;
|
||||
MirrorRenamer mirrorRenamer = backend.mirrorRenamer;
|
||||
|
||||
Expect.equals(null, mirrorRenamer.helperLibrary);
|
||||
Expect.equals(null, mirrorRenamer.getNameFunction);
|
||||
}
|
||||
|
||||
const MEMORY_SOURCE_FILES = const <String, String> {
|
||||
'main.dart': """
|
||||
import 'dart:mirrors';
|
||||
|
||||
class Foo {
|
||||
noSuchMethod(Invocation invocation) {
|
||||
MirrorSystem.getName(invocation.memberName);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
new Foo().fisk();
|
||||
}
|
||||
"""};
|
|
@ -1,94 +0,0 @@
|
|||
// 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.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'memory_compiler.dart' show runCompiler;
|
||||
import 'package:compiler/src/apiimpl.dart' show
|
||||
CompilerImpl;
|
||||
import 'package:compiler/src/elements/elements.dart' show
|
||||
Element, LibraryElement, ClassElement;
|
||||
import 'package:compiler/src/tree/tree.dart' show
|
||||
Block, ExpressionStatement, FunctionExpression, Node, Send;
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart' show
|
||||
DartBackend, ElementAst;
|
||||
import 'package:compiler/src/mirror_renamer/mirror_renamer.dart' show
|
||||
MirrorRenamerImpl;
|
||||
|
||||
main() {
|
||||
asyncTest(() async {
|
||||
await testWithMirrorRenaming(minify: true);
|
||||
await testWithMirrorRenaming(minify: false);
|
||||
await testWithoutMirrorRenaming(minify: true);
|
||||
await testWithoutMirrorRenaming(minify: false);
|
||||
});
|
||||
}
|
||||
|
||||
Future<CompilerImpl> run({useMirrorHelperLibrary: false, minify: false}) async {
|
||||
List<String> options = ['--output-type=dart'];
|
||||
if (minify) {
|
||||
options.add('--minify');
|
||||
}
|
||||
var result = await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES,
|
||||
options: options,
|
||||
beforeRun: (CompilerImpl compiler) {
|
||||
DartBackend backend = compiler.backend;
|
||||
backend.useMirrorHelperLibrary = useMirrorHelperLibrary;
|
||||
});
|
||||
return result.compiler;
|
||||
}
|
||||
|
||||
Future testWithMirrorRenaming({bool minify}) async {
|
||||
CompilerImpl compiler =
|
||||
await run(useMirrorHelperLibrary: true, minify: minify);
|
||||
DartBackend backend = compiler.backend;
|
||||
MirrorRenamerImpl mirrorRenamer = backend.mirrorRenamer;
|
||||
Map<Node, String> renames = backend.placeholderRenamer.renames;
|
||||
Iterable<LibraryElement> imports =
|
||||
backend.placeholderRenamer.platformImports.keys;
|
||||
|
||||
FunctionExpression node = backend.memberNodes.values.first.first;
|
||||
Block block = node.body;
|
||||
ExpressionStatement getNameFunctionNode = block.statements.nodes.head;
|
||||
Send send = getNameFunctionNode.expression;
|
||||
|
||||
Expect.equals(renames[mirrorRenamer.getNameFunctionNode.name],
|
||||
renames[send.selector]);
|
||||
Expect.equals("",
|
||||
renames[send.receiver]);
|
||||
Expect.equals(1, imports.length);
|
||||
}
|
||||
|
||||
Future testWithoutMirrorRenaming({bool minify}) async {
|
||||
CompilerImpl compiler =
|
||||
await run(useMirrorHelperLibrary: false, minify: minify);
|
||||
DartBackend backend = compiler.backend;
|
||||
Map<Node, String> renames = backend.placeholderRenamer.renames;
|
||||
Iterable<LibraryElement> imports =
|
||||
backend.placeholderRenamer.platformImports.keys;
|
||||
FunctionExpression node = backend.memberNodes.values.first.first;
|
||||
Block block = node.body;
|
||||
ExpressionStatement getNameFunctionNode = block.statements.nodes.head;
|
||||
Send send = getNameFunctionNode.expression;
|
||||
|
||||
Expect.isFalse(renames.containsKey(send.selector));
|
||||
Expect.equals(1, imports.length);
|
||||
}
|
||||
|
||||
const MEMORY_SOURCE_FILES = const <String, String> {
|
||||
'main.dart': """
|
||||
import 'dart:mirrors';
|
||||
|
||||
class Foo {
|
||||
noSuchMethod(Invocation invocation) {
|
||||
MirrorSystem.getName(invocation.memberName);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
new Foo().fisk();
|
||||
}
|
||||
"""};
|
|
@ -1,89 +0,0 @@
|
|||
// 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.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
import 'dart:async';
|
||||
import "package:async_helper/async_helper.dart";
|
||||
import 'memory_compiler.dart' show runCompiler;
|
||||
import 'package:compiler/src/apiimpl.dart' show
|
||||
CompilerImpl;
|
||||
import 'package:compiler/src/dart_backend/dart_backend.dart' show
|
||||
DartBackend;
|
||||
import 'package:compiler/src/tree/tree.dart' show
|
||||
Identifier, Node, Send;
|
||||
import 'package:compiler/src/mirror_renamer/mirror_renamer.dart' show
|
||||
MirrorRenamerImpl;
|
||||
|
||||
main() {
|
||||
asyncTest(() async {
|
||||
await testUniqueMinification();
|
||||
await testNoUniqueMinification();
|
||||
});
|
||||
}
|
||||
|
||||
Future<CompilerImpl> run({useMirrorHelperLibrary: false, minify: false}) async {
|
||||
List<String> options = ['--output-type=dart'];
|
||||
if (minify) {
|
||||
options.add('--minify');
|
||||
}
|
||||
var result = await runCompiler(
|
||||
memorySourceFiles: MEMORY_SOURCE_FILES,
|
||||
options: options,
|
||||
beforeRun: (CompilerImpl compiler) {
|
||||
DartBackend backend = compiler.backend;
|
||||
backend.useMirrorHelperLibrary = useMirrorHelperLibrary;
|
||||
});
|
||||
return result.compiler;
|
||||
}
|
||||
|
||||
Future testUniqueMinification() async {
|
||||
CompilerImpl compiler = await run(useMirrorHelperLibrary: true, minify: true);
|
||||
DartBackend backend = compiler.backend;
|
||||
MirrorRenamerImpl mirrorRenamer = backend.mirrorRenamer;
|
||||
Map<Node, String> renames = backend.placeholderRenamer.renames;
|
||||
Map<String, String> symbols = mirrorRenamer.symbols;
|
||||
|
||||
// Check that no two different source code names get the same mangled name,
|
||||
// with the exception of MirrorSystem.getName that gets renamed to the same
|
||||
// mangled name as the getNameHelper from _mirror_helper.dart.
|
||||
for (Node node in renames.keys) {
|
||||
Identifier identifier = node.asIdentifier();
|
||||
if (identifier != null) {
|
||||
String source = identifier.source;
|
||||
Send send = mirrorRenamer.mirrorSystemGetNameNodes.first;
|
||||
if (send.selector == node)
|
||||
continue;
|
||||
if (symbols.containsKey(renames[node])) {
|
||||
print(node);
|
||||
Expect.equals(source, symbols[renames[node]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future testNoUniqueMinification() async {
|
||||
CompilerImpl compiler =
|
||||
await run(useMirrorHelperLibrary: false, minify: true);
|
||||
DartBackend backend = compiler.backend;
|
||||
Map<Node, String> renames = backend.placeholderRenamer.renames;
|
||||
|
||||
// 'Foo' appears twice and 'invocation' and 'hest' get the same mangled
|
||||
// name.
|
||||
Expect.equals(renames.values.toSet().length, renames.values.length - 2);
|
||||
}
|
||||
|
||||
const MEMORY_SOURCE_FILES = const <String, String> {
|
||||
'main.dart': """
|
||||
import 'dart:mirrors';
|
||||
|
||||
class Foo {
|
||||
noSuchMethod(invocation) {
|
||||
MirrorSystem.getName(null);
|
||||
}
|
||||
}
|
||||
|
||||
main(hest) {
|
||||
new Foo().fisk();
|
||||
}
|
||||
"""};
|
|
@ -80,7 +80,6 @@ class MockCompiler extends Compiler {
|
|||
bool disableTypeInference: false,
|
||||
bool analyzeAll: false,
|
||||
bool analyzeOnly: false,
|
||||
bool emitJavaScript: true,
|
||||
bool preserveComments: false,
|
||||
// Our unit tests check code generation output that is
|
||||
// affected by inlining support.
|
||||
|
@ -106,7 +105,6 @@ class MockCompiler extends Compiler {
|
|||
disableTypeInference: disableTypeInference,
|
||||
analyzeAll: analyzeAll,
|
||||
analyzeOnly: analyzeOnly,
|
||||
emitJavaScript: emitJavaScript,
|
||||
preserveComments: preserveComments,
|
||||
trustTypeAnnotations: trustTypeAnnotations,
|
||||
trustJSInteropTypeAnnotations: trustJSInteropTypeAnnotations,
|
||||
|
|
|
@ -20,12 +20,8 @@ main() async {
|
|||
Map<String, Uri> shared = await load(
|
||||
Uri.base.resolve("sdk/lib/dart_shared.platform"),
|
||||
input);
|
||||
Map<String, Uri> dart2dart = await load(
|
||||
Uri.base.resolve("sdk/lib/dart2dart.platform"),
|
||||
input);
|
||||
Expect.setEquals(new Set.from(shared.keys), new Set.from(client.keys));
|
||||
Expect.setEquals(new Set.from(shared.keys), new Set.from(server.keys));
|
||||
Expect.setEquals(new Set.from(shared.keys), new Set.from(dart2dart.keys));
|
||||
|
||||
for (String libraryName in shared.keys) {
|
||||
test(Map<String, Uri> m) {
|
||||
|
@ -36,6 +32,5 @@ main() async {
|
|||
}
|
||||
test(client);
|
||||
test(server);
|
||||
test(dart2dart);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// 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.
|
||||
// Tests dangling else. The VM should not have any problems, but dart2js or
|
||||
// dart2dart could get this wrong.
|
||||
// Tests dangling else. The VM should not have any problems, but dart2js could
|
||||
// get this wrong.
|
||||
|
||||
import "package:expect/expect.dart";
|
||||
|
||||
|
|
|
@ -44,7 +44,6 @@
|
|||
# ......dart_client.platform
|
||||
# ......dart_server.platform
|
||||
# ......dart_shared.platform
|
||||
# ......dart2dart.platform
|
||||
# ......_internal/
|
||||
#.........spec.sum
|
||||
#.........strong.sum
|
||||
|
@ -267,8 +266,7 @@ def Main():
|
|||
# Copy the platform descriptors.
|
||||
for file_name in ["dart_client.platform",
|
||||
"dart_server.platform",
|
||||
"dart_shared.platform",
|
||||
"dart2dart.platform"]:
|
||||
"dart_shared.platform"]:
|
||||
copyfile(join(HOME, 'sdk', 'lib', file_name), join(LIB, file_name));
|
||||
|
||||
# Copy libraries.dart to lib/_internal/libraries.dart for backwards
|
||||
|
|
Loading…
Reference in a new issue