org.apache.flink.api.java.sca.ModifiedASMAnalyzer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.flink.api.java.sca.ModifiedASMAnalyzer.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.flink.api.java.sca;

import org.apache.flink.annotation.Internal;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.analysis.Analyzer;
import org.objectweb.asm.tree.analysis.Frame;
import org.objectweb.asm.tree.analysis.Interpreter;

import java.lang.reflect.Field;

/**
 * Modified version of ASMs Analyzer. It defines a custom ASM Frame
 * and allows jump modification which is necessary for UDFs with
 * one iterable input e.g. GroupReduce.
 * (see also UdfAnalyzer's field "iteratorTrueAssumptionApplied")
 */
@Internal
public class ModifiedASMAnalyzer extends Analyzer {

    private NestedMethodAnalyzer interpreter;

    public ModifiedASMAnalyzer(Interpreter interpreter) {
        super(interpreter);
        this.interpreter = (NestedMethodAnalyzer) interpreter;
    }

    protected Frame newFrame(int nLocals, int nStack) {
        return new ModifiedASMFrame(nLocals, nStack);
    }

    protected Frame newFrame(Frame src) {
        return new ModifiedASMFrame(src);
    }

    // type of jump modification
    private int jumpModification = NO_MOD;
    private static final int NO_MOD = -1;
    private static final int IFEQ_MOD = 0;
    private static final int IFNE_MOD = 1;
    private int eventInsn;

    // current state of modification
    private int jumpModificationState = DO_NOTHING;
    private static final int DO_NOTHING = -1;
    private static final int PRE_STATE = 0;
    private static final int MOD_STATE = 1;
    private static final int WAIT_FOR_INSN_STATE = 2;

    public void requestIFEQLoopModification() {
        if (jumpModificationState != DO_NOTHING) {
            throw new CodeAnalyzerException("Unable to do jump modifications (unsupported nested jumping).");
        }
        jumpModification = IFEQ_MOD;
        jumpModificationState = PRE_STATE;
    }

    public void requestIFNELoopModification() {
        if (jumpModificationState != DO_NOTHING) {
            throw new CodeAnalyzerException("Unable to do jump modifications (unsupported nested jumping).");
        }
        jumpModification = IFNE_MOD;
        jumpModificationState = PRE_STATE;
    }

    @Override
    protected void newControlFlowEdge(int insn, int successor) {
        try {
            if (jumpModificationState == PRE_STATE) {
                jumpModificationState = MOD_STATE;
            } else if (jumpModificationState == MOD_STATE) {
                // this modification swaps the top 2 values on the queue stack
                // it ensures that the "TRUE" path will be executed first, which is important
                // for a later merge
                if (jumpModification == IFEQ_MOD) {
                    final int top = accessField(Analyzer.class, "top").getInt(this);
                    final int[] queue = (int[]) accessField(Analyzer.class, "queue").get(this);

                    final int tmp = queue[top - 2];
                    queue[top - 2] = queue[top - 1];
                    queue[top - 1] = tmp;

                    eventInsn = queue[top - 2] - 1;
                    final InsnList insns = (InsnList) accessField(Analyzer.class, "insns").get(this);
                    // check if instruction is a goto instruction
                    // if yes this is loop structure
                    if (insns.get(eventInsn) instanceof JumpInsnNode) {
                        jumpModificationState = WAIT_FOR_INSN_STATE;
                    }
                    // no loop -> end of modification
                    else {
                        jumpModificationState = DO_NOTHING;
                    }
                }
                // this modification changes the merge priority of certain frames (the expression part of the IF)
                else if (jumpModification == IFNE_MOD) {
                    final Frame[] frames = (Frame[]) accessField(Analyzer.class, "frames").get(this);
                    final Field indexField = accessField(AbstractInsnNode.class, "index");

                    final InsnList insns = (InsnList) accessField(Analyzer.class, "insns").get(this);
                    final AbstractInsnNode gotoInsnn = insns.get(successor - 1);
                    // check for a loop
                    if (gotoInsnn instanceof JumpInsnNode) {
                        jumpModificationState = WAIT_FOR_INSN_STATE;
                        // sets a merge priority for all instructions (the expression of the IF)
                        // from the label the goto instruction points to until the evaluation with IFEQ
                        final int idx = indexField.getInt(accessField(JumpInsnNode.class, "label").get(gotoInsnn));

                        for (int i = idx; i <= insn; i++) {
                            ((ModifiedASMFrame) frames[i]).mergePriority = true;
                        }
                        eventInsn = idx - 2;
                    } else {
                        jumpModificationState = DO_NOTHING;
                    }
                }
            }
            // wait for the goto instruction
            else if (jumpModificationState == WAIT_FOR_INSN_STATE && insn == eventInsn) {
                jumpModificationState = DO_NOTHING;
                final Frame[] frames = (Frame[]) accessField(Analyzer.class, "frames").get(this);
                // merge the goto instruction frame with the frame that follows
                // this ensures that local variables are kept
                if (jumpModification == IFEQ_MOD) {
                    interpreter.rightMergePriority = true;
                    final Field top = accessField(Frame.class, "top");
                    top.setInt(frames[eventInsn], top.getInt(frames[eventInsn + 1]));
                    frames[eventInsn + 1].merge(frames[eventInsn], interpreter);
                }
                // finally set a merge priority for the last instruction of the loop (before the IF expression)
                else if (jumpModification == IFNE_MOD) {
                    ((ModifiedASMFrame) frames[eventInsn + 1]).mergePriority = true;
                }
            }
        } catch (Exception e) {
            throw new CodeAnalyzerException("Unable to do jump modifications.", e);
        }
    }

    private Field accessField(Class<?> clazz, String name) {
        for (Field f : clazz.getDeclaredFields()) {
            if (f.getName().equals(name)) {
                f.setAccessible(true);
                return f;
            }
        }
        return null;
    }
}