duke@1: /* ohair@554: * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this duke@1: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA ohair@554: * or visit www.oracle.com if you need additional information or have any ohair@554: * questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javah; duke@1: duke@1: import java.io.UnsupportedEncodingException; duke@1: import java.io.ByteArrayOutputStream; jjg@416: import java.io.FileNotFoundException; duke@1: import java.io.IOException; jjg@416: import java.io.InputStream; duke@1: import java.io.OutputStream; jjg@416: import java.io.OutputStreamWriter; duke@1: import java.io.PrintWriter; jjg@416: import java.util.ArrayList; jjg@416: import java.util.Arrays; jjg@416: import java.util.List; jjg@416: import java.util.Set; duke@1: import java.util.Stack; duke@1: jjg@416: import javax.annotation.processing.ProcessingEnvironment; jjg@416: jjg@416: import javax.lang.model.element.ExecutableElement; jjg@416: import javax.lang.model.element.Modifier; jjg@416: import javax.lang.model.element.TypeElement; jjg@416: import javax.lang.model.element.VariableElement; jjg@416: import javax.lang.model.util.ElementFilter; jjg@416: import javax.lang.model.util.Elements; jjg@416: import javax.lang.model.util.Types; jjg@416: jjg@416: import javax.tools.FileObject; jjg@416: import javax.tools.JavaFileManager; jjg@416: import javax.tools.JavaFileObject; jjg@416: import javax.tools.StandardLocation; duke@1: duke@1: /** duke@1: * An abstraction for generating support files required by native methods. duke@1: * Subclasses are for specific native interfaces. At the time of its duke@1: * original writing, this interface is rich enough to support JNI and the duke@1: * old 1.0-style native method interface. duke@1: * jjg@581: *

This is NOT part of any supported API. jjg@416: * If you write code that depends on this, you do so at your own jjg@416: * risk. This code and its internal interfaces are subject to change jjg@416: * or deletion without notice.

jjg@416: * duke@1: * @author Sucheta Dambalkar(Revised) duke@1: */ duke@1: public abstract class Gen { duke@1: protected String lineSep = System.getProperty("line.separator"); duke@1: jjg@416: protected ProcessingEnvironment processingEnvironment; jjg@416: protected Types types; jjg@416: protected Elements elems; jjg@416: protected Mangle mangler; jjg@416: protected Util util; jjg@416: jjg@416: protected Gen(Util util) { jjg@416: this.util = util; jjg@416: } jjg@416: duke@1: /* duke@1: * List of classes for which we must generate output. duke@1: */ jjg@416: protected Set classes; duke@1: static private final boolean isWindows = duke@1: System.getProperty("os.name").startsWith("Windows"); duke@1: duke@1: duke@1: /** duke@1: * Override this abstract method, generating content for the named duke@1: * class into the outputstream. duke@1: */ jjg@416: protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit; duke@1: duke@1: /** duke@1: * Override this method to provide a list of #include statements duke@1: * required by the native interface. duke@1: */ duke@1: protected abstract String getIncludes(); duke@1: duke@1: /* duke@1: * Output location. duke@1: */ jjg@416: protected JavaFileManager fileManager; jjg@416: protected JavaFileObject outFile; duke@1: jjg@416: public void setFileManager(JavaFileManager fm) { jjg@416: fileManager = fm; duke@1: } duke@1: jjg@416: public void setOutFile(JavaFileObject outFile) { duke@1: this.outFile = outFile; duke@1: } duke@1: duke@1: jjg@416: public void setClasses(Set classes) { duke@1: this.classes = classes; duke@1: } duke@1: jjg@416: void setProcessingEnvironment(ProcessingEnvironment pEnv) { jjg@416: processingEnvironment = pEnv; jjg@416: elems = pEnv.getElementUtils(); jjg@416: types = pEnv.getTypeUtils(); jjg@416: mangler = new Mangle(elems, types); jjg@416: } jjg@416: duke@1: /* duke@1: * Smartness with generated files. duke@1: */ duke@1: protected boolean force = false; duke@1: duke@1: public void setForce(boolean state) { duke@1: force = state; duke@1: } duke@1: duke@1: /** duke@1: * We explicitly need to write ASCII files because that is what C duke@1: * compilers understand. duke@1: */ jjg@416: protected PrintWriter wrapWriter(OutputStream o) throws Util.Exit { duke@1: try { jjg@416: return new PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true); duke@1: } catch (UnsupportedEncodingException use) { jjg@416: util.bug("encoding.iso8859_1.not.found"); duke@1: return null; /* dead code */ duke@1: } duke@1: } duke@1: duke@1: /** duke@1: * After initializing state of an instance, use this method to start duke@1: * processing. duke@1: * duke@1: * Buffer size chosen as an approximation from a single sampling of: duke@1: * expr `du -sk` / `ls *.h | wc -l` duke@1: */ jjg@416: public void run() throws IOException, ClassNotFoundException, Util.Exit { duke@1: int i = 0; duke@1: if (outFile != null) { duke@1: /* Everything goes to one big file... */ duke@1: ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); duke@1: writeFileTop(bout); /* only once */ duke@1: jjg@416: for (TypeElement t: classes) { jjg@416: write(bout, t); duke@1: } duke@1: duke@1: writeIfChanged(bout.toByteArray(), outFile); duke@1: } else { duke@1: /* Each class goes to its own file... */ jjg@416: for (TypeElement t: classes) { duke@1: ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); duke@1: writeFileTop(bout); jjg@416: write(bout, t); jjg@416: writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName())); duke@1: } duke@1: } duke@1: } duke@1: duke@1: /* duke@1: * Write the contents of byte[] b to a file named file. Writing duke@1: * is done if either the file doesn't exist or if the contents are duke@1: * different. duke@1: */ jjg@416: private void writeIfChanged(byte[] b, FileObject file) throws IOException { duke@1: boolean mustWrite = false; duke@1: String event = "[No need to update file "; duke@1: duke@1: if (force) { duke@1: mustWrite = true; duke@1: event = "[Forcefully writing file "; duke@1: } else { jjg@416: InputStream in; jjg@416: byte[] a; jjg@416: try { jjg@416: // regrettably, there's no API to get the length in bytes jjg@416: // for a FileObject, so we can't short-circuit reading the jjg@416: // file here jjg@416: in = file.openInputStream(); jjg@416: a = readBytes(in); jjg@416: if (!Arrays.equals(a, b)) { jjg@416: mustWrite = true; jjg@416: event = "[Overwriting file "; jjg@416: jjg@416: } jjg@416: } catch (FileNotFoundException e) { duke@1: mustWrite = true; duke@1: event = "[Creating file "; duke@1: } duke@1: } jjg@416: jjg@416: if (util.verbose) jjg@416: util.log(event + file + "]"); jjg@416: duke@1: if (mustWrite) { jjg@416: OutputStream out = file.openOutputStream(); duke@1: out.write(b); /* No buffering, just one big write! */ duke@1: out.close(); duke@1: } duke@1: } duke@1: jjg@416: protected byte[] readBytes(InputStream in) throws IOException { jjg@416: try { jjg@416: byte[] array = new byte[in.available() + 1]; jjg@416: int offset = 0; jjg@416: int n; jjg@416: while ((n = in.read(array, offset, array.length - offset)) != -1) { jjg@416: offset += n; jjg@416: if (offset == array.length) jjg@416: array = Arrays.copyOf(array, array.length * 2); jjg@416: } duke@1: jjg@416: return Arrays.copyOf(array, offset); jjg@416: } finally { jjg@416: in.close(); jjg@416: } jjg@416: } duke@1: jjg@416: protected String defineForStatic(TypeElement c, VariableElement f) jjg@416: throws Util.Exit { jjg@416: CharSequence cnamedoc = c.getQualifiedName(); jjg@416: CharSequence fnamedoc = f.getSimpleName(); duke@1: jjg@416: String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS); jjg@416: String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB); duke@1: jjg@416: if (!f.getModifiers().contains(Modifier.STATIC)) jjg@416: util.bug("tried.to.define.non.static"); jjg@416: jjg@416: if (f.getModifiers().contains(Modifier.FINAL)) { duke@1: Object value = null; duke@1: jjg@416: value = f.getConstantValue(); duke@1: duke@1: if (value != null) { /* so it is a ConstantExpression */ duke@1: String constString = null; duke@1: if ((value instanceof Integer) duke@1: || (value instanceof Byte) jjg@416: || (value instanceof Short)) { jjg@416: /* covers byte, short, int */ jjg@416: constString = value.toString() + "L"; jjg@416: } else if (value instanceof Boolean) { jjg@416: constString = ((Boolean) value) ? "1L" : "0L"; jjg@416: } else if (value instanceof Character) { jjg@416: Character ch = (Character) value; jjg@416: constString = String.valueOf(((int) ch) & 0xffff) + "L"; duke@1: } else if (value instanceof Long) { duke@1: // Visual C++ supports the i64 suffix, not LL. duke@1: if (isWindows) duke@1: constString = value.toString() + "i64"; duke@1: else duke@1: constString = value.toString() + "LL"; duke@1: } else if (value instanceof Float) { duke@1: /* bug for bug */ duke@1: float fv = ((Float)value).floatValue(); duke@1: if (Float.isInfinite(fv)) duke@1: constString = ((fv < 0) ? "-" : "") + "Inff"; duke@1: else duke@1: constString = value.toString() + "f"; duke@1: } else if (value instanceof Double) { duke@1: /* bug for bug */ duke@1: double d = ((Double)value).doubleValue(); duke@1: if (Double.isInfinite(d)) duke@1: constString = ((d < 0) ? "-" : "") + "InfD"; duke@1: else duke@1: constString = value.toString(); duke@1: } duke@1: if (constString != null) { duke@1: StringBuffer s = new StringBuffer("#undef "); duke@1: s.append(cname); s.append("_"); s.append(fname); s.append(lineSep); duke@1: s.append("#define "); s.append(cname); s.append("_"); duke@1: s.append(fname); s.append(" "); s.append(constString); duke@1: return s.toString(); duke@1: } duke@1: duke@1: } duke@1: } duke@1: return null; duke@1: } duke@1: duke@1: /* duke@1: * Deal with the C pre-processor. duke@1: */ duke@1: protected String cppGuardBegin() { duke@1: return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif"; duke@1: } duke@1: duke@1: protected String cppGuardEnd() { duke@1: return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif"; duke@1: } duke@1: duke@1: protected String guardBegin(String cname) { duke@1: return "/* Header for class " + cname + " */" + lineSep + lineSep + duke@1: "#ifndef _Included_" + cname + lineSep + duke@1: "#define _Included_" + cname; duke@1: } duke@1: duke@1: protected String guardEnd(String cname) { duke@1: return "#endif"; duke@1: } duke@1: duke@1: /* duke@1: * File name and file preamble related operations. duke@1: */ jjg@416: protected void writeFileTop(OutputStream o) throws Util.Exit { duke@1: PrintWriter pw = wrapWriter(o); duke@1: pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + duke@1: getIncludes()); duke@1: } duke@1: jjg@416: protected String baseFileName(CharSequence className) { jjg@416: return mangler.mangle(className, Mangle.Type.CLASS); duke@1: } duke@1: jjg@416: protected FileObject getFileObject(CharSequence className) throws IOException { jjg@416: String name = baseFileName(className) + getFileSuffix(); jjg@416: return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null); duke@1: } duke@1: duke@1: protected String getFileSuffix() { duke@1: return ".h"; duke@1: } duke@1: duke@1: /** duke@1: * Including super classes' fields. duke@1: */ duke@1: jjg@416: List getAllFields(TypeElement subclazz) { jjg@416: List fields = new ArrayList(); jjg@416: TypeElement cd = null; jjg@416: Stack s = new Stack(); duke@1: duke@1: cd = subclazz; duke@1: while (true) { duke@1: s.push(cd); jjg@416: TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass())); duke@1: if (c == null) duke@1: break; duke@1: cd = c; duke@1: } duke@1: duke@1: while (!s.empty()) { jjg@416: cd = s.pop(); jjg@416: fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements())); duke@1: } duke@1: jjg@416: return fields; jjg@416: } jjg@416: jjg@416: // c.f. MethodDoc.signature jjg@416: String signature(ExecutableElement e) { jjg@416: StringBuffer sb = new StringBuffer("("); jjg@416: String sep = ""; jjg@416: for (VariableElement p: e.getParameters()) { jjg@416: sb.append(sep); jjg@416: sb.append(types.erasure(p.asType()).toString()); jjg@416: sep = ","; jjg@416: } jjg@416: sb.append(")"); jjg@416: return sb.toString(); duke@1: } duke@1: } jjg@416: