001: /**************************************************************************************
002: * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
003: * http://aspectwerkz.codehaus.org *
004: * ---------------------------------------------------------------------------------- *
005: * The software in this package is published under the terms of the LGPL license *
006: * a copy of which has been included with this distribution in the license.txt file. *
007: **************************************************************************************/package org.codehaus.aspectwerkz.transform.inlining.weaver;
008:
009: import org.objectweb.asm.ClassAdapter;
010: import org.objectweb.asm.ClassVisitor;
011: import org.objectweb.asm.CodeVisitor;
012: import org.objectweb.asm.Attribute;
013: import org.objectweb.asm.CodeAdapter;
014: import org.objectweb.asm.Type;
015: import org.objectweb.asm.Label;
016: import org.codehaus.aspectwerkz.definition.SystemDefinition;
017: import org.codehaus.aspectwerkz.expression.ExpressionContext;
018: import org.codehaus.aspectwerkz.expression.PointcutType;
019: import org.codehaus.aspectwerkz.joinpoint.management.JoinPointType;
020: import org.codehaus.aspectwerkz.reflect.ClassInfo;
021: import org.codehaus.aspectwerkz.reflect.FieldInfo;
022: import org.codehaus.aspectwerkz.reflect.MemberInfo;
023: import org.codehaus.aspectwerkz.reflect.impl.asm.AsmClassInfo;
024: import org.codehaus.aspectwerkz.transform.Context;
025: import org.codehaus.aspectwerkz.transform.TransformationUtil;
026: import org.codehaus.aspectwerkz.transform.TransformationConstants;
027: import org.codehaus.aspectwerkz.transform.inlining.compiler.AbstractJoinPointCompiler;
028: import org.codehaus.aspectwerkz.transform.inlining.ContextImpl;
029: import org.codehaus.aspectwerkz.transform.inlining.AsmHelper;
030: import org.codehaus.aspectwerkz.transform.inlining.EmittedJoinPoint;
031:
032: import java.lang.reflect.Modifier;
033: import java.util.Iterator;
034: import java.util.Set;
035:
036: /**
037: * Instruments method SET and GET join points by replacing PUTFIELD and GETFIELD instructions with invocations
038: * of the compiled join point.
039: *
040: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
041: */
042: public class FieldSetFieldGetVisitor extends ClassAdapter implements
043: TransformationConstants {
044:
045: private final ContextImpl m_ctx;
046: private final ClassLoader m_loader;
047: private final ClassInfo m_callerClassInfo;
048:
049: private Label m_lastLabelForLineNumber = EmittedJoinPoint.NO_LINE_NUMBER;
050:
051: /**
052: * Creates a new instance.
053: *
054: * @param cv
055: * @param loader
056: * @param classInfo
057: * @param ctx
058: */
059: public FieldSetFieldGetVisitor(final ClassVisitor cv,
060: final ClassLoader loader, final ClassInfo classInfo,
061: final Context ctx) {
062: super (cv);
063: m_loader = loader;
064: m_callerClassInfo = classInfo;
065: m_ctx = (ContextImpl) ctx;
066: }
067:
068: /**
069: * Visits the caller methods.
070: *
071: * @param access
072: * @param name
073: * @param desc
074: * @param exceptions
075: * @param attrs
076: * @return
077: */
078: public CodeVisitor visitMethod(final int access, final String name,
079: final String desc, final String[] exceptions,
080: final Attribute attrs) {
081:
082: if (name.startsWith(WRAPPER_METHOD_PREFIX)) {
083: return super .visitMethod(access, name, desc, exceptions,
084: attrs);
085: }
086:
087: CodeVisitor mv = cv.visitMethod(access, name, desc, exceptions,
088: attrs);
089: return mv == null ? null
090: : new ReplacePutFieldAndGetFieldInstructionCodeAdapter(
091: mv, m_loader, m_callerClassInfo, m_ctx
092: .getClassName(), name, desc);
093: }
094:
095: /**
096: * Replaces PUTFIELD and GETFIELD instructions with a call to the compiled JoinPoint instance.
097: *
098: * @author <a href="mailto:jboner@codehaus.org">Jonas Bonér </a>
099: * @author <a href="mailto:alex AT gnilux DOT com">Alexandre Vasseur</a>
100: */
101: public class ReplacePutFieldAndGetFieldInstructionCodeAdapter
102: extends AfterObjectInitializationCodeAdapter {
103:
104: private final ClassLoader m_loader;
105: private final ClassInfo m_callerClassInfo;
106: private final String m_callerClassName;
107: private final String m_callerMethodName;
108: private final String m_callerMethodDesc;
109: private final MemberInfo m_callerMemberInfo;
110:
111: /**
112: * Creates a new instance.
113: *
114: * @param ca
115: * @param loader
116: * @param callerClassInfo
117: * @param callerClassName
118: * @param callerMethodName
119: * @param callerMethodDesc
120: */
121: public ReplacePutFieldAndGetFieldInstructionCodeAdapter(
122: final CodeVisitor ca, final ClassLoader loader,
123: final ClassInfo callerClassInfo,
124: final String callerClassName,
125: final String callerMethodName,
126: final String callerMethodDesc) {
127: super (ca, callerMethodName);
128: m_loader = loader;
129: m_callerClassInfo = callerClassInfo;
130: m_callerClassName = callerClassName;
131: m_callerMethodName = callerMethodName;
132: m_callerMethodDesc = callerMethodDesc;
133:
134: if (CLINIT_METHOD_NAME.equals(m_callerMethodName)) {
135: m_callerMemberInfo = m_callerClassInfo
136: .staticInitializer();
137: } else if (INIT_METHOD_NAME.equals(m_callerMethodName)) {
138: int hash = AsmHelper
139: .calculateConstructorHash(m_callerMethodDesc);
140: m_callerMemberInfo = m_callerClassInfo
141: .getConstructor(hash);
142: } else {
143: int hash = AsmHelper.calculateMethodHash(
144: m_callerMethodName, m_callerMethodDesc);
145: m_callerMemberInfo = m_callerClassInfo.getMethod(hash);
146: }
147: if (m_callerMemberInfo == null) {
148: System.err
149: .println("AW::WARNING "
150: + "metadata structure could not be build for method ["
151: + m_callerClassInfo.getName().replace(
152: '/', '.') + '.'
153: + m_callerMethodName + ':'
154: + m_callerMethodDesc + ']');
155: }
156: }
157:
158: /**
159: * Label
160: *
161: * @param label
162: */
163: public void visitLabel(Label label) {
164: m_lastLabelForLineNumber = label;
165: super .visitLabel(label);
166: }
167:
168: /**
169: * Visits PUTFIELD and GETFIELD instructions.
170: *
171: * @param opcode
172: * @param className
173: * @param fieldName
174: * @param fieldDesc
175: */
176: public void visitFieldInsn(final int opcode,
177: final String className, final String fieldName,
178: final String fieldDesc) {
179:
180: if (className
181: .endsWith(AbstractJoinPointCompiler.JOIN_POINT_CLASS_SUFFIX)
182: || fieldName.startsWith(ASPECTWERKZ_PREFIX)
183: || fieldName.startsWith(SYNTHETIC_MEMBER_PREFIX) || // synthetic field
184: fieldName.equals(SERIAL_VERSION_UID_FIELD_NAME) // can have been added by the weaver (not safe)
185: ) {
186: super .visitFieldInsn(opcode, className, fieldName,
187: fieldDesc);
188: return;
189: }
190:
191: // if within ctor, make sure object initialization has been reached
192: if (!m_isObjectInitialized) {
193: super .visitFieldInsn(opcode, className, fieldName,
194: fieldDesc);
195: return;
196: }
197:
198: final Type fieldType = Type.getType(fieldDesc);
199: final int joinPointHash = AsmHelper.calculateFieldHash(
200: fieldName, fieldDesc);
201: final ClassInfo classInfo = AsmClassInfo.getClassInfo(
202: className, m_loader);
203: final FieldInfo fieldInfo = getFieldInfo(classInfo,
204: className, fieldName, fieldDesc, joinPointHash);
205:
206: if (opcode == PUTFIELD || opcode == PUTSTATIC) {
207: handleFieldModification(fieldInfo, opcode, className,
208: fieldName, fieldDesc, joinPointHash);
209: } else if (opcode == GETFIELD || opcode == GETSTATIC) {
210: handleFieldAccess(fieldInfo, opcode, className,
211: fieldName, fieldDesc, joinPointHash, fieldType);
212: } else {
213: super .visitFieldInsn(opcode, className, fieldName,
214: fieldDesc);
215: }
216: }
217:
218: /**
219: * Handles field access.
220: *
221: * @param fieldInfo
222: * @param opcode
223: * @param className
224: * @param fieldName
225: * @param fieldDesc
226: * @param joinPointHash
227: * @param fieldType
228: */
229: private void handleFieldAccess(final FieldInfo fieldInfo,
230: final int opcode, final String className,
231: final String fieldName, final String fieldDesc,
232: int joinPointHash, final Type fieldType) {
233: if (m_callerMemberInfo == null) {
234: super .visitFieldInsn(opcode, className, fieldName,
235: fieldDesc);
236: return;
237: }
238:
239: ExpressionContext ctx = new ExpressionContext(
240: PointcutType.GET, fieldInfo, m_callerMemberInfo);
241:
242: if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
243: super .visitFieldInsn(opcode, className, fieldName,
244: fieldDesc);
245: } else {
246: m_ctx.markAsAdvised();
247:
248: String joinPointClassName = TransformationUtil
249: .getJoinPointClassName(m_callerClassName,
250: m_callerMethodName, m_callerMethodDesc,
251: className, JoinPointType.FIELD_GET_INT,
252: joinPointHash);
253:
254: // no param to field, so pass a default value to the invoke method
255: AsmHelper.addDefaultValue(this , fieldType);
256:
257: // if static context load NULL else 'this'
258: if (Modifier
259: .isStatic(m_callerMemberInfo.getModifiers())) {
260: visitInsn(ACONST_NULL);
261: } else {
262: visitVarInsn(ALOAD, 0);
263: }
264:
265: // add the call to the join point
266: super .visitMethodInsn(INVOKESTATIC, joinPointClassName,
267: INVOKE_METHOD_NAME, TransformationUtil
268: .getInvokeSignatureForFieldJoinPoints(
269: fieldInfo.getModifiers(),
270: fieldDesc, m_callerClassName,
271: className));
272:
273: // emit the joinpoint
274: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
275: JoinPointType.FIELD_GET_INT, m_callerClassName,
276: m_callerMethodName, m_callerMethodDesc,
277: m_callerMemberInfo.getModifiers(), className,
278: fieldName, fieldDesc, fieldInfo.getModifiers(),
279: joinPointHash, joinPointClassName,
280: m_lastLabelForLineNumber));
281: }
282: }
283:
284: /**
285: * Handles field modification.
286: *
287: * @param fieldInfo
288: * @param opcode
289: * @param className
290: * @param fieldName
291: * @param fieldDesc
292: * @param joinPointHash
293: */
294: private void handleFieldModification(final FieldInfo fieldInfo,
295: final int opcode, final String className,
296: final String fieldName, final String fieldDesc,
297: final int joinPointHash) {
298: if (m_callerMemberInfo == null) {
299: super .visitFieldInsn(opcode, className, fieldName,
300: fieldDesc);
301: return;
302: }
303:
304: ExpressionContext ctx = new ExpressionContext(
305: PointcutType.SET, fieldInfo, m_callerMemberInfo);
306:
307: if (fieldFilter(m_ctx.getDefinitions(), ctx, fieldInfo)) {
308: super .visitFieldInsn(opcode, className, fieldName,
309: fieldDesc);
310: } else {
311: m_ctx.markAsAdvised();
312:
313: String joinPointClassName = TransformationUtil
314: .getJoinPointClassName(m_callerClassName,
315: m_callerMethodName, m_callerMethodDesc,
316: className, JoinPointType.FIELD_SET_INT,
317: joinPointHash);
318:
319: // load the caller instance (this), or null if in a static context
320: // note that callee instance [optional] and args are already on the stack
321: if (Modifier
322: .isStatic(m_callerMemberInfo.getModifiers())) {
323: visitInsn(ACONST_NULL);
324: } else {
325: visitVarInsn(ALOAD, 0);
326: }
327:
328: // add the call to the join point
329: super .visitMethodInsn(INVOKESTATIC, joinPointClassName,
330: INVOKE_METHOD_NAME, TransformationUtil
331: .getInvokeSignatureForFieldJoinPoints(
332: fieldInfo.getModifiers(),
333: fieldDesc, m_callerClassName,
334: className));
335:
336: final int sort = Type.getType(fieldDesc).getSort();
337: if (sort != Type.LONG && sort != Type.DOUBLE) {
338: super .visitInsn(POP);
339: } else {
340: //AW-437
341: super .visitInsn(POP2);
342: }
343:
344: // emit the joinpoint
345: m_ctx.addEmittedJoinPoint(new EmittedJoinPoint(
346: JoinPointType.FIELD_SET_INT, m_callerClassName,
347: m_callerMethodName, m_callerMethodDesc,
348: m_callerMemberInfo.getModifiers(), className,
349: fieldName, fieldDesc, fieldInfo.getModifiers(),
350: joinPointHash, joinPointClassName,
351: m_lastLabelForLineNumber));
352: }
353: }
354:
355: /**
356: * Returns the field info.
357: *
358: * @param classInfo
359: * @param className
360: * @param fieldName
361: * @param fieldDesc
362: * @param joinPointHash
363: * @return
364: */
365: private FieldInfo getFieldInfo(final ClassInfo classInfo,
366: final String className, final String fieldName,
367: final String fieldDesc, final int joinPointHash) {
368: FieldInfo fieldInfo = classInfo.getField(joinPointHash);
369: if (fieldInfo == null) {
370: throw new Error(
371: "field info metadata structure could not be build for field: "
372: + className + '.' + fieldName + ':'
373: + fieldDesc);
374: }
375: return fieldInfo;
376: }
377:
378: /**
379: * Filters out the fields that are not eligible for transformation.
380: *
381: * @param definitions
382: * @param ctx
383: * @param fieldInfo
384: * @return boolean true if the field should be filtered out
385: */
386: public boolean fieldFilter(final Set definitions,
387: final ExpressionContext ctx, final FieldInfo fieldInfo) {
388: if (fieldInfo.getName().startsWith(ORIGINAL_METHOD_PREFIX)) {
389: return true;
390: }
391: for (Iterator it = definitions.iterator(); it.hasNext();) {
392: if (((SystemDefinition) it.next()).hasPointcut(ctx)) {
393: return false;
394: } else {
395: continue;
396: }
397: }
398: return true;
399: }
400: }
401: }
|