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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26package jdk.jshell; 27 28import java.io.IOException; 29import java.io.StringWriter; 30import com.sun.source.tree.ClassTree; 31import com.sun.source.tree.MethodTree; 32import com.sun.source.tree.Tree; 33import com.sun.tools.javac.code.Flags; 34import com.sun.tools.javac.tree.JCTree; 35import com.sun.tools.javac.tree.JCTree.JCBlock; 36import com.sun.tools.javac.tree.JCTree.JCClassDecl; 37import com.sun.tools.javac.tree.JCTree.JCExpression; 38import com.sun.tools.javac.tree.JCTree.JCMethodDecl; 39import com.sun.tools.javac.tree.JCTree.JCNewClass; 40import com.sun.tools.javac.tree.JCTree.JCStatement; 41import com.sun.tools.javac.tree.JCTree.JCVariableDecl; 42import com.sun.tools.javac.tree.Pretty; 43import com.sun.tools.javac.tree.TreeMaker; 44import com.sun.tools.javac.util.Context; 45import com.sun.tools.javac.util.List; 46import com.sun.tools.javac.util.ListBuffer; 47import com.sun.tools.javac.util.Names; 48import static com.sun.tools.javac.code.Flags.STATIC; 49import static com.sun.tools.javac.code.Flags.INTERFACE; 50import static com.sun.tools.javac.code.Flags.ENUM; 51import static com.sun.tools.javac.code.Flags.PUBLIC; 52import com.sun.tools.javac.util.Name; 53import jdk.jshell.spi.SPIResolutionException; 54 55/** 56 * Produce a corralled version of the Wrap for a snippet. 57 * Incoming tree is mutated. 58 * 59 * @author Robert Field 60 */ 61class Corraller extends Pretty { 62 63 private final StringWriter out; 64 private final int keyIndex; 65 private final TreeMaker make; 66 private final Names names; 67 private JCBlock resolutionExceptionBlock; 68 69 public Corraller(int keyIndex, Context context) { 70 this(new StringWriter(), keyIndex, context); 71 } 72 73 private Corraller(StringWriter out, int keyIndex, Context context) { 74 super(out, false); 75 this.out = out; 76 this.keyIndex = keyIndex; 77 this.make = TreeMaker.instance(context); 78 this.names = Names.instance(context); 79 } 80 81 public Wrap corralType(ClassTree ct) { 82 ((JCClassDecl) ct).mods.flags |= Flags.STATIC | Flags.PUBLIC; 83 return corral(ct); 84 } 85 86 public Wrap corralMethod(MethodTree mt) { 87 ((JCMethodDecl) mt).mods.flags |= Flags.STATIC | Flags.PUBLIC; 88 return corral(mt); 89 } 90 91 private Wrap corral(Tree tree) { 92 try { 93 printStat((JCTree) tree); 94 } catch (IOException e) { 95 throw new AssertionError(e); 96 } 97 return Wrap.simpleWrap(out.toString()); 98 } 99 100 @Override 101 public void visitBlock(JCBlock tree) { 102 // Top-level executable blocks (usually method bodies) are corralled 103 super.visitBlock((tree.flags & STATIC) != 0 104 ? tree 105 : resolutionExceptionBlock()); 106 } 107 108 @Override 109 public void visitVarDef(JCVariableDecl tree) { 110 // No field inits in corralled classes 111 tree.init = null; 112 super.visitVarDef(tree); 113 } 114 115 @Override 116 public void visitClassDef(JCClassDecl tree) { 117 if ((tree.mods.flags & (INTERFACE | ENUM)) == 0 && 118 !tree.getMembers().stream() 119 .anyMatch(t -> t.getKind() == Tree.Kind.METHOD && 120 ((MethodTree) t).getName() == tree.name.table.names.init)) { 121 // Generate a default constructor, since 122 // this is a regular class and there are no constructors 123 ListBuffer<JCTree> ndefs = new ListBuffer<>(); 124 ndefs.addAll(tree.defs); 125 ndefs.add(make.MethodDef(make.Modifiers(PUBLIC), 126 tree.name.table.names.init, 127 null, List.nil(), List.nil(), List.nil(), 128 resolutionExceptionBlock(), null)); 129 tree.defs = ndefs.toList(); 130 } 131 super.visitClassDef(tree); 132 } 133 134 // Build a compiler tree for an exception throwing block, e.g.: 135 // { 136 // throw new jdk.jshell.spi.SPIResolutionException(9); 137 // } 138 private JCBlock resolutionExceptionBlock() { 139 if (resolutionExceptionBlock == null) { 140 JCExpression expClass = null; 141 // Split the exception class name at dots 142 for (String id : SPIResolutionException.class.getName().split("\\.")) { 143 Name nm = names.fromString(id); 144 if (expClass == null) { 145 expClass = make.Ident(nm); 146 } else { 147 expClass = make.Select(expClass, nm); 148 } 149 } 150 JCNewClass exp = make.NewClass(null, 151 null, expClass, List.of(make.Literal(keyIndex)), null); 152 resolutionExceptionBlock = make.Block(0L, List.of(make.Throw(exp))); 153 } 154 return resolutionExceptionBlock; 155 } 156} 157