Java8 support

This commit is contained in:
Andy Clement
2014-02-17 16:24:52 -08:00
parent f9a8252317
commit 39be705aa5
4 changed files with 99 additions and 31 deletions

View File

@@ -1,7 +1,8 @@
apply plugin: 'maven'
group = "org.springsource.loaded"
version = '1.1.5.BUILD-SNAPSHOT'
//version = '1.1.5.RELEASE'
version = '1.2.0.BUILD-SNAPSHOT'
jar.baseName = 'springloaded'
task writeNewPom << {

View File

@@ -22,6 +22,7 @@ import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
@@ -223,6 +224,50 @@ public class ClassRenamer {
}
}
private String toString(Handle bsm) {
return "["+bsm.getTag()+"]"+bsm.getOwner()+"."+bsm.getName()+bsm.getDesc();
}
private String toString(Object[] os) {
StringBuilder buf = new StringBuilder();
if (os!=null) {
buf.append("[");
for (int i=0;i<os.length;i++) {
if (i>0) buf.append(",");
buf.append(os[i]);
}
buf.append("]");
}
else {
return "null";
}
return buf.toString();
}
private Handle retargetHandle(Handle oldHandle) {
int tag = oldHandle.getTag();
String owner = oldHandle.getOwner();
String name = oldHandle.getName();
String desc = oldHandle.getDesc();
owner = renameRetargetIfNecessary(owner);
Handle newHandle = new Handle(tag,owner,name,desc);
return newHandle;
}
@Override
public void visitInvokeDynamicInsn(String name, String desc, org.objectweb.asm.Handle bsm, Object... bsmArgs) {
// System.out.println("visitInvokeDynamicInsn(name="+name+",desc="+desc+",bsm="+toString(bsm)+",bsmArgs="+toString(bsmArgs)+")");
// Example:
// visitInvokeDynamicInsn(name=m,desc=()Lbasic/LambdaA2$Foo;,
// bsm=[6]java/lang/invoke/LambdaMetafactory.metafactory(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;,
// bsmArgs=[()I,basic/LambdaA2.lambda$run$1()I (6),()I])
desc = renameRetargetIfNecessary(desc);
if (bsmArgs[1] instanceof Handle) {
bsmArgs[1] = retargetHandle((Handle)bsmArgs[1]);
}
mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs);
}
public void visitMethodInsn(int opcode, String owner, String name, String desc) {
if (owner.equals(oldname)) {
owner = newname;

View File

@@ -670,6 +670,7 @@ public class MethodInvokerRewriter {
boolean isEnum = false;
private boolean isGroovyClosure = false;
int fieldcount = 0;
private ReloadableType rtype; // Can be null if rewriting in a non reloadable type
public RewriteClassAdaptor(TypeRegistry typeRegistry, ClassVisitor classWriter) {
// TODO should it also compute frames?
@@ -867,6 +868,7 @@ public class MethodInvokerRewriter {
}
private void rewritePUTSTATIC(int opcode, String owner, String name, String desc) {
// TODO [perf] cache this information for 'us' so lookup not always necessary
int classId = typeRegistry.getTypeIdFor(owner, true);
mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(), classId));
// Make a call to check if this field operation must be intercepted:
@@ -967,6 +969,10 @@ public class MethodInvokerRewriter {
@Override
public void visitInvokeDynamicInsn(String name, String desc, org.objectweb.asm.Handle bsm, Object... bsmArgs) {
int classId = typeRegistry.getTypeIdFor(slashedclassname, false);
if (classId==-1) {
throw new IllegalStateException();
}
// TODO *shudder* what about invoke dynamic calls that target reflective APIs
boolean handled = false;
// TODO Perhaps (for sake of my sanity initially) make a distinction here between the general invokedynamic case and the special lambda support case?
@@ -988,12 +994,26 @@ public class MethodInvokerRewriter {
// int classId = typeRegistry.getTypeIdFor(owner, true);
// Call type registry to determine 'can we do what we were going to do?'
int bsmReferenceId = typeRegistry.recordBootstrapMethod(bsm,bsmArgs);
// Stack parameters at callsite into object array
// The name and descriptor (desc) show what the parameters are on the stack
if (desc.charAt(1)==')') {
// no params
mv.visitInsn(ACONST_NULL);
}
else {
Utils.collapseStackToArray(mv, desc);
}
int bsmReferenceId = typeRegistry.recordBootstrapMethod(slashedclassname,bsm,bsmArgs);
// Method java/lang/invoke/MethodHandles.lookup:()Ljava/lang/invoke/MethodHandles$Lookup;
mv.visitLdcInsn(typeRegistry.getId());
mv.visitLdcInsn(classId);
mv.visitMethodInsn(INVOKESTATIC,"java/lang/invoke/MethodHandles","lookup","()Ljava/lang/invoke/MethodHandles$Lookup;");
mv.visitLdcInsn(name+desc); // Ljava/lang/String;
mv.visitLdcInsn(bsmReferenceId); // I
mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mPerformInvokeDynamicName, "(Ljava/lang/Object;Ljava/lang/String;I)Ljava/lang/Object;");
mv.visitMethodInsn(INVOKESTATIC, tRegistryType, mPerformInvokeDynamicName, "([Ljava/lang/Object;IILjava/lang/Object;Ljava/lang/String;I)Ljava/lang/Object;");
handled=true;
// TODO handle return type
// mv.visitLdcInsn(Utils.toCombined(typeRegistry.getId(),classId));

View File

@@ -15,8 +15,6 @@
*/
package org.springsource.loaded;
import j8code.J8Helper;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@@ -50,6 +48,7 @@ import org.springsource.loaded.agent.ReloadDecision;
import org.springsource.loaded.agent.ReloadableFileChangeListener;
import org.springsource.loaded.agent.SpringLoadedPreProcessor;
import org.springsource.loaded.infra.UsedByGeneratedCode;
import org.springsource.loaded.support.Java8;
// TODO debug: stepping into deleted methods - should delete line number table for deleted methods
@@ -1464,21 +1463,13 @@ public class TypeRegistry {
}
@UsedByGeneratedCode
public static Object idyrun(Object methodHandles_lookup, String nameAndDescriptor, int bsmId) {
// bsmId = 0
// nameAndDescriptor = m()Lbasic/LambdaA$Foo;
// 0: #31 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:
// (Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;
// Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
// Method arguments:
// #32 ()I
// #33 invokestatic basic/LambdaA.lambda$run$0:()I
// #32 ()I
System.out.println("idyrun("+methodHandles_lookup+","+nameAndDescriptor+","+bsmId+")");
return J8Helper.simulateInvokeDynamic(methodHandles_lookup);
public static Object idyrun(Object[] indyParams, int typeRegistryId, int classId, Object caller, String nameAndDescriptor, int bsmId) {
// Typical next line: lookup=basic.LambdaA nameAD=m()Lbasic/LambdaA$Foo; bsmId=0
// System.err.println("idyrun("+caller+","+nameAndDescriptor+","+bsmId+")");
// TODO Currently leaking entries in bsmmap with reloads (new ones get added, old ones not removed)
ReloadableType rtype = TypeRegistry.getReloadableType(typeRegistryId, classId);
BsmInfo bsmi = bsmmap.get(rtype.getSlashedName())[bsmId];
return Java8.emulateInvokeDynamic(rtype.getLatestExecutorClass(),bsmi.bsm,bsmi.bsmArgs,caller,nameAndDescriptor, indyParams);
}
/**
@@ -1886,20 +1877,31 @@ public class TypeRegistry {
*
* @return id that represents this bootstrap method usage
*/
public synchronized int recordBootstrapMethod(Handle bsm, Object[] bsmArgs) {
// TODO [memory] search the existing bsmInfos for a matching one! Reuse!
if (nextBsmIndex >= bsmInfo.length) {
BsmInfo[] newarray = new BsmInfo[bsmInfo.length+4];
System.arraycopy(bsmInfo, 0, newarray, 0, bsmInfo.length);
bsmInfo = newarray;
public synchronized int recordBootstrapMethod(String slashedClassName, Handle bsm, Object[] bsmArgs) {
if (bsmmap == null) {
bsmmap = new HashMap<String,BsmInfo[]>();
}
bsmInfo[nextBsmIndex] = new BsmInfo(bsm,bsmArgs);
nextBsmIndex++;
return nextBsmIndex-1;
BsmInfo[] bsminfo = bsmmap.get(slashedClassName);
if (bsminfo== null) {
bsminfo = new BsmInfo[1];
// TODO do we need BsmInfo or can we just use Handle directly?
bsminfo[0] = new BsmInfo(bsm, bsmArgs);
bsmmap.put(slashedClassName,bsminfo);
return 0;
}
else {
int len = bsminfo.length;
BsmInfo[] newarray = new BsmInfo[len+1];
System.arraycopy(bsminfo, 0, newarray, 0, len);
bsminfo = newarray;
bsmmap.put(slashedClassName,bsminfo);
bsminfo[len] = new BsmInfo(bsm,bsmArgs);
return len;
}
// TODO [memory] search the existing bsmInfos for a matching one! Reuse!
}
private int nextBsmIndex = 0;
private BsmInfo[] bsmInfo = new BsmInfo[4];
private static Map<String,BsmInfo[]> bsmmap;
static class BsmInfo {
Handle bsm;