internal virtual void  init(int p1, ActionList p2, ConstantPool p3, String p4, DefineFunction p5)
		{
			at = p1;
			actions = p2;
			pool = p3;
			className = p4;
			function = p5;
		}
		/// <summary> This routine is called from the DSwfInfo object
		/// and is used to obtain LineRecord information 
		/// from the ActionLists
		/// 
		/// The ActionLocation record is used as a holding
		/// container for state as we traverse the lists
		/// </summary>
		internal virtual void  probeForLineRecords(ActionList list, ActionLocation where, DSwfInfo info)
		{
			int size = list.size();
			for (int i = 0; i < size; i++)
			{
				try
				{
					// set our context
					where.at = i;
					where.actions = list;
					
					// pull the action
					Action a = list.getAction(i);
					
					// then see if we need to traverse
					if ((a.code == ActionConstants.sactionDefineFunction) || (a.code == ActionConstants.sactionDefineFunction2))
					{
						where.function = (DefineFunction) a;
						probeForLineRecords(((DefineFunction) a).actionList, where, info);
						where.function = null;
					}
					else if (a.code == ActionList.sactionLineRecord)
					{
						// hit a line record, so let's do our callback
						info.processLineRecord(where, (LineRecord) a);
					}
					else if (a is DummyAction)
					{
						// our dummy container, then we drop in
						where.className = ((DummyAction) a).ClassName;
						probeForLineRecords(((DummyAction) a).ActionList, where, info);
						where.className = null;
					}
				}
				catch (Exception e)
				{
					// this is fairly bad and probably means that we have corrupt line
					// records in the swd, the exception being an ArrayIndexOutOfBoundsException. 
					// I've seen this in cases where a bad swc is built by authoring wherein a
					// script id collision occurs and thus the offset table will contain references
					// to line numbers that are non existent in one of the scripts.
					// If its another type of exception...well, hopefully the trace message will
					// help you track it down :)
					if (Trace.error)
					{
						Trace.trace("Error processing ActionList at " + where.at + " at offset " + where.actions.getOffset(where.at) + " in swf " + info.Url); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
						Console.Error.Write(e.StackTrace);
                        Console.Error.Flush();
                    }
				}
			}
		}
		public static void  disassemble(ActionList list, ConstantPool pool, int startIndex, int endIndex, StreamWriter out_Renamed)
		{
			Disassembler d = new Disassembler(out_Renamed, pool, "    ");
			d.Format = "    0x%08O  %a";
			d.ShowLineRecord = false;
			
			// probe backward for a register record if any to set up register to variable name mapping
			int at = list.lastIndexOf(ActionList.sactionRegisterRecord, startIndex);
			if (at > - 1)
				d.registerRecord_Renamed_Field = (RegisterRecord) list.getAction(at);
			
			// now dump the contents of our request
			list.visit(d, startIndex, endIndex);
			out_Renamed.Flush();
		}
		/// <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;
					
				}
			}
		}
		private void  evalActions(ActionList c)
		{
			try
			{
				walkActions(c, header_field.version, pool, className, skipOffsets);
			}
			catch (Exception)
			{
			}
		}
		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;
					}
			}
		}
		private DebugModule findDebugModule(ActionList c)
		{
			MFUCache modules = new MFUCache();
			
			for (int i = 0; i < c.size(); i++)
			{
				Action a = c.getAction(i);
				
				DebugModule temp = null;
				
				switch (a.code)
				{
					
					case ActionConstants.sactionDefineFunction: 
					case ActionConstants.sactionDefineFunction2: 
						temp = findDebugModule(((DefineFunction) a).actionList);
						break;
					
					case ActionList.sactionLineRecord: 
						if (((LineRecord) a).module != null)
						{
							temp = ((LineRecord) a).module;
						}
						break;
					}
				
				if (temp != null)
				{
					modules.add(temp);
				}
			}
			
			// ActionList may have actions pointing to more than one debug module because of #include, etc.
			// The majority wins.
			
			return modules.topModule;
		}
		private static bool hasLineRecord(ActionList c)
		{
			if (c == null || c.size() == 0)
			{
				return true;
			}
			
			bool result = false;
			
			for (int i = 0; i < c.size() && !result; i++)
			{
				Action action = c.getAction(i);
				
				switch (action.code)
				{
					
					case ActionConstants.sactionDefineFunction: 
					case ActionConstants.sactionDefineFunction2: 
						result = result || hasLineRecord(((DefineFunction) action).actionList);
						break;
					
					case ActionList.sactionLineRecord: 
						result = true;
						break;
					}
			}
			
			return result;
		}
		// TODO: Use an evaluation stack to figure out the Object.registerClass() call.
		public static bool isRegisterClass(ActionList actionList)
		{
			if (!hasLineRecord(actionList))
			{
				int[] opcodes;
				
				if (actionList.size() == 9)
				{
					opcodes = regClassCall9;
				}
				else if (actionList.size() == 10)
				{
					opcodes = regClassCall10;
				}
				else
				{
					return false;
				}
				
				for (int i = 0; i < opcodes.Length; i++)
				{
					if (actionList.getAction(i).code != opcodes[i])
					{
						return false;
					}
					else
					{
						// TODO: need to check the PUSH values...
					}
				}
				
				return true;
			}
			
			return false;
		}
Пример #10
0
		/// <summary> Ask a TagDecoder to do its magic, calling us 
		/// upon each encounter of a new tag.
		/// </summary>
		internal virtual void  process(TagDecoder d)
		{
			m_master = new ActionList(true);
			d.KeepOffsets = true;
			d.parse(this);
		}
Пример #11
0
		/// <summary> Go off and fill our DefineFunction records with function names.</summary>
		/// <seealso cref="Flash.Swf.MovieMetaData.walkActions(ActionList, int, String[], String, System.Collections.IList)"> for a discussion on how this is done.
		/// </seealso>
		internal virtual void buildFunctionNames(ActionList list, int version)
		{
			int size = list.size();
			for (int i = 0; i < size; i++)
			{
				DummyAction a = (DummyAction) list.getAction(i);
				MovieMetaData.walkActions(a.ActionList, version, null, a.ClassName, null);
			}
		}
Пример #12
0
		/// <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;
		}
Пример #13
0
			public DummyAction(ActionList list) : base(ActionConstants.sactionNone)
			{
				m_actionList = list;
			}
Пример #14
0
		// 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
		}
Пример #15
0
		// find the index of the largest offset in the list that does not
		// exceed the offset value provided. 
		public static int findLessOrEqualTo(ActionList list, int offset)
		{
			int i = find(list, offset);
			if (i < 0)
			{
				// means we didn't locate it, so get the next closest one
				// which is 1 below the insertion point
				i = (- i - 1) - 1;
			}
			return i;
		}
Пример #16
0
		public static ActionLocation locationLessOrEqualTo(ActionLocation location, ActionList list, int offset)
		{
			int at = findLessOrEqualTo(list, offset);
			if (at > - 1)
			{
				// we hit so mark it and extract a constant pool if any
				location.at = at;
				location.actions = list;
				
				Action a = list.getAction(0);
				if (a.code == ActionConstants.sactionConstantPool)
					location.pool = (ConstantPool) a;
				
				// then see if we need to traverse
				a = list.getAction(at);
				if ((a.code == ActionConstants.sactionDefineFunction) || (a.code == ActionConstants.sactionDefineFunction2))
				{
					location.function = (DefineFunction) a;
					locationLessOrEqualTo(location, ((DefineFunction) a).actionList, offset);
				}
				else if (a is DummyAction)
				{
					// our dummy container, then we drop in
					locationLessOrEqualTo(location, ((DummyAction) a).ActionList, offset);
				}
			}
			return location;
		}