public Object visit(SubstitutionStatement node) {

            // Inject pre-call profiling bytecode.
            TypeDesc methodObserverType =
                TypeDesc.forClass(MergedClass.InvocationEventObserver.class);

            boolean profilingEnabled = isProfilingEnabled();

            if (profilingEnabled) {
                if (mSubTime == null)
                    mSubTime = mBuilder.createLocalVariable("subTime",
                        TypeDesc.forClass(long.class));
                mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                    "getInvocationObserver", methodObserverType);
                mBuilder.invokeInterface(methodObserverType.getFullName(), "currentTime",
                   TypeDesc.forClass(long.class));
                mBuilder.storeLocal(mSubTime);
            }


            Class<?> subClass = Substitution.class;
            Method subMethod;
            try {
                // Always pass context into substitution in order for this
                // template's own substitutions to be safely detached.
                subMethod = subClass.getMethod
                    ("substitute", new Class[]{Context.class});
            }
            catch (NoSuchMethodException e) {
                throw new RuntimeException(e.toString());
            }

            generate(mSubParam);
            generateContext();
            mBuilder.invoke(subMethod);


            // Inject post-call profiling bytecode.
            if (profilingEnabled) {
                mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                    "getInvocationObserver", methodObserverType);
                mBuilder.loadConstant(mUnit.getName());
                mBuilder.loadConstant("__substitution");
                mBuilder.invokeStatic(mContextParam.getVariable().getType().getObjectClass().getName(),
                    "getInvocationObserver", methodObserverType);
                mBuilder.invokeInterface(methodObserverType.getFullName(), "currentTime",
                    TypeDesc.forClass(long.class));
                mBuilder.loadLocal(mSubTime);
                mBuilder.math(Opcode.LSUB);
                mBuilder.invokeInterface(methodObserverType.getFullName(), "invokedEvent", null,
                    new TypeDesc[] { TypeDesc.forClass(String.class), TypeDesc.forClass(String.class),
                        TypeDesc.forClass(long.class) });
            }

            return null;
        }