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 }
--- EOF ---