1/* 2 * Copyright 2016 Google Inc. All Rights Reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.google.turbine.binder; 18 19import com.google.common.base.Joiner; 20import com.google.common.base.Optional; 21import com.google.common.collect.ImmutableList; 22import com.google.common.collect.ImmutableMap; 23import com.google.common.collect.ImmutableSet; 24import com.google.common.collect.Iterables; 25import com.google.turbine.binder.bound.SourceBoundClass; 26import com.google.turbine.binder.sym.ClassSymbol; 27import com.google.turbine.diag.SourceFile; 28import com.google.turbine.model.TurbineFlag; 29import com.google.turbine.model.TurbineTyKind; 30import com.google.turbine.tree.Tree; 31import com.google.turbine.tree.Tree.CompUnit; 32import com.google.turbine.tree.Tree.ImportDecl; 33import com.google.turbine.tree.Tree.PkgDecl; 34import com.google.turbine.tree.Tree.TyDecl; 35import com.google.turbine.tree.TurbineModifier; 36import java.util.List; 37 38/** 39 * Processes compilation units before binding, creating symbols for type declarations and desugaring 40 * access modifiers. 41 */ 42public class CompUnitPreprocessor { 43 44 /** A pre-processed compilation unit. */ 45 public static class PreprocessedCompUnit { 46 private final ImmutableList<Tree.ImportDecl> imports; 47 private final ImmutableList<SourceBoundClass> types; 48 private final SourceFile source; 49 private final String packageName; 50 51 public PreprocessedCompUnit( 52 ImmutableList<ImportDecl> imports, 53 ImmutableList<SourceBoundClass> types, 54 SourceFile source, 55 String packageName) { 56 this.imports = imports; 57 this.types = types; 58 this.source = source; 59 this.packageName = packageName; 60 } 61 62 public ImmutableList<ImportDecl> imports() { 63 return imports; 64 } 65 66 public ImmutableList<SourceBoundClass> types() { 67 return types; 68 } 69 70 public SourceFile source() { 71 return source; 72 } 73 74 public String packageName() { 75 return packageName; 76 } 77 } 78 79 public static ImmutableList<PreprocessedCompUnit> preprocess(List<CompUnit> units) { 80 ImmutableList.Builder<PreprocessedCompUnit> result = ImmutableList.builder(); 81 for (CompUnit unit : units) { 82 result.add(preprocess(unit)); 83 } 84 return result.build(); 85 } 86 87 public static PreprocessedCompUnit preprocess(CompUnit unit) { 88 String packageName; 89 Iterable<TyDecl> decls = unit.decls(); 90 if (unit.pkg().isPresent()) { 91 packageName = Joiner.on('/').join(unit.pkg().get().name()); 92 // "While the file could technically contain the source code 93 // for one or more package-private (default-access) classes, 94 // it would be very bad form." -- JLS 7.4.1 95 if (!unit.pkg().get().annos().isEmpty()) { 96 decls = Iterables.concat(decls, ImmutableList.of(packageInfoTree(unit.pkg().get()))); 97 } 98 } else { 99 packageName = ""; 100 } 101 ImmutableList.Builder<SourceBoundClass> types = ImmutableList.builder(); 102 for (TyDecl decl : decls) { 103 ClassSymbol sym = 104 new ClassSymbol((!packageName.isEmpty() ? packageName + "/" : "") + decl.name()); 105 int access = access(decl.mods(), decl.tykind()); 106 ImmutableMap<String, ClassSymbol> children = 107 preprocessChildren(types, sym, decl.members(), access); 108 types.add(new SourceBoundClass(sym, null, children, access, decl)); 109 } 110 return new PreprocessedCompUnit(unit.imports(), types.build(), unit.source(), packageName); 111 } 112 113 private static ImmutableMap<String, ClassSymbol> preprocessChildren( 114 ImmutableList.Builder<SourceBoundClass> types, 115 ClassSymbol owner, 116 ImmutableList<Tree> members, 117 int enclosing) { 118 ImmutableMap.Builder<String, ClassSymbol> result = ImmutableMap.builder(); 119 for (Tree member : members) { 120 if (member.kind() == Tree.Kind.TY_DECL) { 121 Tree.TyDecl decl = (Tree.TyDecl) member; 122 ClassSymbol sym = new ClassSymbol(owner.binaryName() + '$' + decl.name()); 123 result.put(decl.name(), sym); 124 125 int access = innerClassAccess(enclosing, decl); 126 127 ImmutableMap<String, ClassSymbol> children = 128 preprocessChildren(types, sym, decl.members(), access); 129 types.add(new SourceBoundClass(sym, owner, children, access, decl)); 130 } 131 } 132 return result.build(); 133 } 134 135 /** Desugars access flags for a class. */ 136 public static int access(ImmutableSet<TurbineModifier> mods, TurbineTyKind tykind) { 137 int access = 0; 138 for (TurbineModifier m : mods) { 139 access |= m.flag(); 140 } 141 switch (tykind) { 142 case CLASS: 143 access |= TurbineFlag.ACC_SUPER; 144 break; 145 case INTERFACE: 146 access |= TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_INTERFACE; 147 break; 148 case ENUM: 149 // Assuming all enums are final is safe, because nothing outside 150 // the compilation unit can extend abstract enums anyways, and 151 // refactoring an existing enum to implement methods in the container 152 // class instead of the constants is not a breaking change. 153 access |= TurbineFlag.ACC_SUPER | TurbineFlag.ACC_ENUM | TurbineFlag.ACC_FINAL; 154 break; 155 case ANNOTATION: 156 access |= TurbineFlag.ACC_ABSTRACT | TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION; 157 break; 158 } 159 return access; 160 } 161 162 /** Desugars access flags for an inner class. */ 163 private static int innerClassAccess(int enclosing, TyDecl decl) { 164 int access = access(decl.mods(), decl.tykind()); 165 166 // types declared in interfaces and annotations are implicitly public (JLS 9.5) 167 if ((enclosing & (TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION)) != 0) { 168 access &= ~(TurbineFlag.ACC_PRIVATE | TurbineFlag.ACC_PROTECTED); 169 access |= TurbineFlag.ACC_PUBLIC; 170 } 171 172 // Nested enums, interfaces, and annotations, and any types nested within interfaces and 173 // annotations (JLS 9.5) are implicitly static. 174 switch (decl.tykind()) { 175 case INTERFACE: 176 case ENUM: 177 case ANNOTATION: 178 access |= TurbineFlag.ACC_STATIC; 179 break; 180 case CLASS: 181 if ((enclosing & (TurbineFlag.ACC_INTERFACE | TurbineFlag.ACC_ANNOTATION)) != 0) { 182 access |= TurbineFlag.ACC_STATIC; 183 } 184 } 185 186 // propagate strictfp to nested types 187 access |= (enclosing & TurbineFlag.ACC_STRICT); 188 return access; 189 } 190 191 /** package-info.java's are desugared into synthetic class declarations. */ 192 private static TyDecl packageInfoTree(PkgDecl pkgDecl) { 193 return new TyDecl( 194 pkgDecl.position(), 195 ImmutableSet.of(TurbineModifier.ACC_SYNTHETIC), 196 pkgDecl.annos(), 197 "package-info", 198 ImmutableList.of(), 199 Optional.absent(), 200 ImmutableList.of(), 201 ImmutableList.of(), 202 TurbineTyKind.INTERFACE); 203 } 204} 205