1 /* 2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /** 25 * @test 26 * @library /tools/lib 27 * @modules jdk.compiler/com.sun.tools.javac.api 28 * jdk.compiler/com.sun.tools.javac.code 29 * jdk.compiler/com.sun.tools.javac.comp 30 * jdk.compiler/com.sun.tools.javac.tree 31 * jdk.compiler/com.sun.tools.javac.util 32 */ 33 34 import java.io.StringWriter; 35 import java.nio.file.Files; 36 import java.nio.file.Path; 37 import java.nio.file.Paths; 38 import java.util.Arrays; 39 import java.util.Comparator; 40 import java.util.HashMap; 41 import java.util.HashSet; 42 import java.util.Map; 43 import java.util.Set; 44 import java.util.TreeSet; 45 46 import javax.tools.JavaFileObject; 47 import javax.tools.ToolProvider; 48 49 import com.sun.source.tree.IdentifierTree; 50 import com.sun.source.tree.Tree; 51 import com.sun.source.tree.VariableTree; 52 import com.sun.source.util.JavacTask; 53 import com.sun.tools.javac.api.JavacTool; 54 import com.sun.tools.javac.code.Symbol; 55 import com.sun.tools.javac.comp.AttrContext; 56 import com.sun.tools.javac.comp.Env; 57 import com.sun.tools.javac.comp.Lower; 58 import com.sun.tools.javac.tree.JCTree; 59 import com.sun.tools.javac.tree.JCTree.JCBlock; 60 import com.sun.tools.javac.tree.JCTree.JCExpression; 61 import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 62 import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; 63 import com.sun.tools.javac.tree.JCTree.JCModifiers; 64 import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 65 import com.sun.tools.javac.tree.JCTree.LetExpr; 66 import com.sun.tools.javac.tree.JCTree.Tag; 67 import com.sun.tools.javac.tree.TreeCopier; 68 import com.sun.tools.javac.tree.TreeInfo; 69 import com.sun.tools.javac.tree.TreeMaker; 70 import com.sun.tools.javac.tree.TreeScanner; 71 import com.sun.tools.javac.util.Context; 72 import com.sun.tools.javac.util.List; 73 import com.sun.tools.javac.util.Log; 74 import com.sun.tools.javac.util.Log.WriterKind; 75 import com.sun.tools.javac.util.Names; 76 77 import toolbox.ToolBox; 78 79 public class BoxingAndSuper { 80 public static void main(String... args) throws Exception { 81 new BoxingAndSuper().testSuper(); 82 new BoxingAndSuper().testThis(); 83 } 84 85 public void testSuper() throws Exception { 86 //super, same package: 87 runTest("package p;\n" + 88 "class Test extends Parent {\n" + 89 " protected Integer i=20;\n" + 90 " private Integer dump() {\n" + 91 " return super.i++;\n" + 92 " }\n" + 93 "}\n" + 94 "---" + 95 "package p;\n" + 96 "class Parent {\n" + 97 " protected Integer i=10;\n" + 98 "} ", 99 "p.Test.dump()java.lang.Integer\n" + 100 "{\n" + 101 " return (let /*synthetic*/ final Integer $le0 = (Integer)super.i " + 102 "in (let /*synthetic*/ final Integer $le1 = super.i = Integer.valueOf((int)(super.i.intValue() + 1)) " + 103 "in $le0));\n" + 104 "}\n"); 105 //qualified super, same package: 106 runTest("package p;\n" + 107 "class Test extends Parent {\n" + 108 " protected Integer i=20;\n" + 109 " class Inner {\n" + 110 " private Integer dump() {\n" + 111 " return Test.super.i++;\n" + 112 " }\n" + 113 " }\n" + 114 "}\n" + 115 "---" + 116 "package p;\n" + 117 "class Parent {\n" + 118 " protected Integer i=10;\n" + 119 "} ", 120 "p.Test.Inner.dump()java.lang.Integer\n" + 121 "{\n" + 122 " return (let /*synthetic*/ final Integer $le0 = (Integer)Test.access$001(this$0) " + 123 "in (let /*synthetic*/ final Integer $le1 = Test.access$103(this$0, Integer.valueOf((int)(Test.access$201(this$0).intValue() + 1))) " + 124 "in $le0));\n" + 125 "}\n" + 126 "p.Test.access$001(p.Test)java.lang.Integer\n" + 127 "{\n" + 128 " return x0.i;\n" + 129 "}\n" + 130 "p.Test.access$103(p.Test,java.lang.Integer)java.lang.Integer\n" + 131 "{\n" + 132 " return x0.i = x1;\n" + 133 "}\n" + 134 "p.Test.access$201(p.Test)java.lang.Integer\n" + 135 "{\n" + 136 " return x0.i;\n" + 137 "}\n"); 138 //super, different packages: 139 runTest("package p1;\n" + 140 "class Test extends p2.Parent {\n" + 141 " protected Integer i=20;\n" + 142 " private Integer dump() {\n" + 143 " return super.i++;\n" + 144 " }\n" + 145 "}\n" + 146 "---" + 147 "package p2;\n" + 148 "public class Parent {\n" + 149 " protected Integer i=10;\n" + 150 "} ", 151 "p1.Test.dump()java.lang.Integer\n" + 152 "{\n" + 153 " return (let /*synthetic*/ final Integer $le0 = (Integer)super.i " + 154 "in (let /*synthetic*/ final Integer $le1 = super.i = Integer.valueOf((int)(super.i.intValue() + 1)) " + 155 "in $le0));\n" + 156 "}\n"); 157 //qualified super, different packages: 158 runTest("package p1;\n" + 159 "class Test extends p2.Parent {\n" + 160 " protected Integer i=20;\n" + 161 " class Inner {\n" + 162 " private Integer dump() {\n" + 163 " return Test.super.i++;\n" + 164 " }\n" + 165 " }\n" + 166 "}\n" + 167 "---" + 168 "package p2;\n" + 169 "public class Parent {\n" + 170 " protected Integer i=10;\n" + 171 "} ", 172 "p1.Test.Inner.dump()java.lang.Integer\n" + 173 "{\n" + 174 " return (let /*synthetic*/ final Integer $le0 = (Integer)Test.access$001(this$0) " + 175 "in (let /*synthetic*/ final Integer $le1 = Test.access$103(this$0, Integer.valueOf((int)(Test.access$201(this$0).intValue() + 1))) " + 176 "in $le0));\n" + 177 "}\n" + 178 "p1.Test.access$001(p1.Test)java.lang.Integer\n" + 179 "{\n" + 180 " return x0.i;\n" + 181 "}\n" + 182 "p1.Test.access$103(p1.Test,java.lang.Integer)java.lang.Integer\n" + 183 "{\n" + 184 " return x0.i = x1;\n" + 185 "}\n" + 186 "p1.Test.access$201(p1.Test)java.lang.Integer\n" + 187 "{\n" + 188 " return x0.i;\n" + 189 "}\n"); 190 } 191 192 public void testThis() throws Exception { 193 String code = "public class Test {\n" + 194 " Integer i;\n" + 195 " private void dump() {\n" + 196 " i++;\n" + 197 " this.i++;\n" + 198 " }\n" + 199 "}"; 200 String expected = 201 "Test.dump()void\n" + 202 "{\n" + 203 " (let /*synthetic*/ final Integer $le0 = i in (let /*synthetic*/ final Integer $le1 = i = Integer.valueOf((int)(i.intValue() + 1)) in $le0));\n" + 204 " (let /*synthetic*/ final Integer $le2 = (Integer)this.i in (let /*synthetic*/ final Integer $le3 = this.i = Integer.valueOf((int)(this.i.intValue() + 1)) in $le2));\n" + 205 "}\n"; 206 runTest(code, expected); 207 } 208 209 private final ToolBox tb = new ToolBox(); 210 211 private void runTest(String code, String expectedDesugar) throws Exception { 212 List<JavaFileObject> files = List.nil(); 213 214 for (String file : code.split("---")) { 215 files = files.prepend(new ToolBox.JavaSource(file)); 216 } 217 218 Path classes = Paths.get("classes"); 219 220 if (Files.exists(classes)) { 221 tb.cleanDirectory(classes); 222 } else { 223 Files.createDirectories(classes); 224 } 225 226 JavacTool compiler = (JavacTool) ToolProvider.getSystemJavaCompiler(); 227 StringWriter out = new StringWriter(); 228 Context context = new Context(); 229 TestLower.preRegister(context); 230 Iterable<String> options = Arrays.asList("-d", classes.toString()); 231 JavacTask task = (JavacTask) compiler.getTask(out, null, null, options, null, files, context); 232 233 task.generate(); 234 235 out.flush(); 236 237 String actual = out.toString(); 238 239 if (!expectedDesugar.equals(actual)) { 240 throw new IllegalStateException("Actual does not match expected: " + actual); 241 } 242 } 243 244 private static final class TestLower extends Lower { 245 246 public static void preRegister(Context context) { 247 context.put(lowerKey, new Context.Factory<Lower>() { 248 public Lower make(Context c) { 249 return new TestLower(c); 250 } 251 }); 252 } 253 254 private final TreeMaker make; 255 private final Names names; 256 private final Log log; 257 258 public TestLower(Context context) { 259 super(context); 260 make = TreeMaker.instance(context); 261 names = Names.instance(context); 262 log = Log.instance(context); 263 } 264 265 @Override 266 public List<JCTree> translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 267 List<JCTree> result = super.translateTopLevelClass(env, cdef, make); 268 Map<Symbol, JCMethodDecl> declarations = new HashMap<>(); 269 Set<Symbol> toDump = new TreeSet<>(symbolComparator); 270 271 new TreeScanner() { 272 @Override 273 public void visitMethodDef(JCMethodDecl tree) { 274 if (tree.name.toString().startsWith("dump")) { 275 toDump.add(tree.sym); 276 } 277 declarations.put(tree.sym, tree); 278 super.visitMethodDef(tree); 279 } 280 }.scan(result); 281 282 for (Symbol d : toDump) { 283 dump(d, declarations, new HashSet<>()); 284 } 285 286 return result; 287 } 288 289 private void dump(Symbol methodSym, Map<Symbol, JCMethodDecl> declarations, Set<Symbol> alreadyPrinted) { 290 if (!alreadyPrinted.add(methodSym)) 291 return ; 292 293 JCMethodDecl method = declarations.get(methodSym); 294 295 if (method == null) { 296 return ; 297 } 298 299 log.getWriter(WriterKind.NOTICE).println(symbol2String(methodSym)); 300 301 JCBlock body = new TreeCopier<Void>(make) { 302 private final Map<String, String> letExprRemap = new HashMap<>(); 303 private int i; 304 305 @Override 306 public JCTree visitOther(Tree node, Void p) { 307 JCTree tree = (JCTree) node; 308 if (tree.hasTag(Tag.LETEXPR)) { 309 LetExpr le = (LetExpr) tree; 310 311 for (JCVariableDecl var : le.defs) { 312 letExprRemap.put(var.name.toString(), "$le" + i++); 313 } 314 } 315 return super.visitOther(node, p); 316 } 317 318 @Override 319 public JCTree visitVariable(VariableTree node, Void p) { 320 String newName = letExprRemap.get(node.getName().toString()); 321 if (newName != null) { 322 node = make.VarDef((JCModifiers) node.getModifiers(), names.fromString(newName), (JCExpression) node.getType(), (JCExpression) node.getInitializer()); 323 } 324 return super.visitVariable(node, p); 325 } 326 327 @Override 328 public JCTree visitIdentifier(IdentifierTree node, Void p) { 329 String newName = letExprRemap.get(node.getName().toString()); 330 if (newName != null) { 331 node = make.Ident(names.fromString(newName)); 332 } 333 return super.visitIdentifier(node, p); 334 } 335 336 @Override 337 public <T extends JCTree> T copy(T tree, Void p) { 338 if (tree.hasTag(Tag.LETEXPR)) { 339 return (T) visitOther(tree, p); 340 } 341 return super.copy(tree, p); 342 } 343 344 }.copy(method.body); 345 log.getWriter(WriterKind.NOTICE).println(body.toString()); 346 347 Set<Symbol> invoked = new TreeSet<>(symbolComparator); 348 349 new TreeScanner() { 350 @Override 351 public void visitApply(JCMethodInvocation tree) { 352 invoked.add(TreeInfo.symbol(tree.meth)); 353 super.visitApply(tree); 354 } 355 }.scan(method); 356 357 for (Symbol search : invoked) { 358 dump(search, declarations, alreadyPrinted); 359 } 360 } 361 362 private String symbol2String(Symbol sym) { 363 switch (sym.kind) { 364 case TYP: 365 return sym.getQualifiedName().toString(); 366 case MTH: 367 return symbol2String(sym.owner) + "." + sym.name + sym.type.toString(); 368 default: 369 throw new UnsupportedOperationException(); 370 } 371 } 372 373 private final Comparator<Symbol> symbolComparator = (s1, s2) -> symbol2String(s1).compareTo(symbol2String(s2)); 374 } 375 376 }