001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package org.eclipse.golo.runtime.adapters; 012 013import org.objectweb.asm.ClassWriter; 014import org.objectweb.asm.Handle; 015import org.objectweb.asm.MethodVisitor; 016import org.objectweb.asm.Type; 017 018import java.lang.reflect.Constructor; 019import java.lang.reflect.InvocationTargetException; 020import java.lang.reflect.Method; 021import java.lang.reflect.Modifier; 022import java.util.Collections; 023import java.util.HashSet; 024import java.util.Set; 025import java.util.TreeSet; 026 027import static org.eclipse.golo.runtime.adapters.AdapterSupport.DEFINITION_FIELD; 028import static java.lang.reflect.Modifier.*; 029import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES; 030import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS; 031import static org.objectweb.asm.Opcodes.*; 032 033public class JavaBytecodeAdapterGenerator { 034 035 private static final Handle ADAPTER_HANDLE; 036 037 static { 038 String bootstrapOwner = "org/eclipse/golo/runtime/adapters/AdapterSupport"; 039 String bootstrapMethod = "bootstrap"; 040 String description = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"; 041 ADAPTER_HANDLE = new Handle(H_INVOKESTATIC, bootstrapOwner, bootstrapMethod, description, false); 042 } 043 044 private String jvmType(String klass) { 045 return klass.replace(".", "/"); 046 } 047 048 private String[] interfaceTypesArray(Set<String> interfaces) { 049 String[] types = new String[interfaces.size()]; 050 int i = 0; 051 for (String iface : interfaces) { 052 types[i] = jvmType(iface); 053 i++; 054 } 055 return types; 056 } 057 058 public byte[] generate(AdapterDefinition adapterDefinition) { 059 ClassWriter classWriter = new ClassWriter(COMPUTE_FRAMES | COMPUTE_MAXS); 060 TreeSet<String> interfaces = new TreeSet<>(adapterDefinition.getInterfaces()); 061 interfaces.add("gololang.GoloAdapter"); 062 classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER | ACC_FINAL | ACC_SYNTHETIC, 063 adapterDefinition.getName(), null, 064 jvmType(adapterDefinition.getParent()), 065 interfaceTypesArray(interfaces)); 066 makeDefinitionField(classWriter); 067 makeConstructors(classWriter, adapterDefinition); 068 makeFrontendOverrides(classWriter, adapterDefinition); 069 classWriter.visitEnd(); 070 return classWriter.toByteArray(); 071 } 072 073 public Class<?> generateIntoDefinitionClassloader(AdapterDefinition adapterDefinition) { 074 try { 075 byte[] bytecode = generate(adapterDefinition); 076 ClassLoader classLoader = adapterDefinition.getClassLoader(); 077 Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class); 078 if (!defineClass.isAccessible()) { 079 defineClass.setAccessible(true); 080 } 081 return (Class<?>) defineClass.invoke(classLoader, adapterDefinition.getName(), bytecode, 0, bytecode.length); 082 } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { 083 throw new RuntimeException(e); 084 } 085 } 086 087 private void makeDefinitionField(ClassWriter classWriter) { 088 classWriter.visitField(ACC_FINAL | ACC_PUBLIC, DEFINITION_FIELD, 089 "Lorg/eclipse/golo/runtime/adapters/AdapterDefinition;", null, null).visitEnd(); 090 } 091 092 private void makeFrontendOverrides(ClassWriter classWriter, AdapterDefinition adapterDefinition) { 093 for (Method method : getAllVirtualMethods(adapterDefinition)) { 094 int access = isPublic(method.getModifiers()) ? ACC_PUBLIC : ACC_PROTECTED; 095 if (method.isVarArgs()) { 096 access &= ACC_VARARGS; 097 } 098 String name = method.getName(); 099 String descriptor = Type.getMethodDescriptor(method); 100 Class<?>[] exceptionTypes = method.getExceptionTypes(); 101 String[] exceptions = new String[exceptionTypes.length]; 102 for (int i = 0; i < exceptionTypes.length; i++) { 103 exceptions[i] = Type.getInternalName(exceptionTypes[i]); 104 } 105 MethodVisitor methodVisitor = classWriter.visitMethod(access, name, descriptor, null, exceptions); 106 methodVisitor.visitCode(); 107 Class<?>[] parameterTypes = method.getParameterTypes(); 108 Type[] indyTypes = new Type[parameterTypes.length + 1]; 109 indyTypes[0] = Type.getType(Object.class); 110 methodVisitor.visitVarInsn(ALOAD, 0); 111 int argIndex = 1; 112 for (int i = 0; i < parameterTypes.length; i++) { 113 argIndex = loadArgument(methodVisitor, parameterTypes[i], argIndex); 114 indyTypes[i + 1] = Type.getType(parameterTypes[i]); 115 } 116 methodVisitor.visitInvokeDynamicInsn(method.getName(), Type.getMethodDescriptor(Type.getReturnType(method), indyTypes), ADAPTER_HANDLE); 117 makeReturn(methodVisitor, method.getReturnType()); 118 methodVisitor.visitMaxs(0, 0); 119 methodVisitor.visitEnd(); 120 } 121 } 122 123 private HashSet<Method> getAllVirtualMethods(AdapterDefinition adapterDefinition) { 124 try { 125 HashSet<Method> methods = new HashSet<>(); 126 Class<?> parentClass = Class.forName(adapterDefinition.getParent(), true, adapterDefinition.getClassLoader()); 127 for (Method method : parentClass.getMethods()) { 128 if (!isStatic(method.getModifiers()) && !isFinal(method.getModifiers())) { 129 methods.add(method); 130 } 131 } 132 for (Method method : parentClass.getDeclaredMethods()) { 133 if (!isStatic(method.getModifiers()) && !isPrivate(method.getModifiers()) && !isFinal(method.getModifiers())) { 134 methods.add(method); 135 } 136 } 137 for (String iface : adapterDefinition.getInterfaces()) { 138 Collections.addAll(methods, Class.forName(iface, true, adapterDefinition.getClassLoader()).getMethods()); 139 } 140 return methods; 141 } catch (ClassNotFoundException e) { 142 throw new RuntimeException(e); 143 } 144 } 145 146 private void makeConstructors(ClassWriter classWriter, AdapterDefinition adapterDefinition) { 147 try { 148 Class<?> parentClass = Class.forName(adapterDefinition.getParent(), true, adapterDefinition.getClassLoader()); 149 for (Constructor<?> constructor : parentClass.getDeclaredConstructors()) { 150 if (Modifier.isPublic(constructor.getModifiers()) || Modifier.isProtected(constructor.getModifiers())) { 151 Class<?>[] parameterTypes = constructor.getParameterTypes(); 152 Type[] adapterParameterTypes = new Type[parameterTypes.length + 1]; 153 adapterParameterTypes[0] = Type.getType(AdapterDefinition.class); 154 for (int i = 1; i < adapterParameterTypes.length; i++) { 155 adapterParameterTypes[i] = Type.getType(parameterTypes[i - 1]); 156 } 157 String descriptor = Type.getMethodDescriptor(Type.VOID_TYPE, adapterParameterTypes); 158 MethodVisitor methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", descriptor, null, null); 159 methodVisitor.visitCode(); 160 methodVisitor.visitVarInsn(ALOAD, 0); 161 methodVisitor.visitVarInsn(ALOAD, 1); 162 methodVisitor.visitFieldInsn(PUTFIELD, jvmType(adapterDefinition.getName()), DEFINITION_FIELD, 163 "Lorg/eclipse/golo/runtime/adapters/AdapterDefinition;"); 164 methodVisitor.visitVarInsn(ALOAD, 0); 165 int argIndex = 2; 166 for (Class<?> parameterType : parameterTypes) { 167 argIndex = loadArgument(methodVisitor, parameterType, argIndex); 168 } 169 methodVisitor.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(parentClass), "<init>", Type.getConstructorDescriptor(constructor), false); 170 methodVisitor.visitInsn(RETURN); 171 methodVisitor.visitMaxs(0, 0); 172 methodVisitor.visitEnd(); 173 } 174 } 175 } catch (ClassNotFoundException e) { 176 throw new RuntimeException(e); 177 } 178 } 179 180 private int loadArgument(MethodVisitor methodVisitor, Class<?> type, int index) { 181 if (type.isPrimitive()) { 182 if (type == Integer.TYPE) { 183 methodVisitor.visitVarInsn(ILOAD, index); 184 return index + 1; 185 } else if (type == Boolean.TYPE) { 186 methodVisitor.visitVarInsn(ILOAD, index); 187 return index + 1; 188 } else if (type == Byte.TYPE) { 189 methodVisitor.visitVarInsn(ILOAD, index); 190 return index + 1; 191 } else if (type == Character.TYPE) { 192 methodVisitor.visitVarInsn(ILOAD, index); 193 return index + 1; 194 } else if (type == Short.TYPE) { 195 methodVisitor.visitVarInsn(ILOAD, index); 196 return index + 1; 197 } else if (type == Double.TYPE) { 198 methodVisitor.visitVarInsn(DLOAD, index); 199 return index + 2; 200 } else if (type == Float.TYPE) { 201 methodVisitor.visitVarInsn(FLOAD, index); 202 return index + 1; 203 } else { 204 methodVisitor.visitVarInsn(LLOAD, index); 205 return index + 2; 206 } 207 } else { 208 methodVisitor.visitVarInsn(ALOAD, index); 209 return index + 1; 210 } 211 } 212 213 private void makeReturn(MethodVisitor methodVisitor, Class<?> type) { 214 if (type.isPrimitive()) { 215 if (type == Integer.TYPE) { 216 methodVisitor.visitInsn(IRETURN); 217 } else if (type == Void.TYPE) { 218 methodVisitor.visitInsn(RETURN); 219 } else if (type == Boolean.TYPE) { 220 methodVisitor.visitInsn(IRETURN); 221 } else if (type == Byte.TYPE) { 222 methodVisitor.visitInsn(IRETURN); 223 } else if (type == Character.TYPE) { 224 methodVisitor.visitInsn(IRETURN); 225 } else if (type == Short.TYPE) { 226 methodVisitor.visitInsn(IRETURN); 227 } else if (type == Double.TYPE) { 228 methodVisitor.visitInsn(DRETURN); 229 } else if (type == Float.TYPE) { 230 methodVisitor.visitInsn(FRETURN); 231 } else if (type == Long.TYPE) { 232 methodVisitor.visitInsn(LRETURN); 233 } 234 } else { 235 methodVisitor.visitInsn(ARETURN); 236 } 237 } 238}