1 /* 2 * Copyright (c) 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. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package com.sun.tools.jextract; 24 25 import java.io.IOException; 26 import java.foreign.layout.Layout; 27 import java.nio.file.Path; 28 import java.nio.file.Paths; 29 import java.util.ArrayList; 30 import java.util.Collections; 31 import java.util.HashMap; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.logging.Logger; 37 import java.util.stream.Stream; 38 import jdk.internal.clang.Cursor; 39 import jdk.internal.clang.CursorKind; 40 import jdk.internal.clang.SourceLocation; 41 import jdk.internal.clang.Type; 42 import jdk.internal.foreign.Util; 43 import jdk.internal.org.objectweb.asm.AnnotationVisitor; 44 import jdk.internal.org.objectweb.asm.ClassVisitor; 45 import jdk.internal.org.objectweb.asm.ClassWriter; 46 import jdk.internal.org.objectweb.asm.MethodVisitor; 47 import jdk.internal.org.objectweb.asm.TypeReference; 48 49 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; 50 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ANNOTATION; 51 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 52 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE; 53 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 54 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 55 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 56 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; 57 import static jdk.internal.org.objectweb.asm.Opcodes.DRETURN; 58 import static jdk.internal.org.objectweb.asm.Opcodes.FRETURN; 59 import static jdk.internal.org.objectweb.asm.Opcodes.I2C; 60 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; 61 import static jdk.internal.org.objectweb.asm.Opcodes.LRETURN; 62 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 63 64 /** 65 * Scan a header file and generate classes for entities defined in that header 66 * file. 67 */ 68 final class AsmCodeFactory { 69 private static final String ANNOTATION_PKG_PREFIX = "Ljava/foreign/annotations/"; 70 private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;"; 71 private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;"; 72 private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;"; 73 private static final String NATIVE_STRUCT = ANNOTATION_PKG_PREFIX + "NativeStruct;"; 74 75 private final Context ctx; 76 private final ClassWriter global_cw; 77 // to avoid duplicate generation of methods, field accessors 78 private final Set<String> global_methods = new HashSet<>(); 79 private final Set<String> global_fields = new HashSet<>(); 80 private final String internal_name; 81 private final HeaderFile owner; 82 private final Map<String, byte[]> types; 83 private final Logger logger = Logger.getLogger(getClass().getPackage().getName()); 84 private final List<String> headerDeclarations = new ArrayList<>(); 85 private transient boolean built = false; 86 87 AsmCodeFactory(Context ctx, HeaderFile header) { 88 this.ctx = ctx; 89 logger.info(() -> "Instantiate AsmCodeFactory for " + header.path); 90 this.owner = header; 91 this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName); 92 this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 93 this.types = new HashMap<>(); 94 } 95 96 private String[] getSuperInterfaces() { 97 String[] interfaces = owner.getIncludedFiles(). 98 map(hf -> Utils.toInternalName(hf.pkgName, hf.clsName)). 99 toArray(String[]::new); 100 return interfaces.length != 0 ? interfaces : null; 101 } 102 103 private void generateNativeHeader() { 104 generateMacros(); 105 AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true); 106 av.visit("path", owner.path.toAbsolutePath().toString()); 107 if (owner.libraries != null && !owner.libraries.isEmpty()) { 108 AnnotationVisitor libNames = av.visitArray("libraries"); 109 for (String name : owner.libraries) { 110 libNames.visit(null, name); 111 } 112 libNames.visitEnd(); 113 if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) { 114 AnnotationVisitor libPaths = av.visitArray("libraryPaths"); 115 for (String path : owner.libraryPaths) { 116 libPaths.visit(null, path); 117 } 118 libPaths.visitEnd(); 119 } 120 } 121 av.visit("declarations", String.join(" ", headerDeclarations)); 122 av.visitEnd(); 123 } 124 125 private void handleException(Exception ex) { 126 ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex)); 127 if (Main.DEBUG) { 128 ex.printStackTrace(ctx.err); 129 } 130 } 131 132 private void annotateNativeLocation(ClassVisitor cw, Cursor dcl) { 133 AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true); 134 SourceLocation src = dcl.getSourceLocation(); 135 SourceLocation.Location loc = src.getFileLocation(); 136 Path p = loc.path(); 137 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 138 av.visit("line", loc.line()); 139 av.visit("column", loc.column()); 140 av.visit("USR", dcl.USR()); 141 av.visitEnd(); 142 } 143 144 private void writeClassFile(final ClassWriter cw, String clsName) 145 throws IOException { 146 cw.visitEnd(); 147 byte[] bytecodes = cw.toByteArray(); 148 if (null != types.put(clsName, bytecodes)) { 149 logger.warning("Class " + clsName + " definition is overwritten"); 150 } 151 } 152 153 /** 154 * 155 * @param cw ClassWriter for the struct 156 * @param c The FieldDecl cursor 157 * @param parentType The struct type 158 */ 159 private void addField(ClassVisitor cw, Cursor c, Type parentType) { 160 String fieldName = c.spelling(); 161 if (fieldName.isEmpty()) { 162 //skip anon fields 163 return; 164 } 165 Type t = c.type(); 166 JType jt = owner.globalLookup(t); 167 assert (jt != null); 168 if (cw == global_cw) { 169 String uniqueName = fieldName + "." + jt.getDescriptor(); 170 if (! global_fields.add(uniqueName)) { 171 return; // added already 172 } 173 } 174 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get", 175 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null); 176 177 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 178 SourceLocation src = c.getSourceLocation(); 179 SourceLocation.Location loc = src.getFileLocation(); 180 Path p = loc.path(); 181 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 182 av.visit("line", loc.line()); 183 av.visit("column", loc.column()); 184 av.visit("USR", c.USR()); 185 av.visitEnd(); 186 187 mv.visitEnd(); 188 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$set", 189 "(" + jt.getDescriptor() + ")V", 190 "(" + JType.getPointerVoidAsWildcard(jt) + ")V", null); 191 if (!c.isBitField()) { 192 JType ptrType = new PointerType(jt); 193 cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$ptr", 194 "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(), null); 195 } 196 } 197 198 private void addVar(ClassVisitor cw, Cursor c, Type parentType) { 199 addField(cw, c, parentType); 200 Layout layout = Utils.getLayout(c.type()); 201 String descStr = decorateAsAccessor(c, layout).toString(); 202 addHeaderDecl(c.spelling(), descStr); 203 } 204 205 private void addHeaderDecl(String symbol, String desc) { 206 headerDeclarations.add(String.format("%s=%s", symbol, desc)); 207 } 208 209 private void addConstant(ClassVisitor cw, Cursor c) { 210 assert (c.kind() == CursorKind.EnumConstantDecl); 211 String name = c.spelling(); 212 String desc = owner.globalLookup(c.type()).getDescriptor(); 213 Object value = null; 214 switch (desc) { 215 case "J": 216 value = c.getEnumConstantValue(); 217 break; 218 case "I": 219 value = (int) c.getEnumConstantValue(); 220 break; 221 } 222 cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value); 223 } 224 225 private void createStruct(Cursor cursor) { 226 String nativeName = Utils.getIdentifier(cursor); 227 Type t = cursor.type(); 228 logger.fine(() -> "Create struct: " + nativeName); 229 230 String intf = Utils.toClassName(nativeName); 231 String name = internal_name + "$" + intf; 232 233 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 234 /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not 235 * taking static as a valid flag, so comment this out during development. 236 */ 237 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 238 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 239 cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT, 240 name, "Ljava/lang/Object;Ljava/foreign/memory/Struct<L" + name + ";>;", 241 "java/lang/Object", new String[] {"java/foreign/memory/Struct"}); 242 annotateNativeLocation(cw, cursor); 243 244 AnnotationVisitor av = cw.visitAnnotation(NATIVE_STRUCT, true); 245 Layout structLayout = Utils.getRecordLayout(t, this::decorateAsAccessor); 246 av.visit("value", structLayout.toString()); 247 av.visitEnd(); 248 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 249 250 // fields 251 structFields(cursor).forEach(cx -> addField(cw, cx, cursor.type())); 252 // Write class 253 try { 254 writeClassFile(cw, owner.clsName + "$" + intf); 255 } catch (IOException ex) { 256 handleException(ex); 257 } 258 } 259 260 Layout decorateAsAccessor(Cursor accessorCursor, Layout layout) { 261 String accessorName = accessorCursor.spelling(); 262 layout = layout 263 .withAnnotation("get", accessorName + "$get") 264 .withAnnotation("set", accessorName + "$set"); 265 if (!accessorCursor.isBitField()) { 266 //no pointer accessors for bitfield! 267 layout = layout.withAnnotation("ptr", accessorName + "$ptr"); 268 } 269 return layout; 270 } 271 272 // A stream of fields of a struct (or union). Note that we have to include 273 // fields from nested annoymous unions and structs in the containing struct. 274 private Stream<Cursor> structFields(Cursor cursor) { 275 return cursor.children() 276 .flatMap(c -> c.isAnonymousStruct()? structFields(c) : Stream.of(c)) 277 .filter(c -> c.kind() == CursorKind.FieldDecl); 278 } 279 280 private void createEnum(Cursor cursor) { 281 // define enum constants in global_cw 282 cursor.stream() 283 .filter(cx -> cx.kind() == CursorKind.EnumConstantDecl) 284 .forEachOrdered(cx -> addConstant(global_cw, cx)); 285 286 if (cursor.isAnonymousEnum()) { 287 // We are done with anonymous enum 288 return; 289 } 290 291 // generate annotation class for named enum 292 createAnnotationCls(cursor); 293 } 294 295 private void createAnnotationCls(Cursor dcl) { 296 String nativeName = Utils.getIdentifier(dcl); 297 logger.fine(() -> "Create annotation for: " + nativeName); 298 299 String intf = Utils.toClassName(nativeName); 300 String name = internal_name + "$" + intf; 301 302 logger.fine(() -> "Define class " + name + " for native type " + nativeName); 303 global_cw.visitInnerClass(name, internal_name, intf, 304 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 305 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 306 String[] superAnno = { "java/lang/annotation/Annotation" }; 307 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION, 308 name, null, "java/lang/Object", superAnno); 309 annotateNativeLocation(cw, dcl); 310 Type t = dcl.type().canonicalType(); 311 AnnotationVisitor av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true); 312 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE"); 313 av.visitEnd(); 314 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true); 315 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME"); 316 av.visitEnd(); 317 cw.visitInnerClass(name, internal_name, intf, 318 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION); 319 // Write class 320 try { 321 writeClassFile(cw, owner.clsName + "$" + intf); 322 } catch (IOException ex) { 323 handleException(ex); 324 } 325 } 326 327 private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) { 328 JType.Function fn = fnif.getFunction(); 329 String intf; 330 String nativeName; 331 String nDesc = fnif.getFunction().getNativeDescriptor(); 332 if (dcl == null) { 333 intf = ((JType.InnerType) fnif.type).getName(); 334 nativeName = "anonymous function"; 335 } else { 336 nativeName = Utils.getIdentifier(dcl); 337 intf = Utils.toClassName(nativeName); 338 } 339 logger.fine(() -> "Create FunctionalInterface " + intf); 340 341 final String name = internal_name + "$" + intf; 342 343 logger.fine(() -> "Define class " + name + " for native type " + nativeName + nDesc); 344 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 345 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 346 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 347 name, "Ljava/lang/Object;Ljava/foreign/memory/Callback<L" + name + ";>;", 348 "java/lang/Object", new String[] {"java/foreign/memory/Callback"}); 349 if (dcl != null) { 350 annotateNativeLocation(cw, dcl); 351 } 352 AnnotationVisitor av = cw.visitAnnotation( 353 "Ljava/lang/FunctionalInterface;", true); 354 av.visitEnd(); 355 av = cw.visitAnnotation(NATIVE_CALLBACK, true); 356 av.visit("value", nDesc); 357 av.visitEnd(); 358 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 359 360 // add the method 361 362 int flags = ACC_PUBLIC | ACC_ABSTRACT; 363 if (fn.isVarArgs) { 364 flags |= ACC_VARARGS; 365 } 366 MethodVisitor mv = cw.visitMethod(flags, "fn", 367 fn.getDescriptor(), fn.getSignature(), null); 368 mv.visitEnd(); 369 // Write class 370 try { 371 writeClassFile(cw, owner.clsName + "$" + intf); 372 } catch (IOException ex) { 373 handleException(ex); 374 } 375 } 376 377 private void defineType(Cursor dcl, TypeAlias alias) { 378 if (alias.getAnnotationDescriptor() != null) { 379 createAnnotationCls(dcl); 380 } else { 381 JType real = alias.canonicalType(); 382 if (real instanceof JType.FnIf) { 383 createFunctionalInterface(dcl, (JType.FnIf) real); 384 } 385 // Otherwise, type alias is a same named stuct 386 } 387 } 388 389 private void addMethod(Cursor dcl, JType.Function fn) { 390 String uniqueName = dcl.spelling() + "." + fn.getDescriptor(); 391 if (! global_methods.add(uniqueName)) { 392 return; // added already 393 } 394 logger.fine(() -> "Add method: " + fn.getSignature()); 395 int flags = ACC_PUBLIC | ACC_ABSTRACT; 396 if (fn.isVarArgs) { 397 flags |= ACC_VARARGS; 398 } 399 MethodVisitor mv = global_cw.visitMethod(flags, 400 dcl.spelling(), fn.getDescriptor(), fn.getSignature(), null); 401 final int arg_cnt = dcl.numberOfArgs(); 402 for (int i = 0; i < arg_cnt; i++) { 403 String name = dcl.getArgument(i).spelling(); 404 final int tmp = i; 405 logger.finer(() -> " arg " + tmp + ": " + name); 406 mv.visitParameter(name, 0); 407 } 408 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 409 SourceLocation src = dcl.getSourceLocation(); 410 SourceLocation.Location loc = src.getFileLocation(); 411 Path p = loc.path(); 412 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 413 av.visit("line", loc.line()); 414 av.visit("column", loc.column()); 415 av.visit("USR", dcl.USR()); 416 av.visitEnd(); 417 Type t = dcl.type(); 418 final String descStr = Utils.getFunction(t).toString(); 419 addHeaderDecl(dcl.spelling(), descStr); 420 421 int idx = 0; 422 for (JType arg: fn.args) { 423 if (arg instanceof TypeAlias) { 424 TypeAlias alias = (TypeAlias) arg; 425 final int tmp = idx; 426 logger.finest(() -> " arg " + tmp + " is an alias " + alias); 427 if (alias.getAnnotationDescriptor() != null) { 428 mv.visitTypeAnnotation( 429 TypeReference.newFormalParameterReference(idx).getValue(), 430 null, alias.getAnnotationDescriptor(), true) 431 .visitEnd(); 432 } 433 } 434 idx++; 435 } 436 437 if (fn.returnType instanceof TypeAlias) { 438 TypeAlias alias = (TypeAlias) fn.returnType; 439 logger.finest(() -> " return type is an alias " + alias); 440 if (alias.getAnnotationDescriptor() != null) { 441 mv.visitTypeAnnotation( 442 TypeReference.newTypeReference(TypeReference.METHOD_RETURN).getValue(), 443 null, alias.getAnnotationDescriptor(), true) 444 .visitEnd(); 445 } 446 } 447 mv.visitEnd(); 448 } 449 450 protected AsmCodeFactory addType(JType jt, Cursor cursor) { 451 JType2 jt2 = null; 452 if (jt instanceof JType2) { 453 jt2 = (JType2) jt; 454 jt = jt2.getDelegate(); 455 } else { 456 logger.warning(() -> "Should have JType2 in addType"); 457 if (Main.DEBUG) { 458 new Throwable().printStackTrace(ctx.err); 459 } 460 } 461 if (cursor == null) { 462 assert (jt2 != null); 463 if (jt instanceof JType.FnIf) { 464 createFunctionalInterface(null, (JType.FnIf) jt); 465 } 466 return this; 467 } 468 boolean noDef = cursor.isInvalid(); 469 if (noDef) { 470 cursor = jt2.getCursor(); 471 } 472 473 final Cursor c = cursor; 474 475 try { 476 logger.fine(() -> "Process cursor " + c.spelling()); 477 switch (cursor.kind()) { 478 case StructDecl: 479 case UnionDecl: 480 createStruct(cursor); 481 break; 482 case FunctionDecl: 483 assert (jt instanceof JType.Function); 484 addMethod(cursor, (JType.Function) jt); 485 break; 486 case EnumDecl: 487 createEnum(cursor); 488 break; 489 case TypedefDecl: 490 // anonymous typedef struct {} xxx will not get TypeAlias 491 if (jt instanceof TypeAlias) { 492 defineType(cursor, (TypeAlias) jt); 493 } 494 break; 495 case VarDecl: 496 addVar(global_cw, cursor, null); 497 break; 498 default: 499 logger.warning(() -> "Unsupported declaration Cursor:"); 500 logger.warning(() -> Printer.Stringifier(p -> p.dumpCursor(c, true))); 501 break; 502 } 503 } catch (Exception ex) { 504 handleException(ex); 505 logger.warning("Cursor causing above exception is: " + c.spelling()); 506 logger.warning(() -> Printer.Stringifier(p -> p.dumpCursor(c, true))); 507 } 508 return this; 509 } 510 511 AsmCodeFactory generateMacros() { 512 for (MacroParser.Macro macro : ctx.macros(owner)) { 513 if (macro.isConstantMacro()) { 514 logger.fine(() -> "Adding macro " + macro.name()); 515 Object value = macro.value(); 516 Class<?> macroType = (Class<?>) Util.unboxIfNeeded(value.getClass()); 517 518 String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(macroType)); 519 MethodVisitor mv = global_cw.visitMethod(ACC_PUBLIC, macro.name(), sig, sig, null); 520 521 Cursor cursor = macro.cursor(); 522 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true); 523 SourceLocation src = cursor.getSourceLocation(); 524 SourceLocation.Location loc = src.getFileLocation(); 525 Path p = loc.path(); 526 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString()); 527 av.visit("line", loc.line()); 528 av.visit("column", loc.column()); 529 av.visit("USR", cursor.USR()); 530 av.visitEnd(); 531 532 mv.visitCode(); 533 534 mv.visitLdcInsn(value); 535 if (macroType.equals(char.class)) { 536 mv.visitInsn(I2C); 537 mv.visitInsn(IRETURN); 538 } else if (macroType.equals(int.class)) { 539 mv.visitInsn(IRETURN); 540 } else if (macroType.equals(float.class)) { 541 mv.visitInsn(FRETURN); 542 } else if (macroType.equals(long.class)) { 543 mv.visitInsn(LRETURN); 544 } else if (macroType.equals(double.class)) { 545 mv.visitInsn(DRETURN); 546 } else if (macroType.equals(String.class)) { 547 mv.visitInsn(ARETURN); 548 } 549 mv.visitMaxs(0, 0); 550 mv.visitEnd(); 551 } else { 552 logger.fine(() -> "Skipping unrecognized object-like macro " + macro.name()); 553 } 554 } 555 556 return this; 557 } 558 559 protected synchronized void produce() { 560 if (built) { 561 throw new IllegalStateException("Produce is called multiple times"); 562 } 563 built = true; 564 global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, 565 internal_name, 566 null, "java/lang/Object", getSuperInterfaces()); 567 generateNativeHeader(); 568 try { 569 writeClassFile(global_cw, owner.clsName); 570 } catch (IOException ex) { 571 handleException(ex); 572 } 573 } 574 575 protected Map<String, byte[]> collect() { 576 // Ensure classes are produced 577 if (!built) produce(); 578 HashMap<String, byte[]> rv = new HashMap<>(); 579 // Not copying byte[] for efficiency, perhaps not a safe idea though 580 if (owner.pkgName.isEmpty()) { 581 types.forEach((clsName, bytecodes) -> { 582 rv.put(clsName, bytecodes); 583 }); 584 } else { 585 types.forEach((clsName, bytecodes) -> { 586 rv.put(owner.pkgName + "." + clsName, bytecodes); 587 }); 588 } 589 return Collections.unmodifiableMap(rv); 590 } 591 592 public static void main(String[] args) throws IOException { 593 final Path file = Paths.get(args[1]); 594 final String pkg = args[0]; 595 Context ctx = new Context(); 596 ctx.usePackageForFolder(file, pkg); 597 ctx.usePackageForFolder(Paths.get("/usr/include"), "system"); 598 ctx.addSource(file); 599 ctx.parse(); 600 ctx.collectJarFile(Paths.get(args[2]), args, pkg); 601 } 602 } --- EOF ---