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.foreign.Scope; 27 import java.lang.invoke.MethodHandles; 28 import java.lang.invoke.MethodHandles.Lookup; 29 import java.lang.invoke.MethodType; 30 import java.lang.reflect.Method; 31 import java.util.ArrayList; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 import java.util.function.Consumer; 37 import java.util.logging.Level; 38 39 import com.sun.tools.jextract.parser.MacroParser; 40 import com.sun.tools.jextract.tree.Tree; 41 import jdk.internal.org.objectweb.asm.FieldVisitor; 42 import jdk.internal.org.objectweb.asm.ClassWriter; 43 import jdk.internal.org.objectweb.asm.MethodVisitor; 44 import jdk.internal.org.objectweb.asm.Type; 45 import com.sun.tools.jextract.tree.EnumTree; 46 import com.sun.tools.jextract.tree.FieldTree; 47 import com.sun.tools.jextract.tree.FunctionTree; 48 import com.sun.tools.jextract.tree.MacroTree; 49 import com.sun.tools.jextract.tree.VarTree; 50 51 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; 52 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_FINAL; 53 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_INTERFACE; 54 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 55 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PRIVATE; 56 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 57 import static jdk.internal.org.objectweb.asm.Opcodes.ARETURN; 58 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VARARGS; 59 import static jdk.internal.org.objectweb.asm.Opcodes.CHECKCAST; 60 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 61 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 62 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKEINTERFACE; 63 import static jdk.internal.org.objectweb.asm.Opcodes.INVOKESTATIC; 64 import static jdk.internal.org.objectweb.asm.Opcodes.IRETURN; 65 import static jdk.internal.org.objectweb.asm.Opcodes.PUTSTATIC; 66 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 67 import static jdk.internal.org.objectweb.asm.Opcodes.V1_8; 68 69 /** 70 * This extended factory generates a class with only static methods and fields. A native 71 * library interface instance (of the given header file) is kept as a static private field. 72 * One static method is generated for every library interface method. Enum and macro constants 73 * are mapped to static final fields. By importing the "static forwarder" class, the user code 74 * looks more or less like C code. Libraries.bind and header interface usage is hidden. 75 */ 76 final class AsmCodeFactoryExt extends AsmCodeFactory { 77 private final String headerClassNameDesc; 78 private final String forwarderClassName; 79 private final ClassWriter cw; 80 // suffix for static forwarder class name 81 // field name for the header interface instance. 82 private static final String STATICS_LIBRARY_FIELD_NAME = "_theLibrary"; 83 84 private final List<Consumer<MethodVisitor>> constantInitializers = new ArrayList<>(); 85 private final List<EnumFactory> enumFactories = new ArrayList<>(); 86 87 AsmCodeFactoryExt(Context ctx, HeaderFile header) { 88 super(ctx, header); 89 log.print(Level.INFO, () -> "Instantiate StaticForwarderGenerator for " + header.path); 90 this.headerClassNameDesc = "L" + headerClassName + ";"; 91 this.forwarderClassName = Utils.toInternalName(header.pkgName, header.staticForwarderClsName); 92 this.cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 93 this.cw.visit(V1_8, ACC_PUBLIC | ACC_FINAL, getClassName(), 94 null, "java/lang/Object", null); 95 scopeAccessor(); 96 } 97 98 private class EnumFactory { 99 private final EnumTree enumTree; 100 private final String enumClassName; 101 102 EnumFactory(EnumTree enumTree) { 103 log.print(Level.INFO, () -> "Instantiate EnumFactory for " + enumTree.name()); 104 this.enumTree = enumTree; 105 this.enumClassName = AsmCodeFactoryExt.this.getClassName() + "$" + enumTree.name(); 106 107 } 108 109 String getEnumName() { 110 return enumTree.name(); 111 } 112 113 String getClassName() { 114 return enumClassName; 115 } 116 117 byte[] getClassBytes() { 118 return generate(); 119 } 120 121 private byte[] generate() { 122 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 123 cw.visit(V1_8, ACC_PUBLIC | ACC_ABSTRACT | ACC_INTERFACE, enumClassName, 124 null, "java/lang/Object", null); 125 cw.visitInnerClass(enumClassName, AsmCodeFactoryExt.this.getClassName(), enumTree.name(), ACC_PUBLIC | ACC_FINAL); 126 enumTree.constants().forEach(constant -> { 127 String name = constant.name(); 128 JType type = headerFile.dictionary().lookup(constant.type()); 129 Object constantValue = makeConstantValue(type, constant.enumConstant().get()); 130 FieldVisitor fv = cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, name, type.getDescriptor(), 131 type.getSignature(false), constantValue); 132 fv.visitEnd(); 133 }); 134 135 cw.visitEnd(); 136 return cw.toByteArray(); 137 } 138 } 139 140 @Override 141 public Boolean visitVar(VarTree varTree, JType jt) { 142 if (super.visitVar(varTree, jt)) { 143 String fieldName = varTree.name(); 144 assert !fieldName.isEmpty(); 145 146 emitStaticForwarder(fieldName + "$get", 147 "()" + jt.getDescriptor(), "()" + jt.getSignature(false), false); 148 jt.visitInner(cw); 149 150 emitStaticForwarder(fieldName + "$set", 151 "(" + jt.getDescriptor() + ")V", 152 "(" + jt.getSignature(true) + ")V", false); 153 JType ptrType = JType.GenericType.ofPointer(jt); 154 emitStaticForwarder(fieldName + "$ptr", 155 "()" + ptrType.getDescriptor(), "()" + ptrType.getSignature(false), false); 156 ptrType.visitInner(cw); 157 158 return true; 159 } else { 160 return false; 161 } 162 } 163 164 @Override 165 public Boolean visitEnum(EnumTree enumTree, JType jt) { 166 if (super.visitEnum(enumTree, jt)) { 167 if (enumTree.name().isEmpty()) { 168 enumTree.constants().forEach(constant -> addConstant(constant.name(), 169 headerFile.dictionary().lookup(constant.type()), 170 constant.enumConstant().get())); 171 } else { 172 EnumFactory ef = new EnumFactory(enumTree); 173 enumFactories.add(ef); 174 cw.visitInnerClass(ef.getClassName(), getClassName(), ef.getEnumName(), 175 ACC_PUBLIC | ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE); 176 } 177 return true; 178 } else { 179 return false; 180 } 181 } 182 183 @Override 184 public Boolean visitFunction(FunctionTree funcTree, JType jt) { 185 if (super.visitFunction(funcTree, jt)) { 186 assert (jt instanceof JType.Function); 187 JType.Function fn = (JType.Function)jt; 188 log.print(Level.FINE, () -> "Add method: " + fn.getSignature(false)); 189 emitStaticForwarder(funcTree.name(), fn.getDescriptor(), fn.getSignature(false), fn.isVarArgs); 190 fn.visitInner(cw); 191 return true; 192 } else { 193 return false; 194 } 195 } 196 197 @Override 198 public Boolean visitMacro(MacroTree macroTree, JType jt) { 199 if (super.visitMacro(macroTree, jt)) { 200 String name = macroTree.name(); 201 MacroParser.Macro macro = macroTree.macro().get(); 202 log.print(Level.FINE, () -> "Adding macro " + name); 203 addConstant(Utils.toMacroName(name), macro.type(), macro.value()); 204 return true; 205 } else { 206 return false; 207 } 208 } 209 210 private void addConstant(String name, JType type, Object value) { 211 Object constantValue = makeConstantValue(type, value); 212 FieldVisitor fv = cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_FINAL, name, type.getDescriptor(), 213 type.getSignature(false), constantValue); 214 fv.visitEnd(); 215 if (constantValue == null) { 216 constantInitializers.add(mv -> { 217 // load library interface (static) field 218 String desc = type.getDescriptor(); 219 mv.visitFieldInsn(GETSTATIC, getClassName(), 220 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 221 mv.visitMethodInsn(INVOKEINTERFACE, headerClassName, name, "()" + desc, true); 222 mv.visitFieldInsn(PUTSTATIC, getClassName(), name, desc); 223 }); 224 } 225 } 226 227 private Object makeConstantValue(JType type, Object value) { 228 switch (type.getDescriptor()) { 229 case "Z": 230 return ((long)value) != 0; 231 case "C": 232 return (char)(long)value; 233 case "B": 234 return (byte)(long)value; 235 case "S": 236 return (short)(long)value; 237 case "I": 238 return (int)(long)value; 239 case "F": 240 return (float)(double)value; 241 case "J": case "D": 242 return value; 243 default: 244 return null; 245 } 246 } 247 248 @Override 249 public Map<String, byte[]> generateNativeHeader(List<Tree> decls) { 250 Map<String, byte[]> results = new HashMap<>(); 251 results.putAll(super.generateNativeHeader(decls)); 252 staticsInitializer(); 253 results.put(getClassName(), getClassBytes()); 254 for (EnumFactory ef : enumFactories) { 255 results.put(ef.getClassName(), ef.getClassBytes()); 256 } 257 return Collections.unmodifiableMap(results); 258 } 259 260 // Internals only below this point 261 262 private String getClassName() { 263 return forwarderClassName; 264 } 265 266 // return the generated static forwarder class bytes 267 private byte[] getClassBytes() { 268 cw.visitEnd(); 269 return cw.toByteArray(); 270 } 271 272 // emit library interface static field and <clinit> initializer for that field 273 private void staticsInitializer() { 274 // library interface field 275 FieldVisitor fv = cw.visitField(ACC_PRIVATE|ACC_STATIC|ACC_FINAL, 276 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc, null, null); 277 fv.visitEnd(); 278 279 // <clinit> to bind library interface field 280 MethodVisitor mv = cw.visitMethod(ACC_STATIC, 281 "<clinit>", "()V", null, null); 282 mv.visitCode(); 283 284 // MethodHandles.lookup() 285 Method lookupMethod = null; 286 try { 287 lookupMethod = MethodHandles.class.getMethod("lookup"); 288 } catch (NoSuchMethodException nsme) { 289 throw new RuntimeException(nsme); 290 } 291 mv.visitMethodInsn(INVOKESTATIC, 292 Type.getInternalName(MethodHandles.class), "lookup", 293 Type.getMethodDescriptor(lookupMethod), false); 294 295 // ldc library-interface-class 296 mv.visitLdcInsn(Type.getObjectType(headerClassName)); 297 298 // Libraries.bind(lookup, class); 299 Method bindMethod = null; 300 try { 301 bindMethod = Libraries.class.getMethod("bind", Lookup.class, Class.class); 302 } catch (NoSuchMethodException nsme) { 303 throw new RuntimeException(nsme); 304 } 305 mv.visitMethodInsn(INVOKESTATIC, 306 Type.getInternalName(Libraries.class), "bind", 307 Type.getMethodDescriptor(bindMethod), false); 308 309 // store it in library interface field 310 mv.visitTypeInsn(CHECKCAST, headerClassName); 311 mv.visitFieldInsn(PUTSTATIC, getClassName(), 312 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 313 314 constantInitializers.forEach(init -> init.accept(mv)); 315 316 mv.visitInsn(RETURN); 317 mv.visitMaxs(0, 0); 318 mv.visitEnd(); 319 } 320 321 private void scopeAccessor() { 322 String scopeAccessorDesc = MethodType.methodType(Scope.class).descriptorString(); 323 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "scope", scopeAccessorDesc, null, null); 324 mv.visitCode(); 325 326 // load library interface (static) field 327 mv.visitFieldInsn(GETSTATIC, getClassName(), 328 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 329 330 String libraryScopeDesc = MethodType.methodType(Scope.class, Object.class).descriptorString(); 331 mv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Libraries.class), "libraryScope", libraryScopeDesc, false); 332 mv.visitInsn(ARETURN); 333 mv.visitMaxs(1,1); 334 mv.visitEnd(); 335 } 336 337 // emit static forwarder method for a specific library interface method 338 private void emitStaticForwarder(String name, String desc, String signature, boolean isVarArgs) { 339 int accessFlags = ACC_PUBLIC | ACC_STATIC; 340 if (isVarArgs) { 341 accessFlags |= ACC_VARARGS; 342 } 343 344 MethodVisitor mv = cw.visitMethod(accessFlags, name, desc, signature, null); 345 mv.visitCode(); 346 347 // load library interface (static) field 348 mv.visitFieldInsn(GETSTATIC, getClassName(), 349 STATICS_LIBRARY_FIELD_NAME, headerClassNameDesc); 350 351 // forward the call to the interface 352 Type[] argTypes = Type.getArgumentTypes(desc); 353 Type retType = Type.getReturnType(desc); 354 355 int loadIdx = 0; 356 for (int i = 0; i < argTypes.length; i++) { 357 mv.visitVarInsn(argTypes[i].getOpcode(ILOAD), loadIdx); 358 loadIdx += argTypes[i].getSize(); 359 } 360 mv.visitMethodInsn(INVOKEINTERFACE, headerClassName, name, desc, true); 361 mv.visitInsn(retType.getOpcode(IRETURN)); 362 363 mv.visitMaxs(0, 0); 364 mv.visitEnd(); 365 } 366 }