Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Codea+ : Debugger & Threading Library (On WebRepo)

edited May 16 in Code Sharing Posts: 412

Latest feature list:

  • Threads
  • Debugger
  • Promises
  • Semaphores
  • Preprocessor
  • Async / Await (used in conjunction with Promises)
  • Documentation generator powered by annotated comments.

Hi All,

This is just a little something I’ve been working on I’m sure some of you may appreciate :smile:

Codea+ is a threading library allowing complex thread based systems to be created with ease.

As an added bonus, having everything run on a thread allows us to integrate a debugger directly into Codea's side panel!

Steppers,
o7

P.S. Simeon & John I hope you don’t mind the name :wink:


Further documentation will follow but for now:
- dbg([optional boolean condition]) : Break into the debugger

While in the debugger, you may enter the commands below into the sidepanel input.

Flow:
- c() : Exit the debugger and continue execution.

View Backtrace:
- t() : Print the backtrace again.

Viewing Local Variables:
- l() : List the local variables in the top stack frame.
- l(stackframe_number) : List the local variables in the given stack frame.
- l(variable_name) : Display the value of the named local variable (scans stack frames automatically).
- l(variable_name, stackframe_number) : Display the value of the named local variable in the given stack frame.

Setting Local Variables:
- sl(variable_name, value) : Sets the value of the named local variable (scans stack frames automatically).
- sl(variable_name, value, stackframe_number) : Sets the value of the named local variable in the given stack frame.


Example:

function setup()
    local test = 19
    dbg()
    print("Test value is: " .. tostring(test))
end

Flow

OUTPUT:

Debug break:
[Main]:3
[Thread]:141



USER INPUT: l()
OUTPUT:

Locals (level 1):
test = number: 19



USER INPUT: sl("test", 180)
OUTPUT:

Local 'test' set in stack frame 1



USER INPUT: l()
OUTPUT:

Locals (level 1):
test = number: 180



USER INPUT: c() (exits debugger)
OUTPUT: (from user program)

Test value is: 180

Comments

  • Posts: 1,795

    Holy heck @Steppers i haven’t tried this yet but I must say between you and @jfperusse you seem to be single-handedly (double-single-handedly?) addressing lots of the biggest obstacles to using Codea.

  • Posts: 1,795

    @Steppers i don’t know if you ever saw my debugger—it was glitchy to the point where @RonJeffries swore he would never run it again on his projects, so it probably was around 80% trash, but it did have a line-by-line step-through feature. If you check it out and there’s anything there that looks salvageable please take it with my blessing!

  • JohnJohn Admin Mod
    Posts: 762

    @Steppers wow this is a very clever use of co-routines and tween(). I'm surprised how much tween gets used to hack in special updates outside of the draw() function.

    I'm currently looking into proper debug support in Codea 4 although this looks like a nice stop gap in the mean time

  • Posts: 412

    @UberGoober Oh nice! I’ve just taken a look. As much as I’d really love to have stepping functionality, due to the usage of coroutines in all of this (avoids locking up of Codea’s UI) I can’t call coroutine.yield() from inside a debug hook :disappointed:

    @John Thanks, I’d heard you were adding better support so I’m looking forward to it!

  • Posts: 1,795

    A little weird that coroutines won’t yield during debugging. I wonder why.

  • Posts: 412
    @UberGoober Lua has a limitation that you can't yield across a C function boundary and as the hook is triggered from the C side of Lua it hits that.
  • JohnJohn Admin Mod
    Posts: 762

    I've got something working based on this: https://github.com/devcat-studio/VSCodeLuaDebug

    This gives complete debugger support via TCP sockets. The protocol is fairly straight forward (basically REST over TCP). So that means breakpoints, step (in, over, out), stacktrace, view/change local variables, etc... The main difficulty is making a working UI for it. It also freezes the runtime in place (which is pretty typical of any debugger, the entire Unity editor freezes during debugging for instance).

    I did have to revert to Lua 5.3 in order to apply the HALT_OP patch, which lets you breakpoints with virtually no overhead (including conditional breakpoints). But at some point it should be possible to apply it to Lua 5.4

    This is still for Codea 4 but if we're able to integrate it into the editor, it should make debugging far easier (I've been wanting this for my own projects for a while)

  • Posts: 412

    Got a few more goodies some of you may like! I just need to tidy some of it up before I update it on WebRepo.

    Very JavaScript-like Promises!

        -- Async functions return a Promise object
        -- and don’t execute immediately.
        local sendMessage = async(function(delay)
            delay = delay or 1 -- Delay for 1 second by default
            Thread.sleep(delay)
            if delay > 2 then
                error("Failed to send message!")
            end
            return "Message sent!"
        end)
    
        -- Call the async 'sendMessage' function
        sendMessage()
        -- Add a success handler
        :thenDo(function(msg)
            print(msg)
        end)
        -- Add an error handler
        :catch(function(err)
            objc.warning("Err: " .. err)
        end)
        -- Add a 'finally' handler
        :finally(function()
            print("Promise chain complete!")
        end)
    
        -- Use of 'await()' to wait for a Promise
        -- to be fulfilled.
        --
        -- Try to send a message with a delay of 3!
        -- > 2 seconds throws an error so we add
        -- a 'catch' handler here to catch the error.
        --
        -- Prints 'nil'
        print(await(sendMessage(3):catch(function()end)))
    
        -- Same thing but without the error handler.
        -- This causes a crash instead
        print(await(sendMessage(3)))
    

    All of this is still compatible with the debugger too :smile:

    Steppers
    o7

  • Posts: 308

    been testing this out, and i’m having a couple trouble spots - here’s my experience

    can’t resume a coroutine ( if dbg() is used in a coroutine then c() will not resume that)

    can’t index “self” with l(self) or l(self.myVar)

    l(table) just says it’s a table, not useful to as what’s inside

  • Posts: 412

    Hey @skar I'll look into the coroutine issue but you should be able to do:
    'l("self").myVar'

    l() only works with the immediate value and also returns it so you can use the value in other ways.

  • edited May 11 Posts: 412

    @skar The latest version ≥1.3.0 on WebRepo should now fully support the debugger within coroutines :smile:

  • edited May 12 Posts: 412

    @Simeon @John Any chance we could pass in the asset key corresponding to the current source file when it’s loaded? I have something in the works and it would be really very useful.

    Like this (Main.lua):

    local thisFileAsset = …
    assert(thisFileAsset == asset.Main)
    
    function setup()
        print(thisFileAsset)
    end
    

    If passing a plain filepath would be easier then that would work too.

    Cheers!

  • edited May 12 Posts: 412

    @Simeon @John OR even better, a callback that would be called to modify source code that will be loaded prior to it actually being loaded to allow for dynamic code modifications?

    function onTabLoad(source, tab)
    
        -- Modify the source here
    
        return source -- return the modified source to be loaded.
    end
    

    Pretty much, I’m working on a preprocessor of sorts but at the moment I have to rely on hardcoded file paths in each file I want processed.

    if Preprocess(asset.Main) then return end -- Preprocess
    
    -- Compile time constant
    -- Occurances of 'kClearColor' are replaced with '40, 40, 50'
    -- at LOAD time. kClearColor is NOT a variable!
    MACRO(kClearColor, 40, 40, 50)
    
    -- Another compile time constant.
    MACRO(kStartupMessage, "Hello Preprocessor!")
    
    function setup()
        print(kStartupMessage)
    end
    
    function draw()
        background(kClearColor)
    end
    
  • Posts: 308

    another small caveat i’ve encountered, if the high level project uses coroutines like mine do, a call to coroutine.yield() can have unexpected results.

    basically it can give a false impression that the local coroutine is yielding when in fact it’s just the codea+ thread that is yielding, this is unfortunately because we can’t pass a coroutine to yield specifically, instead the function will decide what the current coroutine is to yield

    probably nothing that can be fixed about this, just need to be aware

  • Posts: 412
    @skar What unexpected results are you seeing?

    Codea+ threads are just managed coroutines and the overridden coroutine functions just wrap around the Codea+ thread objects.

    The behaviour itself should match original behaviour (without Codea+).
  • Posts: 308

    yes I guess I wasn’t completely clear, it’s not a fault of Codea+, it’s a pitfall of understanding that can be easy to fall into

    For example, in my new bitmapfont I use a coroutine to compute the text to mesh, and this coroutine will attempt to yield once every letter, but let’s say I as the developer forgot to actually write my coroutine and all I did was call coroutine.yield? Well if I have Codea+ running in the back, nothing happens, but if I don’t have Codea+ as a dependency then I get an error saying “attempted to yield outside of a coroutine”

  • Posts: 412
    @skar Ah, I see what you mean. In that specific case I can cover that in Codea+ too.
  • Posts: 1,795
    Can’t run latest Codea+
  • edited May 15 Posts: 412
    @UberGoober What project is that? Try deleting the Codea+ project and redownload it. The TweenMod tab should have been deleted.

    Edit: yep, that's the issue. WebRepo currently doesn't handle deleted files during a project update so I'll look into fixing that soon.
  • Posts: 297

    @UberGoober @Steppers I encountered the same error while running WebRepo.

  • Posts: 412
    @binaryblues Try deleting the WebRepo project and redownloading from https://codeawebrepo.co.uk/webrepo_latest.zip

    I've been looking into how to handle this but it's a little finicky when deleting files from the current running project.
  • Posts: 412

    @binaryblues I’m hoping this should be fixed from now on as of the latest WebRepo version.

    If anyone is interested here’s a complete feature list as of Codea+ 1.4.0:
    - Threads
    - Debugger
    - Promises
    - Semaphores
    - Preprocessor
    - Async / Await (used in conjunction with Promises)
    - Documentation generator powered by annotated comments.

  • Posts: 297

    @Steppers Thanks! I deleted WebRepo from the Codea, downloaded the latest version, and now it works.

Sign In or Register to comment.