Initial work on CFI-like functionality for better debugging
This commit is contained in:
parent
dbeae16dc4
commit
48d1c3fc8c
|
@ -239,7 +239,7 @@ proc dispatch*(self: PeonVM) =
|
||||||
# pushing it on the stack
|
# pushing it on the stack
|
||||||
let retVal = self.pop()
|
let retVal = self.pop()
|
||||||
let frame = self.frames.pop()
|
let frame = self.frames.pop()
|
||||||
for i in countdown(self.stack.high(), frame):
|
for i in countdown(0, frame):
|
||||||
discard self.pop()
|
discard self.pop()
|
||||||
self.ip = int(self.pop().uInt)
|
self.ip = int(self.pop().uInt)
|
||||||
self.push(retVal)
|
self.push(retVal)
|
||||||
|
|
|
@ -290,14 +290,22 @@ proc patchJump(self: Compiler, offset: int) =
|
||||||
case OpCode(self.chunk.code[offset]):
|
case OpCode(self.chunk.code[offset]):
|
||||||
of LongJumpForwards:
|
of LongJumpForwards:
|
||||||
self.chunk.code[offset] = JumpForwards.uint8()
|
self.chunk.code[offset] = JumpForwards.uint8()
|
||||||
|
jump -= 4
|
||||||
of LongJumpBackwards:
|
of LongJumpBackwards:
|
||||||
self.chunk.code[offset] = JumpBackwards.uint8()
|
self.chunk.code[offset] = JumpBackwards.uint8()
|
||||||
|
jump -= 4
|
||||||
of LongJumpIfFalse:
|
of LongJumpIfFalse:
|
||||||
self.chunk.code[offset] = JumpIfFalse.uint8()
|
self.chunk.code[offset] = JumpIfFalse.uint8()
|
||||||
of LongJumpIfFalsePop:
|
of LongJumpIfFalsePop:
|
||||||
self.chunk.code[offset] = JumpIfFalsePop.uint8()
|
self.chunk.code[offset] = JumpIfFalsePop.uint8()
|
||||||
of LongJumpIfFalseOrPop:
|
of LongJumpIfFalseOrPop:
|
||||||
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
self.chunk.code[offset] = JumpIfFalseOrPop.uint8()
|
||||||
|
of JumpForwards, JumpBackwards:
|
||||||
|
# We do this because a relative jump
|
||||||
|
# does not normally take into account
|
||||||
|
# its argument, which is hardcoded in
|
||||||
|
# the bytecode itself
|
||||||
|
jump -= 3
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
|
self.chunk.code.delete(offset + 1) # Discards the first 8 bits of the jump offset (which are empty)
|
||||||
|
@ -308,14 +316,22 @@ proc patchJump(self: Compiler, offset: int) =
|
||||||
case OpCode(self.chunk.code[offset]):
|
case OpCode(self.chunk.code[offset]):
|
||||||
of JumpForwards:
|
of JumpForwards:
|
||||||
self.chunk.code[offset] = LongJumpForwards.uint8()
|
self.chunk.code[offset] = LongJumpForwards.uint8()
|
||||||
|
jump -= 3
|
||||||
of JumpBackwards:
|
of JumpBackwards:
|
||||||
self.chunk.code[offset] = LongJumpBackwards.uint8()
|
self.chunk.code[offset] = LongJumpBackwards.uint8()
|
||||||
|
jump -= 3
|
||||||
of JumpIfFalse:
|
of JumpIfFalse:
|
||||||
self.chunk.code[offset] = LongJumpIfFalse.uint8()
|
self.chunk.code[offset] = LongJumpIfFalse.uint8()
|
||||||
of JumpIfFalsePop:
|
of JumpIfFalsePop:
|
||||||
self.chunk.code[offset] = LongJumpIfFalsePop.uint8()
|
self.chunk.code[offset] = LongJumpIfFalsePop.uint8()
|
||||||
of JumpIfFalseOrPop:
|
of JumpIfFalseOrPop:
|
||||||
self.chunk.code[offset] = LongJumpIfFalseOrPop.uint8()
|
self.chunk.code[offset] = LongJumpIfFalseOrPop.uint8()
|
||||||
|
of LongJumpForwards, LongJumpBackwards:
|
||||||
|
# We do this because a relative jump
|
||||||
|
# does not normally take into account
|
||||||
|
# its argument, which is hardcoded in
|
||||||
|
# the bytecode itself
|
||||||
|
jump -= 4
|
||||||
else:
|
else:
|
||||||
discard
|
discard
|
||||||
let offsetArray = jump.toTriple()
|
let offsetArray = jump.toTriple()
|
||||||
|
@ -751,7 +767,7 @@ proc matchImpl(self: Compiler, name: string, kind: Type): Name =
|
||||||
if name.valueType.kind != Function:
|
if name.valueType.kind != Function:
|
||||||
msg &= ", not a callable"
|
msg &= ", not a callable"
|
||||||
elif kind.args.len() != name.valueType.args.len():
|
elif kind.args.len() != name.valueType.args.len():
|
||||||
msg &= &", wrong number of arguments ({name.valueType.args.len()} expected, got {kind.args.len()})\n"
|
msg &= &", wrong number of arguments ({name.valueType.args.len()} expected, got {kind.args.len()})"
|
||||||
else:
|
else:
|
||||||
for i, arg in kind.args:
|
for i, arg in kind.args:
|
||||||
if not self.compareTypes(arg, name.valueType.args[i]):
|
if not self.compareTypes(arg, name.valueType.args[i]):
|
||||||
|
@ -930,7 +946,7 @@ proc identifier(self: Compiler, node: IdentExpr) =
|
||||||
if not t.closedOver:
|
if not t.closedOver:
|
||||||
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
# Static name resolution, loads value at index in the stack. Very fast. Much wow.
|
||||||
self.emitByte(LoadVar)
|
self.emitByte(LoadVar)
|
||||||
self.emitBytes((index - self.frames[^1]).toTriple())
|
self.emitBytes((index - self.frames[self.scopeDepth]).toTriple())
|
||||||
else:
|
else:
|
||||||
if self.closedOver.len() == 0:
|
if self.closedOver.len() == 0:
|
||||||
self.error("error: closure variable array is empty but LoadHeap would be emitted (this is an internal error and most likely a bug)")
|
self.error("error: closure variable array is empty but LoadHeap would be emitted (this is an internal error and most likely a bug)")
|
||||||
|
@ -995,6 +1011,7 @@ proc beginScope(self: Compiler) =
|
||||||
## Begins a new local scope by incrementing the current
|
## Begins a new local scope by incrementing the current
|
||||||
## scope's depth
|
## scope's depth
|
||||||
inc(self.scopeDepth)
|
inc(self.scopeDepth)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc endScope(self: Compiler) =
|
proc endScope(self: Compiler) =
|
||||||
|
@ -1181,7 +1198,6 @@ proc returnStmt(self: Compiler, node: ReturnStmt) =
|
||||||
self.emitByte(OpCode.ReturnValue)
|
self.emitByte(OpCode.ReturnValue)
|
||||||
else:
|
else:
|
||||||
self.emitByte(OpCode.Return)
|
self.emitByte(OpCode.Return)
|
||||||
discard self.frames.pop()
|
|
||||||
|
|
||||||
|
|
||||||
proc yieldStmt(self: Compiler, node: YieldStmt) =
|
proc yieldStmt(self: Compiler, node: YieldStmt) =
|
||||||
|
@ -1302,10 +1318,11 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
## Compiles function declarations
|
## Compiles function declarations
|
||||||
# A function's code is just compiled linearly
|
# A function's code is just compiled linearly
|
||||||
# and then jumped over
|
# and then jumped over
|
||||||
let jmp = self.emitJump(Jump)
|
let jmp = self.emitJump(JumpForwards)
|
||||||
var function = self.currentFunction
|
var function = self.currentFunction
|
||||||
self.declareName(node)
|
self.declareName(node)
|
||||||
self.frames.add(self.names.high())
|
self.frames.add(self.names.high())
|
||||||
|
|
||||||
# TODO: Forward declarations
|
# TODO: Forward declarations
|
||||||
if node.body != nil:
|
if node.body != nil:
|
||||||
if BlockStmt(node.body).code.len() == 0:
|
if BlockStmt(node.body).code.len() == 0:
|
||||||
|
@ -1334,6 +1351,8 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
# the try/finally block with the deferred
|
# the try/finally block with the deferred
|
||||||
# code
|
# code
|
||||||
var deferStart = self.deferred.len()
|
var deferStart = self.deferred.len()
|
||||||
|
# We let our debugger know a function is starting
|
||||||
|
self.chunk.cfi.add(self.chunk.code.high().toTriple())
|
||||||
|
|
||||||
self.blockStmt(BlockStmt(node.body))
|
self.blockStmt(BlockStmt(node.body))
|
||||||
# Yup, we're done. That was easy, huh?
|
# Yup, we're done. That was easy, huh?
|
||||||
|
@ -1346,6 +1365,19 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
# are resolved properly). There's a need for a bit
|
# are resolved properly). There's a need for a bit
|
||||||
# of boilerplate code to make closures work, but
|
# of boilerplate code to make closures work, but
|
||||||
# that's about it
|
# that's about it
|
||||||
|
|
||||||
|
# Function is ending!
|
||||||
|
self.chunk.cfi.add(self.chunk.code.high().toTriple())
|
||||||
|
self.chunk.cfi.add(self.frames[^1].toTriple())
|
||||||
|
self.chunk.cfi.add(uint8(node.arguments.len()))
|
||||||
|
if not system.`==`(node.name, nil):
|
||||||
|
self.chunk.cfi.add(node.name.token.lexeme.len().toDouble())
|
||||||
|
var s = node.name.token.lexeme
|
||||||
|
if node.name.token.lexeme.len() >= uint16.high().int:
|
||||||
|
s = node.name.token.lexeme[0..uint16.high()]
|
||||||
|
self.chunk.cfi.add(s.toBytes())
|
||||||
|
else:
|
||||||
|
self.chunk.cfi.add(0.toDouble())
|
||||||
case self.currentFunction.kind:
|
case self.currentFunction.kind:
|
||||||
of NodeKind.funDecl:
|
of NodeKind.funDecl:
|
||||||
if not self.currentFunction.hasExplicitReturn:
|
if not self.currentFunction.hasExplicitReturn:
|
||||||
|
@ -1368,6 +1400,7 @@ proc funDecl(self: Compiler, node: FunDecl) =
|
||||||
self.patchJump(jmp)
|
self.patchJump(jmp)
|
||||||
# This makes us compile nested functions correctly
|
# This makes us compile nested functions correctly
|
||||||
self.currentFunction = function
|
self.currentFunction = function
|
||||||
|
discard self.frames.pop()
|
||||||
|
|
||||||
|
|
||||||
proc patchReturnAddress(self: Compiler, retAddr: int) =
|
proc patchReturnAddress(self: Compiler, retAddr: int) =
|
||||||
|
|
|
@ -38,9 +38,21 @@ type
|
||||||
## are 3 and 4"
|
## are 3 and 4"
|
||||||
## This is more efficient than using the naive approach, which would encode
|
## This is more efficient than using the naive approach, which would encode
|
||||||
## the same line number multiple times and waste considerable amounts of space.
|
## the same line number multiple times and waste considerable amounts of space.
|
||||||
|
## cfi represents Call Frame Information and encodes the following information:
|
||||||
|
## - Function name
|
||||||
|
## - Stack bottom
|
||||||
|
## - Argument count
|
||||||
|
## The encoding for CFI data is the following:
|
||||||
|
## - First, the position into the bytecode where the function begins is encoded
|
||||||
|
## - Second, the position into the bytecode where the function ends is encoded
|
||||||
|
## - Then, the frame's stack bottom is encoded as a 3 byte integer
|
||||||
|
## - After the frame's stack bottom follows the argument count as a 1 byte integer
|
||||||
|
## - Lastly, the function's name (optional) is encoded in ASCII, prepended with
|
||||||
|
## its size as a 2-byte integer
|
||||||
consts*: seq[uint8]
|
consts*: seq[uint8]
|
||||||
code*: seq[uint8]
|
code*: seq[uint8]
|
||||||
lines*: seq[int]
|
lines*: seq[int]
|
||||||
|
cfi*: seq[uint8]
|
||||||
|
|
||||||
OpCode* {.pure.} = enum
|
OpCode* {.pure.} = enum
|
||||||
## Enum of Peon's bytecode opcodes
|
## Enum of Peon's bytecode opcodes
|
||||||
|
@ -165,7 +177,7 @@ const jumpInstructions* = {Jump, LongJump, JumpIfFalse, JumpIfFalsePop,
|
||||||
|
|
||||||
proc newChunk*: Chunk =
|
proc newChunk*: Chunk =
|
||||||
## Initializes a new, empty chunk
|
## Initializes a new, empty chunk
|
||||||
result = Chunk(consts: @[], code: @[], lines: @[])
|
result = Chunk(consts: @[], code: @[], lines: @[], cfi: @[])
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: Chunk): string = &"""Chunk(consts=[{self.consts.join(", ")}], code=[{self.code.join(", ")}], lines=[{self.lines.join(", ")}])"""
|
proc `$`*(self: Chunk): string = &"""Chunk(consts=[{self.consts.join(", ")}], code=[{self.code.join(", ")}], lines=[{self.lines.join(", ")}])"""
|
||||||
|
|
11
src/main.nim
11
src/main.nim
|
@ -49,6 +49,7 @@ proc repl =
|
||||||
tokenizer = newLexer()
|
tokenizer = newLexer()
|
||||||
parser = newParser()
|
parser = newParser()
|
||||||
compiler = newCompiler()
|
compiler = newCompiler()
|
||||||
|
debugger = newDebugger()
|
||||||
serializer = newSerializer()
|
serializer = newSerializer()
|
||||||
vm = newPeonVM()
|
vm = newPeonVM()
|
||||||
editor = getLineEditor()
|
editor = getLineEditor()
|
||||||
|
@ -91,8 +92,9 @@ proc repl =
|
||||||
styledEcho fgCyan, "Compilation step:"
|
styledEcho fgCyan, "Compilation step:"
|
||||||
styledEcho fgCyan, "\tRaw byte stream: ", fgGreen, "[", fgYellow, compiled.code.join(", "), fgGreen, "]"
|
styledEcho fgCyan, "\tRaw byte stream: ", fgGreen, "[", fgYellow, compiled.code.join(", "), fgGreen, "]"
|
||||||
styledEcho fgCyan, "\tConstant table: ", fgGreen, "[", fgYellow, compiled.consts.join(", "), fgGreen, "]"
|
styledEcho fgCyan, "\tConstant table: ", fgGreen, "[", fgYellow, compiled.consts.join(", "), fgGreen, "]"
|
||||||
|
styledEcho fgCyan, "\tCFI data: ", fgGreen, "[", fgYellow, compiled.cfi.join(", "), fgGreen, "]"
|
||||||
styledEcho fgCyan, "\nBytecode disassembler output below:\n"
|
styledEcho fgCyan, "\nBytecode disassembler output below:\n"
|
||||||
disassembleChunk(compiled, "stdin")
|
debugger.disassembleChunk(compiled, "stdin")
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
serializer.dumpFile(compiled, input, "stdin", "stdin.pbc")
|
serializer.dumpFile(compiled, input, "stdin", "stdin.pbc")
|
||||||
|
@ -175,6 +177,7 @@ proc runFile(f: string) =
|
||||||
tokenizer = newLexer()
|
tokenizer = newLexer()
|
||||||
parser = newParser()
|
parser = newParser()
|
||||||
compiler = newCompiler()
|
compiler = newCompiler()
|
||||||
|
debugger = newDebugger()
|
||||||
serializer = newSerializer()
|
serializer = newSerializer()
|
||||||
vm = newPeonVM()
|
vm = newPeonVM()
|
||||||
input: string
|
input: string
|
||||||
|
@ -205,8 +208,10 @@ proc runFile(f: string) =
|
||||||
styledEcho fgCyan, "Compilation step:"
|
styledEcho fgCyan, "Compilation step:"
|
||||||
styledEcho fgCyan, "\tRaw byte stream: ", fgGreen, "[", fgYellow, compiled.code.join(", "), fgGreen, "]"
|
styledEcho fgCyan, "\tRaw byte stream: ", fgGreen, "[", fgYellow, compiled.code.join(", "), fgGreen, "]"
|
||||||
styledEcho fgCyan, "\tConstant table: ", fgGreen, "[", fgYellow, compiled.consts.join(", "), fgGreen, "]"
|
styledEcho fgCyan, "\tConstant table: ", fgGreen, "[", fgYellow, compiled.consts.join(", "), fgGreen, "]"
|
||||||
|
styledEcho fgCyan, "\tCFI data: ", fgGreen, "[", fgYellow, compiled.cfi.join(", "), fgGreen, "]"
|
||||||
|
|
||||||
styledEcho fgCyan, "\nBytecode disassembler output below:\n"
|
styledEcho fgCyan, "\nBytecode disassembler output below:\n"
|
||||||
disassembleChunk(compiled, f)
|
debugger.disassembleChunk(compiled, f)
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
serializer.dumpFile(compiled, input, f, splitFile(f).name & ".pbc")
|
serializer.dumpFile(compiled, input, f, splitFile(f).name & ".pbc")
|
||||||
|
@ -215,7 +220,7 @@ proc runFile(f: string) =
|
||||||
var hashMatches = computeSHA256(input).toHex().toLowerAscii() == serialized.fileHash
|
var hashMatches = computeSHA256(input).toHex().toLowerAscii() == serialized.fileHash
|
||||||
styledEcho fgCyan, "Serialization step: "
|
styledEcho fgCyan, "Serialization step: "
|
||||||
styledEcho fgBlue, &"\t- File hash: ", fgYellow, serialized.fileHash, fgBlue, " (", if hashMatches: fgGreen else: fgRed, if hashMatches: "OK" else: "Fail", fgBlue, ")"
|
styledEcho fgBlue, &"\t- File hash: ", fgYellow, serialized.fileHash, fgBlue, " (", if hashMatches: fgGreen else: fgRed, if hashMatches: "OK" else: "Fail", fgBlue, ")"
|
||||||
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.peonVer.major}.{serialized.peonVer.minor}.{serialized.peonVer.patch}", fgBlue, " (commit ", fgYellow, serialized.commitHash[0..8], fgBlue, ") on branch ", fgYellow, serialized.peonBranch
|
styledEcho fgBlue, "\t- Peon version: ", fgYellow, &"{serialized.version.major}.{serialized.version.minor}.{serialized.version.patch}", fgBlue, " (commit ", fgYellow, serialized.commit[0..8], fgBlue, ") on branch ", fgYellow, serialized.branch
|
||||||
stdout.styledWriteLine(fgBlue, "\t- Compilation date & time: ", fgYellow, fromUnix(serialized.compileDate).format("d/M/yyyy HH:mm:ss"))
|
stdout.styledWriteLine(fgBlue, "\t- Compilation date & time: ", fgYellow, fromUnix(serialized.compileDate).format("d/M/yyyy HH:mm:ss"))
|
||||||
stdout.styledWrite(fgBlue, &"\t- Constants segment: ")
|
stdout.styledWrite(fgBlue, &"\t- Constants segment: ")
|
||||||
if serialized.chunk.consts == compiled.consts:
|
if serialized.chunk.consts == compiled.consts:
|
||||||
|
|
|
@ -6,7 +6,11 @@ operator `+`(a: int32): int32 {
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn `+`(a, b: int): int {
|
||||||
|
return a + b;
|
||||||
|
}
|
||||||
|
|
||||||
var `+`: int = 1; # hehehehe
|
var `+`: int = 1; # hehehehe
|
||||||
|
|
||||||
+1; # Works: defined for int64
|
+1; # Works: defined for int64
|
||||||
+1'u8; # Nope!
|
# +1'u8; # Nope!
|
||||||
|
|
|
@ -21,6 +21,22 @@ import strutils
|
||||||
import terminal
|
import terminal
|
||||||
|
|
||||||
|
|
||||||
|
type
|
||||||
|
CFIElement = ref object
|
||||||
|
start, stop, bottom, argc: int
|
||||||
|
name: string
|
||||||
|
Debugger* = ref object
|
||||||
|
chunk: Chunk
|
||||||
|
cfiData: seq[CFIElement]
|
||||||
|
|
||||||
|
|
||||||
|
proc newDebugger*: Debugger =
|
||||||
|
## Initializes a new, empty
|
||||||
|
## debugger object
|
||||||
|
new(result)
|
||||||
|
result.cfiData = @[]
|
||||||
|
|
||||||
|
|
||||||
proc nl = stdout.write("\n")
|
proc nl = stdout.write("\n")
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,16 +60,29 @@ proc printInstruction(instruction: OpCode, newline: bool = false) =
|
||||||
nl()
|
nl()
|
||||||
|
|
||||||
|
|
||||||
proc simpleInstruction(instruction: OpCode, offset: int): int =
|
proc checkFrame(self: Debugger, n: int) =
|
||||||
|
for i, e in self.cfiData:
|
||||||
|
if n == e.start:
|
||||||
|
styledEcho fgBlue, "==== Peon Bytecode Debugger - Begin Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ===="
|
||||||
|
styledEcho fgGreen, "\t- Start offset: ", fgYellow, $e.start
|
||||||
|
styledEcho fgGreen, "\t- End offset: ", fgYellow, $e.stop
|
||||||
|
styledEcho fgGreen, "\t- Stack bottom: ", fgYellow, $e.bottom
|
||||||
|
styledEcho fgGreen, "\t- Argument count: ", fgYellow, $e.argc
|
||||||
|
echo ""
|
||||||
|
elif n == e.stop:
|
||||||
|
styledEcho fgBlue, "==== Peon Bytecode Debugger - End Frame ", fgYellow, &"'{e.name}' ", fgBlue, "(", fgYellow, $i, fgBlue, ") ====\n"
|
||||||
|
|
||||||
|
|
||||||
|
proc simpleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
nl()
|
nl()
|
||||||
return offset + 1
|
return offset + 1
|
||||||
|
|
||||||
|
|
||||||
proc stackTripleInstruction(instruction: OpCode, chunk: Chunk,
|
proc stackTripleInstruction(self: Debugger, instruction: OpCode,
|
||||||
offset: int): int =
|
offset: int): int =
|
||||||
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
|
## Debugs instructions that operate on a single value on the stack using a 24-bit operand
|
||||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[
|
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
|
||||||
offset + 3]].fromTriple()
|
offset + 3]].fromTriple()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.styledWrite(fgGreen, &", points to index ")
|
stdout.styledWrite(fgGreen, &", points to index ")
|
||||||
|
@ -61,10 +90,10 @@ proc stackTripleInstruction(instruction: OpCode, chunk: Chunk,
|
||||||
return offset + 4
|
return offset + 4
|
||||||
|
|
||||||
|
|
||||||
proc stackDoubleInstruction(instruction: OpCode, chunk: Chunk,
|
proc stackDoubleInstruction(self: Debugger, instruction: OpCode,
|
||||||
offset: int): int =
|
offset: int): int =
|
||||||
## Debugs instructions that operate on a single value on the stack using a 16-bit operand
|
## Debugs instructions that operate on a single value on the stack using a 16-bit operand
|
||||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble()
|
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.write(&", points to index ")
|
stdout.write(&", points to index ")
|
||||||
stdout.styledWrite(fgGreen, &", points to index ")
|
stdout.styledWrite(fgGreen, &", points to index ")
|
||||||
|
@ -72,28 +101,28 @@ proc stackDoubleInstruction(instruction: OpCode, chunk: Chunk,
|
||||||
return offset + 3
|
return offset + 3
|
||||||
|
|
||||||
|
|
||||||
proc argumentDoubleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc argumentDoubleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
## Debugs instructions that operate on a hardcoded value on the stack using a 16-bit operand
|
## Debugs instructions that operate on a hardcoded value on the stack using a 16-bit operand
|
||||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble()
|
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.styledWrite(fgGreen, &", has argument ")
|
stdout.styledWrite(fgGreen, &", has argument ")
|
||||||
stdout.styledWriteLine(fgYellow, $slot)
|
stdout.styledWriteLine(fgYellow, $slot)
|
||||||
return offset + 3
|
return offset + 3
|
||||||
|
|
||||||
|
|
||||||
proc argumentTripleInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc argumentTripleInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
|
## Debugs instructions that operate on a hardcoded value on the stack using a 24-bit operand
|
||||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple()
|
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[offset + 3]].fromTriple()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.styledWrite(fgGreen, ", has argument ")
|
stdout.styledWrite(fgGreen, ", has argument ")
|
||||||
stdout.styledWriteLine(fgYellow, $slot)
|
stdout.styledWriteLine(fgYellow, $slot)
|
||||||
return offset + 4
|
return offset + 3
|
||||||
|
|
||||||
|
|
||||||
proc callInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc callInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
## Debugs function calls
|
## Debugs function calls
|
||||||
var slot = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[offset + 3]].fromTriple()
|
var slot = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[offset + 3]].fromTriple()
|
||||||
var args = [chunk.code[offset + 4], chunk.code[offset + 5], chunk.code[offset + 6]].fromTriple()
|
var args = [self.chunk.code[offset + 4], self.chunk.code[offset + 5], self.chunk.code[offset + 6]].fromTriple()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.styledWrite(fgGreen, &", jumps to address ", fgYellow, $slot, fgGreen, " with ", fgYellow, $args, fgGreen, " argument")
|
stdout.styledWrite(fgGreen, &", jumps to address ", fgYellow, $slot, fgGreen, " with ", fgYellow, $args, fgGreen, " argument")
|
||||||
if args > 1:
|
if args > 1:
|
||||||
|
@ -102,27 +131,27 @@ proc callInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
return offset + 7
|
return offset + 7
|
||||||
|
|
||||||
|
|
||||||
proc constantInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc constantInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
## Debugs instructions that operate on the constant table
|
## Debugs instructions that operate on the constant table
|
||||||
var constant = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[
|
var constant = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
|
||||||
offset + 3]].fromTriple()
|
offset + 3]].fromTriple()
|
||||||
printInstruction(instruction)
|
printInstruction(instruction)
|
||||||
stdout.styledWrite(fgGreen, &", points to constant at position ", fgYellow, $constant)
|
stdout.styledWrite(fgGreen, &", points to constant at position ", fgYellow, $constant)
|
||||||
nl()
|
nl()
|
||||||
printDebug("Operand: ")
|
printDebug("Operand: ")
|
||||||
stdout.styledWriteLine(fgYellow, &"{chunk.consts[constant]}")
|
stdout.styledWriteLine(fgYellow, &"{self.chunk.consts[constant]}")
|
||||||
return offset + 4
|
return offset + 4
|
||||||
|
|
||||||
|
|
||||||
proc jumpInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
proc jumpInstruction(self: Debugger, instruction: OpCode, offset: int): int =
|
||||||
## Debugs jumps
|
## Debugs jumps
|
||||||
var jump: int
|
var jump: int
|
||||||
case instruction:
|
case instruction:
|
||||||
of Jump, JumpIfFalse, JumpIfTrue, JumpIfFalsePop, JumpForwards, JumpBackwards:
|
of Jump, JumpIfFalse, JumpIfTrue, JumpIfFalsePop, JumpForwards, JumpBackwards:
|
||||||
jump = [chunk.code[offset + 1], chunk.code[offset + 2]].fromDouble().int()
|
jump = [self.chunk.code[offset + 1], self.chunk.code[offset + 2]].fromDouble().int()
|
||||||
of LongJump, LongJumpIfFalse, LongJumpIfTrue, LongJumpIfFalsePop,
|
of LongJump, LongJumpIfFalse, LongJumpIfTrue, LongJumpIfFalsePop,
|
||||||
LongJumpForwards, LongJumpBackwards:
|
LongJumpForwards, LongJumpBackwards:
|
||||||
jump = [chunk.code[offset + 1], chunk.code[offset + 2], chunk.code[
|
jump = [self.chunk.code[offset + 1], self.chunk.code[offset + 2], self.chunk.code[
|
||||||
offset + 3]].fromTriple().int()
|
offset + 3]].fromTriple().int()
|
||||||
else:
|
else:
|
||||||
discard # Unreachable
|
discard # Unreachable
|
||||||
|
@ -133,42 +162,67 @@ proc jumpInstruction(instruction: OpCode, chunk: Chunk, offset: int): int =
|
||||||
return offset + 3
|
return offset + 3
|
||||||
|
|
||||||
|
|
||||||
proc disassembleInstruction*(chunk: Chunk, offset: int): int =
|
proc disassembleInstruction*(self: Debugger, offset: int): int =
|
||||||
## Takes one bytecode instruction and prints it
|
## Takes one bytecode instruction and prints it
|
||||||
|
self.checkFrame(offset)
|
||||||
printDebug("Offset: ")
|
printDebug("Offset: ")
|
||||||
stdout.styledWriteLine(fgYellow, $offset)
|
stdout.styledWriteLine(fgYellow, $offset)
|
||||||
printDebug("Line: ")
|
printDebug("Line: ")
|
||||||
stdout.styledWriteLine(fgYellow, &"{chunk.getLine(offset)}")
|
stdout.styledWriteLine(fgYellow, &"{self.chunk.getLine(offset)}")
|
||||||
var opcode = OpCode(chunk.code[offset])
|
var opcode = OpCode(self.chunk.code[offset])
|
||||||
case opcode:
|
case opcode:
|
||||||
of simpleInstructions:
|
of simpleInstructions:
|
||||||
result = simpleInstruction(opcode, offset)
|
result = self.simpleInstruction(opcode, offset)
|
||||||
of constantInstructions:
|
of constantInstructions:
|
||||||
result = constantInstruction(opcode, chunk, offset)
|
result = self.constantInstruction(opcode, offset)
|
||||||
of stackDoubleInstructions:
|
of stackDoubleInstructions:
|
||||||
result = stackDoubleInstruction(opcode, chunk, offset)
|
result = self.stackDoubleInstruction(opcode, offset)
|
||||||
of stackTripleInstructions:
|
of stackTripleInstructions:
|
||||||
result = stackTripleInstruction(opcode, chunk, offset)
|
result = self.stackTripleInstruction(opcode, offset)
|
||||||
of argumentDoubleInstructions:
|
of argumentDoubleInstructions:
|
||||||
result = argumentDoubleInstruction(opcode, chunk, offset)
|
result = self.argumentDoubleInstruction(opcode, offset)
|
||||||
of argumentTripleInstructions:
|
of argumentTripleInstructions:
|
||||||
result = argumentTripleInstruction(opcode, chunk, offset)
|
result = self.argumentTripleInstruction(opcode, offset)
|
||||||
of callInstructions:
|
of callInstructions:
|
||||||
result = callInstruction(opcode, chunk, offset)
|
result = self.callInstruction(opcode, offset)
|
||||||
of jumpInstructions:
|
of jumpInstructions:
|
||||||
result = jumpInstruction(opcode, chunk, offset)
|
result = self.jumpInstruction(opcode, offset)
|
||||||
else:
|
else:
|
||||||
echo &"DEBUG - Unknown opcode {opcode} at index {offset}"
|
echo &"DEBUG - Unknown opcode {opcode} at index {offset}"
|
||||||
result = offset + 1
|
result = offset + 1
|
||||||
|
|
||||||
|
|
||||||
proc disassembleChunk*(chunk: Chunk, name: string) =
|
proc parseCFIData(self: Debugger) =
|
||||||
## Takes a chunk of bytecode, and prints it
|
var
|
||||||
echo &"==== Peon Bytecode Debugger - Chunk '{name}' ====\n"
|
start, stop, bottom, argc: int
|
||||||
|
fnName: string
|
||||||
|
idx = 0
|
||||||
|
size = 0
|
||||||
|
while idx < len(self.chunk.cfi):
|
||||||
|
start = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
|
||||||
|
idx += 3
|
||||||
|
stop = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
|
||||||
|
idx += 3
|
||||||
|
bottom = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1], self.chunk.cfi[idx + 2]].fromTriple())
|
||||||
|
idx += 3
|
||||||
|
argc = int(self.chunk.cfi[idx])
|
||||||
|
inc(idx)
|
||||||
|
size = int([self.chunk.cfi[idx], self.chunk.cfi[idx + 1]].fromDouble())
|
||||||
|
idx += 2
|
||||||
|
fnName = self.chunk.cfi[idx..<idx + size].fromBytes()
|
||||||
|
inc(idx, size)
|
||||||
|
self.cfiData.add(CFIElement(start: start, stop: stop, bottom: bottom, argc: argc, name: fnName))
|
||||||
|
|
||||||
|
|
||||||
|
proc disassembleChunk*(self: Debugger, chunk: Chunk, name: string) =
|
||||||
|
## Takes a chunk of bytecode and prints it
|
||||||
|
self.chunk = chunk
|
||||||
|
styledEcho fgBlue, &"==== Peon Bytecode Debugger - Chunk '{name}' ====\n"
|
||||||
var index = 0
|
var index = 0
|
||||||
while index < chunk.code.len:
|
self.parseCFIData()
|
||||||
index = disassembleInstruction(chunk, index)
|
while index < self.chunk.code.len:
|
||||||
|
index = self.disassembleInstruction(index)
|
||||||
echo ""
|
echo ""
|
||||||
echo &"==== Debug session ended - Chunk '{name}' ===="
|
styledEcho fgBlue, &"==== Debug session ended - Chunk '{name}' ===="
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,8 @@
|
||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
## Utilities to convert from/to our 16-bit and 24-bit representations
|
## Utilities to handle multibyte sequences
|
||||||
## of numbers
|
import nimSHA2
|
||||||
|
|
||||||
|
|
||||||
proc toDouble*(input: int | uint | uint16): array[2, uint8] =
|
proc toDouble*(input: int | uint | uint16): array[2, uint8] =
|
||||||
|
@ -58,4 +58,36 @@ proc fromQuad*(input: array[4, uint8]): uint =
|
||||||
proc fromLong*(input: array[8, uint8]): uint =
|
proc fromLong*(input: array[8, uint8]): uint =
|
||||||
## Rebuilts the output of toQuad into
|
## Rebuilts the output of toQuad into
|
||||||
## an uint
|
## an uint
|
||||||
copyMem(result.addr, unsafeAddr(input), sizeof(uint64))
|
copyMem(result.addr, unsafeAddr(input), sizeof(uint64))
|
||||||
|
|
||||||
|
|
||||||
|
proc toBytes*(s: string): seq[byte] =
|
||||||
|
## Converts a string into a sequence
|
||||||
|
## of bytes
|
||||||
|
for c in s:
|
||||||
|
result.add(byte(c))
|
||||||
|
|
||||||
|
|
||||||
|
proc toBytes*(s: int): array[8, uint8] =
|
||||||
|
## Converts
|
||||||
|
result = cast[array[8, uint8]](s)
|
||||||
|
|
||||||
|
|
||||||
|
proc toBytes*(d: SHA256Digest): seq[byte] =
|
||||||
|
## Converts a SHA256 hash digest to
|
||||||
|
## a sequence of bytes
|
||||||
|
for b in d:
|
||||||
|
result.add(b)
|
||||||
|
|
||||||
|
|
||||||
|
proc fromBytes*(input: seq[byte]): string =
|
||||||
|
## Converts a sequence of bytes to
|
||||||
|
## a string
|
||||||
|
for b in input:
|
||||||
|
result.add(char(b))
|
||||||
|
|
||||||
|
|
||||||
|
proc extend*[T](s: var seq[T], a: openarray[T]) =
|
||||||
|
## Extends s with the elements of a
|
||||||
|
for e in a:
|
||||||
|
s.add(e)
|
|
@ -13,9 +13,10 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
import ../frontend/meta/errors
|
import ../frontend/meta/errors
|
||||||
import ../frontend/meta/bytecode
|
import ../frontend/meta/bytecode
|
||||||
import ../config
|
|
||||||
import multibyte
|
|
||||||
import ../frontend/compiler
|
import ../frontend/compiler
|
||||||
|
import multibyte
|
||||||
|
import ../config
|
||||||
|
|
||||||
|
|
||||||
import strformat
|
import strformat
|
||||||
import strutils
|
import strutils
|
||||||
|
@ -36,15 +37,15 @@ type
|
||||||
## procedures to store
|
## procedures to store
|
||||||
## metadata
|
## metadata
|
||||||
fileHash*: string
|
fileHash*: string
|
||||||
peonVer*: tuple[major, minor, patch: int]
|
version*: tuple[major, minor, patch: int]
|
||||||
peonBranch*: string
|
branch*: string
|
||||||
commitHash*: string
|
commit*: string
|
||||||
compileDate*: int
|
compileDate*: int
|
||||||
chunk*: Chunk
|
chunk*: Chunk
|
||||||
|
|
||||||
|
|
||||||
proc `$`*(self: Serialized): string =
|
proc `$`*(self: Serialized): string =
|
||||||
result = &"Serialized(fileHash={self.fileHash}, version={self.peonVer.major}.{self.peonVer.minor}.{self.peonVer.patch}, branch={self.peonBranch}), commitHash={self.commitHash}, date={self.compileDate}, chunk={self.chunk[]}"
|
result = &"Serialized(fileHash={self.fileHash}, version={self.version.major}.{self.version.minor}.{self.version.patch}, branch={self.branch}), commitHash={self.commit}, date={self.compileDate}, chunk={self.chunk[]}"
|
||||||
|
|
||||||
|
|
||||||
proc error(self: Serializer, message: string) =
|
proc error(self: Serializer, message: string) =
|
||||||
|
@ -61,44 +62,17 @@ proc newSerializer*(self: Serializer = nil): Serializer =
|
||||||
result.chunk = nil
|
result.chunk = nil
|
||||||
|
|
||||||
|
|
||||||
## Basic routines and helpers to convert various objects from and to to their byte representation
|
|
||||||
|
|
||||||
proc toBytes(self: Serializer, s: string): seq[byte] =
|
|
||||||
for c in s:
|
|
||||||
result.add(byte(c))
|
|
||||||
|
|
||||||
|
|
||||||
proc toBytes(self: Serializer, s: int): array[8, uint8] =
|
|
||||||
result = cast[array[8, uint8]](s)
|
|
||||||
|
|
||||||
|
|
||||||
proc toBytes(self: Serializer, d: SHA256Digest): seq[byte] =
|
|
||||||
for b in d:
|
|
||||||
result.add(b)
|
|
||||||
|
|
||||||
|
|
||||||
proc bytesToString(self: Serializer, input: seq[byte]): string =
|
|
||||||
for b in input:
|
|
||||||
result.add(char(b))
|
|
||||||
|
|
||||||
|
|
||||||
proc extend[T](s: var seq[T], a: openarray[T]) =
|
|
||||||
## Extends s with the elements of a
|
|
||||||
for e in a:
|
|
||||||
s.add(e)
|
|
||||||
|
|
||||||
|
|
||||||
proc writeHeaders(self: Serializer, stream: var seq[byte], file: string) =
|
proc writeHeaders(self: Serializer, stream: var seq[byte], file: string) =
|
||||||
## Writes the Peon bytecode headers in-place into a byte stream
|
## Writes the Peon bytecode headers in-place into a byte stream
|
||||||
stream.extend(self.toBytes(BYTECODE_MARKER))
|
stream.extend(BYTECODE_MARKER.toBytes())
|
||||||
stream.add(byte(PEON_VERSION.major))
|
stream.add(byte(PEON_VERSION.major))
|
||||||
stream.add(byte(PEON_VERSION.minor))
|
stream.add(byte(PEON_VERSION.minor))
|
||||||
stream.add(byte(PEON_VERSION.patch))
|
stream.add(byte(PEON_VERSION.patch))
|
||||||
stream.add(byte(len(PEON_BRANCH)))
|
stream.add(byte(len(PEON_BRANCH)))
|
||||||
stream.extend(self.toBytes(PEON_BRANCH))
|
stream.extend(PEON_BRANCH.toBytes())
|
||||||
stream.extend(self.toBytes(PEON_COMMIT_HASH))
|
stream.extend(PEON_COMMIT_HASH.toBytes())
|
||||||
stream.extend(self.toBytes(getTime().toUnixFloat().int()))
|
stream.extend(getTime().toUnixFloat().int().toBytes())
|
||||||
stream.extend(self.toBytes(computeSHA256(file)))
|
stream.extend(computeSHA256(file).toBytes())
|
||||||
|
|
||||||
|
|
||||||
proc writeLineData(self: Serializer, stream: var seq[byte]) =
|
proc writeLineData(self: Serializer, stream: var seq[byte]) =
|
||||||
|
@ -107,7 +81,14 @@ proc writeLineData(self: Serializer, stream: var seq[byte]) =
|
||||||
stream.extend(len(self.chunk.lines).toQuad())
|
stream.extend(len(self.chunk.lines).toQuad())
|
||||||
for b in self.chunk.lines:
|
for b in self.chunk.lines:
|
||||||
stream.extend(b.toTriple())
|
stream.extend(b.toTriple())
|
||||||
|
|
||||||
|
|
||||||
|
proc writeCFIData(self: Serializer, stream: var seq[byte]) =
|
||||||
|
## Writes Call Frame Information for debugging
|
||||||
|
## functions
|
||||||
|
stream.extend(len(self.chunk.cfi).toQuad())
|
||||||
|
stream.extend(self.chunk.cfi)
|
||||||
|
|
||||||
|
|
||||||
proc writeConstants(self: Serializer, stream: var seq[byte]) =
|
proc writeConstants(self: Serializer, stream: var seq[byte]) =
|
||||||
## Writes the constants table in-place into the
|
## Writes the constants table in-place into the
|
||||||
|
@ -128,27 +109,27 @@ proc readHeaders(self: Serializer, stream: seq[byte], serialized: Serialized): i
|
||||||
## Reads the bytecode headers from a given stream
|
## Reads the bytecode headers from a given stream
|
||||||
## of bytes
|
## of bytes
|
||||||
var stream = stream
|
var stream = stream
|
||||||
if stream[0..<len(BYTECODE_MARKER)] != self.toBytes(BYTECODE_MARKER):
|
if stream[0..<len(BYTECODE_MARKER)] != BYTECODE_MARKER.toBytes():
|
||||||
self.error("malformed bytecode marker")
|
self.error("malformed bytecode marker")
|
||||||
result += len(BYTECODE_MARKER)
|
result += len(BYTECODE_MARKER)
|
||||||
stream = stream[len(BYTECODE_MARKER)..^1]
|
stream = stream[len(BYTECODE_MARKER)..^1]
|
||||||
serialized.peonVer = (major: int(stream[0]), minor: int(stream[1]), patch: int(stream[2]))
|
serialized.version = (major: int(stream[0]), minor: int(stream[1]), patch: int(stream[2]))
|
||||||
stream = stream[3..^1]
|
stream = stream[3..^1]
|
||||||
result += 3
|
result += 3
|
||||||
let branchLength = stream[0]
|
let branchLength = stream[0]
|
||||||
stream = stream[1..^1]
|
stream = stream[1..^1]
|
||||||
result += 1
|
result += 1
|
||||||
serialized.peonBranch = self.bytesToString(stream[0..<branchLength])
|
serialized.branch = stream[0..<branchLength].fromBytes()
|
||||||
stream = stream[branchLength..^1]
|
stream = stream[branchLength..^1]
|
||||||
result += int(branchLength)
|
result += int(branchLength)
|
||||||
serialized.commitHash = self.bytesToString(stream[0..<40]).toLowerAscii()
|
serialized.commit = stream[0..<40].fromBytes().toLowerAscii()
|
||||||
stream = stream[40..^1]
|
stream = stream[40..^1]
|
||||||
result += 40
|
result += 40
|
||||||
serialized.compileDate = int(fromLong([stream[0], stream[1], stream[2],
|
serialized.compileDate = int(fromLong([stream[0], stream[1], stream[2],
|
||||||
stream[3], stream[4], stream[5], stream[6], stream[7]]))
|
stream[3], stream[4], stream[5], stream[6], stream[7]]))
|
||||||
stream = stream[8..^1]
|
stream = stream[8..^1]
|
||||||
result += 8
|
result += 8
|
||||||
serialized.fileHash = self.bytesToString(stream[0..<32]).toHex().toLowerAscii()
|
serialized.fileHash = stream[0..<32].fromBytes().toHex().toLowerAscii()
|
||||||
result += 32
|
result += 32
|
||||||
|
|
||||||
|
|
||||||
|
@ -164,6 +145,17 @@ proc readLineData(self: Serializer, stream: seq[byte]): int =
|
||||||
stream = stream[3..^1]
|
stream = stream[3..^1]
|
||||||
|
|
||||||
|
|
||||||
|
proc readCFIData(self: Serializer, stream: seq[byte]): int =
|
||||||
|
## Reads Call Frame Information from a stream
|
||||||
|
## of bytes
|
||||||
|
let size = [stream[0], stream[1], stream[2], stream[3]].fromQuad()
|
||||||
|
result += 4
|
||||||
|
var stream = stream[4..^1]
|
||||||
|
for i in countup(0, int(size) - 1):
|
||||||
|
self.chunk.cfi.add(stream[i])
|
||||||
|
inc(result)
|
||||||
|
|
||||||
|
|
||||||
proc readConstants(self: Serializer, stream: seq[byte]): int =
|
proc readConstants(self: Serializer, stream: seq[byte]): int =
|
||||||
## Reads the constant table from the given stream
|
## Reads the constant table from the given stream
|
||||||
## of bytes
|
## of bytes
|
||||||
|
@ -195,6 +187,7 @@ proc dumpBytes*(self: Serializer, chunk: Chunk, file, filename: string): seq[byt
|
||||||
self.chunk = chunk
|
self.chunk = chunk
|
||||||
self.writeHeaders(result, self.file)
|
self.writeHeaders(result, self.file)
|
||||||
self.writeLineData(result)
|
self.writeLineData(result)
|
||||||
|
self.writeCFIData(result)
|
||||||
self.writeConstants(result)
|
self.writeConstants(result)
|
||||||
self.writeCode(result)
|
self.writeCode(result)
|
||||||
|
|
||||||
|
@ -218,6 +211,7 @@ proc loadBytes*(self: Serializer, stream: seq[byte]): Serialized =
|
||||||
try:
|
try:
|
||||||
stream = stream[self.readHeaders(stream, result)..^1]
|
stream = stream[self.readHeaders(stream, result)..^1]
|
||||||
stream = stream[self.readLineData(stream)..^1]
|
stream = stream[self.readLineData(stream)..^1]
|
||||||
|
stream = stream[self.readCFIData(stream)..^1]
|
||||||
stream = stream[self.readConstants(stream)..^1]
|
stream = stream[self.readConstants(stream)..^1]
|
||||||
stream = stream[self.readCode(stream)..^1]
|
stream = stream[self.readCode(stream)..^1]
|
||||||
except IndexDefect:
|
except IndexDefect:
|
||||||
|
|
Loading…
Reference in New Issue