Skip to content

Commit 3c2b71e

Browse files
sgodbillonerwan
authored andcommitted
[playframework#802] Replace the runtime local variables tracker by a static one
1 parent 957ec38 commit 3c2b71e

File tree

15 files changed

+308
-528
lines changed

15 files changed

+308
-528
lines changed

COPYING

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ http://www.apache.org/licenses/LICENSE-2.0
3838
- Signpost
3939

4040
http://www.gnu.org/licenses/lgpl.html
41+
- Bytecodeparser
4142
- c3p0
4243
- Hibernate
4344

framework/dependencies.yml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ transitiveDependencies: false
88
# This core dependencies are required by Play framework
99
require: &allDependencies
1010
- antlr 2.7.6
11+
- bytecodeparser 0.1
1112
- c3p0 0.9.1.2
1213
- cglib -> cglib-nodep 2.2
1314
- com.google.code.gson -> gson 1.7.1

framework/lib/bytecodeparser-0.1.jar

132 KB
Binary file not shown.

framework/src/play/CorePlugin.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
import play.classloading.enhancers.ContinuationEnhancer;
1717
import play.classloading.enhancers.ControllersEnhancer;
1818
import play.classloading.enhancers.Enhancer;
19-
import play.classloading.enhancers.LocalvariablesNamesEnhancer;
19+
import play.classloading.enhancers.LVEnhancer;
2020
import play.classloading.enhancers.MailerEnhancer;
2121
import play.classloading.enhancers.PropertiesEnhancer;
2222
import play.classloading.enhancers.SigEnhancer;
@@ -285,10 +285,10 @@ public void enhance(ApplicationClass applicationClass) throws Exception {
285285
Class<?>[] enhancers = new Class[]{
286286
SigEnhancer.class,
287287
ControllersEnhancer.class,
288+
LVEnhancer.class,
288289
ContinuationEnhancer.class,
289290
MailerEnhancer.class,
290-
PropertiesEnhancer.class,
291-
LocalvariablesNamesEnhancer.class
291+
PropertiesEnhancer.class
292292
};
293293
for (Class<?> enhancer : enhancers) {
294294
try {

framework/src/play/Invoker.java

-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import java.util.ArrayList;
1717

1818
import play.Play.Mode;
19-
import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
2019
import play.exceptions.PlayException;
2120
import play.exceptions.UnexpectedException;
2221
import play.libs.F;
@@ -211,7 +210,6 @@ public void before() {
211210
*/
212211
public void after() {
213212
Play.pluginCollection.afterInvocation();
214-
LocalVariablesNamesTracer.checkEmpty(); // detect bugs ....
215213
}
216214

217215
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
package play.classloading.enhancers;
2+
3+
import java.util.Arrays;
4+
import java.util.Stack;
5+
6+
import javassist.CtBehavior;
7+
import javassist.CtClass;
8+
import javassist.CtField;
9+
import javassist.Modifier;
10+
import javassist.NotFoundException;
11+
import javassist.bytecode.BadBytecode;
12+
import javassist.bytecode.Bytecode;
13+
import javassist.bytecode.CodeAttribute;
14+
import javassist.compiler.CompileError;
15+
import javassist.compiler.Javac;
16+
import bytecodeparser.analysis.decoders.DecodedMethodInvocationOp;
17+
import bytecodeparser.analysis.decoders.DecodedMethodInvocationOp.MethodParams;
18+
import bytecodeparser.analysis.opcodes.ExitOpcode;
19+
import bytecodeparser.analysis.stack.StackAnalyzer;
20+
import bytecodeparser.analysis.stack.StackAnalyzer.Frame;
21+
import bytecodeparser.analysis.stack.StackAnalyzer.Frames;
22+
import bytecodeparser.analysis.stack.StackAnalyzer.Frames.FrameIterator;
23+
import bytecodeparser.utils.Utils;
24+
import play.Logger;
25+
import play.classloading.ApplicationClasses.ApplicationClass;
26+
import play.exceptions.UnexpectedException;
27+
28+
public class LVEnhancer extends Enhancer {
29+
@Override
30+
public void enhanceThisClass(ApplicationClass applicationClass)
31+
throws Exception {
32+
CtClass ctClass = makeClass(applicationClass);
33+
for(CtBehavior behavior : ctClass.getDeclaredMethods()) {
34+
try {
35+
if(behavior.isEmpty())
36+
continue;
37+
if(behavior.getMethodInfo().getCodeAttribute() == null)
38+
continue;
39+
if(Utils.getLocalVariableAttribute(behavior) == null)
40+
continue;
41+
42+
StackAnalyzer parser = new StackAnalyzer(behavior);
43+
44+
// first, compute hash for parameter names
45+
CtClass[] signatureTypes = behavior.getParameterTypes();
46+
int memberShift = Modifier.isStatic(behavior.getModifiers()) ? 0 : 1;
47+
48+
if(signatureTypes.length > parser.context.localVariables.size() - memberShift) {
49+
Logger.debug("ignoring method: %s %s (local vars numbers differs : %s != %s)", Modifier.toString(behavior.getModifiers()), behavior.getLongName(), signatureTypes.length, parser.context.localVariables.size() - memberShift);
50+
continue;
51+
}
52+
53+
StringBuffer signatureNames;
54+
if(signatureTypes.length == 0)
55+
signatureNames = new StringBuffer("new String[0];");
56+
else {
57+
signatureNames = new StringBuffer("new String[] {");
58+
59+
for(int i = memberShift; i < signatureTypes.length + memberShift; i++) {
60+
if(i > memberShift)
61+
signatureNames.append(",");
62+
63+
signatureNames.append("\"").append(parser.context.localVariables.get(i).name).append("\"");
64+
}
65+
signatureNames.append("};");
66+
}
67+
68+
CtField signature = CtField.make("public static String[] $" + behavior.getName() + computeMethodHash(signatureTypes) + " = " + signatureNames.toString(), ctClass);
69+
ctClass.addField(signature);
70+
// end
71+
72+
Frames frames = parser.analyze();
73+
CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
74+
FrameIterator iterator = frames.iterator();
75+
while(iterator.hasNext()) {
76+
Frame frame = iterator.next();
77+
if(!frame.isAccessible) {
78+
Logger.debug("WARNING : frame " + frame.index + " is NOT accessible");
79+
continue;
80+
}
81+
if(frame.decodedOp instanceof DecodedMethodInvocationOp) {
82+
DecodedMethodInvocationOp dmio = (DecodedMethodInvocationOp) frame.decodedOp;
83+
StringBuffer stmt = new StringBuffer("{");
84+
MethodParams methodParams = DecodedMethodInvocationOp.resolveParameters(frame);
85+
stmt.append("String[] $$paramNames = new String[").append((methodParams.subject != null ? 1 : 0) + methodParams.params.length + (methodParams.varargs != null ? methodParams.varargs.length : 0)).append("];");
86+
if(methodParams.subject != null && methodParams.subject.name != null)
87+
stmt.append("$$paramNames[").append(0).append("] = \"").append(methodParams.subject.name).append("\";");
88+
for(int i = 0; i < methodParams.params.length; i++)
89+
if(methodParams.params[i] != null && methodParams.params[i].name != null)
90+
stmt.append("$$paramNames[").append(i + (methodParams.subject != null ? 1 : 0)).append("] = \"").append(methodParams.params[i].name).append("\";");
91+
if(methodParams.varargs != null)
92+
for(int i = 0, j = methodParams.params.length + (methodParams.subject != null ? 1 : 0); i < methodParams.varargs.length; i++, j++)
93+
if(methodParams.varargs[i] != null && methodParams.varargs[i].name != null)
94+
stmt.append("$$paramNames[").append(j).append("] = \"").append(methodParams.varargs[i].name).append("\";");
95+
96+
stmt.append("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.initMethodCall(\"" + dmio.getName() + "\", " + dmio.getNbParameters() + ", " + (methodParams.subject != null ? ("\"" + methodParams.subject + "\"") : "null") + ", $$paramNames);");
97+
stmt.append("}");
98+
99+
insert(stmt.toString(), ctClass, behavior, codeAttribute, iterator, frame, false);
100+
}
101+
if(frame.decodedOp.op instanceof ExitOpcode) {
102+
insert("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.exitMethod(\""+ ctClass.getName() + "\", \"" + behavior.getName() + "\", \"" + behavior.getSignature() + "\");", ctClass, behavior, codeAttribute, iterator, frame, false);
103+
}
104+
if(iterator.isFirst()) {
105+
insert("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.enterMethod(\""+ ctClass.getName() + "\", \"" + behavior.getName() + "\", \"" + behavior.getSignature() + "\");", ctClass, behavior, codeAttribute, iterator, frame, false);
106+
}
107+
}
108+
} catch(Exception e) {
109+
throw new UnexpectedException("LVEnhancer: cannot enhance the behavior '" + behavior.getLongName() + "'", e);
110+
//e.printStackTrace();
111+
//new FileOutputStream("/tmp/" + ctClass.getName() + ".class").write(applicationClass.enhancedByteCode);
112+
//System.exit(0);
113+
}
114+
}
115+
applicationClass.enhancedByteCode = ctClass.toBytecode();
116+
ctClass.defrost();
117+
}
118+
119+
private static void insert(String stmt, CtClass ctClass, CtBehavior behavior, CodeAttribute codeAttribute, FrameIterator iterator, Frame frame, boolean after) throws CompileError, BadBytecode, NotFoundException {
120+
Javac jv = new Javac(ctClass);
121+
jv.recordLocalVariables(codeAttribute, frame.index);
122+
jv.recordParams(behavior.getParameterTypes(), Modifier.isStatic(behavior.getModifiers()));
123+
jv.setMaxLocals(codeAttribute.getMaxLocals());
124+
jv.compileStmnt(stmt);
125+
126+
Bytecode b = jv.getBytecode();
127+
int locals = b.getMaxLocals();
128+
codeAttribute.setMaxLocals(locals);
129+
iterator.insert(b.get(), after);
130+
codeAttribute.setMaxStack(codeAttribute.computeMaxStack());
131+
}
132+
133+
private static Integer computeMethodHash(CtClass[] parameters) {
134+
String[] names = new String[parameters.length];
135+
for (int i = 0; i < parameters.length; i++) {
136+
names[i] = parameters[i].getName();
137+
}
138+
return computeMethodHash(names);
139+
}
140+
141+
public static Integer computeMethodHash(Class<?>[] parameters) {
142+
String[] names = new String[parameters.length];
143+
for (int i = 0; i < parameters.length; i++) {
144+
Class<?> param = parameters[i];
145+
names[i] = "";
146+
if (param.isArray()) {
147+
int level = 1;
148+
param = param.getComponentType();
149+
// Array of array
150+
while (param.isArray()) {
151+
level++;
152+
param = param.getComponentType();
153+
}
154+
names[i] = param.getName();
155+
for (int j = 0; j < level; j++) {
156+
names[i] += "[]";
157+
}
158+
} else {
159+
names[i] = param.getName();
160+
}
161+
}
162+
return computeMethodHash(names);
163+
}
164+
165+
public static Integer computeMethodHash(String[] parameters) {
166+
StringBuffer buffer = new StringBuffer();
167+
for (String param : parameters) {
168+
buffer.append(param);
169+
}
170+
Integer hash = buffer.toString().hashCode();
171+
if (hash < 0) {
172+
return -hash;
173+
}
174+
return hash;
175+
}
176+
177+
public static class LVEnhancerRuntime {
178+
private static ThreadLocal<Stack<MethodExecution>> methodParams = new ThreadLocal<Stack<MethodExecution>>();
179+
180+
public static void enterMethod(String clazz, String method, String signature) {
181+
getCurrentMethodParams().push(new MethodExecution());
182+
}
183+
184+
public static void exitMethod(String clazz, String method, String signature) {
185+
getCurrentMethodParams().pop();
186+
}
187+
188+
public static void initMethodCall(String method, int nbParams, String subject, String[] paramNames) {
189+
getCurrentMethodParams().peek().currentNestedMethodCall = new MethodExecution(subject, paramNames, nbParams);
190+
Logger.trace("initMethodCall for '" + method + "' with " + Arrays.toString(paramNames));
191+
}
192+
193+
private static Stack<MethodExecution> getCurrentMethodParams() {
194+
Stack<MethodExecution> result = methodParams.get();
195+
if(result == null) {
196+
result = new Stack<MethodExecution>();
197+
methodParams.set(result);
198+
}
199+
return result;
200+
}
201+
202+
public static ParamsNames getParamNames() {
203+
Stack<MethodExecution> stack = getCurrentMethodParams();
204+
if(stack.size() > 0) {
205+
MethodExecution me = getCurrentMethodExecution();
206+
return new ParamsNames(me.subject, me.paramsNames, me.varargsNames);
207+
}
208+
throw new UnexpectedException("empty methodParams!");
209+
}
210+
211+
protected static LVEnhancer.MethodExecution getCurrentMethodExecution() {
212+
Stack<MethodExecution> stack = getCurrentMethodParams();
213+
if(stack.size() > 0)
214+
return stack.get(stack.size() - 1).currentNestedMethodCall;
215+
throw new UnexpectedException("empty methodParams!");
216+
}
217+
218+
public static class ParamsNames {
219+
public final String subject;
220+
public final String[] params;
221+
public final String[] varargs;
222+
223+
public boolean hasVarargs() {
224+
return varargs != null;
225+
}
226+
227+
public String[] mergeParamsAndVarargs() {
228+
Logger.trace("ParamsNames: %s :: %s", Arrays.toString(params), Arrays.toString(varargs));
229+
if(!hasVarargs())
230+
return Arrays.copyOf(params, params.length);
231+
String[] result = new String[params.length + varargs.length - 1];
232+
for(int i = 0; i < params.length - 1; i++)
233+
result[i] = params[i];
234+
for(int i = 0, j = params.length - 1; i < varargs.length; i++, j++)
235+
result[j] = varargs[i];
236+
return result;
237+
}
238+
239+
public ParamsNames(String subject, String[] params, String[] varargs) {
240+
this.subject = subject;
241+
this.params = params;
242+
this.varargs = varargs;
243+
}
244+
}
245+
}
246+
247+
protected static class MethodExecution {
248+
protected String[] paramsNames;
249+
protected String[] varargsNames;
250+
protected String subject;
251+
protected MethodExecution currentNestedMethodCall;
252+
253+
private MethodExecution() { }
254+
255+
private MethodExecution(String subject, String[] params, int nb) {
256+
this.subject = subject;
257+
paramsNames = Arrays.copyOfRange(params, 0, nb);
258+
if(nb < params.length)
259+
varargsNames = Arrays.copyOfRange(params, nb, params.length);
260+
else varargsNames = null;
261+
}
262+
}
263+
}

0 commit comments

Comments
 (0)