Skip to content

Commit

Permalink
junkdog#385: transplanting method bytecode between classes
Browse files Browse the repository at this point in the history
  • Loading branch information
junkdog committed Apr 30, 2016
1 parent 12ca463 commit 5d4bd25
Show file tree
Hide file tree
Showing 11 changed files with 356 additions and 249 deletions.
1 change: 0 additions & 1 deletion artemis-weaver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@
<groupId>${project.groupId}</groupId>
<artifactId>artemis-odb</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

Expand Down
2 changes: 1 addition & 1 deletion artemis-weaver/src/main/java/com/artemis/ClassUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public static void writeClass(ClassWriter writer, String file) {
}
}
}

public static String verifyClass(ClassWriter writer) {
StringWriter sw = new StringWriter();
PrintWriter printer = new PrintWriter(sw);
Expand Down
39 changes: 0 additions & 39 deletions artemis-weaver/src/main/java/com/artemis/Weaver.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ public WeaverLog execute() {

List<File> classes = ClassUtil.find(targetClasses);
rewriteComponents(classes, log);
// rewriteFieldAccess(classes, packedFieldAccess(log.components), log);
rewriteProfilers(classes);

if (ClassMetadata.GlobalConfiguration.optimizeEntitySystems)
Expand Down Expand Up @@ -107,44 +106,6 @@ private static void rewriteComponents(List<File> classes, WeaverLog log) {
log.timeComponents = timer.duration();
}


// // TODO: collect rewritten systems
// private static void rewriteFieldAccess(List<File> classes, List<ClassMetadata> packed, WeaverLog log) {
// if (packed.isEmpty())
// return;
//
// Timer timer = new Timer();
//
// ExecutorService threadPool = newThreadPool();
//
// List<Future<ClassMetadata>> tasks = new ArrayList<Future<ClassMetadata>>();
// for (File file : classes) {
// String path = file.getAbsolutePath();
// ClassReader cr = classReaderFor(path);
// ComponentAccessTransmuter transmuter = new ComponentAccessTransmuter(path, cr, packed);
//
// tasks.add(threadPool.submit(transmuter));
// }
//
// try {
//
// List<ClassMetadata> processed = new ArrayList<ClassMetadata>();
// for (Future<ClassMetadata> result : tasks) {
// ClassMetadata metadata = result.get();
// if (metadata != null)
// processed.add(metadata);
// }
//
// awaitTermination(threadPool);
// log.timeComponentSystems = timer.duration();
// log.componentSystems = processed;
// } catch (InterruptedException e) {
// e.printStackTrace();
// } catch (ExecutionException e) {
// e.printStackTrace();
// }
// }

public static void retainFieldsWhenPacking(boolean ideFriendlyPacking) {
ClassMetadata.GlobalConfiguration.ideFriendlyPacking = ideFriendlyPacking;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,13 @@ public MethodDescriptor(int access, String name, String desc, String signature,
this.signature = signature;
this.exceptions = exceptions;
}

@Override
public String toString() {
return "MethodDescriptor[" +
", name='" + name + '\'' +
", desc='" + desc + '\'' +
", signature='" + signature + '\'' +
']';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@

import com.artemis.ClassUtil;
import com.artemis.meta.ClassMetadata;
import com.artemis.systems.EntityProcessingSystem;
import com.artemis.systems.IteratingSystem;
import com.artemis.weaver.optimizer.EntitySystemType;
import com.artemis.weaver.optimizer.OptimizingSystemWeaver;
import com.artemis.weaver.optimizer.SystemBytecodeInjector;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import com.artemis.weaver.transplant.MethodTransplantAdapter;
import org.objectweb.asm.*;

import java.io.IOException;

Expand All @@ -21,27 +21,86 @@ public OptimizationTransmuter(String file, ClassReader cr, ClassMetadata meta) {
this.cr = cr;
this.meta = meta;
}

@Override
protected Void process(String file) throws IOException {
cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
ClassVisitor cv = cw;

cr = new SystemBytecodeInjector(cr, meta).transform();

cv = new ClassVisitor(ASM5, cv) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("processSystem".equals(name) && "()V".equals(desc))
mv = new ProcessInvocationOptimizer(meta, mv);

return mv;
}
};
switch (EntitySystemType.resolve(meta)) {
case ENTITY_PROCESSING:
cv = new MethodTransplantAdapter(
EntityProcessingSystem.class, "processSystem", "()V", cv, meta);

break;
case ITERATING:
cv = new MethodTransplantAdapter(
IteratingSystem.class, "processSystem", "()V", cv, meta);

break;
default:
throw new RuntimeException("missing case: " + EntitySystemType.resolve(meta));
}
cv = new OptimizingSystemWeaver(cv, meta);

try {
cr.accept(cv, ClassReader.EXPAND_FRAMES);
if (file != null) ClassUtil.writeClass(cw, file);
} catch (Exception e) {
e.printStackTrace();
}

return null;
}

public ClassWriter getClassWriter() {
return cw;
}

}

static class ProcessInvocationOptimizer extends MethodVisitor {
private final ClassMetadata meta;

public ProcessInvocationOptimizer(ClassMetadata meta, MethodVisitor mv) {
super(ASM5, mv);
this.meta = meta;
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if ("process".equals(name) && "(I)V".equals(desc) && !itf) {
mv.visitMethodInsn(invocation(meta.sysetemOptimizable),
owner, name, desc, false);
} else if ("process".equals(name) && "(Lcom/artemis/Entity;)V".equals(desc) && !itf) {
mv.visitMethodInsn(invocation(meta.sysetemOptimizable),
owner, name, desc, false);
} else {
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
}


private static int invocation(ClassMetadata.OptimizationType systemOptimization) {
switch (systemOptimization) {
case FULL:
return INVOKESPECIAL;
case SAFE:
return INVOKEVIRTUAL;
case NOT_OPTIMIZABLE:
assert false;
default:
throw new RuntimeException("Missing case: " + systemOptimization);

}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package com.artemis.weaver.optimizer;

import com.artemis.meta.ClassMetadata;
import com.artemis.weaver.WeaverException;
import org.objectweb.asm.*;

import java.io.IOException;
import java.io.InputStream;

public class ProcessSystemMethodTransplanter extends ClassVisitor implements Opcodes {
private final ClassReader source;
private final String method;
private final String methodDesc;
private final ClassMetadata meta;

public ProcessSystemMethodTransplanter(ClassReader source,
String method,
String methodDesc,
ClassVisitor target,
ClassMetadata meta) {
super(ASM5, target);
this.source = source;
this.method = method;
this.methodDesc = methodDesc;
this.meta = meta;
}

public ProcessSystemMethodTransplanter(Class<?> source,
String method,
String methodDesc,
ClassVisitor target,
ClassMetadata meta) {

this(toClassReader(source), method, methodDesc, target, meta);
}

@Override
public void visitEnd() {
source.accept(new ClassVisitor(ASM5) {
@Override
public MethodVisitor visitMethod(int access,
String name,
String desc,
String signature,
String[] exceptions) {

if (!(method.equals(name) && methodDesc.equals(desc)))
return null;

MethodVisitor mv = ProcessSystemMethodTransplanter.this.visitMethod(access, name, desc, signature, exceptions);
return new BodyTransplantAdapter(source.getClassName(), meta, mv);
}
}, 0);


super.visitEnd();
}

private static ClassReader toClassReader(Class<?> klazz) {
try {
String resourceName = "/" + klazz.getName().replace('.', '/') + ".class";
InputStream classStream = klazz.getResourceAsStream(resourceName);
return new ClassReader(classStream);
} catch (IOException e) {
throw new WeaverException("Failed to create reader for " + klazz, e);
}
}

protected static class BodyTransplantAdapter extends MethodVisitor {
private final String owner;
private final String oldOwner;
private final ClassMetadata meta;

public BodyTransplantAdapter(String oldOwner, ClassMetadata meta, MethodVisitor mv) {
super(ASM5, mv);
this.oldOwner = oldOwner;
this.meta = meta;
this.owner = meta.type.getInternalName();
}

@Override
public void visitLineNumber(int line, Label start) {
// remove line numbers
}

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
if (oldOwner.equals(owner))
owner = this.owner;

super.visitFieldInsn(opcode, owner, name, desc);
}

@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
if (local.length > 0 && oldOwner.equals(local[0]))
local[0] = owner;

super.visitFrame(type, nLocal, local, nStack, stack);
}

@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (oldOwner.equals(owner))
owner = this.owner;

super.visitMethodInsn(opcode, owner, name, desc, itf);
}

@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
if ("this".equals(name))
desc = meta.type.toString();

super.visitLocalVariable(name, desc, signature, start, end, index);
}
}
}
Loading

0 comments on commit 5d4bd25

Please sign in to comment.