com.offbynull.coroutines.instrumenter.MethodInstrumenter.java Source code

Java tutorial

Introduction

Here is the source code for com.offbynull.coroutines.instrumenter.MethodInstrumenter.java

Source

/*
 * Copyright (c) 2016, Kasra Faghihi, All rights reserved.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3.0 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.
 */
package com.offbynull.coroutines.instrumenter;

import static com.offbynull.coroutines.instrumenter.ContinuationGenerators.entryPointLoader;
import static com.offbynull.coroutines.instrumenter.ContinuationGenerators.saveState;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodNode;
import static com.offbynull.coroutines.instrumenter.SynchronizationGenerators.enterMonitorAndStore;
import static com.offbynull.coroutines.instrumenter.SynchronizationGenerators.exitMonitorAndDelete;
import com.offbynull.coroutines.instrumenter.generators.DebugGenerators.MarkerType;
import org.apache.commons.lang3.Validate;

final class MethodInstrumenter {

    public void instrument(MethodNode methodNode, MethodAttributes attrs) {
        Validate.notNull(methodNode);
        Validate.notNull(attrs);

        // These sanity checks need to exist. The methodNode.instructions.insertBefore/remove methods don't actually check to make sure the
        // instructions they're operating on belong to the method. This is here to make sure that the properties and methodNode match.
        attrs.getContinuationPoints().stream().map(x -> x.getInvokeInstruction())
                .forEach(x -> methodNode.instructions.contains(x));
        attrs.getSynchronizationPoints().stream().map(x -> x.getMonitorInstruction())
                .forEach(x -> methodNode.instructions.contains(x));

        // Add trycatch nodes
        attrs.getContinuationPoints().stream().filter(x -> x instanceof TryCatchInvokeContinuationPoint)
                .map(x -> (TryCatchInvokeContinuationPoint) x).map(x -> x.getTryCatchBlock())
                .forEach(x -> methodNode.tryCatchBlocks.add(0, x));

        // Add loading code (this includes continuation restore points)
        InsnList entryPoint = entryPointLoader(attrs);
        methodNode.instructions.insert(entryPoint);

        // Add continuation save points
        List<ContinuationPoint> continuationPoints = attrs.getContinuationPoints();
        for (int i = 0; i < continuationPoints.size(); i++) {
            ContinuationPoint cp = continuationPoints.get(i);

            AbstractInsnNode nodeToReplace = cp.getInvokeInstruction();
            InsnList insnsToReplaceWith = saveState(attrs, i);

            methodNode.instructions.insertBefore(nodeToReplace, insnsToReplaceWith);
            methodNode.instructions.remove(nodeToReplace);
        }

        // Add synchronization save points
        List<SynchronizationPoint> synchPoints = attrs.getSynchronizationPoints();
        MarkerType markerType = attrs.getSettings().getMarkerType();
        LockVariables lockVars = attrs.getLockVariables();
        for (int i = 0; i < synchPoints.size(); i++) {
            SynchronizationPoint sp = synchPoints.get(i);

            InsnNode nodeToReplace = sp.getMonitorInstruction();
            InsnList insnsToReplaceWith;
            switch (nodeToReplace.getOpcode()) {
            case Opcodes.MONITORENTER:
                insnsToReplaceWith = enterMonitorAndStore(markerType, lockVars);
                break;
            case Opcodes.MONITOREXIT:
                insnsToReplaceWith = exitMonitorAndDelete(markerType, lockVars);
                break;
            default:
                throw new IllegalStateException(); //should never happen
            }

            methodNode.instructions.insertBefore(nodeToReplace, insnsToReplaceWith);
            methodNode.instructions.remove(nodeToReplace);
        }
    }
}