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