View Javadoc

1   /***
2    * 
3    */
4   package org.vafer.dependency.resources.buildtime;
5   
6   import java.util.Iterator;
7   import java.util.Set;
8   
9   import org.objectweb.asm.ClassVisitor;
10  import org.objectweb.asm.MethodVisitor;
11  import org.objectweb.asm.Opcodes;
12  import org.objectweb.asm.commons.RemappingClassAdapter;
13  import org.objectweb.asm.tree.AbstractInsnNode;
14  import org.objectweb.asm.tree.LdcInsnNode;
15  import org.objectweb.asm.tree.MethodInsnNode;
16  import org.objectweb.asm.tree.MethodNode;
17  import org.objectweb.asm.tree.analysis.Analyzer;
18  import org.objectweb.asm.tree.analysis.AnalyzerException;
19  import org.objectweb.asm.tree.analysis.Frame;
20  import org.objectweb.asm.tree.analysis.SourceInterpreter;
21  import org.objectweb.asm.tree.analysis.SourceValue;
22  import org.vafer.dependency.asm.Remapper;
23  import org.vafer.dependency.resources.ResolverUtils;
24  
25  final class BuildtimeResourceResolvingClassAdapter extends RemappingClassAdapter implements Opcodes {
26  
27  	private final Remapper mapper;
28  	
29  	public BuildtimeResourceResolvingClassAdapter(ClassVisitor cv, Remapper pMapper) {
30  		super(cv, pMapper);
31  		mapper = pMapper;
32  	}
33  
34  	protected MethodVisitor createRemappingMethodAdapter( int access, String newDesc, MethodVisitor mv ) {
35  		final MethodVisitor rmv = super.createRemappingMethodAdapter( access, newDesc, mv );
36  
37      // TODO make sure you aren't analyzing an abstract methods (no code)
38  		return new MethodNode(access, null, newDesc, null, null) {
39  			public void visitEnd() {
40  				final Analyzer an = new Analyzer(new SourceInterpreter());
41  				try {
42  					final Frame[] frames = an.analyze(className, this);
43  					// Elements of the frames array now contains info for each instruction
44  					// from the analyzed method.
45  
46  					for (int i = 0; i < frames.length; i++) {
47  						final Frame frame = frames[i];
48  						
49  						final AbstractInsnNode insn = instructions.get(i);
50  
51  						if (insn.getOpcode() == Opcodes.INVOKEVIRTUAL) {
52  							final MethodInsnNode minsn = (MethodInsnNode) insn;
53  
54  							final String owner = minsn.owner;
55  							final String name = minsn.name;
56  							
57  							if (ResolverUtils.needsResourceResolving(owner, name)) {
58  
59  								changeResource(frame, owner, name);
60  								
61  							} else if (ResolverUtils.needsClassResolving(owner, name)) {
62  								
63  								changeResource(frame, owner, name);
64  							}
65  						}
66  					}
67  					// got your mapping. can remap now. replaying recorded method
68  					accept(rmv);
69  				} catch (AnalyzerException ex) {
70  					ex.printStackTrace();
71  				}
72  			}
73  		};
74  	}
75  	
76  	private void changeResource( Frame frame, String owner, String name ) {
77  		// now check where params come from (top of the stack)
78  		final SourceValue value = (SourceValue) frame.getStack(1);
79  		final Set sources = value.insns;  // instructions that produced this value
80  		for ( Iterator it = sources.iterator(); it.hasNext(); ) {
81  			final AbstractInsnNode source = (AbstractInsnNode) it.next();
82  			if (source.getOpcode() == Opcodes.LDC) {
83  				final LdcInsnNode constant = (LdcInsnNode) source;
84  				// can change constant.cst value here
85  				
86  				final String oldValue = (String) constant.cst;
87  				final String newValue = mapper.map(oldValue);
88  				constant.cst = newValue;
89  				
90  				System.out.println("Changing " + owner + "." + name + " " + oldValue + "->" + newValue);
91  
92  			} else {
93  				// can log something about value that came not from the constant
94  				System.out.println("ERROR: resource source:" + source);
95  			}
96  		}
97  		
98  	}
99  	
100 }