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

edu.columbia.cs.psl.phosphor.instrumenter.PrimitiveArrayAnalyzer Maven / Gradle / Ivy

The newest version!
package edu.columbia.cs.psl.phosphor.instrumenter;

import edu.columbia.cs.psl.phosphor.Configuration;
import edu.columbia.cs.psl.phosphor.TaintUtils;
import edu.columbia.cs.psl.phosphor.instrumenter.analyzer.BasicArrayInterpreter;
import edu.columbia.cs.psl.phosphor.instrumenter.analyzer.NeverNullArgAnalyzerAdapter;
import edu.columbia.cs.psl.phosphor.struct.Field;
import edu.columbia.cs.psl.phosphor.struct.SinglyLinkedList;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.*;
import org.objectweb.asm.tree.analysis.*;

import java.util.*;
import java.util.Map.Entry;

public class PrimitiveArrayAnalyzer extends MethodVisitor {
	public boolean isEmptyMethod = true;

	final class PrimitiveArrayAnalyzerMN extends MethodNode {
		private final String className;
		private final boolean shouldTrackExceptions;
		private final MethodVisitor cmv;
		boolean[] endsWithGOTO;
		int curLabel = 0;
		HashMap lvsThatAreArrays = new HashMap();
		ArrayList inFrames = new ArrayList();
		ArrayList outFrames = new ArrayList();

		public PrimitiveArrayAnalyzerMN(int access, String name, String desc, String signature, String[] exceptions, String className, MethodVisitor cmv) {
			super(Configuration.ASM_VERSION,access, name, desc, signature, exceptions);
			this.className = className;
			this.cmv = cmv;
			if(Configuration.IMPLICIT_EXCEPTION_FLOW)
				shouldTrackExceptions = true;
			else
				shouldTrackExceptions = false;
		}

		@Override
		protected LabelNode getLabelNode(Label l) {
			if(!Configuration.READ_AND_SAVE_BCI)
				return super.getLabelNode(l);
			if (!(l.info instanceof LabelNode)) {
				l.info = new LabelNode(l);
			}
			return (LabelNode) l.info;
		}

		@Override
		public void visitCode() {
			if (DEBUG)
				System.out.println("Visiting: " + className + "." + name + desc);
			Label firstLabel = new Label();
			super.visitCode();
			visitLabel(firstLabel);

		}

		//			@Override
		//			public void visitVarInsn(int opcode, int var) {
		//				if(opcode == Opcodes.ASTORE)
		//				{
		//					boolean isPrimArray = TaintAdapter.isPrimitiveStackType(analyzer.stack.get(analyzer.stack.size() - 1));
		//					if(lvsThatAreArrays.containsKey(var))
		//					{
		//						if(lvsThatAreArrays.get(var) != isPrimArray)
		//						{
		//							throw new IllegalStateException("This analysis is currently too lazy to handle when you have 1 var slot take different kinds of arrays");
		//						}
		//					}
		//					lvsThatAreArrays.put(var, isPrimArray);
		//				}
		//				super.visitVarInsn(opcode, var);
		//			}
		 private  void visitFrameTypes(final int n, final Object[] types,
		            final List result) {
		        for (int i = 0; i < n; ++i) {
		            Object type = types[i];
		            result.add(type);
		            if (type == Opcodes.LONG || type == Opcodes.DOUBLE) {
		                result.add(Opcodes.TOP);
		            }
		        }
		    }

		FrameNode generateFrameNode(int type, int nLocal, Object[] local, int nStack, Object[] stack)
		{
			FrameNode ret = new FrameNode(type, nLocal, local, nStack, stack);
			ret.local = new ArrayList();
			ret.stack= new ArrayList();
			visitFrameTypes(nLocal, local, ret.local);
			visitFrameTypes(nStack, stack, ret.stack);
			return ret;
		}

		@Override
		public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
			if (DEBUG)
				System.out.println("Visitframe curlabel " + (curLabel - 1));
			super.visitFrame(type, nLocal, local, nStack, stack);
			if (DEBUG)
				System.out.println("label " + (curLabel - 1) + " reset to " + Arrays.toString(stack));
			if (inFrames.size() == curLabel - 1)
				inFrames.add(generateFrameNode(type, nLocal, local, nStack, stack));
			else
				inFrames.set(curLabel - 1, generateFrameNode(type, nLocal, local, nStack, stack));
//							System.out.println(name+" " +Arrays.toString(local));
			//				if (curLabel > 0) {
			//				System.out.println("And resetting outframe " + (curLabel - 2));
			//					if (outFrames.size() == curLabel - 1)
			//						outFrames.add(new FrameNode(type, nLocal, local, nStack, stack));
			//					 if(outFrames.get(curLabel -1) == null)
			//						outFrames.set(curLabel - 1, new FrameNode(type, nLocal, local, nStack, stack));
			//				}
		}

		@Override
		public void visitLabel(Label label) {
			//				if (curLabel >= 0)
			if (DEBUG)
				System.out.println("Visit label: " + curLabel + " analyzer: " + analyzer.stack + " inframes size " + inFrames.size() + " " + outFrames.size());
			if (analyzer.locals == null || analyzer.stack == null)
				inFrames.add(new FrameNode(0, 0, new Object[0], 0, new Object[0]));
			else
				inFrames.add(new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), analyzer.stack.size(), analyzer.stack.toArray()));
			//				if (outFrames.size() <= curLabel) {
			//					if(analyzer.stack == null)
			outFrames.add(null);
			if (curLabel > 0 && outFrames.get(curLabel - 1) == null && analyzer.stack != null)
				outFrames.set(curLabel - 1, new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), analyzer.stack.size(), analyzer.stack.toArray()));
			if (DEBUG)
				System.out.println("Added outframe for " + (outFrames.size() - 1) + " : " + analyzer.stack);
			//				}

			super.visitLabel(label);
			curLabel++;
		}

		@Override
		public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
			if (DEBUG)
				System.out.println("Rewriting " + curLabel + " OUT to " + analyzer.stack);
			outFrames.set(curLabel - 1, new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), analyzer.stack.size(), analyzer.stack.toArray()));
			super.visitTableSwitchInsn(min, max, dflt, labels);
		}

		@Override
		public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
			if (DEBUG)
				System.out.println("Rewriting " + curLabel + " OUT to " + analyzer.stack);
			outFrames.set(curLabel - 1, new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), analyzer.stack.size(), analyzer.stack.toArray()));
			super.visitLookupSwitchInsn(dflt, keys, labels);
		}

		@Override
		public void visitInsn(int opcode) {
			if (opcode == Opcodes.ATHROW) {
				if (DEBUG)
					System.out.println("Rewriting " + curLabel + " OUT to " + analyzer.stack);
				if (analyzer.locals != null && analyzer.stack != null)
					outFrames.set(curLabel - 1, new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), analyzer.stack.size(), analyzer.stack.toArray()));
			}
			super.visitInsn(opcode);
		}

		public void visitJumpInsn(int opcode, Label label) {
			//				System.out.println(opcode);
			//				if (opcode == Opcodes.GOTO) {
			super.visitJumpInsn(opcode, label);
			int nToPop = 0;
			switch (opcode) {
			case Opcodes.IFEQ:
			case Opcodes.IFNE:
			case Opcodes.IFLT:
			case Opcodes.IFGE:
			case Opcodes.IFGT:
			case Opcodes.IFLE:
			case Opcodes.IFNULL:
			case Opcodes.IFNONNULL:
				//pop 1
				nToPop = 1;
				break;
			case Opcodes.IF_ICMPEQ:
			case Opcodes.IF_ICMPNE:
			case Opcodes.IF_ICMPLT:
			case Opcodes.IF_ICMPGE:
			case Opcodes.IF_ICMPGT:
			case Opcodes.IF_ICMPLE:
			case Opcodes.IF_ACMPEQ:
			case Opcodes.IF_ACMPNE:
				//pop 2
				nToPop = 2;
				break;
			case Opcodes.GOTO:
				//pop none
				break;
			default:
				throw new IllegalArgumentException();
			}
			//The analyzer won't have executed yet, so simulate it did :'(
			List stack = new ArrayList(analyzer.stack);
			//				System.out.println("got to remove " + nToPop +  " from " + analyzer.stack + " in " + className + "."+name );
			while (nToPop > 0 && !stack.isEmpty()) {
				stack.remove(stack.size() - 1);
				nToPop--;
			}

			if (DEBUG)
				System.out.println(name + " Rewriting " + curLabel + " OUT to " + stack);
			outFrames.set(curLabel - 1, new FrameNode(0, analyzer.locals.size(), analyzer.locals.toArray(), stack.size(), stack.toArray()));
			visitLabel(new Label());
			//				}

		}

		@Override
		public void visitEnd() {
			final HashMap> neverAutoBoxByFrame = new HashMap>();
			final HashMap> alwaysAutoBoxByFrame = new HashMap>();
			final HashMap> outEdges = new HashMap>();
			final HashSet insertACHECKCASTBEFORE = new HashSet();
			final HashSet insertACONSTNULLBEFORE = new HashSet();
			@SuppressWarnings("unchecked")
			Analyzer a = new Analyzer(new BasicArrayInterpreter((this.access & Opcodes.ACC_STATIC) != 0, isImplicitLightTracking)) {
			    protected int[] insnToLabel;

				int getLabel(int insn) {
					int label = -1;
					for (int j = 0; j <= insn; j++) {
						label = insnToLabel[j];
					}
					return label;
				}

				int getInsnAfterFrameFor(int insn) {
					int r = 0;
					for (int i = 0; i < insn; i++) {
						if (instructions.get(i).getType() == AbstractInsnNode.FRAME)
							r = i + 1;
					}
					return r;
				}

				int getLastInsnByLabel(int label) {
					int r = 0;
					for (int j = 0; j < insnToLabel.length; j++) {
						if (insnToLabel[j] == label) {
							if (instructions.get(j).getType() == AbstractInsnNode.FRAME)
								continue;
							r = j;
						}
					}
					return r;
				}

				int getFirstInsnByLabel(int label) {
					for (int j = 0; j < insnToLabel.length; j++) {
						if (insnToLabel[j] == label) {
							if (instructions.get(j).getType() == AbstractInsnNode.FRAME || instructions.get(j).getType() == AbstractInsnNode.LABEL
									|| instructions.get(j).getType() == AbstractInsnNode.LINE)
								continue;
							return j;
						}
					}
					return -1;
				}

				protected Frame newFrame(int nLocals, int nStack) {
					return new Frame(nLocals, nStack){
						@Override
						public void execute(AbstractInsnNode insn, Interpreter interpreter) throws AnalyzerException {
							if(insn.getOpcode() > 200)
								return;
							super.execute(insn, interpreter);
						}
					};
				}
				@Override
				public Frame[] analyze(String owner, MethodNode m) throws AnalyzerException {
					Iterator insns = m.instructions.iterator();
					insnToLabel = new int[m.instructions.size()];
//											System.out.println("PAAA"+ name);
					int label = -1;
					boolean isFirst = true;
					while (insns.hasNext()) {
						AbstractInsnNode insn = insns.next();
						int idx = m.instructions.indexOf(insn);

						if (insn instanceof LabelNode) {
							label++;
						}

						insnToLabel[idx] = (isFirst ? 1 : label);
						isFirst = false;
						//														System.out.println(idx + "->"+label);
					}
					Frame[] ret = super.analyze(owner, m);
					if (shouldTrackExceptions) {
						insns = m.instructions.iterator();
						while (insns.hasNext()) {
							AbstractInsnNode insn = insns.next();
							int idx = m.instructions.indexOf(insn);
							if (insn.getOpcode() == Opcodes.ATHROW) {
								//Are we in a try/catch block that can catch this?


								for(TryCatchBlockNode each : m.tryCatchBlocks){
									try{
										Class caught = Class.forName((each.type == null ? "java.lang.Throwable" : each.type.replace('/','.')),false,PrimitiveArrayAnalyzer.class.getClassLoader());
										if(caught == Throwable.class){
											//if catching Throwable, we'll catch this regardless of whether we can load the exception type or not
											newControlFlowEdge(idx,m.instructions.indexOf(each.handler));
											break;
										}
										Class thrown = null;
										try{
											String onStack = ret[idx].getStack(ret[idx].getStackSize()-1).toString();
											if(!onStack.startsWith("L"))
												continue;
											thrown = Class.forName(Type.getType(ret[idx].getStack(0).toString()).getClassName(),false, PrimitiveArrayAnalyzer.class.getClassLoader());
										}catch (Throwable t){
											continue;
										}
										if(caught.isAssignableFrom(thrown))
										{
											//Found a handler for this thrown
											newControlFlowEdge(idx,m.instructions.indexOf(each.handler));
											break;
										}
									}catch(Throwable t){} //Maybe can't load exception type, that's ok
								}
							}
						}
					}

//					if (DEBUG)
//						for (int i = 0; i < inFrames.size(); i++) {
//							System.out.println("IN: " + i + " " + inFrames.get(i).stack);
//						}
//					if (DEBUG)
//						for (int i = 0; i < outFrames.size(); i++) {
//							System.out.println("OUT: " + i + " " + (outFrames.get(i) == null ? "null" : outFrames.get(i).stack));
//						}

					for (Entry> edge : edges.entrySet()) {
					    Integer successor = edge.getKey();
						if (edge.getValue().size() > 1) {
							int labelToSuccessor = getLabel(successor);

							if (DEBUG)
								System.out.println(name + " Must merge: " + edge.getValue() + " into " + successor + " AKA " + labelToSuccessor);
							if (DEBUG)
								System.out.println("Input to successor: " + inFrames.get(labelToSuccessor).stack);

							for (Integer toMerge : edge.getValue()) {
								if(shouldTrackExceptions)
								{
									BasicBlock b = implicitAnalysisblocks.get(toMerge);
									if(b.insn.getOpcode() == Opcodes.ATHROW)
										continue;
								}
								int labelToMerge = getLabel(toMerge);
								if (DEBUG)
									System.out.println(toMerge + " AKA " + labelToMerge);
								if (DEBUG)
									System.out.println((outFrames.get(labelToMerge) == null ? "null" : outFrames.get(labelToMerge).stack));
								if(inFrames.get(labelToSuccessor) == null || outFrames.get(labelToMerge) == null) //e.g. for an edge into the first instruction in an exception handler
									continue;
								if (!outFrames.get(labelToMerge).stack.isEmpty() && !inFrames.get(labelToSuccessor).stack.isEmpty()) {
									Object output1Top = outFrames.get(labelToMerge).stack.get(outFrames.get(labelToMerge).stack.size() - 1);
									Object inputTop = inFrames.get(labelToSuccessor).stack.get(inFrames.get(labelToSuccessor).stack.size() - 1);
									if (output1Top == Opcodes.TOP)
										output1Top = outFrames.get(labelToMerge).stack.get(outFrames.get(labelToMerge).stack.size() - 2);
									if (inputTop == Opcodes.TOP)
										inputTop = inFrames.get(labelToSuccessor).stack.get(inFrames.get(labelToSuccessor).stack.size() - 2);
//									System.out.println(className+"."+name+ " IN"+inputTop +" OUT " + output1Top);
									if (output1Top != null && output1Top != inputTop) {
										Type inputTopType = TaintAdapter.getTypeForStackType(inputTop);
										Type outputTopType = TaintAdapter.getTypeForStackType(output1Top);
										if ((output1Top == Opcodes.NULL) && inputTopType.getSort() == Type.ARRAY && inputTopType.getElementType().getSort() != Type.OBJECT
												&& inputTopType.getDimensions() == 1) {
											insertACONSTNULLBEFORE.add(toMerge);
										} else if ((inputTopType.getSort() == Type.OBJECT || (inputTopType.getSort() == Type.ARRAY && inputTopType.getElementType().getSort() == Type.OBJECT)) && outputTopType.getSort() == Type.ARRAY && outputTopType.getElementType().getSort() != Type.OBJECT
												&& inputTopType.getDimensions() == 1) {
											insertACHECKCASTBEFORE.add(toMerge);
										}
									}
								}
								if (!outFrames.get(labelToMerge).local.isEmpty() && !inFrames.get(labelToSuccessor).local.isEmpty()) {
									for (int i = 0; i < Math.min(outFrames.get(labelToMerge).local.size(), inFrames.get(labelToSuccessor).local.size()); i++) {
										Object out = outFrames.get(labelToMerge).local.get(i);
										Object in = inFrames.get(labelToSuccessor).local.get(i);
//										System.out.println(name +" " +out + " out, " + in + " In" + " i "+i);
										if (out instanceof String && in instanceof String) {
											Type tout = Type.getObjectType((String) out);
											Type tin = Type.getObjectType((String) in);
											if (tout.getSort() == Type.ARRAY && tout.getElementType().getSort() != Type.OBJECT && tout.getDimensions() == 1 && tin.getSort() == Type.OBJECT) {
												int insnN = getLastInsnByLabel(labelToMerge);
//												System.out.println(name+desc);
//																							System.out.println(outFrames.get(labelToMerge).local + " out, \n" + inFrames.get(labelToSuccessor).local + " In" + " i "+i);
//												System.out.println("T1::"+tout + " to " + tin + " this may be unsupported but should be handled by the above! in label " + instructions.get(insnN));
//												System.out.println("In insn is " + getFirstInsnByLabel(labelToSuccessor));
//												System.out.println("insn after frame is " + insnN +", " + instructions.get(insnN) + "<"+Printer.OPCODES[instructions.get(insnN).getOpcode()]);
//													System.out.println(inFrames.get(labelToSuccessor).local);
												if (!alwaysAutoBoxByFrame.containsKey(insnN))
													alwaysAutoBoxByFrame.put(insnN, new LinkedList());
												alwaysAutoBoxByFrame.get(insnN).add(i);
											}
										}
									}
								}
							}
						}
					}

					//TODO: if the output of a frame is an array but the input is an obj, hint to always box?
					//or is that necessary, because we already assume that it's unboxed.
					return ret;
				}

				HashMap> edges = new HashMap>();


				LinkedList varsStoredThisInsn = new LinkedList();
				HashSet visited = new HashSet();
				int insnIdxOrderVisited = 0;

				@Override
				protected boolean newControlFlowExceptionEdge(int insnIndex, int successorIndex) {
//					if(Configuration.IMPLICIT_EXCEPTION_FLOW) {
//						this.newControlFlowEdge(insnIndex, successorIndex, true);
//					}
					return true;
				}

				@Override
				protected void newControlFlowEdge(int insn, int successor) {
					newControlFlowEdge(insn, successor, false);

				}

				protected void newControlFlowEdge(int insn, int successor, boolean isExceptionalEdge){
					if(visited.contains(insn+"-"+successor))
						return;
					visited.add(insn+"-"+successor);
					if (!edges.containsKey(successor))
						edges.put(successor, new LinkedList());
					if (!edges.get(successor).contains(insn))
						edges.get(successor).add(insn);
					if (!outEdges.containsKey(insn))
						outEdges.put(insn, new LinkedList());
					if (!outEdges.get(insn).contains(successor))
						outEdges.get(insn).add(successor);

					BasicBlock fromBlock;
					if(!implicitAnalysisblocks.containsKey(insn))
					{
						//insn not added yet
						fromBlock = new BasicBlock();
						fromBlock.idx = insn;
						fromBlock.insn = instructions.get(insn);
						implicitAnalysisblocks.put(insn,fromBlock);
					}
					else
						fromBlock = implicitAnalysisblocks.get(insn);

					AbstractInsnNode insnN = instructions.get(insn);
					fromBlock.isJump = (insnN.getType()== AbstractInsnNode.JUMP_INSN && insnN.getOpcode() != Opcodes.GOTO)
							|| insnN.getType() == AbstractInsnNode.LOOKUPSWITCH_INSN || insnN.getType() == AbstractInsnNode.TABLESWITCH_INSN;
					if(fromBlock.isJump && insnN.getType() == AbstractInsnNode.JUMP_INSN)
					{
						switch(insnN.getOpcode())
						{
							case Opcodes.IF_ICMPEQ:
							case Opcodes.IF_ICMPNE:
							case Opcodes.IF_ICMPGE:
							case Opcodes.IF_ICMPGT:
							case Opcodes.IF_ICMPLT:
							case Opcodes.IF_ICMPLE:
							case Opcodes.IF_ACMPEQ:
							case Opcodes.IF_ACMPNE:
								fromBlock.is2ArgJump = true;
								break;
						}
					}
					BasicBlock succesorBlock;
					if(implicitAnalysisblocks.containsKey(successor))
						succesorBlock = implicitAnalysisblocks.get(successor);
					else
					{
						succesorBlock = new BasicBlock();
						succesorBlock.idx = successor;
						succesorBlock.insn = instructions.get(successor);
						implicitAnalysisblocks.put(successor, succesorBlock);
						if(succesorBlock.insn.getType() == AbstractInsnNode.IINC_INSN)
						{
							succesorBlock.varsWritten.add(new LVAccess(((IincInsnNode)succesorBlock.insn).var, "I"));
						}
						else if(succesorBlock.insn.getType() == AbstractInsnNode.VAR_INSN)
						{
							switch(succesorBlock.insn.getOpcode())
							{
								case ISTORE:
									succesorBlock.varsWritten.add(new LVAccess(((VarInsnNode)succesorBlock.insn).var, "I"));
									break;
								case ASTORE:
									succesorBlock.varsWritten.add(new LVAccess(((VarInsnNode)succesorBlock.insn).var, "Ljava/lang/Object;"));
									break;
								case DSTORE:
									succesorBlock.varsWritten.add(new LVAccess(((VarInsnNode)succesorBlock.insn).var, "D"));
									break;
								case LSTORE:
									succesorBlock.varsWritten.add(new LVAccess(((VarInsnNode)succesorBlock.insn).var, "J"));
									break;
							}
						}
						else if(succesorBlock.insn.getType() == AbstractInsnNode.FIELD_INSN)
						{
							FieldInsnNode fin = (FieldInsnNode) succesorBlock.insn;
							if(fin.getOpcode() == Opcodes.PUTFIELD)
							{
								Frame fr = this.getFrames()[successor];
								if(fr != null && fr.getStack(fr.getStackSize()-2) == BasicArrayInterpreter.THIS_VALUE) {
									succesorBlock.fieldsWritten.add(new Field(false,fin.owner,fin.name,fin.desc));
								}
							}
							else if(fin.getOpcode() == Opcodes.PUTSTATIC){
								succesorBlock.fieldsWritten.add(new Field(true,fin.owner,fin.name,fin.desc));
							}
						}
						else if(succesorBlock.insn.getType() == AbstractInsnNode.METHOD_INSN){
							MethodInsnNode min = (MethodInsnNode) succesorBlock.insn;
							if(min.getOpcode() == INVOKEVIRTUAL || min.getOpcode() == INVOKESPECIAL){
								Type[] desc = Type.getArgumentTypes(min.desc);
								if(
										(desc.length == 1 && (Type.getReturnType(min.desc).getSort()==Type.VOID || min.desc.equals("Ljava/lang/StringBuilder;")))
												||
												(desc.length == 2 && Type.getReturnType(min.desc).getSort() == Type.VOID && min.name.startsWith("set")))
								{
									Frame fr = this.getFrames()[successor];
									if(fr != null && fr.getStack(fr.getStackSize()-2) instanceof BasicArrayInterpreter.BasicThisFieldValue) {
										BasicArrayInterpreter.BasicThisFieldValue vv = (BasicArrayInterpreter.BasicThisFieldValue) fr.getStack(fr.getStackSize()-2);
										succesorBlock.fieldsWritten.add(vv.getField());
									}
								}
							}
						}
						else if(succesorBlock.insn.getOpcode() == Opcodes.ATHROW){
							BasicValue ex = (BasicValue) this.getFrames()[successor].getStack(0);
							if(shouldTrackExceptions && ex!= null && ex.getType() != null && (ex.getType().getDescriptor().contains("Exception") || ex.getType().getDescriptor().contains("Error")))
							{
								succesorBlock.exceptionsThrown.add(ex.getType().getInternalName() + "#" + successor);
							}
						}
					}
					fromBlock.successors.add(succesorBlock);
					succesorBlock.predecessors.add(fromBlock);

					if (fromBlock.isJump) {
						if (fromBlock.covered)
							succesorBlock.onTrueSideOfJumpFrom.add(fromBlock);
						else {
							succesorBlock.onFalseSideOfJumpFrom.add(fromBlock);
							fromBlock.covered = true;
						}
					}
					super.newControlFlowEdge(insn, successor);
				}
			};
			try {

				Frame[] frames = a.analyze(className, this);
				for(int i = 0 ; i < instructions.size(); i++)
				{
					if(frames[i] == null)
					{
						//TODO dead code elimination.
						//This should be done more generically
						//But, this worked for JDT's stupid bytecode, so...
						AbstractInsnNode insn = instructions.get(i);
						//while (insn != null && insn.getType() != AbstractInsnNode.LABEL) {
							if(insn.getOpcode() == Opcodes.ATHROW || insn.getOpcode() == Opcodes.GOTO || (insn.getOpcode() >= Opcodes.IRETURN && insn.getOpcode() <= Opcodes.RETURN))
							{
								instructions.insertBefore(insn, new InsnNode(Opcodes.ATHROW));
								instructions.remove(insn);
								break;
							}
							else if (insn instanceof FrameNode)
							{
								FrameNode fn = (FrameNode) insn;
								fn.local = Collections.EMPTY_LIST;
								fn.stack = Collections.singletonList((Object) "java/lang/Throwable");
							}
							else if (!(insn instanceof LineNumberNode) && !(insn instanceof LabelNode)){
								instructions.insertBefore(insn, new InsnNode(Opcodes.NOP));
								instructions.remove(insn);
							}
							//insn = insn.getNext();
					//	}
					}
				}
//				HashMap cfg = new HashMap();
//				for(Integer i : outEdges.keySet())
//				{
//					BasicBlock b = new BasicBlock();
//					b.idx = i;
//					b.outEdges = outEdges.get(i);
//					int endIdx = this.instructions.size();
//					for(Integer jj : outEdges.get(i))
//						if(i < endIdx)
//							endIdx = jj;
//					for(int j =i; j < endIdx; j++)
//					{
//						if(instructions.get(i) instanceof VarInsnNode)
//						{
//							VarInsnNode n = ((VarInsnNode) instructions.get(i));
//							b.varsAccessed.add(n.var);
//						}
//					}
//					cfg.put(i, b);
//				}
//				for(Integer i : cfg.keySet())
//				{
//					computeVarsAccessed(i,cfg);
//				}
				ArrayList toAddNullBefore = new ArrayList();
//				toAddNullBefore.addAll(insertACONSTNULLBEFORE);

				toAddNullBefore.addAll(insertACHECKCASTBEFORE);
				toAddNullBefore.addAll(neverAutoBoxByFrame.keySet());
				toAddNullBefore.addAll(alwaysAutoBoxByFrame.keySet());
				Collections.sort(toAddNullBefore);

				HashMap problemLabels = new HashMap();
				HashMap> problemVars = new HashMap>();
				int nNewNulls = 0;
				for (Integer i : toAddNullBefore) {
					AbstractInsnNode insertAfter = this.instructions.get(i + nNewNulls);

					if (insertACONSTNULLBEFORE.contains(i)) {
//						if (DEBUG)
//							System.out.println("Adding Null before: " + i);
//						if (insertAfter.getOpcode() == Opcodes.GOTO)
//							insertAfter = insertAfter.getPrevious();
//						this.instructions.insert(insertAfter, new InsnNode(Opcodes.ACONST_NULL));
//						nNewNulls++;
					} else if (insertACHECKCASTBEFORE.contains(i)) {
						if (DEBUG)
							System.out.println("Adding checkcast before: " + i + " (plus " + nNewNulls + ")");
						if (insertAfter.getOpcode() == Opcodes.GOTO)
							insertAfter = insertAfter.getPrevious();
						this.instructions.insert(insertAfter, new TypeInsnNode(Opcodes.CHECKCAST, Type.getInternalName(Object.class)));
						nNewNulls++;
					} else if (neverAutoBoxByFrame.containsKey(i)) {
						if (insertAfter.getOpcode() == Opcodes.GOTO)
							insertAfter = insertAfter.getPrevious();
						for (int j : neverAutoBoxByFrame.get(i)) {
//							System.out.println("Adding nevefbox: before " + i + " (plus " + nNewNulls + ")");

							this.instructions.insert(insertAfter, new VarInsnNode(TaintUtils.NEVER_AUTOBOX, j));
							nNewNulls++;
						}
					} else if (alwaysAutoBoxByFrame.containsKey(i)) {
						for (int j : alwaysAutoBoxByFrame.get(i)) {
//							System.out.println("Adding checkcast always: before " + i + " (plus " + nNewNulls + ")");
//								while(insertAfter.getType() == AbstractInsnNode.LABEL ||
//										insertAfter.getType() == AbstractInsnNode.LINE||
//										insertAfter.getType() == AbstractInsnNode.FRAME)
//									insertAfter = insertAfter.getNext();
							AbstractInsnNode query = insertAfter.getNext();
							while(query.getNext() != null && (query.getType() == AbstractInsnNode.LABEL ||
									query.getType() == AbstractInsnNode.LINE ||
									query.getType() == AbstractInsnNode.FRAME || query.getOpcode() > 200))
								query = query.getNext();
							if(query.getOpcode() == Opcodes.ALOAD && query.getNext().getOpcode() == Opcodes.MONITOREXIT)
								insertAfter = query.getNext();
							if(query.getType() == AbstractInsnNode.JUMP_INSN)
								insertAfter = query;
							if(insertAfter.getType() == AbstractInsnNode.JUMP_INSN)
							{
								insertAfter = insertAfter.getPrevious();
//								System.out.println(Printer.OPCODES[insertAfter.getNext().getOpcode()]);
//								System.out.println("insertbefore  : " + ((JumpInsnNode) insertAfter.getNext()).toString());
								if(insertAfter.getNext().getOpcode() != Opcodes.GOTO)
								{
									this.instructions.insert(insertAfter, new VarInsnNode(TaintUtils.ALWAYS_BOX_JUMP, j));
								}
								else
								{
//									System.out.println("box immediately");
									this.instructions.insert(insertAfter, new VarInsnNode(TaintUtils.ALWAYS_AUTOBOX, j));
								}
							}
							else
							{
								if(insertAfter.getOpcode() == Opcodes.ATHROW)
									this.instructions.insertBefore(query, new VarInsnNode(TaintUtils.ALWAYS_AUTOBOX, j));
								else
									this.instructions.insert(insertAfter, new VarInsnNode(TaintUtils.ALWAYS_AUTOBOX, j));
							}
							nNewNulls++;
						}
					}
				}

//				System.out.println(name+desc);
				//fix LVs for android (sigh)
//				for(LabelNode l : problemLabels.keySet())
//				{
//					System.out.println("Problem label: "+l);
//				}
				boolean hadChanges = true;
				while (hadChanges) {
					hadChanges = false;

					HashSet newLVNodes = new HashSet();
					if (this.localVariables != null) {
						for (Object _lv : this.localVariables) {
							LocalVariableNode lv = (LocalVariableNode) _lv;
							AbstractInsnNode toCheck = lv.start;
							LabelNode veryEnd = lv.end;
							while (toCheck != null && toCheck != lv.end) {
								if ((toCheck.getOpcode() == TaintUtils.ALWAYS_BOX_JUMP || toCheck.getOpcode() ==TaintUtils.ALWAYS_AUTOBOX) && ((VarInsnNode) toCheck).var == lv.index) {
//									System.out.println("LV " + lv.name + " will be a prob around " + toCheck);
									LabelNode beforeProblem = new LabelNode(new Label());
									LabelNode afterProblem = new LabelNode(new Label());
									this.instructions.insertBefore(toCheck, beforeProblem);
									this.instructions.insert(toCheck.getNext(), afterProblem);
									LocalVariableNode newLV = new LocalVariableNode(lv.name, lv.desc, lv.signature, afterProblem, veryEnd, lv.index);
									lv.end = beforeProblem;
									newLVNodes.add(newLV);
									hadChanges = true;
									break;
								}
								toCheck = toCheck.getNext();
							}
						}
						this.localVariables.addAll(newLVNodes);
					}
				}

			} catch (Throwable e) {
				System.err.println("While analyzing " + className);
				e.printStackTrace();
			}
			if (Configuration.ANNOTATE_LOOPS) {
				GraphBasedAnalyzer.doGraphAnalysis(this, implicitAnalysisblocks);
			}

			if (Configuration.IMPLICIT_TRACKING || isImplicitLightTracking) {
				boolean hasJumps = false;
				HashSet tryCatchHandlers = new HashSet<>();
				if(shouldTrackExceptions && nTryCatch > 0)
				{
					int exceptionHandlerCount = 1;
					hasJumps = true;
					for (Object o : tryCatchBlocks) {
						TryCatchBlockNode t = (TryCatchBlockNode) o;
						BasicBlock startBlock = null;
						BasicBlock handlerBlock = null;
						BasicBlock endBlock = null;
						Integer startKey = null;
						Integer endKey = null;


						for(Entry e : implicitAnalysisblocks.entrySet()){
							BasicBlock b =e.getValue();
							Integer i = e.getKey();
							if(b.insn == t.handler){
								handlerBlock = b;
							}
							if(b.insn == t.start)
							{
								startBlock = b;
								startKey = i;
							}
							if(b.insn == t.end) {
								endBlock = b;
								endKey = i;
							}
							if(startBlock != null && handlerBlock != null && endBlock != null)
								break;
						}
						if(startBlock == handlerBlock || endBlock == null)
							continue;

						//Identify all of the instructions in this try block
						if(startBlock != null && endBlock != null)
						{
							for(int i =startKey; i<= endKey; i++){
								if(implicitAnalysisblocks.get(i) != null)
								implicitAnalysisblocks.get(i).coveredByTryBlockFor.add(t.type);
							}
						}
						handlerBlock.exceptionsHandled.add(t.type);
						tryCatchHandlers.add(handlerBlock);
						startBlock.isTryBlockStart = true;
						startBlock.exceptionsHandled.add(t.type);
						handlerBlock.onFalseSideOfJumpFrom.add(startBlock);
						handlerBlock.handlerForRegionStartingAt.add(startBlock);
						startBlock.successors.add(handlerBlock);
						startBlock.ex_count = exceptionHandlerCount;
						startBlock.tryBlockEnd = endBlock;
						startBlock.handledAt = handlerBlock;


						exceptionHandlerCount++;
						for(BasicBlock suc : startBlock.successors)
						{
							if(!suc.onFalseSideOfJumpFrom.contains(startBlock))
								suc.onTrueSideOfJumpFrom.add(startBlock);
						}
//						handlerBlock.onTrueSideOfJumpFrom.remove(startBlock);
//						System.out.println(handlerBlock + " " + handlerBlock.onTrueSideOfJumpFrom + " " + handlerBlock.onFalseSideOfJumpFrom);
					}
//					debug(instructions.getFirst());

				}

				for(BasicBlock b : implicitAnalysisblocks.values())
					if(b.isJump)
					{
						hasJumps = true;
						break;
					}
				if (implicitAnalysisblocks.size() > 1 && hasJumps) {
					Stack stack = new Stack();

					//Fix successors to only point to jumps or labels
					boolean changed = true;
					while (changed) {
						changed = false;
						for (BasicBlock b : implicitAnalysisblocks.values()) {
								for (BasicBlock s : b.successors) {
									if (s.isInteresting()){
										changed |= b.successorsCompact.add(s);
									}
									else
									{
										changed |= b.successorsCompact.addAll(s.successorsCompact);
									}
								}
						}
					}
					//Post dominator analysis
                    HashSet interestingBlocks = new HashSet<>();
					for(BasicBlock b : implicitAnalysisblocks.values())
						if(b.isInteresting())
							interestingBlocks.add(b);
					for(BasicBlock b : implicitAnalysisblocks.values()) {
						if (b.successorsCompact.size() == 0)
							b.postDominators.add(b);
						else
							b.postDominators.addAll(interestingBlocks);
					}
					changed = true;
					while(changed)
					{
						changed = false;
						for(BasicBlock b : implicitAnalysisblocks.values())
						{
							if(b.successorsCompact.size() > 0 && b.isInteresting())
							{
								HashSet intersectionOfPredecessors = new HashSet();
								Iterator iter = b.successorsCompact.iterator();
								BasicBlock successor = iter.next();
								intersectionOfPredecessors.addAll(successor.postDominators);
								while (iter.hasNext()) {
									successor = iter.next();
									intersectionOfPredecessors.retainAll(successor.postDominators);
								}
								intersectionOfPredecessors.add(b);
								if (!b.postDominators.equals(intersectionOfPredecessors)) {
									changed = true;
									b.postDominators = intersectionOfPredecessors;
								}
							}
						}
					}


					//Add in markings for where jumps are resolved
					for(BasicBlock j : implicitAnalysisblocks.values()) {
						if(j.isJump || j.isTryBlockStart)
						{
//							System.out.println(j + " " + j.postDominators);
							j.postDominators.remove(j);
							HashSet visited = new HashSet<>();
							BasicBlock min = findImmediatePostDominator(j, visited);
//							System.out.println(j + " resolved at " + min +", of " + j.postDominators);
							if (min != null) {
								min.resolvedBlocks.add(j);
								min.resolvedHereBlocks.add(j);
							} else {
								// There are no post-dominators of this branch. That means that one leg of the
								// branch goes to a return. So, we'll say that this gets resolved at each return that
								// is a successor
								for(BasicBlock b : visited) {
									if(isExitInstruction(b.insn)) {
										b.resolvedHereBlocks.add(j);
									}
								}
							}
						}
					}

//					for(BasicBlock j : implicitAnalysisblocks.values())
//					{
//						this.instructions.insertBefore(j.insn, new LdcInsnNode(j.idx + " " + j.onTrueSideOfJumpFrom + " " + j.onFalseSideOfJumpFrom +" RH:" + j.resolvedHereBlocks + j.successorsCompact +" "+j.exceptionsThrown));
//						this.instructions.insertBefore(j.insn,new InsnNode(Opcodes.POP));
//					}
					//Propogate forward true-side/false-side to determine which vars are written
					stack.add(implicitAnalysisblocks.get(0));
//					stack.addAll(tryCatchHandlers);
					while (!stack.isEmpty()) {
						BasicBlock b = stack.pop();
						if (b.visited)
							continue;
						b.visited = true;

//						System.out.println("\t"+b.onFalseSideOfJumpFrom+b.onTrueSideOfJumpFrom+ b.resolvedHereBlocks);
						b.onFalseSideOfJumpFrom.removeAll(b.resolvedBlocks);
						b.onTrueSideOfJumpFrom.removeAll(b.resolvedBlocks);
						//Propogate markings to successors
						for (BasicBlock s : b.successors) {
							boolean _changed = false;

							_changed |= s.onFalseSideOfJumpFrom.addAll(b.onFalseSideOfJumpFrom);
							_changed |= s.onTrueSideOfJumpFrom.addAll(b.onTrueSideOfJumpFrom);
//							if(!s.visited)
								_changed |= s.resolvedBlocks.addAll(b.resolvedBlocks);
//							if(_changed)
//								s.visited = false;
							s.onFalseSideOfJumpFrom.remove(s);
							s.onTrueSideOfJumpFrom.remove(s);
//							if (!s.visited)
								stack.add(s);
						}
					}

//					for(BasicBlock j : implicitAnalysisblocks.values())
//					{
//						System.out.println(j.idx +  " " + j.onTrueSideOfJumpFrom + " "+j.onFalseSideOfJumpFrom);
//						this.instructions.insertBefore(j.insn, new LdcInsnNode(j.idx + " " + j.onTrueSideOfJumpFrom + " " + j.onFalseSideOfJumpFrom +" SUC:" + j.successors));
//						this.instructions.insertBefore(j.insn,new InsnNode(Opcodes.POP));
//					}
					for(BasicBlock j : implicitAnalysisblocks.values())
						j.visited = false;


					for(BasicBlock j : implicitAnalysisblocks.values())
					{
//						System.out.println(j.idx + " " + j.postDominators);
						if(j.isJump || j.isTryBlockStart)
						{
							stack = new Stack();
							stack.addAll(j.successors);
							HashSet visited = new HashSet<>();
//							System.out.println("START JUMP " + j.idx);
							while(!stack.isEmpty())
							{
								BasicBlock b = stack.pop();
								if(!visited.add(b))
									continue;
//								System.out.println(b.idx);
//								System.out.println(b.exceptionsThrown.toString() + b.onFalseSideOfJumpFrom + " "+b.onTrueSideOfJumpFrom);
//								System.out.println(b.fieldsWritten);
//								System.out.println(b.varsWritten);
								if(b.onTrueSideOfJumpFrom.contains(j))
								{
									j.varsWrittenTrueSide.addAll(b.varsWritten);
									j.fieldsWrittenTrueSide.addAll(b.fieldsWritten);
									j.exceptionsThrownTrueSide.addAll(b.exceptionsThrown);
									stack.addAll(b.successors);
								}
								else if(b.onFalseSideOfJumpFrom.contains(j))
								{
									j.varsWrittenFalseSide.addAll(b.varsWritten);
									j.fieldsWrittenFalseSide.addAll(b.fieldsWritten);
									j.exceptionsThrownFalseSide.addAll(b.exceptionsThrown);
									stack.addAll(b.successors);
								}
							}
//							System.out.println("Visited: " + visited);
						}
					}
					HashMap jumpIDs = new HashMap();
					int jumpID = 0;
					for (BasicBlock r : implicitAnalysisblocks.values()) {
						if(r.isTryBlockStart){
							//Need to actually insert this code at every exit from the SCC that is this try-catch block.

							//Find the end of the handler
							//this is any block that succeeds the handler and either: has no successors or has a successor in common with the start block
							LinkedList handlerEndBlock = new LinkedList<>();
							LinkedList toCheck = new LinkedList<>(r.handledAt.successors);
							HashSet visited = new HashSet<>();
							while(!toCheck.isEmpty()){
								BasicBlock e = toCheck.pop();
								if(!visited.add(e))
									continue;
								if(e.successors.size() == 0)
								{
									handlerEndBlock.add(e);
								}
								else if(r.postDominators.contains(e))
								{
									handlerEndBlock.add(e);
								}
								else
								{
									toCheck.addAll(e.successors);
								}
							}

							AbstractInsnNode lastInstructionInTryBlock = r.tryBlockEnd.insn;
							while (lastInstructionInTryBlock.getType() == AbstractInsnNode.FRAME || lastInstructionInTryBlock.getType() == AbstractInsnNode.LINE || lastInstructionInTryBlock.getType() == AbstractInsnNode.LABEL)
								lastInstructionInTryBlock = lastInstructionInTryBlock.getNext();


							//Set up the force control store's at the bottom of the try block
							HashSet lvsOnlyInHandler = new HashSet<>(r.varsWrittenFalseSide);
							lvsOnlyInHandler.removeAll(r.varsWrittenTrueSide);
							HashSet fieldsOnlyInHandler = new HashSet<>(r.fieldsWrittenFalseSide);
							fieldsOnlyInHandler.removeAll(r.fieldsWrittenTrueSide);
							for (LVAccess i : lvsOnlyInHandler)
								this.instructions.insertBefore(lastInstructionInTryBlock, i.getNewForceCtrlStoreNode());
							for (Field f : fieldsOnlyInHandler)
								this.instructions.insertBefore(lastInstructionInTryBlock, new FieldInsnNode((f.isStatic ? TaintUtils.FORCE_CTRL_STORE_SFIELD : TaintUtils.FORCE_CTRL_STORE), f.owner, f.name, f.description));


							AbstractInsnNode handledAtInsn = r.handledAt.insn;
							HashSet handledHereAlready = new HashSet<>();
							HashSet forceStoreAlready = new HashSet<>();
							while (handledAtInsn.getType() == AbstractInsnNode.FRAME || handledAtInsn.getType() == AbstractInsnNode.LINE || handledAtInsn.getType() == AbstractInsnNode.LABEL || handledAtInsn.getOpcode() > 200) {
								if(handledAtInsn.getOpcode() == TaintUtils.EXCEPTION_HANDLER_START){
									TypeInsnNode tin = (TypeInsnNode) handledAtInsn;
									if(tin.desc != null)
										handledHereAlready.add(tin.desc);
								}
								else if(handledAtInsn.getOpcode() == TaintUtils.FORCE_CTRL_STORE && handledAtInsn.getType() == AbstractInsnNode.VAR_INSN){
									VarInsnNode vn = (VarInsnNode) handledAtInsn;
									forceStoreAlready.add(vn.var);
								}
								handledAtInsn = handledAtInsn.getNext();
							}

							//Then do all of the force-ctr-stores
							//In the exception handler, force a store of what was written
							HashSet diff = new HashSet();

							diff.addAll(r.varsWrittenTrueSide);
							diff.removeAll(r.varsWrittenFalseSide);

							HashSet diffFields = new HashSet<>();
							diffFields.addAll(r.fieldsWrittenTrueSide);
							diffFields.removeAll(r.fieldsWrittenFalseSide);


							for (LVAccess i : diff)
								if(!forceStoreAlready.contains(i.idx))
									instructions.insertBefore(handledAtInsn, i.getNewForceCtrlStoreNode());
							for (Field f : diffFields)
								instructions.insertBefore(handledAtInsn, new FieldInsnNode((f.isStatic ? TaintUtils.FORCE_CTRL_STORE_SFIELD : TaintUtils.FORCE_CTRL_STORE), f.owner, f.name, f.description));

							//At the START of the handler, note that it's the start...
							if(handledHereAlready.size() == 0)
								instructions.insertBefore(handledAtInsn, new TypeInsnNode(TaintUtils.EXCEPTION_HANDLER_START, null));
							for (String ex : r.exceptionsHandled) {
								if (ex == null)
									ex = "java/lang/Throwable";
								if(!handledHereAlready.contains(ex))
									instructions.insertBefore(handledAtInsn, new TypeInsnNode(TaintUtils.EXCEPTION_HANDLER_START, ex));
								this.instructions.insertBefore(lastInstructionInTryBlock,new TypeInsnNode(TaintUtils.EXCEPTION_HANDLER_END,ex));
							}

							//At the END of the handler, remove this exception from the queue
							for(BasicBlock b : handlerEndBlock) {
								AbstractInsnNode insn = b.insn;
//								while (insn.getType() == AbstractInsnNode.FRAME || insn.getType() == AbstractInsnNode.LINE || insn.getType() == AbstractInsnNode.LABEL)
//									insn = insn.getNext();
								//Peek backwards to see if we are behind a GOTO
								while(insn != null && insn.getPrevious() != null && mightEndBlock(insn.getPrevious())) {
									insn = insn.getPrevious();
								}
								if(insn.getType() == AbstractInsnNode.LABEL || insn.getType() == AbstractInsnNode.LINE || insn.getType() == AbstractInsnNode.FRAME)
									insn = b.insn;
//								System.out.println(b +"," + insn);


								instructions.insertBefore(insn, new TypeInsnNode(TaintUtils.EXCEPTION_HANDLER_END, null));
							}

//							debug(instructions.getFirst());
						}
						else if (r.isJump) {
							jumpID++;

							HashSet common = new HashSet();
							common.addAll(r.varsWrittenFalseSide);
							common.retainAll(r.varsWrittenTrueSide);
							HashSet diff =new HashSet();
							diff.addAll(r.varsWrittenTrueSide);
							diff.addAll(r.varsWrittenFalseSide);
							diff.removeAll(common);

							HashSet commonFields = new HashSet<>();
							commonFields.addAll(r.fieldsWrittenTrueSide);
							commonFields.retainAll(r.fieldsWrittenFalseSide);
							HashSet diffFields = new HashSet<>();
							diffFields.addAll(r.fieldsWrittenFalseSide);
							diffFields.addAll(r.fieldsWrittenTrueSide);
							diffFields.removeAll(common);


							HashSet commonExceptionsThrown = new HashSet<>();
							commonExceptionsThrown.addAll(r.exceptionsThrownFalseSide);
							commonExceptionsThrown.retainAll(r.exceptionsThrownTrueSide);
							HashSet diffExceptions = new HashSet<>();
							diffExceptions.addAll(r.exceptionsThrownTrueSide);
							diffExceptions.addAll(r.exceptionsThrownFalseSide);
							diffExceptions.removeAll(commonExceptionsThrown);


//							System.out.println(b.idx + " " + b.varsWrittenFalseSide +" "+b.varsWrittenTrueSide + " " + b.exceptionsThrownFalseSide +  b.exceptionsThrownTrueSide);

							instructions.insertBefore(r.insn, new VarInsnNode(TaintUtils.BRANCH_START, jumpID));
							jumpIDs.put(r, jumpID);
							if (r.is2ArgJump)
								jumpID++;

							for (LVAccess i : diff)
								instructions.insertBefore(r.insn, i.getNewForceCtrlStoreNode());
							for (Field f : diffFields)
								instructions.insertBefore(r.insn, new FieldInsnNode((f.isStatic ? TaintUtils.FORCE_CTRL_STORE_SFIELD : TaintUtils.FORCE_CTRL_STORE), f.owner, f.name, f.description));
//							for (String s : diffExceptions)
//								instructions.insertBefore(r.insn, new TypeInsnNode(TaintUtils.FORCE_CTRL_STORE, s));



						}
						else if(shouldTrackExceptions && r.insn.getOpcode() >= Opcodes.IRETURN && r.insn.getOpcode() <= Opcodes.RETURN){
							//Return statement: check to see how we might have gotten here, and then find which exceptions we might have thrown if we came otherwise
							HashSet missedExceptions = new HashSet<>();
							for(BasicBlock b : r.onFalseSideOfJumpFrom)
							{
								HashSet tmp = new HashSet(b.exceptionsThrownTrueSide);
								tmp.removeAll(b.exceptionsThrownFalseSide);
								missedExceptions.addAll(tmp);
							}
							for(BasicBlock b : r.onTrueSideOfJumpFrom)
							{
								HashSet tmp = new HashSet(b.exceptionsThrownFalseSide);
								tmp.removeAll(b.exceptionsThrownTrueSide);
								missedExceptions.addAll(tmp);
							}
							HashSet filtered =new HashSet<>();
//							System.out.println(name + ":"+r.idx+" " + missedExceptions);
							for(String s : missedExceptions){
								if(s == null)
									s = "java/lang/Throwable";
								if(s.contains("#"))
									s = s.substring(0,s.indexOf('#'));
								if(filtered.add(s))
									instructions.insertBefore(r.insn, new TypeInsnNode(TaintUtils.UNTHROWN_EXCEPTION, s));
							}
						}
						else if(shouldTrackExceptions && (r.insn.getType() == AbstractInsnNode.METHOD_INSN || r.insn
						.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN)){
							//Are we in a try handler? If so, after this instruction, we should note that our execution may be contingent on some exception
							for(String s : r.coveredByTryBlockFor)
							{
								if(s == null)
									s = "java/lang/Throwable";
								instructions.insert(r.insn,new TypeInsnNode(TaintUtils.UNTHROWN_EXCEPTION_CHECK, s));
							}
						}
					}


					for (BasicBlock b : implicitAnalysisblocks.values()) {
//						System.out.println(b.idx + " -> " + b.successorsCompact);
//						System.out.println(b.successors);
//						System.out.println(b.resolvedBlocks);
						AbstractInsnNode insn = b.insn;
						while (insn.getType() == AbstractInsnNode.FRAME || insn.getType() == AbstractInsnNode.LINE || insn.getType() == AbstractInsnNode.LABEL)
							insn = insn.getNext();

						if(b.resolvedHereBlocks.size() == jumpIDs.size())
						{
							//Everything is resolved
							instructions.insertBefore(insn, new VarInsnNode(TaintUtils.BRANCH_END, -1));
						} else
							for (BasicBlock r : b.resolvedHereBlocks) {
//							System.out.println("Resolved: " + jumpIDs.get(r) + " at " + b.idx);
								//								System.out.println("GOt" + jumpIDs);
								if (r.isTryBlockStart) {

								} else {
									if (b.successors.size() > 0) {
										//for any return/athrow, we'll just bulk pop-all
										instructions.insertBefore(insn, new VarInsnNode(TaintUtils.BRANCH_END, jumpIDs.get(r)));
										if (r.is2ArgJump)
											instructions.insertBefore(insn, new VarInsnNode(TaintUtils.BRANCH_END, jumpIDs.get(r) + 1));
									}
								}
							}
						if (b.resolvedHereBlocks.size() > 0) {
							insn = b.insn;
							while (insn.getType() == AbstractInsnNode.FRAME || insn.getType() == AbstractInsnNode.LINE || insn.getType() == AbstractInsnNode.LABEL || insn.getOpcode() > 200)
								insn = insn.getNext();
							if (insn.getOpcode() == Opcodes.NEW) //maybe its a NEW
							{
								//Need to patch all frames to have the correct label in them :'(
								AbstractInsnNode i = insn;
								while (i != null && i.getType() != AbstractInsnNode.LABEL)
									i = i.getPrevious();

								LinkedList oldLabels = new LinkedList<>();
								oldLabels.add(((LabelNode)i));
								if(i.getPrevious() != null && i.getPrevious().getType() == AbstractInsnNode.LABEL)
									oldLabels.add(((LabelNode) i.getPrevious()));

								LabelNode newLabel = new LabelNode(new Label());
								instructions.insertBefore(insn, newLabel);
								i = instructions.getFirst();
								while (i != null) {
									if (i instanceof FrameNode) {
										FrameNode fr = (FrameNode) i;
										for (int j = 0; j < fr.stack.size(); j++) {
											if(oldLabels.contains(fr.stack.get(j))){
												fr.stack.set(j, newLabel.getLabel());
											}
										}
									}
									i = i.getNext();
								}
							}
						}
						if(b.successors.isEmpty() && !isImplicitLightTracking) //in light tracking mode no need to POP off of control at RETURN/THROW, becasue we don't reuse the obj
						{
							instructions.insertBefore(b.insn, new InsnNode(TaintUtils.FORCE_CTRL_STORE));
//							if (b.insn.getOpcode() != Opcodes.ATHROW) {
							instructions.insertBefore(b.insn, new VarInsnNode(TaintUtils.BRANCH_END, -1));
//							}
						}
						//						System.out.println(b.insn + " - " + b.domBlocks + "-" + b.antiDomBlocks);
					}
					nJumps = jumpID;
				}

			}

			this.maxStack += 100;

			AbstractInsnNode insn = instructions.getFirst();
			while(insn != null)
			{
				if(insn.getType() == AbstractInsnNode.FRAME)
				{
					//Insert a note before the instruction before this guy
					AbstractInsnNode insertBefore = insn;
					while (insertBefore != null && (insertBefore.getType() == AbstractInsnNode.FRAME || insertBefore.getType() == AbstractInsnNode.LINE
							|| insertBefore.getType() == AbstractInsnNode.LABEL))
						insertBefore = insertBefore.getPrevious();
					if (insertBefore != null)
						this.instructions.insertBefore(insertBefore, new InsnNode(TaintUtils.FOLLOWED_BY_FRAME));
				}
				insn = insn.getNext();
			}
			this.accept(cmv);
		}
		HashMap implicitAnalysisblocks = new HashMap();

	}

	/**
	 * Returns the immediate post-dominator of the specified block if one the specified block's post-dominators can be
	 * reached from the block along successor edges. If such a post-dominator cannot be found, returns null and
	 * ensures that the specified visited set contains all of the blocks reachable from the specified block using
	 * successor edges.
	 */
	private BasicBlock findImmediatePostDominator(BasicBlock block, HashSet visited) {
		SinglyLinkedList queue = new SinglyLinkedList<>();
		queue.enqueue(block);
		visited.add(block);
		while(!queue.isEmpty()) {
			BasicBlock cur = queue.dequeue();
			if(!cur.equals(block) && block.postDominators.contains(cur)) {
				return cur; // Found a post-dominator, first one found using BFS has shortest path
			} else {
				for(BasicBlock successor : cur.successors) {
					if(visited.add(successor)) {
						queue.enqueue(successor);
					}
				}
			}
		}
		return null;
	}


	/* Returns whether the specified instruction triggers a method exit. */
	private boolean isExitInstruction(AbstractInsnNode instruction) {
		switch(instruction.getOpcode()) {
			case Opcodes.IRETURN:
			case Opcodes.LRETURN:
			case Opcodes.FRETURN:
			case Opcodes.DRETURN:
			case Opcodes.ARETURN:
			case Opcodes.RETURN:
			case Opcodes.ATHROW:
				return true;
			default:
				return false;
		}
	}
	private boolean mightEndBlock(AbstractInsnNode insn) {
		return insn.getType() == AbstractInsnNode.LABEL || isExitInstruction(insn) || insn.getOpcode() == Opcodes.GOTO;
	}

	static void debug(AbstractInsnNode insn){
		while(insn != null) {
			if (insn.getType() == AbstractInsnNode.LABEL)
				System.out.println("LABEL " + ((LabelNode)insn).getLabel());
			if (insn.getOpcode() > 0) {
				if (insn.getOpcode() > PhosphorTextifier.OPCODES.length) {
					if (insn.getType() == AbstractInsnNode.TYPE_INSN)
						System.out.println(PhosphorTextifier.TYPE_OR_INT_OPCODES[insn.getOpcode() - 200]);
					else
						System.out.println(PhosphorTextifier.MORE_OPCODES[insn.getOpcode() - 200]);
				} else
					System.out.println(PhosphorTextifier.OPCODES[insn.getOpcode()]);
			}
			insn=insn.getNext();
		}
	}
	static class BasicBlock{
		public BasicBlock handledAt;
		protected int idxOrder;
		public HashSet postDominators= new HashSet() ;
		int idx;
//		LinkedList outEdges = new LinkedList();
		HashSet successorsCompact = new HashSet();
		HashSet successors = new HashSet();
		HashSet predecessors  = new HashSet();
		AbstractInsnNode insn;
		boolean covered;
		boolean visited;
		boolean isTryBlockStart;
		boolean isJump;
		boolean is2ArgJump;
		int ex_count;
		HashSet exceptionsHandled = new HashSet<>();
		HashSet coveredByTryBlockFor = new HashSet<>();
		HashSet handlerForRegionStartingAt = new HashSet<>();
		BasicBlock tryBlockEnd;

		HashSet resolvedHereBlocks = new HashSet();

		private boolean compactSuccessorsCalculated;
		public boolean isInteresting()
		{
			return isJump || isTryBlockStart || insn instanceof LabelNode;
		}

		HashSet resolvedBlocks = new HashSet();
		HashSet onFalseSideOfJumpFrom = new HashSet();
		HashSet onTrueSideOfJumpFrom = new HashSet();
		HashSet varsWritten = new HashSet();
		HashSet fieldsWritten = new HashSet<>();
		HashSet exceptionsThrown = new HashSet<>();
		HashSet exceptionsThrownTrueSide = new HashSet<>();
		HashSet exceptionsThrownFalseSide = new HashSet<>();

		HashSet varsWrittenTrueSide = new HashSet();
		HashSet varsWrittenFalseSide = new HashSet();
		HashSet fieldsWrittenTrueSide = new HashSet<>();
		HashSet fieldsWrittenFalseSide = new HashSet<>();

		public Set getSuccessorsOutsideOfRegion(int s, int e, Set visited){
			if(visited.contains(this))
				return Collections.emptySet();
			visited.add(this);
			if(idx < s || idx > e)
				return Collections.singleton(this);
			Set ret = new HashSet<>();
			for(BasicBlock suc : successors)
			{
				ret.addAll(suc.getSuccessorsOutsideOfRegion(s,e,visited));
			}
			return ret;

		}
		public AbstractInsnNode getNextNormalBlockAfterGOTO() {
			System.out.println(this + "," + successors+ " " + this.insn);
			if(this.insn instanceof LineNumberNode)
				System.out.println("("+((LineNumberNode)insn).line+")");
			if(this.insn.getOpcode() == Opcodes.GOTO){
				BasicBlock suc = successors.iterator().next();
				AbstractInsnNode insn = suc.insn;
				while (insn.getType() == AbstractInsnNode.FRAME || insn.getType() == AbstractInsnNode.LINE || insn.getType() == AbstractInsnNode.LABEL)
					insn = insn.getNext();
				return insn;
			}
			if(successors.size() > 1)
				throw new IllegalStateException();
			return successors.iterator().next().getNextNormalBlockAfterGOTO();
		}

		@Override
		public String toString() {
//			return insn.toString();
			return ""+idx;
		}
	}
	private static boolean isPrimitiveArrayType(BasicValue v) {
		if (v == null || v.getType() == null)
			return false;
		return v.getType().getSort() == Type.ARRAY && v.getType().getElementType().getSort() != Type.OBJECT;
	}

	static final boolean DEBUG = false;
	public HashSet wrapperTypesToPreAlloc = new HashSet();
	public int nJumps;
	@Override
	public void visitInsn(int opcode) {
		super.visitInsn(opcode);
		switch (opcode) {
		case Opcodes.FADD:
		case Opcodes.FREM:
		case Opcodes.FSUB:
		case Opcodes.FMUL:
		case Opcodes.FDIV:
			if(Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("F"));
			break;
		case Opcodes.DADD:
		case Opcodes.DSUB:
		case Opcodes.DMUL:
		case Opcodes.DDIV:
		case Opcodes.DREM:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("D"));
			break;
		case Opcodes.LSHL:
		case Opcodes.LUSHR:
		case Opcodes.LSHR:
		case Opcodes.LSUB:
		case Opcodes.LMUL:
		case Opcodes.LADD:
		case Opcodes.LDIV:
		case Opcodes.LREM:
		case Opcodes.LAND:
		case Opcodes.LOR:
		case Opcodes.LXOR:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("J"));
			break;
		case Opcodes.LCMP:
		case Opcodes.DCMPL:
		case Opcodes.DCMPG:
		case Opcodes.FCMPG:
		case Opcodes.FCMPL:
		case Opcodes.IADD:
		case Opcodes.ISUB:
		case Opcodes.IMUL:
		case Opcodes.IDIV:
		case Opcodes.IREM:
		case Opcodes.ISHL:
		case Opcodes.ISHR:
		case Opcodes.IUSHR:
		case Opcodes.IOR:
		case Opcodes.IAND:
		case Opcodes.IXOR:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("I"));
			break;
		case Opcodes.IALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("I"));
			break;
		case Opcodes.BALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("B"));
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("Z"));
			break;
		case Opcodes.CALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("C"));
			break;
		case Opcodes.DALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("D"));
			break;
		case Opcodes.LALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("J"));
			break;
		case Opcodes.FALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("F"));
			break;
		case Opcodes.SALOAD:
			wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("S"));
			break;
		case Opcodes.I2C:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("C"));
			break;
		case Opcodes.I2B:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("B"));
			break;
		case Opcodes.I2D:
		case Opcodes.F2D:
		case Opcodes.L2D:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("D"));
			break;
		case Opcodes.I2F:
		case Opcodes.L2F:
		case Opcodes.D2F:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("F"));
			break;
		case Opcodes.I2L:
		case Opcodes.F2L:
		case Opcodes.D2L:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("J"));
			break;
		case Opcodes.I2S:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("S"));
			break;
		case Opcodes.F2I:
		case Opcodes.L2I:
		case Opcodes.D2I:
			if (Configuration.PREALLOC_STACK_OPS)
				wrapperTypesToPreAlloc.add(TaintUtils.getContainerReturnType("I"));
			break;
		case Opcodes.ATHROW:
			nThrow++;
			break;
		}
	}
	@Override
	public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itfc) {
		super.visitMethodInsn(opcode, owner, name, desc,itfc);
		Type returnType = Type.getReturnType(desc);
		Type newReturnType = TaintUtils.getContainerReturnType(returnType);
		isEmptyMethod = false;
		if(newReturnType != returnType && !(returnType.getSort() == Type.ARRAY))
			wrapperTypesToPreAlloc.add(newReturnType);
	}

	public MethodNode mn;
	private boolean isImplicitLightTracking;
	public PrimitiveArrayAnalyzer(final String className, int access, final String name, final String desc, String signature, String[] exceptions, final MethodVisitor cmv, final boolean isImplicitLightTracking) {
		super(Configuration.ASM_VERSION);
		this.mn = new PrimitiveArrayAnalyzerMN(access, name, desc, signature, exceptions, className, cmv);
		this.mv = mn;
		this.isImplicitLightTracking = isImplicitLightTracking;
	}

	public PrimitiveArrayAnalyzer(Type singleWrapperTypeToAdd) {
		super(Configuration.ASM_VERSION);
		this.mv = new PrimitiveArrayAnalyzerMN(0, null,null,null,null,null, null);
		if(singleWrapperTypeToAdd.getSort() == Type.OBJECT && singleWrapperTypeToAdd.getInternalName().startsWith("edu/columbia/cs/psl/phosphor/struct/Tainted"))
			this.wrapperTypesToPreAlloc.add(singleWrapperTypeToAdd);
	}

	NeverNullArgAnalyzerAdapter analyzer;

	public void setAnalyzer(NeverNullArgAnalyzerAdapter preAnalyzer) {
		analyzer = preAnalyzer;
	}

	public boolean hasFinally;
	public int nTryCatch;
	public int nThrow;
	HashMap> newLabels = new HashMap<>();
	private Label addUniqueLabelFor(Label existing)
	{
		Label ret = new Label();
		if(!newLabels.containsKey(existing))
			newLabels.put(existing,new LinkedList