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