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(); +}
