private void VarMapToGraph(VarVersionPair varpaar, SFormsFastMapDirect varmap) { VBStyleCollection <VarVersionNode, VarVersionPair> nodes = ssuversions.nodes; VarVersionNode node = nodes.GetWithKey(varpaar); node.live = new SFormsFastMapDirect(varmap); }
public override TextBuffer ToJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buffer = new TextBuffer(); tracer.AddMapping(bytecode); if (classDef) { ClassesProcessor.ClassNode child = DecompilerContext.GetClassProcessor().GetMapRootClasses ().GetOrNull(varType.value); new ClassWriter().ClassToJava(child, buffer, indent, tracer); tracer.IncrementCurrentSourceLine(buffer.CountLines()); } else { VarVersionPair varVersion = GetVarVersionPair(); string name = null; if (processor != null) { name = processor.GetVarName(varVersion); } if (definition) { if (processor != null && processor.GetVarFinal(varVersion) == VarTypeProcessor.Var_Explicit_Final) { buffer.Append("final "); } AppendDefinitionType(buffer); buffer.Append(" "); } buffer.Append(name == null ? ("var" + index + (this.version == 0 ? string.Empty : "_" + this.version)) : name); } return(buffer); }
private static bool IsExprentIndependent(Exprent exprent, MethodWrapper method) { List <Exprent> lst = exprent.GetAllExprents(true); lst.Add(exprent); foreach (Exprent expr in lst) { switch (expr.type) { case Exprent.Exprent_Var: { VarVersionPair varPair = new VarVersionPair((VarExprent)expr); if (!method.varproc.GetExternalVars().Contains(varPair)) { string varName = method.varproc.GetVarName(varPair); if (!varName.Equals("this") && !varName.EndsWith(".this")) { // FIXME: remove direct comparison with strings return(false); } } break; } case Exprent.Exprent_Field: { return(false); } } } return(true); }
public static List <VarVersionPair> GetSyntheticParametersMask(ClassesProcessor.ClassNode node, string descriptor, int parameters) { List <VarVersionPair> mask = null; ClassWrapper wrapper = node.GetWrapper(); if (wrapper != null) { // own class MethodWrapper methodWrapper = wrapper.GetMethodWrapper(ICodeConstants.Init_Name, descriptor); if (methodWrapper == null) { if (DecompilerContext.GetOption(IFernflowerPreferences.Ignore_Invalid_Bytecode)) { return(null); } throw new Exception("Constructor " + node.classStruct.qualifiedName + "." + ICodeConstants .Init_Name + descriptor + " not found"); } mask = methodWrapper.synthParameters; } else if (parameters > 0 && node.type == ClassesProcessor.ClassNode.Class_Member && (node.access & ICodeConstants.Acc_Static) == 0) { // non-static member class mask = new List <VarVersionPair>(Enumerable.Repeat <VarVersionPair>(null, parameters)); mask[0] = new VarVersionPair(-1, 0); } return(mask); }
// vers.size() == 0 means uninitialized variable, which is impossible private void CreateOrUpdatePhiNode(VarVersionPair phivar, FastSparseSetFactory <int> .FastSparseSet <int> vers, Statement stat) { FastSparseSetFactory <int> .FastSparseSet <int> versCopy = vers.GetCopy(); HashSet <int> phiVers = new HashSet <int>(); // take into account the corresponding mm/pp node if existing int ppvers = phantomppnodes.ContainsKey(phivar) ? phantomppnodes.GetOrNull(phivar ).version : -1; // ssu graph VarVersionNode phinode = ssuversions.nodes.GetWithKey(phivar); List <VarVersionEdge> lstPreds = new List <VarVersionEdge>(phinode.preds); if (lstPreds.Count == 1) { // not yet a phi node VarVersionEdge edge = lstPreds[0]; edge.source.RemoveSuccessor(edge); phinode.RemovePredecessor(edge); } else { foreach (VarVersionEdge edge in lstPreds) { int verssrc = new Sharpen.EnumeratorAdapter <VarVersionEdge>(edge.source.preds.GetEnumerator()).Next().source.version; if (!vers.Contains(verssrc) && verssrc != ppvers) { edge.source.RemoveSuccessor(edge); phinode.RemovePredecessor(edge); } else { versCopy.Remove(verssrc); phiVers.Add(verssrc); } } } List <VarVersionNode> colnodes = new List <VarVersionNode>(); List <VarVersionPair> colpaars = new List <VarVersionPair>(); foreach (int ver in versCopy) { VarVersionNode prenode = ssuversions.nodes.GetWithKey(new VarVersionPair(phivar.var , ver)); int tempver = GetNextFreeVersion(phivar.var, stat); VarVersionNode tempnode = new VarVersionNode(phivar.var, tempver); colnodes.Add(tempnode); colpaars.Add(new VarVersionPair(phivar.var, tempver)); VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.Edge_General, prenode, tempnode ); prenode.AddSuccessor(edge); tempnode.AddPredecessor(edge); edge = new VarVersionEdge(VarVersionEdge.Edge_General, tempnode, phinode); tempnode.AddSuccessor(edge); phinode.AddPredecessor(edge); phiVers.Add(tempver); } ssuversions.AddNodes(colnodes, colpaars); }
public virtual SFormsFastMapDirect GetLiveVarVersionsMap(VarVersionPair varpaar) { VarVersionNode node = ssuversions.nodes.GetWithKey(varpaar); if (node != null) { return(node.live); } return(null); }
private static Dictionary <int, HashSet <VarVersionPair> > GetAllVarVersions(VarVersionPair leftvar, Exprent exprent, SSAUConstructorSparseEx ssau) { Dictionary <int, HashSet <VarVersionPair> > map = new Dictionary <int, HashSet <VarVersionPair > >(); SFormsFastMapDirect mapLiveVars = ssau.GetLiveVarVersionsMap(leftvar); List <Exprent> lst = exprent.GetAllExprents(true); lst.Add(exprent); foreach (Exprent expr in lst) { if (expr.type == Exprent.Exprent_Var) { int varindex = ((VarExprent)expr).GetIndex(); if (leftvar.var != varindex) { if (mapLiveVars.ContainsKey(varindex)) { HashSet <VarVersionPair> verset = new HashSet <VarVersionPair>(); foreach (int vers in mapLiveVars.Get(varindex)) { verset.Add(new VarVersionPair(varindex, vers)); } Sharpen.Collections.Put(map, varindex, verset); } else { throw new Exception("inkonsistent live map!"); } } else { Sharpen.Collections.Put(map, varindex, null); } } else if (expr.type == Exprent.Exprent_Field) { if (ssau.GetMapFieldVars().ContainsKey(expr.id)) { int?varindex = ssau.GetMapFieldVars().GetOrNullable(expr.id); if (mapLiveVars.ContainsKey(varindex.Value)) { HashSet <VarVersionPair> verset = new HashSet <VarVersionPair>(); foreach (int vers in mapLiveVars.Get(varindex.Value)) { verset.Add(new VarVersionPair(varindex.Value, vers)); } Sharpen.Collections.Put(map, varindex.Value, verset); } } } } return(map); }
private static bool IsVersionToBeReplaced(VarVersionPair usedvar, Dictionary <int , HashSet <VarVersionPair> > mapVars, SSAUConstructorSparseEx ssau, VarVersionPair leftpaar) { VarVersionsGraph ssuversions = ssau.GetSsuversions(); SFormsFastMapDirect mapLiveVars = ssau.GetLiveVarVersionsMap(usedvar); if (mapLiveVars == null) { // dummy version, predecessor of a phi node return(false); } // compare protected ranges if (!InterpreterUtil.EqualObjects(ssau.GetMapVersionFirstRange().GetOrNullable(leftpaar ), ssau.GetMapVersionFirstRange().GetOrNullable(usedvar))) { return(false); } foreach (KeyValuePair <int, HashSet <VarVersionPair> > ent in mapVars) { FastSparseSetFactory <int> .FastSparseSet <int> liveverset = mapLiveVars.Get(ent.Key); if (liveverset == null) { return(false); } HashSet <VarVersionNode> domset = new HashSet <VarVersionNode>(); foreach (VarVersionPair verpaar in ent.Value) { domset.Add(ssuversions.nodes.GetWithKey(verpaar)); } bool isdom = false; foreach (int livever in liveverset) { VarVersionNode node = ssuversions.nodes.GetWithKey(new VarVersionPair(ent.Key, livever )); if (ssuversions.IsDominatorSet(node, domset)) { isdom = true; break; } } if (!isdom) { return(false); } } return(true); }
private static bool GetUsedVersions(SSAUConstructorSparseEx ssa, VarVersionPair var, List <VarVersionNode> res) { VarVersionsGraph ssuversions = ssa.GetSsuversions(); VarVersionNode varnode = ssuversions.nodes.GetWithKey(var); HashSet <VarVersionNode> setVisited = new HashSet <VarVersionNode>(); HashSet <VarVersionNode> setNotDoms = new HashSet <VarVersionNode>(); LinkedList <VarVersionNode> stack = new LinkedList <VarVersionNode>(); stack.AddLast(varnode); while (!(stack.Count == 0)) { VarVersionNode nd = stack.RemoveAtReturningValue(0); setVisited.Add(nd); if (nd != varnode && (nd.flags & VarVersionNode.Flag_Phantom_Finexit) == 0) { res.Add(nd); } foreach (VarVersionEdge edge in nd.succs) { VarVersionNode succ = edge.dest; if (!setVisited.Contains(edge.dest)) { bool isDominated = true; foreach (VarVersionEdge prededge in succ.preds) { if (!setVisited.Contains(prededge.source)) { isDominated = false; break; } } if (isDominated) { stack.AddLast(succ); } else { setNotDoms.Add(succ); } } } } setNotDoms.RemoveAll(setVisited); return(!(setNotDoms.Count == 0)); }
public static bool IsInvocationInitConstructor(InvocationExprent inv, MethodWrapper method, ClassWrapper wrapper, bool withThis) { if (inv.GetFunctype() == InvocationExprent.Typ_Init && inv.GetInstance().type == Exprent.Exprent_Var) { VarExprent instVar = (VarExprent)inv.GetInstance(); VarVersionPair varPair = new VarVersionPair(instVar); string className = method.varproc.GetThisVars().GetOrNull(varPair); if (className != null) { // any this instance. TODO: Restrict to current class? return(withThis || !wrapper.GetClassStruct().qualifiedName.Equals(inv.GetClassname ())); } } return(false); }
// propagate (var = new X) forward to the <init> invocation private static bool IsConstructorInvocationRemote(List <Exprent> list, int index) { Exprent current = list[index]; if (current.type == Exprent.Exprent_Assignment) { AssignmentExprent @as = (AssignmentExprent)current; if (@as.GetLeft().type == Exprent.Exprent_Var && @as.GetRight().type == Exprent.Exprent_New) { NewExprent newExpr = (NewExprent)@as.GetRight(); VarType newType = newExpr.GetNewType(); VarVersionPair leftPair = new VarVersionPair((VarExprent)@as.GetLeft()); if (newType.type == ICodeConstants.Type_Object && newType.arrayDim == 0 && newExpr .GetConstructor() == null) { for (int i = index + 1; i < list.Count; i++) { Exprent remote = list[i]; // <init> invocation if (remote.type == Exprent.Exprent_Invocation) { InvocationExprent @in = (InvocationExprent)remote; if (@in.GetFunctype() == InvocationExprent.Typ_Init && @in.GetInstance().type == Exprent.Exprent_Var && @as.GetLeft().Equals(@in.GetInstance())) { newExpr.SetConstructor(@in); @in.SetInstance(null); list[i] = @as.Copy(); return(true); } } // check for variable in use HashSet <VarVersionPair> setVars = remote.GetAllVariables(); if (setVars.Contains(leftPair)) { // variable used somewhere in between -> exit, need a better reduced code return(false); } } } } } return(false); }
private int[] IterateExprent(List <Exprent> lstExprents, int index, Exprent next, Dictionary <VarVersionPair, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { Exprent exprent = lstExprents[index]; int changed = 0; foreach (Exprent expr in exprent.GetAllExprents()) { var oldExpr = expr; while (true) { object[] arr = IterateChildExprent(oldExpr, exprent, next, mapVarValues, ssau); Exprent retexpr = (Exprent)arr[0]; changed |= (bool)arr[1] ? 1 : 0; bool isReplaceable = (bool)arr[2]; if (retexpr != null) { if (isReplaceable) { ReplaceSingleVar(exprent, (VarExprent)oldExpr, retexpr, ssau); oldExpr = retexpr; } else { exprent.ReplaceExprent(oldExpr, retexpr); } changed = 1; } if (!isReplaceable) { break; } } } // no var on the highest level, so no replacing VarExprent left = null; Exprent right = null; if (exprent.type == Exprent.Exprent_Assignment) { AssignmentExprent @as = (AssignmentExprent)exprent; if (@as.GetLeft().type == Exprent.Exprent_Var) { left = (VarExprent)@as.GetLeft(); right = @as.GetRight(); } } if (left == null) { return(new int[] { -1, changed }); } VarVersionPair leftpaar = new VarVersionPair(left); List <VarVersionNode> usedVers = new List <VarVersionNode>(); bool notdom = GetUsedVersions(ssau, leftpaar, usedVers); if (!notdom && (usedVers.Count == 0)) { if (left.IsStack() && (right.type == Exprent.Exprent_Invocation || right.type == Exprent.Exprent_Assignment || right.type == Exprent.Exprent_New)) { if (right.type == Exprent.Exprent_New) { // new Object(); permitted NewExprent nexpr = (NewExprent)right; if (nexpr.IsAnonymous() || nexpr.GetNewType().arrayDim > 0 || nexpr.GetNewType(). type != ICodeConstants.Type_Object) { return(new int[] { -1, changed }); } } lstExprents[index] = right; return(new int[] { index + 1, 1 }); } else if (right.type == Exprent.Exprent_Var) { lstExprents.RemoveAtReturningValue(index); return(new int[] { index, 1 }); } else { return(new int[] { -1, changed }); } } int useflags = right.GetExprentUse(); // stack variables only if (!left.IsStack() && (right.type != Exprent.Exprent_Var || ((VarExprent)right). IsStack())) { // special case catch(... ex) return(new int[] { -1, changed }); } if ((useflags & Exprent.Multiple_Uses) == 0 && (notdom || usedVers.Count > 1)) { return(new int[] { -1, changed }); } Dictionary <int, HashSet <VarVersionPair> > mapVars = GetAllVarVersions(leftpaar, right , ssau); bool isSelfReference = mapVars.ContainsKey(leftpaar.var); if (isSelfReference && notdom) { return(new int[] { -1, changed }); } HashSet <VarVersionPair> setNextVars = next == null ? null : GetAllVersions(next); // FIXME: fix the entire method! if (right.type != Exprent.Exprent_Const && right.type != Exprent.Exprent_Var && setNextVars != null && mapVars.ContainsKey(leftpaar.var)) { foreach (VarVersionNode usedvar in usedVers) { if (!setNextVars.Contains(new VarVersionPair(usedvar.var, usedvar.version))) { return(new int[] { -1, changed }); } } } Sharpen.Collections.Remove(mapVars, leftpaar.var); bool vernotreplaced = false; bool verreplaced = false; HashSet <VarVersionPair> setTempUsedVers = new HashSet <VarVersionPair>(); foreach (VarVersionNode usedvar in usedVers) { VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version); if (IsVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && (right.type == Exprent .Exprent_Const || right.type == Exprent.Exprent_Var || right.type == Exprent.Exprent_Field || setNextVars == null || setNextVars.Contains(usedver))) { setTempUsedVers.Add(usedver); verreplaced = true; } else { vernotreplaced = true; } } if (isSelfReference && vernotreplaced) { return(new int[] { -1, changed }); } else { foreach (VarVersionPair usedver in setTempUsedVers) { Exprent copy = right.Copy(); if (right.type == Exprent.Exprent_Field && ssau.GetMapFieldVars().ContainsKey(right .id)) { Sharpen.Collections.Put(ssau.GetMapFieldVars(), copy.id, ssau.GetMapFieldVars().GetOrNullable (right.id)); } Sharpen.Collections.Put(mapVarValues, usedver, copy); } } if (!notdom && !vernotreplaced) { // remove assignment lstExprents.RemoveAtReturningValue(index); return(new int[] { index, 1 }); } else if (verreplaced) { return(new int[] { index + 1, changed }); } else { return(new int[] { -1, changed }); } }
private SFormsFastMapDirect GetFilteredOutMap(string nodeid, string predid, DirectGraph dgraph, string destid) { SFormsFastMapDirect mapNew = new SFormsFastMapDirect(); bool isFinallyExit = dgraph.mapShortRangeFinallyPaths.ContainsKey(predid); if (nodeid.Equals(dgraph.mapNegIfBranch.GetOrNull(predid))) { if (outNegVarVersions.ContainsKey(predid)) { mapNew = outNegVarVersions.GetOrNull(predid).GetCopy(); } } else if (outVarVersions.ContainsKey(predid)) { mapNew = outVarVersions.GetOrNull(predid).GetCopy(); } if (isFinallyExit) { SFormsFastMapDirect mapNewTemp = mapNew.GetCopy(); SFormsFastMapDirect mapTrueSource = new SFormsFastMapDirect(); string exceptionDest = dgraph.mapFinallyMonitorExceptionPathExits.GetOrNull(predid ); bool isExceptionMonitorExit = (exceptionDest != null && !nodeid.Equals(exceptionDest )); HashSet <string> setLongPathWrapper = new HashSet <string>(); foreach (List <FlattenStatementsHelper.FinallyPathWrapper> lstwrapper in dgraph.mapLongRangeFinallyPaths .Values) { foreach (FlattenStatementsHelper.FinallyPathWrapper finwraplong in lstwrapper) { setLongPathWrapper.Add(finwraplong.destination + "##" + finwraplong.source); } } foreach (FlattenStatementsHelper.FinallyPathWrapper finwrap in dgraph.mapShortRangeFinallyPaths .GetOrNull(predid)) { SFormsFastMapDirect map; bool recFinally = dgraph.mapShortRangeFinallyPaths.ContainsKey(finwrap.source); if (recFinally) { // recursion map = GetFilteredOutMap(finwrap.entry, finwrap.source, dgraph, destid); } else if (finwrap.entry.Equals(dgraph.mapNegIfBranch.GetOrNull(finwrap.source))) { map = outNegVarVersions.GetOrNull(finwrap.source); } else { map = outVarVersions.GetOrNull(finwrap.source); } // false path? bool isFalsePath; if (recFinally) { isFalsePath = !finwrap.destination.Equals(nodeid); } else { isFalsePath = !setLongPathWrapper.Contains(destid + "##" + finwrap.source); } if (isFalsePath) { mapNewTemp.Complement(map); } else if (mapTrueSource.IsEmpty()) { if (map != null) { mapTrueSource = map.GetCopy(); } } else { MergeMaps(mapTrueSource, map); } } if (isExceptionMonitorExit) { mapNew = mapTrueSource; } else { mapNewTemp.Union(mapTrueSource); mapNew.Intersection(mapNewTemp); if (!mapTrueSource.IsEmpty() && !mapNew.IsEmpty()) { // FIXME: what for?? // replace phi versions with corresponding phantom ones Dictionary <VarVersionPair, VarVersionPair> mapPhantom = phantomexitnodes.GetOrNull (predid); if (mapPhantom == null) { mapPhantom = new Dictionary <VarVersionPair, VarVersionPair>(); } SFormsFastMapDirect mapExitVar = mapNew.GetCopy(); mapExitVar.Complement(mapTrueSource); foreach (KeyValuePair <int, FastSparseSetFactory <int> .FastSparseSet <int> > ent in mapExitVar .EntryList()) { foreach (int version in ent.Value) { int varindex = ent.Key; VarVersionPair exitvar = new VarVersionPair(varindex, version); FastSparseSetFactory <int> .FastSparseSet <int> newSet = mapNew.Get(varindex); // remove the actual exit version newSet.Remove(version); // get or create phantom version VarVersionPair phantomvar = mapPhantom.GetOrNull(exitvar); if (phantomvar == null) { int newversion = GetNextFreeVersion(exitvar.var, null); phantomvar = new VarVersionPair(exitvar.var, newversion); VarVersionNode exitnode = ssuversions.nodes.GetWithKey(exitvar); VarVersionNode phantomnode = ssuversions.CreateNode(phantomvar); phantomnode.flags |= VarVersionNode.Flag_Phantom_Finexit; VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.Edge_Phantom, exitnode, phantomnode ); exitnode.AddSuccessor(edge); phantomnode.AddPredecessor(edge); Sharpen.Collections.Put(mapPhantom, exitvar, phantomvar); } // add phantom version newSet.Add(phantomvar.version); } } if (!(mapPhantom.Count == 0)) { Sharpen.Collections.Put(phantomexitnodes, predid, mapPhantom); } } } } return(mapNew); }
private FinallyProcessor.Record GetFinallyInformation(StructMethod mt, RootStatement root, CatchAllStatement fstat) { Dictionary <BasicBlock, bool> mapLast = new Dictionary <BasicBlock, bool>(); BasicBlockStatement firstBlockStatement = fstat.GetHandler().GetBasichead(); BasicBlock firstBasicBlock = firstBlockStatement.GetBlock(); Instruction instrFirst = firstBasicBlock.GetInstruction(0); int firstcode = 0; switch (instrFirst.opcode) { case ICodeConstants.opc_pop: { firstcode = 1; break; } case ICodeConstants.opc_astore: { firstcode = 2; break; } } ExprProcessor proc = new ExprProcessor(methodDescriptor, varProcessor); proc.ProcessStatement(root, mt.GetClassStruct()); SSAConstructorSparseEx ssa = new SSAConstructorSparseEx(); ssa.SplitVariables(root, mt); List <Exprent> lstExprents = firstBlockStatement.GetExprents(); VarVersionPair varpaar = new VarVersionPair((VarExprent)((AssignmentExprent)lstExprents [firstcode == 2 ? 1 : 0]).GetLeft()); FlattenStatementsHelper flatthelper = new FlattenStatementsHelper(); DirectGraph dgraph = flatthelper.BuildDirectGraph(root); LinkedList <DirectNode> stack = new LinkedList <DirectNode>(); stack.AddLast(dgraph.first); HashSet <DirectNode> setVisited = new HashSet <DirectNode>(); while (!(stack.Count == 0)) { DirectNode node = Sharpen.Collections.RemoveFirst(stack); if (setVisited.Contains(node)) { continue; } setVisited.Add(node); BasicBlockStatement blockStatement = null; if (node.block != null) { blockStatement = node.block; } else if (node.preds.Count == 1) { blockStatement = node.preds[0].block; } bool isTrueExit = true; if (firstcode != 1) { isTrueExit = false; for (int i = 0; i < node.exprents.Count; i++) { Exprent exprent = node.exprents[i]; if (firstcode == 0) { List <Exprent> lst = exprent.GetAllExprents(); lst.Add(exprent); bool found = false; foreach (Exprent expr in lst) { if (expr.type == Exprent.Exprent_Var && new VarVersionPair((VarExprent)expr).Equals (varpaar)) { found = true; break; } } if (found) { found = false; if (exprent.type == Exprent.Exprent_Exit) { ExitExprent exexpr = (ExitExprent)exprent; if (exexpr.GetExitType() == ExitExprent.Exit_Throw && exexpr.GetValue().type == Exprent .Exprent_Var) { found = true; } } if (!found) { return(null); } else { isTrueExit = true; } } } else if (firstcode == 2) { // search for a load instruction if (exprent.type == Exprent.Exprent_Assignment) { AssignmentExprent assexpr = (AssignmentExprent)exprent; if (assexpr.GetRight().type == Exprent.Exprent_Var && new VarVersionPair((VarExprent )assexpr.GetRight()).Equals(varpaar)) { Exprent next = null; if (i == node.exprents.Count - 1) { if (node.succs.Count == 1) { DirectNode nd = node.succs[0]; if (!(nd.exprents.Count == 0)) { next = nd.exprents[0]; } } } else { next = node.exprents[i + 1]; } bool found = false; if (next != null && next.type == Exprent.Exprent_Exit) { ExitExprent exexpr = (ExitExprent)next; if (exexpr.GetExitType() == ExitExprent.Exit_Throw && exexpr.GetValue().type == Exprent .Exprent_Var && assexpr.GetLeft().Equals(exexpr.GetValue())) { found = true; } } if (!found) { return(null); } else { isTrueExit = true; } } } } } } // find finally exits if (blockStatement != null && blockStatement.GetBlock() != null) { Statement handler = fstat.GetHandler(); foreach (StatEdge edge in blockStatement.GetSuccessorEdges(Statement.Statedge_Direct_All )) { if (edge.GetType() != StatEdge.Type_Regular && handler.ContainsStatement(blockStatement ) && !handler.ContainsStatement(edge.GetDestination())) { bool?existingFlag = mapLast.GetOrNullable(blockStatement.GetBlock()); // note: the dummy node is also processed! if (existingFlag == null || !existingFlag.Value) { Sharpen.Collections.Put(mapLast, blockStatement.GetBlock(), isTrueExit); break; } } } } Sharpen.Collections.AddAll(stack, node.succs); } // empty finally block? if (fstat.GetHandler().type == Statement.Type_Basicblock) { bool isEmpty = false; bool isFirstLast = mapLast.ContainsKey(firstBasicBlock); InstructionSequence seq = firstBasicBlock.GetSeq(); switch (firstcode) { case 0: { isEmpty = isFirstLast && seq.Length() == 1; break; } case 1: { isEmpty = seq.Length() == 1; break; } case 2: { isEmpty = isFirstLast ? seq.Length() == 3 : seq.Length() == 1; break; } } if (isEmpty) { firstcode = 3; } } return(new FinallyProcessor.Record(firstcode, mapLast)); }
public override TextBuffer ToJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buf = new TextBuffer(); if (isStatic__) { ClassesProcessor.ClassNode node = (ClassesProcessor.ClassNode)DecompilerContext.GetProperty (DecompilerContext.Current_Class_Node); if (node == null || !classname.Equals(node.classStruct.qualifiedName) || IsAmbiguous ()) { buf.Append(DecompilerContext.GetImportCollector().GetShortNameInClassContext(ExprProcessor .BuildJavaClassName(classname))); buf.Append("."); } } else { string super_qualifier = null; if (instance != null && instance.type == Exprent.Exprent_Var) { VarExprent instVar = (VarExprent)instance; VarVersionPair pair = new VarVersionPair(instVar); MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.GetProperty(DecompilerContext .Current_Method_Wrapper); if (currentMethod != null) { // FIXME: remove string this_classname = currentMethod.varproc.GetThisVars().GetOrNull(pair); if (this_classname != null) { if (!classname.Equals(this_classname)) { // TODO: direct comparison to the super class? super_qualifier = this_classname; } } } } if (super_qualifier != null) { TextUtil.WriteQualifiedSuper(buf, super_qualifier); } else { TextBuffer buff = new TextBuffer(); bool casted = ExprProcessor.GetCastedExprent(instance, new VarType(ICodeConstants .Type_Object, 0, classname), buff, indent, true, tracer); string res = buff.ToString(); if (casted || instance.GetPrecedence() > GetPrecedence()) { res = "(" + res + ")"; } buf.Append(res); } if (buf.ToString().Equals(VarExprent.Var_Nameless_Enclosure)) { // FIXME: workaround for field access of an anonymous enclosing class. Find a better way. buf.SetLength(0); } else { buf.Append("."); } } buf.Append(name); tracer.AddMapping(bytecode); return(buf); }
private void ProcessExprent(Exprent expr, SFormsFastMapDirect[] varmaparr, Statement stat, bool calcLiveVars) { if (expr == null) { return; } VarExprent varassign = null; bool finished = false; switch (expr.type) { case Exprent.Exprent_Assignment: { AssignmentExprent assexpr = (AssignmentExprent)expr; if (assexpr.GetCondType() == AssignmentExprent.Condition_None) { Exprent dest = assexpr.GetLeft(); if (dest.type == Exprent.Exprent_Var) { varassign = (VarExprent)dest; } } break; } case Exprent.Exprent_Function: { FunctionExprent func = (FunctionExprent)expr; switch (func.GetFuncType()) { case FunctionExprent.Function_Iif: { ProcessExprent(func.GetLstOperands()[0], varmaparr, stat, calcLiveVars); SFormsFastMapDirect varmapFalse; if (varmaparr[1] == null) { varmapFalse = new SFormsFastMapDirect(varmaparr[0]); } else { varmapFalse = varmaparr[1]; varmaparr[1] = null; } ProcessExprent(func.GetLstOperands()[1], varmaparr, stat, calcLiveVars); SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] { varmapFalse, null }; ProcessExprent(func.GetLstOperands()[2], varmaparrNeg, stat, calcLiveVars); MergeMaps(varmaparr[0], varmaparrNeg[0]); varmaparr[1] = null; finished = true; break; } case FunctionExprent.Function_Cadd: { ProcessExprent(func.GetLstOperands()[0], varmaparr, stat, calcLiveVars); SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] { new SFormsFastMapDirect (varmaparr[0]), null }; ProcessExprent(func.GetLstOperands()[1], varmaparrAnd, stat, calcLiveVars); // false map varmaparr[1] = MergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd [1] == null ? 0 : 1]); // true map varmaparr[0] = varmaparrAnd[0]; finished = true; break; } case FunctionExprent.Function_Cor: { ProcessExprent(func.GetLstOperands()[0], varmaparr, stat, calcLiveVars); SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] { new SFormsFastMapDirect (varmaparr[varmaparr[1] == null ? 0 : 1]), null }; ProcessExprent(func.GetLstOperands()[1], varmaparrOr, stat, calcLiveVars); // false map varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; // true map varmaparr[0] = MergeMaps(varmaparr[0], varmaparrOr[0]); finished = true; break; } } break; } } if (!finished) { List <Exprent> lst = expr.GetAllExprents(); lst.Remove(varassign); foreach (Exprent ex in lst) { ProcessExprent(ex, varmaparr, stat, calcLiveVars); } } SFormsFastMapDirect varmap = varmaparr[0]; // field access if (expr.type == Exprent.Exprent_Field) { int?index; if (mapFieldVars.ContainsKey(expr.id)) { index = mapFieldVars.GetOrNullable(expr.id); } else { index = fieldvarcounter--; Sharpen.Collections.Put(mapFieldVars, expr.id, index); // ssu graph ssuversions.CreateNode(new VarVersionPair(index, 1)); } SetCurrentVar(varmap, index, 1); } else if (expr.type == Exprent.Exprent_Invocation || (expr.type == Exprent.Exprent_Assignment && ((AssignmentExprent)expr).GetLeft().type == Exprent.Exprent_Field) || (expr. type == Exprent.Exprent_New && ((NewExprent)expr).GetNewType().type == ICodeConstants .Type_Object) || expr.type == Exprent.Exprent_Function) { bool ismmpp = true; if (expr.type == Exprent.Exprent_Function) { ismmpp = false; FunctionExprent fexpr = (FunctionExprent)expr; if (fexpr.GetFuncType() >= FunctionExprent.Function_Imm && fexpr.GetFuncType() <= FunctionExprent.Function_Ppi) { if (fexpr.GetLstOperands()[0].type == Exprent.Exprent_Field) { ismmpp = true; } } } if (ismmpp) { varmap.RemoveAllFields(); } } if (varassign != null) { int varindex = varassign.GetIndex(); if (varassign.GetVersion() == 0) { // get next version int nextver = GetNextFreeVersion(varindex, stat); // set version varassign.SetVersion(nextver); // ssu graph ssuversions.CreateNode(new VarVersionPair(varindex, nextver)); SetCurrentVar(varmap, varindex, nextver); } else { if (calcLiveVars) { VarMapToGraph(new VarVersionPair(varindex, varassign.GetVersion()), varmap); } SetCurrentVar(varmap, varindex, varassign.GetVersion()); } } else if (expr.type == Exprent.Exprent_Function) { // MM or PP function FunctionExprent func_1 = (FunctionExprent)expr; switch (func_1.GetFuncType()) { case FunctionExprent.Function_Imm: case FunctionExprent.Function_Mmi: case FunctionExprent.Function_Ipp: case FunctionExprent.Function_Ppi: { if (func_1.GetLstOperands()[0].type == Exprent.Exprent_Var) { VarExprent var = (VarExprent)func_1.GetLstOperands()[0]; int varindex = var.GetIndex(); VarVersionPair varpaar = new VarVersionPair(varindex, var.GetVersion()); // ssu graph VarVersionPair phantomver = phantomppnodes.GetOrNull(varpaar); if (phantomver == null) { // get next version int nextver = GetNextFreeVersion(varindex, null); phantomver = new VarVersionPair(varindex, nextver); //ssuversions.createOrGetNode(phantomver); ssuversions.CreateNode(phantomver); VarVersionNode vernode = ssuversions.nodes.GetWithKey(varpaar); FastSparseSetFactory <int> .FastSparseSet <int> vers = factory.SpawnEmptySet(); if (vernode.preds.Count == 1) { vers.Add(new Sharpen.EnumeratorAdapter <VarVersionEdge>(vernode.preds.GetEnumerator()).Next().source.version); } else { foreach (VarVersionEdge edge in vernode.preds) { vers.Add(new Sharpen.EnumeratorAdapter <VarVersionEdge>(edge.source.preds.GetEnumerator()).Next().source.version); } } vers.Add(nextver); CreateOrUpdatePhiNode(varpaar, vers, stat); Sharpen.Collections.Put(phantomppnodes, varpaar, phantomver); } if (calcLiveVars) { VarMapToGraph(varpaar, varmap); } SetCurrentVar(varmap, varindex, var.GetVersion()); } break; } } } else if (expr.type == Exprent.Exprent_Var) { VarExprent vardest = (VarExprent)expr; int varindex = vardest.GetIndex(); int current_vers = vardest.GetVersion(); FastSparseSetFactory <int> .FastSparseSet <int> vers = varmap.Get(varindex); int cardinality = vers.GetCardinality(); if (cardinality == 1) { // size == 1 if (current_vers != 0) { if (calcLiveVars) { VarMapToGraph(new VarVersionPair(varindex, current_vers), varmap); } SetCurrentVar(varmap, varindex, current_vers); } else { // split last version int usever = GetNextFreeVersion(varindex, stat); // set version vardest.SetVersion(usever); SetCurrentVar(varmap, varindex, usever); // ssu graph int lastver = new Sharpen.EnumeratorAdapter <int>(vers.GetEnumerator()).Next(); VarVersionNode prenode = ssuversions.nodes.GetWithKey(new VarVersionPair(varindex , lastver)); VarVersionNode usenode = ssuversions.CreateNode(new VarVersionPair(varindex, usever )); VarVersionEdge edge = new VarVersionEdge(VarVersionEdge.Edge_General, prenode, usenode ); prenode.AddSuccessor(edge); usenode.AddPredecessor(edge); } } else if (cardinality == 2) { // size > 1 if (current_vers != 0) { if (calcLiveVars) { VarMapToGraph(new VarVersionPair(varindex, current_vers), varmap); } SetCurrentVar(varmap, varindex, current_vers); } else { // split version int usever = GetNextFreeVersion(varindex, stat); // set version vardest.SetVersion(usever); // ssu node ssuversions.CreateNode(new VarVersionPair(varindex, usever)); SetCurrentVar(varmap, varindex, usever); current_vers = usever; } CreateOrUpdatePhiNode(new VarVersionPair(varindex, current_vers), vers, stat); } } }
private void ProcessExprent(Exprent expr, SFormsFastMapDirect[] varmaparr) { if (expr == null) { return; } VarExprent varassign = null; bool finished = false; switch (expr.type) { case Exprent.Exprent_Assignment: { AssignmentExprent assexpr = (AssignmentExprent)expr; if (assexpr.GetCondType() == AssignmentExprent.Condition_None) { Exprent dest = assexpr.GetLeft(); if (dest.type == Exprent.Exprent_Var) { varassign = (VarExprent)dest; } } break; } case Exprent.Exprent_Function: { FunctionExprent func = (FunctionExprent)expr; switch (func.GetFuncType()) { case FunctionExprent.Function_Iif: { ProcessExprent(func.GetLstOperands()[0], varmaparr); SFormsFastMapDirect varmapFalse; if (varmaparr[1] == null) { varmapFalse = new SFormsFastMapDirect(varmaparr[0]); } else { varmapFalse = varmaparr[1]; varmaparr[1] = null; } ProcessExprent(func.GetLstOperands()[1], varmaparr); SFormsFastMapDirect[] varmaparrNeg = new SFormsFastMapDirect[] { varmapFalse, null }; ProcessExprent(func.GetLstOperands()[2], varmaparrNeg); MergeMaps(varmaparr[0], varmaparrNeg[0]); varmaparr[1] = null; finished = true; break; } case FunctionExprent.Function_Cadd: { ProcessExprent(func.GetLstOperands()[0], varmaparr); SFormsFastMapDirect[] varmaparrAnd = new SFormsFastMapDirect[] { new SFormsFastMapDirect (varmaparr[0]), null }; ProcessExprent(func.GetLstOperands()[1], varmaparrAnd); // false map varmaparr[1] = MergeMaps(varmaparr[varmaparr[1] == null ? 0 : 1], varmaparrAnd[varmaparrAnd [1] == null ? 0 : 1]); // true map varmaparr[0] = varmaparrAnd[0]; finished = true; break; } case FunctionExprent.Function_Cor: { ProcessExprent(func.GetLstOperands()[0], varmaparr); SFormsFastMapDirect[] varmaparrOr = new SFormsFastMapDirect[] { new SFormsFastMapDirect (varmaparr[varmaparr[1] == null ? 0 : 1]), null }; ProcessExprent(func.GetLstOperands()[1], varmaparrOr); // false map varmaparr[1] = varmaparrOr[varmaparrOr[1] == null ? 0 : 1]; // true map varmaparr[0] = MergeMaps(varmaparr[0], varmaparrOr[0]); finished = true; break; } } break; } } if (finished) { return; } List <Exprent> lst = expr.GetAllExprents(); lst.Remove(varassign); foreach (Exprent ex in lst) { ProcessExprent(ex, varmaparr); } SFormsFastMapDirect varmap = varmaparr[0]; if (varassign != null) { int varindex = varassign.GetIndex(); if (varassign.GetVersion() == 0) { // get next version int nextver = GetNextFreeVersion(varindex); // set version varassign.SetVersion(nextver); SetCurrentVar(varmap, varindex, nextver); } else { SetCurrentVar(varmap, varindex, varassign.GetVersion()); } } else if (expr.type == Exprent.Exprent_Var) { VarExprent vardest = (VarExprent)expr; int varindex = vardest.GetIndex(); FastSparseSetFactory <int> .FastSparseSet <int> vers = varmap.Get(varindex); int cardinality = vers.GetCardinality(); if (cardinality == 1) { // == 1 // set version int it = new Sharpen.EnumeratorAdapter <int>(vers.GetEnumerator()).Next(); vardest.SetVersion(it); } else if (cardinality == 2) { // size > 1 int current_vers = vardest.GetVersion(); VarVersionPair currpaar = new VarVersionPair(varindex, current_vers); if (current_vers != 0 && phi.ContainsKey(currpaar)) { SetCurrentVar(varmap, varindex, current_vers); // update phi node phi.GetOrNull(currpaar).Union(vers); } else { // increase version int nextver = GetNextFreeVersion(varindex); // set version vardest.SetVersion(nextver); SetCurrentVar(varmap, varindex, nextver); // create new phi node Sharpen.Collections.Put(phi, new VarVersionPair(varindex, nextver), vers); } } } }
public override TextBuffer ToJava(int indent, BytecodeMappingTracer tracer) { TextBuffer buf = new TextBuffer(); string super_qualifier = null; bool isInstanceThis = false; tracer.AddMapping(bytecode); if (instance is InvocationExprent) { ((InvocationExprent)instance).MarkUsingBoxingResult(); } if (isStatic__) { if (IsBoxingCall() && canIgnoreBoxing) { // process general "boxing" calls, e.g. 'Object[] data = { true }' or 'Byte b = 123' // here 'byte' and 'short' values do not need an explicit narrowing type cast ExprProcessor.GetCastedExprent(lstParameters[0], descriptor.@params[0], buf, indent , false, false, false, false, tracer); return(buf); } ClassesProcessor.ClassNode node = (ClassesProcessor.ClassNode)DecompilerContext.GetProperty (DecompilerContext.Current_Class_Node); if (node == null || !classname.Equals(node.classStruct.qualifiedName)) { buf.Append(DecompilerContext.GetImportCollector().GetShortNameInClassContext(ExprProcessor .BuildJavaClassName(classname))); } } else { if (instance != null && instance.type == Exprent.Exprent_Var) { VarExprent instVar = (VarExprent)instance; VarVersionPair varPair = new VarVersionPair(instVar); VarProcessor varProc = instVar.GetProcessor(); if (varProc == null) { MethodWrapper currentMethod = (MethodWrapper)DecompilerContext.GetProperty(DecompilerContext .Current_Method_Wrapper); if (currentMethod != null) { varProc = currentMethod.varproc; } } string this_classname = null; if (varProc != null) { this_classname = varProc.GetThisVars().GetOrNull(varPair); } if (this_classname != null) { isInstanceThis = true; if (invocationTyp == Invoke_Special) { if (!classname.Equals(this_classname)) { // TODO: direct comparison to the super class? StructClass cl = DecompilerContext.GetStructContext().GetClass(classname); bool isInterface = cl != null && cl.HasModifier(ICodeConstants.Acc_Interface); super_qualifier = !isInterface ? this_classname : classname; } } } } if (functype == Typ_General) { if (super_qualifier != null) { TextUtil.WriteQualifiedSuper(buf, super_qualifier); } else if (instance != null) { TextBuffer res = instance.ToJava(indent, tracer); if (IsUnboxingCall()) { // we don't print the unboxing call - no need to bother with the instance wrapping / casting buf.Append(res); return(buf); } VarType rightType = instance.GetExprType(); VarType leftType = new VarType(ICodeConstants.Type_Object, 0, classname); if (rightType.Equals(VarType.Vartype_Object) && !leftType.Equals(rightType)) { buf.Append("((").Append(ExprProcessor.GetCastTypeName(leftType)).Append(")"); if (instance.GetPrecedence() >= FunctionExprent.GetPrecedence(FunctionExprent.Function_Cast )) { res.Enclose("(", ")"); } buf.Append(res).Append(")"); } else if (instance.GetPrecedence() > GetPrecedence()) { buf.Append("(").Append(res).Append(")"); } else { buf.Append(res); } } } } switch (functype) { case Typ_General: { if (VarExprent.Var_Nameless_Enclosure.Equals(buf.ToString())) { buf = new TextBuffer(); } if (buf.Length() > 0) { buf.Append("."); } buf.Append(name); if (invocationTyp == Invoke_Dynamic) { buf.Append("<invokedynamic>"); } buf.Append("("); break; } case Typ_Clinit: { throw new Exception("Explicit invocation of " + ICodeConstants.Clinit_Name); } case Typ_Init: { if (super_qualifier != null) { buf.Append("super("); } else if (isInstanceThis) { buf.Append("this("); } else if (instance != null) { buf.Append(instance.ToJava(indent, tracer)).Append(".<init>("); } else { throw new Exception("Unrecognized invocation of " + ICodeConstants.Init_Name); } break; } } List <VarVersionPair> mask = null; bool isEnum = false; if (functype == Typ_Init) { ClassesProcessor.ClassNode newNode = DecompilerContext.GetClassProcessor().GetMapRootClasses ().GetOrNull(classname); if (newNode != null) { mask = ExprUtil.GetSyntheticParametersMask(newNode, stringDescriptor, lstParameters .Count); isEnum = newNode.classStruct.HasModifier(ICodeConstants.Acc_Enum) && DecompilerContext .GetOption(IFernflowerPreferences.Decompile_Enum); } } BitSet setAmbiguousParameters = GetAmbiguousParameters(); // omit 'new Type[] {}' for the last parameter of a vararg method call if (lstParameters.Count == [email protected] && IsVarArgCall()) { Exprent lastParam = lstParameters[lstParameters.Count - 1]; if (lastParam.type == Exprent_New && lastParam.GetExprType().arrayDim >= 1) { ((NewExprent)lastParam).SetVarArgParam(true); } } bool firstParameter = true; int start = isEnum ? 2 : 0; for (int i = start; i < lstParameters.Count; i++) { if (mask == null || mask[i] == null) { TextBuffer buff = new TextBuffer(); bool ambiguous = setAmbiguousParameters.Get(i); // 'byte' and 'short' literals need an explicit narrowing type cast when used as a parameter ExprProcessor.GetCastedExprent(lstParameters[i], descriptor.@params[i], buff, indent , true, ambiguous, true, true, tracer); // the last "new Object[0]" in the vararg call is not printed if (buff.Length() > 0) { if (!firstParameter) { buf.Append(", "); } buf.Append(buff); } firstParameter = false; } } buf.Append(')'); return(buf); }
private static object[] IterateChildExprent(Exprent exprent, Exprent parent, Exprent next, Dictionary <VarVersionPair, Exprent> mapVarValues, SSAUConstructorSparseEx ssau) { bool changed = false; foreach (Exprent expr in exprent.GetAllExprents()) { var oldExpr = expr; while (true) { object[] arr = IterateChildExprent(oldExpr, parent, next, mapVarValues, ssau); Exprent retexpr = (Exprent)arr[0]; changed |= (bool)arr[1]; bool isReplaceable = (bool)arr[2]; if (retexpr != null) { if (isReplaceable) { ReplaceSingleVar(exprent, (VarExprent)oldExpr, retexpr, ssau); oldExpr = retexpr; } else { exprent.ReplaceExprent(oldExpr, retexpr); } changed = true; } if (!isReplaceable) { break; } } } Exprent dest = IsReplaceableVar(exprent, mapVarValues); if (dest != null) { return(new object[] { dest, true, true }); } VarExprent left = null; Exprent right = null; if (exprent.type == Exprent.Exprent_Assignment) { AssignmentExprent @as = (AssignmentExprent)exprent; if (@as.GetLeft().type == Exprent.Exprent_Var) { left = (VarExprent)@as.GetLeft(); right = @as.GetRight(); } } if (left == null) { return(new object[] { null, changed, false }); } bool isHeadSynchronized = false; if (next == null && parent.type == Exprent.Exprent_Monitor) { MonitorExprent monexpr = (MonitorExprent)parent; if (monexpr.GetMonType() == MonitorExprent.Monitor_Enter && exprent.Equals(monexpr .GetValue())) { isHeadSynchronized = true; } } // stack variable or synchronized head exprent if (!left.IsStack() && !isHeadSynchronized) { return(new object[] { null, changed, false }); } VarVersionPair leftpaar = new VarVersionPair(left); List <VarVersionNode> usedVers = new List <VarVersionNode>(); bool notdom = GetUsedVersions(ssau, leftpaar, usedVers); if (!notdom && (usedVers.Count == 0)) { return(new object[] { right, changed, false }); } // stack variables only if (!left.IsStack()) { return(new object[] { null, changed, false }); } int useflags = right.GetExprentUse(); if ((useflags & Exprent.Both_Flags) != Exprent.Both_Flags) { return(new object[] { null, changed, false }); } Dictionary <int, HashSet <VarVersionPair> > mapVars = GetAllVarVersions(leftpaar, right , ssau); if (mapVars.ContainsKey(leftpaar.var) && notdom) { return(new object[] { null, changed, false }); } Sharpen.Collections.Remove(mapVars, leftpaar.var); HashSet <VarVersionPair> setAllowedVars = GetAllVersions(parent); if (next != null) { Sharpen.Collections.AddAll(setAllowedVars, GetAllVersions(next)); } bool vernotreplaced = false; HashSet <VarVersionPair> setTempUsedVers = new HashSet <VarVersionPair>(); foreach (VarVersionNode usedvar in usedVers) { VarVersionPair usedver = new VarVersionPair(usedvar.var, usedvar.version); if (IsVersionToBeReplaced(usedver, mapVars, ssau, leftpaar) && (right.type == Exprent .Exprent_Var || setAllowedVars.Contains(usedver))) { setTempUsedVers.Add(usedver); } else { vernotreplaced = true; } } if (!notdom && !vernotreplaced) { foreach (VarVersionPair usedver in setTempUsedVers) { Exprent copy = right.Copy(); if (right.type == Exprent.Exprent_Field && ssau.GetMapFieldVars().ContainsKey(right .id)) { Sharpen.Collections.Put(ssau.GetMapFieldVars(), copy.id, ssau.GetMapFieldVars().GetOrNullable (right.id)); } Sharpen.Collections.Put(mapVarValues, usedver, copy); } // remove assignment return(new object[] { right, changed, false }); } return(new object[] { null, changed, false }); }