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 }