| // 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. |
| |
| package com.google.dart.compiler.resolver; |
| |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.common.collect.Sets; |
| import com.google.dart.compiler.DartCompilationError; |
| import com.google.dart.compiler.DartCompilerContext; |
| import com.google.dart.compiler.DartCompilerListener; |
| import com.google.dart.compiler.ErrorCode; |
| import com.google.dart.compiler.ast.ASTVisitor; |
| import com.google.dart.compiler.ast.DartClass; |
| import com.google.dart.compiler.ast.DartClassTypeAlias; |
| import com.google.dart.compiler.ast.DartField; |
| import com.google.dart.compiler.ast.DartFieldDefinition; |
| import com.google.dart.compiler.ast.DartFunctionTypeAlias; |
| import com.google.dart.compiler.ast.DartIdentifier; |
| import com.google.dart.compiler.ast.DartLibraryDirective; |
| import com.google.dart.compiler.ast.DartMethodDefinition; |
| import com.google.dart.compiler.ast.DartNode; |
| import com.google.dart.compiler.ast.DartTypeParameter; |
| import com.google.dart.compiler.ast.DartUnit; |
| import com.google.dart.compiler.ast.LibraryExport; |
| import com.google.dart.compiler.ast.LibraryImport; |
| import com.google.dart.compiler.ast.LibraryUnit; |
| import com.google.dart.compiler.ast.Modifiers; |
| import com.google.dart.compiler.common.SourceInfo; |
| import com.google.dart.compiler.type.Type; |
| import com.google.dart.compiler.type.TypeVariable; |
| import com.google.dart.compiler.type.Types; |
| import com.google.dart.compiler.util.apache.StringUtils; |
| |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Builds all class elements and types of a library. Once all libraries |
| * of an application have built their types, the library scope per |
| * library can be computed. |
| */ |
| public class TopLevelElementBuilder { |
| |
| public void exec(LibraryUnit library, DartCompilerContext context) { |
| assert library.getElement().getScope().isClear(); |
| for (DartUnit unit : library.getUnits()) { |
| unit.accept(new Builder(library.getElement())); |
| } |
| } |
| |
| public void exec(LibraryUnit library, DartUnit unit, DartCompilerContext context) { |
| unit.accept(new Builder(library.getElement())); |
| } |
| |
| public void exec(LibraryUnit library, DartClass cls, DartCompilerContext context) { |
| cls.accept(new Builder(library.getElement())); |
| } |
| |
| /** |
| * Fill the scope for this library, using its own top-level elements and elements from imported |
| * libraries. |
| */ |
| public void fillInLibraryScope(LibraryUnit library, DartCompilerListener listener) { |
| fillInLibraryScope(library, listener, Sets.newHashSet()); |
| } |
| |
| /** |
| * Fill the scope for this library, using its own top-level elements and elements from imported |
| * libraries. |
| */ |
| private void fillInLibraryScope(LibraryUnit library, DartCompilerListener listener, |
| Set<Object> processedObjects) { |
| Scope importScope = library.getElement().getImportScope(); |
| Scope scope = library.getElement().getScope(); |
| |
| // We are done with this library. |
| if (library.getElement().getScope().isStateReady()) { |
| return; |
| } |
| |
| // Fill "library" scope. |
| if (processedObjects.add(library)) { |
| List<Element> exportedElements = Lists.newArrayList(); |
| { |
| DartUnit selfUnit = library.getSelfDartUnit(); |
| if (selfUnit != null && !selfUnit.getDirectives().isEmpty() |
| && selfUnit.getDirectives().get(0) instanceof DartLibraryDirective) { |
| DartLibraryDirective libraryDirective = (DartLibraryDirective) selfUnit.getDirectives().get( |
| 0); |
| if (!libraryDirective.isObsoleteFormat()) { |
| String name = libraryDirective.getLibraryName(); |
| if (name != null) { |
| String elementName = "__library_" + name; |
| scope.declareElement(elementName, library.getElement()); |
| } |
| } |
| } |
| } |
| for (DartUnit unit : library.getUnits()) { |
| fillInUnitScope(unit, listener, scope, exportedElements); |
| } |
| // Remember exported elements. |
| for (Element exportedElement : exportedElements) { |
| Elements.addExportedElement(library.getElement(), exportedElement); |
| } |
| } |
| |
| // Fill "import" scope. |
| Map<String, LibraryPrefixElement> libraryPrefixElements = Maps.newHashMap(); |
| for (LibraryImport libraryImport : library.getImports()) { |
| if (!processedObjects.add(libraryImport)) { |
| continue; |
| } |
| LibraryUnit lib = libraryImport.getLibrary(); |
| // Prepare scope for this import. |
| Scope scopeForImport; |
| String prefix = libraryImport.getPrefix(); |
| { |
| if (prefix != null) { |
| // Put the prefix in the scope. |
| LibraryPrefixElement libraryPrefixElement = libraryPrefixElements.get(prefix); |
| if (libraryPrefixElement == null) { |
| libraryPrefixElement = new LibraryPrefixElementImplementation(prefix, scope); |
| libraryPrefixElements.put(prefix, libraryPrefixElement); |
| Element existingElement = scope.declareElement(prefix, libraryPrefixElement); |
| // Check for conflict between import prefix and top-level element. |
| if (existingElement != null) { |
| listener.onError(new DartCompilationError(existingElement.getNameLocation(), |
| ResolverErrorCode.CANNOT_HIDE_IMPORT_PREFIX, prefix)); |
| } |
| } |
| libraryPrefixElement.addLibrary(lib.getElement()); |
| // Fill prefix scope. |
| scopeForImport = libraryPrefixElement.getScope(); |
| } else { |
| scopeForImport = importScope; |
| } |
| } |
| // Prepare "lib" scope. |
| fillInLibraryScope(lib, listener, processedObjects); |
| // Fill "library" scope with element exported from "lib". |
| for (Element element : lib.getElement().getExportedElements()) { |
| String name = element.getName(); |
| if (libraryImport.isVisible(name)) { |
| Element oldElement = scopeForImport.declareElement(name, element); |
| if (oldElement != null) { |
| scopeForImport.declareElement(name, |
| Elements.createDuplicateElement(oldElement, element)); |
| } |
| } |
| } |
| } |
| |
| // Fill "library" export scope with re-exports. |
| for (LibraryExport export : library.getExports()) { |
| if (!processedObjects.add(export)) { |
| continue; |
| } |
| LibraryUnit lib = export.getLibrary(); |
| fillInLibraryScope(lib, listener, processedObjects); |
| for (Element element : lib.getElement().getExportedElements()) { |
| String name = element.getName(); |
| // re-export only if not defined locally |
| if (scope.findLocalElement(name) != null) { |
| continue; |
| } |
| // check if show/hide combinators of "export" are satisfied |
| if (!export.isVisible(name)) { |
| continue; |
| } |
| // do export |
| Element oldElement = Elements.addExportedElement(library.getElement(), element); |
| if (oldElement != null && oldElement.getEnclosingElement() instanceof LibraryElement) { |
| LibraryElement oldLibrary = (LibraryElement) oldElement.getEnclosingElement(); |
| SourceInfo sourceInfo = export.getSourceInfo(); |
| if (sourceInfo != null) { |
| String oldLibraryName = oldLibrary.getLibraryUnit().getName(); |
| listener.onError(new DartCompilationError(sourceInfo, |
| ResolverErrorCode.DUPLICATE_EXPORTED_NAME, Elements.getUserElementTitle(oldElement), |
| oldLibraryName)); |
| } |
| } |
| } |
| } |
| |
| // Done. |
| library.getElement().getScope().markStateReady(); |
| } |
| |
| @VisibleForTesting |
| public void fillInUnitScope(DartUnit unit, DartCompilerListener listener, Scope scope, |
| List<Element> exportedElements) { |
| for (DartNode node : unit.getTopLevelNodes()) { |
| if (node instanceof DartFieldDefinition) { |
| for (DartField field : ((DartFieldDefinition) node).getFields()) { |
| declareNodeInScope(field, listener, scope, exportedElements); |
| } |
| } else { |
| declareNodeInScope(node, listener, scope, exportedElements); |
| } |
| } |
| } |
| |
| void declareNodeInScope(DartNode node, DartCompilerListener listener, Scope scope, |
| List<Element> exportedElements) { |
| Element element = node.getElement(); |
| String name = element.getName(); |
| declare(element, listener, scope); |
| if (exportedElements != null && !DartIdentifier.isPrivateName(name)) { |
| exportedElements.add(element); |
| } |
| } |
| |
| private static void compilationError(DartCompilerListener listener, SourceInfo node, ErrorCode errorCode, |
| Object... args) { |
| DartCompilationError error = new DartCompilationError(node, errorCode, args); |
| listener.onError(error); |
| } |
| |
| private void declare(Element newElement, DartCompilerListener listener, Scope scope) { |
| String name = newElement.getName(); |
| Element oldElement = scope.findLocalElement(name); |
| if (oldElement == null && newElement instanceof FieldElement) { |
| FieldElement eField = (FieldElement) newElement; |
| if (!eField.getModifiers().isAbstractField()) { |
| oldElement = scope.findLocalElement("setter " + name); |
| } |
| if (eField.getModifiers().isAbstractField() |
| && StringUtils.startsWith(name, "setter ")) { |
| Element other2 = scope.findLocalElement(StringUtils.removeStart(name, "setter ")); |
| if (other2 instanceof FieldElement) { |
| FieldElement otherField = (FieldElement) other2; |
| if (!otherField.getModifiers().isAbstractField()) { |
| oldElement = otherField; |
| } |
| } |
| } |
| } |
| if (oldElement != null) { |
| reportDuplicateDeclaration(listener, oldElement, newElement); |
| reportDuplicateDeclaration(listener, newElement, oldElement); |
| } |
| scope.declareElement(name, newElement); |
| } |
| |
| /** |
| * Reports {@link ResolverErrorCode#DUPLICATE_TOP_LEVEL_DECLARATION} for given named element. |
| */ |
| private void reportDuplicateDeclaration(DartCompilerListener listener, Element element, |
| Element otherElement) { |
| compilationError(listener, element.getNameLocation(), |
| ResolverErrorCode.DUPLICATE_TOP_LEVEL_DECLARATION, |
| Elements.getUserElementTitle(otherElement), |
| Elements.getRelativeElementLocation(element, otherElement)); |
| } |
| |
| /** |
| * Creates a ClassElement for a class. |
| */ |
| private class Builder extends ASTVisitor<Void> { |
| |
| private LibraryElement library; |
| |
| public Builder(LibraryElement library) { |
| this.library = library; |
| } |
| |
| @Override |
| public Void visitClass(DartClass node) { |
| ClassElement element = Elements.classFromNode(node, library); |
| List<DartTypeParameter> parameterNodes = node.getTypeParameters(); |
| List<TypeVariable> typeVariables = Elements.makeTypeVariables(parameterNodes, element); |
| element.setType(Types.interfaceType( |
| element, |
| Collections.<Type>unmodifiableList(typeVariables))); |
| node.setElement(element); |
| node.getName().setElement(element); |
| return null; |
| } |
| |
| @Override |
| public Void visitClassTypeAlias(DartClassTypeAlias node) { |
| ClassAliasElement element = Elements.classFromNode(node, library); |
| List<DartTypeParameter> parameterNodes = node.getTypeParameters(); |
| List<TypeVariable> typeVariables = Elements.makeTypeVariables(parameterNodes, element); |
| element.setType(Types.interfaceType( |
| element, |
| Collections.<Type>unmodifiableList(typeVariables))); |
| node.setElement(element); |
| node.getName().setElement(element); |
| return null; |
| } |
| |
| @Override |
| public Void visitFunctionTypeAlias(DartFunctionTypeAlias node) { |
| FunctionAliasElement element = Elements.functionTypeAliasFromNode(node, library); |
| List<DartTypeParameter> parameterNodes = node.getTypeParameters(); |
| element.setType(Types.functionAliasType(element, |
| Elements.makeTypeVariables(parameterNodes, element))); |
| node.getName().setElement(element); |
| node.setElement(element); |
| return null; |
| } |
| |
| @Override |
| public Void visitMethodDefinition(DartMethodDefinition node) { |
| node.setElement(Elements.methodFromMethodNode(node, library)); |
| return null; |
| } |
| |
| @Override |
| public Void visitField(DartField node) { |
| Modifiers modifiers = node.getModifiers(); |
| node.setElement(Elements.fieldFromNode(node, library, node.getObsoleteMetadata(), modifiers)); |
| return null; |
| } |
| } |
| } |