duke@1: /*
jjg@1362: * Copyright (c) 2002, 2012, 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) {
jjg@1362: StringBuilder s = new StringBuilder("#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@1362: StringBuilder sb = new StringBuilder("(");
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: