28
29 import java.io.IOException;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Map;
36 import java.util.logging.Logger;
37 import java.util.stream.Stream;
38
39 import static jdk.internal.org.objectweb.asm.Opcodes.*;
40
41 /**
42 * Scan a header file and generate classes for entities defined in that header
43 * file.
44 */
45 final class AsmCodeFactory extends CodeFactory {
46 private static final String ANNOTATION_PKG_PREFIX = "Ljava/nicl/metadata/";
47 private static final String ARRAY = ANNOTATION_PKG_PREFIX + "Array;";
48 private static final String C = ANNOTATION_PKG_PREFIX + "C;";
49 private static final String CALLING_CONVENTION = ANNOTATION_PKG_PREFIX + "CallingConvention;";
50 private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
51 private static final String NATIVE_TYPE = ANNOTATION_PKG_PREFIX + "NativeType;";
52 private static final String OFFSET = ANNOTATION_PKG_PREFIX + "Offset;";
53
54 private final Context ctx;
55 private final ClassWriter global_cw;
56 private final String internal_name;
57 private final HeaderFile owner;
58 private final Map<String, byte[]> types;
59 private final HashSet<String> handledMacros = new HashSet<>();
60 private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
61
62 AsmCodeFactory(Context ctx, HeaderFile header) {
63 this.ctx = ctx;
64 logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
65 this.owner = header;
66 this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
67 this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
68 this.types = new HashMap<>();
69 init();
70 }
71
72 private void init() {
73 global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
74 internal_name,
75 null, "java/lang/Object", null);
76 AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
77 av.visit("headerPath", owner.path.toAbsolutePath().toString());
78 if (owner.libraries != null && !owner.libraries.isEmpty()) {
79 AnnotationVisitor libNames = av.visitArray("libraries");
80 for (String name : owner.libraries) {
81 libNames.visit(null, name);
82 }
83 libNames.visitEnd();
84 if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) {
85 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
86 for (String path : owner.libraryPaths) {
87 libPaths.visit(null, path);
88 }
89 libPaths.visitEnd();
90 }
91 }
92 av.visitEnd();
93 }
94
95 private void handleException(Exception ex) {
96 ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
97 if (Main.DEBUG) {
98 ex.printStackTrace(ctx.err);
99 }
100 }
101
102 private void annotateC(ClassVisitor cw, Cursor dcl) {
103 AnnotationVisitor av = cw.visitAnnotation(C, true);
104 SourceLocation src = dcl.getSourceLocation();
105 SourceLocation.Location loc = src.getFileLocation();
106 Path p = loc.path();
107 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
108 av.visit("line", loc.line());
109 av.visit("column", loc.column());
110 av.visit("USR", dcl.USR());
111 av.visitEnd();
112 }
113
114 private void writeClassFile(final ClassWriter cw, String clsName)
115 throws IOException {
116 cw.visitEnd();
117 byte[] bytecodes = cw.toByteArray();
118 if (null != types.put(clsName, bytecodes)) {
119 logger.warning("Class " + clsName + " definition is overwritten");
120 }
121 }
122
123 /**
124 *
125 * @param cw ClassWriter for the struct
126 * @param c The FieldDecl cursor
127 * @param parentType The struct type
128 */
129 private void addField(ClassVisitor cw, Cursor c, Type parentType) {
130 String fieldName = c.spelling();
131 Type t = c.type();
132 JType jt = owner.globalLookup(t);
133 assert (jt != null);
134 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
135 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
136
137 AnnotationVisitor av = mv.visitAnnotation(C, true);
138 SourceLocation src = c.getSourceLocation();
139 SourceLocation.Location loc = src.getFileLocation();
140 Path p = loc.path();
141 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
142 av.visit("line", loc.line());
143 av.visit("column", loc.column());
144 av.visit("USR", c.USR());
145 av.visitEnd();
146
147 av = mv.visitAnnotation(NATIVE_TYPE, true);
148 av.visit("layout", Utils.getLayout(t));
149 av.visit("ctype", t.spelling());
150 av.visit("size", t.size());
151 av.visit("name", fieldName);
152 av.visitEnd();
153
154 if (t.kind() == TypeKind.ConstantArray || t.kind() == TypeKind.IncompleteArray) {
155 logger.finer(() -> "Array field " + fieldName + ", type " + t.kind().name());
156 av = mv.visitAnnotation(ARRAY, true);
157 av.visit("elementType", t.getElementType().canonicalType().spelling());
158 av.visit("elementSize", t.getElementType().canonicalType().size());
159 if (t.kind() == TypeKind.ConstantArray) {
160 av.visit("length", t.getNumberOfElements());
161 }
162 av.visitEnd();
163 }
164
165 if (parentType != null) {
166 long offset = parentType.getOffsetOf(fieldName);
167 av = mv.visitAnnotation(OFFSET, true);
168 av.visit("offset", offset);
169 if (c.isBitField()) {
170 av.visit("bits", c.getBitFieldWidth());
195 cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value);
196 }
197
198 private void createStruct(Cursor cursor) {
199 String nativeName = Utils.getIdentifier(cursor);
200 Type t = cursor.type();
201 logger.fine(() -> "Create struct: " + nativeName);
202
203 String intf = Utils.toClassName(nativeName);
204 String name = internal_name + "$" + intf;
205
206 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
207 /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
208 * taking static as a valid flag, so comment this out during development.
209 */
210 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
211 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
212 cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
213 name, "Ljava/lang/Object;Ljava/nicl/types/Struct<L" + name + ";>;",
214 "java/lang/Object", new String[] {"java/nicl/types/Struct"});
215 annotateC(cw, cursor);
216 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true);
217 av.visit("layout", Utils.getLayout(t));
218 av.visit("ctype", t.spelling());
219 av.visit("size", t.size());
220 av.visit("isRecordType", true);
221 av.visitEnd();
222 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
223
224 // fields
225 Printer dbg = new Printer();
226 structFields(cursor).forEachOrdered(cx -> addField(cw, cx, cursor.type()));
227 // Write class
228 try {
229 writeClassFile(cw, owner.clsName + "$" + intf);
230 } catch (IOException ex) {
231 handleException(ex);
232 }
233 }
234
235 // A stream of fields of a struct (or union). Note that we have to include
236 // fields from nested annoymous unions and structs in the containing struct.
237 private Stream<Cursor> structFields(Cursor cursor) {
238 return cursor.children()
239 .flatMap(c -> c.isAnonymousStruct()? structFields(c) : Stream.of(c))
240 .filter(c -> c.kind() == CursorKind.FieldDecl);
252 }
253
254 // generate annotation class for named enum
255 createAnnotationCls(cursor);
256 }
257
258 private void createAnnotationCls(Cursor dcl) {
259 String nativeName = Utils.getIdentifier(dcl);
260 logger.fine(() -> "Create annotation for: " + nativeName);
261
262 String intf = Utils.toClassName(nativeName);
263 String name = internal_name + "$" + intf;
264
265 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
266 global_cw.visitInnerClass(name, internal_name, intf,
267 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
268 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
269 String[] superAnno = { "java/lang/annotation/Annotation" };
270 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
271 name, null, "java/lang/Object", superAnno);
272 annotateC(cw, dcl);
273 Type t = dcl.type().canonicalType();
274 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true);
275 av.visit("layout", Utils.getLayout(t));
276 av.visit("ctype", t.spelling());
277 av.visit("size", t.size());
278 av.visitEnd();
279
280 av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
281 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
282 av.visitEnd();
283 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
284 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
285 av.visitEnd();
286 cw.visitInnerClass(name, internal_name, intf,
287 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
288 // Write class
289 try {
290 writeClassFile(cw, owner.clsName + "$" + intf);
291 } catch (IOException ex) {
292 handleException(ex);
293 }
294 }
295
296 private void createFunctionalInterface(JType2 jt2) {
297 JType.FnIf fnif = (JType.FnIf) jt2.getDelegate();
298 JType.Function fn = fnif.getFunction();
299 String intf = ((JType.InnerType) fnif.type).getName();
300 logger.fine(() -> "Create FunctionalInterface " + intf);
301 String nDesc = jt2.getNativeDescriptor();
302
303 final String name = internal_name + "$" + intf;
304
305 logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc);
306 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
307 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
308 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
309 name, null, "java/lang/Object", null);
310 AnnotationVisitor av = cw.visitAnnotation(
311 "Ljava/lang/FunctionalInterface;", true);
312 av.visitEnd();
313 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
314
315 // add the method
316 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
317 fn.getDescriptor(), fn.getSignature(), null);
318 av = mv.visitAnnotation(NATIVE_TYPE, true);
319 av.visit("layout", nDesc);
320 av.visit("ctype", "N/A");
321 av.visit("size", -1);
322 av.visitEnd();
323 // FIXME: We need calling convention
324 av = mv.visitAnnotation(CALLING_CONVENTION, true);
325 av.visit("value", jt2.getCallingConvention());
326 av.visitEnd();
327
328 mv.visitEnd();
329 // Write class
330 try {
331 writeClassFile(cw, owner.clsName + "$" + intf);
332 } catch (IOException ex) {
333 handleException(ex);
334 }
335 }
336
337 private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) {
338 JType.Function fn = fnif.getFunction();
339 String intf;
340 String nativeName;
341 if (dcl == null) {
342 intf = ((JType.InnerType) fnif.type).getName();
343 nativeName = "N/A";
344 } else {
345 nativeName = Utils.getIdentifier(dcl);
346 intf = Utils.toClassName(nativeName);
347 }
348 logger.fine(() -> "Create FunctionalInterface " + intf);
349
350 final String name = internal_name + "$" + intf;
351
352 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
353 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
354 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
355 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
356 name, null, "java/lang/Object", null);
357 if (dcl != null) {
358 annotateC(cw, dcl);
359 }
360 AnnotationVisitor av = cw.visitAnnotation(
361 "Ljava/lang/FunctionalInterface;", true);
362 av.visitEnd();
363 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
364
365 // add the method
366 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
367 fn.getDescriptor(), fn.getSignature(), null);
368 if (dcl != null) {
369 av = mv.visitAnnotation(NATIVE_TYPE, true);
370 Type t = dcl.type().canonicalType();
371 av.visit("layout", Utils.getLayout(t));
372 av.visit("ctype", t.spelling());
373 av.visit("size", t.size());
374 av.visitEnd();
375 av = mv.visitAnnotation(CALLING_CONVENTION, true);
376 av.visit("value", t.getCallingConvention().value());
377 av.visitEnd();
378 }
379 mv.visitEnd();
380 // Write class
381 try {
382 writeClassFile(cw, owner.clsName + "$" + intf);
383 } catch (IOException ex) {
384 handleException(ex);
385 }
386 }
387
388 private void defineType(Cursor dcl, TypeAlias alias) {
389 if (alias.getAnnotationDescriptor() != null) {
390 createAnnotationCls(dcl);
391 } else {
392 JType real = alias.canonicalType();
393 if (real instanceof JType.FnIf) {
394 createFunctionalInterface(dcl, (JType.FnIf) real);
395 }
396 // Otherwise, type alias is a same named stuct
397 }
398 }
399
400 private void addMethod(Cursor dcl, JType.Function fn) {
401 logger.fine(() -> "Add method: " + fn.getSignature());
402 int flags = ACC_PUBLIC | ACC_ABSTRACT;
403 if (fn.isVarArgs) {
404 flags |= ACC_VARARGS;
405 }
406 MethodVisitor mv = global_cw.visitMethod(flags,
407 dcl.spelling(), fn.getDescriptor(), fn.getSignature(), null);
408 final int arg_cnt = dcl.numberOfArgs();
409 for (int i = 0; i < arg_cnt; i++) {
410 String name = dcl.getArgument(i).spelling();
411 final int tmp = i;
412 logger.finer(() -> " arg " + tmp + ": " + name);
413 mv.visitParameter(name, 0);
414 }
415 AnnotationVisitor av = mv.visitAnnotation(C, true);
416 SourceLocation src = dcl.getSourceLocation();
417 SourceLocation.Location loc = src.getFileLocation();
418 Path p = loc.path();
419 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
420 av.visit("line", loc.line());
421 av.visit("column", loc.column());
422 av.visit("USR", dcl.USR());
423 av.visitEnd();
424 av = mv.visitAnnotation(NATIVE_TYPE, true);
425 Type t = dcl.type();
426 av.visit("layout", Utils.getLayout(t));
427 av.visit("ctype", t.spelling());
428 av.visit("name", dcl.spelling());
429 av.visit("size", t.size());
430 av.visitEnd();
431 av = mv.visitAnnotation(CALLING_CONVENTION, true);
432 av.visit("value", t.getCallingConvention().value());
433 av.visitEnd();
434
435 int idx = 0;
436 for (JType arg: fn.args) {
437 if (arg instanceof TypeAlias) {
438 TypeAlias alias = (TypeAlias) arg;
439 final int tmp = idx;
440 logger.finest(() -> " arg " + tmp + " is an alias " + alias);
441 if (alias.getAnnotationDescriptor() != null) {
442 mv.visitTypeAnnotation(
443 TypeReference.newFormalParameterReference(idx).getValue(),
444 null, alias.getAnnotationDescriptor(), true)
445 .visitEnd();
446 }
447 }
448 idx++;
449 }
450
451 if (fn.returnType instanceof TypeAlias) {
452 TypeAlias alias = (TypeAlias) fn.returnType;
453 logger.finest(() -> " return type is an alias " + alias);
454 if (alias.getAnnotationDescriptor() != null) {
641 for (String token : tokens) {
642 logger.finest(() -> "TOKEN: " + token);
643 }
644
645 if (tokens.length != 2) {
646 logger.fine(() -> "Skipping macro " + tokens[0] + " because it doesn't have exactly 2 tokens");
647 return this;
648 }
649
650 int flags = ACC_PUBLIC;
651
652 Literal l = Literal.parse(tokens[1]);
653 if (l == null) {
654 logger.fine(() -> "Skipping macro " + tokens[0] + " because its body isn't a recognizable literal constant");
655 return this;
656 }
657
658 String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(l.type().getTypeClass()));
659 MethodVisitor mv = global_cw.visitMethod(flags, macroName, sig, sig, null);
660
661 AnnotationVisitor av = mv.visitAnnotation(C, true);
662 SourceLocation src = cursor.getSourceLocation();
663 SourceLocation.Location loc = src.getFileLocation();
664 Path p = loc.path();
665 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
666 av.visit("line", loc.line());
667 av.visit("column", loc.column());
668 av.visit("USR", cursor.USR());
669 av.visitEnd();
670
671 mv.visitCode();
672 mv.visitLdcInsn(l.getValue());
673 switch (l.type()) {
674 case INT:
675 mv.visitInsn(IRETURN);
676 break;
677 case LONG:
678 mv.visitInsn(LRETURN);
679 break;
680 case STRING:
681 mv.visitInsn(ARETURN);
682 break;
683 }
684 mv.visitMaxs(0, 0);
685 mv.visitEnd();
686
687 handledMacros.add(macroName);
688
689 return this;
690 }
691
692 @Override
693 protected void produce() {
694 try {
695 writeClassFile(global_cw, owner.clsName);
696 } catch (IOException ex) {
697 handleException(ex);
698 }
699 }
700
701 @Override
702 protected Map<String, byte[]> collect() {
703 // Ensure classes are produced
704 produce();
705 HashMap<String, byte[]> rv = new HashMap<>();
706 // Not copying byte[] for efficiency, perhaps not a safe idea though
707 if (owner.pkgName.isEmpty()) {
708 types.forEach((clsName, bytecodes) -> {
709 rv.put(clsName, bytecodes);
710 });
711 } else {
712 types.forEach((clsName, bytecodes) -> {
713 rv.put(owner.pkgName + "." + clsName, bytecodes);
|
28
29 import java.io.IOException;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.Map;
36 import java.util.logging.Logger;
37 import java.util.stream.Stream;
38
39 import static jdk.internal.org.objectweb.asm.Opcodes.*;
40
41 /**
42 * Scan a header file and generate classes for entities defined in that header
43 * file.
44 */
45 final class AsmCodeFactory extends CodeFactory {
46 private static final String ANNOTATION_PKG_PREFIX = "Ljava/nicl/metadata/";
47 private static final String ARRAY = ANNOTATION_PKG_PREFIX + "Array;";
48 private static final String NATIVE_CALLBACK = ANNOTATION_PKG_PREFIX + "NativeCallback;";
49 private static final String NATIVE_HEADER = ANNOTATION_PKG_PREFIX + "NativeHeader;";
50 private static final String NATIVE_LOCATION = ANNOTATION_PKG_PREFIX + "NativeLocation;";
51 private static final String NATIVE_TYPE = ANNOTATION_PKG_PREFIX + "NativeType;";
52 private static final String OFFSET = ANNOTATION_PKG_PREFIX + "Offset;";
53
54 private final Context ctx;
55 private final ClassWriter global_cw;
56 private final String internal_name;
57 private final HeaderFile owner;
58 private final Map<String, byte[]> types;
59 private final HashSet<String> handledMacros = new HashSet<>();
60 private final Logger logger = Logger.getLogger(getClass().getPackage().getName());
61 private final StringBuilder headerDeclarations = new StringBuilder();
62
63 AsmCodeFactory(Context ctx, HeaderFile header) {
64 this.ctx = ctx;
65 logger.info(() -> "Instantiate AsmCodeFactory for " + header.path);
66 this.owner = header;
67 this.internal_name = Utils.toInternalName(owner.pkgName, owner.clsName);
68 this.global_cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
69 this.types = new HashMap<>();
70 global_cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
71 internal_name,
72 null, "java/lang/Object", null);
73 }
74
75 private void generateNativeHeader() {
76 AnnotationVisitor av = global_cw.visitAnnotation(NATIVE_HEADER, true);
77 av.visit("path", owner.path.toAbsolutePath().toString());
78 if (owner.libraries != null && !owner.libraries.isEmpty()) {
79 AnnotationVisitor libNames = av.visitArray("libraries");
80 for (String name : owner.libraries) {
81 libNames.visit(null, name);
82 }
83 libNames.visitEnd();
84 if (owner.libraryPaths != null && !owner.libraryPaths.isEmpty()) {
85 AnnotationVisitor libPaths = av.visitArray("libraryPaths");
86 for (String path : owner.libraryPaths) {
87 libPaths.visit(null, path);
88 }
89 libPaths.visitEnd();
90 }
91 }
92 av.visit("declarations", headerDeclarations.toString());
93 av.visitEnd();
94 }
95
96 private void handleException(Exception ex) {
97 ctx.err.println(Main.format("cannot.write.class.file", owner.pkgName + "." + owner.clsName, ex));
98 if (Main.DEBUG) {
99 ex.printStackTrace(ctx.err);
100 }
101 }
102
103 private void annotateNativeLocation(ClassVisitor cw, Cursor dcl) {
104 AnnotationVisitor av = cw.visitAnnotation(NATIVE_LOCATION, true);
105 SourceLocation src = dcl.getSourceLocation();
106 SourceLocation.Location loc = src.getFileLocation();
107 Path p = loc.path();
108 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
109 av.visit("line", loc.line());
110 av.visit("column", loc.column());
111 av.visit("USR", dcl.USR());
112 av.visitEnd();
113 }
114
115 private void writeClassFile(final ClassWriter cw, String clsName)
116 throws IOException {
117 cw.visitEnd();
118 byte[] bytecodes = cw.toByteArray();
119 if (null != types.put(clsName, bytecodes)) {
120 logger.warning("Class " + clsName + " definition is overwritten");
121 }
122 }
123
124 /**
125 *
126 * @param cw ClassWriter for the struct
127 * @param c The FieldDecl cursor
128 * @param parentType The struct type
129 */
130 private void addField(ClassVisitor cw, Cursor c, Type parentType) {
131 String fieldName = c.spelling();
132 Type t = c.type();
133 JType jt = owner.globalLookup(t);
134 assert (jt != null);
135 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, fieldName + "$get",
136 "()" + jt.getDescriptor(), "()" + jt.getSignature(), null);
137
138 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
139 SourceLocation src = c.getSourceLocation();
140 SourceLocation.Location loc = src.getFileLocation();
141 Path p = loc.path();
142 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
143 av.visit("line", loc.line());
144 av.visit("column", loc.column());
145 av.visit("USR", c.USR());
146 av.visitEnd();
147
148 av = mv.visitAnnotation(NATIVE_TYPE, true);
149 av.visit("layout", Utils.getLayout(t));
150 av.visit("ctype", t.spelling());
151 av.visit("name", fieldName);
152 av.visitEnd();
153
154 if (t.kind() == TypeKind.ConstantArray || t.kind() == TypeKind.IncompleteArray) {
155 logger.finer(() -> "Array field " + fieldName + ", type " + t.kind().name());
156 av = mv.visitAnnotation(ARRAY, true);
157 av.visit("elementType", t.getElementType().canonicalType().spelling());
158 av.visit("elementSize", t.getElementType().canonicalType().size());
159 if (t.kind() == TypeKind.ConstantArray) {
160 av.visit("length", t.getNumberOfElements());
161 }
162 av.visitEnd();
163 }
164
165 if (parentType != null) {
166 long offset = parentType.getOffsetOf(fieldName);
167 av = mv.visitAnnotation(OFFSET, true);
168 av.visit("offset", offset);
169 if (c.isBitField()) {
170 av.visit("bits", c.getBitFieldWidth());
195 cw.visitField(ACC_PUBLIC | ACC_FINAL | ACC_STATIC, name, desc, null, value);
196 }
197
198 private void createStruct(Cursor cursor) {
199 String nativeName = Utils.getIdentifier(cursor);
200 Type t = cursor.type();
201 logger.fine(() -> "Create struct: " + nativeName);
202
203 String intf = Utils.toClassName(nativeName);
204 String name = internal_name + "$" + intf;
205
206 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
207 /* FIXME: Member interface is implicit static, also ASM.CheckClassAdapter is not
208 * taking static as a valid flag, so comment this out during development.
209 */
210 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
211 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
212 cw.visit(V1_8, ACC_PUBLIC /*| ACC_STATIC*/ | ACC_INTERFACE | ACC_ABSTRACT,
213 name, "Ljava/lang/Object;Ljava/nicl/types/Struct<L" + name + ";>;",
214 "java/lang/Object", new String[] {"java/nicl/types/Struct"});
215 annotateNativeLocation(cw, cursor);
216 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true);
217 av.visit("layout", Utils.getLayout(t));
218 av.visit("ctype", t.spelling());
219 av.visit("size", t.size());
220 av.visitEnd();
221 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
222
223 // fields
224 Printer dbg = new Printer();
225 structFields(cursor).forEachOrdered(cx -> addField(cw, cx, cursor.type()));
226 // Write class
227 try {
228 writeClassFile(cw, owner.clsName + "$" + intf);
229 } catch (IOException ex) {
230 handleException(ex);
231 }
232 }
233
234 // A stream of fields of a struct (or union). Note that we have to include
235 // fields from nested annoymous unions and structs in the containing struct.
236 private Stream<Cursor> structFields(Cursor cursor) {
237 return cursor.children()
238 .flatMap(c -> c.isAnonymousStruct()? structFields(c) : Stream.of(c))
239 .filter(c -> c.kind() == CursorKind.FieldDecl);
251 }
252
253 // generate annotation class for named enum
254 createAnnotationCls(cursor);
255 }
256
257 private void createAnnotationCls(Cursor dcl) {
258 String nativeName = Utils.getIdentifier(dcl);
259 logger.fine(() -> "Create annotation for: " + nativeName);
260
261 String intf = Utils.toClassName(nativeName);
262 String name = internal_name + "$" + intf;
263
264 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
265 global_cw.visitInnerClass(name, internal_name, intf,
266 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
267 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
268 String[] superAnno = { "java/lang/annotation/Annotation" };
269 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION,
270 name, null, "java/lang/Object", superAnno);
271 annotateNativeLocation(cw, dcl);
272 Type t = dcl.type().canonicalType();
273 AnnotationVisitor av = cw.visitAnnotation(NATIVE_TYPE, true);
274 av.visit("layout", Utils.getLayout(t));
275 av.visit("ctype", t.spelling());
276 av.visit("size", t.size());
277 av.visitEnd();
278
279 av = cw.visitAnnotation("Ljava/lang/annotation/Target;", true);
280 av.visitEnum("value", "Ljava/lang/annotation/ElementType;", "TYPE_USE");
281 av.visitEnd();
282 av = cw.visitAnnotation("Ljava/lang/annotation/Retention;", true);
283 av.visitEnum("value", "Ljava/lang/annotation/RetentionPolicy;", "RUNTIME");
284 av.visitEnd();
285 cw.visitInnerClass(name, internal_name, intf,
286 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE | ACC_ANNOTATION);
287 // Write class
288 try {
289 writeClassFile(cw, owner.clsName + "$" + intf);
290 } catch (IOException ex) {
291 handleException(ex);
292 }
293 }
294
295 private void createFunctionalInterface(JType2 jt2) {
296 JType.FnIf fnif = (JType.FnIf) jt2.getDelegate();
297 JType.Function fn = fnif.getFunction();
298 String intf = ((JType.InnerType) fnif.type).getName();
299 logger.fine(() -> "Create FunctionalInterface " + intf);
300 String nDesc = jt2.getNativeDescriptor();
301
302 final String name = internal_name + "$" + intf;
303
304 logger.fine(() -> "Define class " + name + " for anonymous function " + nDesc);
305 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
306 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
307 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
308 name, null, "java/lang/Object", null);
309 AnnotationVisitor av = cw.visitAnnotation(
310 "Ljava/lang/FunctionalInterface;", true);
311 av.visitEnd();
312 av = cw.visitAnnotation(NATIVE_CALLBACK, true);
313 av.visit("value", nDesc);
314 av.visitEnd();
315 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
316
317 // add the method
318 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "fn",
319 fn.getDescriptor(), fn.getSignature(), null);
320 av.visitEnd();
321
322 mv.visitEnd();
323 // Write class
324 try {
325 writeClassFile(cw, owner.clsName + "$" + intf);
326 } catch (IOException ex) {
327 handleException(ex);
328 }
329 }
330
331 private void createFunctionalInterface(Cursor dcl, JType.FnIf fnif) {
332 JType.Function fn = fnif.getFunction();
333 String intf;
334 String nativeName;
335 if (dcl == null) {
336 intf = ((JType.InnerType) fnif.type).getName();
337 nativeName = "N/A";
338 } else {
339 nativeName = Utils.getIdentifier(dcl);
340 intf = Utils.toClassName(nativeName);
341 }
342 logger.fine(() -> "Create FunctionalInterface " + intf);
343
344 final String name = internal_name + "$" + intf;
345
346 logger.fine(() -> "Define class " + name + " for native type " + nativeName);
347 global_cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
348 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
349 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE,
350 name, null, "java/lang/Object", null);
351 if (dcl != null) {
352 annotateNativeLocation(cw, dcl);
353 }
354 AnnotationVisitor av = cw.visitAnnotation(
355 "Ljava/lang/FunctionalInterface;", true);
356 av.visitEnd();
357 if (dcl != null) {
358 av = cw.visitAnnotation(NATIVE_CALLBACK, true);
359 Type t = dcl.type().canonicalType();
360 av.visit("value", Utils.getLayout(t));
361 av.visitEnd();
362 }
363 cw.visitInnerClass(name, internal_name, intf, ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE);
364
365 // add the method
366 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_ABSTRACT, "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 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 av = mv.visitAnnotation(NATIVE_TYPE, true);
414 Type t = dcl.type();
415 String layout = Utils.getLayout(t);
416 av.visit("layout", layout);
417 av.visit("ctype", t.spelling());
418 av.visit("name", dcl.spelling());
419 av.visitEnd();
420
421 headerDeclarations.append(dcl.spelling());
422 headerDeclarations.append('=');
423 headerDeclarations.append(layout);
424 headerDeclarations.append(' ');
425
426 int idx = 0;
427 for (JType arg: fn.args) {
428 if (arg instanceof TypeAlias) {
429 TypeAlias alias = (TypeAlias) arg;
430 final int tmp = idx;
431 logger.finest(() -> " arg " + tmp + " is an alias " + alias);
432 if (alias.getAnnotationDescriptor() != null) {
433 mv.visitTypeAnnotation(
434 TypeReference.newFormalParameterReference(idx).getValue(),
435 null, alias.getAnnotationDescriptor(), true)
436 .visitEnd();
437 }
438 }
439 idx++;
440 }
441
442 if (fn.returnType instanceof TypeAlias) {
443 TypeAlias alias = (TypeAlias) fn.returnType;
444 logger.finest(() -> " return type is an alias " + alias);
445 if (alias.getAnnotationDescriptor() != null) {
632 for (String token : tokens) {
633 logger.finest(() -> "TOKEN: " + token);
634 }
635
636 if (tokens.length != 2) {
637 logger.fine(() -> "Skipping macro " + tokens[0] + " because it doesn't have exactly 2 tokens");
638 return this;
639 }
640
641 int flags = ACC_PUBLIC;
642
643 Literal l = Literal.parse(tokens[1]);
644 if (l == null) {
645 logger.fine(() -> "Skipping macro " + tokens[0] + " because its body isn't a recognizable literal constant");
646 return this;
647 }
648
649 String sig = jdk.internal.org.objectweb.asm.Type.getMethodDescriptor(jdk.internal.org.objectweb.asm.Type.getType(l.type().getTypeClass()));
650 MethodVisitor mv = global_cw.visitMethod(flags, macroName, sig, sig, null);
651
652 AnnotationVisitor av = mv.visitAnnotation(NATIVE_LOCATION, true);
653 SourceLocation src = cursor.getSourceLocation();
654 SourceLocation.Location loc = src.getFileLocation();
655 Path p = loc.path();
656 av.visit("file", p == null ? "builtin" : p.toAbsolutePath().toString());
657 av.visit("line", loc.line());
658 av.visit("column", loc.column());
659 av.visit("USR", cursor.USR());
660 av.visitEnd();
661
662 mv.visitCode();
663 mv.visitLdcInsn(l.getValue());
664 switch (l.type()) {
665 case INT:
666 mv.visitInsn(IRETURN);
667 break;
668 case LONG:
669 mv.visitInsn(LRETURN);
670 break;
671 case STRING:
672 mv.visitInsn(ARETURN);
673 break;
674 }
675 mv.visitMaxs(0, 0);
676 mv.visitEnd();
677
678 handledMacros.add(macroName);
679
680 return this;
681 }
682
683 @Override
684 protected void produce() {
685 generateNativeHeader();
686 try {
687 writeClassFile(global_cw, owner.clsName);
688 } catch (IOException ex) {
689 handleException(ex);
690 }
691 }
692
693 @Override
694 protected Map<String, byte[]> collect() {
695 // Ensure classes are produced
696 produce();
697 HashMap<String, byte[]> rv = new HashMap<>();
698 // Not copying byte[] for efficiency, perhaps not a safe idea though
699 if (owner.pkgName.isEmpty()) {
700 types.forEach((clsName, bytecodes) -> {
701 rv.put(clsName, bytecodes);
702 });
703 } else {
704 types.forEach((clsName, bytecodes) -> {
705 rv.put(owner.pkgName + "." + clsName, bytecodes);
|