| /* |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| |
| package flash.swf; |
| |
| import flash.swf.Dictionary; |
| import flash.swf.actions.*; |
| import flash.swf.debug.DebugModule; |
| import flash.swf.debug.LineRecord; |
| import flash.swf.tags.*; |
| import flash.swf.types.*; |
| import flash.util.IntMap; |
| |
| import java.io.*; |
| import java.net.*; |
| import java.util.*; |
| |
| /** |
| * Represents SWF metadata, which should not be confuses with AS3 |
| * metadata. |
| */ |
| public final class MovieMetaData extends TagHandler |
| { |
| public MovieMetaData(byte[] swf, byte[] swd) |
| { |
| this(new ByteArrayInputStream(swf), new ByteArrayInputStream(swd)); |
| } |
| |
| |
| public MovieMetaData(InputStream swf, InputStream swd) |
| { |
| try |
| { |
| init(); |
| TagDecoder p = new TagDecoder(swf, swd); |
| parse(p); |
| } |
| catch (IOException ex) |
| { |
| } |
| } |
| |
| public MovieMetaData(String u) |
| { |
| try |
| { |
| init(); |
| URL url = new URL(u); |
| InputStream in = url.openStream(); |
| TagDecoder p = new TagDecoder(in, url); |
| parse(p); |
| } |
| catch (MalformedURLException ex) |
| { |
| } |
| catch (IOException ex) |
| { |
| } |
| } |
| |
| private void init() |
| { |
| actions = new IntMap(); |
| modules = new IntMap(); |
| functionNames = new IntMap(); |
| functionSizes = new IntMap(); |
| functionLines = new IntMap(); |
| preciseLines = new IntMap(); |
| mxml = new HashMap<String, DebugModule>(); |
| |
| pool = null; |
| skipOffsets = new ArrayList<Integer>(); |
| } |
| |
| private void parse(TagDecoder p) throws IOException |
| { |
| p.setKeepOffsets(true); |
| p.parse(this); |
| |
| Collections.sort(skipOffsets); |
| className = null; |
| } |
| |
| private Dictionary dict; |
| private Header header; |
| |
| // given an offset, what's the bytecode? |
| public IntMap actions; |
| |
| // given an offset, what debug module it's in? |
| public IntMap modules; |
| |
| // given an offset, what function it's in? |
| public IntMap functionNames; |
| public IntMap functionSizes; |
| public IntMap functionLines; |
| public IntMap preciseLines; |
| |
| // MXML DebugModule |
| public Map<String, DebugModule> mxml; |
| |
| // offsets that we don't want to profile |
| public List<Integer> skipOffsets; |
| |
| // temporarily store AS2 class name... |
| private String className; |
| |
| private String[] pool; |
| |
| public DebugModule getDebugModule(int offset) |
| { |
| DebugModule d = (DebugModule) modules.get(offset); |
| if (d == null) |
| { |
| return null; |
| } |
| else |
| { |
| return d; |
| } |
| } |
| |
| public String getFunctionName(int offset) |
| { |
| return (String) functionNames.get(offset); |
| } |
| |
| public Iterator getFunctionLines() |
| { |
| return preciseLines.iterator(); |
| } |
| |
| public Integer getOpCode(int offset) |
| { |
| return (Integer) actions.get(offset); |
| } |
| |
| protected Integer getFunctionLineNumber(int offset) |
| { |
| return (Integer) functionLines.get(offset); |
| } |
| |
| protected boolean isFunction(int offset) |
| { |
| String s = getFunctionName(offset); |
| return (s != null); |
| } |
| |
| public void setDecoderDictionary(Dictionary dict) |
| { |
| this.dict = dict; |
| } |
| |
| public void header(Header h) |
| { |
| header = h; |
| } |
| |
| public void defineButton(DefineButton tag) |
| { |
| String[] temp = pool; |
| collectActions(tag.condActions[0].actionList); |
| pool = temp; |
| } |
| |
| public void doAction(DoAction tag) |
| { |
| String[] temp = pool; |
| collectActions(tag.actionList); |
| pool = temp; |
| } |
| |
| public void placeObject2(PlaceObject tag) |
| { |
| collectClipActions(tag.clipActions); |
| } |
| |
| public void placeObject3(PlaceObject tag) |
| { |
| collectClipActions(tag.clipActions); |
| } |
| |
| public void defineButton2(DefineButton tag) |
| { |
| collectCondActions(tag.condActions); |
| } |
| |
| public void defineSprite(DefineSprite tag) |
| { |
| collectSpriteActions(tag.tagList); |
| } |
| |
| public void doInitAction(DoInitAction tag) |
| { |
| if (header.version > 6 && tag.sprite != null) |
| { |
| String __Packages = idRef(tag.sprite); |
| className = (__Packages != null && __Packages.startsWith("__Packages")) ? __Packages.substring(11) : null; // length("__Packages.") = 11 |
| |
| if (isRegisterClass(tag.actionList)) |
| { |
| DebugModule dm = new DebugModule(); |
| // C: We actually want the class name here, not the linkage ID. |
| dm.name = "<" + __Packages + ".2>"; |
| // C: We want the class name as the second input argument. Fortunately, we don't |
| // really do anything with the source, so it's okay. |
| dm.setText("Object.registerClass(" + __Packages + ", " + __Packages + ");"); |
| dm.bitmap = 1; |
| |
| LineRecord lr = new LineRecord(1, dm); |
| |
| int startOffset = tag.actionList.getOffset(0); |
| dm.addOffset(lr, startOffset); |
| |
| tag.actionList.insert(startOffset, lr); |
| modules.put((int) (Math.random() * Integer.MAX_VALUE), dm); |
| } |
| } |
| |
| String[] temp = pool; |
| collectActions(tag.actionList); |
| pool = temp; |
| |
| className = null; |
| } |
| |
| private static final int[] regClassCall9 = new int[] |
| { |
| ActionConstants.sactionPush, // class name |
| ActionConstants.sactionGetVariable, |
| ActionConstants.sactionPush, // linkage id |
| ActionConstants.sactionPush, // 2 |
| ActionConstants.sactionPush, // Object |
| ActionConstants.sactionGetVariable, |
| ActionConstants.sactionPush, // registerClass |
| ActionConstants.sactionCallMethod, |
| ActionConstants.sactionPop |
| }; |
| |
| private static final int[] regClassCall10 = new int[] |
| { |
| ActionConstants.sactionConstantPool, // constant pool |
| ActionConstants.sactionPush, // class name |
| ActionConstants.sactionGetVariable, |
| ActionConstants.sactionPush, // linkage id |
| ActionConstants.sactionPush, // 2 |
| ActionConstants.sactionPush, // Object |
| ActionConstants.sactionGetVariable, |
| ActionConstants.sactionPush, // registerClass |
| ActionConstants.sactionCallMethod, |
| ActionConstants.sactionPop |
| }; |
| |
| // TODO: Use an evaluation stack to figure out the Object.registerClass() call. |
| public static final boolean isRegisterClass(ActionList actionList) |
| { |
| if (!hasLineRecord(actionList)) |
| { |
| int[] opcodes; |
| |
| if (actionList.size() == 9) |
| { |
| opcodes = regClassCall9; |
| } |
| else if (actionList.size() == 10) |
| { |
| opcodes = regClassCall10; |
| } |
| else |
| { |
| return false; |
| } |
| |
| for (int i = 0;i < opcodes.length;i++) |
| { |
| if (actionList.getAction(i).code != opcodes[i]) |
| { |
| return false; |
| } |
| else |
| { |
| // TODO: need to check the PUSH values... |
| } |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| String idRef(DefineTag tag) { return idRef(tag, dict); } |
| |
| public static String idRef(DefineTag tag, Dictionary d) |
| { |
| if (tag == null) |
| { |
| // if tag is null then it isn't in the dict -- the SWF is invalid. |
| // lets be lax and print something; Matador generates invalid SWF sometimes. |
| return "-1"; |
| } |
| else if (tag.name == null) |
| { |
| // just print the character id since no name was exported |
| return String.valueOf(d.getId(tag)); |
| } |
| else |
| { |
| return tag.name; |
| } |
| } |
| |
| private static final boolean hasLineRecord(ActionList c) |
| { |
| if (c == null || c.size() == 0) |
| { |
| return true; |
| } |
| |
| boolean result = false; |
| |
| for (int i=0; i < c.size() && !result; i++) |
| { |
| Action action = c.getAction(i); |
| |
| switch (action.code) |
| { |
| case ActionConstants.sactionDefineFunction: |
| case ActionConstants.sactionDefineFunction2: |
| result = result || hasLineRecord(((DefineFunction) action).actionList); |
| break; |
| case ActionList.sactionLineRecord: |
| result = true; |
| break; |
| } |
| } |
| |
| return result; |
| } |
| |
| private void collectSpriteActions(TagList s) |
| { |
| String[] temp; |
| |
| int len = s.tags.size(); |
| for (int i = 0; i < len; i++) |
| { |
| Tag t = s.tags.get(i); |
| switch (t.code) |
| { |
| case TagValues.stagDoAction: |
| temp = pool; |
| collectActions(((DoAction) t).actionList); |
| pool = temp; |
| break; |
| case TagValues.stagDefineButton2: |
| collectCondActions(((DefineButton) t).condActions); |
| break; |
| case TagValues.stagDefineButton: |
| temp = pool; |
| collectActions(((DefineButton) t).condActions[0].actionList); |
| pool = temp; |
| break; |
| case TagValues.stagDoInitAction: |
| temp = pool; |
| collectActions(((DoInitAction) t).actionList); |
| pool = temp; |
| break; |
| case TagValues.stagDefineSprite: |
| collectSpriteActions(((DefineSprite) t).tagList); |
| break; |
| case TagValues.stagPlaceObject2: |
| collectClipActions(((PlaceObject) t).clipActions); |
| break; |
| } |
| } |
| } |
| |
| private DebugModule findDebugModule(ActionList c) |
| { |
| MFUCache modules = new MFUCache(); |
| |
| for (int i=0; i < c.size(); i++) |
| { |
| Action a = c.getAction(i); |
| |
| DebugModule temp = null; |
| |
| switch (a.code) |
| { |
| case ActionConstants.sactionDefineFunction: |
| case ActionConstants.sactionDefineFunction2: |
| temp = findDebugModule(((DefineFunction) a).actionList); |
| break; |
| case ActionList.sactionLineRecord: |
| if (((LineRecord)a).module != null) |
| { |
| temp = ((LineRecord)a).module; |
| } |
| break; |
| } |
| |
| if (temp != null) |
| { |
| modules.add(temp); |
| } |
| } |
| |
| // ActionList may have actions pointing to more than one debug module because of #include, etc. |
| // The majority wins. |
| |
| return modules.topModule; |
| } |
| |
| private static Integer[] codes = new Integer[256]; |
| static |
| { |
| for (int i=0; i < 256; i++) |
| { |
| codes[i] = new Integer(i); |
| } |
| } |
| |
| private void collectActions(ActionList c) |
| { |
| // assumption: ActionContext c is always not null! try-catch-finally may be busted. |
| if (c == null) |
| { |
| return; |
| } |
| |
| // interprets the actions. try to assign names to anonymous functions... |
| evalActions(c); |
| |
| DebugModule d = findDebugModule(c); |
| |
| String emptyMethodName = null; |
| |
| // loop again, this time, we register all the actions... |
| for (int i=0; i < c.size(); i++) |
| { |
| int ioffset = c.getOffset(i); |
| Action a = c.getAction(i); |
| |
| if (emptyMethodName != null && emptyMethodName.length() != 0) |
| { |
| functionNames.put(ioffset, emptyMethodName); |
| emptyMethodName = null; |
| } |
| |
| if (a.code == ActionList.sactionLineRecord) |
| { |
| LineRecord line = (LineRecord) a; |
| if (line.module != null) |
| { |
| d = line.module; |
| if (d.name.endsWith(".mxml")) |
| { |
| mxml.put(d.name, d); |
| } |
| } |
| |
| continue; |
| } |
| |
| if (a.code >= 256) |
| { |
| // something synthetic we don't care about |
| continue; |
| } |
| |
| actions.put(ioffset, codes[a.code]); |
| modules.put(ioffset, d); |
| |
| switch (a.code) |
| { |
| case ActionConstants.sactionDefineFunction: |
| case ActionConstants.sactionDefineFunction2: |
| DefineFunction f = (DefineFunction) a; |
| Integer size = new Integer(f.codeSize); |
| |
| if (f.actionList.size() == 0) |
| { |
| emptyMethodName = f.name; |
| } |
| else |
| { |
| Integer lineno = null; |
| |
| // map all the offsets in this function to the function name |
| for (int j=0; j < f.actionList.size(); j++) |
| { |
| int o = f.actionList.getOffset(j); |
| Action child = f.actionList.getAction(j); |
| if (child.code == ActionList.sactionLineRecord) |
| { |
| // also find out the first line number of this function |
| if (lineno == null) |
| lineno = new Integer(((LineRecord)child).lineno); |
| |
| preciseLines.put(o, new Integer( ((LineRecord)child).lineno )); |
| } |
| functionNames.put(o, f.name); |
| functionSizes.put(o, size); |
| } |
| |
| |
| // map all the offsets in this function to the first line number of this function. |
| for (int j=0; j < f.actionList.size(); j++) |
| { |
| int o = f.actionList.getOffset(j); |
| functionLines.put(o, lineno); |
| } |
| } |
| |
| collectActions(f.actionList); |
| break; |
| } |
| } |
| } |
| |
| private void collectCondActions(ButtonCondAction[] actions) |
| { |
| for (int i = 0; i < actions.length; i++) |
| { |
| collectActions(actions[i].actionList); |
| } |
| } |
| |
| private void collectClipActions(ClipActions actions) |
| { |
| if (actions != null) |
| { |
| Iterator it = actions.clipActionRecords.iterator(); |
| while (it.hasNext()) |
| { |
| ClipActionRecord record = (ClipActionRecord) it.next(); |
| collectActions(record.actionList); |
| } |
| } |
| } |
| |
| private static Object pop(Stack<Object> stack) |
| { |
| return (stack.isEmpty()) ? null : stack.pop(); |
| } |
| |
| private void evalActions(ActionList c) |
| { |
| try |
| { |
| walkActions(c, header.version, pool, className, skipOffsets); |
| } |
| catch(Throwable t) |
| { |
| } |
| } |
| |
| // data used in our walkActions routine |
| private static Object dummy = new Object(); |
| private static Object[] registers = new Object[256]; |
| static |
| { |
| for (int i = 0;i < 256;i++) |
| { |
| registers[i] = dummy; |
| } |
| } |
| |
| /** |
| * Walk the actions filling in the names of functions as we go. |
| * This is done by looking for DefineFunction's actions and then |
| * examining the content of the stack for a name. |
| * |
| * @param c list of actions to be traversed |
| * @param swfVersion version of swf file that housed the ActionList (just use 7 if you don't know) |
| * @param pool optional; constant pool for the list of actions |
| * @param className optional; used to locate a constructor function (i.e if funcName == className) |
| * @param profileOffsets optional; is filled with offsets if a call to a |
| * function named 'profile' is encountered. Can be null if caller is not |
| * interested in obtaining this information. |
| */ |
| public static void walkActions(ActionList c, int swfVersion, String[] pool, String className, List<Integer> profileOffsets) |
| { |
| // assumption: ActionContext c is always not null! try-catch-finally may be busted. |
| if (c == null) return; |
| |
| Stack<Object> evalStack = new Stack<Object>(); |
| HashMap<Object, Object> variables = new HashMap<Object, Object>(); |
| |
| // loop again, this time, we register all the actions... |
| int offset; |
| Action a; |
| |
| for (int i=0; i < c.size(); i++) |
| { |
| offset = c.getOffset(i); |
| a = c.getAction(i); |
| |
| switch (a.code) |
| { |
| // Flash 1 and 2 actions |
| case ActionConstants.sactionHasLength: |
| case ActionConstants.sactionNone: |
| case ActionConstants.sactionGotoFrame: |
| case ActionConstants.sactionGetURL: |
| case ActionConstants.sactionNextFrame: |
| case ActionConstants.sactionPrevFrame: |
| case ActionConstants.sactionPlay: |
| case ActionConstants.sactionStop: |
| case ActionConstants.sactionToggleQuality: |
| case ActionConstants.sactionStopSounds: |
| case ActionConstants.sactionWaitForFrame: |
| // Flash 3 Actions |
| case ActionConstants.sactionSetTarget: |
| case ActionConstants.sactionGotoLabel: |
| // no action |
| break; |
| |
| // Flash 4 Actions |
| case ActionConstants.sactionAdd: |
| case ActionConstants.sactionSubtract: |
| case ActionConstants.sactionMultiply: |
| case ActionConstants.sactionDivide: |
| case ActionConstants.sactionEquals: |
| case ActionConstants.sactionLess: |
| case ActionConstants.sactionAnd: |
| case ActionConstants.sactionOr: |
| case ActionConstants.sactionStringEquals: |
| case ActionConstants.sactionStringAdd: |
| case ActionConstants.sactionStringLess: |
| case ActionConstants.sactionMBStringLength: |
| case ActionConstants.sactionGetProperty: |
| // pop, pop, push |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionNot: |
| case ActionConstants.sactionStringLength: |
| case ActionConstants.sactionToInteger: |
| case ActionConstants.sactionCharToAscii: |
| case ActionConstants.sactionAsciiToChar: |
| case ActionConstants.sactionMBCharToAscii: |
| case ActionConstants.sactionMBAsciiToChar: |
| case ActionConstants.sactionRandomNumber: |
| // pop, push |
| break; |
| case ActionConstants.sactionGetVariable: |
| Object key = pop(evalStack); |
| if (variables.get(key) == null) |
| { |
| evalStack.push(key); |
| } |
| else |
| { |
| evalStack.push(variables.get(key)); |
| } |
| break; |
| case ActionConstants.sactionStringExtract: |
| case ActionConstants.sactionMBStringExtract: |
| // pop, pop, pop, push |
| pop(evalStack); |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionPush: |
| Push p = (Push) a; |
| Object o = p.value; |
| int type = Push.getTypeCode(o); |
| switch (type) |
| { |
| case Push.kPushStringType: |
| evalStack.push(o); |
| break; |
| case Push.kPushNullType: |
| evalStack.push("null"); |
| break; |
| case Push.kPushUndefinedType: |
| evalStack.push("undefined"); |
| break; |
| case Push.kPushRegisterType: |
| evalStack.push(registers[((Byte)o).intValue()&0xFF]); |
| break; |
| case Push.kPushConstant8Type: |
| case Push.kPushConstant16Type: |
| evalStack.push(pool[((Number) o).intValue()&0xFFFF]); |
| break; |
| case Push.kPushFloatType: |
| evalStack.push(o + "F"); |
| break; |
| case Push.kPushBooleanType: |
| case Push.kPushDoubleType: |
| case Push.kPushIntegerType: |
| evalStack.push(o); |
| break; |
| default: |
| evalStack.push("type" + type); |
| break; |
| } |
| break; |
| case ActionConstants.sactionIf: |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionPop: |
| case ActionConstants.sactionCall: |
| case ActionConstants.sactionGotoFrame2: |
| case ActionConstants.sactionSetTarget2: |
| case ActionConstants.sactionRemoveSprite: |
| case ActionConstants.sactionWaitForFrame2: |
| case ActionConstants.sactionTrace: |
| // pop |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionJump: |
| case ActionConstants.sactionEndDrag: |
| // no action |
| break; |
| case ActionConstants.sactionSetVariable: |
| key = pop(evalStack); |
| Object val = pop(evalStack); |
| variables.put(key, val); |
| break; |
| case ActionConstants.sactionGetURL2: |
| // pop, pop |
| pop(evalStack); |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionSetProperty: |
| case ActionConstants.sactionCloneSprite: |
| // pop, pop, pop |
| pop(evalStack); |
| pop(evalStack); |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionStartDrag: |
| // pop, pop, pop, if the 3rd pop is non-zero, pop, pop, pop, pop |
| pop(evalStack); |
| pop(evalStack); |
| Object obj = pop(evalStack); |
| if (Integer.parseInt(obj.toString()) != 0) |
| { |
| pop(evalStack); |
| pop(evalStack); |
| pop(evalStack); |
| pop(evalStack); |
| } |
| break; |
| case ActionConstants.sactionGetTime: |
| // push |
| evalStack.push(dummy); |
| break; |
| |
| // Flash 5 actions |
| case ActionConstants.sactionDelete: |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionDefineLocal: |
| // pop, pop |
| val = pop(evalStack); |
| key = pop(evalStack); |
| variables.put(key, val); |
| break; |
| case ActionConstants.sactionDefineFunction: |
| case ActionConstants.sactionDefineFunction2: |
| DefineFunction f = (DefineFunction) a; |
| |
| if (swfVersion > 6 && className != null) |
| { |
| if (f.name == null || f.name.length() == 0) |
| { |
| int depth = evalStack.size(); |
| if (depth != 0) |
| { |
| o = evalStack.peek(); |
| if (o == dummy) |
| { |
| f.name = ""; |
| } |
| else if (o != null) |
| { |
| f.name = o.toString(); |
| } |
| } |
| evalStack.push(dummy); |
| } |
| |
| if ("null".equals(f.name)) |
| { |
| f.name = ""; |
| } |
| |
| if (f.name == null || f.name.length() == 0) |
| { |
| // do nothing... it's an anonymous function! |
| } |
| else if (!className.endsWith(f.name)) |
| { |
| f.name = className + "." + f.name; |
| } |
| else |
| { |
| f.name = className + ".[constructor]"; |
| } |
| } |
| else |
| { |
| if (f.name == null || f.name.length() == 0) |
| { |
| StringBuilder buffer = new StringBuilder(); |
| int depth = evalStack.size(); |
| for (int k = depth - 1; k >= 0; k--) |
| { |
| o = evalStack.get(k); |
| if (o == dummy) |
| { |
| break; |
| } |
| else if (k == depth - 1) |
| { |
| buffer.append(o); |
| } |
| else |
| { |
| buffer.insert(0, '.'); |
| buffer.insert(0, o); |
| } |
| } |
| f.name = buffer.toString(); |
| |
| if (f.name != null && f.name.indexOf(".prototype.") == -1) |
| { |
| f.name = ""; |
| } |
| evalStack.push(dummy); |
| } |
| } |
| // evalActions(f.actions); |
| break; |
| case ActionConstants.sactionCallFunction: |
| Object function = pop(evalStack); |
| if (profileOffsets != null && "profile".equals(function)) |
| { |
| profileOffsets.add(new Integer(offset - 13)); // Push 1 |
| profileOffsets.add(new Integer(offset - 5)); // Push 'profile' |
| profileOffsets.add(new Integer(offset)); // CallFunction |
| profileOffsets.add(new Integer(offset + 1)); // Pop |
| } |
| int n = ((Number) pop(evalStack)).intValue(); |
| for (int k = 0; k < n; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionReturn: |
| // return function() { ... } doesn't push... |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionModulo: |
| // pop, push |
| break; |
| case ActionConstants.sactionNewObject: |
| pop(evalStack); |
| int num = ((Number) pop(evalStack)).intValue(); |
| for (int k = 0; k < num; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionDefineLocal2: |
| case ActionConstants.sactionDelete2: |
| case ActionConstants.sactionAdd2: |
| case ActionConstants.sactionLess2: |
| // pop |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionInitArray: |
| // pop, if the first pop is non-zero, keep popping |
| num = ((Number) pop(evalStack)).intValue(); |
| for (int k = 0; k < num; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionInitObject: |
| num = ((Number) pop(evalStack)).intValue() * 2; |
| for (int k = 0; k < num; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionTargetPath: |
| case ActionConstants.sactionEnumerate: |
| case ActionConstants.sactionToNumber: |
| case ActionConstants.sactionToString: |
| case ActionConstants.sactionTypeOf: |
| // no action |
| break; |
| case ActionConstants.sactionStoreRegister: |
| StoreRegister r = (StoreRegister) a; |
| registers[r.register] = evalStack.peek(); |
| break; |
| case ActionConstants.sactionEquals2: |
| // pop, pop, push |
| // if (evalStack.size() >= 2) |
| { |
| pop(evalStack); |
| } |
| break; |
| case ActionConstants.sactionPushDuplicate: |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionStackSwap: |
| // pop, pop, push, push |
| break; |
| case ActionConstants.sactionGetMember: |
| // pop, pop, concat, push |
| Object o1 = pop(evalStack); |
| Object o2 = pop(evalStack); |
| if (pool != null) |
| { |
| try |
| { |
| evalStack.push(pool[Integer.parseInt(o2.toString())] + "." + pool[Integer.parseInt(o1.toString())]); |
| } |
| catch (Exception ex) |
| { |
| if (o1 == dummy || o2 == dummy) |
| { |
| evalStack.push(dummy); |
| } |
| else |
| { |
| evalStack.push(o2 + "." + o1); |
| } |
| } |
| } |
| else |
| { |
| evalStack.push(o2 + "." + o1); |
| } |
| break; |
| case ActionConstants.sactionSetMember: |
| // pop, pop, pop |
| pop(evalStack); |
| pop(evalStack); |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionIncrement: |
| case ActionConstants.sactionDecrement: |
| break; |
| case ActionConstants.sactionCallMethod: |
| pop(evalStack); |
| pop(evalStack); |
| Object obj2 = pop(evalStack); |
| if (obj2 instanceof String) |
| { |
| try { |
| n = Integer.parseInt((String) obj2); |
| } |
| catch (NumberFormatException ex) |
| { |
| n = 1; |
| } |
| } |
| else |
| { |
| n = ((Number) obj2).intValue(); |
| } |
| for (int k = 0; k < n; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionNewMethod: |
| /*Object meth =*/ pop(evalStack); |
| /*Object cls =*/ pop(evalStack); |
| num = ((Number) pop(evalStack)).intValue(); |
| for (int k = 0; k < num; k++) |
| { |
| pop(evalStack); |
| } |
| evalStack.push(dummy); |
| break; |
| case ActionConstants.sactionWith: |
| // pop |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionConstantPool: |
| pool = ((ConstantPool) a).pool; |
| // no action |
| break; |
| case ActionConstants.sactionStrictMode: |
| break; |
| |
| case ActionConstants.sactionBitAnd: |
| case ActionConstants.sactionBitOr: |
| case ActionConstants.sactionBitLShift: |
| // pop, push |
| break; |
| case ActionConstants.sactionBitXor: |
| case ActionConstants.sactionBitRShift: |
| case ActionConstants.sactionBitURShift: |
| pop(evalStack); |
| break; |
| |
| // Flash 6 actions |
| case ActionConstants.sactionInstanceOf: |
| pop(evalStack); |
| break; |
| case ActionConstants.sactionEnumerate2: |
| // pop, push, more pushes? |
| break; |
| case ActionConstants.sactionStrictEquals: |
| case ActionConstants.sactionGreater: |
| case ActionConstants.sactionStringGreater: |
| pop(evalStack); |
| break; |
| |
| // FEATURE_EXCEPTIONS |
| case ActionConstants.sactionTry: |
| // do nothing |
| break; |
| case ActionConstants.sactionThrow: |
| pop(evalStack); |
| break; |
| |
| // FEATURE_AS2_INTERFACES |
| case ActionConstants.sactionCastOp: |
| break; |
| case ActionConstants.sactionImplementsOp: |
| break; |
| |
| // Reserved for Quicktime |
| case ActionConstants.sactionQuickTime: |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| } |
| |
| class MFUCache |
| { |
| HashMap<DebugModule, Integer> cache = new HashMap<DebugModule, Integer>(5); |
| DebugModule topModule; |
| int topCount; |
| |
| void add(DebugModule m) |
| { |
| Integer count = cache.get(m); |
| if (count == null) |
| { |
| count = new Integer(0); |
| } |
| count = new Integer(count.intValue() + 1); |
| cache.put(m, count); |
| |
| if (count.intValue() > topCount) |
| { |
| topCount = count.intValue(); |
| topModule = m; |
| } |
| } |
| |
| DebugModule getTopModule() |
| { |
| return topModule; |
| } |
| } |