All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.flink.api.java.sca.ModifiedASMAnalyzer Maven / Gradle / Ivy

There is a newer version: 1.20.0
Show newest version
/*
 * 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;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy