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.foreign.Libraries; 26 import java.lang.invoke.MethodHandles; 27 import java.lang.invoke.MethodHandles.Lookup; 28 import java.lang.reflect.Method; 29 30 import jdk.internal.org.objectweb.asm.FieldVisitor; 31 import jdk.internal.org.objectweb.asm.ClassWriter; 32 import jdk.internal.org.objectweb.asm.MethodVisitor; 33 import jdk.internal.org.objectweb.asm.Type; 34 import com.sun.tools.jextract.tree.EnumTree; 35 import com.sun.tools.jextract.tree.FieldTree; 36 import com.sun.tools.jextract.tree.FunctionTree; 37 import com.sun.tools.jextract.tree.MacroTree; 38 import com.sun.tools.jextract.tree.VarTree; 39 40 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 41 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 42 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 43 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 44 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; 45 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 46 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 47 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; 48 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; 49 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; 50 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; 51 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 52 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 53 54 /** 55 * This extended factory generates a class with only static methods and fields. A native 56 * library interface instance (of the given header file) is kept as a static private field. 57 * One static method is generated for every library interface method. Enum and macro constants 58 * are mapped to static final fields. By importing the "static forwarder" class, the user code 59 * looks more or less like C code. Libraries.bind and header interface usage is hidden. 60 */ 61 final class AsmCodeFactoryExt extends AsmCodeFactory { 62 private final String headerClassNameDesc; 63 private final ClassWriter cw; 64 // suffix for static forwarder class name 65 private static final String STATICS_CLASS_NAME_SUFFIX = "_h"; 66 // field name for the header interface instance. 67 private static final String STATICS_LIBRARY_FIELD_NAME = "_theLibrary"; 68 69 AsmCodeFactoryExt(Context ctx, HeaderFile header) { 70 super(ctx, header); 71 logger.info(() -> "Instantiate StaticForwarderGenerator for " + header.path); 72 this.headerClassNameDesc = "L" + headerClassName + ";"; 73 this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 74 this.cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, getClassName(), 75 null, "java/lang/Object", null); 76 staticsInitializer(); 77 } 78 79 @Override 80 public Boolean visitVar(VarTree varTree, JType jt) { 81 if (super.visitVar(varTree, jt)) { 82 String fieldName = varTree.name(); 83 assert !fieldName.isEmpty(); 84 85 emitStaticForwarder(fieldName + "$get", 86 "()" + jt.getDescriptor(), "()" + jt.getSignature(false)); 87 88 emitStaticForwarder(fieldName + "$set", 89 "(" + jt.getDescriptor() + ")V", 90 "(" + jt.getSignature(true) + ")V"); 91 JType ptrType = JType.GenericType.ofPointer(jt); 92 emitStaticForwarder(fieldName + "$ptr", 93 "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false)); 94 95 return true; 96 } else { 97 return false; 98 } 99 } 100 101 @Override 102 public Boolean visitEnum(EnumTree enumTree, JType jt) { 103 if (super.visitEnum(enumTree, jt)) { 104 enumTree.constants().forEach(constant -> addEnumConstant(constant)); 105 return true; 106 } else { 107 return false; 108 } 109 } 110 111 @Override 112 public Boolean visitFunction(FunctionTree funcTree, JType jt) { 113 if (super.visitFunction(funcTree, jt)) { 114 assert (jt instanceof JType.Function); 115 JType.Function fn = (JType.Function)jt; 116 logger.fine(() -> "Add method: " + fn.getSignature(false)); 117 emitStaticForwarder(funcTree.name(), fn.getDescriptor(), fn.getSignature(false)); 118 return true; 119 } else { 120 return false; 121 } 122 } 123 124 @Override 125 public Boolean visitMacro(MacroTree macroTree, JType jt) { 126 if (super.visitMacro(macroTree, jt)) { 127 String name = macroTree.name(); 128 Object value = macroTree.value().get(); 129 logger.fine(() -> "Adding macro " + name); 130 Class<?> macroType = Utils.unboxIfNeeded(value.getClass()); 131 String sig = Type.getType(macroType).getDescriptor(); 132 FieldVisitor fv = cw.visitField(ACC_PUBLIC | ACC_STATIC, name, sig, null, value); 133 fv.visitEnd(); 134 return true; 135 } else { 136 return false; 137 } 138 } 139 140 @Override 141 protected synchronized void produce() { 142 super.produce(); 143 types.put(getSimpleClassName(), getClassBytes()); 144 } 145 146 // Internals only below this point 147 // not fully qualified 148 private String getSimpleClassName() { 149 return headerFile.clsName + STATICS_CLASS_NAME_SUFFIX; 150 } 151 152 private String getClassName() { 153 return headerClassName + STATICS_CLASS_NAME_SUFFIX; 154 } 155 156 // return the generated static forwarder class bytes 157 private byte[] getClassBytes() { 158 cw.visitEnd(); 159 return cw.toByteArray(); 160 } 161 162 // map each C enum constant as a static final field of the static forwarder class 163 private void addEnumConstant(FieldTree fieldTree) { 164 assert (fieldTree.isEnumConstant()); 165 String name = fieldTree.name(); 166 String desc = dict.lookup(fieldTree.type()).getDescriptor(); 167 if (desc.length() != 1) { 168 throw new AssertionError("expected single char descriptor: " + desc); 169 } 170 FieldVisitor fv = null; 171 switch (desc.charAt(0)) { 172 case 'J': 173 long lvalue = fieldTree.enumConstant().get(); 174 fv = cw.visitField(ACC_PUBLIC | ACC_STATIC, name, desc, null, lvalue); 175 break; 176 case 'I': 177 int ivalue = fieldTree.enumConstant().get().intValue(); 178 fv = cw.visitField(ACC_PUBLIC | ACC_STATIC, name, desc, null, ivalue); 179 break; 180 default: 181 throw new AssertionError("should not reach here"); 182 } 183 fv.visitEnd(); 184 } 185 186 // emit library interface static field and <clinit> initializer for that field 187 private void staticsInitializer() { 188 // library interface field 189 FieldVisitor fv = cw.visitField(ACC_PRIVATE|ACC_STATIC|ACC_FINAL, 190 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc, null, null); 191 fv.visitEnd(); 192 193 // <clinit> to bind library interface field 194 MethodVisitor mv = cw.visitMethod(ACC_STATIC, 195 "<clinit>", "()V", null, null); 196 mv.visitCode(); 197 198 // MethodHandles.lookup() 199 Method lookupMethod = null; 200 try { 201 lookupMethod = MethodHandles.class.getMethod("lookup"); 202 } catch (NoSuchMethodException nsme) { 203 throw new RuntimeException(nsme); 204 } 205 mv.visitMethodInsn(INVOKESTATIC, 206 Type.getInternalName(MethodHandles.class), "lookup", 207 Type.getMethodDescriptor(lookupMethod), false); 208 209 // ldc library-interface-class 210 mv.visitLdcInsn(Type.getObjectType(headerClassName)); 211 212 // Libraries.bind(lookup, class); 213 Method bindMethod = null; 214 try { 215 bindMethod = Libraries.class.getMethod("bind", Lookup.class, Class.class); 216 } catch (NoSuchMethodException nsme) { 217 throw new RuntimeException(nsme); 218 } 219 mv.visitMethodInsn(INVOKESTATIC, 220 Type.getInternalName(Libraries.class), "bind", 221 Type.getMethodDescriptor(bindMethod), false); 222 223 // store it in library interface field 224 mv.visitTypeInsn(CHECKCAST, headerClassName); 225 mv.visitFieldInsn(PUTSTATIC, getClassName(), 226 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 227 228 mv.visitInsn(RETURN); 229 mv.visitMaxs(0, 0); 230 mv.visitEnd(); 231 } 232 233 // emit static forwarder method for a specific library interface method 234 private void emitStaticForwarder(String name, String desc, String signature) { 235 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, 236 name, desc, signature, null); 237 mv.visitCode(); 238 239 // load library interface (static) field 240 mv.visitFieldInsn(GETSTATIC, getClassName(), 241 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 242 243 // forward the call to the interface 244 Type[] argTypes = Type.getArgumentTypes(desc); 245 Type retType = Type.getReturnType(desc); 246 247 int loadIdx = 0; 248 for (int i = 0; i < argTypes.length; i++) { 249 mv.visitVarInsn(argTypes[i].getOpcode(ILOAD), loadIdx); 250 loadIdx += argTypes[i].getSize(); 251 } 252 mv.visitMethodInsn(INVOKEINTERFACE, headerClassName, name, desc, true); 253 mv.visitInsn(retType.getOpcode(IRETURN)); 254 255 mv.visitMaxs(0, 0); 256 mv.visitEnd(); 257 } 258 } --- EOF ---