1 /***************************************************************************************
2 * Copyright (c) Jonas Bonér, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.transform.inlining.weaver;
9
10 import org.objectweb.asm.Constants;
11 import org.objectweb.asm.ClassAdapter;
12 import org.objectweb.asm.ClassVisitor;
13 import org.objectweb.asm.CodeVisitor;
14 import org.objectweb.asm.Type;
15 import org.objectweb.asm.Attribute;
16 import org.codehaus.aspectwerkz.transform.Context;
17 import org.codehaus.aspectwerkz.transform.TransformationUtil;
18 import org.codehaus.aspectwerkz.transform.TransformationConstants;
19 import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
20 import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
21 import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
22 import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
23
24 import java.util.List;
25 import java.util.Iterator;
26 import java.util.Set;
27 import java.lang.reflect.Modifier;
28
29 /***
30 * Adds field and method wrappers when there has been at least one joinpoint emitted.
31 *
32 * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
33 * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
34 */
35 public class AddWrapperVisitor extends ClassAdapter implements Constants, TransformationConstants {
36
37 private ContextImpl m_context;
38
39 private Set m_addedMethods;
40
41
42 public AddWrapperVisitor(ClassVisitor classVisitor, Context context, Set alreadyAddedMethods) {
43 super(classVisitor);
44 m_context = (ContextImpl) context;
45 m_addedMethods = alreadyAddedMethods;
46 }
47
48 /***
49 * Visits the class.
50 *
51 * @param access
52 * @param name
53 * @param superName
54 * @param interfaces
55 * @param sourceFile
56 */
57 public void visit(final int version, final int access,
58 final String name,
59 final String superName,
60 final String[] interfaces,
61 final String sourceFile) {
62
63
64 List jps = m_context.getEmittedJoinPoints();
65 for (Iterator iterator = jps.iterator(); iterator.hasNext();) {
66 EmittedJoinPoint emittedJoinPoint = (EmittedJoinPoint) iterator.next();
67 int jpType = emittedJoinPoint.getJoinPointType();
68 String calleeName = emittedJoinPoint.getCalleeMemberName();
69 if (Modifier.isPublic(emittedJoinPoint.getCalleeMemberModifiers())
70 || !name.equals(emittedJoinPoint.getCalleeClassName())) {
71 continue;
72 }
73 switch (jpType) {
74 case (JoinPointType.FIELD_GET_INT) :
75 createGetFieldWrapperMethod(
76 Modifier.isStatic(emittedJoinPoint.getCalleeMemberModifiers()),
77 name,
78 emittedJoinPoint.getCalleeMemberName(),
79 emittedJoinPoint.getCalleeMemberDesc()
80 );
81 break;
82 case (JoinPointType.FIELD_SET_INT) :
83 createPutFieldWrapperMethod(
84 Modifier.isStatic(emittedJoinPoint.getCalleeMemberModifiers()),
85 name,
86 emittedJoinPoint.getCalleeMemberName(),
87 emittedJoinPoint.getCalleeMemberDesc()
88 );
89 break;
90 case (JoinPointType.METHOD_EXECUTION_INT) :
91 case (JoinPointType.METHOD_CALL_INT) :
92 createMethodWrapperMethod(
93 emittedJoinPoint.getCalleeMemberModifiers(),
94 name,
95 emittedJoinPoint.getCalleeMemberName(),
96 emittedJoinPoint.getCalleeMemberDesc(),
97 new String[0],
98 null
99 );
100 break;
101 }
102 }
103
104 super.visit(version, access, name, superName, interfaces, sourceFile);
105 }
106
107 /***
108 * Creates a public wrapper method that delegates to the GETFIELD instruction of the non-public field.
109 *
110 * @param isStaticField
111 * @param declaringTypeName
112 * @param name
113 * @param desc
114 */
115 private void createGetFieldWrapperMethod(final boolean isStaticField,
116 final String declaringTypeName,
117 final String name,
118 final String desc) {
119 String wrapperName = TransformationUtil.getWrapperMethodName(
120 name, desc, declaringTypeName, GETFIELD_WRAPPER_METHOD_PREFIX
121 );
122
123 StringBuffer signature = new StringBuffer();
124 signature.append('(');
125 signature.append(')');
126 signature.append(desc);
127
128 final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, signature.toString());
129 if (m_addedMethods.contains(wrapperKey)) {
130 return;
131 }
132 m_addedMethods.add(wrapperKey);
133
134 int modifiers = ACC_SYNTHETIC;
135 if (isStaticField) {
136 modifiers |= ACC_STATIC;
137 }
138
139 CodeVisitor mv = cv.visitMethod(
140 modifiers,
141 wrapperName,
142 signature.toString(),
143 new String[]{},
144 null
145 );
146
147 if (isStaticField) {
148 mv.visitFieldInsn(GETSTATIC, declaringTypeName, name, desc);
149 } else {
150 mv.visitVarInsn(ALOAD, 0);
151 mv.visitFieldInsn(GETFIELD, declaringTypeName, name, desc);
152 }
153
154 AsmHelper.addReturnStatement(mv, Type.getType(desc));
155 mv.visitMaxs(0, 0);
156 }
157
158 /***
159 * Creates a public wrapper method that delegates to the PUTFIELD instruction of the non-public field.
160 * Static method if field is static (PUTSTATIC instr)
161 *
162 * @param isStaticField
163 * @param declaringTypeName
164 * @param name
165 * @param desc
166 */
167 private void createPutFieldWrapperMethod(boolean isStaticField,
168 final String declaringTypeName,
169 final String name,
170 final String desc) {
171 String wrapperName = TransformationUtil.getWrapperMethodName(
172 name, desc, declaringTypeName, PUTFIELD_WRAPPER_METHOD_PREFIX
173 );
174
175 StringBuffer signature = new StringBuffer();
176 signature.append('(');
177 signature.append(desc);
178 signature.append(')');
179 signature.append('V');
180
181 final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, signature.toString());
182 if (m_addedMethods.contains(wrapperKey)) {
183 return;
184 }
185 m_addedMethods.add(wrapperKey);
186
187 int modifiers = ACC_SYNTHETIC;
188 if (isStaticField) {
189 modifiers |= ACC_STATIC;
190 }
191
192 CodeVisitor mv = cv.visitMethod(
193 modifiers,
194 wrapperName,
195 signature.toString(),
196 new String[]{},
197 null
198 );
199
200 Type fieldType = Type.getType(desc);
201 if (isStaticField) {
202 AsmHelper.loadArgumentTypes(mv, new Type[]{fieldType}, true);
203 mv.visitFieldInsn(PUTSTATIC, declaringTypeName, name, desc);
204 } else {
205 mv.visitVarInsn(ALOAD, 0);
206 AsmHelper.loadArgumentTypes(mv, new Type[]{fieldType}, false);
207 mv.visitFieldInsn(PUTFIELD, declaringTypeName, name, desc);
208 }
209
210 AsmHelper.addReturnStatement(mv, Type.VOID_TYPE);
211 mv.visitMaxs(0, 0);
212 }
213
214 /***
215 * Creates a public wrapper method that delegates to the non-public target method.
216 *
217 * @param access
218 * @param declaringTypeName
219 * @param name
220 * @param desc
221 * @param exceptions
222 * @param attrs
223 */
224 private void createMethodWrapperMethod(final int access,
225 final String declaringTypeName,
226 final String name,
227 final String desc,
228 final String[] exceptions,
229 final Attribute attrs) {
230 final String wrapperName = TransformationUtil.getWrapperMethodName(
231 name, desc, declaringTypeName, INVOKE_WRAPPER_METHOD_PREFIX
232 );
233
234 final String wrapperKey = AlreadyAddedMethodAdapter.getMethodKey(wrapperName, desc);
235 if (m_addedMethods.contains(wrapperKey)) {
236 return;
237 }
238 m_addedMethods.add(wrapperKey);
239
240 int modifiers = ACC_SYNTHETIC;
241 if (Modifier.isStatic(access)) {
242 modifiers |= ACC_STATIC;
243 }
244
245 CodeVisitor mv = super.visitMethod(
246 modifiers,
247 wrapperName,
248 desc,
249 exceptions,
250 attrs
251 );
252
253 if (Modifier.isStatic(access)) {
254 AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
255 mv.visitMethodInsn(INVOKESTATIC, declaringTypeName, name, desc);
256 } else {
257 mv.visitVarInsn(ALOAD, 0);
258 AsmHelper.loadArgumentTypes(mv, Type.getArgumentTypes(desc), Modifier.isStatic(access));
259 mv.visitMethodInsn(INVOKEVIRTUAL, declaringTypeName, name, desc);
260 }
261
262 AsmHelper.addReturnStatement(mv, Type.getReturnType(desc));
263
264 mv.visitMaxs(0, 0);
265 }
266
267 }