1 /*
  2  *  Copyright (c) 2020, 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  */
 26 
 27 package jdk.internal.jextract.impl;
 28 
 29 import java.lang.constant.Constable;
 30 import java.util.Set;
 31 import java.util.stream.Collectors;
 32 import jdk.incubator.foreign.MemoryLayout;
 33 import jdk.incubator.jextract.Declaration;
 34 import jdk.incubator.jextract.Position;
 35 import jdk.incubator.jextract.Type;
 36 
 37 public class PrettyPrinter implements Declaration.Visitor<Void, Void> {
 38 
 39     private static String SPACES = " ".repeat(92);
 40     int align = 0;
 41 
 42     void incr() {
 43         align += 4;
 44     }
 45 
 46     void decr() {
 47         align -= 4;
 48     }
 49 
 50     void indent() {
 51         builder.append(SPACES.substring(0, align));
 52     }
 53 
 54     StringBuilder builder = new StringBuilder();
 55 
 56     private void getAttributes(Declaration decl) {
 57         Set<String> attrs = decl.attributeNames();
 58         if (attrs.isEmpty()) {
 59             return;
 60         }
 61         incr();
 62         indent();
 63         for (String k: attrs) {
 64             builder.append("Attr: ");
 65             builder.append(k);
 66             builder.append(" -> [");
 67             builder.append(decl.getAttribute(k).get().stream()
 68                 .map(Constable::toString)
 69                 .collect(Collectors.joining(", ")));
 70             builder.append("]\n");
 71         }
 72         decr();
 73     }
 74 
 75     public String print(Declaration decl) {
 76         decl.accept(this, null);
 77         return builder.toString();
 78     }
 79 
 80     @Override
 81     public Void visitScoped(Declaration.Scoped d, Void aVoid) {
 82         indent();
 83         builder.append("Scoped: " + d.kind() + " " + d.name() + d.layout().map(l -> " layout = " + l).orElse(""));
 84         builder.append("\n");
 85         getAttributes(d);
 86         incr();
 87         d.members().forEach(m -> m.accept(this, null));
 88         decr();
 89         return null;
 90     }
 91 
 92     @Override
 93     public Void visitFunction(Declaration.Function d, Void aVoid) {
 94         indent();
 95         builder.append("Function: " + d.name() + " type = " + d.type().accept(typeVisitor, null));
 96         builder.append("\n");
 97         getAttributes(d);
 98         incr();
 99         d.parameters().forEach(m -> m.accept(this, null));
100         decr();
101         return null;
102     }
103 
104     @Override
105     public Void visitVariable(Declaration.Variable d, Void aVoid) {
106         indent();
107         builder.append("Variable: " + d.kind() + " " + d.name() + " type = " + d.type().accept(typeVisitor, null) + ", layout = " + d.layout());
108         builder.append("\n");
109         getAttributes(d);
110         return null;
111     }
112 
113     @Override
114     public Void visitConstant(Declaration.Constant d, Void aVoid) {
115         indent();
116         builder.append("Constant: " + d.name() + " " + d.value() + " type = " + d.type().accept(typeVisitor, null));
117         builder.append("\n");
118         getAttributes(d);
119         return null;
120     }
121 
122     @Override
123     public Void visitTypedef(Declaration.Typedef d, Void aVoid) {
124         indent();
125         builder.append("Typedef: ").append(d.name()).append(" = ")
126                .append(d.type().accept(typeVisitor, null)).append("\n");
127         getAttributes(d);
128         return null;
129     }
130 
131     private static Type.Visitor<String, Void> typeVisitor = new Type.Visitor<>() {
132         @Override
133         public String visitPrimitive(Type.Primitive t, Void aVoid) {
134             return t.kind().toString() + t.kind().layout().map(l -> "(layout = " + l + ")").orElse("");
135         }
136 
137         @Override
138         public String visitDelegated(Type.Delegated t, Void aVoid) {
139             switch (t.kind()) {
140                 case TYPEDEF:
141                     return "typedef " + t.name() + " = " + t.type().accept(this, null);
142                 case POINTER:
143                     return "(" + t.type().accept(this, null) + ")*";
144                 default:
145                     return t.kind() + " = " + t.type().accept(this, null);
146             }
147         }
148 
149         @Override
150         public String visitFunction(Type.Function t, Void aVoid) {
151             String res = t.returnType().accept(this, null);
152             String args = t.argumentTypes().stream()
153                     .map(a -> a.accept(this, null))
154                     .collect(Collectors.joining(",", "(", ")"));
155             return res + args;
156         }
157 
158         @Override
159         public String visitDeclared(Type.Declared t, Void aVoid) {
160             return "Declared(" + t.tree().layout().map(MemoryLayout::toString).orElse("") + ")";
161         }
162 
163         @Override
164         public String visitArray(Type.Array t, Void aVoid) {
165             String brackets = String.format("%s[%s]", t.kind() == Type.Array.Kind.VECTOR ? "v" : "",
166                     t.elementCount().isPresent() ? t.elementCount().getAsLong() : "");
167             return t.elementType().accept(this, null) + brackets;
168         }
169 
170         @Override
171         public String visitType(Type t, Void aVoid) {
172             return "Unknown type: " + t.getClass().getName();
173         }
174     };
175 
176     public static String type(Type type) {
177         return type.accept(typeVisitor, null);
178     }
179 
180     public static String position(Position pos) {
181         return String.format("%s:%d:%d",
182                 pos.path() == null ? "N/A" : pos.path().toString(),
183                 pos.line(), pos.col());
184     }
185 }