/// <summary> Walk the actions filling in the names of functions as we go.
		/// This is done by looking for DefineFunction's actions and then
		/// examining the content of the stack for a name.
		/// 
		/// </summary>
		/// <param name="c">list of actions to be traversed
		/// </param>
		/// <param name="swfVersion">version of swf file that housed the ActionList (just use 7 if you don't know)
		/// </param>
		/// <param name="pool">optional; constant pool for the list of actions
		/// </param>
		/// <param name="className">optional; used to locate a constructor function (i.e if funcName == className)
		/// </param>
		/// <param name="profileOffsets">optional; is filled with offsets if a call to a 
		/// function named 'profile' is encountered.  Can be null if caller is not
		/// interested in obtaining this information.
		/// </param>
		public static void walkActions(ActionList c, int swfVersion, String[] pool, String className, System.Collections.IList profileOffsets)
		{
			// assumption: ActionContext c is always not null! try-catch-finally may be busted.
			if (c == null)
				return ;
			
			System.Collections.Stack evalStack = new System.Collections.Stack();
			System.Collections.Hashtable variables = new System.Collections.Hashtable();
			
			// loop again, this time, we register all the actions...
			int offset;
			Action a;
			
			for (int i = 0; i < c.size(); i++)
			{
				offset = c.getOffset(i);
				a = c.getAction(i);
				
				switch (a.code)
				{
					
					// Flash 1 and 2 actions
					case ActionConstants.sactionHasLength: 
					case ActionConstants.sactionNone: 
					case ActionConstants.sactionGotoFrame: 
					case ActionConstants.sactionGetURL: 
					case ActionConstants.sactionNextFrame: 
					case ActionConstants.sactionPrevFrame: 
					case ActionConstants.sactionPlay: 
					case ActionConstants.sactionStop: 
					case ActionConstants.sactionToggleQuality: 
					case ActionConstants.sactionStopSounds: 
					case ActionConstants.sactionWaitForFrame: 
					// Flash 3 Actions
					case ActionConstants.sactionSetTarget: 
					case ActionConstants.sactionGotoLabel: 
						// no action
						break;
						
						// Flash 4 Actions
					
					case ActionConstants.sactionAdd: 
					case ActionConstants.sactionSubtract: 
					case ActionConstants.sactionMultiply: 
					case ActionConstants.sactionDivide: 
					case ActionConstants.sactionEquals: 
					case ActionConstants.sactionLess: 
					case ActionConstants.sactionAnd: 
					case ActionConstants.sactionOr: 
					case ActionConstants.sactionStringEquals: 
					case ActionConstants.sactionStringAdd: 
					case ActionConstants.sactionStringLess: 
					case ActionConstants.sactionMBStringLength: 
					case ActionConstants.sactionGetProperty: 
						// pop, pop, push
						pop(evalStack);
						break;
					
					case ActionConstants.sactionNot: 
					case ActionConstants.sactionStringLength: 
					case ActionConstants.sactionToInteger: 
					case ActionConstants.sactionCharToAscii: 
					case ActionConstants.sactionAsciiToChar: 
					case ActionConstants.sactionMBCharToAscii: 
					case ActionConstants.sactionMBAsciiToChar: 
					case ActionConstants.sactionRandomNumber: 
						// pop, push
						break;
					
					case ActionConstants.sactionGetVariable: 
						Object key = pop(evalStack);
						if (variables[key] == null)
						{
							evalStack.Push(key);
						}
						else
						{
							evalStack.Push(variables[key]);
						}
						break;
					
					case ActionConstants.sactionStringExtract: 
					case ActionConstants.sactionMBStringExtract: 
						// pop, pop, pop, push
						pop(evalStack);
						pop(evalStack);
						break;
					
					case ActionConstants.sactionPush: 
						Push p = (Push) a;
						System.Object o = p.value;
						int type = Push.getTypeCode(o);
						switch (type)
						{
							
							case ActionConstants.kPushStringType: 
								evalStack.Push(o);
								break;
							
							case ActionConstants.kPushNullType: 
								evalStack.Push("null");
								break;
							
							case ActionConstants.kPushUndefinedType: 
								evalStack.Push("undefined");
								break;
							
							case ActionConstants.kPushRegisterType: 
								evalStack.Push(registers[(int) ((SByte) o) & 0xFF]);
								break;
							
							case ActionConstants.kPushConstant8Type: 
							case ActionConstants.kPushConstant16Type: 
								evalStack.Push(pool[Convert.ToInt32(((ValueType) o)) & 0xFFFF]);
								break;
							
							case ActionConstants.kPushFloatType: 
								evalStack.Push(o + "F");
								break;
							
							case ActionConstants.kPushBooleanType: 
							case ActionConstants.kPushDoubleType: 
							case ActionConstants.kPushIntegerType: 
								evalStack.Push(o);
								break;
							
							default: 
								evalStack.Push("type" + type);
								break;
							
						}
						break;
					
					case ActionConstants.sactionIf: 
						pop(evalStack);
						break;
					
					case ActionConstants.sactionPop: 
					case ActionConstants.sactionCall: 
					case ActionConstants.sactionGotoFrame2: 
					case ActionConstants.sactionSetTarget2: 
					case ActionConstants.sactionRemoveSprite: 
					case ActionConstants.sactionWaitForFrame2: 
					case ActionConstants.sactionTrace: 
						// pop
						pop(evalStack);
						break;
					
					case ActionConstants.sactionJump: 
					case ActionConstants.sactionEndDrag: 
						// no action
						break;
					
					case ActionConstants.sactionSetVariable: 
						key = pop(evalStack);
						Object val = pop(evalStack);
						variables[key] = val;
						break;
					
					case ActionConstants.sactionGetURL2: 
						// pop, pop
						pop(evalStack);
						pop(evalStack);
						break;
					
					case ActionConstants.sactionSetProperty: 
					case ActionConstants.sactionCloneSprite: 
						// pop, pop, pop
						pop(evalStack);
						pop(evalStack);
						pop(evalStack);
						break;
					
					case ActionConstants.sactionStartDrag: 
						// pop, pop, pop, if the 3rd pop is non-zero, pop, pop, pop, pop
						pop(evalStack);
						pop(evalStack);
						Object obj = pop(evalStack);
						if (Int32.Parse(obj.ToString()) != 0)
						{
							pop(evalStack);
							pop(evalStack);
							pop(evalStack);
							pop(evalStack);
						}
						break;
					
					case ActionConstants.sactionGetTime: 
						// push
						evalStack.Push(dummy);
						break;
						
						// Flash 5 actions
					
					case ActionConstants.sactionDelete: 
						pop(evalStack);
						break;
					
					case ActionConstants.sactionDefineLocal: 
						// pop, pop
						val = pop(evalStack);
						key = pop(evalStack);
						variables[key] = val;
						break;
					
					case ActionConstants.sactionDefineFunction: 
					case ActionConstants.sactionDefineFunction2: 
						DefineFunction f = (DefineFunction) a;
						
						if (swfVersion > 6 && className != null)
						{
							if (f.name == null || f.name.Length == 0)
							{
								int depth = evalStack.Count;
								if (depth != 0)
								{
									o = evalStack.Peek();
									if (o == dummy)
									{
										f.name = "";
									}
									else if (o != null)
									{
										f.name = o.ToString();
									}
								}
								evalStack.Push(dummy);
							}
							
							if (f.name == "null")
							{
								f.name = "";
							}
							
							if (f.name == null || f.name.Length == 0)
							{
								// do nothing... it's an anonymous function!
							}
							else if (!className.EndsWith(f.name))
							{
								f.name = className + "." + f.name;
							}
							else
							{
								f.name = className + ".[constructor]";
							}
						}
						else
						{
							if (f.name == null || f.name.Length == 0)
							{
                                System.Text.StringBuilder buffer = new System.Text.StringBuilder();

                                Boolean bFirst = true;

								foreach (Object ob in evalStack)
								{
									if (ob == dummy)
									{
										break;
									}
									else if (bFirst)
									{
										buffer.Append(ob);
                                        bFirst = false;
									}
									else
									{
										buffer.Insert(0, '.');
										buffer.Insert(0, ob);
									}
								}
								f.name = buffer.ToString();
								
								if (f.name != null && f.name.IndexOf(".prototype.") == - 1)
								{
									f.name = "";
								}
								evalStack.Push(dummy);
							}
						}
						// evalActions(f.actions);
						break;
					
					case ActionConstants.sactionCallFunction: 
						Object function = pop(evalStack);
						if (profileOffsets != null && "profile".Equals(function))
						{
							profileOffsets.Add((Int32) (offset - 13)); // Push 1
							profileOffsets.Add((Int32) (offset - 5)); // Push 'profile'
							profileOffsets.Add((Int32) offset); // CallFunction
							profileOffsets.Add((Int32) (offset + 1)); // Pop
						}
						int n = Convert.ToInt32(((System.ValueType) pop(evalStack)));
						for (int k = 0; k < n; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionReturn: 
						// return function() { ... } doesn't push...
						pop(evalStack);
						break;
					
					case ActionConstants.sactionModulo: 
						// pop, push
						break;
					
					case ActionConstants.sactionNewObject: 
						pop(evalStack);
						int num = Convert.ToInt32(((ValueType) pop(evalStack)));
						for (int k = 0; k < num; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionDefineLocal2: 
					case ActionConstants.sactionDelete2: 
					case ActionConstants.sactionAdd2: 
					case ActionConstants.sactionLess2: 
						// pop
						pop(evalStack);
						break;
					
					case ActionConstants.sactionInitArray: 
						// pop, if the first pop is non-zero, keep popping
						num = Convert.ToInt32(((ValueType) pop(evalStack)));
						for (int k = 0; k < num; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionInitObject: 
						num = Convert.ToInt32(((ValueType) pop(evalStack))) * 2;
						for (int k = 0; k < num; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionTargetPath: 
					case ActionConstants.sactionEnumerate: 
					case ActionConstants.sactionToNumber: 
					case ActionConstants.sactionToString: 
					case ActionConstants.sactionTypeOf: 
						// no action
						break;
					
					case ActionConstants.sactionStoreRegister: 
						StoreRegister r = (StoreRegister) a;
						registers[r.register] = evalStack.Peek();
						break;
					
					case ActionConstants.sactionEquals2: 
						// pop, pop, push
						// if (evalStack.size() >= 2)
						{
							pop(evalStack);
						}
						break;
					
					case ActionConstants.sactionPushDuplicate: 
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionStackSwap: 
						// pop, pop, push, push
						break;
					
					case ActionConstants.sactionGetMember: 
						// pop, pop, concat, push
						Object o1 = pop(evalStack);
						Object o2 = pop(evalStack);
						if (pool != null)
						{
							try
							{
								evalStack.Push(pool[Int32.Parse(o2.ToString())] + "." + pool[Int32.Parse(o1.ToString())]);
							}
							catch (Exception)
							{
								if (o1 == dummy || o2 == dummy)
								{
									evalStack.Push(dummy);
								}
								else
								{
                                    evalStack.Push(o2 + "." + o1);
								}
							}
						}
						else
						{
							evalStack.Push(o2 + "." + o1);
						}
						break;
					
					case ActionConstants.sactionSetMember: 
						// pop, pop, pop
						pop(evalStack);
						pop(evalStack);
						pop(evalStack);
						break;
					
					case ActionConstants.sactionIncrement: 
					case ActionConstants.sactionDecrement: 
						break;
					
					case ActionConstants.sactionCallMethod: 
						pop(evalStack);
						pop(evalStack);
						Object obj2 = pop(evalStack);
						if (obj2 is String)
						{
							try
							{
								n = Int32.Parse((String) obj2);
							}
							catch (FormatException)
							{
								n = 1;
							}
						}
						else
						{
							n = Convert.ToInt32(((ValueType) obj2));
						}
						for (int k = 0; k < n; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionNewMethod: 
						/*Object meth =*/ pop(evalStack);
						/*Object cls =*/ pop(evalStack);
						num = Convert.ToInt32(((ValueType) pop(evalStack)));
						for (int k = 0; k < num; k++)
						{
							pop(evalStack);
						}
						evalStack.Push(dummy);
						break;
					
					case ActionConstants.sactionWith: 
						// pop
						pop(evalStack);
						break;
					
					case ActionConstants.sactionConstantPool: 
						pool = ((ConstantPool) a).pool;
						// no action
						break;
					
					case ActionConstants.sactionStrictMode: 
						break;
					
					
					case ActionConstants.sactionBitAnd: 
					case ActionConstants.sactionBitOr: 
					case ActionConstants.sactionBitLShift: 
						// pop, push
						break;
					
					case ActionConstants.sactionBitXor: 
					case ActionConstants.sactionBitRShift: 
					case ActionConstants.sactionBitURShift: 
						pop(evalStack);
						break;
						
						// Flash 6 actions
					
					case ActionConstants.sactionInstanceOf: 
						pop(evalStack);
						break;
					
					case ActionConstants.sactionEnumerate2: 
						// pop, push, more pushes?
						break;
					
					case ActionConstants.sactionStrictEquals: 
					case ActionConstants.sactionGreater: 
					case ActionConstants.sactionStringGreater: 
						pop(evalStack);
						break;
						
						// FEATURE_EXCEPTIONS
					
					case ActionConstants.sactionTry: 
						// do nothing
						break;
					
					case ActionConstants.sactionThrow: 
						pop(evalStack);
						break;
						
						// FEATURE_AS2_INTERFACES
					
					case ActionConstants.sactionCastOp: 
						break;
					
					case ActionConstants.sactionImplementsOp: 
						break;
						
						// Reserved for Quicktime
					
					case ActionConstants.sactionQuickTime: 
						break;
					
					default: 
						break;
					
				}
			}
		}
		/// <summary> Store away the ActionLists for later retrieval</summary>
		internal virtual DummyAction recordActions(ActionList list)
		{
			DummyAction da = null;
			if (list != null && list.size() > 0)
			{
				// use the first offset as our reference point
				int offset = list.getOffset(0);
				
				// now create a pseudo action for this action list in our master
				da = new DummyAction(list);
				m_master.setActionOffset(offset, da);
			}
			return da;
		}
		private void  collectActions(ActionList c)
		{
			// assumption: ActionContext c is always not null! try-catch-finally may be busted.
			if (c == null)
			{
				return ;
			}
			
			// interprets the actions. try to assign names to anonymous functions...
			evalActions(c);
			
			DebugModule d = findDebugModule(c);
			
			String emptyMethodName = null;
			
			// loop again, this time, we register all the actions...
			for (int i = 0; i < c.size(); i++)
			{
				int ioffset = c.getOffset(i);
				Action a = c.getAction(i);
				
				if (emptyMethodName != null && emptyMethodName.Length != 0)
				{
					functionNames.put(ioffset, emptyMethodName);
					emptyMethodName = null;
				}
				
				if (a.code == ActionList.sactionLineRecord)
				{
					LineRecord line = (LineRecord) a;
					if (line.module != null)
					{
						d = line.module;
						if (d.name.EndsWith(".mxml"))
						{
							mxml[d.name] = d;
						}
					}
					
					continue;
				}
				
				if (a.code >= 256)
				{
					// something synthetic we don't care about
					continue;
				}
				
				actions.put(ioffset, (Object) codes[a.code]);
				modules.put(ioffset, d);
				
				switch (a.code)
				{
					
					case ActionConstants.sactionDefineFunction: 
					case ActionConstants.sactionDefineFunction2: 
						DefineFunction f = (DefineFunction) a;
						Int32 size = (Int32) f.codeSize;
						
						if (f.actionList.size() == 0)
						{
							emptyMethodName = f.name;
						}
						else
						{
							Int32 lineno = -1;
							
							// map all the offsets in this function to the function name
							for (int j = 0; j < f.actionList.size(); j++)
							{
								int o = f.actionList.getOffset(j);
								Action child = f.actionList.getAction(j);
								if (child.code == ActionList.sactionLineRecord)
								{
									if (lineno == -1)
										lineno = (Int32) ((LineRecord) child).lineno;
									
									preciseLines.put(o, (Object) ((LineRecord) child).lineno);
								}
								functionNames.put(o, f.name);
								functionSizes.put(o, (Object) size);
							}
							
							
							// map all the offsets in this function to the first line number of this function.
							for (int j = 0; j < f.actionList.size(); j++)
							{
								int o = f.actionList.getOffset(j);
								functionLines.put(o, (Object) lineno);
							}
						}
						
						collectActions(f.actionList);
						break;
					}
			}
		}
		// perform a binary search to locate the offset within the sorted
		// list of offsets within the action list.
		// if no match then (-i - 1) provides the index of where an insertion
		// would occur for this offset in the list.
		public static int find(ActionList list, int offset)
		{
			int lo = 0;
			int hi = list.size() - 1;
			
			while (lo <= hi)
			{
				int i = (lo + hi) / 2;
				int m = list.getOffset(i);
				if (offset > m)
					lo = i + 1;
				else if (offset < m)
					hi = i - 1;
				else
					return i; // offset found
			}
			return - (lo + 1); // offset not found, low is the insertion point
		}