You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@flex.apache.org by ah...@apache.org on 2016/04/22 21:29:41 UTC
[19/52] [abbrv] [partial] git commit: [flex-falcon]
[refs/heads/feature/maven-migration-test] - move stuff to where I think Maven
wants it
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DMessage.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DMessage.java b/debugger/src/main/java/flash/tools/debugger/concrete/DMessage.java
new file mode 100644
index 0000000..a0658a5
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DMessage.java
@@ -0,0 +1,1016 @@
+/*
+ * 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.lang.ArrayIndexOutOfBoundsException;
+import java.io.UnsupportedEncodingException;
+
+import flash.tools.debugger.Isolate;
+import flash.util.FieldFormat;
+import flash.util.Trace;
+
+/**
+ * DMessage.java
+ *
+ * Wraps the contents of a specific message and provides a set of APIs that allow for
+ * typed extraction of fields within the message.
+ *
+ * No interpretation of the messages contents is performed, this is left to the
+ * user of this class. The code was constructed in this fashion, so that it more
+ * closely mimics the DataReader/DataWriter classes used in the Player code.
+ *
+ * The type of the message should be one of the InXXX or OutXXX constant integers,
+ * but no checking of conformance is provided in this class.
+ */
+public class DMessage
+{
+ /**
+ * This set of constants defines the message types RECEIVED from the player
+ * through our debug socket
+ */
+ public static final int InUnknown = -1;
+ public static final int InSetMenuState = 0;
+ public static final int InSetProperty = 1;
+ public static final int InExit = 2;
+ public static final int InNewObject = 3;
+ public static final int InRemoveObject = 4;
+ public static final int InTrace = 5;
+ public static final int InErrorTarget = 6;
+ public static final int InErrorExecLimit = 7;
+ public static final int InErrorWith = 8;
+ public static final int InErrorProtoLimit = 9;
+ public static final int InSetVariable = 10;
+ public static final int InDeleteVariable = 11;
+ public static final int InParam = 12;
+ public static final int InPlaceObject = 13;
+ public static final int InScript = 14;
+ public static final int InAskBreakpoints = 15;
+ public static final int InBreakAt = 16;
+ public static final int InContinue = 17;
+ public static final int InSetLocalVariables = 18;
+ public static final int InSetBreakpoint = 19;
+ public static final int InNumScript = 20;
+ public static final int InRemoveScript = 21;
+ public static final int InRemoveBreakpoint = 22;
+ public static final int InNotSynced = 23;
+ public static final int InErrorURLOpen = 24;
+ public static final int InProcessTag = 25;
+ public static final int InVersion = 26;
+ public static final int InBreakAtExt = 27;
+ public static final int InSetVariable2 = 28;
+ public static final int InSquelch = 29;
+ public static final int InGetVariable = 30;
+ public static final int InFrame = 31;
+ public static final int InOption = 32;
+ public static final int InWatch = 33;
+ public static final int InGetSwf = 34;
+ public static final int InGetSwd = 35;
+ public static final int InErrorException = 36;
+ public static final int InErrorStackUnderflow = 37;
+ public static final int InErrorZeroDivide = 38;
+ public static final int InErrorScriptStuck = 39;
+ public static final int InBreakReason = 40;
+ public static final int InGetActions = 41;
+ public static final int InSwfInfo = 42;
+ public static final int InConstantPool = 43;
+ public static final int InErrorConsole = 44;
+ public static final int InGetFncNames = 45;
+ // 46 through 52 are for profiling
+ public static final int InCallFunction = 54;
+ public static final int InWatch2 = 55;
+ public static final int InPassAllExceptionsToDebugger = 56;
+ public static final int InBinaryOp = 57;
+ public static final int InIsolateCreate = 58;
+ public static final int InIsolateExit = 59;
+ public static final int InIsolateEnumerate = 60;
+ public static final int InSetActiveIsolate = 61;
+ public static final int InIsolate = 62;
+ public static final int InSetExceptionBreakpoint = 63;
+ public static final int InRemoveExceptionBreakpoint = 64;
+ // If you add another message here, adjust the following line
+ // and add a new case to the inTypeName() method below.
+ public static final int InSIZE = InRemoveExceptionBreakpoint + 1; /* last ID used +1 */
+
+ /**
+ * This set of constants defines the message types SENT to the player from our
+ * debug socket (WARNING: ID space overlaps with InXXX)
+ */
+ public static final int OutUnknown = -2;
+ public static final int OutZoomIn = 0;
+ public static final int OutZoomOut = 1;
+ public static final int OutZoom100 = 2;
+ public static final int OutHome = 3;
+ public static final int OutSetQuality = 4;
+ public static final int OutPlay = 5;
+ public static final int OutLoop = 6;
+ public static final int OutRewind = 7;
+ public static final int OutForward = 8;
+ public static final int OutBack = 9;
+ public static final int OutPrint = 10;
+ public static final int OutSetVariable = 11;
+ public static final int OutSetProperty = 12;
+ public static final int OutExit = 13;
+ public static final int OutSetFocus = 14;
+ public static final int OutContinue = 15;
+ public static final int OutStopDebug = 16;
+ public static final int OutSetBreakpoints = 17;
+ public static final int OutRemoveBreakpoints = 18;
+ public static final int OutRemoveAllBreakpoints = 19;
+ public static final int OutStepOver = 20;
+ public static final int OutStepInto = 21;
+ public static final int OutStepOut = 22;
+ public static final int OutProcessedTag = 23;
+ public static final int OutSetSquelch = 24;
+ public static final int OutGetVariable = 25;
+ public static final int OutGetFrame = 26;
+ public static final int OutGetOption = 27;
+ public static final int OutSetOption = 28;
+ public static final int OutAddWatch = 29; // 16-bit id; used for as2
+ public static final int OutRemoveWatch = 30; // 16-bit id; used for as2
+ public static final int OutStepContinue = 31;
+ public static final int OutGetSwf = 32;
+ public static final int OutGetSwd = 33;
+ public static final int OutGetVariableWhichInvokesGetter = 34;
+ public static final int OutGetBreakReason = 35;
+ public static final int OutGetActions = 36;
+ public static final int OutSetActions = 37;
+ public static final int OutSwfInfo = 38;
+ public static final int OutConstantPool = 39;
+ public static final int OutGetFncNames = 40;
+ // 41 through 47 are for profiling
+ public static final int OutCallFunction = 48;
+ public static final int OutAddWatch2 = 49; // 32-bit id; used for as3
+ public static final int OutRemoveWatch2 = 50; // 32-bit id; used for as3
+ public static final int OutPassAllExceptionsToDebugger = 51;
+ public static final int OutBinaryOp = 52;
+ public static final int OutIsolateEnumerate = 53;
+ public static final int OutSetActiveIsolate = 54;
+ public static final int OutSetExceptionBreakpoint = 55;
+ public static final int OutRemoveExceptionBreakpoint= 56;
+ // If you add another message here, adjust the following line
+ // and add a new case to the outTypeName() method below.
+ public static final int OutSIZE = OutRemoveExceptionBreakpoint + 1; /* last ID used +1 */
+
+ /**
+ * Enums originally extracted from shared_tcserver/tcparser.h; these correspond
+ * to Flash player values that are currently in playerdebugger.h, class DebugAtomType.
+ */
+ public static final int kNumberType = 0;
+ public static final int kBooleanType = 1;
+ public static final int kStringType = 2;
+ public static final int kObjectType = 3;
+ public static final int kMovieClipType = 4;
+ public static final int kNullType = 5;
+ public static final int kUndefinedType = 6;
+ public static final int kReferenceType = 7;
+ public static final int kArrayType = 8;
+ public static final int kObjectEndType = 9;
+ public static final int kStrictArrayType = 10;
+ public static final int kDateType = 11;
+ public static final int kLongStringType = 12;
+ public static final int kUnsupportedType = 13;
+ public static final int kRecordSetType = 14;
+ public static final int kXMLType = 15;
+ public static final int kTypedObjectType = 16;
+ public static final int kAvmPlusObjectType = 17;
+ public static final int kNamespaceType = 18;
+ public static final int kTraitsType = 19; // 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.
+
+ /* byte array of our message and current index into it */
+ byte[] m_content; /* the data bytes of the message */
+ int m_index; /* current position within the content array */
+ int m_type; /* one of OutXXX or InXXX integer constants */
+
+ /**
+ * Pointer size (in bytes) expected by the Flash player; either
+ * 4 for the 32-bit player, or 8 for the 64-bit player.
+ */
+ private static int m_sizeofPtr;
+
+ /* Debugging only: The contents of this message, formatted as a string for display */
+ private StringBuilder m_debugFormatted;
+ /* Debugging only: The number of bytes from the input that we have formatted into m_debugFormatted */
+ private int m_debugFormattedThroughIndex;
+
+ private int m_targetIsolate;
+
+ /* used by our cache to create empty DMessages */
+ public DMessage(int size)
+ {
+ m_content = new byte[size];
+ m_debugFormatted = new StringBuilder();
+ m_debugFormattedThroughIndex = 0;
+ m_targetIsolate = Isolate.DEFAULT_ID;
+ clear();
+ }
+
+ /* getters/setters */
+ public int getType() { return m_type; }
+ public String getInTypeName() { return inTypeName(getType()); }
+ public String getOutTypeName() { return outTypeName(getType()); }
+ public byte[] getData() { return m_content; }
+ public int getSize() { return (m_content == null) ? 0 : m_content.length; }
+ public int getRemaining() { return getSize()-m_index; }
+ public int getPosition() { return m_index; }
+ public int getTargetIsolate() { return m_targetIsolate; }
+ public void setType(int t) { m_type = t; }
+ public void setTargetIsolate(int id) {m_targetIsolate = id;}
+
+ /**
+ * Gets pointer size (in bytes) expected by the Flash player; either
+ * 4 for the 32-bit player, or 8 for the 64-bit player.
+ */
+ public static int getSizeofPtr()
+ {
+ assert m_sizeofPtr != 0;
+ return m_sizeofPtr;
+ }
+
+ /**
+ * Sets pointer size (in bytes) expected by the Flash player; either
+ * 4 for the 32-bit player, or 8 for the 64-bit player.
+ */
+ public static void setSizeofPtr(int size)
+ {
+ assert size != 0;
+ m_sizeofPtr = size;
+ }
+
+ /**
+ * Allow the message to be 're-parsed' by someone else
+ */
+ public void reset()
+ {
+ m_index = 0;
+ }
+
+ /**
+ * Allow the message to be reused later
+ */
+ public void clear()
+ {
+ setType(-1);
+ setTargetIsolate(Isolate.DEFAULT_ID);
+ m_debugFormatted.setLength(0);
+ m_debugFormattedThroughIndex = 0;
+ reset();
+ }
+
+ private long get(int bytes) throws ArrayIndexOutOfBoundsException
+ {
+ if (m_index+bytes > m_content.length)
+ throw new ArrayIndexOutOfBoundsException(m_content.length-m_index+" < "+bytes); //$NON-NLS-1$
+
+ long value = 0;
+ for (int i=0; i<bytes; ++i) {
+ long byteValue = m_content[m_index++] & 0xff;
+ long byteValueShifted = byteValue << (8*i);
+ value |= byteValueShifted;
+ }
+
+ debugAppendNumber(value, bytes);
+ return value;
+ }
+
+ /**
+ * Extract the next byte
+ */
+ public int getByte() throws ArrayIndexOutOfBoundsException
+ {
+ return (int) get(1);
+ }
+
+ /**
+ * Extract the next 2 bytes, which form a 16b integer, from the message
+ */
+ public int getWord() throws ArrayIndexOutOfBoundsException
+ {
+ return (int) get(2);
+ }
+
+ /**
+ * Extract the next 4 bytes, which form a 32b integer, from the message
+ */
+ public long getDWord() throws ArrayIndexOutOfBoundsException
+ {
+ return get(4);
+ }
+
+ /**
+ * Extract the next 8 bytes, which form a 64b integer, from the message
+ */
+ public long getLong() throws ArrayIndexOutOfBoundsException
+ {
+ return get(8);
+ }
+
+ /**
+ * Extract a pointer from the message -- either 8 bytes or 4 bytes,
+ * depending on how big pointers are in the target Flash player
+ */
+ public long getPtr() throws ArrayIndexOutOfBoundsException
+ {
+ return get(getSizeofPtr());
+ }
+
+ /**
+ * Heart wrenchingly slow but since we don't have a length so we can't
+ * do much better
+ */
+ public String getString() throws ArrayIndexOutOfBoundsException
+ {
+ int startAt = m_index;
+ boolean done = false;
+
+ /* scan looking for a terminating null */
+ while(!done)
+ {
+ int ch = m_content[m_index++];
+ if (ch == 0)
+ done = true;
+ else if (m_index > m_content.length)
+ throw new ArrayIndexOutOfBoundsException("no string terminator found @"+m_index); //$NON-NLS-1$
+ }
+
+ /* build a new string and return it */
+ String s;
+ try
+ {
+ // The player uses UTF-8
+ s = new String(m_content, startAt, m_index-startAt-1, "UTF-8"); //$NON-NLS-1$
+ }
+ catch(UnsupportedEncodingException uee)
+ {
+ // couldn't convert so let's try the default
+ s = new String(m_content, startAt, m_index-startAt-1);
+ }
+ debugAppendString(s);
+ return s;
+ }
+
+ /**
+ * Appends a number to the end of the message
+ * @param val the number
+ * @param bytes how many bytes should be written
+ */
+ public void put(long val, int bytes) throws ArrayIndexOutOfBoundsException
+ {
+ if (m_index+bytes > m_content.length)
+ throw new ArrayIndexOutOfBoundsException(m_content.length-m_index+" < "+bytes); //$NON-NLS-1$
+
+ for (int i=0; i<bytes; ++i)
+ m_content[m_index++] = (byte)(val >> 8*i);
+
+ debugAppendNumber(val, bytes);
+ }
+
+ /**
+ * Appends a byte to the end of the message
+ */
+ public void putByte(byte val) throws ArrayIndexOutOfBoundsException
+ {
+ put(val, 1);
+ }
+
+ /**
+ * Appends 2 bytes, which form a 16b integer, into the message
+ */
+ public void putWord(int val) throws ArrayIndexOutOfBoundsException
+ {
+ put(val, 2);
+ }
+
+ /**
+ * Appends 4 bytes, which form a 32b integer, into the message
+ */
+ public void putDWord(int val) throws ArrayIndexOutOfBoundsException
+ {
+ put(val, 4);
+ }
+
+ /**
+ * Appends 8 bytes, which form a 64b integer, into the message
+ */
+ public void putLong(long val) throws ArrayIndexOutOfBoundsException
+ {
+ put(val, 8);
+ }
+
+ /**
+ * Appends a pointer into the message -- either 8 bytes or 4 bytes,
+ * depending on how big pointers are in the target Flash player
+ */
+ public void putPtr(long val) throws ArrayIndexOutOfBoundsException
+ {
+ put(val, getSizeofPtr());
+ }
+
+ /**
+ * Helper to get the number of bytes that a string will need when it is sent
+ * across the socket to the Flash player. Do *not* use string.length(),
+ * because that will return an incorrect result for strings that have non-
+ * ASCII characters.
+ */
+ public static int getStringLength(String s)
+ {
+ try
+ {
+ return s.getBytes("UTF-8").length; //$NON-NLS-1$
+ }
+ catch (UnsupportedEncodingException e)
+ {
+ if (Trace.error) Trace.trace(e.toString());
+ return 0;
+ }
+ }
+
+ /**
+ * Place a string into the message (using UTF-8 encoding)
+ */
+ public void putString(String s) throws ArrayIndexOutOfBoundsException, UnsupportedEncodingException
+ {
+ /* convert the string into a byte array */
+ byte[] bytes = s.getBytes("UTF-8"); //$NON-NLS-1$
+ int length = bytes.length;
+ int endAt = m_index + length + 1;
+
+ if (endAt > m_content.length)
+ throw new ArrayIndexOutOfBoundsException(endAt+" > "+m_content.length); //$NON-NLS-1$
+
+ /* copy the string as a byte array */
+ System.arraycopy(bytes, 0, m_content, m_index, length);
+ m_index += length;
+
+ /* now the null terminator */
+ m_content[m_index++] = '\0';
+
+ debugAppendString(s);
+ }
+
+ // Debugging helper function: we've parsed a number out of the stream of input bytes,
+ // so record that as a hex number of the appropriate length, e.g. "0x12" or "0x1234"
+ // or "0x12345678", depending on numBytes.
+ private void debugAppendNumber(long value, int numBytes)
+ {
+ if (PlayerSession.m_debugMsgOn || PlayerSession.m_debugMsgFileOn)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append("0x"); //$NON-NLS-1$
+ FieldFormat.formatLongToHex(sb, value, numBytes * 2, true);
+ debugAppend(sb.toString());
+ }
+ }
+
+ // Debugging helper function: we've parsed a string out of the stream of input bytes,
+ // so record it as a quoted string in the formatted debugging output.
+ private void debugAppendString(String s)
+ {
+ if (PlayerSession.m_debugMsgOn || PlayerSession.m_debugMsgFileOn)
+ debugAppend('"' + s + '"');
+ }
+
+ // Debugging helper function: append a string to the formatted debugging output.
+ private void debugAppend(String s)
+ {
+ if (PlayerSession.m_debugMsgOn || PlayerSession.m_debugMsgFileOn)
+ {
+ if (m_index > m_debugFormattedThroughIndex)
+ {
+ m_debugFormattedThroughIndex = m_index;
+ if (m_debugFormatted.length() > 0)
+ m_debugFormatted.append(' ');
+ m_debugFormatted.append(s);
+ }
+ }
+ }
+
+ public String inToString() { return inToString(16); }
+
+ public String inToString(int maxContentBytes)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getInTypeName());
+ sb.append('[');
+ sb.append(getSize());
+ sb.append("] "); //$NON-NLS-1$
+ if (getSize() > 0)
+ appendContent(sb, maxContentBytes);
+
+ return sb.toString();
+ }
+
+ public String outToString() { return outToString(16); }
+
+ public String outToString(int maxContentBytes)
+ {
+ StringBuilder sb = new StringBuilder();
+ sb.append(getOutTypeName());
+ sb.append('[');
+ sb.append(getSize());
+ sb.append("] "); //$NON-NLS-1$
+ if (getSize() > 0)
+ appendContent(sb, maxContentBytes);
+
+ return sb.toString();
+ }
+
+ public StringBuilder appendContent(StringBuilder sb, int max)
+ {
+ int size = getSize();
+ byte[] data = getData();
+ int i = 0;
+
+ // First, output formatted content -- content for which some of the other functions
+ // in this class, such as getDWord and getString, did formatting.
+ sb.append(m_debugFormatted);
+
+ // Now, for any left-over bytes which no one bothered to parse, output them as hex.
+ for(i=0; i<max && i+m_debugFormattedThroughIndex<size; i++)
+ {
+ int v = data[i+m_debugFormattedThroughIndex] & 0xff;
+ sb.append(" 0x"); //$NON-NLS-1$
+ FieldFormat.formatLongToHex(sb, v, 2, true);
+ }
+
+ if (i+m_debugFormattedThroughIndex < size)
+ sb.append(" ..."); //$NON-NLS-1$
+
+ return sb;
+ }
+
+ /**
+ * Convenience function for converting a type into a name used mainly for debugging
+ * but can also be used during trace facility of command line session
+ */
+ public static String inTypeName(int type)
+ {
+ String s = "InUnknown(" + type + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ switch(type)
+ {
+ case InSetMenuState:
+ s = "InSetMenuState"; //$NON-NLS-1$
+ break;
+
+ case InSetProperty:
+ s = "InSetProperty"; //$NON-NLS-1$
+ break;
+
+ case InExit:
+ s = "InExit"; //$NON-NLS-1$
+ break;
+
+ case InNewObject:
+ s = "InNewObject"; //$NON-NLS-1$
+ break;
+
+ case InRemoveObject:
+ s = "InRemoveObject"; //$NON-NLS-1$
+ break;
+
+ case InTrace:
+ s = "InTrace"; //$NON-NLS-1$
+ break;
+
+ case InErrorTarget:
+ s = "InErrorTarget"; //$NON-NLS-1$
+ break;
+
+ case InErrorExecLimit:
+ s = "InErrorExecLimit"; //$NON-NLS-1$
+ break;
+
+ case InErrorWith:
+ s = "InErrorWith"; //$NON-NLS-1$
+ break;
+
+ case InErrorProtoLimit:
+ s = "InErrorProtoLimit"; //$NON-NLS-1$
+ break;
+
+ case InSetVariable:
+ s = "InSetVariable"; //$NON-NLS-1$
+ break;
+
+ case InDeleteVariable:
+ s = "InDeleteVariable"; //$NON-NLS-1$
+ break;
+
+ case InParam:
+ s = "InParam"; //$NON-NLS-1$
+ break;
+
+ case InPlaceObject:
+ s = "InPlaceObject"; //$NON-NLS-1$
+ break;
+
+ case InScript:
+ s = "InScript"; //$NON-NLS-1$
+ break;
+
+ case InAskBreakpoints:
+ s = "InAskBreakpoints"; //$NON-NLS-1$
+ break;
+
+ case InBreakAt:
+ s = "InBreakAt"; //$NON-NLS-1$
+ break;
+
+ case InContinue:
+ s = "InContinue"; //$NON-NLS-1$
+ break;
+
+ case InSetLocalVariables:
+ s = "InSetLocalVariables"; //$NON-NLS-1$
+ break;
+
+ case InSetBreakpoint:
+ s = "InSetBreakpoint"; //$NON-NLS-1$
+ break;
+
+ case InNumScript:
+ s = "InNumScript"; //$NON-NLS-1$
+ break;
+
+ case InRemoveScript:
+ s = "InRemoveScript"; //$NON-NLS-1$
+ break;
+
+ case InRemoveBreakpoint:
+ s = "InRemoveBreakpoint"; //$NON-NLS-1$
+ break;
+
+ case InNotSynced:
+ s = "InNotSynced"; //$NON-NLS-1$
+ break;
+
+ case InErrorURLOpen:
+ s = "InErrorURLOpen"; //$NON-NLS-1$
+ break;
+
+ case InProcessTag:
+ s = "InProcessTag"; //$NON-NLS-1$
+ break;
+
+ case InVersion:
+ s = "InVersion"; //$NON-NLS-1$
+ break;
+
+ case InBreakAtExt:
+ s = "InBreakAtExt"; //$NON-NLS-1$
+ break;
+
+ case InSetVariable2:
+ s = "InSetVariable2"; //$NON-NLS-1$
+ break;
+
+ case InSquelch:
+ s = "InSquelch"; //$NON-NLS-1$
+ break;
+
+ case InGetVariable:
+ s = "InGetVariable"; //$NON-NLS-1$
+ break;
+
+ case InFrame:
+ s = "InFrame"; //$NON-NLS-1$
+ break;
+
+ case InOption:
+ s = "InOption"; //$NON-NLS-1$
+ break;
+
+ case InWatch:
+ s = "InWatch"; //$NON-NLS-1$
+ break;
+
+ case InGetSwf:
+ s = "InGetSwf"; //$NON-NLS-1$
+ break;
+
+ case InGetSwd:
+ s = "InGetSwd"; //$NON-NLS-1$
+ break;
+
+ case InErrorException:
+ s = "InErrorException"; //$NON-NLS-1$
+ break;
+
+ case InErrorStackUnderflow:
+ s = "InErrorStackUnderflow"; //$NON-NLS-1$
+ break;
+
+ case InErrorZeroDivide:
+ s = "InErrorZeroDivide"; //$NON-NLS-1$
+ break;
+
+ case InErrorScriptStuck:
+ s = "InErrorScriptStuck"; //$NON-NLS-1$
+ break;
+
+ case InBreakReason:
+ s = "InBreakReason"; //$NON-NLS-1$
+ break;
+
+ case InGetActions:
+ s = "InGetActions"; //$NON-NLS-1$
+ break;
+
+ case InSwfInfo:
+ s = "InSwfInfo"; //$NON-NLS-1$
+ break;
+
+ case InConstantPool:
+ s = "InConstantPool"; //$NON-NLS-1$
+ break;
+
+ case InErrorConsole:
+ s = "InErrorConsole"; //$NON-NLS-1$
+ break;
+
+ case InGetFncNames:
+ s = "InGetFncNames"; //$NON-NLS-1$
+ break;
+
+ case InCallFunction:
+ s = "InCallFunction"; //$NON-NLS-1$
+ break;
+
+ case InWatch2:
+ s = "InWatch2"; //$NON-NLS-1$
+ break;
+
+ case InPassAllExceptionsToDebugger:
+ s = "InPassAllExceptionsToDebugger"; //$NON-NLS-1$
+ break;
+
+ case InBinaryOp:
+ s = "InBinaryOp"; //$NON-NLS-1$
+ break;
+
+ case InIsolateCreate:
+ s = "InIsolateCreate"; //$NON-NLS-1$
+ break;
+
+ case InIsolateExit:
+ s = "InIsolateExit"; //$NON-NLS-1$
+ break;
+
+ case InIsolateEnumerate:
+ s = "InIsolateEnumerate"; //$NON-NLS-1$
+ break;
+
+ case InSetActiveIsolate:
+ s = "InSetActiveIsolate"; //$NON-NLS-1$
+ break;
+
+ case InIsolate:
+ s = "InIsolate"; //$NON-NLS-1$
+ break;
+
+ case InSetExceptionBreakpoint:
+ s = "InSetExceptionBreakpoint"; //$NON-NLS-1$
+ break;
+
+ case InRemoveExceptionBreakpoint:
+ s = "InRemoveExceptionBreakpoint"; //$NON-NLS-1$
+ break;
+ }
+ return s;
+ }
+
+ /**
+ * Convenience function for converting a type into a name used mainly for debugging
+ * but can also be used during trace facility of command line session
+ */
+ public static String outTypeName(int type)
+ {
+ String s = "OutUnknown(" + type + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+
+ switch(type)
+ {
+ case OutZoomIn:
+ s = "OutZoomIn"; //$NON-NLS-1$
+ break;
+
+ case OutZoomOut:
+ s = "OutZoomOut"; //$NON-NLS-1$
+ break;
+
+ case OutZoom100:
+ s = "OutZoom100"; //$NON-NLS-1$
+ break;
+
+ case OutHome:
+ s = "OutHome"; //$NON-NLS-1$
+ break;
+
+ case OutSetQuality:
+ s = "OutSetQuality"; //$NON-NLS-1$
+ break;
+
+ case OutPlay:
+ s = "OutPlay"; //$NON-NLS-1$
+ break;
+
+ case OutLoop:
+ s = "OutLoop"; //$NON-NLS-1$
+ break;
+
+ case OutRewind:
+ s = "OutRewind"; //$NON-NLS-1$
+ break;
+
+ case OutForward:
+ s = "OutForward"; //$NON-NLS-1$
+ break;
+
+ case OutBack:
+ s = "OutBack"; //$NON-NLS-1$
+ break;
+
+ case OutPrint:
+ s = "OutPrint"; //$NON-NLS-1$
+ break;
+
+ case OutSetVariable:
+ s = "OutSetVariable"; //$NON-NLS-1$
+ break;
+
+ case OutSetProperty:
+ s = "OutSetProperty"; //$NON-NLS-1$
+ break;
+
+ case OutExit:
+ s = "OutExit"; //$NON-NLS-1$
+ break;
+
+ case OutSetFocus:
+ s = "OutSetFocus"; //$NON-NLS-1$
+ break;
+
+ case OutContinue:
+ s = "OutContinue"; //$NON-NLS-1$
+ break;
+
+ case OutStopDebug:
+ s = "OutStopDebug"; //$NON-NLS-1$
+ break;
+
+ case OutSetBreakpoints:
+ s = "OutSetBreakpoints"; //$NON-NLS-1$
+ break;
+
+ case OutRemoveBreakpoints:
+ s = "OutRemoveBreakpoints"; //$NON-NLS-1$
+ break;
+
+ case OutRemoveAllBreakpoints:
+ s = "OutRemoveAllBreakpoints"; //$NON-NLS-1$
+ break;
+
+ case OutStepOver:
+ s = "OutStepOver"; //$NON-NLS-1$
+ break;
+
+ case OutStepInto:
+ s = "OutStepInto"; //$NON-NLS-1$
+ break;
+
+ case OutStepOut:
+ s = "OutStepOut"; //$NON-NLS-1$
+ break;
+
+ case OutProcessedTag:
+ s = "OutProcessedTag"; //$NON-NLS-1$
+ break;
+
+ case OutSetSquelch:
+ s = "OutSetSquelch"; //$NON-NLS-1$
+ break;
+
+ case OutGetVariable:
+ s = "OutGetVariable"; //$NON-NLS-1$
+ break;
+
+ case OutGetFrame:
+ s = "OutGetFrame"; //$NON-NLS-1$
+ break;
+
+ case OutGetOption:
+ s = "OutGetOption"; //$NON-NLS-1$
+ break;
+
+ case OutSetOption:
+ s = "OutSetOption"; //$NON-NLS-1$
+ break;
+
+ case OutAddWatch:
+ s = "OutAddWatch"; //$NON-NLS-1$
+ break;
+
+ case OutRemoveWatch:
+ s = "OutRemoveWatch"; //$NON-NLS-1$
+ break;
+
+ case OutStepContinue:
+ s = "OutStepContinue"; //$NON-NLS-1$
+ break;
+
+ case OutGetSwf:
+ s = "OutGetSwf"; //$NON-NLS-1$
+ break;
+
+ case OutGetSwd:
+ s = "OutGetSwd"; //$NON-NLS-1$
+ break;
+
+ case OutGetVariableWhichInvokesGetter:
+ s = "OutGetVariableWhichInvokesGetter"; //$NON-NLS-1$
+ break;
+
+ case OutGetBreakReason:
+ s = "OutGetBreakReason"; //$NON-NLS-1$
+ break;
+
+ case OutGetActions:
+ s = "OutGetActions"; //$NON-NLS-1$
+ break;
+
+ case OutSetActions:
+ s = "OutSetActions"; //$NON-NLS-1$
+ break;
+
+ case OutSwfInfo:
+ s = "OutSwfInfo"; //$NON-NLS-1$
+ break;
+
+ case OutConstantPool:
+ s = "OutConstantPool"; //$NON-NLS-1$
+ break;
+
+ case OutGetFncNames:
+ s = "OutGetFncNames"; //$NON-NLS-1$
+ break;
+
+ case OutCallFunction:
+ s = "OutCallFunction"; //$NON-NLS-1$
+ break;
+
+ case OutAddWatch2:
+ s = "OutAddWatch2"; //$NON-NLS-1$
+ break;
+
+ case OutRemoveWatch2:
+ s = "OutRemoveWatch2"; //$NON-NLS-1$
+ break;
+
+ case OutPassAllExceptionsToDebugger:
+ s = "OutPassAllExceptionsToDebugger"; //$NON-NLS-1$
+ break;
+
+ case OutBinaryOp:
+ s = "OutBinaryOp"; //$NON-NLS-1$
+ break;
+
+ case OutIsolateEnumerate:
+ s = "OutIsolateEnumerate"; //$NON-NLS-1$
+ break;
+
+ case OutSetActiveIsolate:
+ s = "OutSetActiveIsolate"; //$NON-NLS-1$
+ break;
+
+ case OutSetExceptionBreakpoint:
+ s = "OutSetExceptionBreakpoint"; //$NON-NLS-1$
+ break;
+
+ case OutRemoveExceptionBreakpoint:
+ s = "OutRemoveExceptionBreakpoint"; //$NON-NLS-1$
+ break;
+
+ }
+ return s;
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCache.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCache.java b/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCache.java
new file mode 100644
index 0000000..319235b
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCache.java
@@ -0,0 +1,110 @@
+/*
+ * 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;
+
+/**
+ * This cache directly manages the creation/destruction of DMessages
+ * by allowing DMessages to be re-used.
+ *
+ * It has been observed that the Player send a tremendous number of
+ * small (< 8Byte of data) messages and that by allocating a fixed
+ * number of these, and then re-using them, we can assist the garbage
+ * collector greatly.
+ *
+ * The cache is arranged as an array whereby DMessages with 'index'
+ * number of bytes for data are housed. It is asssumed that at
+ * any moment in time only one DMessage will be required and thus
+ * this technique works. If DMessages are to be stored for
+ * later processing (implying that many will exist at any moment)
+ * then we need to implement a more sophisticated cache (probably
+ * storing a Vector of DMessages at each index).
+ *
+ * Very large DMessages are currently not cached.
+ *
+ * This is class is a singleton.
+ */
+public class DMessageCache
+{
+ public static final int MAX_CACHED_DATA_SIZE = 128; /* should consume around 4n + n(n+1)/2 bytes */
+
+ /* our cache */
+ static DMessage[] m_cache = new DMessage[MAX_CACHED_DATA_SIZE];
+
+ /**
+ * Obtain a DMessage from the cache if possible, otherwise make one for me.
+ */
+ public static DMessage alloc(int size)
+ {
+ DMessage msg;
+
+ int index = size2Index(size);
+
+ /**
+ * We see if this could possibly be found in our cache,
+ * if so, then see if there is one for us to use,
+ * otherwise create a new one
+ */
+ if (index < 0)
+ msg = new DMessage(size);
+ else if (m_cache[index] == null)
+ msg = new DMessage(size);
+ else
+ {
+ msg = m_cache[index];
+ m_cache[index] = null;
+ }
+
+// System.out.println("msgsize="+size+uft());
+ return msg;
+ }
+
+ private static String uft()
+ {
+ Runtime rt = Runtime.getRuntime();
+ long free = rt.freeMemory(), total = rt.totalMemory(), used = total - free;
+// long max = rt.maxMemory();
+ java.text.NumberFormat nf = java.text.NumberFormat.getInstance() ;
+// System.out.println("used: "+nf.format(used)+" free: "+nf.format(free)+" total: "+nf.format(total)+" max: "+nf.format(max));
+ return ", used "+nf.format(used)+", free "+nf.format(free)+", total "+nf.format(total); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+
+ /**
+ * Put a DMessage into the cache for reuse
+ */
+ public static void free(DMessage msg)
+ {
+ int index = size2Index(msg.getSize());
+
+ msg.clear(); /* clear stuff up for re-use */
+
+ /**
+ * If it is too big we don't store cache, assuming
+ * the GC can do a better job than us at reusing the memory,
+ * Otherwise we put it in our cache
+ */
+ if (index < 0)
+ ;
+ else if (m_cache[index] != null)
+ ; /* bad => need to use a Vector in the array to house multiple DMessages */
+ else
+ m_cache[index] = msg;
+ }
+
+ public static int size2Index(int size) { return ((size < MAX_CACHED_DATA_SIZE) ? size : -1); }
+// public static int size2Index(int size) { return -1; }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCounter.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCounter.java b/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCounter.java
new file mode 100644
index 0000000..e13d601
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DMessageCounter.java
@@ -0,0 +1,208 @@
+/*
+ * 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.util.HashMap;
+import java.util.Map;
+
+import flash.tools.debugger.Isolate;
+
+/**
+ * This class can be to count the number of messages
+ * received during a debug session.
+ *
+ */
+public class DMessageCounter implements DProtocolNotifierIF
+{
+ long[] m_inCounts;
+ long[] m_outCounts;
+ long m_lastIsolate;
+ Object m_isolateLock;
+ boolean m_isolate;
+
+ Map<Long, DMessageCounter> m_isolateCounterMap;
+
+ public DMessageCounter()
+ {
+ m_inCounts = new long[DMessage.InSIZE+1];
+ m_outCounts = new long[DMessage.OutSIZE+1];
+ m_lastIsolate = 1;
+ m_isolateCounterMap = new HashMap<Long, DMessageCounter>();
+ m_isolateLock = new Object();
+ clearArray(m_inCounts);
+ clearArray(m_outCounts);
+ }
+
+ public void disconnected()
+ {
+ // We're being notified (via the DProtocolNotifierIF interface) that
+ // the socket connection has been broken. If anyone is waiting for
+ // a message to come in, they ain't gonna get one. So, we'll notify()
+ // them so that they can wake up and realize that the connection has
+ // been broken.
+ Object inLock = getInLock();
+ synchronized (inLock) { inLock.notifyAll(); }
+ Object outLock = getOutLock();
+ synchronized (outLock) { outLock.notifyAll(); }
+ }
+
+ /**
+ * Returns the object on which external code can call "wait" in order
+ * to block until a message is received.
+ */
+ public Object getInLock() { return m_inCounts; }
+
+ /**
+ * Returns the object on which external code can call "wait" in order
+ * to block until a message is sent.
+ */
+ public Object getOutLock() { return m_outCounts; }
+
+ /**
+ * Collect stats on outgoing messages
+ */
+ public void messageSent(DMessage msg)
+ {
+ int type = msg.getType();
+ if (type < 0 || type >=DMessage.OutSIZE)
+ type = DMessage.OutSIZE;
+ long targetIsolate = msg.getTargetIsolate();
+ Object outLock = getOutLock();
+ if (!m_isolate) {
+ synchronized (m_isolateLock) {
+ if (m_lastIsolate != Isolate.DEFAULT_ID) {
+ DMessageCounter counter = m_isolateCounterMap.get(m_lastIsolate);
+ outLock = counter.getOutLock();
+ }
+ }
+ }
+ synchronized (outLock) {
+
+ if (!m_isolate && targetIsolate != Isolate.DEFAULT_ID) {
+// if (m_isolateCounterMap.containsKey(targetIsolate)) {
+ DMessageCounter counter = m_isolateCounterMap.get(targetIsolate);
+ counter.messageSent(msg);
+ m_outCounts[type] += 1;
+ outLock.notifyAll(); // tell anyone who is waiting that a message has been sent
+ //counter.getOutLock().notifyAll();
+// }
+// else {
+// System.out.println("No counter for worker " + targetIsolate);
+// m_outCounts[type] += 1;
+// outLock.notifyAll(); // tell anyone who is waiting that a message has been sent
+// }
+ }
+ else {
+ m_outCounts[type] += 1;
+ outLock.notifyAll(); // tell anyone who is waiting that a message has been sent
+ }
+ }
+ }
+
+ public void setIsolate(boolean value) {
+ m_isolate = value;
+ }
+
+ /**
+ * Collect stats on the messages
+ */
+ public void messageArrived(DMessage msg, DProtocol which)
+ {
+ /* extract type */
+ int type = msg.getType();
+
+// System.out.println("msg counter ="+type);
+
+ /* anything we don't know about goes in a special slot at the end of the array. */
+ if (type < 0 || type >= DMessage.InSIZE)
+ type = DMessage.InSIZE;
+ Object inLock = getInLock();
+ if (!m_isolate) {
+ synchronized (m_isolateLock) {
+ if (m_lastIsolate != Isolate.DEFAULT_ID) {
+ DMessageCounter counter = m_isolateCounterMap.get(m_lastIsolate);
+ inLock = counter.getInLock();
+ }
+ }
+ }
+
+ synchronized (inLock) {
+ if (type == DMessage.InIsolate) {
+ long isolate = msg.getDWord();
+ if (isolate != Isolate.DEFAULT_ID) {
+ /** Check if our map has a counter for this isolate */
+ if (!m_isolateCounterMap.containsKey(isolate)) {
+ DMessageCounter isolateCounter = new DMessageCounter();
+ isolateCounter.setIsolate(true);
+ m_isolateCounterMap.put(isolate, isolateCounter);
+ }
+ }
+ synchronized (m_isolateLock) {
+ m_lastIsolate = isolate;
+ }
+ m_inCounts[type] += 1;
+ inLock.notifyAll(); // tell anyone who is waiting that a message has been received
+ }
+ else if (!m_isolate && m_lastIsolate != Isolate.DEFAULT_ID) {
+ DMessageCounter counter = m_isolateCounterMap.get(m_lastIsolate);
+ counter.messageArrived(msg, which);
+ synchronized (counter.getInLock()) {
+ counter.getInLock().notifyAll();
+ }
+
+ }
+ else {
+ m_inCounts[type] += 1;
+ inLock.notifyAll(); // tell anyone who is waiting that a message has been received
+ }
+ }
+ }
+
+ /* getters */
+ public long getInCount(int type) { synchronized (getInLock()) { return m_inCounts[type]; } }
+ public long getOutCount(int type) { synchronized (getOutLock()) { return m_outCounts[type]; } }
+
+ public long getIsolateInCount(long isolate, int type) {
+ DMessageCounter counter = m_isolateCounterMap.get(isolate);
+ return counter.getInCount(type);
+ }
+
+ public long getIsolateOutCount(long isolate, int type) {
+ DMessageCounter counter = m_isolateCounterMap.get(isolate);
+ return counter.getOutCount(type);
+ }
+
+ public Object getIsolateInLock(long isolate) {
+ DMessageCounter counter = m_isolateCounterMap.get(isolate);
+ return counter.getInLock();
+ }
+
+
+ /* setters */
+ public void clearInCounts() { synchronized (getInLock()) { clearArray(m_inCounts); } }
+ public void clearOutCounts() { synchronized (getOutLock()) { clearArray(m_outCounts); } }
+
+ /**
+ * Clear out the array
+ */
+ void clearArray(long[] ar)
+ {
+ for(int i=0; i<ar.length; i++)
+ ar[i] = 0;
+ }
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DModule.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DModule.java b/debugger/src/main/java/flash/tools/debugger/concrete/DModule.java
new file mode 100644
index 0000000..d2577cb
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DModule.java
@@ -0,0 +1,832 @@
+/*
+ * 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.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import flash.tools.debugger.NoResponseException;
+import flash.tools.debugger.Session;
+import flash.tools.debugger.SourceFile;
+import flash.tools.debugger.SourceLocator;
+import flash.tools.debugger.VersionException;
+import flash.util.FileUtils;
+
+/**
+ * A module which is uniquly identified by an id, contains
+ * a short and long name and also a script
+ */
+public class DModule implements SourceFile
+{
+ private ScriptText m_script; // lazy-initialized by getScript()
+ private boolean m_gotRealScript;
+ private final String m_rawName;
+ private final String m_shortName;
+ private final String m_path;
+ private final String m_basePath;
+ private final int m_id;
+ private final int m_bitmap;
+ private final ArrayList<Integer> m_line2Offset;
+ private final ArrayList<Object> m_line2Func; // each array is either null, String, or String[]
+ private final HashMap<String, Integer> m_func2FirstLine; // maps function name (String) to first line of function (Integer)
+ private final HashMap<String, Integer> m_func2LastLine; // maps function name (String) to last line of function (Integer)
+ private String m_packageName;
+ private boolean m_gotAllFncNames;
+ private int m_anonymousFunctionCounter = 0;
+ private SourceLocator m_sourceLocator;
+ private int m_sourceLocatorChangeCount;
+ private int m_isolateId;
+ private final static String m_newline = System.getProperty("line.separator"); //$NON-NLS-1$
+
+ /**
+ * @param name filename in "basepath;package;filename" format
+ */
+ public DModule(SourceLocator sourceLocator, int id, int bitmap, String name, String script, int isolateId)
+ {
+ // If the caller gave us the script text, then we will create m_script
+ // now. But if the caller gave us an empty string, then we won't bother
+ // looking for a disk file until someone actually asks for it.
+ if (script != null && script.length() > 0)
+ {
+ m_script = new ScriptText(script);
+ m_gotRealScript = true;
+ }
+
+ NameParser nameParser = new NameParser(name);
+
+ m_sourceLocator = sourceLocator;
+ m_rawName = name;
+ m_basePath = nameParser.getBasePath(); // may be null
+ m_bitmap = bitmap;
+ m_id = id;
+ m_shortName = generateShortName(nameParser);
+ m_path = generatePath(nameParser);
+ m_line2Offset = new ArrayList<Integer>();
+ m_line2Func = new ArrayList<Object>();
+ m_func2FirstLine = new HashMap<String, Integer>();
+ m_func2LastLine = new HashMap<String, Integer>();
+ m_packageName = nameParser.getPackage();
+ m_gotAllFncNames = false;
+ m_isolateId = isolateId;
+ }
+
+ public synchronized ScriptText getScript()
+ {
+ // If we have been using "dummy" source, and the user has changed the list of
+ // directories that are searched for source, then we want to search again
+ if (!m_gotRealScript &&
+ m_sourceLocator != null &&
+ m_sourceLocator.getChangeCount() != m_sourceLocatorChangeCount)
+ {
+ m_script = null;
+ }
+
+ // lazy-initialize m_script, so that we don't read a disk file until
+ // someone actually needs to look at the file
+ if (m_script == null)
+ {
+ String script = scriptFromDisk(getRawName());
+ if (script == null)
+ {
+ script = ""; // use dummy source for now //$NON-NLS-1$
+ }
+ else
+ {
+ m_gotRealScript = true; // we got the real source
+ }
+ m_script = new ScriptText(script);
+ }
+ return m_script;
+ }
+
+ /* getters */
+ public String getBasePath() { return m_basePath; }
+ public String getName() { return m_shortName; }
+ public String getFullPath() { return m_path; }
+ public String getPackageName() { return (m_packageName == null) ? "" : m_packageName; } //$NON-NLS-1$
+ public String getRawName() { return m_rawName; }
+ public int getId() { return m_id; }
+ public int getBitmap() { return m_bitmap; }
+ public int getLineCount() { return getScript().getLineCount(); }
+ public String getLine(int i) { return (i > getLineCount()) ? "// code goes here" : getScript().getLine(i); } //$NON-NLS-1$
+
+ void setPackageName(String name) { m_packageName = name; }
+
+ /**
+ * @return the offset within the swf for a given line
+ * of source. 0 if unknown.
+ */
+ public int getOffsetForLine(int line)
+ {
+ int offset = 0;
+ if (line < m_line2Offset.size())
+ {
+ Integer i = m_line2Offset.get(line);
+ if (i != null)
+ offset = i.intValue();
+ }
+ return offset;
+ }
+
+ public int getLineForFunctionName(Session s, String name)
+ {
+ int value = -1;
+ primeAllFncNames(s);
+ Integer i = m_func2FirstLine.get(name);
+ if (i != null)
+ value = i.intValue();
+
+ return value;
+ }
+
+ /*
+ * @see flash.tools.debugger.SourceFile#getFunctionNameForLine(flash.tools.debugger.Session, int)
+ */
+ public String getFunctionNameForLine(Session s, int line)
+ {
+ primeFncName(s, line);
+
+ String[] funcNames = getFunctionNamesForLine(s, line);
+
+ if (funcNames != null && funcNames.length == 1)
+ return funcNames[0];
+ else
+ return null;
+ }
+
+ /**
+ * Return the function names for a given line number, or an empty array
+ * if there are none; never returns <code>null</code>.
+ */
+ private String[] getFunctionNamesForLine(Session s, int line)
+ {
+ primeFncName(s, line);
+
+ if (line < m_line2Func.size())
+ {
+ Object obj = m_line2Func.get(line);
+
+ if (obj instanceof String)
+ return new String[] { (String) obj };
+ else if (obj instanceof String[])
+ return (String[]) obj;
+ }
+
+ return new String[0];
+ }
+
+ public String[] getFunctionNames(Session s)
+ {
+ /* find out the size of the array */
+ primeAllFncNames(s);
+ int count = m_func2FirstLine.size();
+ return m_func2FirstLine.keySet().toArray(new String[count]);
+ }
+
+ private static String generateShortName(NameParser nameParser)
+ {
+ String name = nameParser.getOriginalName();
+ String s = name;
+
+ if (nameParser.isPathPackageAndFilename()) {
+ s = nameParser.getFilename();
+ } else {
+ /* do we have a file name? */
+ int dotAt = name.lastIndexOf('.');
+ if (dotAt > 1)
+ {
+ /* yes let's strip the directory off */
+ int lastSlashAt = name.lastIndexOf('\\', dotAt);
+ lastSlashAt = Math.max(lastSlashAt, name.lastIndexOf('/', dotAt));
+
+ s = name.substring(lastSlashAt+1);
+ }
+ else
+ {
+ /* not a file name ... */
+ s = name;
+ }
+ }
+ return s.trim();
+ }
+
+ /**
+ * Produce a name that contains a file specification including full path.
+ * File names may come in as 'mx.bla : file:/bla.foo.as' or as
+ * 'file://bla.foo.as' or as 'C:\'(?) or as 'basepath;package;filename'
+ */
+ private static String generatePath(NameParser nameParser)
+ {
+ String name = nameParser.getOriginalName();
+ String s = name;
+
+ /* strip off first colon of stuff if package exists */
+ int colonAt = name.indexOf(':');
+ if (colonAt > 1 && !name.startsWith("Actions for")) //$NON-NLS-1$
+ {
+ if (name.charAt(colonAt+1) == ' ')
+ s = name.substring(colonAt+2);
+ }
+ else if (name.indexOf('.') > -1 && name.charAt(0) != '<' )
+ {
+ /* some other type of file name */
+ s = nameParser.recombine();
+ }
+ else
+ {
+ // no path
+ s = ""; //$NON-NLS-1$
+ }
+ return s.trim();
+ }
+
+ public void lineMapping(StringBuilder sb)
+ {
+ Map<String, String> args = new HashMap<String, String>();
+ args.put("fileName", getName() ); //$NON-NLS-1$
+ args.put("fileNumber", Integer.toString(getId()) ); //$NON-NLS-1$
+ sb.append(PlayerSessionManager.getLocalizationManager().getLocalizedTextString("functionsInFile", args)); //$NON-NLS-1$
+ sb.append(m_newline);
+
+ String[] funcNames = m_func2FirstLine.keySet().toArray(new String[m_func2FirstLine.size()]);
+ Arrays.sort(funcNames, new Comparator<String>() {
+
+ public int compare(String o1, String o2) {
+ int line1 = m_func2FirstLine.get(o1).intValue();
+ int line2 = m_func2FirstLine.get(o2).intValue();
+ return line1 - line2;
+ }
+
+ });
+
+ for (int i=0; i<funcNames.length; ++i)
+ {
+ String name = funcNames[i];
+ int firstLine = m_func2FirstLine.get(name).intValue();
+ int lastLine = m_func2LastLine.get(name).intValue();
+
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(name);
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(firstLine);
+ sb.append(" "); //$NON-NLS-1$
+ sb.append(lastLine);
+ sb.append(m_newline);
+ }
+ }
+
+ int compareTo(DModule other)
+ {
+ return getName().compareTo(other.getName());
+ }
+
+ /**
+ * Called in order to make sure that we have a function name available
+ * at the given location. For AVM+ swfs we don't need a swd and therefore
+ * don't have access to function names in the same fashion.
+ * We need to ask the player for a particular function name.
+ */
+ void primeFncName(Session s, int line)
+ {
+ // for now we do all, optimize later
+ primeAllFncNames(s);
+ }
+
+ void primeAllFncNames(Session s)
+ {
+ // send out the request for all functions that the player knows
+ // about for this module
+
+ // we block on this call waiting for an answer and after we get it
+ // the DManager thread should have populated our mapping tables
+ // under the covers. If its fails then no biggie we just won't
+ // see anything in the tables.
+ PlayerSession ps = (PlayerSession)s;
+ if (!m_gotAllFncNames && ps.playerVersion() >= 9)
+ {
+ try
+ {
+ ps.requestFunctionNames(m_id, -1, m_isolateId);
+ }
+ catch (VersionException e)
+ {
+ ;
+ }
+ catch (NoResponseException e)
+ {
+ ;
+ }
+ }
+ m_gotAllFncNames = true;
+ }
+
+ void addLineFunctionInfo(int offset, int line, String funcName)
+ {
+ addLineFunctionInfo(offset, line, line, funcName);
+ }
+
+ /**
+ * Called by DSwfInfo in order to add function name / line / offset mapping
+ * information to the module.
+ */
+ void addLineFunctionInfo(int offset, int firstLine, int lastLine, String funcName)
+ {
+ int line;
+
+ // strip down the function name
+ if (funcName == null || funcName.length() == 0)
+ {
+ funcName = "<anonymous$" + (++m_anonymousFunctionCounter) + ">"; //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ // colons or slashes then this is an AS3 name, strip off the core::
+ int colon = funcName.lastIndexOf(':');
+ int slash = funcName.lastIndexOf('/');
+ if (colon > -1 || slash > -1)
+ {
+ int greater = Math.max(colon, slash);
+ funcName = funcName.substring(greater+1);
+ }
+ else
+ {
+ int dot = funcName.lastIndexOf('.');
+ if (dot > -1)
+ {
+ // extract function and package
+ String pkg = funcName.substring(0, dot);
+ funcName = funcName.substring(dot+1);
+
+ // attempt to set the package name while we're in here
+ setPackageName(pkg);
+// System.out.println(m_id+"-func="+funcName+",pkg="+pkg);
+ }
+ }
+ }
+
+// System.out.println(m_id+"@"+offset+"="+getPath()+".adding func="+funcName);
+
+ // make sure m_line2Offset is big enough for the lines we're about to set
+ m_line2Offset.ensureCapacity(firstLine+1);
+ while (firstLine >= m_line2Offset.size())
+ m_line2Offset.add(null);
+
+ // add the offset mapping
+ m_line2Offset.set(firstLine, new Integer(offset));
+
+ // make sure m_line2Func is big enough for the lines we're about to se
+ m_line2Func.ensureCapacity(lastLine+1);
+ while (lastLine >= m_line2Func.size())
+ m_line2Func.add(null);
+
+ // offset and byteCode ignored currently, only add the name for the first hit
+ for (line = firstLine; line <= lastLine; ++line)
+ {
+ Object funcs = m_line2Func.get(line);
+ // A line can correspond to more than one function. The most common case
+ // of that is an MXML tag with two event handlers on the same line, e.g.
+ // <mx:Button mouseOver="overHandler()" mouseOut="outHandler()" />;
+ // another case is the line that declares an inner anonymous function:
+ // var f:Function = function() { trace('hi') }
+ // In any such case, we store a list of function names separated by commas,
+ // e.g. "func1, func2"
+ if (funcs == null)
+ {
+ m_line2Func.set(line, funcName);
+ }
+ else if (funcs instanceof String)
+ {
+ String oldFunc = (String) funcs;
+ m_line2Func.set(line, new String[] { oldFunc, funcName });
+ }
+ else if (funcs instanceof String[])
+ {
+ String[] oldFuncs = (String[]) funcs;
+ String[] newFuncs = new String[oldFuncs.length + 1];
+ System.arraycopy(oldFuncs, 0, newFuncs, 0, oldFuncs.length);
+ newFuncs[newFuncs.length - 1] = funcName;
+ m_line2Func.set(line, newFuncs);
+ }
+ }
+
+ // add to our function name list
+ if (m_func2FirstLine.get(funcName) == null)
+ {
+ m_func2FirstLine.put(funcName, new Integer(firstLine));
+ m_func2LastLine.put(funcName, new Integer(lastLine));
+ }
+ }
+
+ /**
+ * Scan the disk looking for the location of where the source resides. May
+ * also peel open a swd file looking for the source file.
+ * @param name original full path name of the source file
+ * @return string containing the contents of the file, or null if not found
+ */
+ private String scriptFromDisk(String name)
+ {
+ // we expect the form of the filename to be in the form
+ // "c:/src/project;debug;myFile.as"
+ // where the semicolons demark the include directory searched by the
+ // compiler followed by package directories then file name.
+ // any slashes are to be forward slash only!
+
+ // translate to neutral form
+ name = name.replace('\\','/'); //@todo remove this when compiler is complete
+
+ // pull the name apart
+ final char SEP = ';';
+ String pkgPart = ""; //$NON-NLS-1$
+ String pathPart = ""; //$NON-NLS-1$
+ String namePart = ""; //$NON-NLS-1$
+ int at = name.indexOf(SEP);
+ if (at > -1)
+ {
+ // have at least 2 parts to name
+ int nextAt = name.indexOf(SEP, at+1);
+ if (nextAt > -1)
+ {
+ // have 3 parts
+ pathPart = name.substring(0, at);
+ pkgPart = name.substring(at+1, nextAt);
+ namePart = name.substring(nextAt+1);
+ }
+ else
+ {
+ // 2 parts means no package.
+ pathPart = name.substring(0, at);
+ namePart = name.substring(at+1);
+ }
+ }
+ else
+ {
+ // should not be here....
+ // trim by last slash
+ at = name.lastIndexOf('/');
+ if (at > -1)
+ {
+ // cheat by looking for dirname "mx" in path
+ int mx = name.lastIndexOf("/mx/"); //$NON-NLS-1$
+ if (mx > -1)
+ {
+ pathPart = name.substring(0, mx);
+ pkgPart = name.substring(mx+1, at);
+ }
+ else
+ {
+ pathPart = name.substring(0, at);
+ }
+
+ namePart = name.substring(at+1);
+ }
+ else
+ {
+ pathPart = "."; //$NON-NLS-1$
+ namePart = name;
+ }
+ }
+
+ String script = null;
+ try
+ {
+ // now try to locate the thing on disk or in a swd.
+ Charset realEncoding = null;
+ Charset bomEncoding = null;
+ InputStream in = locateScriptFile(pathPart, pkgPart, namePart);
+ if (in != null)
+ {
+ try
+ {
+ // Read the file using the appropriate encoding, based on
+ // the BOM (if there is a BOM) or the default charset for
+ // the system (if there isn't a BOM)
+ BufferedInputStream bis = new BufferedInputStream( in );
+ bomEncoding = getEncodingFromBOM(bis);
+ script = pullInSource(bis, bomEncoding);
+
+ // If the file is an XML file with an <?xml> directive,
+ // it may specify a different directive
+ realEncoding = getEncodingFromXMLDirective(script);
+ }
+ finally
+ {
+ try { in.close(); } catch (IOException e) {}
+ }
+ }
+
+ // If we found an <?xml> directive with a specified encoding, and
+ // it doesn't match the encoding we used to read the file initially,
+ // start over.
+ if (realEncoding != null && !realEncoding.equals(bomEncoding))
+ {
+ in = locateScriptFile(pathPart, pkgPart, namePart);
+ if (in != null)
+ {
+ try
+ {
+ // Read the file using the real encoding, based on the
+ // <?xml...> directive
+ BufferedInputStream bis = new BufferedInputStream( in );
+ getEncodingFromBOM(bis);
+ script = pullInSource(bis, realEncoding);
+ }
+ finally
+ {
+ try { in.close(); } catch (IOException e) {}
+ }
+ }
+ }
+ }
+ catch(FileNotFoundException fnf)
+ {
+ fnf.printStackTrace(); // shouldn't really happen
+ }
+ return script;
+ }
+
+ /**
+ * Logic to poke around on disk in order to find the given
+ * filename. We look under the mattress and all other possible
+ * places for the silly thing. We always try locating
+ * the file directly first, if that fails then we hunt out
+ * the swd.
+ */
+ InputStream locateScriptFile(String path, String pkg, String name) throws FileNotFoundException
+ {
+ if (m_sourceLocator != null)
+ {
+ m_sourceLocatorChangeCount = m_sourceLocator.getChangeCount();
+ InputStream is = m_sourceLocator.locateSource(path, pkg, name);
+ if (is != null)
+ return is;
+ }
+
+ // convert slashes first
+ path = path.replace('/', File.separatorChar);
+ pkg = pkg.replace('/', File.separatorChar);
+ File f;
+
+ // use a package base directory if it exists
+ if (path.length() > 0)
+ {
+ try
+ {
+ String pkgAndName = ""; //$NON-NLS-1$
+ if (pkg.length() > 0) // have to do this so we don't end up with just "/filename"
+ pkgAndName += pkg + File.separatorChar;
+ pkgAndName += name;
+ f = new File(path, pkgAndName);
+ if (f.exists())
+ return new FileInputStream(f);
+ }
+ catch(NullPointerException npe)
+ {
+ // skip it.
+ }
+ }
+
+ // try the current directory plus package
+ if (pkg.length() > 0) // new File("", foo) looks in root directory!
+ {
+ f = new File(pkg, name);
+ if (f.exists())
+ return new FileInputStream(f);
+ }
+
+ // look in the current directory without the package
+ f = new File(name);
+ if (f.exists())
+ return new FileInputStream(f);
+
+ // @todo try to pry open a swd file...
+
+ return null;
+ }
+
+ /**
+ * See if this document starts with a BOM and try to figure
+ * out an encoding from it.
+ * @param bis BufferedInputStream for document (so that we can reset the stream
+ * if we establish that the first characters aren't a BOM)
+ * @return CharSet from BOM (or system default / null)
+ */
+ private Charset getEncodingFromBOM(BufferedInputStream bis)
+ {
+ Charset bomEncoding = null;
+ bis.mark(3);
+ String bomEncodingString;
+ try
+ {
+ bomEncodingString = FileUtils.consumeBOM(bis, null);
+ }
+ catch (IOException e)
+ {
+ bomEncodingString = System.getProperty("file.encoding"); //$NON-NLS-1$
+ }
+
+ bomEncoding = Charset.forName(bomEncodingString);
+
+ return bomEncoding;
+ }
+
+ /**
+ * Syntax for an <?xml ...> directive with an encoding (used by getEncodingFromXMLDirective)
+ */
+ private static final Pattern sXMLDeclarationPattern = Pattern.compile("^<\\?xml[^>]*encoding\\s*=\\s*(\"([^\"]*)\"|'([^']*)')"); //$NON-NLS-1$
+
+ /**
+ * See if this document starts with an <?xml ...> directive and
+ * try to figure out an encoding from it.
+ * @param entireSource source of document
+ * @return specified Charset (or null)
+ */
+ private Charset getEncodingFromXMLDirective(String entireSource)
+ {
+ String encoding = null;
+ Matcher xmlDeclarationMatcher = sXMLDeclarationPattern.matcher(entireSource);
+ if (xmlDeclarationMatcher.find())
+ {
+ encoding = xmlDeclarationMatcher.group(2);
+ if (encoding == null)
+ encoding = xmlDeclarationMatcher.group(3);
+
+ try
+ {
+ return Charset.forName(encoding);
+ }
+ catch (IllegalArgumentException e)
+ {}
+ }
+ return null;
+ }
+
+ /**
+ * Given an input stream containing source file contents, read in each line
+ * @param in stream of source file contents (with BOM removed)
+ * @param encoding encoding to use (based on BOM, system default, or <?xml...> directive
+ * if this is null, the system default will be used)
+ * @return source file contents (as String)
+ */
+ String pullInSource(InputStream in, Charset encoding)
+ {
+ String script = ""; //$NON-NLS-1$
+ BufferedReader f = null;
+ try
+ {
+ StringBuilder sb = new StringBuilder();
+ Reader reader = null;
+ if (encoding == null)
+ reader = new InputStreamReader(in);
+ else
+ reader = new InputStreamReader(in, encoding);
+ f = new BufferedReader(reader);
+ String line;
+ while((line = f.readLine()) != null)
+ {
+ sb.append(line);
+ sb.append('\n');
+ }
+ script = sb.toString();
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
+ }
+ return script;
+ }
+
+ /** for debugging */
+ @Override
+ public String toString()
+ {
+ return getFullPath();
+ }
+
+ /**
+ * Given a filename of the form "basepath;package;filename", return an
+ * array of 3 strings, one for each segment.
+ * @param name a string which *may* be of the form "basepath;package;filename"
+ * @return an array of 3 strings for the three pieces; or, if 'name' is
+ * not of expected form, returns null
+ */
+ private static class NameParser
+ {
+ private String fOriginalName;
+ private String fBasePath;
+ private String fPackage;
+ private String fFilename;
+ private String fRecombinedName;
+
+ public NameParser(String name)
+ {
+ fOriginalName = name;
+
+ /* is it of "basepath;package;filename" format? */
+ int semicolonCount = 0;
+ int i = 0;
+ int firstSemi = -1;
+ int lastSemi = -1;
+ while ( (i = name.indexOf(';', i)) >= 0 )
+ {
+ ++semicolonCount;
+ if (firstSemi == -1)
+ firstSemi = i;
+ lastSemi = i;
+ ++i;
+ }
+
+ if (semicolonCount == 2)
+ {
+ fBasePath = name.substring(0, firstSemi);
+ fPackage = name.substring(firstSemi+1, lastSemi);
+ fFilename = name.substring(lastSemi+1);
+ }
+ }
+
+ public boolean isPathPackageAndFilename()
+ {
+ return (fBasePath != null);
+ }
+
+ public String getOriginalName()
+ {
+ return fOriginalName;
+ }
+
+ public String getBasePath()
+ {
+ return fBasePath;
+ }
+
+ public String getFilename()
+ {
+ return fFilename;
+ }
+
+ public String getPackage()
+ {
+ return fPackage;
+ }
+
+ /**
+ * Returns a "recombined" form of the original name.
+ *
+ * For filenames which came in in the form "basepath;package;filename",
+ * the recombined name is the original name with the semicolons replaced
+ * by platform-appropriate slash characters. For any other type of original
+ * name, the recombined name is the same as the incoming name.
+ */
+ public String recombine()
+ {
+ if (fRecombinedName == null)
+ {
+ if (isPathPackageAndFilename())
+ {
+ char slashChar;
+ if (fOriginalName.indexOf('\\') != -1)
+ slashChar = '\\';
+ else
+ slashChar = '/';
+
+ fRecombinedName = fOriginalName.replaceAll(";;", ";").replace(';', slashChar); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ else
+ {
+ fRecombinedName = fOriginalName;
+ }
+ }
+ return fRecombinedName;
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DProtocol.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DProtocol.java b/debugger/src/main/java/flash/tools/debugger/concrete/DProtocol.java
new file mode 100644
index 0000000..7965acb
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DProtocol.java
@@ -0,0 +1,495 @@
+/*
+ * 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.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.SocketTimeoutException;
+import java.util.EnumMap;
+
+import flash.tools.debugger.SessionManager;
+import flash.util.Trace;
+
+/**
+ * Implements the lower portion of Flash Player debug protocol. This class is able to
+ * communicate with the Flash Player sending and receiving any and all messages neccessary
+ * in order to continue a debug session with the Player.
+ *
+ * It does not understand the context of messages that it receives and merely provides
+ * a channel for formatting and unformatting the messages.
+ *
+ * The messages are defined on the flash side in core/debugtags.h and handled in the
+ * code under core/playerdebugger.cpp
+ *
+ * Messages that are received via this class are packaged in a DMessage and then
+ * provided to any listeners if requested. Filtering of incoming messages
+ * at this level is not supported.
+ */
+public class DProtocol implements Runnable
+{
+ public static final int DEBUG_PORT = 7935;
+
+ /* We connect to AIR in the case of AIR on Android over USB */
+ public static final int DEBUG_CONNECT_PORT = 7936;
+
+ private final BufferedInputStream m_in;
+ private final BufferedOutputStream m_out;
+ private final EnumMap<ListenerIndex, DProtocolNotifierIF> m_listeners; // WARNING: accessed from multiple threads
+ private long m_msgRx; // WARNING: accessed from multiple threads; use synchronized (this)
+ private long m_msgTx; // WARNING: accessed from multiple threads; use synchronized (this)
+ private volatile boolean m_stopRx; // WARNING: accessed from multiple threads
+ private volatile Thread m_rxThread; // WARNING: accessed from multiple threads
+ private volatile Exception m_disconnectCause;
+ private volatile Socket m_socket;
+ private boolean m_detectBrokenSocket;
+
+ public enum ListenerIndex
+ {
+ PlayerSession,
+
+ /**
+ * The DMessageCounter must always be the LAST listener, so that the message has
+ * been fully processed before we wake up any threads that were waiting until a
+ * message comes in.
+ */
+ MessageCounter
+ }
+
+ public DProtocol(BufferedInputStream in, BufferedOutputStream out)
+ {
+ m_in = in;
+ m_out = out;
+ m_listeners = new EnumMap<ListenerIndex, DProtocolNotifierIF>(ListenerIndex.class);
+ m_msgRx = 0;
+ m_msgTx = 0;
+ m_stopRx = false;
+ m_rxThread = null;
+ m_socket = null;
+ m_detectBrokenSocket = false;
+ // Create a message counter, which will listen to us for messages
+ addListener(ListenerIndex.MessageCounter, new DMessageCounter());
+ }
+
+ public DProtocol(BufferedInputStream in, BufferedOutputStream out,
+ Socket s, boolean detectBrokenSocket)
+ {
+ this(in, out);
+ m_socket = s;
+ m_detectBrokenSocket = detectBrokenSocket;
+ }
+
+
+ /**
+ * Set the base socket options
+ * @throws SocketException
+ */
+ static void applyBaseSocketSettings(Socket s) throws SocketException
+ {
+ // For performance reasons, it is very important that we setTcpNoDelay(true),
+ // thus disabling Nagle's algorithm. Google for TCP_NODELAY or Nagle
+ // for more information.
+ //
+ // In addition, we now use a BufferedOutputStream instead of an OutputStream.
+ //
+ // These changes result in a huge speedup on the Mac.
+ s.setTcpNoDelay(true);
+ }
+
+ static DProtocol createDProtocolFromSocket(Socket s, boolean detectBrokenSocket) throws IOException
+ {
+ BufferedInputStream in = new BufferedInputStream(s.getInputStream());
+ BufferedOutputStream out = new BufferedOutputStream(s.getOutputStream());
+
+ DProtocol dp = new DProtocol(in, out, s, detectBrokenSocket);
+ return dp;
+ }
+
+ /**
+ * Build a DProtocol object from a the given socket connection.
+ */
+ static DProtocol createFromSocket(Socket s) throws IOException
+ {
+ applyBaseSocketSettings(s);
+ return createDProtocolFromSocket(s, false);
+ }
+
+ /**
+ * Build a DProtocol object from a the given socket connection
+ * and applies socket specific settings set in SessionManager
+ * like socket timeout.
+ */
+ static DProtocol createFromSocket(Socket s, SessionManager sessionManager) throws IOException
+ {
+ applyBaseSocketSettings(s);
+ int socketTimeout = sessionManager.getPreference(SessionManager.PREF_SOCKET_TIMEOUT);
+ boolean checkSocket = false;
+ if (socketTimeout > 0)
+ {
+ s.setSoTimeout(socketTimeout);
+ checkSocket = true;
+ }
+ return createDProtocolFromSocket(s, checkSocket);
+ }
+
+ /**
+ * Allow outside entities to listen for incoming DMessages.
+ *
+ * @param index
+ * the index of this listener. Listeners have a strictly defined
+ * order.
+ * @param n
+ * the listener
+ */
+ public boolean addListener(ListenerIndex index, DProtocolNotifierIF n)
+ {
+ synchronized (m_listeners)
+ {
+ m_listeners.put(index, n);
+ }
+ return true;
+ }
+
+ public long messagesReceived() { synchronized (this) { return m_msgRx; } }
+ public long messagesSent() { synchronized (this) { return m_msgTx; } }
+
+ /**
+ * Entry point for our receive thread
+ */
+ public void run()
+ {
+ try
+ {
+ m_stopRx = false;
+ listenForMessages();
+ }
+ catch(Exception ex)
+ {
+ m_disconnectCause = ex;
+ if (Trace.error &&
+ !(ex instanceof SocketException && ex.getMessage().equalsIgnoreCase("socket closed"))) // closed-socket is not an error //$NON-NLS-1$
+ {
+ ex.printStackTrace();
+ }
+ }
+
+ /* notify our listeners that we are no longer listening; game over */
+ DProtocolNotifierIF[] listeners;
+ synchronized (m_listeners)
+ {
+ listeners = m_listeners.values().toArray(new DProtocolNotifierIF[m_listeners.size()]); // copy the list to avoid multithreading problems
+ }
+ for (int i=0; i<listeners.length; ++i)
+ {
+ DProtocolNotifierIF elem = listeners[i];
+ try
+ {
+ elem.disconnected();
+ }
+ catch(Exception exc) /* catch unchecked exceptions */
+ {
+ if (Trace.error)
+ exc.printStackTrace();
+ }
+ }
+
+ // final notice that this thread is dead!
+ m_rxThread = null;
+ m_socket = null;
+ }
+
+ /**
+ * Create and start up a thread for our receiving messages.
+ */
+ public boolean bind()
+ {
+ /* create a new thread object for us which just listens to incoming messages */
+ boolean worked = true;
+ if (m_rxThread == null)
+ {
+ getMessageCounter().clearInCounts();
+ getMessageCounter().clearOutCounts();
+
+ m_rxThread = new Thread(this, "DJAPI message listener"); //$NON-NLS-1$
+ m_rxThread.setDaemon(true);
+ m_rxThread.start();
+ }
+ else
+ worked = false;
+
+ return worked;
+ }
+
+ /**
+ * Shutdown our receive thread
+ */
+ public boolean unbind()
+ {
+ boolean worked = true;
+ if (m_rxThread == null)
+ worked = false;
+ else
+ m_stopRx = true;
+
+ return worked;
+ }
+
+ /**
+ * Main rx loop which waits for commands and then issues them to anyone listening.
+ */
+ void listenForMessages() throws IOException
+ {
+ DProtocolNotifierIF[] listeners = new DProtocolNotifierIF[0];
+
+ while(!m_stopRx)
+ {
+ /* read the data */
+ try
+ {
+ DMessage msg = rxMessage();
+
+ /* Now traverse our list of interested parties and let them deal with the message */
+ synchronized (m_listeners)
+ {
+ listeners = m_listeners.values().toArray(listeners); // copy the array to avoid multithreading problems
+ }
+ for (int i=0; i<listeners.length; ++i)
+ {
+ DProtocolNotifierIF elem = listeners[i];
+ try
+ {
+ elem.messageArrived(msg, this);
+ }
+ catch (Exception exc) /* catch unchecked exceptions */
+ {
+// if (Trace.error)
+// {
+ System.err.println("Error in listener parsing incoming message :"); //$NON-NLS-1$
+ System.err.println(msg.inToString(16));
+ exc.printStackTrace();
+// }
+ }
+ msg.reset(); /* allow others to reparse the message */
+ }
+
+ /* now dispose with the message */
+ DMessageCache.free(msg);
+ }
+ catch(InterruptedIOException iio)
+ {
+ // this is a healthy exception that we simply ignore, since it means we haven't seen
+ // data for a while; is all.
+ }
+ }
+ }
+
+ /**
+ * Transmit the message down the socket.
+ *
+ * This function is not synchronized; it is only called from one place, which is
+ * PlayerSession.sendMessage(). That function is synchronized.
+ */
+ void txMessage(DMessage message) throws IOException
+ {
+ int size = message.getSize();
+ int command = message.getType();
+
+ //System.out.println("txMessage: " + DMessage.outTypeName(command) + " size=" + size);
+
+ writeDWord(size);
+ writeDWord(command);
+ writeData(message.getData(), size);
+
+ m_out.flush();
+ synchronized (this) { m_msgTx++; }
+ getMessageCounter().messageSent(message);
+ }
+
+ class SendThread extends Thread {
+ public IOException exception = null;
+ public volatile boolean completed = false;
+
+ @Override
+ public void run() {
+ try {
+ DMessage dm = DMessageCache.alloc(4);
+ dm.setType(DMessage.OutSetSquelch);
+ dm.putDWord(1);
+ txMessage(dm);
+ DMessageCache.free(dm);
+ this.completed = true;
+ }
+ catch (IOException e) {
+ this.exception = e;
+ }
+ }
+ }
+ /**
+ * Get the next message on the input stream, using the context contained within
+ * the message itself to demark its end
+ */
+ private DMessage rxMessage() throws IOException
+ {
+ int size = -1;
+ int command = 0;
+
+ try
+ {
+ size = (int)readDWord();
+ command = (int)readDWord();
+ }
+ catch (SocketTimeoutException e)
+ {
+ if (!m_detectBrokenSocket)
+ throw e;
+ //schedule a simple message to be sent for
+ //heartbeat check
+ /**
+ * Our logic kicks in after PREF_SOCKET_TIMEOUT
+ * milliseconds to try and detect broken connection by writing
+ * a squelch message to the player. If the write
+ * succeeds, we assume everything is normal
+ * (we don't wait for an ack). Otherwise, we save the error
+ * that clients of FDB can use.
+ *
+ * On Mac, the write() blocks which is why it must
+ * be done in a separate thread. The thread may take
+ * upto five minutes to die even after interrupt().
+ *
+ * On Windows, the write() succeeds, but we later get
+ * a recv abort.
+ */
+ int oldBufferSize = -1;
+
+ if (m_socket != null) {
+ oldBufferSize = m_socket.getSendBufferSize();
+ m_socket.setSendBufferSize(1);
+ }
+
+ SendThread t = new SendThread();
+ t.start();
+ long waitBegin = System.currentTimeMillis();
+
+ while (true) {
+ try {
+ t.join(1000);
+ if (t.completed)
+ break;
+ } catch (InterruptedException e1) {
+ break;
+ }
+ long waitEnd = System.currentTimeMillis();
+ if (waitEnd - waitBegin > 10000)
+ break;
+ }
+ boolean success = true;
+ if (t.isAlive()) {
+ t.interrupt();
+ success = false;
+ }
+ if (oldBufferSize > 0) {
+ m_socket.setSendBufferSize(oldBufferSize);
+ }
+ if (!t.completed) {
+ success = false;
+ }
+ if (t.exception != null) {
+ throw t.exception;
+ }
+ if (success)
+ throw e;
+ else
+ throw new SocketException("Broken pipe"); //$NON-NLS-1$
+ }
+ //System.out.println("rxMessage: " + DMessage.inTypeName(command) + " size=" + size);
+
+ if (size < 0)
+ throw new IOException("socket closed"); //$NON-NLS-1$
+
+// if (DMessage.inTypeName(command).startsWith("InUnknown")) {
+// System.out.println("Ignoring unknown message");
+// size = 0;
+// }
+
+ /**
+ * Ask our message cache for a message
+ */
+ DMessage message = DMessageCache.alloc(size);
+ byte[] messageContent = message.getData();
+ int offset = 0;
+
+ /* block until we get the entire message, which may come in pieces */
+ while (offset < size)
+ offset += m_in.read(messageContent, offset, size - offset);
+
+ /* now we have the data of the message, set its type and we are done */
+ message.setType(command);
+ synchronized (this) { m_msgRx++; }
+ return message;
+ }
+
+ void writeDWord(long dw) throws IOException
+ {
+ byte b0 = (byte)(dw & 0xff);
+ byte b1 = (byte)((dw >> 8) & 0xff);
+ byte b2 = (byte)((dw >> 16) & 0xff);
+ byte b3 = (byte)((dw >> 24) & 0xff);
+
+ m_out.write(b0);
+ m_out.write(b1);
+ m_out.write(b2);
+ m_out.write(b3);
+ }
+
+ void writeData(byte[] data, long size) throws IOException
+ {
+ if (size > 0)
+ m_out.write(data, 0, (int)size);
+ }
+
+
+ /**
+ * Extract the next 4 bytes, which form a 32b integer, from the stream
+ */
+ long readDWord() throws IOException
+ {
+ int b0 = m_in.read();
+ int b1 = m_in.read();
+ int b2 = m_in.read();
+ int b3 = m_in.read();
+
+ long value = ((b3 << 24) & 0xff000000) | ((b2 << 16) & 0xff0000) | ((b1 << 8) & 0xff00) | (b0 & 0xff);
+ return value;
+ }
+
+ public DMessageCounter getMessageCounter()
+ {
+ synchronized (m_listeners)
+ {
+ return (DMessageCounter) m_listeners.get(ListenerIndex.MessageCounter);
+ }
+ }
+
+ public Exception getDisconnectCause() {
+ return m_disconnectCause;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/07f5a7de/debugger/src/main/java/flash/tools/debugger/concrete/DProtocolNotifierIF.java
----------------------------------------------------------------------
diff --git a/debugger/src/main/java/flash/tools/debugger/concrete/DProtocolNotifierIF.java b/debugger/src/main/java/flash/tools/debugger/concrete/DProtocolNotifierIF.java
new file mode 100644
index 0000000..8144711
--- /dev/null
+++ b/debugger/src/main/java/flash/tools/debugger/concrete/DProtocolNotifierIF.java
@@ -0,0 +1,34 @@
+/*
+ * 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;
+
+/**
+ * Interface for receiving DMessages from the DProtocol object
+ */
+public interface DProtocolNotifierIF
+{
+ /**
+ * Issused when a message has been received from the socket
+ */
+ public void messageArrived(DMessage message, DProtocol which);
+
+ /**
+ * Issued when the socket connection to the player is cut
+ */
+ public void disconnected();
+}