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