001    package org.shiftone.jrat.inject.bytecode.asm;
002    
003    
004    import org.objectweb.asm.Label;
005    import org.objectweb.asm.MethodVisitor;
006    import org.objectweb.asm.Opcodes;
007    import org.objectweb.asm.Type;
008    import org.objectweb.asm.commons.GeneratorAdapter;
009    import org.objectweb.asm.commons.Method;
010    import org.shiftone.jrat.inject.bytecode.Modifier;
011    import org.shiftone.jrat.util.log.Logger;
012    
013    
014    /**
015     * @author jeff@shiftone.org (Jeff Drost)
016     */
017    public class ProxyMethodVisitor extends GeneratorAdapter implements Constants, Opcodes {
018    
019        private static final Logger LOG = Logger.getLogger(ProxyMethodVisitor.class);
020        private boolean isStatic;
021        private boolean isVoidReturn;
022        private Type classType;
023        private String handlerFieldName;
024        private String targetMethodName;
025        private Method method;
026    
027        public ProxyMethodVisitor(int access, Method method, MethodVisitor mv, Type classType, String targetMethodName,
028                                  String handlerFieldName) {
029    
030            super(access, method, mv);
031    
032            this.method = method;
033            this.isStatic = Modifier.isStatic(access);
034            this.isVoidReturn = Type.VOID_TYPE.equals(method.getReturnType());
035            this.classType = classType;
036            this.targetMethodName = targetMethodName;
037            this.handlerFieldName = handlerFieldName;
038        }
039    
040    
041        public void visitCode() {
042    
043            Label tryLabel = newLabel();
044    
045            // -------------------------------------------------------------------------------
046            // HANDLER.onMethodStart(this);
047            getStatic(classType, handlerFieldName, Constants.MethodHandler.TYPE);
048            pushThis();
049            invokeInterface(MethodHandler.TYPE, MethodHandler.onMethodStart);
050            mark(tryLabel);
051    
052            // -------------------------------------------------------------------------------
053            // long startTime = System.currentTimeNanos();
054            int startTime = newLocal(Type.LONG_TYPE);
055    
056            invokeStatic(Clock.TYPE, Clock.currentTimeNanos);
057            storeLocal(startTime, Type.LONG_TYPE);
058    
059            // -------------------------------------------------------------------------------
060            // local var result is defined only if there is a non-void return type
061            // Object result = method(args)
062            Label tryStart = mark();    // try {
063    
064            if (isStatic) {
065                loadArgs();    // push the args on the stack
066                invokeStatic(classType, new Method(targetMethodName, method.getDescriptor()));
067            } else {
068                loadThis();    // push this on the stack (for non-static methods)
069                loadArgs();    // push the args on the stack
070                invokeVirtual(classType, new Method(targetMethodName, method.getDescriptor()));
071            }
072    
073            int result = -1;
074    
075            if (!isVoidReturn) {
076                result = newLocal(method.getReturnType());
077    
078                storeLocal(result);
079            }
080    
081            // -------------------------------------------------------------------------------
082            // HANDLER.onMethodFinish(this, System.currentTimeNanos - start, null);
083            getStatic(classType, handlerFieldName, MethodHandler.TYPE);    // get the
084    
085            // MethodHandler
086            pushThis();                                                    // param 1
087            invokeStatic(Clock.TYPE, Clock.currentTimeNanos);              // param 2 : obtain
088    
089            // end time
090            // (Clock.currentTimeNanos)
091            loadLocal(startTime);                                          // param 2 : getPreferences the start time onto the stack
092            math(GeneratorAdapter.SUB, Type.LONG_TYPE);                    // param 2 : subtract,
093    
094            // leaving the result on the
095            // stack
096            visitInsn(ACONST_NULL);                                        // param 2 : null (no exception)
097            invokeInterface(MethodHandler.TYPE, MethodHandler.onMethodFinish);
098    
099            // -------------------------------------------------------------------------------
100            // return result;
101            if (!isVoidReturn) {
102                loadLocal(result);
103            }
104    
105            returnValue();
106    
107            Label tryEnd = mark();    // } catch (Throwable e) {
108    
109            // this is the beginning of the catch block
110            catchException(tryStart, tryEnd, Throwable.TYPE);
111    
112            // -------------------------------------------------------------------------------
113            // Throwable exception = e;
114            int exception = newLocal(Throwable.TYPE);
115    
116            storeLocal(exception);
117    
118            // -------------------------------------------------------------------------------
119            // HANDLER.onMethodFinish(this, System.currentTimeNanos - start,
120            // exception);
121            getStatic(classType, handlerFieldName, MethodHandler.TYPE);
122            pushThis();                                          // param 1
123            invokeStatic(Clock.TYPE, Clock.currentTimeNanos);    // param 2 : obtain
124    
125            // end time
126            // (Clock.currentTimeNanos)
127            loadLocal(startTime);                                // param 2 : getPreferences the start time back onto the
128    
129            // stack
130            math(GeneratorAdapter.SUB, Type.LONG_TYPE);          // param 2 : subtract,
131    
132            // leaving the result on the
133            // stack
134            loadLocal(exception);                                // param 3 : getPreferences the exception
135            invokeInterface(MethodHandler.TYPE, MethodHandler.onMethodFinish);
136            loadLocal(exception);
137            throwException();
138    
139            // -------------------------------------------------------------------------------
140            endMethod();
141        }
142    
143    
144        private void pushThis() {
145    
146            if (isStatic) {
147                push("test");
148            } else {
149                loadThis();
150            }
151        }
152    }