added thread info and symbol info to debug info
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Aug 2022 01:18:17 +0000 (20:18 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Fri, 12 Aug 2022 01:18:17 +0000 (20:18 -0500)
include/astnodes.h
include/wasm_emit.h
lib/linux_x86_64/lib/libovmwasm.so
misc/vscode/onyx-0.0.3.vsix
misc/vscode/out/ovmDebug.js
misc/vscode/ovmDebug.ts
src/onyxrun.c
src/parser.c
src/wasm_emit.c
src/wasm_output.h

index 0f83350116bfeab878153dd42c4803bc1f3b8a64..51acba0ab0103374181f21b6a12426c003ad1504 100644 (file)
@@ -1206,6 +1206,8 @@ struct AstFunction {
     // for this particular function.
     OnyxToken* exported_name;
 
+    OnyxToken* closing_brace;
+
     union {
         OnyxToken* intrinsic_name;
 
index 7c607295ada50222bd375569ea7abd089ef64e8b..5fde5772381b4c9736d05cc1c256ba02d313f38a 100644 (file)
@@ -645,6 +645,7 @@ typedef enum DeferredStmtType {
 typedef struct DeferredStmt {
     DeferredStmtType type;
     u32 depth;
+    AstDefer *defer_node;
 
     union {
         AstNode *stmt;
@@ -820,12 +821,29 @@ typedef struct DebugFileInfo {
     u32 line_count;
 } DebugFileInfo;
 
+typedef enum DebugSymLoc {
+    DSL_REGISTER = 1,
+    DSL_STACK    = 2,
+    DSL_GLOBAL   = 3,
+} DebugSymLoc;
+
+typedef struct DebugSymInfo {
+    u32 sym_id;
+    u32 location_type;
+    u32 location_num;
+    char *name;
+    u32 type;
+} DebugSymInfo;
+
 typedef struct DebugContext {
     bh_allocator allocator;
 
     Table(DebugFileInfo) file_info;
     u32 next_file_id;
 
+    bh_arr(DebugSymInfo) sym_info;
+    u32 next_sym_id;
+
     bh_arr(DebugFuncContext) funcs;
     bh_buffer                op_buffer;
 
index e7177dd43c39971068b780b853d3225e9cadc35a..a2c402399d031ccf244cb4710ceb478e102f400a 100755 (executable)
Binary files a/lib/linux_x86_64/lib/libovmwasm.so and b/lib/linux_x86_64/lib/libovmwasm.so differ
index 958ce8d2b8263eb07c3312267360da7e16be8413..1a8df041dded5244c71198733895b8abbcbeaa27 100644 (file)
Binary files a/misc/vscode/onyx-0.0.3.vsix and b/misc/vscode/onyx-0.0.3.vsix differ
index 08e088e2e747ecc7dc91ade335930ca96f6e9482..e7e95a70db78e98be07437968ceb0d875cd73697 100644 (file)
@@ -20,6 +20,8 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
         super("ovm-debug-log.txt");
         this._configurationDone = new await_notify_1.Subject();
         this._clientConnectedNotifier = new await_notify_1.Subject();
+        this._variableReferences = new debugadapter_1.Handles();
+        this._frameReferences = new debugadapter_1.Handles();
         this.setDebuggerLinesStartAt1(true);
         this.setDebuggerColumnsStartAt1(true);
         this._loadedSources = new Map();
@@ -98,14 +100,11 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
         this.sendEvent(new debugadapter_1.InitializedEvent());
     }
     configurationDoneRequest(response, args, request) {
-        console.log("CONFIGURATION DONE");
         super.configurationDoneRequest(response, args);
         this._configurationDone.notify();
     }
     disconnectRequest(response, args, request) {
-        console.log(`disconnectRequest suspend: ${args.suspendDebuggee}, terminate: ${args.terminateDebuggee}`);
         if (args.terminateDebuggee) {
-            console.log("TERMINATE");
             if (this.running_process) {
                 this.running_process.kill('SIGTERM');
             }
@@ -117,7 +116,6 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
     }
     setBreakPointsRequest(response, args, request) {
         return __awaiter(this, void 0, void 0, function* () {
-            console.log("BREAKPOINTS", args, response);
             while (!this._clientConnected) {
                 yield this._clientConnectedNotifier.wait();
             }
@@ -147,31 +145,49 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
                         this._loadedSources.set(source.name, source);
                         this.sendEvent(new debugadapter_1.LoadedSourceEvent("new", source));
                     }
-                    return new debugadapter_1.StackFrame(i, f.funcname, source, f.line);
+                    let frameRef = this._frameReferences.create({
+                        threadId: args.threadId,
+                        frameIndex: i
+                    });
+                    return new debugadapter_1.StackFrame(frameRef, f.funcname, source, f.line);
                 })
             };
             this.sendResponse(response);
         });
     }
     threadsRequest(response, request) {
-        console.log("THREADS");
-        response.body = {
-            threads: [
-                new debugadapter_1.Thread(1, "main thread"),
-            ]
-        };
-        this.sendResponse(response);
+        return __awaiter(this, void 0, void 0, function* () {
+            let threads = yield this.debugger.threads();
+            response.body = {
+                threads: threads.map(t => new debugadapter_1.Thread(t.id, t.name))
+            };
+            this.sendResponse(response);
+        });
     }
     scopesRequest(response, args, request) {
-        console.log("SCOPES");
+        let frameId = args.frameId;
+        let frameRef = this._frameReferences.get(frameId);
+        let varRef = this._variableReferences.create(frameRef);
         response.body = {
             scopes: [
-                new debugadapter_1.Scope("Locals", 1, false),
-                new debugadapter_1.Scope("Globals", 2, true)
+                new debugadapter_1.Scope("Locals", varRef, false),
             ]
         };
         this.sendResponse(response);
     }
+    variablesRequest(response, args, request) {
+        return __awaiter(this, void 0, void 0, function* () {
+            const frameRef = this._variableReferences.get(args.variablesReference, { frameIndex: 0, threadId: 0 });
+            let vs = (yield this.debugger.variables(frameRef.frameIndex, frameRef.threadId))
+                .map(v => {
+                let nv = new debugadapter_1.Variable(v.name, v.value);
+                nv.type = v.type;
+                return nv;
+            });
+            response.body = { variables: vs };
+            this.sendResponse(response);
+        });
+    }
     launchRequest(response, args, request) {
         this.running_process = child_process.spawn("onyx-run", ["--debug", args.wasmFile], {
             "cwd": args.workingDir,
@@ -184,7 +200,6 @@ class OVMDebugSession extends debugadapter_1.LoggingDebugSession {
     }
     attachRequest(response, args, request) {
         return __awaiter(this, void 0, void 0, function* () {
-            console.log("ATTACH");
             yield this._configurationDone.wait(1000);
             yield this.debugger.connect(args.socketPath);
             this._clientConnected = true;
@@ -232,6 +247,8 @@ var OVMCommand;
     OVMCommand[OVMCommand["CLR_BRK"] = 3] = "CLR_BRK";
     OVMCommand[OVMCommand["STEP"] = 5] = "STEP";
     OVMCommand[OVMCommand["TRACE"] = 6] = "TRACE";
+    OVMCommand[OVMCommand["THREADS"] = 7] = "THREADS";
+    OVMCommand[OVMCommand["VARS"] = 8] = "VARS";
 })(OVMCommand || (OVMCommand = {}));
 var OVMEvent;
 (function (OVMEvent) {
@@ -335,6 +352,28 @@ class OVMDebugger extends EventEmitter {
         this.pending_responses[cmd_id] = OVMCommand.TRACE;
         return this.preparePromise(cmd_id);
     }
+    threads() {
+        let data = new ArrayBuffer(8);
+        let view = new DataView(data);
+        let cmd_id = this.next_command_id;
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.THREADS, true);
+        this.client.write(new Uint8Array(data));
+        this.pending_responses[cmd_id] = OVMCommand.THREADS;
+        return this.preparePromise(cmd_id);
+    }
+    variables(frame_index, thread_id) {
+        let data = new ArrayBuffer(16);
+        let view = new DataView(data);
+        let cmd_id = this.next_command_id;
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.VARS, true);
+        view.setUint32(8, frame_index, true);
+        view.setUint32(12, thread_id, true);
+        this.client.write(new Uint8Array(data));
+        this.pending_responses[cmd_id] = OVMCommand.VARS;
+        return this.preparePromise(cmd_id);
+    }
     parseIncoming(data) {
         let parser = new DataParser(data);
         while (parser.offset != data.length) {
@@ -413,6 +452,28 @@ class OVMDebugger extends EventEmitter {
                 this.resolvePromise(msg_id, result);
                 break;
             }
+            case OVMCommand.THREADS: {
+                let result = new Array();
+                let count = parser.parseUint32();
+                for (let i = 0; i < count; i++) {
+                    let id = parser.parseUint32();
+                    let name = parser.parseString();
+                    result.push({ id, name });
+                }
+                this.resolvePromise(msg_id, result);
+                break;
+            }
+            case OVMCommand.VARS: {
+                let result = new Array();
+                while (parser.parseInt32() == 0) {
+                    let name = parser.parseString();
+                    let value = parser.parseString();
+                    let type = parser.parseString();
+                    result.push({ name, value, type });
+                }
+                this.resolvePromise(msg_id, result);
+                break;
+            }
             default:
                 console.log("Unrecognized command. ", cmd_id, msg_id);
         }
@@ -449,14 +510,17 @@ class DataParser {
     constructor(data) {
         this.data = data;
         this.view = new DataView(data.buffer);
-        console.log("PARSING", this.data);
         this.offset = 0;
     }
     parseInt32() {
+        if (this.offset >= this.data.length)
+            return 0;
         this.offset += 4;
         return this.view.getInt32(this.offset - 4, true);
     }
     parseUint32() {
+        if (this.offset >= this.data.length)
+            return 0;
         this.offset += 4;
         return this.view.getUint32(this.offset - 4, true);
     }
@@ -470,6 +534,8 @@ class DataParser {
         return str;
     }
     parseBool() {
+        if (this.offset >= this.data.length)
+            return 0;
         this.offset += 1;
         return this.view.getUint8(this.offset - 1) != 0;
     }
index f5020a4e987b66b9d31353f0ccd4fc97720aa62a..9c6228515645fc54072c91a4706078acce7c9d9d 100644 (file)
@@ -3,7 +3,7 @@ import {
        LoggingDebugSession,
        InitializedEvent, TerminatedEvent, StoppedEvent, BreakpointEvent, OutputEvent,
        ProgressStartEvent, ProgressUpdateEvent, ProgressEndEvent, InvalidatedEvent,
-       Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent
+       Thread, StackFrame, Scope, Source, Handles, Breakpoint, MemoryEvent, ThreadEvent, LoadedSourceEvent, Variable
 } from '@vscode/debugadapter';
 import { DebugProtocol } from '@vscode/debugprotocol';
 import EventEmitter = require('node:events');
@@ -12,7 +12,6 @@ import { Subject } from "await-notify";
 import * as net from "node:net";
 import * as child_process from "node:child_process";
 import { ChildProcess } from 'node:child_process';
-import { Message } from '@vscode/debugadapter/lib/messages';
 
 
 interface IOVMAttachRequestArguments extends DebugProtocol.AttachRequestArguments {
@@ -26,6 +25,11 @@ interface IOVMLaunchRequestArguments extends DebugProtocol.AttachRequestArgument
     stopOnEntry?: boolean;
 }
 
+interface IFrameReference {
+       frameIndex: number;
+       threadId: number;
+}
+
 export class OVMDebugSession extends LoggingDebugSession {
 
        private debugger: OVMDebugger;
@@ -37,6 +41,9 @@ export class OVMDebugSession extends LoggingDebugSession {
 
        private _loadedSources: Map<string, Source>;
 
+       private _variableReferences = new Handles<IFrameReference>();
+       private _frameReferences = new Handles<IFrameReference>();
+
     public constructor() {
         super("ovm-debug-log.txt");
 
@@ -143,18 +150,13 @@ export class OVMDebugSession extends LoggingDebugSession {
     }
 
     protected configurationDoneRequest(response: DebugProtocol.ConfigurationDoneResponse, args: DebugProtocol.ConfigurationDoneArguments, request?: DebugProtocol.Request): void {
-               console.log("CONFIGURATION DONE");
         super.configurationDoneRequest(response, args);
 
                this._configurationDone.notify();
     }
 
        protected disconnectRequest(response: DebugProtocol.DisconnectResponse, args: DebugProtocol.DisconnectArguments, request?: DebugProtocol.Request): void {
-               console.log(`disconnectRequest suspend: ${args.suspendDebuggee}, terminate: ${args.terminateDebuggee}`);
-
                if (args.terminateDebuggee) {
-                       console.log("TERMINATE");
-
                        if (this.running_process) {
                                this.running_process.kill('SIGTERM');
                        }
@@ -168,8 +170,6 @@ export class OVMDebugSession extends LoggingDebugSession {
     }
 
        protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments, request?: DebugProtocol.Request): Promise<void> {
-               console.log("BREAKPOINTS", args, response);
-
                while (!this._clientConnected) {
                        await this._clientConnectedNotifier.wait();
                }
@@ -211,37 +211,54 @@ export class OVMDebugSession extends LoggingDebugSession {
                                        this.sendEvent(new LoadedSourceEvent("new", source));
                                }
                                
-                               return new StackFrame(i, f.funcname, source, f.line);
+                               let frameRef = this._frameReferences.create({
+                                       threadId: args.threadId,
+                                       frameIndex: i
+                               });
+                               return new StackFrame(frameRef, f.funcname, source, f.line);
                        })
                };
 
                this.sendResponse(response);
        }
 
-       protected threadsRequest(response: DebugProtocol.ThreadsResponse, request?: DebugProtocol.Request): void {
-               console.log("THREADS");
+       protected async threadsRequest(response: DebugProtocol.ThreadsResponse, request?: DebugProtocol.Request): Promise<void> {
+               let threads = await this.debugger.threads();
 
                response.body = {
-                       threads: [
-                               new Thread(1, "main thread"),
-                       ]
+                       threads: threads.map(t => new Thread(t.id, t.name))
                };
 
                this.sendResponse(response);
        }
 
        protected scopesRequest(response: DebugProtocol.ScopesResponse, args: DebugProtocol.ScopesArguments, request?: DebugProtocol.Request): void {
-               console.log("SCOPES");
+               let frameId = args.frameId;
+
+               let frameRef = this._frameReferences.get(frameId);
+               let varRef   = this._variableReferences.create(frameRef);
 
                response.body = {
                        scopes: [
-                               new Scope("Locals", 1, false),
-                               new Scope("Globals", 2, true)
+                               new Scope("Locals", varRef, false),
                        ]
                };
                this.sendResponse(response);    
        }
 
+       protected async variablesRequest(response: DebugProtocol.VariablesResponse, args: DebugProtocol.VariablesArguments, request?: DebugProtocol.Request): Promise<void> {
+               const frameRef = this._variableReferences.get(args.variablesReference, {frameIndex: 0, threadId: 0});
+               let vs: Variable[] = (await this.debugger.variables(frameRef.frameIndex, frameRef.threadId))
+                       .map(v => {
+                               let nv = new Variable(v.name, v.value) as DebugProtocol.Variable;
+                               nv.type = v.type;
+                               return nv;
+                       });
+
+               response.body = { variables: vs };
+               this.sendResponse(response);
+       }
+
     protected launchRequest(response: DebugProtocol.LaunchResponse, args: IOVMLaunchRequestArguments, request?: DebugProtocol.Request): void {
                this.running_process = child_process.spawn("onyx-run", ["--debug", args.wasmFile], {
                        "cwd": args.workingDir, 
@@ -256,7 +273,6 @@ export class OVMDebugSession extends LoggingDebugSession {
     }
 
     protected async attachRequest(response: DebugProtocol.AttachResponse, args: IOVMAttachRequestArguments, request?: DebugProtocol.Request): Promise<void> {
-               console.log("ATTACH");
                await this._configurationDone.wait(1000);
 
                await this.debugger.connect(args.socketPath);
@@ -318,13 +334,26 @@ interface IBreakpointValidation {
        line: number;
 }
 
+interface IThreadInfo {
+       id: number;
+       name: string;
+}
+
+interface IVariableInfo {
+       name: string;
+       value: string;
+       type: string;
+}
+
 enum OVMCommand {
        NOP     = 0,
        RES     = 1,
        BRK     = 2,
        CLR_BRK = 3,
        STEP    = 5,
-       TRACE   = 6
+       TRACE   = 6,
+       THREADS = 7,
+       VARS    = 8
 }
 
 enum OVMEvent {
@@ -461,6 +490,40 @@ class OVMDebugger extends EventEmitter {
                return this.preparePromise(cmd_id);
        }
 
+       threads(): Promise<IThreadInfo[]> {
+        let data = new ArrayBuffer(8);
+        let view = new DataView(data);
+
+        let cmd_id = this.next_command_id;
+
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.THREADS, true);
+
+        this.client.write(new Uint8Array(data));
+
+        this.pending_responses[cmd_id] = OVMCommand.THREADS;
+
+               return this.preparePromise(cmd_id);
+       }
+
+       variables(frame_index: number, thread_id: number): Promise<IVariableInfo[]> {
+        let data = new ArrayBuffer(16);
+        let view = new DataView(data);
+
+        let cmd_id = this.next_command_id;
+
+        view.setUint32(0, cmd_id, true);
+        view.setUint32(4, OVMCommand.VARS, true);
+        view.setUint32(8, frame_index, true);
+        view.setUint32(12, thread_id, true);
+
+        this.client.write(new Uint8Array(data));
+
+        this.pending_responses[cmd_id] = OVMCommand.VARS;
+
+               return this.preparePromise(cmd_id);
+       }
+
        private parseIncoming(data: Buffer): void {
                let parser = new DataParser(data);
 
@@ -557,6 +620,34 @@ class OVMDebugger extends EventEmitter {
                                break;
                        }
 
+                       case OVMCommand.THREADS: {
+                               let result = new Array<IThreadInfo>();
+
+                               let count = parser.parseUint32();
+                               for (let i = 0; i < count; i++) {
+                                       let id   = parser.parseUint32();
+                                       let name = parser.parseString();
+                                       result.push({id, name});
+                               }
+
+                               this.resolvePromise(msg_id, result);
+                               break;
+                       }
+
+                       case OVMCommand.VARS: {
+                               let result = new Array<IVariableInfo>();
+
+                               while (parser.parseInt32() == 0) {
+                                       let name  = parser.parseString();
+                                       let value = parser.parseString();
+                                       let type  = parser.parseString();
+                                       result.push({name, value, type});
+                               }
+
+                               this.resolvePromise(msg_id, result);
+                               break;
+                       }
+
                        default:
                                console.log("Unrecognized command. ", cmd_id, msg_id);
                }
@@ -604,16 +695,19 @@ class DataParser {
     constructor(data: Buffer) {
         this.data = data;
         this.view = new DataView(data.buffer);
-               console.log("PARSING", this.data);
         this.offset = 0;
     }
 
     parseInt32() {
+               if (this.offset >= this.data.length) return 0;
+
         this.offset += 4;
         return this.view.getInt32(this.offset - 4, true);
     }
 
     parseUint32() {
+               if (this.offset >= this.data.length) return 0;
+
         this.offset += 4;
         return this.view.getUint32(this.offset - 4, true);
     }
@@ -630,6 +724,8 @@ class DataParser {
     }
 
     parseBool() {
+               if (this.offset >= this.data.length) return 0;
+
         this.offset += 1;
         return this.view.getUint8(this.offset - 1) != 0;
     }
index 1f9bb1908993679c1cc41c4d0a7d5ef5465ac2e8..5200090b37078244b1cb525034d80d481bce7d82 100644 (file)
@@ -18,7 +18,7 @@ int main(int argc, char *argv[]) {
         wasm_file_idx = 2;
     }
 
-    if (argc < 3) {
+    if (debug && argc < 3) {
         fprintf(stderr, "Expected a WASM file to run.\n");
         return 1;
     }
@@ -38,5 +38,5 @@ int main(int argc, char *argv[]) {
     bh_buffer data;
     data.data = wasm_data.data;
     data.length = wasm_data.length;
-    return onyx_run_wasm(data, argc - 1, argv + 1);
+    return onyx_run_wasm(data, argc - 1 - debug, argv + 1 + debug);
 }
index 22fcf9d8fef4d230bc942adb351bd0a02e741f7f..e7a08a2a8e5bcb695c4631b44c4672bbc48fa78d 100644 (file)
@@ -2465,6 +2465,9 @@ static AstFunction* parse_function_definition(OnyxParser* parser, OnyxToken* tok
 
     func_def->body = parse_block(parser, 1, name);
 
+    // :LinearTokenDependent
+    func_def->closing_brace = parser->curr - 1;
+
 function_defined:
     if (bh_arr_length(polymorphic_vars) > 0) {
         func_def->kind = Ast_Kind_Polymorphic_Proc;
index a1df61e357c705107dd70a62f79dd00e342e60da..173614c12f2bf4e69705f61febf3fb7d8859a65e 100644 (file)
@@ -219,6 +219,35 @@ static inline b32 should_emit_function(AstFunction* fd) {
 
 #ifdef ENABLE_DEBUG_INFO
 
+static u32 debug_introduce_symbol(OnyxWasmModule *mod, OnyxToken *token, DebugSymLoc loc, u32 num, Type* type) {
+
+    u32 id = mod->debug_context->next_sym_id++;
+
+    DebugSymInfo sym_info;
+    sym_info.sym_id = id;
+    sym_info.location_type = loc;
+    sym_info.location_num  = num;
+    sym_info.type          = type->id;
+
+    if (token) {
+        token_toggle_end(token);
+        sym_info.name = bh_strdup(context.ast_alloc, token->text);
+        token_toggle_end(token);
+    } else {
+        sym_info.name = NULL;
+    }
+
+    bh_arr_push(mod->debug_context->sym_info, sym_info);
+
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_SYM);
+    u32 leb_len=0;
+    u8 *bytes = uint_to_uleb128(id, &leb_len);
+    bh_buffer_append(&mod->debug_context->op_buffer, bytes, leb_len);
+
+    mod->debug_context->last_op_was_rep = 0;
+    return id;
+}
+
 static u32 debug_get_file_id(OnyxWasmModule *mod, const char *name) {
     assert(mod && mod->debug_context);
 
@@ -360,13 +389,26 @@ static void debug_end_function(OnyxWasmModule *mod) {
     mod->debug_context->last_token = NULL;
 }
 
+static void debug_enter_symbol_frame(OnyxWasmModule *mod) {
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_PUSHF);
+    mod->debug_context->last_op_was_rep = 0;
+}
+
+static void debug_leave_symbol_frame(OnyxWasmModule *mod) {
+    bh_buffer_write_byte(&mod->debug_context->op_buffer, DOT_POPF);
+    mod->debug_context->last_op_was_rep = 0;
+}
+
 #else
 
+#define debug_introduce_symbol(mod, name, loc, num, type) (void)0
 #define debug_get_file_id(mod, name) (void)0
 #define debug_set_position(mod, token) (void)0
 #define debug_emit_instruction(mod, token) (void)0
 #define debug_begin_function(mod, idx, token, name) (void)0
 #define debug_end_function(mod) (void)0
+#define debug_enter_symbol_frame(mod) (void)0
+#define debug_leave_symbol_frame(mod) (void)0
 
 #endif
 
@@ -492,6 +534,7 @@ EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
                                                 ? SBT_Return_Block
                                                 : SBT_Breakable_Block,
                                                 block->token);
+        debug_enter_symbol_frame(mod);
     }
 
     forll (AstNode, stmt, block->body, next) {
@@ -511,8 +554,10 @@ EMIT_FUNC(block, AstBlock* block, b32 generate_block_headers) {
         emit_free_local_allocations(mod, &code);
     }
 
-    if (generate_block_headers)
+    if (generate_block_headers) {
         emit_leave_structured_block(mod, &code);
+        debug_leave_symbol_frame(mod);
+    }
 
     *pcode = code;
 }
@@ -646,6 +691,12 @@ EMIT_FUNC(local_allocation, AstTyped* stmt) {
     u64 local_idx = local_allocate(mod->local_alloc, stmt);
     bh_imap_put(&mod->local_map, (u64) stmt, local_idx);
 
+    if (local_is_wasm_local(stmt)) {
+        debug_introduce_symbol(mod, stmt->token, DSL_REGISTER, local_idx, stmt->type);
+    } else {
+        debug_introduce_symbol(mod, stmt->token, DSL_STACK, local_idx, stmt->type);
+    }
+
     if (stmt->kind == Ast_Kind_Local && !(stmt->flags & Ast_Flag_Decl_Followed_By_Init)) {
         bh_arr(WasmInstruction) code = *pcode;
         if (local_is_wasm_local(stmt)) {
@@ -1409,6 +1460,11 @@ EMIT_FUNC(for, AstFor* for_node) {
     u64 iter_local = local_allocate(mod->local_alloc, (AstTyped *) var);
     bh_imap_put(&mod->local_map, (u64) var, iter_local);
 
+    debug_enter_symbol_frame(mod);
+    debug_introduce_symbol(mod, var->token,
+        local_is_wasm_local((AstTyped *) var) ? DSL_REGISTER : DSL_STACK,
+        iter_local, var->type);
+
     emit_expression(mod, &code, for_node->iter);
 
     switch (for_node->loop_type) {
@@ -1425,6 +1481,7 @@ EMIT_FUNC(for, AstFor* for_node) {
     }
 
     local_free(mod->local_alloc, (AstTyped *) var);
+    debug_leave_symbol_frame(mod);
 
     *pcode = code;
 }
@@ -1512,7 +1569,10 @@ EMIT_FUNC(switch, AstSwitch* switch_node) {
 
         u64 bn = bh_imap_get(&block_map, (u64) sc->block);
 
+        // Maybe the Symbol Frame idea should be controlled as a block_flag?
+        debug_enter_symbol_frame(mod);
         emit_block(mod, &code, sc->block, 0);
+        debug_leave_symbol_frame(mod);
 
         if (bh_arr_last(code).type != WI_JUMP)
             WID(NULL, WI_JUMP, block_num - bn);
@@ -1536,6 +1596,7 @@ EMIT_FUNC(defer, AstDefer* defer) {
     bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
         .type = Deferred_Stmt_Node,
         .depth = bh_arr_length(mod->structured_jump_target),
+        .defer_node= defer,
         .stmt = defer->stmt,
     }));
 }
@@ -1544,6 +1605,7 @@ EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
     bh_arr_push(mod->deferred_stmts, ((DeferredStmt) {
         .type = Deferred_Stmt_Code,
         .depth = bh_arr_length(mod->structured_jump_target),
+        .defer_node= NULL,
         .instructions = deferred_code,
         .instruction_count = code_count,
     }));
@@ -1552,6 +1614,10 @@ EMIT_FUNC(defer_code, WasmInstruction* deferred_code, u32 code_count) {
 EMIT_FUNC(deferred_stmt, DeferredStmt deferred_stmt) {
     bh_arr(WasmInstruction) code = *pcode;
 
+    if (deferred_stmt.defer_node) {
+        WI(deferred_stmt.defer_node->token, WI_NOP);
+    }
+
     switch (deferred_stmt.type) {
         case Deferred_Stmt_Node: emit_statement(mod, &code, deferred_stmt.stmt); break;
         case Deferred_Stmt_Code: {
@@ -3639,6 +3705,7 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
             switch (type_get_param_pass(param->local->type)) {
                 case Param_Pass_By_Value: {
                     if (type_is_structlike_strict(param->local->type)) {
+                        debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx, param->local->type);
                         bh_imap_put(&mod->local_map, (u64) param->local, localidx | LOCAL_IS_WASM);
                         localidx += type_structlike_mem_count(param->local->type);
 
@@ -3648,6 +3715,7 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
                 }
 
                 case Param_Pass_By_Implicit_Pointer: {
+                    debug_introduce_symbol(mod, param->local->token, DSL_REGISTER, localidx, param->local->type);
                     bh_imap_put(&mod->local_map, (u64) param->local, localidx++ | LOCAL_IS_WASM);
                     break;
                 }
@@ -3701,6 +3769,11 @@ static void emit_function(OnyxWasmModule* mod, AstFunction* fd) {
     WasmFuncType* ft = mod->types[type_idx];
     emit_zero_value(mod, &wasm_func.code, ft->return_type);
 
+    if (fd->closing_brace) {
+        debug_emit_instruction(mod, fd->closing_brace);
+        bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_NOP, 0x00 }));
+    }
+
     debug_emit_instruction(mod, NULL);
     bh_arr_push(wasm_func.code, ((WasmInstruction){ WI_BLOCK_END, 0x00 }));
 
@@ -4210,9 +4283,11 @@ OnyxWasmModule onyx_wasm_module_create(bh_allocator alloc) {
     module.debug_context = bh_alloc_item(context.ast_alloc, DebugContext);
     module.debug_context->allocator = global_heap_allocator;
     module.debug_context->next_file_id = 0;
+    module.debug_context->next_sym_id = 0;
     module.debug_context->last_token = NULL;
 
     sh_new_arena(module.debug_context->file_info);
+    bh_arr_new(global_heap_allocator, module.debug_context->sym_info, 32);
     bh_arr_new(global_heap_allocator, module.debug_context->funcs, 16);
 
     bh_buffer_init(&module.debug_context->op_buffer, global_heap_allocator, 1024);
index 8f9ccc4a021ba50d7a31e0935a5f84d55816c8f3..1e2b60075a2116ef2f68433a258ddd858aac85e4 100644 (file)
@@ -821,6 +821,34 @@ static i32 output_ovm_debug_sections(OnyxWasmModule* module, bh_buffer* buff) {
         bh_buffer_concat(buff, section_buff);
     }
 
+    {
+        // ovm_debug_syms sections
+        bh_buffer_clear(&section_buff);
+        bh_buffer_write_byte(buff, WASM_SECTION_ID_CUSTOM);
+
+        output_custom_section_name("ovm_debug_syms", &section_buff);
+
+        i32 sym_count = bh_arr_length(ctx->sym_info);
+        output_unsigned_integer(sym_count, &section_buff);
+
+        fori (i, 0, sym_count) {
+            DebugSymInfo *sym = &ctx->sym_info[i];
+            output_unsigned_integer(sym->sym_id, &section_buff);
+            if (sym->name) {
+                output_name(sym->name, strlen(sym->name), &section_buff);
+            } else {
+                output_unsigned_integer(0, &section_buff);
+            }
+            output_unsigned_integer(sym->location_type, &section_buff);
+            output_unsigned_integer(sym->location_num, &section_buff);
+            output_unsigned_integer(sym->type, &section_buff);
+        }
+
+        output_unsigned_integer(section_buff.length, buff);
+
+        bh_buffer_concat(buff, section_buff);
+    }
+
     {
         // ovm_debug_ops section
         bh_buffer_clear(&section_buff);