| /* |
| * |
| * 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.actions.Push; |
| import flash.swf.actions.StoreRegister; |
| import flash.swf.actions.StrictMode; |
| import flash.swf.actions.Label; |
| import flash.swf.actions.WaitForFrame; |
| import flash.swf.types.ActionList; |
| import flash.swf.debug.LineRecord; |
| import flash.swf.debug.RegisterRecord; |
| |
| import java.util.List; |
| import java.util.ArrayList; |
| import java.util.Iterator; |
| |
| /** |
| * This is a factory for decoding ActionScript bytecode. It keeps |
| * track of temporary information we need while decoding but can |
| * discard once we are done. |
| */ |
| final public class ActionFactory |
| { |
| public static final Object UNDEFINED = new Object() |
| { |
| public String toString() |
| { |
| return "undefined"; |
| } |
| }; |
| |
| public static final Object STACKTOP = new Object() |
| { |
| public String toString() |
| { |
| return "stack"; |
| } |
| }; |
| |
| /** flyweight action objects for 1-byte opcodes 0..7F */ |
| private static final Action[] actionFlyweights = new Action[0x80]; |
| private static final Push[] pushCpoolFlyweights = new Push[256]; |
| private static final Push[] pushRegisterFlyweights = new Push[256]; |
| private static final StoreRegister[] storeRegisterFlyweights = new StoreRegister[256]; |
| private static final Push pushTrueFlyweight = new Push(Boolean.TRUE); |
| private static final Push pushFalseFlyweight = new Push(Boolean.FALSE); |
| private static final Push pushUndefinedFlyweight = new Push(UNDEFINED); |
| private static final Push pushNullFlyweight = new Push(null); |
| private static final Push pushFloat0Flyweight = new Push(new Float(0)); |
| private static final Push pushInteger0Flyweight = new Push(new Integer(0)); |
| private static final Push pushDouble0Flyweight = new Push(new Double(0)); |
| private static final Action callFlyweight = new Action(ActionConstants.sactionCall); |
| private static final StrictMode strictTrueFlyweight = new StrictMode(true); |
| private static final StrictMode strictFalseFlyweight = new StrictMode(false); |
| |
| static |
| { |
| for (int i=0; i < 0x80; i++) |
| { |
| ActionFactory.actionFlyweights[i] = new Action(i); |
| } |
| |
| for (int i=0; i < 256; i++) |
| { |
| ActionFactory.pushRegisterFlyweights[i] = new Push(new Byte((byte)i)); |
| ActionFactory.pushCpoolFlyweights[i] = new Push(new Short((short)i)); |
| ActionFactory.storeRegisterFlyweights[i] = new StoreRegister(i); |
| } |
| } |
| |
| public static Action createAction(int code) |
| { |
| return actionFlyweights[code]; |
| } |
| |
| public static Push createPushCpool(int index) |
| { |
| return (index < pushCpoolFlyweights.length) |
| ? pushCpoolFlyweights[index] |
| : new Push(new Short((short)index)); |
| } |
| |
| public static Push createPush(String s) |
| { |
| return new Push(s); |
| } |
| |
| public static Push createPush(float fvalue) |
| { |
| return fvalue == 0 |
| ? pushFloat0Flyweight |
| : new Push(new Float(fvalue)); |
| } |
| |
| public static Push createPushNull() |
| { |
| return pushNullFlyweight; |
| } |
| |
| public static Push createPushUndefined() |
| { |
| return pushUndefinedFlyweight; |
| } |
| |
| public static Push createPushRegister(int regno) |
| { |
| return pushRegisterFlyweights[regno]; |
| } |
| |
| public static Push createPush(boolean b) |
| { |
| return (b ? pushTrueFlyweight : pushFalseFlyweight); |
| } |
| |
| public static Push createPush(double dvalue) |
| { |
| return dvalue == 0 |
| ? pushDouble0Flyweight |
| : new Push(new Double(dvalue)); |
| } |
| |
| public static Push createPush(int ivalue) |
| { |
| return ivalue == 0 |
| ? pushInteger0Flyweight |
| : new Push(new Integer(ivalue)); |
| } |
| |
| public static StoreRegister createStoreRegister(int register) |
| { |
| return storeRegisterFlyweights[register]; |
| } |
| |
| public static Action createCall() |
| { |
| return callFlyweight; |
| } |
| |
| public static StrictMode createStrictMode(boolean mode) |
| { |
| return mode ? strictTrueFlyweight : strictFalseFlyweight; |
| } |
| |
| private final int startOffset; |
| private final int startCount; |
| private final Action[] actions; |
| private final Label[] labels; |
| private final LineRecord[] lines; |
| private final RegisterRecord[] registers; |
| private final int[] actionOffsets; |
| private int count; |
| private List<SkipEntry> skipRecords; |
| |
| public ActionFactory(int length, int startOffset, int startCount) |
| { |
| this.startOffset = startOffset; |
| this.startCount = startCount; |
| |
| labels = new Label[length+1]; // length+1 to handle labels after last action |
| lines = new LineRecord[length]; |
| registers = new RegisterRecord[length]; |
| actions = new Action[length]; |
| actionOffsets = new int[length+1]; |
| skipRecords = new ArrayList<SkipEntry>(); |
| } |
| |
| public void setLine(int offset, LineRecord line) |
| { |
| int i = offset-startOffset; |
| if (lines[i] == null) |
| count++; |
| lines[i] = line; |
| } |
| |
| public void setRegister(int offset, RegisterRecord record) |
| { |
| int i = offset-startOffset; |
| if (registers[i] == null) |
| count++; |
| registers[i] = record; |
| } |
| |
| public void setAction(int offset, Action a) |
| { |
| int i = offset-startOffset; |
| if (actions[i] == null) |
| count++; |
| actions[i] = a; |
| } |
| |
| public Label getLabel(int target) |
| { |
| int i = target-startOffset; |
| Label label = null; |
| |
| // See http://bugs.adobe.com/jira/browse/SDK-23169, for a SWF |
| // where i is negative. This seems like a broken SWF, because |
| // a branch is trying to jump before the start of the |
| // DoInitAction. To avoid a ArrayIndexOutOfBoundsException, |
| // do a range check. |
| if ((i >= 0) && (i < labels.length)) |
| { |
| label = labels[i]; |
| |
| if (label == null) |
| { |
| labels[i] = label = new Label(); |
| count++; |
| } |
| } |
| |
| return label; |
| } |
| |
| public void setActionOffset(int actionCount, int offset) |
| { |
| actionOffsets[actionCount-startCount] = offset; |
| } |
| |
| /** |
| * now that everything has been decoded, build a single actionlist |
| * with the labels and jump targets merged in. |
| * @param keepOffsets |
| * @return |
| */ |
| public ActionList createActionList(boolean keepOffsets) |
| { |
| processSkipEntries(); |
| |
| ActionList list = new ActionList(keepOffsets); |
| list.grow(count); |
| Action a; |
| int length = actions.length; |
| if (keepOffsets) |
| { |
| for (int i=0; i < length; i++) |
| { |
| int offset = startOffset+i; |
| if ((a=actions[i]) != null) |
| list.insert(offset, a); |
| if ((a=lines[i]) != null) |
| list.insert(offset, a); |
| if ((a=registers[i]) != null) |
| list.insert(offset, a); |
| if ((a=labels[i]) != null) |
| list.insert(offset, a); |
| } |
| if ((a=labels[length]) != null) |
| list.insert(startOffset+length, a); |
| } |
| else |
| { |
| for (int i=0; i < length; i++) |
| { |
| if ((a=labels[i]) != null) |
| list.append(a); |
| if ((a=lines[i]) != null) |
| list.append(a); |
| if ((a=registers[i]) != null) |
| list.append(a); |
| if ((a=actions[i]) != null) |
| list.append(a); |
| } |
| if ((a=labels[length]) != null) |
| list.append(a); |
| } |
| return list; |
| } |
| |
| private static class SkipEntry |
| { |
| WaitForFrame action; |
| int skipTarget; |
| |
| public SkipEntry(WaitForFrame action, int skipTarget) |
| { |
| this.action = action; |
| this.skipTarget = skipTarget; |
| } |
| } |
| |
| /** |
| * postprocess skip records now that we now the offset of each encoded action |
| */ |
| private void processSkipEntries() |
| { |
| for (Iterator<SkipEntry> i = skipRecords.iterator(); i.hasNext();) |
| { |
| SkipEntry skipRecord = i.next(); |
| int labelOffset = actionOffsets[skipRecord.skipTarget-startCount]; |
| skipRecord.action.skipTarget = getLabel(labelOffset); |
| } |
| } |
| |
| public void addSkipEntry(WaitForFrame a, int skipTarget) |
| { |
| skipRecords.add(new SkipEntry(a, skipTarget)); |
| } |
| |
| } |