1 /* 2 * Copyright (c) 2002, 2012, 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.javah; 27 28 import java.io.UnsupportedEncodingException; 29 import java.io.ByteArrayOutputStream; 30 import java.io.FileNotFoundException; 31 import java.io.IOException; 32 import java.io.InputStream; 33 import java.io.OutputStream; 34 import java.io.OutputStreamWriter; 35 import java.io.PrintWriter; 36 import java.util.ArrayList; 37 import java.util.Arrays; 38 import java.util.List; 39 import java.util.Set; 40 import java.util.Stack; 41 42 import javax.annotation.processing.ProcessingEnvironment; 43 44 import javax.lang.model.element.ExecutableElement; 45 import javax.lang.model.element.Modifier; 46 import javax.lang.model.element.TypeElement; 47 import javax.lang.model.element.VariableElement; 48 import javax.lang.model.util.ElementFilter; 49 import javax.lang.model.util.Elements; 50 import javax.lang.model.util.Types; 51 52 import javax.tools.FileObject; 53 import javax.tools.JavaFileManager; 54 import javax.tools.JavaFileObject; 55 import javax.tools.StandardLocation; 56 57 /** 58 * An abstraction for generating support files required by native methods. 59 * Subclasses are for specific native interfaces. At the time of its 60 * original writing, this interface is rich enough to support JNI and the 61 * old 1.0-style native method interface. 62 * 63 * <p><b>This is NOT part of any supported API. 64 * If you write code that depends on this, you do so at your own 65 * risk. This code and its internal interfaces are subject to change 66 * or deletion without notice.</b></p> 67 * 68 * @author Sucheta Dambalkar(Revised) 69 */ 70 public abstract class Gen { 71 protected String lineSep = System.getProperty("line.separator"); 72 73 protected ProcessingEnvironment processingEnvironment; 74 protected Types types; 75 protected Elements elems; 76 protected Mangle mangler; 77 protected Util util; 78 Gen(Util util)79 protected Gen(Util util) { 80 this.util = util; 81 } 82 83 /* 84 * List of classes for which we must generate output. 85 */ 86 protected Set<TypeElement> classes; 87 static private final boolean isWindows = 88 System.getProperty("os.name").startsWith("Windows"); 89 90 91 /** 92 * Override this abstract method, generating content for the named 93 * class into the outputstream. 94 */ write(OutputStream o, TypeElement clazz)95 protected abstract void write(OutputStream o, TypeElement clazz) throws Util.Exit; 96 97 /** 98 * Override this method to provide a list of #include statements 99 * required by the native interface. 100 */ getIncludes()101 protected abstract String getIncludes(); 102 103 /* 104 * Output location. 105 */ 106 protected JavaFileManager fileManager; 107 protected JavaFileObject outFile; 108 setFileManager(JavaFileManager fm)109 public void setFileManager(JavaFileManager fm) { 110 fileManager = fm; 111 } 112 setOutFile(JavaFileObject outFile)113 public void setOutFile(JavaFileObject outFile) { 114 this.outFile = outFile; 115 } 116 117 setClasses(Set<TypeElement> classes)118 public void setClasses(Set<TypeElement> classes) { 119 this.classes = classes; 120 } 121 setProcessingEnvironment(ProcessingEnvironment pEnv)122 void setProcessingEnvironment(ProcessingEnvironment pEnv) { 123 processingEnvironment = pEnv; 124 elems = pEnv.getElementUtils(); 125 types = pEnv.getTypeUtils(); 126 mangler = new Mangle(elems, types); 127 } 128 129 /* 130 * Smartness with generated files. 131 */ 132 protected boolean force = false; 133 setForce(boolean state)134 public void setForce(boolean state) { 135 force = state; 136 } 137 138 /** 139 * We explicitly need to write ASCII files because that is what C 140 * compilers understand. 141 */ wrapWriter(OutputStream o)142 protected PrintWriter wrapWriter(OutputStream o) throws Util.Exit { 143 try { 144 return new PrintWriter(new OutputStreamWriter(o, "ISO8859_1"), true); 145 } catch (UnsupportedEncodingException use) { 146 util.bug("encoding.iso8859_1.not.found"); 147 return null; /* dead code */ 148 } 149 } 150 151 /** 152 * After initializing state of an instance, use this method to start 153 * processing. 154 * 155 * Buffer size chosen as an approximation from a single sampling of: 156 * expr `du -sk` / `ls *.h | wc -l` 157 */ run()158 public void run() throws IOException, ClassNotFoundException, Util.Exit { 159 int i = 0; 160 if (outFile != null) { 161 /* Everything goes to one big file... */ 162 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); 163 writeFileTop(bout); /* only once */ 164 165 for (TypeElement t: classes) { 166 write(bout, t); 167 } 168 169 writeIfChanged(bout.toByteArray(), outFile); 170 } else { 171 /* Each class goes to its own file... */ 172 for (TypeElement t: classes) { 173 ByteArrayOutputStream bout = new ByteArrayOutputStream(8192); 174 writeFileTop(bout); 175 write(bout, t); 176 writeIfChanged(bout.toByteArray(), getFileObject(t.getQualifiedName())); 177 } 178 } 179 } 180 181 /* 182 * Write the contents of byte[] b to a file named file. Writing 183 * is done if either the file doesn't exist or if the contents are 184 * different. 185 */ writeIfChanged(byte[] b, FileObject file)186 private void writeIfChanged(byte[] b, FileObject file) throws IOException { 187 boolean mustWrite = false; 188 String event = "[No need to update file "; 189 190 if (force) { 191 mustWrite = true; 192 event = "[Forcefully writing file "; 193 } else { 194 InputStream in; 195 byte[] a; 196 try { 197 // regrettably, there's no API to get the length in bytes 198 // for a FileObject, so we can't short-circuit reading the 199 // file here 200 in = file.openInputStream(); 201 a = readBytes(in); 202 if (!Arrays.equals(a, b)) { 203 mustWrite = true; 204 event = "[Overwriting file "; 205 206 } 207 } catch (FileNotFoundException e) { 208 mustWrite = true; 209 event = "[Creating file "; 210 } 211 } 212 213 if (util.verbose) 214 util.log(event + file + "]"); 215 216 if (mustWrite) { 217 OutputStream out = file.openOutputStream(); 218 out.write(b); /* No buffering, just one big write! */ 219 out.close(); 220 } 221 } 222 readBytes(InputStream in)223 protected byte[] readBytes(InputStream in) throws IOException { 224 try { 225 byte[] array = new byte[in.available() + 1]; 226 int offset = 0; 227 int n; 228 while ((n = in.read(array, offset, array.length - offset)) != -1) { 229 offset += n; 230 if (offset == array.length) 231 array = Arrays.copyOf(array, array.length * 2); 232 } 233 234 return Arrays.copyOf(array, offset); 235 } finally { 236 in.close(); 237 } 238 } 239 defineForStatic(TypeElement c, VariableElement f)240 protected String defineForStatic(TypeElement c, VariableElement f) 241 throws Util.Exit { 242 CharSequence cnamedoc = c.getQualifiedName(); 243 CharSequence fnamedoc = f.getSimpleName(); 244 245 String cname = mangler.mangle(cnamedoc, Mangle.Type.CLASS); 246 String fname = mangler.mangle(fnamedoc, Mangle.Type.FIELDSTUB); 247 248 if (!f.getModifiers().contains(Modifier.STATIC)) 249 util.bug("tried.to.define.non.static"); 250 251 if (f.getModifiers().contains(Modifier.FINAL)) { 252 Object value = null; 253 254 value = f.getConstantValue(); 255 256 if (value != null) { /* so it is a ConstantExpression */ 257 String constString = null; 258 if ((value instanceof Integer) 259 || (value instanceof Byte) 260 || (value instanceof Short)) { 261 /* covers byte, short, int */ 262 constString = value.toString() + "L"; 263 } else if (value instanceof Boolean) { 264 constString = ((Boolean) value) ? "1L" : "0L"; 265 } else if (value instanceof Character) { 266 Character ch = (Character) value; 267 constString = String.valueOf(((int) ch) & 0xffff) + "L"; 268 } else if (value instanceof Long) { 269 // Visual C++ supports the i64 suffix, not LL. 270 if (isWindows) 271 constString = value.toString() + "i64"; 272 else 273 constString = value.toString() + "LL"; 274 } else if (value instanceof Float) { 275 /* bug for bug */ 276 float fv = ((Float)value).floatValue(); 277 if (Float.isInfinite(fv)) 278 constString = ((fv < 0) ? "-" : "") + "Inff"; 279 else 280 constString = value.toString() + "f"; 281 } else if (value instanceof Double) { 282 /* bug for bug */ 283 double d = ((Double)value).doubleValue(); 284 if (Double.isInfinite(d)) 285 constString = ((d < 0) ? "-" : "") + "InfD"; 286 else 287 constString = value.toString(); 288 } 289 if (constString != null) { 290 StringBuilder s = new StringBuilder("#undef "); 291 s.append(cname); s.append("_"); s.append(fname); s.append(lineSep); 292 s.append("#define "); s.append(cname); s.append("_"); 293 s.append(fname); s.append(" "); s.append(constString); 294 return s.toString(); 295 } 296 297 } 298 } 299 return null; 300 } 301 302 /* 303 * Deal with the C pre-processor. 304 */ cppGuardBegin()305 protected String cppGuardBegin() { 306 return "#ifdef __cplusplus" + lineSep + "extern \"C\" {" + lineSep + "#endif"; 307 } 308 cppGuardEnd()309 protected String cppGuardEnd() { 310 return "#ifdef __cplusplus" + lineSep + "}" + lineSep + "#endif"; 311 } 312 guardBegin(String cname)313 protected String guardBegin(String cname) { 314 return "/* Header for class " + cname + " */" + lineSep + lineSep + 315 "#ifndef _Included_" + cname + lineSep + 316 "#define _Included_" + cname; 317 } 318 guardEnd(String cname)319 protected String guardEnd(String cname) { 320 return "#endif"; 321 } 322 323 /* 324 * File name and file preamble related operations. 325 */ writeFileTop(OutputStream o)326 protected void writeFileTop(OutputStream o) throws Util.Exit { 327 PrintWriter pw = wrapWriter(o); 328 pw.println("/* DO NOT EDIT THIS FILE - it is machine generated */" + lineSep + 329 getIncludes()); 330 } 331 baseFileName(CharSequence className)332 protected String baseFileName(CharSequence className) { 333 return mangler.mangle(className, Mangle.Type.CLASS); 334 } 335 getFileObject(CharSequence className)336 protected FileObject getFileObject(CharSequence className) throws IOException { 337 String name = baseFileName(className) + getFileSuffix(); 338 return fileManager.getFileForOutput(StandardLocation.SOURCE_OUTPUT, "", name, null); 339 } 340 getFileSuffix()341 protected String getFileSuffix() { 342 return ".h"; 343 } 344 345 /** 346 * Including super classes' fields. 347 */ 348 getAllFields(TypeElement subclazz)349 List<VariableElement> getAllFields(TypeElement subclazz) { 350 List<VariableElement> fields = new ArrayList<VariableElement>(); 351 TypeElement cd = null; 352 Stack<TypeElement> s = new Stack<TypeElement>(); 353 354 cd = subclazz; 355 while (true) { 356 s.push(cd); 357 TypeElement c = (TypeElement) (types.asElement(cd.getSuperclass())); 358 if (c == null) 359 break; 360 cd = c; 361 } 362 363 while (!s.empty()) { 364 cd = s.pop(); 365 fields.addAll(ElementFilter.fieldsIn(cd.getEnclosedElements())); 366 } 367 368 return fields; 369 } 370 371 // c.f. MethodDoc.signature signature(ExecutableElement e)372 String signature(ExecutableElement e) { 373 StringBuilder sb = new StringBuilder("("); 374 String sep = ""; 375 for (VariableElement p: e.getParameters()) { 376 sb.append(sep); 377 sb.append(types.erasure(p.asType()).toString()); 378 sep = ","; 379 } 380 sb.append(")"); 381 return sb.toString(); 382 } 383 } 384 385