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