Skip to content

call() scope does not override imported symbols #116

Description

@huafu

I've wrote a Typescript transformer for ucode as well as typings so that one can write ucode code with strict typings. I've also wrote a test library with spies and stubs so that ucode projects can have unit tests also written in Typescript. it's going to be on my public Github once ready (or at least when in a more advanced stage).
For the spies and stubs, I'm using the call function which is pretty handy. Tho there is one blocking issue:

// my_lib.uc
import { to_stub } from './other_lib.uc';

function fn_to_test() {
  // ...
  to_stub("hello");
  // ...
}

export { fn_to_test };
// my_lib.spec.uc
call(fn_to_test, { to_stub: () => null });

here I was expecting fn_to_test to use the dummy () => null when calling to_stub instead of the one imported from my_lib.uc.
I guess it's the normal behavior, tho it'd be nice if the scope given to call() could override imported symbols as well as globals.

Activity

  1. jow- commented on Oct 8, 2022

    @jow-
    Collaborator

    Imported symbols act like local variables, that's why you can't shadow them by overriding the scope (once the compiled bytecode runs, accessing imported symbols is not a property lookup in the global scope but a direct stack slot / upvalue access, basically the same as a local variable).

    Without introducing a set of debug/introspection APIs, the only solution I can think of is compiling and loading the code to test at runtime using loadfile() / loadstring() and modifying module_search_path to inject a path with mock libraries before the standard library search paths. This will then cause the dynamically loaded code to get compiled against a set of stub libraries instead of the actual ones.

    If that is not an option, then we will need a kind of debug library, comparable to the Lua one, which allows getting and setting the bound upvalues of a function in order to patch other values in there.

  2. huafu commented on Oct 8, 2022

    @huafu
    Author

    If that is too complex, then I guess I'll have to improve my Typescript transformer (to make it transparent) or my test lib (to make it work also without TS) so that it loads functions to test not directly but thru a helper. This would then use module_search_path of loadstring() as you suggested to override imported stuff.

  3. huafu commented on Oct 8, 2022

    @huafu
    Author

    Is it normal that I can't shadow defined constants as well?

    // lib.uc
    const to_stub = () => "original"
    function fn_to_test() {
      return to_stub();
    }
    export { fn_to_test };
    // lib.spec.uc
    import { fn_to_test } from './lib.uc';
    print(call(fn_to_test, { to_stub: () => "override" })); // prints "oiriginal"
  4. jow- commented on Oct 8, 2022

    @jow-
    Collaborator

    Yes, those are local variables too.

  5. huafu commented on Oct 8, 2022

    @huafu
    Author

    Imported symbols act like local variables

    Oh, I thought you meant local variables in the function scope, not in the global scope (ie the one holding the function). Now I understand 😄

  6. huafu commented on Nov 29, 2022

    @huafu
    Author

    @jow- as I mentioned before, I've just published the draft of my TypeScript to ucode compiler there - you get typings, classes and code completion for ucode thanks to typescript and some transformers. Plus the code is going thru eslint to ensure no ; or other stuffs of that kind are forgotten.
    A lot more work to be done there, but it does work and having some input on what could be improved will be very welcome ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions