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