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