Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

[#802] Replace the runtime local variables tracker by a static one

  • Loading branch information...
commit 3c2b71ea9818addc2252c55496443d05b40b56fc 1 parent 957ec38
@sgodbillon sgodbillon authored erwan committed
View
1  COPYING
@@ -38,6 +38,7 @@ http://www.apache.org/licenses/LICENSE-2.0
- Signpost
http://www.gnu.org/licenses/lgpl.html
+ - Bytecodeparser
- c3p0
- Hibernate
View
1  framework/dependencies.yml
@@ -8,6 +8,7 @@ transitiveDependencies: false
# This core dependencies are required by Play framework
require: &allDependencies
- antlr 2.7.6
+ - bytecodeparser 0.1
- c3p0 0.9.1.2
- cglib -> cglib-nodep 2.2
- com.google.code.gson -> gson 1.7.1
View
BIN  framework/lib/bytecodeparser-0.1.jar
Binary file not shown
View
6 framework/src/play/CorePlugin.java
@@ -16,7 +16,7 @@
import play.classloading.enhancers.ContinuationEnhancer;
import play.classloading.enhancers.ControllersEnhancer;
import play.classloading.enhancers.Enhancer;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer;
+import play.classloading.enhancers.LVEnhancer;
import play.classloading.enhancers.MailerEnhancer;
import play.classloading.enhancers.PropertiesEnhancer;
import play.classloading.enhancers.SigEnhancer;
@@ -285,10 +285,10 @@ public void enhance(ApplicationClass applicationClass) throws Exception {
Class<?>[] enhancers = new Class[]{
SigEnhancer.class,
ControllersEnhancer.class,
+ LVEnhancer.class,
ContinuationEnhancer.class,
MailerEnhancer.class,
- PropertiesEnhancer.class,
- LocalvariablesNamesEnhancer.class
+ PropertiesEnhancer.class
};
for (Class<?> enhancer : enhancers) {
try {
View
2  framework/src/play/Invoker.java
@@ -16,7 +16,6 @@
import java.util.ArrayList;
import play.Play.Mode;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
import play.exceptions.PlayException;
import play.exceptions.UnexpectedException;
import play.libs.F;
@@ -211,7 +210,6 @@ public void before() {
*/
public void after() {
Play.pluginCollection.afterInvocation();
- LocalVariablesNamesTracer.checkEmpty(); // detect bugs ....
}
/**
View
263 framework/src/play/classloading/enhancers/LVEnhancer.java
@@ -0,0 +1,263 @@
+package play.classloading.enhancers;
+
+import java.util.Arrays;
+import java.util.Stack;
+
+import javassist.CtBehavior;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import javassist.bytecode.BadBytecode;
+import javassist.bytecode.Bytecode;
+import javassist.bytecode.CodeAttribute;
+import javassist.compiler.CompileError;
+import javassist.compiler.Javac;
+import bytecodeparser.analysis.decoders.DecodedMethodInvocationOp;
+import bytecodeparser.analysis.decoders.DecodedMethodInvocationOp.MethodParams;
+import bytecodeparser.analysis.opcodes.ExitOpcode;
+import bytecodeparser.analysis.stack.StackAnalyzer;
+import bytecodeparser.analysis.stack.StackAnalyzer.Frame;
+import bytecodeparser.analysis.stack.StackAnalyzer.Frames;
+import bytecodeparser.analysis.stack.StackAnalyzer.Frames.FrameIterator;
+import bytecodeparser.utils.Utils;
+import play.Logger;
+import play.classloading.ApplicationClasses.ApplicationClass;
+import play.exceptions.UnexpectedException;
+
+public class LVEnhancer extends Enhancer {
+ @Override
+ public void enhanceThisClass(ApplicationClass applicationClass)
+ throws Exception {
+ CtClass ctClass = makeClass(applicationClass);
+ for(CtBehavior behavior : ctClass.getDeclaredMethods()) {
+ try {
+ if(behavior.isEmpty())
+ continue;
+ if(behavior.getMethodInfo().getCodeAttribute() == null)
+ continue;
+ if(Utils.getLocalVariableAttribute(behavior) == null)
+ continue;
+
+ StackAnalyzer parser = new StackAnalyzer(behavior);
+
+ // first, compute hash for parameter names
+ CtClass[] signatureTypes = behavior.getParameterTypes();
+ int memberShift = Modifier.isStatic(behavior.getModifiers()) ? 0 : 1;
+
+ if(signatureTypes.length > parser.context.localVariables.size() - memberShift) {
+ 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);
+ continue;
+ }
+
+ StringBuffer signatureNames;
+ if(signatureTypes.length == 0)
+ signatureNames = new StringBuffer("new String[0];");
+ else {
+ signatureNames = new StringBuffer("new String[] {");
+
+ for(int i = memberShift; i < signatureTypes.length + memberShift; i++) {
+ if(i > memberShift)
+ signatureNames.append(",");
+
+ signatureNames.append("\"").append(parser.context.localVariables.get(i).name).append("\"");
+ }
+ signatureNames.append("};");
+ }
+
+ CtField signature = CtField.make("public static String[] $" + behavior.getName() + computeMethodHash(signatureTypes) + " = " + signatureNames.toString(), ctClass);
+ ctClass.addField(signature);
+ // end
+
+ Frames frames = parser.analyze();
+ CodeAttribute codeAttribute = behavior.getMethodInfo().getCodeAttribute();
+ FrameIterator iterator = frames.iterator();
+ while(iterator.hasNext()) {
+ Frame frame = iterator.next();
+ if(!frame.isAccessible) {
+ Logger.debug("WARNING : frame " + frame.index + " is NOT accessible");
+ continue;
+ }
+ if(frame.decodedOp instanceof DecodedMethodInvocationOp) {
+ DecodedMethodInvocationOp dmio = (DecodedMethodInvocationOp) frame.decodedOp;
+ StringBuffer stmt = new StringBuffer("{");
+ MethodParams methodParams = DecodedMethodInvocationOp.resolveParameters(frame);
+ stmt.append("String[] $$paramNames = new String[").append((methodParams.subject != null ? 1 : 0) + methodParams.params.length + (methodParams.varargs != null ? methodParams.varargs.length : 0)).append("];");
+ if(methodParams.subject != null && methodParams.subject.name != null)
+ stmt.append("$$paramNames[").append(0).append("] = \"").append(methodParams.subject.name).append("\";");
+ for(int i = 0; i < methodParams.params.length; i++)
+ if(methodParams.params[i] != null && methodParams.params[i].name != null)
+ stmt.append("$$paramNames[").append(i + (methodParams.subject != null ? 1 : 0)).append("] = \"").append(methodParams.params[i].name).append("\";");
+ if(methodParams.varargs != null)
+ for(int i = 0, j = methodParams.params.length + (methodParams.subject != null ? 1 : 0); i < methodParams.varargs.length; i++, j++)
+ if(methodParams.varargs[i] != null && methodParams.varargs[i].name != null)
+ stmt.append("$$paramNames[").append(j).append("] = \"").append(methodParams.varargs[i].name).append("\";");
+
+ stmt.append("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.initMethodCall(\"" + dmio.getName() + "\", " + dmio.getNbParameters() + ", " + (methodParams.subject != null ? ("\"" + methodParams.subject + "\"") : "null") + ", $$paramNames);");
+ stmt.append("}");
+
+ insert(stmt.toString(), ctClass, behavior, codeAttribute, iterator, frame, false);
+ }
+ if(frame.decodedOp.op instanceof ExitOpcode) {
+ insert("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.exitMethod(\""+ ctClass.getName() + "\", \"" + behavior.getName() + "\", \"" + behavior.getSignature() + "\");", ctClass, behavior, codeAttribute, iterator, frame, false);
+ }
+ if(iterator.isFirst()) {
+ insert("play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime.enterMethod(\""+ ctClass.getName() + "\", \"" + behavior.getName() + "\", \"" + behavior.getSignature() + "\");", ctClass, behavior, codeAttribute, iterator, frame, false);
+ }
+ }
+ } catch(Exception e) {
+ throw new UnexpectedException("LVEnhancer: cannot enhance the behavior '" + behavior.getLongName() + "'", e);
+ //e.printStackTrace();
+ //new FileOutputStream("/tmp/" + ctClass.getName() + ".class").write(applicationClass.enhancedByteCode);
+ //System.exit(0);
+ }
+ }
+ applicationClass.enhancedByteCode = ctClass.toBytecode();
+ ctClass.defrost();
+ }
+
+ private static void insert(String stmt, CtClass ctClass, CtBehavior behavior, CodeAttribute codeAttribute, FrameIterator iterator, Frame frame, boolean after) throws CompileError, BadBytecode, NotFoundException {
+ Javac jv = new Javac(ctClass);
+ jv.recordLocalVariables(codeAttribute, frame.index);
+ jv.recordParams(behavior.getParameterTypes(), Modifier.isStatic(behavior.getModifiers()));
+ jv.setMaxLocals(codeAttribute.getMaxLocals());
+ jv.compileStmnt(stmt);
+
+ Bytecode b = jv.getBytecode();
+ int locals = b.getMaxLocals();
+ codeAttribute.setMaxLocals(locals);
+ iterator.insert(b.get(), after);
+ codeAttribute.setMaxStack(codeAttribute.computeMaxStack());
+ }
+
+ private static Integer computeMethodHash(CtClass[] parameters) {
+ String[] names = new String[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ names[i] = parameters[i].getName();
+ }
+ return computeMethodHash(names);
+ }
+
+ public static Integer computeMethodHash(Class<?>[] parameters) {
+ String[] names = new String[parameters.length];
+ for (int i = 0; i < parameters.length; i++) {
+ Class<?> param = parameters[i];
+ names[i] = "";
+ if (param.isArray()) {
+ int level = 1;
+ param = param.getComponentType();
+ // Array of array
+ while (param.isArray()) {
+ level++;
+ param = param.getComponentType();
+ }
+ names[i] = param.getName();
+ for (int j = 0; j < level; j++) {
+ names[i] += "[]";
+ }
+ } else {
+ names[i] = param.getName();
+ }
+ }
+ return computeMethodHash(names);
+ }
+
+ public static Integer computeMethodHash(String[] parameters) {
+ StringBuffer buffer = new StringBuffer();
+ for (String param : parameters) {
+ buffer.append(param);
+ }
+ Integer hash = buffer.toString().hashCode();
+ if (hash < 0) {
+ return -hash;
+ }
+ return hash;
+ }
+
+ public static class LVEnhancerRuntime {
+ private static ThreadLocal<Stack<MethodExecution>> methodParams = new ThreadLocal<Stack<MethodExecution>>();
+
+ public static void enterMethod(String clazz, String method, String signature) {
+ getCurrentMethodParams().push(new MethodExecution());
+ }
+
+ public static void exitMethod(String clazz, String method, String signature) {
+ getCurrentMethodParams().pop();
+ }
+
+ public static void initMethodCall(String method, int nbParams, String subject, String[] paramNames) {
+ getCurrentMethodParams().peek().currentNestedMethodCall = new MethodExecution(subject, paramNames, nbParams);
+ Logger.trace("initMethodCall for '" + method + "' with " + Arrays.toString(paramNames));
+ }
+
+ private static Stack<MethodExecution> getCurrentMethodParams() {
+ Stack<MethodExecution> result = methodParams.get();
+ if(result == null) {
+ result = new Stack<MethodExecution>();
+ methodParams.set(result);
+ }
+ return result;
+ }
+
+ public static ParamsNames getParamNames() {
+ Stack<MethodExecution> stack = getCurrentMethodParams();
+ if(stack.size() > 0) {
+ MethodExecution me = getCurrentMethodExecution();
+ return new ParamsNames(me.subject, me.paramsNames, me.varargsNames);
+ }
+ throw new UnexpectedException("empty methodParams!");
+ }
+
+ protected static LVEnhancer.MethodExecution getCurrentMethodExecution() {
+ Stack<MethodExecution> stack = getCurrentMethodParams();
+ if(stack.size() > 0)
+ return stack.get(stack.size() - 1).currentNestedMethodCall;
+ throw new UnexpectedException("empty methodParams!");
+ }
+
+ public static class ParamsNames {
+ public final String subject;
+ public final String[] params;
+ public final String[] varargs;
+
+ public boolean hasVarargs() {
+ return varargs != null;
+ }
+
+ public String[] mergeParamsAndVarargs() {
+ Logger.trace("ParamsNames: %s :: %s", Arrays.toString(params), Arrays.toString(varargs));
+ if(!hasVarargs())
+ return Arrays.copyOf(params, params.length);
+ String[] result = new String[params.length + varargs.length - 1];
+ for(int i = 0; i < params.length - 1; i++)
+ result[i] = params[i];
+ for(int i = 0, j = params.length - 1; i < varargs.length; i++, j++)
+ result[j] = varargs[i];
+ return result;
+ }
+
+ public ParamsNames(String subject, String[] params, String[] varargs) {
+ this.subject = subject;
+ this.params = params;
+ this.varargs = varargs;
+ }
+ }
+ }
+
+ protected static class MethodExecution {
+ protected String[] paramsNames;
+ protected String[] varargsNames;
+ protected String subject;
+ protected MethodExecution currentNestedMethodCall;
+
+ private MethodExecution() { }
+
+ private MethodExecution(String subject, String[] params, int nb) {
+ this.subject = subject;
+ paramsNames = Arrays.copyOfRange(params, 0, nb);
+ if(nb < params.length)
+ varargsNames = Arrays.copyOfRange(params, nb, params.length);
+ else varargsNames = null;
+ }
+ }
+}
View
468 framework/src/play/classloading/enhancers/LocalvariablesNamesEnhancer.java
@@ -1,468 +0,0 @@
-package play.classloading.enhancers;
-
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Stack;
-import javassist.ClassPool;
-import javassist.CtClass;
-import javassist.CtConstructor;
-import javassist.CtField;
-import javassist.CtMethod;
-import javassist.Modifier;
-import javassist.bytecode.Bytecode;
-import javassist.bytecode.CodeAttribute;
-import javassist.bytecode.CodeIterator;
-import javassist.bytecode.LocalVariableAttribute;
-import javassist.bytecode.Opcode;
-import javassist.compiler.Javac;
-import javassist.compiler.NoFieldException;
-import play.Logger;
-import play.classloading.ApplicationClasses.ApplicationClass;
-import play.exceptions.UnexpectedException;
-
-/**
- * Track names of local variables ...
- */
-public class LocalvariablesNamesEnhancer extends Enhancer {
-
- public static List<String> lookupParameterNames(Constructor constructor) {
- try {
- List<String> parameters = new ArrayList<String>();
-
- ClassPool classPool = newClassPool();
- CtClass ctClass = classPool.get(constructor.getDeclaringClass().getName());
- CtClass[] cc = new CtClass[constructor.getParameterTypes().length];
- for (int i = 0; i < constructor.getParameterTypes().length; i++) {
- cc[i] = classPool.get(constructor.getParameterTypes()[i].getName());
- }
- CtConstructor ctConstructor = ctClass.getDeclaredConstructor(cc);
-
- // Signatures names
- CodeAttribute codeAttribute = (CodeAttribute) ctConstructor.getMethodInfo().getAttribute("Code");
- if (codeAttribute != null) {
- LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute) codeAttribute.getAttribute("LocalVariableTable");
- if (localVariableAttribute != null && localVariableAttribute.tableLength() >= ctConstructor.getParameterTypes().length) {
- for (int i = 0; i < ctConstructor.getParameterTypes().length + 1; i++) {
- String name = localVariableAttribute.getConstPool().getUtf8Info(localVariableAttribute.nameIndex(i));
- if (!name.equals("this")) {
- parameters.add(name);
- }
- }
- }
- }
-
- return parameters;
- } catch (Exception e) {
- throw new UnexpectedException("Cannot extract parameter names", e);
- }
- }
-
- //
- @Override
- public void enhanceThisClass(ApplicationClass applicationClass) throws Exception {
- if (isAnon(applicationClass)) {
- return;
- }
-
- CtClass ctClass = makeClass(applicationClass);
- if (!ctClass.subtypeOf(classPool.get(LocalVariablesSupport.class.getName())) && !ctClass.getName().matches("^controllers\\..*\\$class$")) {
- return;
- }
-
- for (CtMethod method : ctClass.getDeclaredMethods()) {
-
- if (method.getName().contains("$")) {
- // Generated method, skip
- continue;
- }
-
- // Signatures names
- CodeAttribute codeAttribute = (CodeAttribute) method.getMethodInfo().getAttribute("Code");
- if (codeAttribute == null || javassist.Modifier.isAbstract(method.getModifiers())) {
- continue;
- }
- LocalVariableAttribute localVariableAttribute = (LocalVariableAttribute) codeAttribute.getAttribute("LocalVariableTable");
- if (localVariableAttribute == null || localVariableAttribute.tableLength() < method.getParameterTypes().length) {
- if (method.getParameterTypes().length > 0) {
- continue;
- }
- }
- List<String> names = new ArrayList<String>();
- for (int i = 0; i < method.getParameterTypes().length + (Modifier.isStatic(method.getModifiers()) ? 0 : 1); i++) {
- if (localVariableAttribute == null) {
- continue;
- }
- try {
- String name = localVariableAttribute.getConstPool().getUtf8Info(localVariableAttribute.nameIndex(i));
- if (!name.equals("this")) {
- names.add(name);
- }
- } catch (Exception e) {
- Logger.warn(e, "While applying localvariables to %s.%s, param %s", ctClass.getName(), method.getName(), i);
- }
- }
- StringBuilder iv = new StringBuilder();
- if (names.isEmpty()) {
- iv.append("new String[0];");
- } else {
- iv.append("new String[] {");
- for (Iterator<String> i = names.iterator(); i.hasNext();) {
- iv.append("\"");
- String aliasedName = i.next();
- if (aliasedName.contains("$")) {
- aliasedName = aliasedName.substring(0, aliasedName.indexOf("$"));
- }
- iv.append(aliasedName);
- iv.append("\"");
- if (i.hasNext()) {
- iv.append(",");
- }
- }
- iv.append("};");
- }
-
- CtField signature = CtField.make("public static String[] $" + method.getName() + LocalVariablesNamesTracer.computeMethodHash(method.getParameterTypes()) + " = " + iv.toString(), ctClass);
- ctClass.addField(signature);
-
- // No variable name, skip...
- if (localVariableAttribute == null) {
- continue;
- }
-
- if (isScala(applicationClass)) {
- continue;
- }
-
- // OK.
- // Here after each local variable creation instruction,
- // we insert a call to play.utils.LocalVariables.addVariable('var', var)
- // without breaking everything...
- for (int i = 0; i < localVariableAttribute.tableLength(); i++) {
-
- // name of the local variable
- String name = localVariableAttribute.getConstPool().getUtf8Info(localVariableAttribute.nameIndex(i));
-
- // Normalize the variable name
- // For several reasons, both variables name and name$1 will be aliased to name
- String aliasedName = name;
- if (aliasedName.contains("$")) {
- aliasedName = aliasedName.substring(0, aliasedName.indexOf("$"));
- }
-
-
- if (name.equals("this")) {
- continue;
- }
-
- /* DEBUG
- IO.write(ctClass.toBytecode(), new File("/tmp/lv_"+applicationClass.name+".class"));
- ctClass.defrost();
- */
-
- try {
-
- // The instruction at which this local variable has been created
- Integer pc = localVariableAttribute.startPc(i);
-
- // Move to the next instruction (insertionPc)
- CodeIterator iterator = codeAttribute.iterator();
- iterator.move(pc);
- Integer insertionPc = iterator.next();
-
- Javac jv = new Javac(ctClass);
-
- // Compile the code snippet
- jv.recordLocalVariables(codeAttribute, insertionPc);
- jv.recordParams(method.getParameterTypes(), Modifier.isStatic(method.getModifiers()));
- jv.setMaxLocals(codeAttribute.getMaxLocals());
- jv.compileStmnt("play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.addVariable(\"" + aliasedName + "\", " + name + ");");
-
- Bytecode b = jv.getBytecode();
- int locals = b.getMaxLocals();
- int stack = b.getMaxStack();
- codeAttribute.setMaxLocals(locals);
- if (stack > codeAttribute.getMaxStack()) {
- codeAttribute.setMaxStack(stack);
- }
- iterator.insert(insertionPc, b.get());
- iterator.insert(b.getExceptionTable(), insertionPc);
-
-
- // Then we need to trace each affectation to the variable
- CodeIterator codeIterator = codeAttribute.iterator();
-
- // Bon chaque instruction de cette méthode
- while (codeIterator.hasNext()) {
- int index = codeIterator.next();
- int op = codeIterator.byteAt(index);
-
- // DEBUG
- // printOp(op);
-
- int varNumber = -1;
- // The variable changes
- if (storeByCode.containsKey(op)) {
- varNumber = storeByCode.get(op);
- if (varNumber == -2) {
- varNumber = codeIterator.byteAt(index + 1);
- }
- }
-
- // Si c'est un store de la variable en cours d'examination
- // et que c'est dans la frame d'utilisation de cette variable on trace l'affectation.
- // (en fait la frame commence à localVariableAttribute.startPc(i)-1 qui est la première affectation
- // mais aussi l'initialisation de la variable qui est deja tracé plus haut, donc on commence à localVariableAttribute.startPc(i))
- if (varNumber == localVariableAttribute.index(i) && index >= localVariableAttribute.startPc(i) && index < localVariableAttribute.startPc(i) + localVariableAttribute.codeLength(i)) {
-
- jv.compileStmnt("play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.addVariable(\"" + aliasedName + "\", " + name + ");");
-
- b = jv.getBytecode();
- locals = b.getMaxLocals();
- stack = b.getMaxStack();
- codeAttribute.setMaxLocals(locals);
-
- if (stack > codeAttribute.getMaxStack()) {
- codeAttribute.setMaxStack(stack);
- }
- codeIterator.insert(b.get());
-
- }
-
- }
-
-
- } catch (NoFieldException e) {
- // Well probably a compiled optimizer (I hope so)
- }
-
- }
-
- // init variable tracer
- method.insertBefore("play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.enter();");
- method.insertAfter("play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.exit();", true);
-
- }
-
- // Done.
- applicationClass.enhancedByteCode = ctClass.toBytecode();
- ctClass.defrost();
-
- }
-
- /**
- * Mark class that need local variables tracking
- */
- public static interface LocalVariablesSupport {
- }
-
- /**
- * Runtime part.
- */
- public static class LocalVariablesNamesTracer {
-
- public static Integer computeMethodHash(CtClass[] parameters) {
- String[] names = new String[parameters.length];
- for (int i = 0; i < parameters.length; i++) {
- names[i] = parameters[i].getName();
- }
- return computeMethodHash(names);
- }
-
- public static Integer computeMethodHash(Class<?>[] parameters) {
- String[] names = new String[parameters.length];
- for (int i = 0; i < parameters.length; i++) {
- Class<?> param = parameters[i];
- names[i] = "";
- if (param.isArray()) {
- int level = 1;
- param = param.getComponentType();
- // Array of array
- while (param.isArray()) {
- level++;
- param = param.getComponentType();
- }
- names[i] = param.getName();
- for (int j = 0; j < level; j++) {
- names[i] += "[]";
- }
- } else {
- names[i] = param.getName();
- }
- }
- return computeMethodHash(names);
- }
-
- public static Integer computeMethodHash(String[] parameters) {
- StringBuffer buffer = new StringBuffer();
- for (String param : parameters) {
- buffer.append(param);
- }
- Integer hash = buffer.toString().hashCode();
- if (hash < 0) {
- return -hash;
- }
- return hash;
- }
- static ThreadLocal<Stack<Map<String, Object>>> localVariables = new ThreadLocal<Stack<Map<String, Object>>>();
-
- public static void checkEmpty() {
- if (localVariables.get() != null && localVariables.get().size() != 0) {
- Logger.error("LocalVariablesNamesTracer.checkEmpty, constraint violated (%s)", localVariables.get().size());
- }
- }
-
- public static void clear() {
- if (localVariables.get() != null) {
- localVariables.set(null);
- }
- }
-
- public static void enter() {
- if (localVariables.get() == null) {
- localVariables.set(new Stack<Map<String, Object>>());
- }
- localVariables.get().push(new HashMap<String, Object>());
- }
-
- public static void exit() {
- if (localVariables.get().isEmpty()) {
- return;
- }
- localVariables.get().pop().clear();
- }
-
- public static Map<String, Object> locals() {
- if (localVariables.get() != null && !localVariables.get().empty()) {
- return localVariables.get().peek();
- }
- return new HashMap<String, Object>();
- }
-
- public static void addVariable(String name, Object o) {
- locals().put(name, o);
- }
-
- public static void addVariable(String name, boolean b) {
- locals().put(name, b);
- }
-
- public static void addVariable(String name, char c) {
- locals().put(name, c);
- }
-
- public static void addVariable(String name, byte b) {
- locals().put(name, b);
- }
-
- public static void addVariable(String name, double d) {
- locals().put(name, d);
- }
-
- public static void addVariable(String name, float f) {
- locals().put(name, f);
- }
-
- public static void addVariable(String name, int i) {
- locals().put(name, i);
- }
-
- public static void addVariable(String name, long l) {
- locals().put(name, l);
- }
-
- public static void addVariable(String name, short s) {
- locals().put(name, s);
- }
-
- public static Map<String, Object> getLocalVariables() {
- return locals();
- }
-
- public static List<String> getAllLocalVariableNames(Object o) {
- List<String> allNames = new ArrayList<String>();
- for (String variable : getLocalVariables().keySet()) {
- if (getLocalVariables().get(variable) == o) {
- allNames.add(variable);
- }
- if (o != null && o instanceof Number && o.equals(getLocalVariables().get(variable))) {
- allNames.add(variable);
- }
- }
- return allNames;
- }
-
- public static Object getLocalVariable(String variable) {
- return getLocalVariables().get(variable);
- }
-
- public static Stack<Map<String, Object>> getLocalVariablesStateBeforeAwait() {
- Stack<Map<String, Object>> state = localVariables.get();
- // must clear the ThreadLocal to prevent destroying the state when exit() is called due to continuations-suspend
- localVariables.set(new Stack<Map<String, Object>>());
- return state;
- }
-
- public static void setLocalVariablesStateAfterAwait(Stack<Map<String, Object>> state) {
- if (state==null) {
- state = new Stack<Map<String, Object>>();
- }
- localVariables.set( state );
- }
- }
- private final static Map<Integer, Integer> storeByCode = new HashMap<Integer, Integer>();
-
- /**
- * Useful instructions
- */
- static {
- storeByCode.put(CodeIterator.ASTORE_0, 0);
- storeByCode.put(CodeIterator.ASTORE_1, 1);
- storeByCode.put(CodeIterator.ASTORE_2, 2);
- storeByCode.put(CodeIterator.ASTORE_3, 3);
- storeByCode.put(CodeIterator.ASTORE, -2);
-
- storeByCode.put(CodeIterator.ISTORE_0, 0);
- storeByCode.put(CodeIterator.ISTORE_1, 1);
- storeByCode.put(CodeIterator.ISTORE_2, 2);
- storeByCode.put(CodeIterator.ISTORE_3, 3);
- storeByCode.put(CodeIterator.ISTORE, -2);
- storeByCode.put(CodeIterator.IINC, -2);
-
- storeByCode.put(CodeIterator.LSTORE_0, 0);
- storeByCode.put(CodeIterator.LSTORE_1, 1);
- storeByCode.put(CodeIterator.LSTORE_2, 2);
- storeByCode.put(CodeIterator.LSTORE_3, 3);
- storeByCode.put(CodeIterator.LSTORE, -2);
-
- storeByCode.put(CodeIterator.FSTORE_0, 0);
- storeByCode.put(CodeIterator.FSTORE_1, 1);
- storeByCode.put(CodeIterator.FSTORE_2, 2);
- storeByCode.put(CodeIterator.FSTORE_3, 3);
- storeByCode.put(CodeIterator.FSTORE, -2);
-
- storeByCode.put(CodeIterator.DSTORE_0, 0);
- storeByCode.put(CodeIterator.DSTORE_1, 1);
- storeByCode.put(CodeIterator.DSTORE_2, 2);
- storeByCode.put(CodeIterator.DSTORE_3, 3);
- storeByCode.put(CodeIterator.DSTORE, -2);
- }
-
- /**
- * Debug utility. Display a byte code op as plain text.
- */
- public static void printOp(int op) {
- try {
- for (Field f : Opcode.class.getDeclaredFields()) {
- if (java.lang.reflect.Modifier.isStatic(f.getModifiers()) && java.lang.reflect.Modifier.isPublic(f.getModifiers()) && f.getInt(null) == op) {
- System.out.println(op + " " + f.getName());
- }
- }
- } catch (Exception e) {
- }
- }
-}
View
11 framework/src/play/data/validation/Validation.java
@@ -10,8 +10,9 @@
import java.util.Map;
import java.util.regex.Pattern;
import net.sf.oval.configuration.annotation.AbstractAnnotationCheck;
+import play.Logger;
import play.Play;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
+import play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime;
import play.exceptions.UnexpectedException;
public class Validation {
@@ -451,10 +452,10 @@ static ValidationResult applyCheck(AbstractAnnotationCheck<?> check, String key,
}
static String getLocalName(Object o) {
- List<String> names = LocalVariablesNamesTracer.getAllLocalVariableNames(o);
- if (names.size() > 0) {
- return names.get(0);
- }
+ String[] names = LVEnhancerRuntime.getParamNames().params;
+ Logger.debug("VALIDATION o's name is " + names[0]);
+ if(names.length > 0 && names[0] != null)
+ return names[0];
return "";
}
View
3  framework/src/play/db/jpa/GenericModel.java
@@ -14,6 +14,7 @@
import javax.persistence.*;
import play.Play;
+import play.classloading.enhancers.LVEnhancer;
import play.data.binding.BeanWrapper;
import play.data.binding.Binder;
import play.data.validation.Validation;
@@ -156,7 +157,7 @@
}
public boolean validateAndSave() {
- if (Validation.current().valid(this).ok) {
+ if (Validation.valid(LVEnhancer.LVEnhancerRuntime.getParamNames().subject, this).ok) {
save();
return true;
}
View
45 framework/src/play/mvc/Controller.java
@@ -7,9 +7,7 @@
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
-import java.util.List;
import java.util.Map;
-import java.util.Stack;
import java.util.concurrent.Future;
import org.w3c.dom.Document;
@@ -19,9 +17,8 @@
import play.Play;
import play.classloading.enhancers.ControllersEnhancer.ControllerInstrumentation;
import play.classloading.enhancers.ControllersEnhancer.ControllerSupport;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesSupport;
+import play.classloading.enhancers.LVEnhancer;
+import play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime;
import play.data.binding.Unbinder;
import play.data.validation.Validation;
import play.exceptions.NoRouteFoundException;
@@ -66,7 +63,7 @@
* This is the class that your controllers should extend.
*
*/
-public class Controller implements ControllerSupport, LocalVariablesSupport {
+public class Controller implements ControllerSupport {
/**
* The current HTTP request: the message sent by the client to the server.
@@ -552,7 +549,7 @@ protected static void redirect(String action, boolean permanent, Object... args)
try {
Map<String, Object> newArgs = new HashMap<String, Object>(args.length);
Method actionMethod = (Method) ActionInvoker.getActionMethod(action)[1];
- String[] names = (String[]) actionMethod.getDeclaringClass().getDeclaredField("$" + actionMethod.getName() + LocalVariablesNamesTracer.computeMethodHash(actionMethod.getParameterTypes())).get(null);
+ String[] names = (String[]) actionMethod.getDeclaringClass().getDeclaredField("$" + actionMethod.getName() + LVEnhancer.computeMethodHash(actionMethod.getParameterTypes())).get(null);
for (int i = 0; i < names.length && i < args.length; i++) {
Annotation[] annotations = actionMethod.getParameterAnnotations()[i];
boolean isDefault = false;
@@ -630,11 +627,11 @@ protected static boolean templateExists(String templateName) {
protected static void renderTemplate(String templateName, Object... args) {
// Template datas
Map<String, Object> templateBinding = new HashMap<String, Object>(16);
- for (Object o : args) {
- List<String> names = LocalVariablesNamesTracer.getAllLocalVariableNames(o);
- for (String name : names) {
- templateBinding.put(name, o);
- }
+ String[] names = LVEnhancerRuntime.getParamNames().varargs;
+ if(args != null && args.length > 0 && names == null)
+ throw new UnexpectedException("no varargs names while args.length > 0 !");
+ for(int i = 0; i < args.length; i++) {
+ templateBinding.put(names[i], args[i]);
}
renderTemplate(templateName, templateBinding);
}
@@ -686,7 +683,7 @@ protected static void renderTemplate(Map<String,Object> args) {
*/
protected static void render(Object... args) {
String templateName = null;
- if (args.length > 0 && args[0] instanceof String && LocalVariablesNamesTracer.getAllLocalVariableNames(args[0]).isEmpty()) {
+ if (args.length > 0 && args[0] instanceof String && LVEnhancerRuntime.getParamNames().mergeParamsAndVarargs()[0] == null) {
templateName = args[0].toString();
} else {
templateName = template();
@@ -784,12 +781,9 @@ protected static String template(String templateName) {
@Deprecated
protected static void parent(Object... args) {
Map<String, Object> map = new HashMap<String, Object>(16);
- for (Object o : args) {
- List<String> names = LocalVariablesNamesTracer.getAllLocalVariableNames(o);
- for (String name : names) {
- map.put(name, o);
- }
- }
+ String[] names = LVEnhancerRuntime.getParamNames().mergeParamsAndVarargs();
+ for(int i = 0; i < names.length; i++)
+ map.put(names[i], args[i]);
parent(map);
}
@@ -899,21 +893,9 @@ protected static void await(String timeout, F.Action0 callback) {
protected static void await(int millis) {
Request.current().isNew = false;
- storeOrRestoreLocalVariableNamesState();
Continuation.suspend(millis);
}
- private static void storeOrRestoreLocalVariableNamesState() {
- Stack<Map<String, Object>> localVariablesState = (Stack<Map<String, Object>>) Http.Request.current().args.remove(ActionInvoker.LV);
- if (localVariablesState!=null) {
- //we are restoring localVariableNames after suspend
- LocalvariablesNamesEnhancer.LocalVariablesNamesTracer.setLocalVariablesStateAfterAwait(localVariablesState);
- } else {
- // we are capturing localVariableNames before suspend
- Request.current().args.put(ActionInvoker.LV, LocalVariablesNamesTracer.getLocalVariablesStateBeforeAwait());
- }
- }
-
protected static void await(int millis, F.Action0 callback) {
Request.current().isNew = false;
Request.current().args.put(ActionInvoker.A, callback);
@@ -922,7 +904,6 @@ protected static void await(int millis, F.Action0 callback) {
@SuppressWarnings("unchecked")
protected static <T> T await(Future<T> future) {
- storeOrRestoreLocalVariableNamesState();
if(future != null) {
Request.current().args.put(ActionInvoker.F, future);
} else if(Request.current().args.containsKey(ActionInvoker.F)) {
View
18 framework/src/play/mvc/Mailer.java
@@ -11,8 +11,7 @@
import org.apache.commons.lang.StringUtils;
import org.apache.commons.mail.*;
import play.Logger;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesSupport;
+import play.classloading.enhancers.LVEnhancer.LVEnhancerRuntime;
import play.exceptions.MailException;
import play.exceptions.TemplateNotFoundException;
import play.exceptions.UnexpectedException;
@@ -25,7 +24,7 @@
/**
* Application mailer support
*/
-public class Mailer implements LocalVariablesSupport {
+public class Mailer {
protected static ThreadLocal<HashMap<String, Object>> infos = new ThreadLocal<HashMap<String, Object>>();
@@ -186,19 +185,18 @@ public static void addHeader(String key, String value) {
templateName = templateName.substring(0, templateName.indexOf("("));
templateName = templateName.replace(".", "/");
+ String[] names = LVEnhancerRuntime.getParamNames().mergeParamsAndVarargs();
+
// overrides Template name
- if (args.length > 0 && args[0] instanceof String && LocalVariablesNamesTracer.getAllLocalVariableNames(args[0]).isEmpty()) {
+ if (args.length > 0 && args[0] instanceof String && names[0] == null) {
templateName = args[0].toString();
}
final Map<String, Object> templateHtmlBinding = new HashMap<String, Object>();
final Map<String, Object> templateTextBinding = new HashMap<String, Object>();
- for (Object o : args) {
- List<String> names = LocalVariablesNamesTracer.getAllLocalVariableNames(o);
- for (String name : names) {
- templateHtmlBinding.put(name, o);
- templateTextBinding.put(name, o);
- }
+ for (int i = 0; i < names.length; i++) {
+ templateHtmlBinding.put(names[i], args[i]);
+ templateTextBinding.put(names[i], args[i]);
}
// The rule is as follow: If we ask for text/plain, we don't care about the HTML
View
3  framework/src/play/mvc/WebSocketController.java
@@ -2,12 +2,11 @@
import java.util.concurrent.Future;
import play.classloading.enhancers.ControllersEnhancer.ControllerSupport;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesSupport;
import play.data.validation.Validation;
import play.libs.F;
import play.mvc.results.WebSocketDisconnect;
-public class WebSocketController implements ControllerSupport, LocalVariablesSupport {
+public class WebSocketController implements ControllerSupport {
protected static Http.Request request = null;
protected static Http.Inbound inbound = null;
View
4 framework/src/play/templates/GroovyTemplate.java
@@ -31,7 +31,7 @@
import play.Play;
import play.Play.Mode;
import play.classloading.BytecodeCache;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
+import play.classloading.enhancers.LVEnhancer;
import play.data.binding.Unbinder;
import play.exceptions.ActionNotFoundException;
import play.exceptions.NoRouteFoundException;
@@ -472,7 +472,7 @@ public Object invokeMethod(String name, Object param) {
try {
Map<String, Object> r = new HashMap<String, Object>();
Method actionMethod = (Method) ActionInvoker.getActionMethod(action)[1];
- String[] names = (String[]) actionMethod.getDeclaringClass().getDeclaredField("$" + actionMethod.getName() + LocalVariablesNamesTracer.computeMethodHash(actionMethod.getParameterTypes())).get(null);
+ String[] names = (String[]) actionMethod.getDeclaringClass().getDeclaredField("$" + actionMethod.getName() + LVEnhancer.computeMethodHash(actionMethod.getParameterTypes())).get(null);
if (param instanceof Object[]) {
if(((Object[])param).length == 1 && ((Object[])param)[0] instanceof Map) {
r = (Map<String,Object>)((Object[])param)[0];
View
4 framework/src/play/utils/Java.java
@@ -19,7 +19,7 @@
import javassist.bytecode.SourceFileAttribute;
import play.Play;
import play.classloading.ApplicationClassloaderState;
-import play.classloading.enhancers.LocalvariablesNamesEnhancer.LocalVariablesNamesTracer;
+import play.classloading.enhancers.LVEnhancer;
import play.data.binding.Binder;
import play.exceptions.UnexpectedException;
import play.mvc.After;
@@ -190,7 +190,7 @@ public static Object invokeStatic(Method method, Object[] args) throws Exception
*/
public static String[] parameterNames(Method method) throws Exception {
try {
- return (String[]) method.getDeclaringClass().getDeclaredField("$" + method.getName() + LocalVariablesNamesTracer.computeMethodHash(method.getParameterTypes())).get(null);
+ return (String[]) method.getDeclaringClass().getDeclaredField("$" + method.getName() + LVEnhancer.computeMethodHash(method.getParameterTypes())).get(null);
} catch (Exception e) {
throw new UnexpectedException("Cannot read parameter names for " + method);
}
View
7 samples-and-tests/booking/app/views/Hotels/list.html
@@ -1,3 +1,8 @@
+%{
+System.out.println("in template: page=" + page + ", hotels=" + hotels);
+}%
+
+
#{if hotels}
<table>
<thead>
@@ -32,4 +37,4 @@
<p>
No more results
</p>
-#{/else}
+#{/else}

1 comment on commit 3c2b71e

@mbknor
Collaborator

This commit broke a test:

postANewJobAsCompany in samples-and-tests/jobboard

I tried to revert the commit and postANewJobAsCompany PASSED again

Please sign in to comment.
Something went wrong with that request. Please try again.