private ABCFile.MethodBody readMethodBody() { var r = new ABCFile.MethodBody(); r.method = readU30(); r.maxStack = readU30(); r.localCount = readU30(); r.initScopeDepth = readU30(); r.maxScopeDepth = readU30(); r.instructions = null; uint len = readU30(); var instructionAtOffset = new uint[len]; r.rawBytes = new byte[len]; Buffer.BlockCopy(buf, (int)pos, r.rawBytes, 0, (int)len); Func <ABCFile.Label, ABCFile.Label> translateLabel = (ABCFile.Label label) => { uint absoluteOffset = label.absoluteOffset; uint instructionOffset = absoluteOffset; while (true) { if (instructionOffset >= len) { label.index = (uint)r.instructions.Length; instructionOffset = len; break; } if (instructionOffset <= 0) { label.index = 0; instructionOffset = 0; break; } if (instructionAtOffset[instructionOffset] != uint.MaxValue) { label.index = instructionAtOffset[instructionOffset]; break; } instructionOffset--; } label.offset = (int)(absoluteOffset - instructionOffset); return(label); }; uint start = pos; uint end = pos + len; Func <uint> offset = () => { return(pos - start); }; try { for (int i = 0; i < instructionAtOffset.Length; i++) { instructionAtOffset[i] = uint.MaxValue; } var instructionOffsets = new List <uint>(); var instrs = new List <ABCFile.Instruction>(); while (pos < end) { uint instructionOffset = offset(); pos = start + instructionOffset; instructionAtOffset[instructionOffset] = (uint)instrs.Count; ABCFile.Instruction instruction; instruction.opcode = (Opcode)readU8(); var opcodeInfo = OpcodeInfo.opcodeDict[instruction.opcode]; instruction.arguments = new ABCFile.Instruction.Argument[opcodeInfo.argumentTypes.Length]; for (int i = 0; i < instruction.arguments.Length; i++) { var type = opcodeInfo.argumentTypes[i]; switch (type) { case OpcodeArgumentType.Unknown: throw new Exception("Don't know how to decode OP_" + opcodeInfo.name); case OpcodeArgumentType.UByteLiteral: instruction.arguments[i].ubytev = readU8(); break; case OpcodeArgumentType.IntLiteral: instruction.arguments[i].intv = readS32(); break; case OpcodeArgumentType.UIntLiteral: instruction.arguments[i].uintv = readU32(); break; case OpcodeArgumentType.Int: case OpcodeArgumentType.UInt: case OpcodeArgumentType.Double: case OpcodeArgumentType.String: case OpcodeArgumentType.Namespace: case OpcodeArgumentType.Multiname: case OpcodeArgumentType.Class: case OpcodeArgumentType.Method: instruction.arguments[i].index = readU30(); break; case OpcodeArgumentType.JumpTarget: int delta = readS24(); instruction.arguments[i].jumpTarget.absoluteOffset = (uint)(offset() + delta); break; case OpcodeArgumentType.SwitchDefaultTarget: instruction.arguments[i].jumpTarget.absoluteOffset = (uint)(instructionOffset + readS24()); break; case OpcodeArgumentType.SwitchTargets: instruction.arguments[i].switchTargets = new ABCFile.Label[readU30() + 1]; for (int j = 0; j < instruction.arguments[i].switchTargets.Length; j++) { instruction.arguments[i].switchTargets[j].absoluteOffset = (uint)(instructionOffset + readS24()); } break; } } instrs.Add(instruction); instructionOffsets.Add(instructionOffset); } r.instructions = instrs.ToArray(); if (pos > end) { throw new Exception("Out-of-bounds code read error"); } // convert jump target offsets to instruction indices for (int i = 0; i < r.instructions.Length; i++) { var opcodeInfo = OpcodeInfo.opcodeDict[r.instructions[i].opcode]; for (int j = 0; j < opcodeInfo.argumentTypes.Length; j++) { switch (opcodeInfo.argumentTypes[j]) { case OpcodeArgumentType.JumpTarget: case OpcodeArgumentType.SwitchDefaultTarget: r.instructions[i].arguments[j].jumpTarget = translateLabel(r.instructions[i].arguments[j].jumpTarget); break; case OpcodeArgumentType.SwitchTargets: for (int k = 0; k < r.instructions[i].arguments[j].switchTargets.Length; k++) { r.instructions[i].arguments[j].switchTargets[k] = translateLabel(r.instructions[i].arguments[j].switchTargets[k]); } break; default: break; } } } } catch (Exception e) { r.instructions = null; r.error = e.Message; instructionAtOffset.Initialize(); } pos = end; r.exceptions = new ABCFile.ExceptionInfo[readU30()]; for (int i = 0; i < r.exceptions.Length; i++) { r.exceptions[i] = readExceptionInfo(); r.exceptions[i].from = translateLabel(r.exceptions[i].from); r.exceptions[i].to = translateLabel(r.exceptions[i].to); r.exceptions[i].target = translateLabel(r.exceptions[i].target); } r.traits = new ABCFile.TraitsInfo[readU30()]; for (int i = 0; i < r.traits.Length; i++) { r.traits[i] = readTrait(); } return(r); }
private void writeMethodBody(ABCFile.MethodBody v) { writeU30(v.method); writeU30(v.maxStack); writeU30(v.localCount); writeU30(v.initScopeDepth); writeU30(v.maxScopeDepth); var instructionOffsets = new uint[v.instructions.Length + 1]; Func <ABCFile.Label, uint> resolveLabel = (ABCFile.Label label) => { return((uint)(instructionOffsets[label.index] + label.offset)); }; { // we don't know the length before writing all the instructions - swap buffer with a temporary one byte[] globalBuf = buf; uint globalPos = pos; var methodBuf = new byte[1024 * 16]; buf = methodBuf; pos = 0; var fixups = new List <Fixup>(); for (int i = 0; i < v.instructions.Length; i++) { var instruction = v.instructions[i]; uint instructionOffset = pos; instructionOffsets[i] = instructionOffset; var opcodeInfo = OpcodeInfo.opcodeDict[instruction.opcode]; writeU8((byte)instruction.opcode); for (int j = 0; j < opcodeInfo.argumentTypes.Length; j++) { switch (opcodeInfo.argumentTypes[j]) { case OpcodeArgumentType.Unknown: throw new Exception("Don't know how to encode OP_" + opcodeInfo.name); case OpcodeArgumentType.UByteLiteral: writeU8(instruction.arguments[j].ubytev); break; case OpcodeArgumentType.IntLiteral: writeS32(instruction.arguments[j].intv); break; case OpcodeArgumentType.UIntLiteral: writeU32(instruction.arguments[j].uintv); break; case OpcodeArgumentType.Int: case OpcodeArgumentType.UInt: case OpcodeArgumentType.Double: case OpcodeArgumentType.String: case OpcodeArgumentType.Namespace: case OpcodeArgumentType.Multiname: case OpcodeArgumentType.Class: case OpcodeArgumentType.Method: writeU30(instruction.arguments[j].index); break; case OpcodeArgumentType.JumpTarget: fixups.Add(new Fixup(instruction.arguments[j].jumpTarget, pos, pos + 3)); writeS24(0); break; case OpcodeArgumentType.SwitchDefaultTarget: fixups.Add(new Fixup(instruction.arguments[j].jumpTarget, pos, instructionOffset)); writeS24(0); break; case OpcodeArgumentType.SwitchTargets: writeU30((uint)instruction.arguments[j].switchTargets.Length - 1); foreach (var off in instruction.arguments[j].switchTargets) { fixups.Add(new Fixup(off, pos, instructionOffset)); writeS24(0); } break; } } } Array.Resize(ref buf, (int)pos); instructionOffsets[v.instructions.Length] = pos; foreach (var fixup in fixups) { pos = fixup.pos; writeS24((int)(resolveLabel(fixup.target) - fixup.baseOffset)); } byte[] code = buf; // restore global buffer buf = globalBuf; pos = globalPos; writeBytes(code); } writeU30((uint)v.exceptions.Length); foreach (var val in v.exceptions) { var value = val; value.from.absoluteOffset = resolveLabel(value.from); value.to.absoluteOffset = resolveLabel(value.to); value.target.absoluteOffset = resolveLabel(value.target); writeExceptionInfo(value); } writeU30((uint)v.traits.Length); foreach (var value in v.traits) { writeTrait(value); } }