// Copyright (c) 2016, 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 kernel.analyzer.loader; import 'dart:async'; import 'dart:io' as io; import 'package:analyzer/analyzer.dart'; import 'package:analyzer/file_system/file_system.dart'; import 'package:analyzer/file_system/physical_file_system.dart'; import 'package:analyzer/source/package_map_resolver.dart'; import 'package:analyzer/src/dart/scanner/scanner.dart'; import 'package:analyzer/src/dart/sdk/sdk.dart'; import 'package:analyzer/src/generated/engine.dart'; import 'package:analyzer/src/generated/parser.dart'; import 'package:analyzer/src/generated/sdk.dart'; import 'package:analyzer/src/generated/source_io.dart'; import 'package:analyzer/src/summary/summary_sdk.dart'; import 'package:kernel/application_root.dart'; import 'package:package_config/discovery.dart'; import 'package:package_config/packages.dart'; import '../ast.dart' as ast; import '../target/targets.dart' show Target; import '../type_algebra.dart'; import 'analyzer.dart'; import 'ast_from_analyzer.dart'; /// Options passed to the Dart frontend. class DartOptions { /// True if user code should be loaded in strong mode. bool strongMode; /// True if the Dart SDK should be loaded in strong mode. bool strongModeSdk; /// Path to the sdk sources, ignored if sdkSummary is provided. String sdk; /// Path to a summary of the sdk sources. String sdkSummary; /// Path to the `.packages` file. String packagePath; /// Root used to relativize app file-urls, making them machine agnostic. ApplicationRoot applicationRoot; Map customUriMappings; /// Environment definitions provided via `-Dkey=value`. Map declaredVariables; DartOptions( {bool strongMode: false, bool strongModeSdk, this.sdk, this.sdkSummary, this.packagePath, ApplicationRoot applicationRoot, Map customUriMappings, Map declaredVariables}) : this.customUriMappings = customUriMappings ?? {}, this.declaredVariables = declaredVariables ?? {}, this.strongMode = strongMode, this.strongModeSdk = strongModeSdk ?? strongMode, this.applicationRoot = applicationRoot ?? new ApplicationRoot.none(); } abstract class ReferenceLevelLoader { ast.Library getLibraryReference(LibraryElement element); ast.Class getClassReference(ClassElement element); ast.Member getMemberReference(Element element); ast.Class getRootClassReference(); ast.Constructor getRootClassConstructorReference(); ast.Class getCoreClassReference(String className); ast.Constructor getCoreClassConstructorReference(String className, {String constructorName, String library}); ast.TypeParameter tryGetClassTypeParameter(TypeParameterElement element); ast.Class getSharedMixinApplicationClass( ast.Library library, ast.Class supertype, ast.Class mixin); bool get strongMode; /// Whether or not to include redirecting factories in the output. bool get ignoreRedirectingFactories; } class DartLoader implements ReferenceLevelLoader { final ast.Program program; final ApplicationRoot applicationRoot; final Bimap _classes = new Bimap(); final Bimap _members = new Bimap(); final Map _classTypeParameters = {}; final Map> _mixinApplications = >{}; final Map _libraries = {}; final AnalysisContext context; LibraryElement _dartCoreLibrary; final List errors = []; final List libraryElements = []; /// Classes that have been referenced, and must be promoted to type level /// so as not to expose partially initialized classes. final List temporaryClassWorklist = []; final Map> mixinLibraryWorklist = {}; final bool ignoreRedirectingFactories; LibraryElement _libraryBeingLoaded = null; ClassElement _classBeingPromotedToMixin = null; bool get strongMode => context.analysisOptions.strongMode; DartLoader(this.program, DartOptions options, Packages packages, {DartSdk dartSdk, AnalysisContext context, this.ignoreRedirectingFactories: true}) : this.context = context ?? createContext(options, packages, dartSdk: dartSdk), this.applicationRoot = options.applicationRoot; String getLibraryName(LibraryElement element) { return element.name.isEmpty ? null : element.name; } LibraryElement getLibraryElementFromUri(Uri uri) { var source = context.sourceFactory.forUri2(uri); if (source == null) return null; return context.computeLibraryElement(source); } ast.Library getLibraryReference(LibraryElement element) { var uri = applicationRoot.relativeUri(element.source.uri); var library = _libraries[element]; if (library == null) { library = new ast.Library(uri) ..isExternal = true ..name = getLibraryName(element) ..fileUri = '${element.source.uri}'; program.libraries.add(library..parent = program); _libraries[element] = library; } return library; } ast.Library getLibraryReferenceFromUri(Uri uri) { return getLibraryReference(getLibraryElementFromUri(uri)); } void _buildTopLevelMember( ast.Member member, Element element, Declaration astNode) { assert(member.parent != null); new MemberBodyBuilder(this, member, element).build(astNode); } /// True if [element] is in the process of being loaded by /// [_buildLibraryBody]. /// /// If this is the case, we should avoid adding new members to the classes /// in the library, since the AST builder will rebuild the member lists. bool isLibraryBeingLoaded(LibraryElement element) { return _libraryBeingLoaded == element; } bool isClassBeingPromotedToMixin(ClassElement element) { return _classBeingPromotedToMixin == element; } void _buildLibraryBody(LibraryElement element, ast.Library library, List units) { assert(_libraryBeingLoaded == null); _libraryBeingLoaded = element; var classes = []; var procedures = []; var fields = []; void loadClass(NamedCompilationUnitMember declaration) { // [declaration] can be a ClassDeclaration, EnumDeclaration, or a // ClassTypeAlias. ClassElement element = declaration.element; var node = getClassReference(element); promoteToBodyLevel(node, element, declaration); classes.add(node); } void loadProcedure(FunctionDeclaration declaration) { var element = declaration.element; var node = getMemberReference(element); _buildTopLevelMember(node, element, declaration); procedures.add(node); } void loadField(TopLevelVariableDeclaration declaration) { for (var field in declaration.variables.variables) { var element = field.element; // Ignore fields inserted through error recovery. if (element.name == '') continue; var node = getMemberReference(element); _buildTopLevelMember(node, element, field); fields.add(node); } } for (var unit in units) { for (CompilationUnitMember declaration in unit.declarations) { if (declaration is ClassDeclaration || declaration is EnumDeclaration || declaration is ClassTypeAlias) { loadClass(declaration); } else if (declaration is FunctionDeclaration) { loadProcedure(declaration); } else if (declaration is TopLevelVariableDeclaration) { loadField(declaration); } else if (declaration is FunctionTypeAlias) { // Nothing to do. Typedefs are handled lazily while constructing type // references. } else { throw "unexpected node: ${declaration.runtimeType} $declaration"; } } } libraryElements.add(element); _iterateTemporaryClassWorklist(); // Ensure everything is stored in the original declaration order. library.classes ..clear() ..addAll(classes) ..addAll(_mixinApplications[library]?.values ?? const []); library.fields ..clear() ..addAll(fields); library.procedures ..clear() ..addAll(procedures); _libraryBeingLoaded = null; } LibraryElement getDartCoreLibrary() { return _dartCoreLibrary ??= _findLibraryElement('dart:core'); } LibraryElement _findLibraryElement(String uri) { var source = context.sourceFactory.forUri(uri); if (source == null) return null; return context.computeLibraryElement(source); } ast.Class getRootClassReference() { return getCoreClassReference('Object'); } ast.Constructor getRootClassConstructorReference() { var element = getDartCoreLibrary().getType('Object').constructors[0]; return getMemberReference(element); } ast.Class getCoreClassReference(String className) { return getClassReference(getDartCoreLibrary().getType(className)); } ast.Constructor getCoreClassConstructorReference(String className, {String constructorName, String library}) { LibraryElement libraryElement = library != null ? _findLibraryElement(library) : getDartCoreLibrary(); ClassElement element = libraryElement.getType(className); if (element == null) { throw 'Missing core class $className from ${libraryElement.name}'; } var constructor = element.constructors.firstWhere((constructor) { return (constructorName == null) ? (constructor.nameLength == 0) : (constructor.name == constructorName); }); return getMemberReference(constructor); } ClassElement getClassElement(ast.Class node) { return _classes.inverse[node]; } void addMixinClassToLibrary(ast.Class class_, ast.Library library) { assert(class_.parent == null); library.addClass(class_); var map = _mixinApplications.putIfAbsent(library, () => {}); map[class_.name] = class_; } /// Returns the IR for a class, at a temporary loading level. /// /// The returned class has the correct name, flags, type parameter arity, /// and enclosing library. ast.Class getClassReference(ClassElement element) { var classNode = _classes[element]; if (classNode != null) return classNode; _classes[element] = classNode = new ast.Class( name: element.name, isAbstract: element.isAbstract, fileUri: '${element.source.uri}')..fileOffset = element.nameOffset; classNode.level = ast.ClassLevel.Temporary; var library = getLibraryReference(element.library); library.addClass(classNode); // Initialize type parameter list without bounds. for (var parameter in element.typeParameters) { var parameterNode = new ast.TypeParameter(parameter.name); _classTypeParameters[parameter] = parameterNode; classNode.typeParameters.add(parameterNode); parameterNode.parent = classNode; } // Ensure the class is at least promoted to type level before exposing it // to kernel consumers. temporaryClassWorklist.add(classNode); return classNode; } /// Ensures the supertypes and type parameter bounds have been generated for /// the given class. void promoteToTypeLevel(ast.Class classNode) { if (classNode.level.index >= ast.ClassLevel.Type.index) return; classNode.level = ast.ClassLevel.Type; var element = getClassElement(classNode); assert(element != null); var library = getLibraryReference(element.library); var scope = new ClassScope(this, library); // Initialize bounds on type parameters. for (int i = 0; i < classNode.typeParameters.length; ++i) { var parameter = element.typeParameters[i]; var parameterNode = classNode.typeParameters[i]; parameterNode.bound = parameter.bound == null ? scope.defaultTypeParameterBound : scope.buildType(parameter.bound); } // Initialize supertypes. Iterable mixins = element.mixins; if (element.isMixinApplication && mixins.isNotEmpty) { classNode.mixedInType = scope.buildSupertype(mixins.last); mixins = mixins.take(mixins.length - 1); } if (element.supertype != null) { ast.Supertype supertype = scope.buildSupertype(element.supertype); bool useSharedMixin = true; for (var mixin in mixins) { var mixinType = scope.buildSupertype(mixin); if (useSharedMixin && areDistinctUnboundTypeVariables(supertype, mixinType)) { // Use a shared mixin application class for this library. var mixinClass = getSharedMixinApplicationClass( scope.currentLibrary, supertype.classNode, mixinType.classNode); if (mixinClass.fileOffset < 0) { mixinClass.fileOffset = element.nameOffset; } supertype = new ast.Supertype( mixinClass, supertype.typeArguments.length > mixinType.typeArguments.length ? supertype.typeArguments : mixinType.typeArguments); } else { // Generate a new class specific for this mixin application. var freshParameters = getFreshTypeParameters(classNode.typeParameters); var mixinClass = new ast.Class( name: '${classNode.name}^${mixinType.classNode.name}', isAbstract: true, typeParameters: freshParameters.freshTypeParameters, supertype: freshParameters.substituteSuper(supertype), mixedInType: freshParameters.substituteSuper(mixinType), fileUri: classNode.fileUri)..fileOffset = element.nameOffset; mixinClass.level = ast.ClassLevel.Type; addMixinClassToLibrary(mixinClass, classNode.enclosingLibrary); supertype = new ast.Supertype(mixinClass, classNode.typeParameters.map(makeTypeParameterType).toList()); // This class cannot be used from anywhere else, so don't try to // generate shared mixin applications using it. useSharedMixin = false; } } classNode.supertype = supertype; for (var implementedType in element.interfaces) { classNode.implementedTypes.add(scope.buildSupertype(implementedType)); } } } void promoteToHierarchyLevel(ast.Class classNode) { if (classNode.level.index >= ast.ClassLevel.Hierarchy.index) return; promoteToTypeLevel(classNode); classNode.level = ast.ClassLevel.Hierarchy; var element = getClassElement(classNode); if (element != null) { // Ensure all instance members are at present. for (var field in element.fields) { if (!field.isStatic && !field.isSynthetic) { getMemberReference(field); } } for (var accessor in element.accessors) { if (!accessor.isStatic && !accessor.isSynthetic) { getMemberReference(accessor); } } for (var method in element.methods) { if (!method.isStatic && !method.isSynthetic) { getMemberReference(method); } } } for (var supertype in classNode.supers) { promoteToHierarchyLevel(supertype.classNode); } } void promoteToMixinLevel(ast.Class classNode, ClassElement element, NamedCompilationUnitMember astNode) { if (classNode.level.index >= ast.ClassLevel.Mixin.index) return; _classBeingPromotedToMixin = element; promoteToHierarchyLevel(classNode); classNode.level = ast.ClassLevel.Mixin; // Clear out the member references that were put in the class. // The AST builder will load them all put back in the right order. classNode..fields.clear()..procedures.clear()..constructors.clear(); new ClassBodyBuilder(this, classNode, element).build(astNode); _classBeingPromotedToMixin = null; // Ensure mixed-in classes are available. for (var mixin in element.mixins) { _ensureMixinBecomesLoaded(mixin.element); } } /// Ensures that [element] eventually becomes loaded at least at mixin level. void _ensureMixinBecomesLoaded(ClassElement element) { if (isClassBeingPromotedToMixin(element)) { return; } var class_ = getClassReference(element); if (class_.level.index >= ast.ClassLevel.Mixin.index) { return; } var list = mixinLibraryWorklist[element.library] ??= []; list.add(element); } void promoteToBodyLevel(ast.Class classNode, ClassElement element, NamedCompilationUnitMember astNode) { if (classNode.level == ast.ClassLevel.Body) return; promoteToMixinLevel(classNode, element, astNode); classNode.level = ast.ClassLevel.Body; // This frontend delivers the same contents for classes at body and mixin // levels, even though as specified, the mixin level does not require all // the static members to be present. So no additional work is needed. } ast.TypeParameter tryGetClassTypeParameter(TypeParameterElement element) { return _classTypeParameters[element]; } Element getMemberElement(ast.Member node) { return _members.inverse[node]; } ast.Member getMemberReference(Element element) { assert(element != null); assert(element is! Member); // Use the "base element". return _members[element] ??= _buildMemberReference(element); } ast.Member _buildMemberReference(Element element) { assert(element != null); var member = _buildOrphanedMemberReference(element); // Set the parent pointer and store it in the enclosing class or library. // If the enclosing library is being built from the AST, do not add the // member, since the AST builder will put it in there. var parent = element.enclosingElement; if (parent is ClassElement) { var class_ = getClassReference(parent); member.parent = class_; if (!isLibraryBeingLoaded(element.library)) { class_.addMember(member); } } else { var library = getLibraryReference(element.library); member.parent = library; if (!isLibraryBeingLoaded(element.library)) { library.addMember(member); } } return member; } ast.Member _buildOrphanedMemberReference(Element element) { assert(element != null); ClassElement classElement = element.enclosingElement is ClassElement ? element.enclosingElement : null; TypeScope scope = classElement != null ? new ClassScope(this, getLibraryReference(element.library)) : new TypeScope(this); if (classElement != null) { getClassReference(classElement); } switch (element.kind) { case ElementKind.CONSTRUCTOR: ConstructorElement constructor = element; if (constructor.isFactory) { return new ast.Procedure( _nameOfMember(constructor), ast.ProcedureKind.Factory, scope.buildFunctionInterface(constructor), isAbstract: false, isStatic: true, isExternal: constructor.isExternal, isConst: constructor.isConst, fileUri: '${element.source.uri}') ..fileOffset = element.nameOffset; } return new ast.Constructor(scope.buildFunctionInterface(constructor), name: _nameOfMember(element), isConst: constructor.isConst, isExternal: constructor.isExternal) ..fileOffset = element.nameOffset; case ElementKind.FIELD: case ElementKind.TOP_LEVEL_VARIABLE: VariableElement variable = element; return new ast.Field(_nameOfMember(variable), isStatic: variable.isStatic, isFinal: variable.isFinal, isConst: variable.isConst, type: scope.buildType(variable.type), fileUri: '${element.source.uri}')..fileOffset = element.nameOffset; case ElementKind.METHOD: case ElementKind.GETTER: case ElementKind.SETTER: case ElementKind.FUNCTION: if (element is FunctionElement && element.enclosingElement is! CompilationUnitElement) { throw 'Function $element is nested in ${element.enclosingElement} ' 'and hence is not a member'; } ExecutableElement executable = element; return new ast.Procedure( _nameOfMember(element), _procedureKindOf(executable), scope.buildFunctionInterface(executable), isAbstract: executable.isAbstract, isStatic: executable.isStatic, isExternal: executable.isExternal, fileUri: '${element.source.uri}')..fileOffset = element.nameOffset; default: throw 'Unexpected member kind: $element'; } } ast.ProcedureKind _procedureKindOf(ExecutableElement element) { if (element is PropertyAccessorElement) { return element.isGetter ? ast.ProcedureKind.Getter : ast.ProcedureKind.Setter; } if (element is MethodElement) { if (element.isOperator) return ast.ProcedureKind.Operator; return ast.ProcedureKind.Method; } if (element is FunctionElement) { return ast.ProcedureKind.Method; } if (element is ConstructorElement) { assert(element.isFactory); return ast.ProcedureKind.Factory; } throw 'Unexpected procedure: $element'; } ast.Name _nameOfMember(Element element) { // Use 'displayName' to avoid a trailing '=' for setters and 'name' to // ensure unary minus is called 'unary-'. String name = element is PropertyAccessorElement ? element.displayName : element.name; return new ast.Name(name, getLibraryReference(element.library)); } /// True if the two types have form `C` and `D`, and /// `T1 ... TN` are distinct type variables with no upper bound, where /// `N = max(m,n)`. bool areDistinctUnboundTypeVariables( ast.Supertype first, ast.Supertype second) { var seen = new Set(); if (first.typeArguments.length < second.typeArguments.length) { var tmp = first; first = second; second = tmp; } for (int i = 0; i < first.typeArguments.length; ++i) { var firstArg = first.typeArguments[i]; if (!(firstArg is ast.TypeParameterType && seen.add(firstArg.parameter) && firstArg.parameter.bound is ast.DynamicType)) { return false; } if (i < second.typeArguments.length && firstArg != second.typeArguments[i]) { return false; } } return true; } /// Returns the canonical mixin application of two classes, instantiated with /// the same list of unbound type variables. /// /// Given two classes: /// class C /// class D /// /// This creates or reuses a mixin application class in the library of form: /// /// abstract class C&D = C with D /// /// where `N = max(m,n)`. /// /// Such a class can in general contain type errors due to incompatible /// inheritance from `C` and `D`. This method therefore should only be called /// if a mixin application `C with D` is seen, where /// `S1 ... SN` are distinct, unbound type variables. ast.Class getSharedMixinApplicationClass( ast.Library library, ast.Class superclass, ast.Class mixedInClass) { // TODO(asgerf): Avoid potential name clash due to associativity. // As it is, these mixins get the same name: // (A with B) with C // A with (B with C) String name = '${superclass.name}&${mixedInClass.name}'; return _mixinApplications .putIfAbsent(library, () => {}) .putIfAbsent(name, () { var fresh = superclass.typeParameters.length >= mixedInClass.typeParameters.length ? getFreshTypeParameters(superclass.typeParameters) : getFreshTypeParameters(mixedInClass.typeParameters); var typeArguments = fresh.freshTypeParameters.map(makeTypeParameterType).toList(); var superArgs = typeArguments.length != superclass.typeParameters.length ? typeArguments.sublist(0, superclass.typeParameters.length) : typeArguments; var mixinArgs = typeArguments.length != mixedInClass.typeParameters.length ? typeArguments.sublist(0, mixedInClass.typeParameters.length) : typeArguments; var result = new ast.Class( name: name, isAbstract: true, typeParameters: fresh.freshTypeParameters, supertype: new ast.Supertype(superclass, superArgs), mixedInType: new ast.Supertype(mixedInClass, mixinArgs), fileUri: library.fileUri); result.level = ast.ClassLevel.Type; library.addClass(result); return result; }); } String formatErrorMessage( AnalysisError error, String filename, LineInfo lines) { var location = lines.getLocation(error.offset); return '[error] ${error.message} ($filename, ' 'line ${location.lineNumber}, ' 'col ${location.columnNumber})'; } void ensureLibraryIsLoaded(ast.Library node) { _ensureLibraryIsLoaded(node); _iterateMixinLibraryWorklist(); } void _ensureLibraryIsLoaded(ast.Library node) { if (!node.isExternal) return; node.isExternal = false; var source = context.sourceFactory .forUri2(applicationRoot.absoluteUri(node.importUri)); assert(source != null); var element = context.computeLibraryElement(source); var units = []; bool reportErrors = node.importUri.scheme != 'dart'; var tree = context.resolveCompilationUnit(source, element); units.add(tree); if (reportErrors) _processErrors(source); for (var part in element.parts) { var source = part.source; units.add(context.resolveCompilationUnit(source, element)); if (reportErrors) _processErrors(source); } _buildLibraryBody(element, node, units); } void _processErrors(Source source) { LineInfo lines; for (var error in context.computeErrors(source)) { if (error.errorCode is CompileTimeErrorCode || error.errorCode is ParserErrorCode || error.errorCode is ScannerErrorCode || error.errorCode is StrongModeCode) { lines ??= context.computeLineInfo(source); errors.add(formatErrorMessage(error, source.shortName, lines)); } } } void loadSdkInterface(ast.Program program, Target target) { var requiredSdkMembers = target.requiredSdkClasses; for (var libraryUri in requiredSdkMembers.keys) { var source = context.sourceFactory.forUri2(Uri.parse(libraryUri)); var libraryElement = context.computeLibraryElement(source); for (var member in requiredSdkMembers[libraryUri]) { var type = libraryElement.getType(member); if (type == null) { throw 'Could not find $member in $libraryUri'; } promoteToTypeLevel(getClassReference(type)); } } _iterateTemporaryClassWorklist(); _iterateMixinLibraryWorklist(); } void loadEverything({Target target, bool compileSdk}) { compileSdk ??= true; if (compileSdk) { ensureLibraryIsLoaded(getLibraryReference(getDartCoreLibrary())); if (target != null) { for (var uri in target.extraRequiredLibraries) { var library = _findLibraryElement(uri); if (library == null) { errors.add('Could not find required library $uri'); continue; } ensureLibraryIsLoaded(getLibraryReference(library)); } } } for (int i = 0; i < program.libraries.length; ++i) { var library = program.libraries[i]; if (compileSdk || library.importUri.scheme != 'dart') { ensureLibraryIsLoaded(library); } } } /// Builds a list of sources that have been loaded. /// /// This operation may be expensive and should only be used for diagnostics. List getLoadedFileNames() { var list = []; for (var library in program.libraries) { LibraryElement element = context.computeLibraryElement(context .sourceFactory .forUri2(applicationRoot.absoluteUri(library.importUri))); for (var unit in element.units) { list.add(unit.source.fullName); } } return list; } void _iterateTemporaryClassWorklist() { while (temporaryClassWorklist.isNotEmpty) { var element = temporaryClassWorklist.removeLast(); promoteToTypeLevel(element); } } void _iterateMixinLibraryWorklist() { // The worklist groups classes in the same library together so that we // request resolved ASTs for each library only once. while (mixinLibraryWorklist.isNotEmpty) { LibraryElement library = mixinLibraryWorklist.keys.first; _libraryBeingLoaded = library; List classes = mixinLibraryWorklist.remove(library); for (var class_ in classes) { var classNode = getClassReference(class_); promoteToMixinLevel(classNode, class_, class_.computeNode()); } _libraryBeingLoaded = null; } _iterateTemporaryClassWorklist(); } ast.Procedure _getMainMethod(Uri uri) { Source source = context.sourceFactory.forUri2(uri); LibraryElement library = context.computeLibraryElement(source); var mainElement = library.entryPoint; if (mainElement == null) return null; var mainMember = getMemberReference(mainElement); if (mainMember is ast.Procedure && !mainMember.isAccessor) { return mainMember; } // Top-level 'main' getters are not supported at the moment. return null; } ast.Procedure _makeMissingMainMethod(ast.Library library) { var main = new ast.Procedure( new ast.Name('main'), ast.ProcedureKind.Method, new ast.FunctionNode(new ast.ExpressionStatement(new ast.Throw( new ast.StringLiteral('Program has no main method')))), isStatic: true)..fileUri = library.fileUri; library.addMember(main); return main; } void loadProgram(Uri mainLibrary, {Target target, bool compileSdk}) { ast.Library library = getLibraryReferenceFromUri(mainLibrary); ensureLibraryIsLoaded(library); var mainMethod = _getMainMethod(mainLibrary); loadEverything(target: target, compileSdk: compileSdk); if (mainMethod == null) { mainMethod = _makeMissingMainMethod(library); } program.mainMethod = mainMethod; for (LibraryElement libraryElement in libraryElements) { for (CompilationUnitElement compilationUnitElement in libraryElement.units) { var source = compilationUnitElement.source; LineInfo lineInfo = context.computeLineInfo(source); String sourceCode; try { sourceCode = context.getContents(source).data; } catch (e) { // The source's contents could not be accessed. sourceCode = ''; } program.uriToSource['${source.uri}'] = new ast.Source(lineInfo.lineStarts, sourceCode); } } } ast.Library loadLibrary(Uri uri) { ast.Library library = getLibraryReferenceFromUri(uri); ensureLibraryIsLoaded(library); return library; } } class Bimap { final Map nodeMap = {}; final Map inverse = {}; bool containsKey(K key) => nodeMap.containsKey(key); V operator [](K key) => nodeMap[key]; void operator []=(K key, V value) { assert(!nodeMap.containsKey(key)); nodeMap[key] = value; inverse[value] = key; } } /// Creates [DartLoader]s for a given configuration, while reusing the /// [DartSdk] and [Packages] object if possible. class DartLoaderBatch { Packages packages; DartSdk dartSdk; String lastSdk; String lastPackagePath; bool lastStrongMode; Future getLoader(ast.Program program, DartOptions options, {String packageDiscoveryPath}) async { if (dartSdk == null || lastSdk != options.sdk || lastStrongMode != options.strongMode) { lastSdk = options.sdk; lastStrongMode = options.strongMode; dartSdk = createDartSdk(options.sdk, strongMode: options.strongModeSdk); } if (packages == null || lastPackagePath != options.packagePath || packageDiscoveryPath != null) { lastPackagePath = options.packagePath; packages = await createPackages(options.packagePath, discoveryPath: packageDiscoveryPath); } return new DartLoader(program, options, packages, dartSdk: dartSdk); } } Future createPackages(String packagePath, {String discoveryPath}) async { if (packagePath != null) { var absolutePath = new io.File(packagePath).absolute.path; if (await new io.Directory(packagePath).exists()) { return getPackagesDirectory(new Uri.file(absolutePath)); } else if (await new io.File(packagePath).exists()) { return loadPackagesFile(new Uri.file(absolutePath)); } else { throw 'Packages not found: $packagePath'; } } if (discoveryPath != null) { return findPackagesFromFile(Uri.parse(discoveryPath)); } return Packages.noPackages; } AnalysisOptions createAnalysisOptions(bool strongMode) { return new AnalysisOptionsImpl() ..strongMode = strongMode ..generateImplicitErrors = false ..generateSdkErrors = false ..preserveComments = false ..hint = false ..enableSuperMixins = true; } DartSdk createDartSdk(String path, {bool strongMode, bool isSummary}) { if (isSummary ?? false) { return new SummaryBasedDartSdk(path, strongMode); } var resources = PhysicalResourceProvider.INSTANCE; return new FolderBasedDartSdk(resources, resources.getFolder(path)) ..context .analysisOptions .setCrossContextOptionsFrom(createAnalysisOptions(strongMode)); } class CustomUriResolver extends UriResolver { final ResourceUriResolver _resourceUriResolver; final Map _customUrlMappings; CustomUriResolver(this._resourceUriResolver, this._customUrlMappings); Source resolveAbsolute(Uri uri, [Uri actualUri]) { // TODO(kustermann): Once dartk supports configurable imports we should be // able to get rid of this. if (uri.toString() == 'package:mojo/src/internal_contract.dart') { uri = actualUri = Uri.parse('dart:mojo.internal'); } Uri baseUri = uri; String relative; String path = uri.path; int index = path.indexOf('/'); if (index > 0) { baseUri = uri.replace(path: path.substring(0, index)); relative = path.substring(index + 1); } Uri baseMapped = _customUrlMappings[baseUri]; if (baseMapped == null) return null; Uri mapped = relative != null ? baseMapped.resolve(relative) : baseMapped; return _resourceUriResolver.resolveAbsolute(mapped, actualUri); } Uri restoreAbsolute(Source source) { return _resourceUriResolver.restoreAbsolute(source); } } AnalysisContext createContext(DartOptions options, Packages packages, {DartSdk dartSdk}) { bool fromSummary = options.sdkSummary != null; dartSdk ??= createDartSdk(fromSummary ? options.sdkSummary : options.sdk, strongMode: options.strongModeSdk, isSummary: fromSummary); var resourceProvider = PhysicalResourceProvider.INSTANCE; var resourceUriResolver = new ResourceUriResolver(resourceProvider); List resolvers = []; var customUriMappings = options.customUriMappings; if (customUriMappings != null && customUriMappings.length > 0) { resolvers .add(new CustomUriResolver(resourceUriResolver, customUriMappings)); } resolvers.add(new DartUriResolver(dartSdk)); resolvers.add(resourceUriResolver); if (packages != null) { var folderMap = >{}; packages.asMap().forEach((String packagePath, Uri uri) { String path = resourceProvider.pathContext.fromUri(uri); folderMap[packagePath] = [resourceProvider.getFolder(path)]; }); resolvers.add(new PackageMapUriResolver(resourceProvider, folderMap)); } AnalysisContext context = AnalysisEngine.instance.createAnalysisContext() ..sourceFactory = new SourceFactory(resolvers) ..analysisOptions = createAnalysisOptions(options.strongMode); options.declaredVariables.forEach((String name, String value) { context.declaredVariables.define(name, value); }); return context; }