/// <summary> /// Convenience method for creating an opcode from a position in a byte array. /// Returns an opcode object with references resolved and advances the position /// to the next place in the byte array. /// </summary> /// <param name="reader">Where to read the next opcode from</param> /// <param name="abc">The code within which we're reading</param> /// <returns>A new Opcode object, or null if there was no more data to be read</returns> public static Opcode BuildOpcode(ABCDataTypeReader reader, AbcCode abc) { Opcode op = new Opcode(abc); int code; code = reader.ReadUI8(); if (code == -1) { return null; } op.Instruction = (uint)code; if (OpcodeTable[code] == null) { throw new SWFModellerException( SWFModellerError.Internal, "Bad opcode 0x" + code.ToString("X") + " at @" + (reader.Offset - 1)); } OpcodeDef info = (OpcodeDef)OpcodeTable[code]; List<object> args = new List<object>(); if (info.Mnemonic == Mnemonics.LookupSwitch) { /* Special case: Has a variable arg */ args.Add(reader.ReadSI24()); /* default offset */ uint caseCount = reader.ReadU30(); args.Add(caseCount); for (int i = 0; i < caseCount + 1; i++) { args.Add(reader.ReadSI24()); } } else { if (info.Args != null) { foreach (ArgType type in info.Args) { switch (type) { case ArgType.MultinameU30: args.Add(abc.GetMultiname((int)reader.ReadU30())); break; case ArgType.OffsetS24: args.Add(reader.ReadSI24()); break; case ArgType.StringU30: args.Add(abc.StringConsts[reader.ReadU30()]); break; case ArgType.RegisterU30: case ArgType.ObjectRegisterU30: case ArgType.PropertyRegisterU30: args.Add(reader.ReadU30()); break; case ArgType.ByteU8: args.Add((byte)reader.ReadUI8()); break; case ArgType.ShortU30: case ArgType.IntU30: case ArgType.UintU30: case ArgType.DoubleU30: args.Add(reader.ReadU30()); break; case ArgType.ShortS30: args.Add((int)reader.ReadU30()); break; case ArgType.ByteS8: args.Add(reader.ReadSI8()); break; case ArgType.NamespaceU30: args.Add(abc.GetNamespace((int)reader.ReadU30())); break; case ArgType.MethodU30: args.Add(abc.GetMethod((int)reader.ReadU30())); break; case ArgType.CountU30: args.Add(reader.ReadU30()); break; case ArgType.ClassU30: args.Add(abc.GetClass((int)reader.ReadU30())); break; case ArgType.ExceptionU30: args.Add(reader.ReadU30()); break; case ArgType.StackU8: args.Add((byte)reader.ReadUI8()); break; case ArgType.SlotU30: args.Add(reader.ReadU30()); break; case ArgType.DebugU8: args.Add((byte)reader.ReadUI8()); break; case ArgType.DebugTypeU30: args.Add(reader.ReadU30()); break; case ArgType.StringU8: args.Add(abc.StringConsts[reader.ReadUI8()]); break; case ArgType.LineNumberU30: args.Add(reader.ReadU30()); break; default: /* ISSUE 73 */ throw new SWFModellerException( SWFModellerError.UnimplementedFeature, "Oops. Not done " + type.ToString()); } } } } op.Args = args.ToArray(); return op; }
/// <summary> /// Convenience method for debug file opcodes, which can be inserted sneakily at the /// last moment when serialising the bytecode, and so need to be created in this lightweight /// way. /// </summary> public static Opcode CreateDebugFile(string fileName) { Opcode op = new Opcode(null); op.Instruction = (uint)Mnemonics.DebugFile; op.Args = new object[] { fileName }; return op; }
/// <summary> /// Convenience method for debug line opcodes, which can be inserted sneakily at the /// last moment when serialising the bytecode, and so need to be created in this lightweight /// way. /// </summary> public static Opcode CreateDebugLine(uint line) { Opcode op = new Opcode(null); op.Instruction = (uint)Mnemonics.DebugLine; op.Args = new object[] { line }; return op; }
/// <summary> /// Technically, from an OO perspective, this isn't the most logical place /// for this method. It's a factory method for opcodes used when building up /// new methods, bytecode by bytecode. It's here because this is the place /// that makes calling it require the least typing, which is good when you're /// creating lists of loads of these things. /// </summary> /// <param name="mnemonic">The opcode mnemonic</param> /// <param name="args">The argument list. If the types of the args don't /// match the type requirements of the opcode, an exception will be /// thrown.</param> /// <returns>An opcode</returns> internal Opcode Op(Opcode.Mnemonics mnemonic, params object[] args) { Opcode op = new Opcode(this) { Mnemonic = mnemonic, Args = args.Length == 0 ? null : args }; if (mnemonic != Opcode.Mnemonics.LookupSwitch) { if (!Opcode.VerifyArgTypes(mnemonic, args)) { throw new SWFModellerException( SWFModellerError.Internal, "Invalid arg values or types in opcode " + mnemonic.ToString()); } } /* ISSUE 7: There must be some way to verify lookupswitch to some degree. * Update: There is. The case count must be correct and the offsets must be references * to preceeding Label opcodes. */ return op; }