/* * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javah; import java.io.ByteArrayOutputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.nio.file.NoSuchFileException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Set; import java.util.Stack; import javax.annotation.processing.ProcessingEnvironment; import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; import javax.tools.FileObject; import javax.tools.JavaFileManager; import javax.tools.JavaFileObject; import javax.tools.StandardLocation; /** * An abstraction for generating support files required by native methods. * Subclasses are for specific native interfaces. At the time of its * original writing, this interface is rich enough to support JNI and the * old 1.0-style native method interface. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own * risk. This code and its internal interfaces are subject to change * or deletion without notice.</b></p> * * @author Sucheta Dambalkar(Revised) */ public abstract class Gen { protected String lineSep = System.getProperty("line.separator"); protected ProcessingEnvironment processingEnvironment; protected Types types; protected Elements elems; protected Mangle mangler; protected Util util; protected Gen(Util util) { this.util = util; } /* * List of classes for which we must generate output. */ protected Set<TypeElement> classes; static private final boolean isWindows = System.getProperty("os.name").startsWith("Windows"); /** * Override this abstract method, generating content for the named * class into the outputstream. */ protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit; /** * Override this method to provide a list of #include statements * required by the native interface. */ protected abstract String getIncludes(); /* * Output location. */ protected JavaFileManager fileManager; protected JavaFileObject outFile; public void setFileManager(JavaFileManager fm) { fileManager = fm; } public void setOutFile(JavaFileObject outFile) { this.outFile = outFile; } public void setClasses(Set<TypeElement> classes) { this.classes = classes; } void setProcessingEnvironment(ProcessingEnvironment pEnv) { processingEnvironment = pEnv; elems = pEnv.getElementUtils(); types = pEnv.getTypeUtils(); mangler = new Mangle(elems, types); } /* * Smartness with generated files. */ protected boolean force = false; public void setForce(boolean state) { force = state; } /** * We explicitly need to write ASCII files because that is what C * compilers understand. */ protected PrintWriter wrapWriter(OutputStream o) throws Util.Exit { try { return new PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true); } catch (UnsupportedEncodingException use) { util.bug("encoding.iso8859_1.not.found"); return null; /* dead code */ } } /** * After initializing state of an instance, use this method to start * processing. * * Buffer size chosen as an approximation from a single sampling of: * expr `du -sk` / `ls *.h | wc -l` */ public void run() throws IOException, ClassNotFoundException, Util.Exit { int i = 0; if (outFile != null) { /* Everything goes to one big file... */ ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); writeFileTop(bout); /* only once */ for (TypeElement t: classes) { write(bout, t); } writeIfChanged(bout.toByteArray(), outFile); } else { /* Each class goes to its own file... */ for (TypeElement t: classes) { ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); writeFileTop(bout); write(bout, t); writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName())); } } } /* * Write the contents of byte[] b to a file named file. Writing * is done if either the file doesn't exist or if the contents are * different. */ private void writeIfChanged(byte[] b, FileObject file) throws IOException { boolean mustWrite = false; String event = "[No need to update file "; if (force) { mustWrite = true; event = "[Forcefully writing file "; } else { InputStream in; byte[] a; try { // regrettably, there's no API to get the length in bytes // for a FileObject, so we can't short-circuit reading the // file here in = file.openInputStream(); a = readBytes(in); if (!Arrays.equals(a, b)) { mustWrite = true; event = "[Overwriting file "; } } catch (FileNotFoundException | NoSuchFileException e) { mustWrite = true; event = "[Creating file "; } } if (util.verbose) util.log(event + file + "]"); if (mustWrite) { OutputStream out = file.openOutputStream(); out.write(b); /* No buffering, just one big write! */ out.close(); } } protected byte[] readBytes(InputStream in) throws IOException { try { byte[] array = new byte[in.available() + 1]; int offset = 0; int n; while ((n = in.read(array, offset, array.length - offset)) != -1) { offset += n; if (offset == array.length) array = Arrays.copyOf(array, array.length * 2); } return Arrays.copyOf(array, offset); } finally { in.close(); } } protected String defineForStatic(TypeElement c, VariableElement f) throws Util.Exit { CharSequence cnamedoc = c.getQualifiedName(); CharSequence fnamedoc = f.getSimpleName(); String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS); String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB); if (!f.getModifiers().contains(Modifier.STATIC)) util.bug("tried.to.define.non.static"); if (f.getModifiers().contains(Modifier.FINAL)) { Object value = null; value = f.getConstantValue(); if (value != null) { /* so it is a ConstantExpression */ String constString = null; if ((value instanceof Integer) || (value instanceof Byte) || (value instanceof Short)) { /* covers byte, short, int */ constString = value.toString() + "L"; } else if (value instanceof Boolean) { constString = ((Boolean) value) ? "1L" : "0L"; } else if (value instanceof Character) { Character ch = (Character) value; constString = String.valueOf(((int) ch) & 0xffff) + "L"; } else if (value instanceof Long) { // Visual C++ supports the i64 suffix, not LL. if (isWindows) constString = value.toString() + "i64"; else constString = value.toString() + "LL"; } else if (value instanceof Float) { /* bug for bug */ float fv = ((Float)value).floatValue(); if (Float.isInfinite(fv)) constString = ((fv < 0) ? "-" : "") + "Inff"; else constString = value.toString() + "f"; } else if (value instanceof Double) { /* bug for bug */ double d = ((Double)value).doubleValue(); if (Double.isInfinite(d)) constString = ((d < 0) ? "-" : "") + "InfD"; else constString = value.toString(); } if (constString != null) { StringBuilder s = new StringBuilder("#undef "); s.append(cname); s.append("_"); s.append(fname); s.append(lineSep); s.append("#define "); s.append(cname); s.append("_"); s.append(fname); s.append(" "); s.append(constString); return s.toString(); } } } return null; } /* * Deal with the C pre-processor. */ protected String cppGuardBegin() { return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif"; } protected String cppGuardEnd() { return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif"; } protected String guardBegin(String cname) { return "/* Header for class " + cname + " */" + lineSep + lineSep + "#ifndef _Included_" + cname + lineSep + "#define _Included_" + cname; } protected String guardEnd(String cname) { return "#endif"; } /* * File name and file preamble related operations. */ protected void writeFileTop(OutputStream o) throws Util.Exit { PrintWriter pw = wrapWriter(o); pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + getIncludes()); } protected String baseFileName(CharSequence className) { return mangler.mangle(className, Mangle.Type.CLASS); } protected FileObject getFileObject(CharSequence className) throws IOException { String name = baseFileName(className) + getFileSuffix(); return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null); } protected String getFileSuffix() { return ".h"; } /** * Including super classes' fields. */ List<VariableElement> getAllFields(TypeElement subclazz) { List<VariableElement> fields = new ArrayList<>(); TypeElement cd = null; Stack<TypeElement> s = new Stack<>(); cd = subclazz; while (true) { s.push(cd); TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass())); if (c == null) break; cd = c; } while (!s.empty()) { cd = s.pop(); fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements())); } return fields; } // c.f. MethodDoc.signature String signature(ExecutableElement e) { StringBuilder sb = new StringBuilder("("); String sep = ""; for (VariableElement p: e.getParameters()) { sb.append(sep); sb.append(types.erasure(p.asType()).toString()); sep = ","; } sb.append(")"); return sb.toString(); } }