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