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