| /* |
| * 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.tools.debugger.concrete; |
| |
| import java.io.InputStream; |
| import java.text.ParseException; |
| import java.text.SimpleDateFormat; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import flash.tools.debugger.Isolate; |
| import flash.tools.debugger.SourceLocator; |
| import flash.tools.debugger.SuspendReason; |
| import flash.tools.debugger.SwfInfo; |
| import flash.tools.debugger.Value; |
| import flash.tools.debugger.VariableAttribute; |
| import flash.tools.debugger.VariableType; |
| import flash.tools.debugger.events.BreakEvent; |
| import flash.tools.debugger.events.ConsoleErrorFault; |
| import flash.tools.debugger.events.DebugEvent; |
| import flash.tools.debugger.events.DivideByZeroFault; |
| import flash.tools.debugger.events.ExceptionFault; |
| import flash.tools.debugger.events.FaultEvent; |
| import flash.tools.debugger.events.FileListModifiedEvent; |
| import flash.tools.debugger.events.InvalidWithFault; |
| import flash.tools.debugger.events.IsolateCreateEvent; |
| import flash.tools.debugger.events.IsolateExitEvent; |
| import flash.tools.debugger.events.ProtoLimitFault; |
| import flash.tools.debugger.events.RecursionLimitFault; |
| import flash.tools.debugger.events.ScriptTimeoutFault; |
| import flash.tools.debugger.events.StackUnderFlowFault; |
| import flash.tools.debugger.events.SwfLoadedEvent; |
| import flash.tools.debugger.events.SwfUnloadedEvent; |
| import flash.tools.debugger.events.TraceEvent; |
| import flash.util.Trace; |
| |
| /** |
| * Implements the receiving and updating of debug state from the socket |
| * connection of the Flash Player. |
| */ |
| public class DManager implements DProtocolNotifierIF, SourceLocator { |
| private final HashMap<String, String> m_parms; |
| |
| private final HashMap<Integer, DIsolate> m_isolates; /* |
| * WARNING: accessed |
| * from multiple threads |
| */ |
| |
| /** |
| * The currently active isolate or worker |
| */ |
| private Isolate m_activeIsolate = DEFAULT_ISOLATE; |
| |
| private LinkedList<DebugEvent> m_event; /* |
| * our event queue; WARNING: |
| * accessed from multiple threads |
| */ |
| private SourceLocator m_sourceLocator; |
| |
| |
| private boolean m_squelchEnabled; /* |
| * true if we are talking to a squelch |
| * enabled debug player |
| */ |
| private int m_playerVersion; /* |
| * player version number obtained from InVersion |
| * message; e.g. 9 for Flash Player 9.0 |
| */ |
| |
| private boolean m_sourceListModified; /* |
| * deprecated; indicates m_source has |
| * changed since last call to |
| * getSource(). WARNING: lock with |
| * synchronized (m_source) { ... } |
| */ |
| private byte[] m_actions; /* deprecated */ |
| private String[] m_lastConstantPool; /* deprecated */ |
| |
| // SWF/SWD fetching and parsing |
| private String m_uri; |
| private byte[] m_swf; // latest swf obtained from get swf |
| private byte[] m_swd; // latest swd obtained from get swd |
| |
| private Map<String, String> m_options = new HashMap<String, String>(); // Player |
| // options |
| // that |
| // have |
| // been |
| // queried |
| // by |
| // OutGetOption, |
| // and |
| // come |
| // back |
| // via |
| // InOption |
| |
| public static final String ARGUMENTS_MARKER = "$arguments"; //$NON-NLS-1$ |
| public static final String SCOPE_CHAIN_MARKER = "$scopechain"; //$NON-NLS-1$ |
| |
| private static final DIsolate DEFAULT_ISOLATE = DIsolate.DEFAULT_ISOLATE; |
| private Isolate m_inIsolate = DEFAULT_ISOLATE; |
| private final Object m_inIsolateLock; |
| private final Object m_activeIsolateLock; |
| private boolean m_wideLines; |
| private DManagerIsolateState m_mainState; |
| private HashMap<Integer, DManagerIsolateState> m_isolateState; |
| |
| |
| class DManagerIsolateState { |
| public DSuspendInfo m_suspendInfo; |
| public DSwfInfo m_lastSwfInfo; /* |
| * hack for syncing swfinfo records with |
| * incoming InScript messages |
| */ |
| public DVariable m_lastInGetVariable;/* |
| * hack for getVariable call to work |
| * with getters |
| */ |
| public boolean m_attachChildren; /* |
| * hack for getVariable call to work |
| * with getters |
| */ |
| public DVariable m_lastInCallFunction; /* hack for callFunction to work */ |
| public DVariable m_lastInBinaryOp; |
| private boolean m_executingPlayerCode; |
| private FaultEvent m_faultEventDuringPlayerCodeExecution; |
| |
| public final ArrayList<DLocation> m_breakpoints; /* |
| * WARNING: accessed |
| * from multiple threads |
| */ |
| public final Map<Integer, DModule> m_source; /* |
| * WARNING: accessed from |
| * multiple threads |
| */ |
| private final ArrayList<DSwfInfo> m_swfInfo; /* |
| * WARNING: accessed from |
| * multiple threads |
| */ |
| private final ArrayList<DWatch> m_watchpoints; /* |
| * WARNING: accessed |
| * from multiple threads |
| */ |
| |
| /** |
| * The currently active stack frames. |
| */ |
| public ArrayList<DStackContext> m_frames; |
| |
| /** |
| * The stack frames that were active the last time the player was |
| * suspended. |
| */ |
| public ArrayList<DStackContext> m_previousFrames; |
| |
| /** |
| * A list of all known variables in the player. Stored as a mapping from |
| * an object's id to its DValue. |
| */ |
| public Map<Long, DValue> m_values; |
| |
| /** |
| * A list of all known variables in the player from the previous time |
| * the player was suspended. Stored as a mapping from an object's id to |
| * its DValue. |
| */ |
| public Map<Long, DValue> m_previousValues; |
| |
| public DManagerIsolateState() { |
| m_source = new HashMap<Integer, DModule>(); |
| m_values = new HashMap<Long, DValue>(); |
| m_previousValues = new HashMap<Long, DValue>(); |
| m_frames = new ArrayList<DStackContext>(); |
| m_previousFrames = new ArrayList<DStackContext>(); |
| m_suspendInfo = null; |
| m_lastInCallFunction = null; |
| m_breakpoints = new ArrayList<DLocation>(); |
| m_swfInfo = new ArrayList<DSwfInfo>(); |
| m_watchpoints = new ArrayList<DWatch>(); |
| m_suspendInfo = null; |
| m_lastInGetVariable = null; |
| m_attachChildren = true; |
| m_lastInCallFunction = null; |
| |
| } |
| } |
| |
| private DManagerIsolateState getIsolateState(int isolateId) { |
| if (isolateId == Isolate.DEFAULT_ID) |
| return m_mainState; |
| |
| DManagerIsolateState isolateState = null; |
| if (!m_isolateState.containsKey(isolateId)) { |
| isolateState = new DManagerIsolateState(); |
| m_isolateState.put(isolateId, isolateState); |
| } else |
| isolateState = m_isolateState.get(isolateId); |
| return isolateState; |
| } |
| |
| public DManager() { |
| m_parms = new HashMap<String, String>(); |
| |
| m_isolates = new HashMap<Integer, DIsolate>(); |
| m_isolates.put(Isolate.DEFAULT_ID, DEFAULT_ISOLATE); |
| m_event = new LinkedList<DebugEvent>(); |
| m_sourceLocator = null; |
| m_squelchEnabled = false; |
| m_lastConstantPool = null; |
| m_playerVersion = -1; // -1 => unknown |
| m_isolateState = new HashMap<Integer, DManagerIsolateState>(); |
| m_mainState = new DManagerIsolateState(); |
| m_isolateState.put(Isolate.DEFAULT_ID, m_mainState); |
| m_inIsolateLock = new Object(); |
| m_activeIsolateLock = new Object(); |
| m_wideLines = false; |
| } |
| |
| public void setWideLines(boolean value) { |
| m_wideLines = value; |
| } |
| |
| public String getURI() { |
| return m_uri; |
| } |
| |
| public byte[] getSWF() { |
| return m_swf; |
| } |
| |
| public byte[] getSWD() { |
| return m_swd; |
| } |
| |
| public byte[] getActions() { |
| return m_actions; |
| } |
| |
| /** Returns the Flash Player version number; e.g. 9 for Flash Player 9.0 */ |
| public int getVersion() { |
| return m_playerVersion; |
| } |
| |
| public SourceLocator getSourceLocator() { |
| return m_sourceLocator; |
| } |
| |
| public void setSourceLocator(SourceLocator sl) { |
| m_sourceLocator = sl; |
| } |
| |
| /** |
| * If this feature is enabled then we do not attempt to attach child |
| * variables to parents. |
| */ |
| public void enableChildAttach(boolean enable, int isolateId) { |
| getIsolateState(isolateId).m_attachChildren = enable; |
| } |
| |
| // return/clear the last variable seen from an InGetVariable message |
| public DVariable lastVariable(int isolateId) { |
| return getIsolateState(isolateId).m_lastInGetVariable; |
| } |
| |
| public void clearLastVariable(int isolateId) { |
| getIsolateState(isolateId).m_lastInGetVariable = null; |
| } |
| |
| // return/clear the last variable seen from an InCallFunction message |
| public DVariable lastFunctionCall(int isolateId) { |
| return getIsolateState(isolateId).m_lastInCallFunction; |
| } |
| |
| public void clearLastFunctionCall(int isolateId) { |
| getIsolateState(isolateId).m_lastInCallFunction = null; |
| } |
| |
| // return/clear the last binary op result seen from an InBinaryOp message |
| public DVariable lastBinaryOp(int isolateId) { |
| return getIsolateState(isolateId).m_lastInBinaryOp; |
| } |
| |
| public void clearLastBinaryOp(int isolateId) { |
| getIsolateState(isolateId).m_lastInBinaryOp = null; |
| } |
| |
| /* |
| * Frees up any information we have kept about |
| */ |
| void freeCaches(int isolateId) { |
| clearFrames(isolateId); |
| freeValueCache(isolateId); |
| } |
| |
| void freeValueCache(int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| state.m_previousValues = state.m_values; |
| state.m_values = new HashMap<Long, DValue>(); |
| |
| int size = getFrameCount(isolateId); |
| for (int i = 0; i < size; i++) |
| getFrame(i, isolateId).markStale(); |
| } |
| |
| // continuing our execution |
| void continuing(int isolateId) { |
| freeCaches(isolateId); |
| getIsolateState(isolateId).m_suspendInfo = null; |
| } |
| |
| /** |
| * Variables |
| */ |
| DValue getOrCreateValue(long id, int isolateId) { |
| DValue v = getValue(id, isolateId); |
| if (v == null) { |
| v = new DValue(id, isolateId); |
| putValue(id, v, isolateId); |
| } |
| return v; |
| } |
| |
| public DSwfInfo[] getSwfInfos(int isolateId) { |
| ArrayList<DSwfInfo> swfInfos = getIsolateState(isolateId).m_swfInfo; |
| synchronized (swfInfos) { |
| return swfInfos.toArray(new DSwfInfo[swfInfos.size()]); |
| } |
| } |
| |
| public DSwfInfo getSwfInfo(int at, int isolateId) { |
| ArrayList<DSwfInfo> swfInfos = getIsolateState(isolateId).m_swfInfo; |
| synchronized (swfInfos) { |
| return swfInfos.get(at); |
| } |
| } |
| |
| public int getSwfInfoCount(int isolateId) { |
| ArrayList<DSwfInfo> swfInfos = getIsolateState(isolateId).m_swfInfo; |
| synchronized (swfInfos) { |
| return swfInfos.size(); |
| } |
| } |
| |
| /** |
| * Obtains a SwfInfo object at the given index or if one doesn't yet exist |
| * at that location, creates a new empty one there and returns it. |
| */ |
| DSwfInfo getOrCreateSwfInfo(int at, int isolateId) { |
| ArrayList<DSwfInfo> swfInfos = getIsolateState(isolateId).m_swfInfo; |
| synchronized (swfInfos) { |
| DSwfInfo i = (at > -1 && at < getSwfInfoCount(isolateId)) ? getSwfInfo( |
| at, isolateId) : null; |
| if (i == null) { |
| // are we above water |
| at = (at < 0) ? 0 : at; |
| |
| // fill all the gaps with null; really shouldn't be any... |
| while (at >= swfInfos.size()) |
| swfInfos.add(null); |
| |
| i = new DSwfInfo(at, isolateId); |
| swfInfos.set(at, i); |
| } |
| return i; |
| } |
| } |
| |
| /** |
| * Get the most recently active swfInfo object. We define active as the most |
| * recently seen swfInfo |
| */ |
| DSwfInfo getActiveSwfInfo(int isolateId) { |
| int count = getSwfInfoCount(isolateId); |
| |
| // pick up the last one seen |
| DSwfInfo swf = getIsolateState(isolateId).m_lastSwfInfo; |
| |
| // still don't have one then get or create the most recent one |
| // works if count = 0 |
| if (swf == null) |
| swf = getOrCreateSwfInfo(count - 1, isolateId); |
| |
| if (swf.hasAllSource()) { |
| // already full so create a new one on the end |
| swf = getOrCreateSwfInfo(count, isolateId); |
| } |
| return swf; |
| } |
| |
| /** |
| * Walk the list of scripts and add them to our swfInfo object This method |
| * may be called when min/max are zero and the swd has not yet fully loaded |
| * in the player or it could be called before we have all the scripts. |
| */ |
| void tieScriptsToSwf(DSwfInfo info, int isolateId) { |
| if (!info.hasAllSource()) { |
| int min = info.getFirstSourceId(); |
| int max = info.getLastSourceId(); |
| // System.out.println("attaching scripts "+min+"-"+max+" to "+info.getUrl()); |
| for (int i = min; i <= max; i++) { |
| DModule m = getSource(i, isolateId); |
| if (m == null) { |
| // this is ok, it means the scripts are coming... |
| } else { |
| info.addSource(i, m); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Record a new source file. |
| * |
| * @param name |
| * filename in "basepath;package;filename" format |
| * @param swfIndex |
| * the index of the SWF with which this source is associated, or |
| * -1 is we don't know |
| * @return true if our list of source files was modified, or false if we |
| * already knew about that particular source file. |
| */ |
| private boolean putSource(int swfIndex, int moduleId, int bitmap, |
| String name, String text, int isolateId) { |
| // if isolateIndex is not -1, augment swfIndex and moduleId with isolate |
| // info. |
| Map<Integer, DModule> source = getIsolateState(isolateId).m_source; |
| synchronized (source) { |
| // if we haven't already recorded this script then do so. |
| if (!source.containsKey(moduleId)) { |
| DModule s = new DModule(this, moduleId, bitmap, name, text, isolateId); |
| |
| // put source in our large pool |
| source.put(moduleId, s); |
| |
| // put the source in the currently active swf |
| DSwfInfo swf; |
| if (swfIndex == -1) // caller didn't tell us what swf thi is for |
| swf = getActiveSwfInfo(isolateId); // ... so guess |
| else |
| swf = getOrCreateSwfInfo(swfIndex, isolateId); |
| |
| swf.addSource(moduleId, s); |
| |
| return true; |
| } |
| |
| return false; |
| } |
| } |
| |
| /** |
| * Remove our record of a particular source file. |
| * |
| * @param id |
| * the id of the file to forget about. |
| * @return true if source file was removed; false if we didn't know about it |
| * to begin with. |
| */ |
| private boolean removeSource(int id, int isolateId) { |
| Map<Integer, DModule> source = getIsolateState(isolateId).m_source; |
| synchronized (source) { |
| try { |
| source.remove(id); |
| } catch (Exception e) { |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| public DModule getSource(int id, int isolateId) { |
| Map<Integer, DModule> source = getIsolateState(isolateId).m_source; |
| synchronized (source) { |
| return source.get(id); |
| } |
| } |
| |
| // @deprecated |
| public DModule[] getSources() { |
| Map<Integer, DModule> source = getIsolateState(Isolate.DEFAULT_ID).m_source; |
| synchronized (source) { |
| m_sourceListModified = false; |
| |
| /* find out the size of the array */ |
| int count = source.size(); |
| DModule[] ar = new DModule[count]; |
| |
| count = 0; |
| for (DModule sf : source.values()) |
| ar[count++] = sf; |
| return ar; |
| } |
| } |
| |
| // @deprecated |
| boolean sourceListModified() { |
| Map<Integer, DModule> source = getIsolateState(Isolate.DEFAULT_ID).m_source; |
| synchronized (source) { |
| return m_sourceListModified; |
| } |
| } |
| |
| public DValue getValue(long id, int isolateId) { |
| return getIsolateState(isolateId).m_values.get(id); |
| } |
| |
| /** |
| * Returns the previous value object for the given id -- that is, the value |
| * that that object had the last time the player was suspended. Never |
| * requests it from the player (because it can't, of course). Returns |
| * <code>null</code> if we don't have a value for that id. |
| */ |
| public DValue getPreviousValue(long id, int isolateId) { |
| return getIsolateState(isolateId).m_previousValues.get(id); |
| } |
| |
| void putValue(long id, DValue v, int isolateId) { |
| if (id != Value.UNKNOWN_ID) { |
| getIsolateState(isolateId).m_values.put(id, v); |
| } |
| } |
| |
| DValue removeValue(long id, int isolateId) { |
| return getIsolateState(isolateId).m_values.remove((int)id); |
| } |
| |
| void addVariableMember(long parentId, DVariable child, int isolateId) { |
| DValue parent = getValue(parentId, isolateId); |
| addVariableMember(parent, child, isolateId); |
| } |
| |
| void addVariableMember(DValue parent, DVariable child, int isolateId) { |
| if (getIsolateState(isolateId).m_attachChildren) { |
| // There are certain situations when the Flash player will send us |
| // more |
| // than one variable or getter with the same name. Basically, when a |
| // subclass implements (or overrides) something that was also |
| // declared in a |
| // superclass, then we'll see that variable or getter in both the |
| // superclass and the subclass. |
| // |
| // Here are a few situations where that affects the debugger in |
| // different |
| // ways: |
| // |
| // 1. When a class implements an interface, the class instance |
| // actually has |
| // *two* members for each implemented function: One which is public |
| // and |
| // represents the implementation function, and another which is |
| // internal |
| // to the interface, and represents the declaration of the function. |
| // Both of these come in to us. In the UI, the one we want to show |
| // is |
| // the public one. They come in in random order (they are stored in |
| // a |
| // hash table in the VM), so we don't know which one will come |
| // first. |
| // |
| // 2. When a superclass has a private member "m", and a subclass has |
| // its own |
| // private member with the same name "m", we will receive both of |
| // them. |
| // (They are scoped by different packages.) In this case, the first |
| // one |
| // the player sent us is the one from the subclass, and that is the |
| // one |
| // we want to display in the debugger. |
| // |
| // The following logic correctly deals with all variations of those |
| // cases. |
| if (parent != null) { |
| DVariable existingChildWithSameName = parent.findMember(child |
| .getName()); |
| if (existingChildWithSameName != null) { |
| int existingScope = existingChildWithSameName.getScope(); |
| int newScope = child.getScope(); |
| |
| if (existingScope == VariableAttribute.NAMESPACE_SCOPE |
| && newScope == VariableAttribute.PUBLIC_SCOPE) { |
| // This is the case described above where a class |
| // implements an interface, |
| // so that class's definition includes both a |
| // namespace-scoped declaration |
| // and a public declaration, in random order; in this |
| // case, the |
| // namespace-scoped declaration came first. We want to |
| // use the public |
| // declaration. |
| parent.addMember(child); |
| } else if (existingScope == VariableAttribute.PUBLIC_SCOPE |
| && newScope == VariableAttribute.NAMESPACE_SCOPE) { |
| // One of two things happened here: |
| // |
| // 1. This is the case described above where a class |
| // implements an interface, |
| // so that class's definition includes both a |
| // namespace-scoped declaration |
| // and a public declaration, in random order; in this |
| // case, the |
| // public declaration came first. It is tempting to use |
| // the public |
| // member in this case, but there is a catch... |
| // 2. It might be more complicated than that: Perhaps |
| // there is interface I, |
| // and class C1 implements I, but class C2 extends C1, |
| // and overrides |
| // one of the members of I that was already implemented |
| // by C1. In this |
| // case, the public declaration from C2 came first, but |
| // now we are seeing |
| // a namespace-scoped declaration in C1. We need to |
| // record that the |
| // member is public, but we also need to record that it |
| // is a member |
| // of the base class, not just a member of the |
| // superclass. |
| // |
| // The easiest way to deal with both cases is to use the |
| // child that came from |
| // the superclass, but to change its scope to public. |
| child.makePublic(); |
| parent.addMember(child); |
| } else if (existingScope != VariableAttribute.PRIVATE_SCOPE |
| && existingScope == newScope) { |
| // This is a public, protected, internal, or |
| // namespace-scoped member which |
| // was defined in a base class and overridden in a |
| // subclass. We want to |
| // use the member from the base class, to that the |
| // debugger knows where the |
| // variable was actually defined. |
| parent.addMember(child); |
| } else if (existingScope == VariableAttribute.PRIVATE_SCOPE |
| && existingScope == newScope) { |
| parent.addInheritedPrivateMember(child); |
| } |
| } else { |
| parent.addMember(child); |
| } |
| } |
| // put child in the registry if it has an id and not already there |
| DValue childValue = (DValue) child.getValue(); |
| long childId = childValue.getId(); |
| if (childId != Value.UNKNOWN_ID) { |
| DValue existingValue = getValue(childId, isolateId); |
| if (existingValue != null) { |
| assert existingValue == childValue; // TODO is this right? |
| // what about getters? |
| } else { |
| putValue(childId, childValue, isolateId); |
| } |
| } |
| } |
| } |
| |
| // TODO is this right? |
| void addVariableMember(long parentId, DVariable child, long doubleAsId, |
| int isolateId) { |
| addVariableMember(parentId, child, isolateId); |
| |
| // double book the child under another id |
| if (getIsolateState(isolateId).m_attachChildren) |
| putValue(doubleAsId, (DValue) child.getValue(), isolateId); |
| } |
| |
| // @deprecated last pool that was read |
| public String[] getConstantPool() { |
| return m_lastConstantPool; |
| } |
| |
| /** |
| * Breakpoints |
| */ |
| public DLocation getBreakpoint(int id, int isolateId) { |
| ArrayList<DLocation> breakpoints = getIsolateState(isolateId).m_breakpoints; |
| synchronized (breakpoints) { |
| DLocation loc = null; |
| int which = findBreakpoint(id, isolateId); |
| if (which > -1) |
| loc = breakpoints.get(which); |
| return loc; |
| } |
| } |
| |
| int findBreakpoint(int id, int isolateId) { |
| ArrayList<DLocation> breakpoints = getIsolateState(isolateId).m_breakpoints; |
| synchronized (breakpoints) { |
| int which = -1; |
| int size = breakpoints.size(); |
| for (int i = 0; which < 0 && i < size; i++) { |
| DLocation l = breakpoints.get(i); |
| if (l.getId() == id) |
| which = i; |
| } |
| return which; |
| } |
| } |
| |
| DLocation removeBreakpoint(int id, int isolateId) { |
| ArrayList<DLocation> breakpoints = getIsolateState(isolateId).m_breakpoints; |
| synchronized (breakpoints) { |
| DLocation loc = null; |
| int which = findBreakpoint(id, isolateId); |
| if (which > -1) { |
| loc = breakpoints.get(which); |
| breakpoints.remove(which); |
| } |
| |
| return loc; |
| } |
| } |
| |
| void addBreakpoint(int id, DLocation l, int isolateId) { |
| ArrayList<DLocation> breakpoints = getIsolateState(isolateId).m_breakpoints; |
| synchronized (breakpoints) { |
| breakpoints.add(l); |
| } |
| } |
| |
| public DLocation[] getBreakpoints(int isolateId) { |
| ArrayList<DLocation> breakpoints = getIsolateState(isolateId).m_breakpoints; |
| synchronized (breakpoints) { |
| return breakpoints.toArray(new DLocation[breakpoints.size()]); |
| } |
| } |
| |
| /** |
| * Watchpoints |
| */ |
| public DWatch getWatchpoint(int at, int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| synchronized (state.m_watchpoints) { |
| return state.m_watchpoints.get(at); |
| } |
| } |
| |
| public int getWatchpointCount(int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| synchronized (state.m_watchpoints) { |
| return state.m_watchpoints.size(); |
| } |
| } |
| |
| public DWatch[] getWatchpoints(int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| synchronized (state.m_watchpoints) { |
| return state.m_watchpoints.toArray(new DWatch[state.m_watchpoints.size()]); |
| } |
| } |
| |
| boolean addWatchpoint(DWatch w, int isolateId) { |
| ArrayList<DWatch> lockObject = getIsolateState(isolateId).m_watchpoints; |
| synchronized (lockObject) { |
| return lockObject.add(w); |
| } |
| } |
| |
| DWatch removeWatchpoint(int tag, int isolateId) { |
| ArrayList<DWatch> lockObject = getIsolateState(isolateId).m_watchpoints; |
| synchronized (lockObject) { |
| DWatch w = null; |
| int at = findWatchpoint(tag, isolateId); |
| if (at > -1) |
| w = lockObject.remove(at); |
| return w; |
| } |
| } |
| |
| int findWatchpoint(int tag, int isolateId) { |
| ArrayList<DWatch> lockObject = getIsolateState(isolateId).m_watchpoints; |
| synchronized (lockObject) { |
| int at = -1; |
| int size = getWatchpointCount(isolateId); |
| for (int i = 0; i < size && at < 0; i++) { |
| DWatch w = getWatchpoint(i, isolateId); |
| if (w.getTag() == tag) |
| at = i; |
| } |
| return at; |
| } |
| } |
| |
| /** |
| * Isolates |
| */ |
| public DIsolate getIsolate(int at) { |
| |
| if (at == Isolate.DEFAULT_ID) |
| return (DIsolate) DEFAULT_ISOLATE; |
| |
| synchronized (m_isolates) { |
| return m_isolates.get(at); |
| } |
| } |
| |
| public DIsolate getOrCreateIsolate(int at) { |
| synchronized (m_isolates) { |
| if (m_isolates.containsKey(at)) { |
| return m_isolates.get(at); |
| } else { |
| DIsolate isolate = new DIsolate(at); |
| m_isolates.put(at, isolate); |
| return isolate; |
| } |
| } |
| } |
| |
| public int getIsolateCount() { |
| synchronized (m_isolates) { |
| return m_isolates.size(); |
| } |
| } |
| |
| public DIsolate[] getIsolates() { |
| synchronized (m_isolates) { |
| return m_isolates.values().toArray(new DIsolate[m_isolates.size()]); |
| } |
| } |
| |
| boolean addIsolate(DIsolate t) { |
| synchronized (m_isolates) { |
| m_isolates.put(t.getId(), t); |
| return true; |
| } |
| } |
| |
| void clearIsolates() { |
| synchronized (m_isolates) { |
| m_isolates.clear(); |
| } |
| } |
| |
| DIsolate removeIsolate(int id) { |
| synchronized (m_isolates) { |
| DIsolate t = null; |
| int at = findIsolate(id); |
| if (at > -1) |
| t = m_isolates.remove(at); |
| return t; |
| } |
| } |
| |
| int findIsolate(int id) { |
| synchronized (m_isolates) { |
| if (m_isolates.containsKey(id)) |
| return id; |
| else |
| return -1; |
| } |
| } |
| |
| void setActiveIsolate(Isolate t) { |
| synchronized (m_activeIsolateLock) { |
| if (t == null) { |
| m_activeIsolate = DEFAULT_ISOLATE; |
| } else |
| m_activeIsolate = t; |
| } |
| } |
| |
| Isolate getActiveIsolate() { |
| synchronized (m_activeIsolateLock) { |
| return m_activeIsolate; |
| } |
| } |
| |
| |
| void setInIsolate(Isolate t) { |
| synchronized (m_inIsolateLock) { |
| if (t == null) { |
| m_inIsolate = DEFAULT_ISOLATE; |
| } else |
| m_inIsolate = t; |
| } |
| } |
| |
| Isolate getInIsolate() { |
| synchronized (m_inIsolateLock) { |
| return m_inIsolate; |
| } |
| } |
| |
| Isolate getDefaultIsolate() { |
| return DEFAULT_ISOLATE; |
| } |
| |
| |
| /** |
| * Frame stack management related stuff |
| * |
| * @return true if we added this frame; false if we ignored it |
| */ |
| boolean addFrame(DStackContext ds, int isolateId) { |
| getIsolateState(isolateId).m_frames.add(ds); |
| return true; |
| } |
| |
| void clearFrames(int isolateId) { |
| if (getIsolateState(isolateId).m_frames.size() > 0) |
| getIsolateState(isolateId).m_previousFrames = getIsolateState(isolateId).m_frames; |
| getIsolateState(isolateId).m_frames = new ArrayList<DStackContext>(); |
| } |
| |
| public DStackContext getFrame(int at, int isolateId) { |
| return getIsolateState(isolateId).m_frames.get(at); |
| } |
| |
| public int getFrameCount(int isolateId) { |
| return getIsolateState(isolateId).m_frames.size(); |
| } |
| |
| public DStackContext[] getFrames(int isolateId) { |
| ArrayList<DStackContext> frames = getIsolateState(isolateId).m_frames; |
| return frames.toArray(new DStackContext[frames.size()]); |
| } |
| |
| private boolean stringsEqual(String s1, String s2) { |
| if (s1 == null) |
| return s2 == null; |
| else |
| return s1.equals(s2); |
| } |
| |
| /** |
| * Correlates the old list of stack frames, from the last time the player |
| * was suspended, with the new list of stack frames, attempting to guess |
| * which frames correspond to each other. This is done so that |
| * Variable.hasValueChanged() can work correctly for local variables. |
| */ |
| private void mapOldFramesToNew(int isolateId) { |
| ArrayList<DStackContext> previousFrames = null; |
| ArrayList<DStackContext> frames = null; |
| Map<Long, DValue> previousValues = null; |
| |
| previousFrames = getIsolateState(isolateId).m_previousFrames; |
| frames = getIsolateState(isolateId).m_frames; |
| previousValues = getIsolateState(isolateId).m_previousValues; |
| |
| int oldSize = previousFrames.size(); |
| int newSize = frames.size(); |
| |
| // discard all old frames (we will restore some of them below) |
| DValue[] oldFrames = new DValue[oldSize]; |
| for (int depth = 0; depth < oldSize; depth++) { |
| oldFrames[depth] = (DValue) previousValues.remove(Value.BASE_ID |
| - depth); |
| } |
| |
| // Start at the end of the stack (the stack frame farthest from the |
| // current one), and try to match up stack frames |
| int oldDepth = oldSize - 1; |
| int newDepth = newSize - 1; |
| while (oldDepth >= 0 && newDepth >= 0) { |
| DStackContext oldFrame = previousFrames.get(oldDepth); |
| DStackContext newFrame = frames.get(newDepth); |
| if (oldFrame != null && newFrame != null) { |
| if (stringsEqual(oldFrame.getCallSignature(), |
| newFrame.getCallSignature())) { |
| DValue frame = oldFrames[oldDepth]; |
| if (frame != null) |
| previousValues.put(Value.BASE_ID - newDepth, frame); |
| } |
| } |
| oldDepth--; |
| newDepth--; |
| } |
| } |
| |
| /** |
| * Get function is only supported in players that recognize the squelch |
| * message. |
| */ |
| public boolean isGetSupported() { |
| return m_squelchEnabled; |
| } |
| |
| |
| /** |
| * Returns a suspend information on why the Player has suspended execution. |
| * |
| * @return see SuspendReason |
| */ |
| public DSuspendInfo getSuspendInfo(int isolateId) { |
| if (m_isolateState.containsKey(isolateId)) { |
| return m_isolateState.get(isolateId).m_suspendInfo; |
| } |
| return null; |
| } |
| |
| public ArrayList<SwfInfo> getIsolateSwfList() { |
| ArrayList<SwfInfo> result = new ArrayList<SwfInfo>(); |
| |
| for (DManagerIsolateState state : m_isolateState.values()) { |
| if (state.m_swfInfo != null) { |
| result.addAll(state.m_swfInfo); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Event management related stuff |
| */ |
| public int getEventCount() { |
| synchronized (m_event) { |
| return m_event.size(); |
| } |
| } |
| |
| /** |
| * Get an object on which callers can call wait(), in order to wait until |
| * something happens. |
| * |
| * Note: The object will be signalled when EITHER of the following happens: |
| * (1) An event is added to the event queue; (2) The network connection is |
| * broken (and thus there will be no more events). |
| * |
| * @return an object on which the caller can call wait() |
| */ |
| public Object getEventNotifier() { |
| return m_event; |
| } |
| |
| public DebugEvent nextEvent() { |
| DebugEvent s = null; |
| synchronized (m_event) { |
| if (m_event.size() > 0) |
| s = m_event.removeFirst(); |
| } |
| return s; |
| } |
| |
| public synchronized void addEvent(DebugEvent e) { |
| synchronized (m_event) { |
| m_event.add(e); |
| m_event.notifyAll(); // wake up listeners (see getEventNotifier()) |
| } |
| } |
| |
| /** |
| * Issued when the socket connection to the player is cut |
| */ |
| public void disconnected() { |
| synchronized (m_event) { |
| m_event.notifyAll(); // see getEventNotifier() |
| } |
| } |
| |
| /** |
| * This is the core routine for decoding incoming messages and deciding what |
| * should be done with them. We have registered ourself with DProtocol to be |
| * notified when any incoming messages have been received. |
| * |
| * It is important to note that we should not rely on the contents of the |
| * message since it may be reused after we exit this method. |
| */ |
| public void messageArrived(DMessage msg, DProtocol which) { |
| /* |
| * at this point we just open up a big switch statement and walk through |
| * all possible cases |
| */ |
| int type = msg.getType(); |
| // System.out.println("manager msg = "+DMessage.inTypeName(type)); |
| int inIsolateId = getInIsolate() != null ? getInIsolate().getId() |
| : Isolate.DEFAULT_ID; |
| if (inIsolateId != Isolate.DEFAULT_ID) { |
| msg.setTargetIsolate(inIsolateId); |
| } |
| switch (type) { |
| case DMessage.InVersion: { |
| long ver = msg.getDWord(); |
| m_playerVersion = (int) ver; |
| |
| // Newer players will send another byte, which is the pointer size |
| // that is used by the player (in bytes). |
| int pointerSize; |
| if (msg.getRemaining() >= 1) |
| pointerSize = msg.getByte(); |
| else |
| pointerSize = 4; |
| DMessage.setSizeofPtr(pointerSize); |
| break; |
| } |
| |
| case DMessage.InErrorExecLimit: { |
| handleFaultEvent(new RecursionLimitFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorWith: { |
| handleFaultEvent(new InvalidWithFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorProtoLimit: { |
| handleFaultEvent(new ProtoLimitFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorURLOpen: { |
| // String url = msg.getString(); |
| // handleFaultEvent(new InvalidURLFault(url, msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorTarget: { |
| // String name = msg.getString(); |
| // handleFaultEvent(new InvalidTargetFault(name, msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorException: { |
| long offset = msg.getDWord(); |
| // As of FP9, the player will also send the "toString()" message |
| // of the exception. But for backward compatibility with older |
| // players, we won't assume that that is there. |
| String exceptionMessage; |
| boolean willExceptionBeCaught = false; |
| Value thrown = null; |
| if (msg.getRemaining() > 0) { |
| exceptionMessage = msg.getString(); |
| if (msg.getRemaining() > 0) { |
| if (msg.getByte() != 0) { |
| willExceptionBeCaught = (msg.getByte() != 0 ? true |
| : false); |
| msg.getPtr(); |
| DVariable thrownVar = extractVariable(msg); |
| thrown = thrownVar.getValue(); |
| } |
| } |
| } else { |
| exceptionMessage = ""; //$NON-NLS-1$ |
| } |
| ExceptionFault exceptionFault = new ExceptionFault( |
| exceptionMessage, willExceptionBeCaught, thrown, msg.getTargetIsolate()); |
| exceptionFault.isolateId = msg.getTargetIsolate(); |
| handleFaultEvent(exceptionFault); |
| break; |
| } |
| |
| case DMessage.InErrorStackUnderflow: { |
| // long offset = msg.getDWord(); |
| handleFaultEvent(new StackUnderFlowFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorZeroDivide: { |
| // long offset = msg.getDWord(); |
| handleFaultEvent(new DivideByZeroFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorScriptStuck: { |
| handleFaultEvent(new ScriptTimeoutFault(msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InErrorConsole: { |
| String s = msg.getString(); |
| handleFaultEvent(new ConsoleErrorFault(s, msg.getTargetIsolate())); |
| break; |
| } |
| |
| case DMessage.InTrace: { |
| String text = msg.getString(); |
| addEvent(new TraceEvent(text)); |
| break; |
| } |
| |
| case DMessage.InSquelch: { |
| long state = msg.getDWord(); |
| m_squelchEnabled = (state != 0) ? true : false; |
| break; |
| } |
| |
| case DMessage.InParam: { |
| String name = msg.getString(); |
| String value = msg.getString(); |
| |
| // here's where we get movie = URL and password which I'm not sure |
| // what to do with? |
| // System.out.println(name+"="+value); |
| m_parms.put(name, value); |
| |
| // if string is a "movie", then this is a URL |
| if (name.startsWith("movie")) //$NON-NLS-1$ |
| m_uri = convertToURI(value); |
| break; |
| } |
| |
| case DMessage.InPlaceObject: { |
| long objId = msg.getPtr(); |
| String path = msg.getString(); |
| // m_bag.placeObject((int)objId, path); |
| break; |
| } |
| |
| case DMessage.InSetProperty: { |
| long objId = msg.getPtr(); |
| int item = msg.getWord(); |
| String value = msg.getString(); |
| break; |
| } |
| |
| case DMessage.InNewObject: { |
| long objId = msg.getPtr(); |
| break; |
| } |
| |
| case DMessage.InRemoveObject: { |
| long objId = msg.getPtr(); |
| // m_bag.removeObject((int)objId); |
| break; |
| } |
| |
| case DMessage.InSetVariable: { |
| long objId = msg.getPtr(); |
| String name = msg.getString(); |
| int dType = msg.getWord(); |
| int flags = (int) msg.getDWord(); |
| String value = msg.getString(); |
| |
| // m_bag.createVariable((int)objId, name, dType, flags, value); |
| break; |
| } |
| |
| case DMessage.InDeleteVariable: { |
| long objId = msg.getPtr(); |
| String name = msg.getString(); |
| // m_bag.deleteVariable((int)objId, name); |
| break; |
| } |
| |
| case DMessage.InScript: { |
| int module = (int) msg.getDWord(); |
| int bitmap = (int) msg.getDWord(); |
| String name = msg.getString(); // in "basepath;package;filename" |
| // format |
| String text = msg.getString(); |
| int swfIndex = -1; |
| int isolateIndex = -1; |
| |
| /* new in flash player 9: player tells us what swf this is for */ |
| if (msg.getRemaining() >= 4) |
| swfIndex = (int) msg.getDWord(); |
| |
| isolateIndex = msg.getTargetIsolate(); |
| getOrCreateIsolate(isolateIndex); |
| if (putSource(swfIndex, module, bitmap, name, text, |
| isolateIndex)) { |
| // have we changed the list since last query |
| if (!m_sourceListModified) |
| addEvent(new FileListModifiedEvent()); |
| |
| m_sourceListModified = true; |
| } |
| break; |
| } |
| |
| case DMessage.InRemoveScript: { |
| long module = msg.getDWord(); |
| int isolateId = msg.getTargetIsolate(); |
| Map<Integer, DModule> source = getIsolateState(isolateId).m_source; |
| synchronized (source) { |
| if (removeSource((int) module, isolateId)) { |
| // have we changed the list since last query |
| if (!m_sourceListModified) |
| addEvent(new FileListModifiedEvent()); |
| |
| m_sourceListModified = true; /* current source list is stale */ |
| } |
| } |
| break; |
| } |
| |
| case DMessage.InAskBreakpoints: { |
| // the player has just loaded a swf and we know the player |
| // has halted, waiting for us to continue. The only caveat |
| // is that it looks like it still does a number of things in |
| // the background which take a few seconds to complete. |
| int targetIsolate = msg.getTargetIsolate(); |
| DSuspendInfo iSusInfo = getIsolateState(targetIsolate).m_suspendInfo; |
| if (iSusInfo == null) { |
| iSusInfo = new DSuspendInfo(SuspendReason.ScriptLoaded, 0, |
| 0, 0, 0); |
| } |
| break; |
| } |
| |
| case DMessage.InBreakAt: { |
| long bp = 0, wideLine = 0, wideModule = 0; |
| if (!m_wideLines) { |
| bp = msg.getDWord(); |
| } |
| else { |
| wideModule = msg.getDWord(); |
| wideLine = msg.getDWord(); |
| } |
| long id = msg.getPtr(); |
| String stack = msg.getString(); |
| int targetIsolate = msg.getTargetIsolate(); |
| |
| int module = DLocation.decodeFile(bp); |
| int line = DLocation.decodeLine(bp); |
| if (m_wideLines) { |
| module = (int)wideModule; |
| line = (int)wideLine; |
| } |
| addEvent(new BreakEvent(module, line, targetIsolate)); |
| break; |
| } |
| |
| case DMessage.InContinue: { |
| /* we are running again so trash all our variable contents */ |
| continuing(msg.getTargetIsolate()); |
| break; |
| } |
| |
| case DMessage.InSetLocalVariables: { |
| // long objId = msg.getPtr(); |
| // m_bag.markObjectLocal((int)objId, true); |
| break; |
| } |
| |
| case DMessage.InSetBreakpoint: { |
| long count = msg.getDWord(); |
| int targetIsolate = msg.getTargetIsolate(); |
| while (count-- > 0) { |
| long bp = 0, moduleNumber = 0, lineNumber = 0; |
| if (!m_wideLines) { |
| bp = msg.getDWord(); |
| } |
| else { |
| moduleNumber = msg.getDWord(); |
| lineNumber = msg.getDWord(); |
| } |
| |
| int fileId = DLocation.decodeFile(bp); |
| int line = DLocation.decodeLine(bp); |
| if (m_wideLines) { |
| fileId = (int)moduleNumber; |
| line = (int)lineNumber; |
| } |
| |
| DModule file = null; |
| file = getSource(fileId, targetIsolate); |
| |
| DLocation l = new DLocation(file, line, targetIsolate); |
| |
| if (file != null) { |
| addBreakpoint((int) bp, l, targetIsolate); |
| } |
| } |
| break; |
| } |
| |
| case DMessage.InNumScript: { |
| /* lets us know how many scripts there are */ |
| int num = (int) msg.getDWord(); |
| int targetIsolate = msg.getTargetIsolate(); |
| DSwfInfo swf; |
| |
| /* |
| * New as of flash player 9: another dword indicating which swf this |
| * is for. That means we don't have to guess whether this is for an |
| * old SWF which has just had some more modules loaded, or for a new |
| * SWF! |
| */ |
| if (msg.getRemaining() >= 4) { |
| int swfIndex = (int) msg.getDWord(); |
| swf = getOrCreateSwfInfo(swfIndex, targetIsolate); |
| getIsolateState(targetIsolate).m_lastSwfInfo = swf; |
| } else { |
| /* |
| * This is not flash player 9 (or it is an early build of fp9). |
| * |
| * We use this message as a trigger that a new swf has been |
| * loaded, so make sure we are ready to accept the scripts. |
| */ |
| swf = getActiveSwfInfo(targetIsolate); |
| } |
| |
| // It is NOT an error for the player to have sent us a new, |
| // different sourceExpectedCount from whatever we had before! |
| // In fact, this happens all the time, whenever a SWF has more |
| // than one ABC. |
| swf.setSourceExpectedCount(num); |
| break; |
| } |
| |
| case DMessage.InRemoveBreakpoint: { |
| long count = msg.getDWord(); |
| int isolateId = msg.getTargetIsolate(); |
| while (count-- > 0) { |
| long bp = msg.getDWord(); |
| removeBreakpoint((int) bp, isolateId); |
| } |
| break; |
| |
| } |
| |
| case DMessage.InBreakAtExt: { |
| long bp = 0, wideLine = 0, wideModule = 0; |
| if (!m_wideLines) { |
| bp = msg.getDWord(); |
| } |
| else { |
| wideModule = msg.getDWord(); |
| wideLine = msg.getDWord(); |
| } |
| long num = msg.getDWord(); |
| |
| int targetIsolate = msg.getTargetIsolate(); |
| // System.out.println(msg.getInTypeName()+",bp="+(bp&0xffff)+":"+(bp>>16)); |
| /* we have stack info to store away */ |
| clearFrames(targetIsolate); // just in case |
| int depth = 0; |
| |
| while (num-- > 0) { |
| long bpi = 0, wideLinei= 0, wideModulei = 0; |
| if (!m_wideLines) { |
| bpi = msg.getDWord(); |
| } |
| else { |
| wideModulei = msg.getDWord(); |
| wideLinei = msg.getDWord(); |
| } |
| long id = msg.getPtr(); |
| String stack = msg.getString(); |
| int module = DLocation.decodeFile(bpi); |
| int line = DLocation.decodeLine(bpi); |
| if (m_wideLines) { |
| module = (int)wideModulei; |
| line = (int)wideLinei; |
| } |
| DModule m = null; |
| m = getSource(module, targetIsolate); |
| DStackContext c = new DStackContext(module, line, m, id, stack, |
| depth, targetIsolate); |
| // If addFrame() returns false, that means it chose to ignore |
| // this |
| // frame, so we do NOT want to increment our depth for the next |
| // time through the loop. If it returns true, then we do want |
| // to. |
| if (addFrame(c, targetIsolate)) |
| ++depth; |
| // System.out.println(" this="+id+",@"+(bpi&0xffff)+":"+(bpi>>16)+",stack="+stack); |
| } |
| mapOldFramesToNew(targetIsolate); |
| if (targetIsolate != Isolate.DEFAULT_ID) { |
| // ask for isolate id if it is present |
| appendIsolateInfoToFrame(targetIsolate); |
| |
| } |
| break; |
| |
| } |
| |
| case DMessage.InFrame: { |
| // For InFrame the first element is really our frame id |
| DValue frame = null; |
| DVariable child = null; |
| ArrayList<DVariable> v = new ArrayList<DVariable>(); |
| ArrayList<DVariable> registers = new ArrayList<DVariable>(); |
| int targetIsolate = msg.getTargetIsolate(); |
| int depth = (int) msg.getDWord(); // depth of frame |
| |
| // make sure we have a valid depth |
| if (depth > -1) { |
| // first thing is number of registers |
| int num = (int) msg.getDWord(); |
| for (int i = 0; i < num; i++) |
| registers.add(extractRegister(msg, i + 1)); |
| } |
| |
| int currentArg = -1; |
| boolean gettingScopeChain = false; |
| |
| // then our frame itself |
| while (msg.getRemaining() > 0) { |
| long frameId = msg.getPtr(); |
| |
| if (frame == null) { |
| frame = getOrCreateValue(frameId, targetIsolate); |
| extractVariable(msg); // put the rest of the info in the |
| // trash |
| } else { |
| child = extractVariable(msg); |
| if (currentArg == -1 |
| && child.getName().equals(ARGUMENTS_MARKER)) { |
| currentArg = 0; |
| gettingScopeChain = false; |
| } else if (child.getName().equals(SCOPE_CHAIN_MARKER)) { |
| currentArg = -1; |
| gettingScopeChain = true; |
| } else if (currentArg >= 0) { |
| // work around a compiler bug: If the variable's name is |
| // "undefined", |
| // then change its name to "_argN", where "N" is the |
| // argument index, |
| // e.g. _arg1, _arg2, etc. |
| ++currentArg; |
| if (child.getName().equals("undefined")) //$NON-NLS-1$ |
| child.setName("_arg" + currentArg); //$NON-NLS-1$ |
| } |
| |
| // All args and locals get added as "children" of |
| // the frame; but scope chain entries do not. |
| if (!gettingScopeChain) |
| addVariableMember(frameId, child, targetIsolate); |
| |
| // Everything gets added to the ordered list of |
| // variables that came in. |
| v.add(child); |
| } |
| } |
| |
| // let's transfer our newly gained knowledge into the stack context |
| if (depth == 0) |
| populateRootNode(frame, v, targetIsolate); |
| else |
| populateFrame(depth, v, targetIsolate); |
| |
| break; |
| } |
| |
| case DMessage.InOption: { |
| String s = msg.getString(); |
| String v = msg.getString(); |
| m_options.put(s, v); |
| break; |
| } |
| |
| case DMessage.InGetVariable: { |
| // For InGetVariable the first element is the original entity we |
| // requested |
| DValue parent = null; |
| DVariable child = null; |
| String definingClass = null; |
| int level = 0; |
| int targetIsolate = msg.getTargetIsolate(); |
| int highestLevelWithMembers = -1; |
| List<String> classes = new ArrayList<String>(); |
| |
| while (msg.getRemaining() > 0) { |
| long parentId = msg.getPtr(); |
| |
| // build or get parent node |
| if (parent == null) { |
| String name = msg.getString(); |
| |
| // pull the contents of the node which normally are disposed |
| // of except if we did a 0,name call |
| getIsolateState(targetIsolate).m_lastInGetVariable = extractVariable(msg, name); |
| |
| parent = getOrCreateValue(parentId, targetIsolate); |
| } else { |
| // extract the child and add it to the parent. |
| child = extractVariable(msg); |
| if (showMember(child)) { |
| if (child.isAttributeSet(VariableAttribute.IS_DYNAMIC)) { |
| // Dynamic attributes always come in marked as a |
| // member of |
| // class "Object"; but to the user, it makes more |
| // sense to |
| // consider them as members of the topmost class. |
| if (classes.size() > 0) { |
| child.setDefiningClass(0, classes.get(0)); |
| highestLevelWithMembers = Math.max( |
| highestLevelWithMembers, 0); |
| } |
| } else { |
| child.setDefiningClass(level, definingClass); |
| if (definingClass != null) { |
| highestLevelWithMembers = Math.max( |
| highestLevelWithMembers, level); |
| } |
| } |
| addVariableMember(parent.getId(), child, targetIsolate); |
| } else { |
| if (isTraits(child)) { |
| definingClass = child.getQualifiedName(); |
| level = classes.size(); |
| |
| // If the traits name end with "$", then it |
| // represents a class object -- |
| // in other words, the variables inside it are |
| // static variables of that |
| // class. In that case, we need to juggle the |
| // information. For example, |
| // if we are told that a variable is a member of |
| // "MyClass$", we actually |
| // store it into the information for "MyClass". |
| if (definingClass.endsWith("$")) { //$NON-NLS-1$ |
| String classWithoutDollar = definingClass |
| .substring(0, |
| definingClass.length() - 1); |
| int indexOfClass = classes |
| .indexOf(classWithoutDollar); |
| if (indexOfClass != -1) { |
| level = indexOfClass; |
| definingClass = classWithoutDollar; |
| } |
| } |
| |
| // It wasn't static -- so, add this class to the end |
| // of the list of classes |
| if (level == classes.size()) { |
| classes.add(definingClass); |
| } |
| } |
| } |
| } |
| } |
| |
| if (parent != null && parent.getClassHierarchy(true) == null) { |
| parent.setClassHierarchy( |
| classes.toArray(new String[classes.size()]), |
| highestLevelWithMembers + 1); |
| } |
| |
| break; |
| } |
| |
| case DMessage.InWatch: // for AS2; sends 16-bit ID field |
| case DMessage.InWatch2: // for AS3; sends 32-bit ID field |
| { |
| // This message is sent whenever a watchpoint is added |
| // modified or removed. |
| // |
| // For an addition, flags will be non-zero and |
| // success will be true. |
| // |
| // For a modification flags will be non-zero. |
| // and oldFlags will be non-zero and success |
| // will be true. Additionally oldFlags will not |
| // be equal to flags. |
| // |
| // For a removal flags will be zero. oldFlags |
| // will be non-zero. |
| // |
| // flags identifies the type of watchpoint added, |
| // see WatchKind. |
| // |
| // success indicates whether the operation was successful |
| // |
| // request. It will be associated with the watchpoint. |
| int success = msg.getWord(); |
| int oldFlags = msg.getWord(); |
| int oldTag = msg.getWord(); |
| int flags = msg.getWord(); |
| int tag = msg.getWord(); |
| // for AS2, the ID came in above as a Word. For AS3, the above value |
| // is |
| // bogus, and it has been sent again as a DWord. |
| long id = ((type == DMessage.InWatch2) ? msg.getPtr() : msg |
| .getWord()); |
| String name = msg.getString(); |
| int targetIsolate = msg.getTargetIsolate(); |
| |
| if (success != 0) { |
| if (flags == 0) { |
| removeWatchpoint(oldTag, targetIsolate); |
| } else { |
| // modification or addition is the same to us |
| // a new watch is created and added into the table |
| // while any old entry if it exists is removed. |
| removeWatchpoint(oldTag, targetIsolate); |
| DWatch w = new DWatch(id, name, flags, tag, targetIsolate); |
| addWatchpoint(w, targetIsolate); |
| } |
| } |
| break; |
| } |
| |
| case DMessage.InGetSwf: { |
| // we only house the swf temporarily, PlayerSession then |
| // pieces it back into swfinfo record. Also, we don't |
| // send any extra data in the message so that we need not |
| // copy the bytes. |
| m_swf = msg.getData(); |
| break; |
| } |
| |
| case DMessage.InGetSwd: { |
| // we only house the swd temporarily, PlayerSession then |
| // pieces it back into swfinfo record. |
| m_swd = msg.getData(); |
| break; |
| } |
| |
| case DMessage.InBreakReason: { |
| // the id map 1-1 with out SuspendReason interface constants |
| int suspendReason = msg.getWord(); |
| int suspendPlayer = msg.getWord(); // item index of player |
| int breakOffset = (int) msg.getDWord(); // current script offset |
| int prevBreakOffset = (int) msg.getDWord(); // prev script offset |
| int nextBreakOffset = (int) msg.getDWord(); // next script offset |
| int targetIsolate = msg.getTargetIsolate(); |
| |
| getIsolateState(targetIsolate).m_suspendInfo = new DSuspendInfo( |
| suspendReason, suspendPlayer, breakOffset, |
| prevBreakOffset, nextBreakOffset); |
| |
| // augment the current frame with this information. It |
| // should work ok since we only get this message after a |
| // InBreakAtExt message |
| try { |
| DStackContext c = getFrame(0, targetIsolate); |
| c.setOffset(breakOffset); |
| c.setSwfIndex(suspendPlayer); |
| } catch (Exception e) { |
| if (Trace.error) { |
| Trace.trace("Oh my god, gag me with a spoon...getFrame(0) call failed"); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| } |
| break; |
| } |
| |
| // obtain raw action script byte codes |
| case DMessage.InGetActions: { |
| int item = msg.getWord(); |
| int rsvd = msg.getWord(); |
| int at = (int) msg.getDWord(); |
| int len = (int) msg.getDWord(); |
| int i = 0; |
| |
| m_actions = (len <= 0) ? null : new byte[len]; |
| while (len-- > 0) |
| m_actions[i++] = (byte) msg.getByte(); |
| |
| break; |
| } |
| |
| // obtain data about a SWF |
| case DMessage.InSwfInfo: { |
| int count = msg.getWord(); |
| int targetIsolate = msg.getTargetIsolate(); |
| for (int i = 0; i < count; i++) { |
| long index = msg.getDWord(); |
| long id = msg.getPtr(); |
| |
| // get it |
| DSwfInfo info = null; |
| |
| info = getOrCreateSwfInfo((int) index, targetIsolate); |
| getIsolateState(targetIsolate).m_lastSwfInfo = info; |
| |
| // remember which was last seen |
| |
| if (id != 0) { |
| boolean debugComing = (msg.getByte() == 0) ? false : true; |
| byte vmVersion = (byte) msg.getByte(); // AS vm version |
| // number (1 = avm+, |
| // 0 == avm-) |
| int rsvd1 = msg.getWord(); |
| |
| long swfSize = msg.getDWord(); |
| long swdSize = msg.getDWord(); |
| long scriptCount = msg.getDWord(); |
| long offsetCount = msg.getDWord(); |
| long breakpointCount = msg.getDWord(); |
| |
| long port = msg.getDWord(); |
| String path = msg.getString(); |
| String url = msg.getString(); |
| String host = msg.getString(); |
| Map<Long, Integer> local2global = new HashMap<Long, Integer>(); |
| int minId = Integer.MAX_VALUE; |
| int maxId = Integer.MIN_VALUE; |
| // now we read in the swd debugging map (which provides |
| // local to global mappings of the script ids |
| /* anirudhs: Parsing this is only necessary if we are in |
| AVM1. (See PlayerSession::run(), there is a vmVersion |
| check before calling parseSwfSwd(). */ |
| if (swdSize > 0) { |
| long num = msg.getDWord(); |
| for (int j = 0; j < num; j++) { |
| if (msg.getRemaining() < DMessage.getSizeofPtr()) { |
| /* The SWD debugging map sent out by |
| * AVM2 often runs short usually in 64-bit |
| * debug player. We can stop with what we know |
| * and move on. |
| */ |
| break; |
| } |
| long local = msg.getPtr(); |
| int global = (int) msg.getDWord(); |
| local2global.put(local, global); |
| minId = (global < minId) ? global : minId; |
| maxId = (global > maxId) ? global : maxId; |
| } |
| } |
| // If its a new record then the swf size would have been |
| // unknown at creation time |
| boolean justCreated = (info.getSwfSize() == 0); |
| |
| // if we are a avm+ engine then we don't wait for the swd to |
| // load |
| if (vmVersion > 0) { |
| debugComing = false; |
| info.setVmVersion(vmVersion); |
| info.setPopulated(); // added by mmorearty on 9/5/05 for |
| // RSL debugging |
| } |
| |
| // update this swfinfo with the lastest data |
| info.freshen(id, path, url, host, port, debugComing, |
| swfSize, swdSize, breakpointCount, offsetCount, |
| scriptCount, local2global, minId, maxId); |
| // now tie any scripts that have been loaded into this |
| // swfinfo object |
| tieScriptsToSwf(info, targetIsolate); |
| |
| // notify if its newly created |
| if (justCreated) |
| addEvent(new SwfLoadedEvent(id, (int) index, path, url, |
| host, port, swfSize)); |
| } else { |
| // note our state before marking it |
| boolean alreadyUnloaded = info.isUnloaded(); |
| |
| // clear it out |
| info.setUnloaded(); |
| |
| // notify if this information is new. |
| if (!alreadyUnloaded) |
| addEvent(new SwfUnloadedEvent(info.getId(), |
| info.getPath(), (int) index)); |
| } |
| // System.out.println("[SWFLOAD] Loaded "+path+", size="+swfSize+", scripts="+scriptCount); |
| } |
| break; |
| } |
| |
| // obtain the constant pool of some player |
| case DMessage.InConstantPool: { |
| int item = msg.getWord(); |
| int count = (int) msg.getDWord(); |
| |
| String[] pool = new String[count]; |
| for (int i = 0; i < count; i++) { |
| long id = msg.getPtr(); |
| DVariable var = extractVariable(msg); |
| |
| // we only need the contents of the variable |
| pool[i] = var.getValue().getValueAsString(); |
| } |
| m_lastConstantPool = pool; |
| break; |
| } |
| |
| // obtain one or more function name line number mappings. |
| case DMessage.InGetFncNames: { |
| long id = msg.getDWord(); // module id |
| long count = msg.getDWord(); // number of entries |
| |
| // get the DModule |
| DModule m = getSource((int) id, msg.getTargetIsolate()); |
| if (m != null) { |
| for (int i = 0; i < count; i++) { |
| int offset = (int) msg.getDWord(); |
| int firstLine = (int) msg.getDWord(); |
| int lastLine = (int) msg.getDWord(); |
| String name = msg.getString(); |
| |
| // now add the entries |
| m.addLineFunctionInfo(offset, firstLine, lastLine, name); |
| } |
| } |
| break; |
| } |
| |
| case DMessage.InCallFunction: |
| case DMessage.InBinaryOp: { |
| // For InCallFunction the first element is the original function we |
| // requested |
| DValue parent = null; |
| int targetIsolate = msg.getTargetIsolate(); |
| DVariable child = null; |
| String definingClass = null; |
| int level = 0; |
| int highestLevelWithMembers = -1; |
| List<String> classes = new ArrayList<String>(); |
| |
| if (type == DMessage.InBinaryOp) |
| msg.getDWord(); // id |
| |
| while (msg.getRemaining() > 0) { |
| long parentId = msg.getPtr(); |
| |
| // build or get parent node |
| if (parent == null) { |
| String name = msg.getString(); |
| |
| // pull the contents of the node which normally are disposed |
| // of except if we did a 0,name call |
| DVariable var = extractVariable(msg, name); |
| if (type == DMessage.InCallFunction) { |
| getIsolateState(targetIsolate).m_lastInCallFunction = var; |
| } |
| else { |
| getIsolateState(targetIsolate).m_lastInBinaryOp = var; |
| } |
| |
| parent = getOrCreateValue(parentId, targetIsolate); |
| } else { |
| // extract the child and add it to the parent. |
| child = extractVariable(msg); |
| if (showMember(child)) { |
| if (child.isAttributeSet(VariableAttribute.IS_DYNAMIC)) { |
| // Dynamic attributes always come in marked as a |
| // member of |
| // class "Object"; but to the user, it makes more |
| // sense to |
| // consider them as members of the topmost class. |
| if (classes.size() > 0) { |
| child.setDefiningClass(0, classes.get(0)); |
| highestLevelWithMembers = Math.max( |
| highestLevelWithMembers, 0); |
| } |
| } else { |
| child.setDefiningClass(level, definingClass); |
| if (definingClass != null) { |
| highestLevelWithMembers = Math.max( |
| highestLevelWithMembers, level); |
| } |
| } |
| addVariableMember(parent.getId(), child, targetIsolate); |
| } else { |
| if (isTraits(child)) { |
| definingClass = child.getQualifiedName(); |
| level = classes.size(); |
| |
| // If the traits name end with "$", then it |
| // represents a class object -- |
| // in other words, the variables inside it are |
| // static variables of that |
| // class. In that case, we need to juggle the |
| // information. For example, |
| // if we are told that a variable is a member of |
| // "MyClass$", we actually |
| // store it into the information for "MyClass". |
| if (definingClass.endsWith("$")) { //$NON-NLS-1$ |
| String classWithoutDollar = definingClass |
| .substring(0, |
| definingClass.length() - 1); |
| int indexOfClass = classes |
| .indexOf(classWithoutDollar); |
| if (indexOfClass != -1) { |
| level = indexOfClass; |
| definingClass = classWithoutDollar; |
| } |
| } |
| |
| // It wasn't static -- so, add this class to the end |
| // of the list of classes |
| if (level == classes.size()) { |
| classes.add(definingClass); |
| } |
| } |
| } |
| } |
| } |
| |
| if (parent != null && parent.getClassHierarchy(true) == null) { |
| parent.setClassHierarchy( |
| classes.toArray(new String[classes.size()]), |
| highestLevelWithMembers + 1); |
| } |
| |
| break; |
| } |
| |
| case DMessage.InIsolateCreate: { |
| long id = msg.getDWord(); |
| isolateCreate((int) id); |
| |
| break; |
| } |
| |
| case DMessage.InIsolateExit: { |
| long id = msg.getDWord(); |
| // Implementation dependency on runtime in case id mechanism is |
| // changed: |
| // Typecast id into an int. |
| DIsolate isolate = removeIsolate((int) id); |
| addEvent(new IsolateExitEvent(isolate)); |
| |
| break; |
| } |
| |
| case DMessage.InIsolateEnumerate: { |
| // clearIsolates(); |
| // |
| // long lenIsolate = msg.getDWord(); |
| // |
| // for ( int i = 0; i < lenIsolate; i++) { |
| // long id = msg.getDWord(); |
| // addIsolate(new DIsolate(id)); |
| // } |
| |
| break; |
| } |
| |
| case DMessage.InSetActiveIsolate: { |
| long id = msg.getDWord(); |
| |
| boolean success = msg.getByte() != 0 ? true : false; |
| |
| /** Ignore inset since we don't wait |
| * for response anymore. |
| */ |
| // synchronized (m_activeIsolateLock) { |
| // if (success) { |
| // int at = findIsolate((int) id); |
| // if (at > -1) |
| // setActiveIsolate(getIsolate(at)); |
| // } else { |
| // setActiveIsolate(null); |
| // } |
| // } |
| |
| break; |
| } |
| |
| case DMessage.InIsolate: { |
| long id = msg.getDWord(); |
| synchronized (m_inIsolateLock) { |
| int at = findIsolate((int) id); |
| if (at != -1) |
| setInIsolate(getIsolate(at)); |
| else { |
| if (id != Isolate.DEFAULT_ID) { |
| setInIsolate(isolateCreate((int) id)); |
| } else |
| setInIsolate(null); |
| } |
| } |
| break; |
| } |
| |
| case DMessage.InSetExceptionBreakpoint: { |
| |
| int result = msg.getWord(); |
| String exceptionBP = msg.getString(); |
| int remaining = msg.getRemaining(); |
| break; |
| } |
| |
| case DMessage.InRemoveExceptionBreakpoint: { |
| int result = msg.getWord(); |
| String exceptionBP = msg.getString(); |
| int remaining = msg.getRemaining(); |
| break; |
| } |
| |
| default: { |
| break; |
| } |
| } |
| } |
| |
| private DIsolate isolateCreate(int id) { |
| int idx = findIsolate(id); |
| if (idx == -1) { |
| DIsolate isolate = new DIsolate(id); |
| addIsolate(isolate); |
| setInIsolate(isolate); |
| addEvent(new IsolateCreateEvent(isolate)); |
| return isolate; |
| } |
| return getIsolate(idx); |
| |
| } |
| |
| private void appendIsolateInfoToFrame(int isolateid) { |
| // augment the current frame with this information. It |
| // should work ok since we only get this message after a |
| // InBreakAtExt message |
| try { |
| DStackContext c = getFrame(0, isolateid); |
| c.setIsolateId(isolateid); |
| } catch (Exception e) { |
| if (Trace.error) { |
| Trace.trace("Oh my god, gag me with a spoon...getFrame(0) call failed"); //$NON-NLS-1$ |
| e.printStackTrace(); |
| } |
| } |
| } |
| |
| /** |
| * Returns whether a given child member should be shown, or should be |
| * filtered out. |
| */ |
| private boolean showMember(DVariable child) { |
| if (isTraits(child)) |
| return false; |
| return true; |
| } |
| |
| /** |
| * Returns whether this is not a variable at all, but is instead a |
| * representation of a "traits" object. A "traits" object is the Flash |
| * player's way of describing one class. |
| */ |
| private boolean isTraits(DVariable variable) { |
| Value value = variable.getValue(); |
| if (value.getType() == VariableType.UNKNOWN |
| && Value.TRAITS_TYPE_NAME.equals(value.getTypeName())) { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Here's where some ugly stuff happens. Since our context contains more |
| * info than what's contained within the stackcontext, we augment it with |
| * the variables. Also, we build up a list of variables that appears under |
| * root, that can be accessed without further qualification; this includes |
| * args, locals and _global. |
| */ |
| void populateRootNode(DValue frame, ArrayList<DVariable> orderedChildList, |
| int isolateId) { |
| // first populate the stack node with children |
| populateFrame(0, orderedChildList, isolateId); |
| |
| /** |
| * We mark it as members obtained so that we don't go to the player and |
| * request it, which would be bad, since its our artifical creation. |
| */ |
| DValue base = getOrCreateValue(Value.BASE_ID, isolateId); |
| base.setMembersObtained(true); |
| |
| /** |
| * Technically, we don't need to create the following nodes, but we like |
| * to give them nice type names |
| */ |
| |
| // now let's create a _global node and attach it to base |
| } |
| |
| /** |
| * We are done, so let's look for a number of special variables, since our |
| * frame comes in 3 pieces. First off is a "this" pointer, followed by a |
| * "$arguments" dummy node, followed by a "super" which marks the end of the |
| * arguments. |
| * |
| * All of this stuff gets pulled apart after we build the frame node. |
| */ |
| void populateFrame(int depth, ArrayList<DVariable> frameVars, int isolateId) { |
| // get our stack context |
| DStackContext context = null; |
| boolean inArgs = false; |
| int nArgs = -1; |
| boolean inScopeChain = false; |
| |
| // create a root node for each stack frame; first is at BASE_ID |
| DValue root = getOrCreateValue(Value.BASE_ID - depth, isolateId); |
| |
| if (depth < getFrameCount(isolateId)) |
| context = getFrame(depth, isolateId); |
| |
| // trim all current args from this context |
| if (context != null) |
| context.removeAllVariables(); |
| |
| // use the ordered child list |
| Iterator<DVariable> e = frameVars.iterator(); |
| while (e.hasNext()) { |
| DVariable v = e.next(); |
| String name = v.getName(); |
| |
| // let's clear a couple of attributes that may get in our way |
| v.clearAttribute(VariableAttribute.IS_LOCAL); |
| v.clearAttribute(VariableAttribute.IS_ARGUMENT); |
| if (name.equals("this")) //$NON-NLS-1$ |
| { |
| if (context != null) |
| context.setThis(v); |
| |
| // from our current frame, put a pseudo this entry into the |
| // cache and hang it off base, mark it as an implied arg |
| v.setAttribute(VariableAttribute.IS_ARGUMENT); |
| addVariableMember(root, v, isolateId); |
| |
| // also add this variable under THIS_ID |
| if (depth == 0) |
| putValue(Value.THIS_ID, (DValue) v.getValue(), isolateId); |
| } else if (name.equals("super")) //$NON-NLS-1$ |
| { |
| // we are at the end of the arg list and let's make super part |
| // of global |
| inArgs = false; |
| } else if (name.equals(ARGUMENTS_MARKER)) { |
| inArgs = true; |
| |
| // see if we can extract an arg count from this variable |
| try { |
| nArgs = ((Number) (v.getValue().getValueAsObject())) |
| .intValue(); |
| } catch (NumberFormatException nfe) { |
| } |
| } else if (name.equals(SCOPE_CHAIN_MARKER)) { |
| inArgs = false; |
| inScopeChain = true; |
| } else { |
| // add it to our root, marking it as an arg if we know, |
| // otherwise local |
| if (inArgs) { |
| v.setAttribute(VariableAttribute.IS_ARGUMENT); |
| |
| if (context != null) |
| context.addArgument(v); |
| |
| // decrement arg count if we have it |
| if (nArgs > -1) { |
| if (--nArgs <= 0) |
| inArgs = false; |
| } |
| } else if (inScopeChain) { |
| if (context != null) |
| context.addScopeChainEntry(v); |
| } else { |
| v.setAttribute(VariableAttribute.IS_LOCAL); |
| if (context != null) |
| context.addLocal(v); |
| } |
| |
| // add locals and arguments to root |
| if (!inScopeChain) |
| addVariableMember(root, v, isolateId); |
| } |
| } |
| } |
| |
| /** |
| * Map DMessage / Player attributes to VariableAttributes |
| */ |
| int toAttributes(int pAttr) { |
| int attr = pAttr; /* 1-1 mapping */ |
| return attr; |
| } |
| |
| DVariable extractVariable(DMessage msg) { |
| DVariable v = extractVariable(msg, msg.getString()); |
| return v; |
| } |
| |
| /** |
| * Build a variable based on the information we can extract from the |
| * messsage |
| */ |
| DVariable extractVariable(DMessage msg, String name) { |
| int oType = msg.getWord(); |
| int flags = (int) msg.getDWord(); |
| return extractAtom(msg, name, oType, flags); |
| } |
| |
| /** |
| * Extracts an builds a register variable |
| */ |
| DVariable extractRegister(DMessage msg, int number) { |
| int oType = msg.getWord(); |
| return extractAtom(msg, "$" + number, oType, 0); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Does the job of pulling together a variable based on the type of object |
| * encountered. |
| */ |
| DVariable extractAtom(DMessage msg, String name, int oType, int flags) { |
| int vType = VariableType.UNKNOWN; |
| Object value = null; |
| String typeName = ""; //$NON-NLS-1$ |
| String className = ""; //$NON-NLS-1$ |
| boolean isPrimitive = false; |
| |
| /* now we vary depending upon type */ |
| switch (oType) { |
| case DMessage.kNumberType: { |
| String s = msg.getString(); |
| double dval = Double.NaN; |
| try { |
| dval = Double.parseDouble(s); |
| } catch (NumberFormatException nfe) { |
| } |
| |
| value = new Double(dval); |
| isPrimitive = true; |
| break; |
| } |
| |
| case DMessage.kBooleanType: { |
| int bval = msg.getByte(); |
| value = new Boolean((bval == 0) ? false : true); |
| isPrimitive = true; |
| break; |
| } |
| |
| case DMessage.kStringType: { |
| String s = msg.getString(); |
| |
| value = s; |
| isPrimitive = true; |
| break; |
| } |
| |
| case DMessage.kObjectType: |
| case DMessage.kNamespaceType: { |
| long oid = msg.getPtr(); |
| long cType = (oid == -1) ? 0 : msg.getDWord(); |
| int isFnc = (oid == -1) ? 0 : msg.getWord(); |
| int rsvd = (oid == -1) ? 0 : msg.getWord(); |
| typeName = (oid == -1) ? "" : msg.getString(); //$NON-NLS-1$ |
| /* anirudhs: Date fix for expression evaluation */ |
| /* Player 10.2 onwards, the typename for Date comes |
| * as <dateformat>@oid where example of date format is: |
| * <Tue Feb 7 15:41:16 GMT+0530 2012> |
| * We have to fix the typename to how it originally |
| * appeared prior to this bug which is Date@oid. |
| * Note that even player 9 did not send oType as 11, |
| * instead oType was Object where as typeName was Date. |
| * What the customer sees is expression evaluation will |
| * always try to interpret date as a number. (ECMA.defaultValue |
| * has a check for preferredType of Date to be String) |
| */ |
| if (typeName.startsWith("<")) { //$NON-NLS-1$ |
| int atIndex = typeName.indexOf('@'); |
| String dateVal = typeName; |
| if (atIndex > -1) { |
| dateVal = typeName.substring(0, atIndex); |
| } |
| SimpleDateFormat dFormat = new SimpleDateFormat("<EEE MMM d HH:mm:ss 'GMT'z yyyy>"); //$NON-NLS-1$ |
| try { |
| Date dateObj = dFormat.parse(dateVal); |
| if (dateObj != null && dateObj.getTime() != 0) { |
| oType = DMessage.kDateType; |
| typeName = "Date" + typeName.substring(atIndex); //$NON-NLS-1$ |
| } |
| } |
| catch (ParseException e) { |
| //ignore |
| } |
| } |
| |
| className = DVariable.classNameFor(cType, false); |
| value = new Long(oid); |
| vType = (isFnc == 0) ? VariableType.OBJECT : VariableType.FUNCTION; |
| break; |
| } |
| |
| case DMessage.kMovieClipType: { |
| long oid = msg.getPtr(); |
| long cType = (oid == -1) ? 0 : msg.getDWord(); |
| long rsvd = (oid == -1) ? 0 : msg.getDWord(); |
| typeName = (oid == -1) ? "" : msg.getString(); //$NON-NLS-1$ |
| className = DVariable.classNameFor(cType, true); |
| |
| value = new Long(oid); |
| vType = VariableType.MOVIECLIP; |
| break; |
| } |
| |
| case DMessage.kNullType: { |
| value = null; |
| isPrimitive = true; |
| break; |
| } |
| |
| case DMessage.kUndefinedType: { |
| value = Value.UNDEFINED; |
| isPrimitive = true; |
| break; |
| } |
| |
| case DMessage.kTraitsType: { |
| // This one is special: When passed to the debugger, it indicates |
| // that the "variable" is not a variable at all, but rather is a |
| // class name. For example, if class Y extends class X, then |
| // we will send a kDTypeTraits for class Y; then we'll send all the |
| // members of class Y; then we'll send a kDTypeTraits for class X; |
| // and then we'll send all the members of class X. This is only |
| // used by the AVM+ debugger. |
| vType = VariableType.UNKNOWN; |
| typeName = Value.TRAITS_TYPE_NAME; |
| break; |
| } |
| |
| case DMessage.kReferenceType: |
| case DMessage.kArrayType: |
| case DMessage.kObjectEndType: |
| case DMessage.kStrictArrayType: |
| case DMessage.kDateType: |
| case DMessage.kLongStringType: |
| case DMessage.kUnsupportedType: |
| case DMessage.kRecordSetType: |
| case DMessage.kXMLType: |
| case DMessage.kTypedObjectType: |
| case DMessage.kAvmPlusObjectType: |
| default: { |
| // System.out.println("<unknown>"); |
| break; |
| } |
| } |
| int isolateId = msg.getTargetIsolate(); |
| // create the variable based on the content we received. |
| DValue valueObject = null; |
| |
| // If value is a Long, then it is the ID of a non-primitive object; |
| // look up to see if we already have that object in our cache. If |
| // it is already in our cache, then we just want to modify the |
| // existing object with the new values. |
| if (value instanceof Long) { |
| valueObject = getValue(((Long) value).longValue(), isolateId); |
| } |
| |
| if (valueObject == null) { |
| // we didn't find it in the cache, so make a new Value |
| |
| if (isPrimitive) { |
| valueObject = DValue.forPrimitive(value, isolateId); |
| valueObject.setAttributes(toAttributes(flags)); |
| } else { |
| valueObject = new DValue(vType, typeName, className, |
| toAttributes(flags), value, isolateId); |
| } |
| |
| if (value instanceof Long |
| && (toAttributes(flags) & VariableAttribute.HAS_GETTER) == 0) |
| putValue(((Long) value).longValue(), valueObject, isolateId); |
| } else { |
| // we found it in the cache, so just modify the properties |
| // of the old Value |
| |
| if (isPrimitive) { |
| // figure out some of the properties |
| DValue temp = DValue.forPrimitive(value, isolateId); |
| vType = temp.getType(); |
| typeName = temp.getTypeName(); |
| className = temp.getClassName(); |
| } |
| |
| valueObject.setType(vType); |
| valueObject.setTypeName(typeName); |
| valueObject.setClassName(className); |
| valueObject.setAttributes(toAttributes(flags)); |
| valueObject.setValue(value); |
| } |
| if (valueObject != null) { |
| valueObject.setIsolateId(isolateId); |
| } |
| DVariable var = new DVariable(name, valueObject, isolateId); |
| return var; |
| } |
| |
| /** |
| * The player sends us a URI using '|' instead of ':' |
| */ |
| public static String convertToURI(String playerURL) { |
| int index = playerURL.indexOf('|'); |
| StringBuilder sb = new StringBuilder(playerURL); |
| while (index > 0) { |
| sb.setCharAt(index, ':'); |
| index = playerURL.indexOf('|', index + 1); |
| } |
| return sb.toString(); |
| } |
| |
| /** |
| * Tell us that we are about to start executing user code in the player, |
| * such as a getter, a setter, or a function call. If a FaultEvent comes in |
| * while the code is executing, it is not added to the event queue in the |
| * normal way -- instead, it is saved, and is returned when |
| * endPlayerCodeExecution() is called. |
| */ |
| public void beginPlayerCodeExecution(int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| state.m_executingPlayerCode = true; |
| state.m_faultEventDuringPlayerCodeExecution = null; |
| } |
| |
| /** |
| * Informs us that user code is no longer executing, and returns the fault, |
| * if any, which occurred while the code was executing. |
| */ |
| public FaultEvent endPlayerCodeExecution(int isolateId) { |
| DManagerIsolateState state = getIsolateState(isolateId); |
| state.m_executingPlayerCode = false; |
| FaultEvent e = state.m_faultEventDuringPlayerCodeExecution; |
| state.m_faultEventDuringPlayerCodeExecution = null; |
| return e; |
| } |
| |
| /** |
| * When we've just received any FaultEvent from the player, this function |
| * gets called. If a getter/setter is currently executing, we'll save the |
| * fault for someone to get later by calling endGetterSetter(). Otherwise, |
| * normal code execution is taking place, so we'll add the event to the |
| * event queue. |
| */ |
| private void handleFaultEvent(FaultEvent faultEvent) { |
| DManagerIsolateState isolateState = getIsolateState(faultEvent.isolateId); |
| boolean executingPlayerCode = isolateState.m_executingPlayerCode; |
| if (executingPlayerCode) { |
| FaultEvent faultEventDuringPlayerCodeExecution = isolateState.m_faultEventDuringPlayerCodeExecution; |
| |
| if (faultEventDuringPlayerCodeExecution == null) // only save the |
| // first fault |
| { |
| // save the event away so that when someone later calls |
| // endGetterSetter(), we can return the fault that |
| // occurred |
| isolateState.m_faultEventDuringPlayerCodeExecution = faultEvent; |
| } |
| } else { |
| // regular code is running; so post the event to the |
| // event queue which the client debugger will see |
| addEvent(faultEvent); |
| } |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see flash.tools.debugger.SourceLocator#locateSource(java.lang.String, |
| * java.lang.String, java.lang.String) |
| */ |
| public InputStream locateSource(String path, String pkg, String name) { |
| if (m_sourceLocator != null) |
| return m_sourceLocator.locateSource(path, pkg, name); |
| |
| return null; |
| } |
| |
| /* |
| * (non-Javadoc) |
| * |
| * @see flash.tools.debugger.SourceLocator#getChangeCount() |
| */ |
| public int getChangeCount() { |
| if (m_sourceLocator != null) |
| return m_sourceLocator.getChangeCount(); |
| |
| return 0; |
| } |
| |
| /** |
| * Returns the value of a Flash Player option that was requested by |
| * OutGetOption and returned by InOption. |
| * |
| * @param optionName |
| * the name of the option |
| * @return its value, or null |
| */ |
| public String getOption(String optionName) { |
| return m_options.get(optionName); |
| } |
| } |