1 /*
2  * Copyright (c) 1999, 2018, 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 package com.sun.tools.javac.jvm;
27 
28 import java.io.IOException;
29 import java.io.PrintWriter;
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.List;
33 
34 import javax.tools.FileObject;
35 import javax.tools.JavaFileManager;
36 import javax.tools.JavaFileManager.Location;
37 import javax.tools.StandardLocation;
38 
39 import com.sun.tools.javac.code.Attribute;
40 import com.sun.tools.javac.code.Flags;
41 import com.sun.tools.javac.code.Symbol;
42 import com.sun.tools.javac.code.Symbol.ClassSymbol;
43 import com.sun.tools.javac.code.Symbol.ModuleSymbol;
44 import com.sun.tools.javac.code.Symbol.VarSymbol;
45 import com.sun.tools.javac.code.Symtab;
46 import com.sun.tools.javac.code.Type;
47 import com.sun.tools.javac.code.Types;
48 import com.sun.tools.javac.model.JavacElements;
49 import com.sun.tools.javac.util.Assert;
50 import com.sun.tools.javac.util.Context;
51 import com.sun.tools.javac.util.Log;
52 import com.sun.tools.javac.util.Options;
53 import com.sun.tools.javac.util.Pair;
54 
55 import static com.sun.tools.javac.main.Option.*;
56 import static com.sun.tools.javac.code.Kinds.Kind.*;
57 import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
58 
59 /** This class provides operations to write native header files for classes.
60  *
61  *  <p><b>This is NOT part of any supported API.
62  *  If you write code that depends on this, you do so at your own risk.
63  *  This code and its internal interfaces are subject to change or
64  *  deletion without notice.</b>
65  */
66 public class JNIWriter {
67     protected static final Context.Key<JNIWriter> jniWriterKey = new Context.Key<>();
68 
69     /** Access to files. */
70     private final JavaFileManager fileManager;
71 
72     Types      types;
73     Symtab     syms;
74 
75     /** The log to use for verbose output.
76      */
77     private final Log log;
78 
79     /** Switch: verbose output.
80      */
81     private boolean verbose;
82 
83     /** Switch: check all nested classes of top level class
84      */
85     private boolean checkAll;
86 
87     /**
88      * If true, class files will be written in module-specific subdirectories
89      * of the NATIVE_HEADER_OUTPUT location.
90      */
91     public boolean multiModuleMode;
92 
93     private Context context;
94 
95     private static final boolean isWindows =
96         System.getProperty("os.name").startsWith("Windows");
97 
98     /** Get the ClassWriter instance for this context. */
instance(Context context)99     public static JNIWriter instance(Context context) {
100         JNIWriter instance = context.get(jniWriterKey);
101         if (instance == null)
102             instance = new JNIWriter(context);
103         return instance;
104     }
105 
106     /** Construct a class writer, given an options table.
107      */
JNIWriter(Context context)108     private JNIWriter(Context context) {
109         context.put(jniWriterKey, this);
110         fileManager = context.get(JavaFileManager.class);
111         log = Log.instance(context);
112 
113         Options options = Options.instance(context);
114         verbose = options.isSet(VERBOSE);
115         checkAll = options.isSet("javah:full");
116 
117         this.context = context; // for lazyInit()
118     }
119 
lazyInit()120     private void lazyInit() {
121         if (types == null)
122             types = Types.instance(context);
123         if (syms == null)
124             syms = Symtab.instance(context);
125 
126     }
127 
isSynthetic(Symbol s)128     static boolean isSynthetic(Symbol s) {
129         return hasFlag(s, Flags.SYNTHETIC);
130     }
isStatic(Symbol s)131     static boolean isStatic(Symbol s) {
132         return hasFlag(s, Flags.STATIC);
133     }
isFinal(Symbol s)134     static boolean isFinal(Symbol s) {
135         return hasFlag(s, Flags.FINAL);
136     }
isNative(Symbol s)137     static boolean isNative(Symbol s) {
138         return hasFlag(s, Flags.NATIVE);
139     }
hasFlag(Symbol m, int flag)140     static private boolean hasFlag(Symbol m, int flag) {
141         return (m.flags() & flag) != 0;
142     }
143 
needsHeader(ClassSymbol c)144     public boolean needsHeader(ClassSymbol c) {
145         lazyInit();
146         if (c.isLocal() || isSynthetic(c))
147             return false;
148         return (checkAll)
149                 ? needsHeader(c.outermostClass(), true)
150                 : needsHeader(c, false);
151     }
152 
needsHeader(ClassSymbol c, boolean checkNestedClasses)153     private boolean needsHeader(ClassSymbol c, boolean checkNestedClasses) {
154         if (c.isLocal() || isSynthetic(c))
155             return false;
156 
157         for (Symbol sym : c.members_field.getSymbols(NON_RECURSIVE)) {
158             if (sym.kind == MTH && isNative(sym))
159                 return true;
160             for (Attribute.Compound a: sym.getDeclarationAttributes()) {
161                 if (a.type.tsym == syms.nativeHeaderType.tsym)
162                     return true;
163             }
164         }
165         if (checkNestedClasses) {
166             for (Symbol sym : c.members_field.getSymbols(NON_RECURSIVE)) {
167                 if ((sym.kind == TYP) && needsHeader(((ClassSymbol) sym), true))
168                     return true;
169             }
170         }
171         return false;
172     }
173 
174     /** Emit a class file for a given class.
175      *  @param c      The class from which a class file is generated.
176      */
write(ClassSymbol c)177     public FileObject write(ClassSymbol c) throws IOException {
178         String className = c.flatName().toString();
179         Location outLocn;
180         if (multiModuleMode) {
181             ModuleSymbol msym = c.owner.kind == MDL ? (ModuleSymbol) c.owner : c.packge().modle;
182             outLocn = fileManager.getLocationForModule(StandardLocation.NATIVE_HEADER_OUTPUT, msym.name.toString());
183         } else {
184             outLocn = StandardLocation.NATIVE_HEADER_OUTPUT;
185         }
186         FileObject outFile
187             = fileManager.getFileForOutput(outLocn,
188                 "", className.replaceAll("[.$]", "_") + ".h", null);
189         PrintWriter out = new PrintWriter(outFile.openWriter());
190         try {
191             write(out, c);
192             if (verbose)
193                 log.printVerbose("wrote.file", outFile.getName());
194             out.close();
195             out = null;
196         } finally {
197             if (out != null) {
198                 // if we are propogating an exception, delete the file
199                 out.close();
200                 outFile.delete();
201                 outFile = null;
202             }
203         }
204         return outFile; // may be null if write failed
205     }
206 
write(PrintWriter out, ClassSymbol sym)207     public void write(PrintWriter out, ClassSymbol sym) throws IOException {
208         lazyInit();
209         try {
210             String cname = encode(sym.fullname, EncoderType.CLASS);
211             fileTop(out);
212             includes(out);
213             guardBegin(out, cname);
214             cppGuardBegin(out);
215 
216             writeStatics(out, sym);
217             writeMethods(out, sym, cname);
218 
219             cppGuardEnd(out);
220             guardEnd(out);
221         } catch (TypeSignature.SignatureException e) {
222             throw new IOException(e);
223         }
224     }
writeStatics(PrintWriter out, ClassSymbol sym)225     protected void writeStatics(PrintWriter out, ClassSymbol sym) throws IOException {
226         List<ClassSymbol> clist = new ArrayList<>();
227         for (ClassSymbol cd = sym; cd != null;
228                 cd = (ClassSymbol) cd.getSuperclass().tsym) {
229             clist.add(cd);
230         }
231         /*
232          * list needs to be super-class, base-class1, base-class2 and so on,
233          * so we reverse class hierarchy
234          */
235         Collections.reverse(clist);
236         for (ClassSymbol cd : clist) {
237             for (Symbol i : cd.getEnclosedElements()) {
238                 // consider only final, static and fields with ConstantExpressions
239                 if (isFinal(i) && i.isStatic() && i.kind == VAR) {
240                     VarSymbol v = (VarSymbol) i;
241                     if (v.getConstantValue() != null) {
242                         Pair<ClassSymbol, VarSymbol> p = new Pair<>(sym, v);
243                         printStaticDefines(out, p);
244                     }
245                 }
246             }
247         }
248     }
printStaticDefines(PrintWriter out, Pair<ClassSymbol, VarSymbol> p)249     static void printStaticDefines(PrintWriter out, Pair<ClassSymbol, VarSymbol> p) {
250         ClassSymbol cls = p.fst;
251         VarSymbol f = p.snd;
252         Object value = f.getConstantValue();
253         String valueStr = null;
254         switch (f.asType().getKind()) {
255             case BOOLEAN:
256                 valueStr = (((Boolean) value) ? "1L" : "0L");
257                 break;
258             case BYTE: case SHORT: case INT:
259                 valueStr = value.toString() + "L";
260                 break;
261             case LONG:
262                 // Visual C++ supports the i64 suffix, not LL.
263                 valueStr = value.toString() + ((isWindows) ? "i64" : "LL");
264                 break;
265             case CHAR:
266                 Character ch = (Character) value;
267                 valueStr = String.valueOf(((int) ch) & 0xffff) + "L";
268                 break;
269             case FLOAT:
270                 // bug compatible
271                 float fv = ((Float) value).floatValue();
272                 valueStr = (Float.isInfinite(fv))
273                         ? ((fv < 0) ? "-" : "") + "Inff"
274                         : value.toString() + "f";
275                 break;
276             case DOUBLE:
277                 // bug compatible
278                 double d = ((Double) value).doubleValue();
279                 valueStr = (Double.isInfinite(d))
280                         ? ((d < 0) ? "-" : "") + "InfD"
281                         : value.toString();
282                 break;
283             default:
284                 valueStr = null;
285         }
286         if (valueStr != null) {
287             out.print("#undef ");
288             String cname = encode(cls.getQualifiedName(), EncoderType.CLASS);
289             String fname = encode(f.getSimpleName(), EncoderType.FIELDSTUB);
290             out.println(cname + "_" + fname);
291             out.print("#define " + cname + "_");
292             out.println(fname + " " + valueStr);
293         }
294     }
writeMethods(PrintWriter out, ClassSymbol sym, String cname)295     protected void writeMethods(PrintWriter out, ClassSymbol sym, String cname)
296             throws IOException, TypeSignature.SignatureException {
297         List<Symbol> classmethods = sym.getEnclosedElements();
298         for (Symbol md : classmethods) {
299             if (isNative(md)) {
300                 TypeSignature newtypesig = new TypeSignature(types);
301                 CharSequence methodName = md.getSimpleName();
302                 boolean isOverloaded = false;
303                 for (Symbol md2 : classmethods) {
304                     if ((md2 != md)
305                             && (methodName.equals(md2.getSimpleName()))
306                             && isNative(md2)) {
307                         isOverloaded = true;
308                     }
309                 }
310                 out.println("/*");
311                 out.println(" * Class:     " + cname);
312                 out.println(" * Method:    " + encode(methodName, EncoderType.FIELDSTUB));
313                 out.println(" * Signature: " + newtypesig.getSignature(md.type));
314                 out.println(" */");
315                 out.println("JNIEXPORT " + jniType(types.erasure(md.type.getReturnType()))
316                         + " JNICALL " + encodeMethod(md, sym, isOverloaded));
317                 out.print("  (JNIEnv *, ");
318                 out.print((md.isStatic())
319                         ? "jclass"
320                         : "jobject");
321                 for (Type arg : types.erasure(md.type.getParameterTypes())) {
322                     out.print(", ");
323                     out.print(jniType(arg));
324                 }
325                 out.println(");");
326                 out.println();
327             }
328         }
329     }
330     @SuppressWarnings("fallthrough")
jniType(Type t)331     protected final String jniType(Type t) {
332         switch (t.getKind()) {
333             case ARRAY: {
334                 Type ct = ((Type.ArrayType)t).getComponentType();
335                 switch (ct.getKind()) {
336                     case BOOLEAN:  return "jbooleanArray";
337                     case BYTE:     return "jbyteArray";
338                     case CHAR:     return "jcharArray";
339                     case SHORT:    return "jshortArray";
340                     case INT:      return "jintArray";
341                     case LONG:     return "jlongArray";
342                     case FLOAT:    return "jfloatArray";
343                     case DOUBLE:   return "jdoubleArray";
344                     case ARRAY:
345                     case DECLARED: return "jobjectArray";
346                     default: throw new Error(ct.toString());
347                 }
348             }
349 
350             case VOID:     return "void";
351             case BOOLEAN:  return "jboolean";
352             case BYTE:     return "jbyte";
353             case CHAR:     return "jchar";
354             case SHORT:    return "jshort";
355             case INT:      return "jint";
356             case LONG:     return "jlong";
357             case FLOAT:    return "jfloat";
358             case DOUBLE:   return "jdouble";
359             case DECLARED: {
360                 if (t.tsym.type == syms.stringType) {
361                     return "jstring";
362                 } else if (types.isAssignable(t, syms.throwableType)) {
363                     return "jthrowable";
364                 } else if (types.isAssignable(t, syms.classType)) {
365                     return "jclass";
366                 } else {
367                     return "jobject";
368                 }
369             }
370         }
371 
372         Assert.check(false, "jni unknown type");
373         return null; /* dead code. */
374     }
375 
fileTop(PrintWriter out)376     protected void  fileTop(PrintWriter out) {
377         out.println("/* DO NOT EDIT THIS FILE - it is machine generated */");
378     }
379 
includes(PrintWriter out)380     protected void includes(PrintWriter out) {
381         out.println("#include <jni.h>");
382     }
383 
384     /*
385      * Deal with the C pre-processor.
386      */
cppGuardBegin(PrintWriter out)387     protected void cppGuardBegin(PrintWriter out) {
388         out.println("#ifdef __cplusplus");
389         out.println("extern \"C\" {");
390         out.println("#endif");
391     }
392 
cppGuardEnd(PrintWriter out)393     protected void cppGuardEnd(PrintWriter out) {
394         out.println("#ifdef __cplusplus");
395         out.println("}");
396         out.println("#endif");
397     }
398 
guardBegin(PrintWriter out, String cname)399     protected void guardBegin(PrintWriter out, String cname) {
400         out.println("/* Header for class " + cname + " */");
401         out.println();
402         out.println("#ifndef _Included_" + cname);
403         out.println("#define _Included_" + cname);
404     }
405 
guardEnd(PrintWriter out)406     protected void guardEnd(PrintWriter out) {
407         out.println("#endif");
408     }
409 
encodeMethod(Symbol msym, ClassSymbol clazz, boolean isOverloaded)410     String encodeMethod(Symbol msym, ClassSymbol clazz,
411             boolean isOverloaded) throws TypeSignature.SignatureException {
412         StringBuilder result = new StringBuilder(100);
413         result.append("Java_");
414         /* JNI */
415         result.append(encode(clazz.flatname.toString(), EncoderType.JNI));
416         result.append('_');
417         result.append(encode(msym.getSimpleName(), EncoderType.JNI));
418         if (isOverloaded) {
419             TypeSignature typeSig = new TypeSignature(types);
420             StringBuilder sig = typeSig.getParameterSignature(msym.type);
421             result.append("__").append(encode(sig, EncoderType.JNI));
422         }
423         return result.toString();
424     }
425 
426     static enum EncoderType {
427         CLASS,
428         FIELDSTUB,
429         FIELD,
430         JNI,
431         SIGNATURE
432     }
433     @SuppressWarnings("fallthrough")
encode(CharSequence name, EncoderType mtype)434     static String encode(CharSequence name, EncoderType mtype) {
435         StringBuilder result = new StringBuilder(100);
436         int length = name.length();
437 
438         for (int i = 0; i < length; i++) {
439             char ch = name.charAt(i);
440             if (isalnum(ch)) {
441                 result.append(ch);
442                 continue;
443             }
444             switch (mtype) {
445                 case CLASS:
446                     switch (ch) {
447                         case '.':
448                         case '_':
449                             result.append("_");
450                             break;
451                         case '$':
452                             result.append("__");
453                             break;
454                         default:
455                             result.append(encodeChar(ch));
456                     }
457                     break;
458                 case JNI:
459                     switch (ch) {
460                         case '/':
461                         case '.':
462                             result.append("_");
463                             break;
464                         case '_':
465                             result.append("_1");
466                             break;
467                         case ';':
468                             result.append("_2");
469                             break;
470                         case '[':
471                             result.append("_3");
472                             break;
473                         default:
474                             result.append(encodeChar(ch));
475                     }
476                     break;
477                 case SIGNATURE:
478                     result.append(isprint(ch) ? ch : encodeChar(ch));
479                     break;
480                 case FIELDSTUB:
481                     result.append(ch == '_' ? ch : encodeChar(ch));
482                     break;
483                 default:
484                     result.append(encodeChar(ch));
485             }
486         }
487         return result.toString();
488     }
489 
encodeChar(char ch)490     static String encodeChar(char ch) {
491         String s = Integer.toHexString(ch);
492         int nzeros = 5 - s.length();
493         char[] result = new char[6];
494         result[0] = '_';
495         for (int i = 1; i <= nzeros; i++) {
496             result[i] = '0';
497         }
498         for (int i = nzeros + 1, j = 0; i < 6; i++, j++) {
499             result[i] = s.charAt(j);
500         }
501         return new String(result);
502     }
503 
504     /* Warning: Intentional ASCII operation. */
isalnum(char ch)505     private static boolean isalnum(char ch) {
506         return ch <= 0x7f && /* quick test */
507                 ((ch >= 'A' && ch <= 'Z')  ||
508                  (ch >= 'a' && ch <= 'z')  ||
509                  (ch >= '0' && ch <= '9'));
510     }
511 
512     /* Warning: Intentional ASCII operation. */
isprint(char ch)513     private static boolean isprint(char ch) {
514         return ch >= 32 && ch <= 126;
515     }
516 
517     private static class TypeSignature {
518         static class SignatureException extends Exception {
519             private static final long serialVersionUID = 1L;
SignatureException(String reason)520             SignatureException(String reason) {
521                 super(reason);
522             }
523         }
524 
525         JavacElements elems;
526         Types    types;
527 
528         /* Signature Characters */
529         private static final String SIG_VOID                   = "V";
530         private static final String SIG_BOOLEAN                = "Z";
531         private static final String SIG_BYTE                   = "B";
532         private static final String SIG_CHAR                   = "C";
533         private static final String SIG_SHORT                  = "S";
534         private static final String SIG_INT                    = "I";
535         private static final String SIG_LONG                   = "J";
536         private static final String SIG_FLOAT                  = "F";
537         private static final String SIG_DOUBLE                 = "D";
538         private static final String SIG_ARRAY                  = "[";
539         private static final String SIG_CLASS                  = "L";
540 
TypeSignature(Types types)541         public TypeSignature(Types types) {
542             this.types = types;
543         }
544 
getParameterSignature(Type mType)545         StringBuilder getParameterSignature(Type mType)
546                 throws SignatureException {
547             StringBuilder result = new StringBuilder();
548             for (Type pType : mType.getParameterTypes()) {
549                 result.append(getJvmSignature(pType));
550             }
551             return result;
552         }
553 
getReturnSignature(Type mType)554         StringBuilder getReturnSignature(Type mType)
555                 throws SignatureException {
556             return getJvmSignature(mType.getReturnType());
557         }
558 
getSignature(Type mType)559         StringBuilder getSignature(Type mType) throws SignatureException {
560             StringBuilder sb = new StringBuilder();
561             sb.append("(").append(getParameterSignature(mType)).append(")");
562             sb.append(getReturnSignature(mType));
563             return sb;
564         }
565 
566         /*
567          * Returns jvm internal signature.
568          */
569         static class JvmTypeVisitor extends JNIWriter.SimpleTypeVisitor<Type, StringBuilder> {
570 
571             @Override
visitClassType(Type.ClassType t, StringBuilder s)572             public Type visitClassType(Type.ClassType t, StringBuilder s) {
573                 setDeclaredType(t, s);
574                 return null;
575             }
576 
577             @Override
visitArrayType(Type.ArrayType t, StringBuilder s)578             public Type visitArrayType(Type.ArrayType t, StringBuilder s) {
579                 s.append("[");
580                 return t.getComponentType().accept(this, s);
581             }
582 
583             @Override
visitType(Type t, StringBuilder s)584             public Type visitType(Type t, StringBuilder s) {
585                 if (t.isPrimitiveOrVoid()) {
586                     s.append(getJvmPrimitiveSignature(t));
587                     return null;
588                 }
589                 return t.accept(this, s);
590             }
setDeclaredType(Type t, StringBuilder s)591             private void setDeclaredType(Type t, StringBuilder s) {
592                     String classname = t.tsym.getQualifiedName().toString();
593                     classname = classname.replace('.', '/');
594                     s.append("L").append(classname).append(";");
595             }
getJvmPrimitiveSignature(Type t)596             private String getJvmPrimitiveSignature(Type t) {
597                 switch (t.getKind()) {
598                     case VOID:      return SIG_VOID;
599                     case BOOLEAN:   return SIG_BOOLEAN;
600                     case BYTE:      return SIG_BYTE;
601                     case CHAR:      return SIG_CHAR;
602                     case SHORT:     return SIG_SHORT;
603                     case INT:       return SIG_INT;
604                     case LONG:      return SIG_LONG;
605                     case FLOAT:     return SIG_FLOAT;
606                     case DOUBLE:    return SIG_DOUBLE;
607                     default:
608                         Assert.error("unknown type: should not happen");
609                 }
610                 return null;
611             }
612         }
613 
getJvmSignature(Type type)614         StringBuilder getJvmSignature(Type type) {
615             Type t = types.erasure(type);
616             StringBuilder sig = new StringBuilder();
617             JvmTypeVisitor jv = new JvmTypeVisitor();
618             jv.visitType(t, sig);
619             return sig;
620         }
621     }
622 
623     static class SimpleTypeVisitor<R, P> implements Type.Visitor<R, P> {
624 
625         protected final R DEFAULT_VALUE;
626 
SimpleTypeVisitor()627         protected SimpleTypeVisitor() {
628             DEFAULT_VALUE = null;
629         }
630 
SimpleTypeVisitor(R defaultValue)631         protected SimpleTypeVisitor(R defaultValue) {
632             DEFAULT_VALUE = defaultValue;
633         }
634 
defaultAction(Type t, P p)635         protected R defaultAction(Type t, P p) {
636             return DEFAULT_VALUE;
637         }
638 
639         @Override
visitClassType(Type.ClassType t, P p)640         public R visitClassType(Type.ClassType t, P p) {
641             return defaultAction(t, p);
642         }
643 
644         @Override
visitWildcardType(Type.WildcardType t, P p)645         public R visitWildcardType(Type.WildcardType t, P p) {
646             return defaultAction(t, p);
647         }
648 
649         @Override
visitArrayType(Type.ArrayType t, P p)650         public R visitArrayType(Type.ArrayType t, P p) {
651             return defaultAction(t, p);
652         }
653 
654         @Override
visitMethodType(Type.MethodType t, P p)655         public R visitMethodType(Type.MethodType t, P p) {
656             return defaultAction(t, p);
657         }
658 
659         @Override
visitPackageType(Type.PackageType t, P p)660         public R visitPackageType(Type.PackageType t, P p) {
661             return defaultAction(t, p);
662         }
663 
664         @Override
visitTypeVar(Type.TypeVar t, P p)665         public R visitTypeVar(Type.TypeVar t, P p) {
666             return defaultAction(t, p);
667         }
668 
669         @Override
visitCapturedType(Type.CapturedType t, P p)670         public R visitCapturedType(Type.CapturedType t, P p) {
671             return defaultAction(t, p);
672         }
673 
674         @Override
visitForAll(Type.ForAll t, P p)675         public R visitForAll(Type.ForAll t, P p) {
676             return defaultAction(t, p);
677         }
678 
679         @Override
visitUndetVar(Type.UndetVar t, P p)680         public R visitUndetVar(Type.UndetVar t, P p) {
681             return defaultAction(t, p);
682         }
683 
684         @Override
visitErrorType(Type.ErrorType t, P p)685         public R visitErrorType(Type.ErrorType t, P p) {
686             return defaultAction(t, p);
687         }
688 
689         @Override
visitType(Type t, P p)690         public R visitType(Type t, P p) {
691             return defaultAction(t, p);
692         }
693 
694         @Override
visitModuleType(Type.ModuleType t, P p)695         public R visitModuleType(Type.ModuleType t, P p) {
696             return defaultAction(t, p);
697         }
698     }
699 }
700