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 //qualified this: 208 runTest("public class Test {\n" + 209 " Integer i;\n" + 210 " class Inner1 {\n" + 211 " class Inner2 {\n" + 212 " private Integer dump() {\n" + 213 " return Test.this.i++;\n" + 214 " }\n" + 215 " }\n" + 216 " }\n" + 217 "}", 218 "Test.Inner1.Inner2.dump()java.lang.Integer\n" + 219 "{\n" + 220 " return (let /*synthetic*/ final Integer $le0 = (Integer)this$1.this$0.i" + 221 " in (let /*synthetic*/ final Integer $le1 = this$1.this$0.i = " + 222 "Integer.valueOf((int)(this$1.this$0.i.intValue() + 1)) " + 223 "in $le0));\n" + 224 "}\n" 225 ); 226 } 227 228 private final ToolBox tb = new ToolBox(); 229 230 private void runTest(String code, String expectedDesugar) throws Exception { 231 List<JavaFileObject> files = List.nil(); 232 233 for (String file : code.split("---")) { 234 files = files.prepend(new ToolBox.JavaSource(file)); 235 } 236 237 Path classes = Paths.get("classes"); 238 239 if (Files.exists(classes)) { 240 tb.cleanDirectory(classes); 241 } else { 242 Files.createDirectories(classes); 243 } 244 245 JavacTool compiler = (JavacTool) ToolProvider.getSystemJavaCompiler(); 246 StringWriter out = new StringWriter(); 247 Context context = new Context(); 248 TestLower.preRegister(context); 249 Iterable<String> options = Arrays.asList("-d", classes.toString()); 250 JavacTask task = (JavacTask) compiler.getTask(out, null, null, options, null, files, context); 251 252 task.generate(); 253 254 out.flush(); 255 256 String actual = out.toString().replace(System.getProperty("line.separator"), "\n"); 257 258 if (!expectedDesugar.equals(actual)) { 259 throw new IllegalStateException("Actual does not match expected: " + actual); 260 } 261 } 262 263 private static final class TestLower extends Lower { 264 265 public static void preRegister(Context context) { 266 context.put(lowerKey, new Context.Factory<Lower>() { 267 public Lower make(Context c) { 268 return new TestLower(c); 269 } 270 }); 271 } 272 273 private final TreeMaker make; 274 private final Names names; 275 private final Log log; 276 277 public TestLower(Context context) { 278 super(context); 279 make = TreeMaker.instance(context); 280 names = Names.instance(context); 281 log = Log.instance(context); 282 } 283 284 @Override 285 public List<JCTree> translateTopLevelClass(Env<AttrContext> env, JCTree cdef, TreeMaker make) { 286 List<JCTree> result = super.translateTopLevelClass(env, cdef, make); 287 Map<Symbol, JCMethodDecl> declarations = new HashMap<>(); 288 Set<Symbol> toDump = new TreeSet<>(symbolComparator); 289 290 new TreeScanner() { 291 @Override 292 public void visitMethodDef(JCMethodDecl tree) { 293 if (tree.name.toString().startsWith("dump")) { 294 toDump.add(tree.sym); 295 } 296 declarations.put(tree.sym, tree); 297 super.visitMethodDef(tree); 298 } 299 }.scan(result); 300 301 for (Symbol d : toDump) { 302 dump(d, declarations, new HashSet<>()); 303 } 304 305 return result; 306 } 307 308 private void dump(Symbol methodSym, Map<Symbol, JCMethodDecl> declarations, Set<Symbol> alreadyPrinted) { 309 if (!alreadyPrinted.add(methodSym)) 310 return ; 311 312 JCMethodDecl method = declarations.get(methodSym); 313 314 if (method == null) { 315 return ; 316 } 317 318 log.getWriter(WriterKind.NOTICE).println(symbol2String(methodSym)); 319 320 JCBlock body = new TreeCopier<Void>(make) { 321 private final Map<String, String> letExprRemap = new HashMap<>(); 322 private int i; 323 324 @Override 325 public JCTree visitOther(Tree node, Void p) { 326 JCTree tree = (JCTree) node; 327 if (tree.hasTag(Tag.LETEXPR)) { 328 LetExpr le = (LetExpr) tree; 329 330 for (JCVariableDecl var : le.defs) { 331 letExprRemap.put(var.name.toString(), "$le" + i++); 332 } 333 } 334 return super.visitOther(node, p); 335 } 336 337 @Override 338 public JCTree visitVariable(VariableTree node, Void p) { 339 String newName = letExprRemap.get(node.getName().toString()); 340 if (newName != null) { 341 node = make.VarDef((JCModifiers) node.getModifiers(), names.fromString(newName), (JCExpression) node.getType(), (JCExpression) node.getInitializer()); 342 } 343 return super.visitVariable(node, p); 344 } 345 346 @Override 347 public JCTree visitIdentifier(IdentifierTree node, Void p) { 348 String newName = letExprRemap.get(node.getName().toString()); 349 if (newName != null) { 350 node = make.Ident(names.fromString(newName)); 351 } 352 return super.visitIdentifier(node, p); 353 } 354 355 @Override 356 public <T extends JCTree> T copy(T tree, Void p) { 357 if (tree.hasTag(Tag.LETEXPR)) { 358 return (T) visitOther(tree, p); 359 } 360 return super.copy(tree, p); 361 } 362 363 }.copy(method.body); 364 log.getWriter(WriterKind.NOTICE).println(body.toString()); 365 366 Set<Symbol> invoked = new TreeSet<>(symbolComparator); 367 368 new TreeScanner() { 369 @Override 370 public void visitApply(JCMethodInvocation tree) { 371 invoked.add(TreeInfo.symbol(tree.meth)); 372 super.visitApply(tree); 373 } 374 }.scan(method); 375 376 for (Symbol search : invoked) { 377 dump(search, declarations, alreadyPrinted); 378 } 379 } 380 381 private String symbol2String(Symbol sym) { 382 switch (sym.kind) { 383 case TYP: 384 return sym.getQualifiedName().toString(); 385 case MTH: 386 return symbol2String(sym.owner) + "." + sym.name + sym.type.toString(); 387 default: 388 throw new UnsupportedOperationException(); 389 } 390 } 391 392 private final Comparator<Symbol> symbolComparator = (s1, s2) -> symbol2String(s1).compareTo(symbol2String(s2)); 393 } 394 395 }