1 /* 2 * Copyright (c) 2018, 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 jdk.internal.nicl; 24 25 import jdk.internal.nicl.types.DescriptorParser; 26 import jdk.internal.org.objectweb.asm.MethodVisitor; 27 import jdk.internal.org.objectweb.asm.Type; 28 29 import java.lang.invoke.MethodHandle; 30 import java.lang.invoke.MethodHandles; 31 import java.lang.invoke.MethodType; 32 import java.lang.reflect.Method; 33 import java.nicl.layout.Layout; 34 import java.nicl.metadata.*; 35 import java.nicl.types.LayoutType; 36 import java.nicl.types.Pointer; 37 import java.nicl.types.Struct; 38 39 import static jdk.internal.org.objectweb.asm.Opcodes.*; 40 41 class StructImplGenerator extends BinderClassGenerator { 42 43 // name of pointer field, only used for record types 44 private static final String POINTER_FIELD_NAME = "ptr"; 45 46 // support method handles 47 private static final MethodHandle BUILD_PTR_MH; 48 private static final MethodHandle PTR_COPY_TO_ARRAY_INT_MH; 49 private static final MethodHandle PTR_COPY_TO_ARRAY_OBJECT_MH; 50 private static final MethodHandle PTR_COPY_FROM_ARRAY_INT_MH; 51 private static final MethodHandle PTR_COPY_FROM_ARRAY_OBJECT_MH; 52 53 static { 54 MethodHandles.Lookup lookup = MethodHandles.lookup(); 55 try { 56 BUILD_PTR_MH = lookup.findStatic(RuntimeSupport.class, "buildPtr", MethodType.methodType(Pointer.class, Pointer.class, long.class, LayoutType.class)); 57 PTR_COPY_TO_ARRAY_INT_MH = lookup.findStatic(RuntimeSupport.class, "copyToArray", MethodType.methodType(void.class, Pointer.class, long.class, int[].class, int.class)); 58 PTR_COPY_TO_ARRAY_OBJECT_MH = lookup.findStatic(RuntimeSupport.class, "copyToArray", MethodType.methodType(void.class, Pointer.class, long.class, Object[].class, int.class, LayoutType.class)); 59 PTR_COPY_FROM_ARRAY_INT_MH = lookup.findStatic(RuntimeSupport.class, "copyFromArray", MethodType.methodType(void.class, int[].class, Pointer.class, long.class, int.class)); 60 PTR_COPY_FROM_ARRAY_OBJECT_MH = lookup.findStatic(RuntimeSupport.class, "copyFromArray", MethodType.methodType(void.class, Object[].class, Pointer.class, long.class, int.class, LayoutType.class)); 61 } catch (ReflectiveOperationException ex) { 62 throw new IllegalStateException(ex); 63 } 64 } 65 66 StructImplGenerator(Class<?> hostClass, String implClassName, Class<?> c) { 67 super(hostClass, implClassName, new Class<?>[] { c }); 68 } 69 70 @Override 71 protected void generateMembers(BinderClassWriter cw) { 72 generatePointerField(cw); 73 generateConstructor(cw); 74 generatePointerGetter(cw); 75 generatePtrHelper(cw); 76 super.generateMembers(cw); 77 } 78 79 private void generatePointerField(BinderClassWriter cw) { 80 cw.visitField(ACC_PRIVATE | ACC_FINAL, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class), null, null); 81 } 82 83 private void generateConstructor(BinderClassWriter cw) { 84 /* 85 * <init>(Pointer p) { 86 * super(); 87 * this.p = p; 88 * } 89 */ 90 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Pointer.class)), null, null); 91 mv.visitCode(); 92 93 mv.visitVarInsn(ALOAD, 0); 94 mv.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", Type.getMethodDescriptor(Type.VOID_TYPE), false); 95 96 mv.visitVarInsn(ALOAD, 0); 97 mv.visitVarInsn(ALOAD, 1); 98 mv.visitFieldInsn(PUTFIELD, implClassName, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class)); 99 100 mv.visitInsn(RETURN); 101 102 mv.visitMaxs(0, 0); 103 mv.visitEnd(); 104 } 105 106 107 private void generatePointerGetter(BinderClassWriter cw) { 108 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "ptr", Type.getMethodDescriptor(Type.getType(Pointer.class)), null, null); 109 mv.visitCode(); 110 mv.visitVarInsn(ALOAD, 0); 111 mv.visitFieldInsn(GETFIELD, implClassName, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class)); 112 mv.visitInsn(ARETURN); 113 mv.visitMaxs(0, 0); 114 mv.visitEnd(); 115 } 116 117 private void generatePtrHelper(BinderClassWriter cw) { 118 /* 119 * private <T> Pointer<T> makePtr(long offset, LayoutType<T> t) { 120 * MethodHandle buildPtr = ldc <buildPtr> 121 * return buildPtr.invokeExact(p, offset, t); 122 * } 123 */ 124 MethodVisitor mv = cw.visitMethod(ACC_PRIVATE, "makePtr", Type.getMethodDescriptor(Type.getType(Pointer.class), Type.LONG_TYPE, Type.getType(LayoutType.class)), null, null); 125 mv.visitCode(); 126 mv.visitLdcInsn(cw.makeConstantPoolPatch(BUILD_PTR_MH)); 127 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); 128 mv.visitVarInsn(ALOAD, 0); 129 mv.visitFieldInsn(GETFIELD, implClassName, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class)); 130 mv.visitVarInsn(LLOAD, 1); 131 mv.visitVarInsn(ALOAD, 3); 132 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", Type.getMethodDescriptor(Type.getType(Pointer.class), Type.getType(Pointer.class), Type.LONG_TYPE, Type.getType(LayoutType.class)), false); 133 mv.visitInsn(ARETURN); 134 mv.visitMaxs(0, 0); 135 mv.visitEnd(); 136 } 137 138 @Override 139 protected void generateMethodImplementation(BinderClassWriter cw, Method method) { 140 if (method.isAnnotationPresent(Offset.class)) { 141 if (!method.isAnnotationPresent(C.class) || !method.isAnnotationPresent(NativeType.class)) { 142 throw new IllegalArgumentException("Unexpectedly found an @Offset annotated method without a @NativeType annotation"); 143 } 144 145 long off = method.getAnnotation(Offset.class).offset(); 146 if (off < 0 || off % 8 != 0) { 147 throw new Error("NYI: Sub-byte offsets (" + off + ") in struct type: " + interfaces[0].getCanonicalName()); 148 } 149 off = off / 8; 150 151 generateFieldAccessors(cw, method, off); 152 } else if (method.getDeclaringClass() == Struct.class) { 153 // ignore - the corresponding methods are generated as part of setting up the record type 154 } else { 155 super.generateMethodImplementation(cw, method); 156 } 157 } 158 159 private void generateFieldAccessors(BinderClassWriter cw, Method method, long offset) { 160 Class<?> javaType = method.getReturnType(); 161 162 try { 163 if (javaType.isArray()) { 164 generateArrayFieldAccessors(cw, method, javaType, offset); 165 } else { 166 generateNormalFieldAccessors(cw, method, javaType, offset); 167 } 168 } catch (Exception e) { 169 throw new RuntimeException("Failed to create accessors for " + method, e); 170 } 171 } 172 173 private void generateArrayFieldAccessors(BinderClassWriter cw, Method method, Class<?> javaType, long offset) { 174 Array ar = method.getAnnotation(Array.class); 175 if (null == ar) { 176 throw new IllegalStateException("Array return type should have Array annotation"); 177 } 178 final long length = ar.length(); 179 if (length > Integer.MAX_VALUE) { 180 throw new IllegalArgumentException("Array size is too large"); 181 } 182 183 generateArrayGetter(cw, method, javaType, offset, (int) length); 184 generateArraySetter(cw, method, javaType, offset, (int) length); 185 } 186 187 private void generateArrayGetter(BinderClassWriter cw, Method method, Class<?> javaType, long offset, int length) { 188 Layout l = new DescriptorParser(method.getAnnotation(NativeType.class).layout()).parseLayout().findFirst().get(); 189 LayoutType<?> lt = Util.makeType(method.getGenericReturnType(), l); 190 191 Class<?> componentType = javaType.getComponentType(); 192 193 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(Type.getType(method.getReturnType())), null, null); 194 mv.visitCode(); 195 196 allocArray(mv, componentType, length); 197 mv.visitVarInsn(ASTORE, 1); 198 199 //load receiver MH 200 mv.visitLdcInsn(cw.makeConstantPoolPatch(componentType.isPrimitive() ? 201 PTR_COPY_TO_ARRAY_INT_MH : PTR_COPY_TO_ARRAY_OBJECT_MH)); 202 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); 203 204 205 mv.visitVarInsn(ALOAD, 0); 206 mv.visitFieldInsn(GETFIELD, implClassName, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class)); 207 mv.visitLdcInsn(offset); 208 209 mv.visitVarInsn(ALOAD, 1); 210 211 mv.visitLdcInsn(length); 212 213 if (componentType.isPrimitive()) { 214 switch (PrimitiveClassType.typeof(componentType)) { 215 case INT: 216 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Pointer.class), Type.LONG_TYPE, Type.getType(int[].class), Type.INT_TYPE), false); 217 break; 218 219 // FIXME: Add other primitives here 220 default: 221 throw new UnsupportedOperationException("Unhandled component type: " + componentType); 222 } 223 } else { 224 mv.visitLdcInsn(cw.makeConstantPoolPatch(lt)); 225 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(LayoutType.class)); 226 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Pointer.class), Type.LONG_TYPE, Type.getType(Object[].class), Type.INT_TYPE, Type.getType(Class.class)), false); 227 } 228 229 mv.visitVarInsn(ALOAD, 1); 230 231 mv.visitInsn(ARETURN); 232 mv.visitMaxs(0, 0); 233 mv.visitEnd(); 234 } 235 236 private void generateArraySetter(BinderClassWriter cw, Method method, Class<?> javaType, long offset, int length) { 237 Layout l = new DescriptorParser(method.getAnnotation(NativeType.class).layout()).parseLayout().findFirst().get(); 238 LayoutType<?> lt = Util.makeType(method.getGenericReturnType(), l); 239 240 Class<?> componentType = javaType.getComponentType(); 241 242 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName().replace("$get", "$set"), Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(method.getReturnType())), null, null); 243 mv.visitCode(); 244 245 //load receiver MH 246 mv.visitLdcInsn(cw.makeConstantPoolPatch(componentType.isPrimitive() ? 247 PTR_COPY_FROM_ARRAY_INT_MH : PTR_COPY_FROM_ARRAY_OBJECT_MH)); 248 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(MethodHandle.class)); 249 250 mv.visitVarInsn(ALOAD, 1); 251 252 mv.visitVarInsn(ALOAD, 0); 253 mv.visitFieldInsn(GETFIELD, implClassName, POINTER_FIELD_NAME, Type.getDescriptor(Pointer.class)); 254 mv.visitLdcInsn(offset); 255 256 mv.visitLdcInsn(length); 257 258 if (componentType.isPrimitive()) { 259 switch (PrimitiveClassType.typeof(componentType)) { 260 case INT: 261 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(int[].class), Type.getType(Pointer.class), Type.LONG_TYPE, Type.INT_TYPE), false); 262 break; 263 264 // FIXME: Add other primitives here 265 default: 266 throw new UnsupportedOperationException("Unhandled component type: " + componentType); 267 } 268 } else { 269 mv.visitLdcInsn(cw.makeConstantPoolPatch(lt)); 270 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(LayoutType.class)); 271 mv.visitMethodInsn(INVOKEVIRTUAL, Type.getInternalName(MethodHandle.class), "invokeExact", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object[].class), Type.getType(Pointer.class), Type.LONG_TYPE, Type.INT_TYPE, Type.getType(Class.class)), false); 272 } 273 274 mv.visitInsn(RETURN); 275 mv.visitMaxs(0, 0); 276 mv.visitEnd(); 277 } 278 279 private void allocArray(MethodVisitor mv, Class<?> componentType, int length) { 280 mv.visitLdcInsn(length); 281 282 if (componentType.isPrimitive()) { 283 switch (PrimitiveClassType.typeof(componentType)) { 284 case INT: 285 mv.visitIntInsn(NEWARRAY, T_INT); 286 break; 287 288 // FIXME: Add other primitives here 289 default: 290 throw new IllegalArgumentException("Unhandled type: " + componentType); 291 } 292 } else { 293 mv.visitTypeInsn(ANEWARRAY, Type.getInternalName(componentType)); 294 } 295 } 296 297 private void generateNormalFieldAccessors(BinderClassWriter cw, Method method, Class<?> javaType, long offset) { 298 Layout l = new DescriptorParser(method.getAnnotation(NativeType.class).layout()).parseLayout().findFirst().get(); 299 LayoutType<?> lt = Util.makeType(method.getGenericReturnType(), l); 300 301 // Getter 302 { 303 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName(), Type.getMethodDescriptor(Type.getType(javaType)), null, null); 304 mv.visitCode(); 305 generateGetter(cw, mv, offset, javaType, lt); 306 mv.visitMaxs(0, 0); 307 mv.visitEnd(); 308 } 309 310 // Setter 311 { 312 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName().replace("$get", "$set"), 313 Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(javaType)), null, null); 314 mv.visitCode(); 315 generateSetter(cw, mv, offset, javaType, lt); 316 mv.visitMaxs(0, 0); 317 mv.visitEnd(); 318 } 319 320 // Reference 321 { 322 MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, method.getName().replace("$get", "$ptr"), 323 Type.getMethodDescriptor(Type.getType(Pointer.class)), null, null); 324 mv.visitCode(); 325 pushPtr(cw, mv, offset, lt); 326 mv.visitInsn(ARETURN); 327 mv.visitMaxs(0, 0); 328 mv.visitEnd(); 329 } 330 } 331 332 private void generateGetter(BinderClassWriter cw, MethodVisitor mv, long off, Class<?> javaType, LayoutType<?> lt) { 333 /* 334 * return this.ref(<offset>, this.<layoutTypeField>).get(); 335 */ 336 pushPtr(cw, mv, off, lt); 337 mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Pointer.class), "get", Type.getMethodDescriptor(Type.getType(Object.class)), true); 338 if (javaType.isPrimitive()) { 339 unbox(mv, javaType); 340 } else { 341 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(javaType)); 342 } 343 mv.visitInsn(returnInsn(javaType)); 344 } 345 346 private void generateSetter(BinderClassWriter cw, MethodVisitor mv, long off, Class<?> javaType, LayoutType<?> lt) { 347 /* 348 * this.ref(<offset>, this.<layoutTypeField>).set(<value>); 349 */ 350 pushPtr(cw, mv, off, lt); 351 mv.visitVarInsn(loadInsn(javaType), 1); 352 if (javaType.isPrimitive()) { 353 box(mv, javaType); 354 } 355 mv.visitMethodInsn(INVOKEINTERFACE, Type.getInternalName(Pointer.class), "set", Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Object.class)), true); 356 mv.visitInsn(RETURN); 357 } 358 359 private void pushPtr(BinderClassWriter cw, MethodVisitor mv, long off, LayoutType<?> layoutType) { 360 /* 361 * ref(<offset>, this.<layoutTypeField>) 362 */ 363 mv.visitVarInsn(ALOAD, 0); 364 mv.visitLdcInsn(off); 365 mv.visitLdcInsn(cw.makeConstantPoolPatch(layoutType)); 366 mv.visitTypeInsn(CHECKCAST, Type.getInternalName(LayoutType.class)); 367 mv.visitMethodInsn(INVOKEVIRTUAL, implClassName, "makePtr", Type.getMethodDescriptor(Type.getType(Pointer.class), Type.LONG_TYPE, Type.getType(LayoutType.class)), false); 368 } 369 }