From c5edeeb3fb0f2fae58e8d8a6f741ec1aa3dc2d99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Thu, 4 Apr 2013 21:34:23 +0200 Subject: [PATCH] AS1/2 ActionTry fix AS1/2 Infinity --- trunk/src/com/jpexs/decompiler/flash/SWF.java | 63 ++- .../decompiler/flash/SWFInputStream.java | 159 +++--- .../abc/avm2/treemodel/NewObjectTreeItem.java | 2 +- .../abc/types/traits/TraitSlotConst.java | 33 +- .../jpexs/decompiler/flash/action/Action.java | 198 ++++--- .../decompiler/flash/action/ActionGraph.java | 2 +- .../flash/action/ActionGraphSource.java | 144 +++-- .../flash/action/gui/ActionPanel.java | 1 + .../flash/action/parser/ASMParser.java | 245 ++++----- .../flash/action/parser/FlasmLexer.java | 495 ++++++++++-------- .../decompiler/flash/action/parser/flasm.flex | 5 +- .../action/swf5/ActionDefineFunction.java | 84 +-- .../flash/action/swf5/ActionWith.java | 66 +-- .../action/swf7/ActionDefineFunction2.java | 95 +--- .../flash/action/swf7/ActionTry.java | 272 ++++++---- .../action/treemodel/DirectValueTreeItem.java | 44 +- .../action/treemodel/clauses/TryTreeItem.java | 13 +- .../jpexs/decompiler/flash/graph/Graph.java | 26 +- .../decompiler/flash/graph/GraphPart.java | 2 +- .../decompiler/flash/graph/GraphSource.java | 29 +- .../flash/graph/GraphSourceItemContainer.java | 19 +- 21 files changed, 1039 insertions(+), 958 deletions(-) diff --git a/trunk/src/com/jpexs/decompiler/flash/SWF.java b/trunk/src/com/jpexs/decompiler/flash/SWF.java index 03aea9f66..643effe99 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWF.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWF.java @@ -19,11 +19,9 @@ package com.jpexs.decompiler.flash; import SevenZip.Compression.LZMA.Encoder; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionGraphSource; -import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.action.swf4.ActionEquals; import com.jpexs.decompiler.flash.action.swf4.ActionGetVariable; import com.jpexs.decompiler.flash.action.swf4.ActionIf; -import com.jpexs.decompiler.flash.action.swf4.ActionJump; import com.jpexs.decompiler.flash.action.swf4.ActionPush; import com.jpexs.decompiler.flash.action.swf4.ActionSetVariable; import com.jpexs.decompiler.flash.action.swf4.Null; @@ -46,12 +44,11 @@ import com.jpexs.decompiler.flash.flv.FLVOutputStream; import com.jpexs.decompiler.flash.flv.FLVTAG; import com.jpexs.decompiler.flash.flv.VIDEODATA; import com.jpexs.decompiler.flash.graph.GraphSourceItem; -import com.jpexs.decompiler.flash.graph.GraphSourceItemPos; +import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.gui.FrameNode; import com.jpexs.decompiler.flash.gui.TagNode; import com.jpexs.decompiler.flash.helpers.Helper; -import com.jpexs.decompiler.flash.helpers.Highlighting; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG2Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG3Tag; import com.jpexs.decompiler.flash.tags.DefineBitsJPEG4Tag; @@ -240,7 +237,7 @@ public class SWF { * @param is Stream to read SWF from * @throws IOException */ - public SWF(InputStream is, PercentListener listener) throws IOException { + public SWF(InputStream is, PercentListener listener) throws IOException { byte hdr[] = new byte[3]; is.read(hdr); String shdr = new String(hdr); @@ -287,7 +284,7 @@ public class SWF { int tmpFirstByetOfFrameRate = sis.readUI8(); frameRate = sis.readUI8(); frameCount = sis.readUI16(); - tags = sis.readTagList(0); + tags = sis.readTagList(0); } /** @@ -949,6 +946,7 @@ public class SWF { } return null; } + private static void getVariables(ConstantPool constantPool, List localData, Stack stack, List output, ActionGraphSource code, int ip, int lastIp, HashMap variables, List functions, List visited) { boolean debugMode = false; while ((ip > -1) && ip < code.size()) { @@ -958,11 +956,11 @@ public class SWF { lastIp = ip; GraphSourceItem ins = code.get(ip); - + if (debugMode) { - System.err.println("Visit " + ip + ": ofs" +Helper.formatAddress(((Action)ins).getAddress())+":" + ((Action)ins).getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), code.version,false) + " stack:" + Helper.stackToString(stack, Helper.toList(new ConstantPool()))); + System.err.println("Visit " + ip + ": ofs" + Helper.formatAddress(((Action) ins).getAddress()) + ":" + ((Action) ins).getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), code.version, false) + " stack:" + Helper.stackToString(stack, Helper.toList(new ConstantPool()))); } - if(ins.isIgnored()){ + if (ins.isIgnored()) { ip++; continue; } @@ -984,11 +982,20 @@ public class SWF { } if (ins instanceof GraphSourceItemContainer) { - long endAddr=((GraphSourceItemContainer)ins).getEndAddress(); - int endIp=code.adr2pos(endAddr); - getVariables(variables, functions, new ActionGraphSource(code.getActions().subList(ip+1, endIp), code.version, new HashMap(),new HashMap(),new HashMap()),0); - ((GraphSourceItemContainer)ins).translateContainer(new ArrayList(), stack, output, new HashMap(),new HashMap(),new HashMap()); - ip=endIp; + GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; + List cntSizes = cnt.getContainerSizes(); + long addr = code.pos2adr(ip + 1); + for (Long size : cntSizes) { + if (size == 0) { + continue; + } + ip = code.adr2pos(addr); + addr += size; + int nextip = code.adr2pos(addr); + getVariables(variables, functions, new ActionGraphSource(code.getActions().subList(ip, nextip), code.version, new HashMap(), new HashMap(), new HashMap()), 0); + ip = nextip; + } + ((GraphSourceItemContainer) ins).translateContainer(new ArrayList>(), stack, output, new HashMap(), new HashMap(), new HashMap()); continue; } @@ -1017,7 +1024,7 @@ public class SWF { break; } - if (ins.isBranch() || ins.isJump()) { + if (ins.isBranch() || ins.isJump()) { if (ins instanceof ActionIf) { stack.pop(); } @@ -1033,7 +1040,7 @@ public class SWF { } } } - // } + // } break; } ip++; @@ -1052,29 +1059,29 @@ public class SWF { private HashMap getVariables(HashMap variables, List functions, ASMSource src) { HashMap ret = new HashMap(); List actions = src.getActions(version); - /* int ip=0; - for(Action a:actions){ - System.out.println("ip "+ip+" "+a.getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), version, false)); - ip++; - } - if(true) - return ret;*/ + /* int ip=0; + for(Action a:actions){ + System.out.println("ip "+ip+" "+a.getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), version, false)); + ip++; + } + if(true) + return ret;*/ actionsMap.put(src, actions); - List ss=new ArrayList(); + List ss = new ArrayList(); ss.addAll(actions); getVariables(variables, functions, new ActionGraphSource(ss, version, new HashMap(), new HashMap(), new HashMap()), 0); return ret; } private HashMap> actionsMap = new HashMap>(); - private void getVariables(List objs,String path) { + private void getVariables(List objs, String path) { for (Object o : objs) { if (o instanceof ASMSource) { - informListeners("getVariables", path+"/"+o.toString()); + informListeners("getVariables", path + "/" + o.toString()); getVariables(allVariableNames, allFunctions, (ASMSource) o); } if (o instanceof Container) { - getVariables(((Container) o).getSubItems(),path+"/"+o.toString()); + getVariables(((Container) o).getSubItems(), path + "/" + o.toString()); } } } @@ -1086,7 +1093,7 @@ public class SWF { List objs = new ArrayList(); int ret = 0; objs.addAll(tags); - getVariables(objs,""); + getVariables(objs, ""); for (GraphSourceItem fun : allFunctions) { if (fun instanceof ActionDefineFunction) { ActionDefineFunction f = (ActionDefineFunction) fun; diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index 847c60ec5..be80ba375 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -18,7 +18,6 @@ package com.jpexs.decompiler.flash; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionGraphSource; -import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.action.special.ActionEnd; import com.jpexs.decompiler.flash.action.special.ActionNop; import com.jpexs.decompiler.flash.action.swf3.*; @@ -29,6 +28,7 @@ import com.jpexs.decompiler.flash.action.swf7.*; import com.jpexs.decompiler.flash.action.treemodel.ConstantPool; import com.jpexs.decompiler.flash.action.treemodel.DirectValueTreeItem; import com.jpexs.decompiler.flash.graph.GraphSourceItem; +import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.graph.GraphSourceItemPos; import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.helpers.Helper; @@ -498,7 +498,7 @@ public class SWFInputStream extends InputStream { private static void getConstantPool(ConstantPool cpool, List localData, Stack stack, List output, ActionGraphSource code, int ip, int lastIp, List constantPools, List visited, int version, int endIp) { boolean debugMode = false; - while (((endIp==-1)||(endIp>ip))&&(ip > -1) && ip < code.size()) { + while (((endIp == -1) || (endIp > ip)) && (ip > -1) && ip < code.size()) { if (visited.contains(ip)) { break; } @@ -510,16 +510,26 @@ public class SWFInputStream extends InputStream { continue; } - if(ins instanceof GraphSourceItemContainer){ - GraphSourceItemContainer cnt=(GraphSourceItemContainer)ins; - long endAddr=((GraphSourceItemContainer)ins).getEndAddress(); - int nendIp = code.adr2pos(endAddr); - List localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); - List output2=new ArrayList(); - getConstantPool(cpool, localData2, new Stack(), output2, code, ip+1, lastIp, constantPools, visited, version,nendIp); - cnt.translateContainer(output2, stack, output,(HashMap) localData.get(0),(HashMap)localData.get(1),(HashMap)localData.get(2)); - ip = nendIp; - continue; + if (ins instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) ins; + if (ins instanceof Action) { + List> output2s = new ArrayList>(); + long endAddr = ((Action) ins).getAddress() + cnt.getHeaderSize(); + for (long size : cnt.getContainerSizes()) { + if (size == 0) { + output2s.add(new ArrayList()); + continue; + } + List localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + List output2 = new ArrayList(); + output2s.add(output2); + getConstantPool(cpool, localData2, new Stack(), output2, code, code.adr2pos(endAddr), lastIp, constantPools, visited, version, code.adr2pos(endAddr + size)); + endAddr += size; + } + cnt.translateContainer(output2s, stack, output, (HashMap) localData.get(0), (HashMap) localData.get(1), (HashMap) localData.get(2)); + ip = code.adr2pos(endAddr); + continue; + } } if (ins instanceof ActionPush) { if (cpool != null) { @@ -540,15 +550,15 @@ public class SWFInputStream extends InputStream { } } if (debugMode) { - String add=""; - if(ins instanceof ActionIf){ - add+=" change:"+((ActionIf)ins).getJumpOffset(); + String add = ""; + if (ins instanceof ActionIf) { + add += " change:" + ((ActionIf) ins).getJumpOffset(); } - if(ins instanceof ActionJump){ - add+=" change:"+(((ActionJump)ins).getJumpOffset()); + if (ins instanceof ActionJump) { + add += " change:" + (((ActionJump) ins).getJumpOffset()); } - System.err.println("getConstantPool ip " + ip + ", addr "+Helper.formatAddress(((Action)ins).getAddress())+": " + ((Action)ins).getASMSource(new ArrayList(),new ArrayList(), Helper.toList(cpool), version, false) +add+ " stack:" + Helper.stackToString(stack,Helper.toList(cpool))); - if(ip==116){ + System.err.println("getConstantPool ip " + ip + ", addr " + Helper.formatAddress(((Action) ins).getAddress()) + ": " + ((Action) ins).getASMSource(new ArrayList(), new ArrayList(), Helper.toList(cpool), version, false) + add + " stack:" + Helper.stackToString(stack, Helper.toList(cpool))); + if (ip == 116) { System.err.println("kok"); } } @@ -611,7 +621,7 @@ public class SWFInputStream extends InputStream { } } stack.pop(); - getConstantPool(cpool, localData, stack, output, code, condition ? (code.adr2pos(((ActionIf) ins).getAddress() + ((ActionIf) ins).getBytes(code.version).length + ((ActionIf) ins).getJumpOffset())) : ip + 1, ip, constantPools, visited, version,endIp); + getConstantPool(cpool, localData, stack, output, code, condition ? (code.adr2pos(((ActionIf) ins).getAddress() + ((ActionIf) ins).getBytes(code.version).length + ((ActionIf) ins).getJumpOffset())) : ip + 1, ip, constantPools, visited, version, endIp); } else { if (ins instanceof ActionIf) { stack.pop(); @@ -621,7 +631,7 @@ public class SWFInputStream extends InputStream { for (int b : branches) { Stack brStack = (Stack) stack.clone(); if (b >= 0) { - getConstantPool(cpool, localData, brStack, output, code, b, ip, constantPools, visited, version,endIp); + getConstantPool(cpool, localData, brStack, output, code, b, ip, constantPools, visited, version, endIp); } else { if (debugMode) { System.out.println("Negative branch:" + b); @@ -642,7 +652,7 @@ public class SWFInputStream extends InputStream { List ret = new ArrayList(); List localData = Helper.toList(new HashMap(), new HashMap(), new HashMap()); try { - getConstantPool(null, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), 0, ret, new ArrayList(), version,-1); + getConstantPool(null, localData, new Stack(), new ArrayList(), code, code.adr2pos(addr), 0, ret, new ArrayList(), version, -1); } catch (Exception ex) { log.log(Level.SEVERE, "Error during getting constantpool", ex); } @@ -674,7 +684,7 @@ public class SWFInputStream extends InputStream { method = 2; goesPrev = readActionListAtPos(true, localData, stack, cpool, sis, rri, ip, retdups, ip); }*/ - goesPrev = readActionListAtPos(new ArrayList(),new HashMap>(),address, containerSWFOffset, false, true, localData, stack, cpool, sis, rri, ip, retdups, ip, endip); + goesPrev = readActionListAtPos(new ArrayList(), new HashMap>(), address, containerSWFOffset, false, true, localData, stack, cpool, sis, rri, ip, retdups, ip, endip); if (goesPrev) { } else { @@ -705,13 +715,13 @@ public class SWFInputStream extends InputStream { } List pools = new ArrayList(); - StringBuilder br=new StringBuilder(); - for(int i=0;i(), new ArrayList(), cpool.constants, version, false)); + br.append(((Action) ret.get(i)).getASMSource(new ArrayList(), new ArrayList(), cpool.constants, version, false)); br.append("\r\n"); } Helper.writeFile("test.txt", br.toString().getBytes()); @@ -724,10 +734,10 @@ public class SWFInputStream extends InputStream { ActionJump aj = new ActionJump(ip); int skip = aj.getBytes(version).length; for (GraphSourceItem s : ret) { - - if(s instanceof Action){ - Action a=(Action)s; - a.setAddress(a.getAddress() + skip, version); + + if (s instanceof Action) { + Action a = (Action) s; + a.setAddress(a.getAddress() + skip, version); } } ret.add(0, aj); @@ -743,16 +753,16 @@ public class SWFInputStream extends InputStream { } catch (ParseException ex) { Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error", ex); }*/ - List reta=new ArrayList(); - for(Object o:ret){ - if(o instanceof Action){ - reta.add((Action)o); + List reta = new ArrayList(); + for (Object o : ret) { + if (o instanceof Action) { + reta.add((Action) o); } } return reta; } - private static boolean readActionListAtPos(List output,HashMap> containers,long address, long containerSWFOffset, boolean notCompileTime, boolean enableVariables, List localData, Stack stack, ConstantPool cpool, SWFInputStream sis, ReReadableInputStream rri, int ip, List ret, int startIp, int endip) throws IOException { + private static boolean readActionListAtPos(List output, HashMap> containers, long address, long containerSWFOffset, boolean notCompileTime, boolean enableVariables, List localData, Stack stack, ConstantPool cpool, SWFInputStream sis, ReReadableInputStream rri, int ip, List ret, int startIp, int endip) throws IOException { boolean debugMode = false; boolean decideBranch = false; boolean retv = false; @@ -762,26 +772,21 @@ public class SWFInputStream extends InputStream { Scanner sc = new Scanner(System.in); int prevIp = ip; while (((endip == -1) || (endip > ip)) && (a = sis.readAction(rri)) != null) { - if ((ip < ret.size()) && (!(ret.get(ip) instanceof ActionNop))) { + if ((ip < ret.size()) && (!(ret.get(ip) instanceof ActionNop))) { a = ret.get(ip); - if(a.getAddress()!=ip){ - new Exception("Jump to the middle of the instruction").printStackTrace(); + if (a.getAddress() != ip) { + new Exception("Jump to the middle of the instruction ip " + ip + " ins " + a.getASMSource(new ArrayList(), new ArrayList(), new ArrayList(), SWF.DEFAULT_VERSION, false)).printStackTrace(); } } a.containerSWFOffset = containerSWFOffset; a.setAddress(prevIp, SWF.DEFAULT_VERSION, false); int info = a.actionLength + 1 + ((a.actionCode > 0x80) ? 2 : 0); byte b[] = a.getBytes(sis.version); - int infoCorrect = info; + int infoCorrect = info; if (b.length != infoCorrect) { //throw new RuntimeException("Wrong length "+a.toString()+" info:"+infoCorrect+" actual:"+b.length+" datalen:"+(infoCorrect-info)); } - int actual = 0; - if (a instanceof GraphSourceItemContainer) { - actual = ((GraphSourceItemContainer) a).getHeaderBytes().length; - } else { - actual = a.getBytes(sis.version).length; - } + int actual = actual = a.getBytes(sis.version).length; if (!(a instanceof GraphSourceItemContainer)) { int change = info - (rri.getPos() - ip); if (change > 0) { @@ -819,22 +824,17 @@ public class SWFInputStream extends InputStream { if (debugMode) { //if(a instanceof ActionIf){ - String atos = a.getASMSource(new ArrayList(),new ArrayList(), cpool.constants, sis.version, false); + String atos = a.getASMSource(new ArrayList(), new ArrayList(), cpool.constants, sis.version, false); if (a instanceof GraphSourceItemContainer) { atos = a.toString(); } System.err.println("readActionListAtPos ip: " + (ip - startIp) + " (0x" + Helper.formatAddress(ip - startIp) + ") " + " action(len " + a.actionLength + "): " + atos + (a.isIgnored() ? " (ignored)" : "") + " stack:" + Helper.stackToString(stack, Helper.toList(cpool)) + " " + Helper.byteArrToString(a.getBytes(SWF.DEFAULT_VERSION))); - HashMap vars=(HashMap)localData.get(1); + HashMap vars = (HashMap) localData.get(1); System.err.print("variables: "); - for(String v:vars.keySet()){ - System.err.print("'"+v+"' = "+Highlighting.stripHilights(vars.get(v).toString(cpool))+", "); + for (String v : vars.keySet()) { + System.err.print("'" + v + "' = " + Highlighting.stripHilights(vars.get(v).toString(cpool)) + ", "); } System.err.println(); - //} - if(ip - startIp==110){ - System.err.println("dd"); - //readActionListAtPos ip: 116 (0x0074) action(len 2): If loc0125 ;compileTime stack:[!(==734)] [9d 02 00 21 00] - } String add = ""; if (a instanceof ActionIf) { add = " change: " + ((ActionIf) a).getJumpOffset(); @@ -952,14 +952,14 @@ public class SWFInputStream extends InputStream { //if(newip>=0){ //rri.setPos(newip); //} - } else if(!(a instanceof GraphSourceItemContainer)){ + } else if (!(a instanceof GraphSourceItemContainer)) { //return in for..in, TODO:Handle this better way if (((a instanceof ActionEquals) || (a instanceof ActionEquals2)) && (stack.size() == 1) && (stack.peek() instanceof DirectValueTreeItem)) { stack.push(new DirectValueTreeItem(null, 0, new Null(), new ArrayList())); } if ((a instanceof ActionStoreRegister) && stack.isEmpty()) { stack.push(new DirectValueTreeItem(null, 0, new Null(), new ArrayList())); - } + } a.translate(localData, stack, output); } } catch (RuntimeException ex) { @@ -990,24 +990,35 @@ public class SWFInputStream extends InputStream { } ret.set(ip + i, a); } - + if (a instanceof GraphSourceItemContainer) { - GraphSourceItemContainer cnt=(GraphSourceItemContainer)a; - long endAddr=((GraphSourceItemContainer)a).getEndAddress(); - /*if(!containers.containsKey(endAddr)){ - containers.put(endAddr, new ArrayList()); + GraphSourceItemContainer cnt = (GraphSourceItemContainer) a; + if (a instanceof Action) { + long endAddr = a.getAddress() + cnt.getHeaderSize(); + /*if(!containers.containsKey(endAddr)){ + containers.put(endAddr, new ArrayList()); + } + containers.get(endAddr).add((ActionContainer)a); + */ + List> output2s = new ArrayList>(); + for (long size : cnt.getContainerSizes()) { + if (size == 0) { + output2s.add(new ArrayList()); + continue; + } + List localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); + List output2 = new ArrayList(); + readActionListAtPos(output2, containers, address, containerSWFOffset, notCompileTime, enableVariables, localData2, new Stack(), cpool, sis, rri, (int) endAddr, ret, startIp, (int) (endAddr + size)); + output2s.add(output2); + endAddr += size; + } + cnt.translateContainer(output2s, stack, output, (HashMap) localData.get(0), (HashMap) localData.get(1), (HashMap) localData.get(2)); + ip = (int) endAddr; + prevIp = ip; + rri.setPos(ip); + filePos = rri.getPos(); + continue; } - containers.get(endAddr).add((ActionContainer)a); - */ - List localData2 = Helper.toList(new HashMap(), new HashMap(), new HashMap()); - List output2=new ArrayList(); - readActionListAtPos(output2,containers, address, containerSWFOffset, notCompileTime, enableVariables, localData2, new Stack(), cpool, sis, rri, ip+info, ret, startIp, (int)endAddr); - cnt.translateContainer(output2, stack, output,(HashMap) localData.get(0),(HashMap)localData.get(1),(HashMap)localData.get(2)); - ip = (int)endAddr; - prevIp = ip; - rri.setPos(ip); - filePos = rri.getPos(); - continue; //infoCorrect += ((ActionContainer) a).getDataLength(); } @@ -1029,7 +1040,7 @@ public class SWFInputStream extends InputStream { aif.jumpUsed = true; int oldPos = rri.getPos(); Stack substack = (Stack) stack.clone(); - if (readActionListAtPos(output,containers,address, containerSWFOffset, true, enableVariables, localData, substack, cpool, sis, rri, rri.getPos() + aif.getJumpOffset(), ret, startIp, endip)) { + if (readActionListAtPos(output, containers, address, containerSWFOffset, true, enableVariables, localData, substack, cpool, sis, rri, rri.getPos() + aif.getJumpOffset(), ret, startIp, endip)) { retv = true; } rri.setPos(oldPos); diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/treemodel/NewObjectTreeItem.java b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/treemodel/NewObjectTreeItem.java index cc73a980e..37205db95 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/avm2/treemodel/NewObjectTreeItem.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/avm2/treemodel/NewObjectTreeItem.java @@ -40,6 +40,6 @@ public class NewObjectTreeItem extends TreeItem { } params += pairs.get(n).toString(constants, localRegNames, fullyQualifiedNames); } - return "\r\n" +Graph.INDENTOPEN + "\r\n" + hilight("{") + "\r\n" + params + "\r\n" + hilight("}")+"\r\n"+Graph.INDENTCLOSE + "\r\n"; + return "\r\n" + Graph.INDENTOPEN + "\r\n" + hilight("{") + "\r\n" + params + "\r\n" + hilight("}") + "\r\n" + Graph.INDENTCLOSE + "\r\n"; } } diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java index 355c69d44..c269a3d98 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/types/traits/TraitSlotConst.java @@ -23,7 +23,6 @@ import com.jpexs.decompiler.flash.abc.types.Namespace; import com.jpexs.decompiler.flash.abc.types.ValueKind; import static com.jpexs.decompiler.flash.abc.types.traits.Trait.TRAIT_CONST; import com.jpexs.decompiler.flash.graph.Graph; -import static com.jpexs.decompiler.flash.graph.Graph.INDENTOPEN; import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.helpers.Helper; import com.jpexs.decompiler.flash.helpers.Highlighting; @@ -130,36 +129,36 @@ public class TraitSlotConst extends Trait { String ret = modifier + getNameStr(abc, fullyQualifiedNames); String valueStr = getValueStr(abc, fullyQualifiedNames); if (valueStr != null) { - ret+=" = "; - int befLen=ret.length(); + ret += " = "; + int befLen = ret.length(); ret = ABC.IDENT_STRING + ABC.IDENT_STRING + ret; String valueStrParts[] = valueStr.split("\r\n"); - boolean first=true; + boolean first = true; for (int i = 0; i < valueStrParts.length; i++) { - if(valueStrParts[i].equals("")){ + if (valueStrParts[i].equals("")) { continue; } - if(Highlighting.stripHilights(valueStrParts[i]).equals(Graph.INDENTOPEN)){ + if (Highlighting.stripHilights(valueStrParts[i]).equals(Graph.INDENTOPEN)) { //befLen+=ABC.IDENT_STRING.length(); continue; } - if(Highlighting.stripHilights(valueStrParts[i]).equals(Graph.INDENTCLOSE)){ + if (Highlighting.stripHilights(valueStrParts[i]).equals(Graph.INDENTCLOSE)) { //befLen-=ABC.IDENT_STRING.length(); continue; } - if(!first){ - ret+=ABC.IDENT_STRING + ABC.IDENT_STRING; - for(int j=0;j importantOffsets, List constantPool, int version, boolean hex, long swfPos) { + public static String actionsToString(long address, List list, List importantOffsets, List constantPool, int version, boolean hex, long swfPos) { String ret = ""; long offset; if (importantOffsets == null) { //setActionsAddresses(list, 0, version); importantOffsets = getActionsAllRefs(list, version); } - List srcList=new ArrayList(); - for(Object o:list){ - if(o instanceof Action){ - srcList.add((Action)o); + List srcList = new ArrayList(); + for (Object o : list) { + if (o instanceof Action) { + srcList.add((Action) o); } } - List cps = SWFInputStream.getConstantPool(new ActionGraphSource(srcList, version, new HashMap(), new HashMap(), new HashMap()), 0, version); + List cps = SWFInputStream.getConstantPool(new ActionGraphSource(srcList, version, new HashMap(), new HashMap(), new HashMap()), 0, version); if (!cps.isEmpty()) { setConstantPool(list, cps.get(cps.size() - 1)); } - HashMap> containers=new HashMap>(); + HashMap> containers = new HashMap>(); + HashMap containersPos = new HashMap(); offset = address; int pos = -1; for (GraphSourceItem s : srcList) { - Action a=null; - if(s instanceof Action){ - a=(Action)s; + Action a = null; + if (s instanceof Action) { + a = (Action) s; } pos++; if (hex) { - ret += "" +/*"0x"+Helper.formatAddress(a.getFileAddress())+": "+*/ Helper.bytesToHexString((a instanceof GraphSourceItemContainer) ? ((GraphSourceItemContainer) a).getHeaderBytes() : a.getBytes(version)) + "\r\n"; + ret += "" +/*"0x"+Helper.formatAddress(a.getFileAddress())+": "+*/ Helper.bytesToHexString(a.getBytes(version)) + "\r\n"; } offset = a.getAddress(); - - if((!(a.ignored)) && (a instanceof GraphSourceItemContainer)){ - if(!containers.containsKey(((GraphSourceItemContainer)a).getEndAddress())){ - containers.put(((GraphSourceItemContainer)a).getEndAddress(), new ArrayList()); - } - containers.get(((GraphSourceItemContainer)a).getEndAddress()).add((GraphSourceItemContainer)a); - } - - if(containers.containsKey(offset)){ - for(int i=0;i sizes = cnt.getContainerSizes(); + long addr = ((Action) cnt).getAddress() + cnt.getHeaderSize(); + for (Long size : sizes) { + addr += size; + if (size == 0) { + continue; + } + if (!containers.containsKey(addr)) { + containers.put(addr, new ArrayList()); + } + containers.get(addr).add(cnt); } } - + + if (containers.containsKey(offset)) { + for (int i = 0; i < containers.get(offset).size(); i++) { + ret += "}\r\n"; + GraphSourceItemContainer cnt = containers.get(offset).get(i); + int cntPos = containersPos.get(cnt); + ret += cnt.getASMSourceBetween(cntPos); + cntPos++; + containersPos.put(cnt, cntPos); + } + } + if (importantOffsets.contains(offset)) { ret += "loc" + Helper.formatAddress(offset) + ":"; } - - + + if (a.replaceWith != null) { - ret += Highlighting.hilighOffset("", offset) + a.replaceWith.getASMSource(list,importantOffsets, constantPool, version, hex) + "\r\n"; + ret += Highlighting.hilighOffset("", offset) + a.replaceWith.getASMSource(list, importantOffsets, constantPool, version, hex) + "\r\n"; } else if (a.ignored) { int len = 0; if (pos + 1 < list.size()) { - len = (int) (((Action)(list.get(pos + 1))).getAddress() - a.getAddress()); + len = (int) (((Action) (list.get(pos + 1))).getAddress() - a.getAddress()); } else { len = a.getBytes(version).length; } @@ -441,27 +456,37 @@ public class Action implements GraphSourceItem { } } else { if (a.beforeInsert != null) { - ret += a.beforeInsert.getASMSource(list,importantOffsets, constantPool, version, hex) + "\r\n"; + ret += a.beforeInsert.getASMSource(list, importantOffsets, constantPool, version, hex) + "\r\n"; } //if (!(a instanceof ActionNop)) { - String add=""; - if(a instanceof ActionIf){ - add = " change: "+((ActionIf)a).getJumpOffset(); - } - if(a instanceof ActionJump){ - add = " change: "+((ActionJump)a).getJumpOffset(); - } - add="; ofs"+Helper.formatAddress(offset)+add; - add=""; - ret += Highlighting.hilighOffset("", offset) + a.getASMSourceReplaced(list,importantOffsets, constantPool, version, hex) + (a.ignored ? "; ignored" : "") +add + "\r\n"; + String add = ""; + if (a instanceof ActionIf) { + add = " change: " + ((ActionIf) a).getJumpOffset(); + } + if (a instanceof ActionJump) { + add = " change: " + ((ActionJump) a).getJumpOffset(); + } + add = "; ofs" + Helper.formatAddress(offset) + add; + add = ""; + ret += Highlighting.hilighOffset("", offset) + a.getASMSourceReplaced(list, importantOffsets, constantPool, version, hex) + (a.ignored ? "; ignored" : "") + add + "\r\n"; //} if (a.afterInsert != null) { - ret += a.afterInsert.getASMSource(list,importantOffsets, constantPool, version, hex) + "\r\n"; + ret += a.afterInsert.getASMSource(list, importantOffsets, constantPool, version, hex) + "\r\n"; } } offset += a.getBytes(version).length; } + if (containers.containsKey(offset)) { + for (int i = 0; i < containers.get(offset).size(); i++) { + ret += "}\r\n"; + GraphSourceItemContainer cnt = containers.get(offset).get(i); + int cntPos = containersPos.get(cnt); + ret += cnt.getASMSourceBetween(cntPos); + cntPos++; + containersPos.put(cnt, cntPos); + } + } if (importantOffsets.contains(offset)) { ret += "loc" + Helper.formatAddress(offset) + ":\r\n"; } @@ -517,12 +542,12 @@ public class Action implements GraphSourceItem { * @return address */ public static long ip2adr(List actions, int ip, int version) { - /* List actions=new ArrayList(); - for(GraphSourceItem s:sources){ - if(s instanceof Action){ - actions.add((Action)s); - } - }*/ + /* List actions=new ArrayList(); + for(GraphSourceItem s:sources){ + if(s instanceof Action){ + actions.add((Action)s); + } + }*/ if (ip >= actions.size()) { if (actions.isEmpty()) { return 0; @@ -655,10 +680,10 @@ public class Action implements GraphSourceItem { } public static List actionsPartToTree(HashMap registerNames, HashMap variables, HashMap functions, Stack stack, List src, int start, int end, int version) { - List actions=new ArrayList(); - for(GraphSourceItem s:src){ - if(s instanceof Action){ - actions.add((Action)s); + List actions = new ArrayList(); + for (GraphSourceItem s : src) { + if (s instanceof Action) { + actions.add((Action) s); } } if (start < actions.size() && (end > 0) && (start > 0)) { @@ -720,17 +745,26 @@ public class Action implements GraphSourceItem { ip++; continue; } - - if(action instanceof GraphSourceItemContainer){ - long endAddr=((GraphSourceItemContainer)action).getEndAddress(); - int endip=adr2ip(actions, endAddr, version); + + if (action instanceof GraphSourceItemContainer) { + GraphSourceItemContainer cnt = (GraphSourceItemContainer) action; //List out=actionsPartToTree(new HashMap(), new HashMap(),new HashMap(), new Stack(), src, ip+1,endip-1 , version); - List out=ActionGraph.translateViaGraph(registerNames, variables, functions, src.subList(ip+1, endip), version); - ((GraphSourceItemContainer)action).translateContainer(out, stack, output, registerNames, variables, functions); - ip=endip; + long endAddr = action.getAddress() + cnt.getHeaderSize(); + List> outs = new ArrayList>(); + for (long size : cnt.getContainerSizes()) { + if (size == 0) { + outs.add(new ArrayList()); + continue; + } + List out = ActionGraph.translateViaGraph(registerNames, variables, functions, src.subList(adr2ip(actions, endAddr, version), adr2ip(actions, endAddr + size, version)), version); + outs.add(out); + endAddr += size; + } + ((GraphSourceItemContainer) action).translateContainer(outs, stack, output, registerNames, variables, functions); + ip = adr2ip(actions, endAddr, version); continue; } - + //System.out.println(" ip "+ip+" "+action); //return in for..in if ((action instanceof ActionPush) && (((ActionPush) action).values.size() == 1) && (((ActionPush) action).values.get(0) instanceof Null)) { @@ -758,26 +792,26 @@ public class Action implements GraphSourceItem { EnumerateTreeItem en = (EnumerateTreeItem) stack.peek(); inItem = en.object; continue; - } else if (action instanceof ActionTry) { - ActionTry atry = (ActionTry) action; - List tryCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.tryBody, version); - TreeItem catchName; - if (atry.catchInRegisterFlag) { - catchName = new DirectValueTreeItem(atry, -1, new RegisterNumber(atry.catchRegister), new ArrayList()); - } else { - catchName = new DirectValueTreeItem(atry, -1, atry.catchName, new ArrayList()); - } - List catchExceptions = new ArrayList(); - catchExceptions.add(catchName); - List> catchCommands = new ArrayList>(); - catchCommands.add(ActionGraph.translateViaGraph(registerNames, variables, functions, atry.catchBody, version)); - List finallyCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.finallyBody, version); - output.add(new TryTreeItem(tryCommands, catchExceptions, catchCommands, finallyCommands)); - } else /*if (action instanceof ActionWith) { - ActionWith awith = (ActionWith) action; - List withCommands = ActionGraph.translateViaGraph(registerNames, variables, functions,new ArrayList() , version); //TODO:parse with actions - output.add(new WithTreeItem(action, stack.pop(), withCommands)); - } else */if (action instanceof ActionStoreRegister) { + } else /*if (action instanceof ActionTry) { + ActionTry atry = (ActionTry) action; + List tryCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.tryBody, version); + TreeItem catchName; + if (atry.catchInRegisterFlag) { + catchName = new DirectValueTreeItem(atry, -1, new RegisterNumber(atry.catchRegister), new ArrayList()); + } else { + catchName = new DirectValueTreeItem(atry, -1, atry.catchName, new ArrayList()); + } + List catchExceptions = new ArrayList(); + catchExceptions.add(catchName); + List> catchCommands = new ArrayList>(); + catchCommands.add(ActionGraph.translateViaGraph(registerNames, variables, functions, atry.catchBody, version)); + List finallyCommands = ActionGraph.translateViaGraph(registerNames, variables, functions, atry.finallyBody, version); + output.add(new TryTreeItem(tryCommands, catchExceptions, catchCommands, finallyCommands)); + } else if (action instanceof ActionWith) { + ActionWith awith = (ActionWith) action; + List withCommands = ActionGraph.translateViaGraph(registerNames, variables, functions,new ArrayList() , version); //TODO:parse with actions + output.add(new WithTreeItem(action, stack.pop(), withCommands)); + } else */ if (action instanceof ActionStoreRegister) { if ((ip + 1 <= end) && (actions.get(ip + 1) instanceof ActionPop)) { action.translate(localData, stack, output); stack.pop(); @@ -1114,15 +1148,17 @@ public class Action implements GraphSourceItem { public static List removeNops(long address, List actions, int version, long swfPos) { List ret = actions; - if(true){ + if (true) { //return ret; } String s = null; try { s = Highlighting.stripHilights(Action.actionsToString(address, ret, null, version, false, swfPos)); + ret = ASMParser.parse(address, swfPos, true, new ByteArrayInputStream(s.getBytes()), SWF.DEFAULT_VERSION); } catch (Exception ex) { Logger.getLogger(SWFInputStream.class.getName()).log(Level.SEVERE, "parsing error", ex); + Helper.writeFile("as2.txt", s.getBytes()); } return ret; } @@ -1148,6 +1184,6 @@ public class Action implements GraphSourceItem { } public String getASMSourceReplaced(List container, List knownAddreses, List constantPool, int version, boolean hex) { - return getASMSource(container,knownAddreses, constantPool, version, hex); + return getASMSource(container, knownAddreses, constantPool, version, hex); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java index 0237c86bf..8d4633297 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraph.java @@ -64,7 +64,7 @@ public class ActionGraph extends Graph { } public static List translateViaGraph(HashMap registerNames, HashMap variables, HashMap functions, List code, int version) { - + ActionGraph g = new ActionGraph(code, registerNames, variables, functions, version); List localData = new ArrayList(); localData.add(registerNames); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java index b6e0f889c..35645fd9c 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/ActionGraphSource.java @@ -1,6 +1,5 @@ package com.jpexs.decompiler.flash.action; -import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.graph.GraphSource; import com.jpexs.decompiler.flash.graph.GraphSourceItem; import com.jpexs.decompiler.flash.graph.GraphTargetItem; @@ -28,8 +27,6 @@ public class ActionGraphSource extends GraphSource { return actions; } - - public ActionGraphSource(List actions, int version, HashMap registerNames, HashMap variables, HashMap functions) { this.actions = actions; this.version = version; @@ -61,93 +58,92 @@ public class ActionGraphSource extends GraphSource { public List translatePart(List localData, Stack stack, int start, int end) { return (Action.actionsPartToTree(registerNames, variables, functions, stack, actions, start, end, version)); } - - private List posCache=null; + private List posCache = null; - private void rebuildCache(){ + private void rebuildCache() { posCache = new ArrayList(); - for(int i=0;iaddr){ - System.err.println("lastAddr="+lastAddr+" addr="+addr+" curAddr="+curAdr); - int contPos=adr2pos(lastAddr); - System.err.println("/insadr2po"); - GraphSourceItem src=get(contPos); - if(src instanceof ActionContainer){ - ActionContainer cnt=(ActionContainer)src; - return new ActionGraphSource(cnt.getActions(), version, registerNames, variables, functions).adr2pos(addr); - }else{ - return -1; - } - } - lastAddr=curAdr; - } - return -1; - }*/ + + /* public int adr2posInside(long addr){ + long lastAddr=0; + if(addr==0){ + return 0; + } + for(int i=0;iaddr){ + System.err.println("lastAddr="+lastAddr+" addr="+addr+" curAddr="+curAdr); + int contPos=adr2pos(lastAddr); + System.err.println("/insadr2po"); + GraphSourceItem src=get(contPos); + if(src instanceof ActionContainer){ + ActionContainer cnt=(ActionContainer)src; + return new ActionGraphSource(cnt.getActions(), version, registerNames, variables, functions).adr2pos(addr); + }else{ + return -1; + } + } + lastAddr=curAdr; + } + return -1; + }*/ @Override public int adr2pos(long adr) { - if(posCache==null){ + if (posCache == null) { rebuildCache(); } - if(adr==0){ - return 0; - } - int ret=posCache.indexOf((Long)adr); - if(ret==-1){ - if(!posCache.isEmpty() && (adr>posCache.get(posCache.size()-1))){ - return size(); - } - //ret = adr2posInside(adr); - if(ret==-1){ - Logger.getLogger(ActionGraphSource.class.getName()).log(Level.SEVERE, "Address loc"+Helper.formatAddress(adr)+" not found"); - /*System.err.println("Addr loc"+Helper.formatAddress(adr)+" not found"); - int pos=0; - for(long l:posCache){ - System.err.println("ip "+pos+" action "+get(pos).toString()+" loc"+Helper.formatAddress(l)); - pos++; - }*/ - } - } - return ret; - /*int pos = 0; - long lastAddr = 0; - for (Action a : actions) { - lastAddr = a.getAddress(); - System.err.println("ip "+pos+" addr "+Helper.formatAddress(lastAddr)); - if (lastAddr == adr) { - return pos; - } - - pos++; - } - if (adr > lastAddr) { - return actions.size(); - } if (adr == 0) { return 0; } - //throw new RuntimeException("Address "+Helper.formatAddress(adr)+" not found"); - return -1;*/ + int ret = posCache.indexOf((Long) adr); + if (ret == -1) { + if (!posCache.isEmpty() && (adr > posCache.get(posCache.size() - 1))) { + return size(); + } + //ret = adr2posInside(adr); + if (ret == -1) { + Logger.getLogger(ActionGraphSource.class.getName()).log(Level.SEVERE, "Address loc" + Helper.formatAddress(adr) + " not found"); + /*System.err.println("Addr loc"+Helper.formatAddress(adr)+" not found"); + int pos=0; + for(long l:posCache){ + System.err.println("ip "+pos+" action "+get(pos).toString()+" loc"+Helper.formatAddress(l)); + pos++; + }*/ + } + } + return ret; + /*int pos = 0; + long lastAddr = 0; + for (Action a : actions) { + lastAddr = a.getAddress(); + System.err.println("ip "+pos+" addr "+Helper.formatAddress(lastAddr)); + if (lastAddr == adr) { + return pos; + } + + pos++; + } + if (adr > lastAddr) { + return actions.size(); + } + if (adr == 0) { + return 0; + } + //throw new RuntimeException("Address "+Helper.formatAddress(adr)+" not found"); + return -1;*/ } @Override public long pos2adr(int pos) { - GraphSourceItem si=actions.get(pos); - if(si instanceof Action){ - return ((Action)si) .getAddress();//Action.ip2adr(actions, pos, version); + GraphSourceItem si = actions.get(pos); + if (si instanceof Action) { + return ((Action) si).getAddress();//Action.ip2adr(actions, pos, version); } return 0; } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java index b0767a63c..4302cf8fb 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java @@ -316,6 +316,7 @@ public class ActionPanel extends JPanel implements ActionListener { setHex(hexButton.isSelected()); } else if (e.getActionCommand().equals("SAVEACTION")) { try { + Helper.writeFile("as.txt", editor.getText().getBytes()); src.setActions(ASMParser.parse(0, src.getPos(), true, new ByteArrayInputStream(editor.getText().getBytes()), SWF.DEFAULT_VERSION), SWF.DEFAULT_VERSION); setSource(this.src); JOptionPane.showMessageDialog(this, "Code successfully saved"); diff --git a/trunk/src/com/jpexs/decompiler/flash/action/parser/ASMParser.java b/trunk/src/com/jpexs/decompiler/flash/action/parser/ASMParser.java index 6f191e583..9503cb5fc 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/parser/ASMParser.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/parser/ASMParser.java @@ -19,17 +19,18 @@ package com.jpexs.decompiler.flash.action.parser; import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.flashlite.ActionFSCommand2; import com.jpexs.decompiler.flash.action.flashlite.ActionStrictMode; -import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.action.special.ActionNop; import com.jpexs.decompiler.flash.action.swf3.*; import com.jpexs.decompiler.flash.action.swf4.*; import com.jpexs.decompiler.flash.action.swf5.*; import com.jpexs.decompiler.flash.action.swf6.*; import com.jpexs.decompiler.flash.action.swf7.*; +import com.jpexs.decompiler.flash.graph.GraphSourceItemContainer; import com.jpexs.decompiler.flash.helpers.Helper; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Stack; import java.util.logging.Level; @@ -39,7 +40,8 @@ public class ASMParser { public static List parse(long containerSWFOffset, boolean ignoreNops, List