From 131d99c45e586e06d2fa3adba32e92c0370ad022 Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Tue, 23 Aug 2022 15:43:25 +0200 Subject: lib: introduce three new functions call(), loadstring() and loadfile() Introduce new functions dealing with on-the-fly compilation of code and execution of functions with different global scope. The `loadstring()` and `loadfile()` functions will compile the given ucode source string or ucode file path respectively and return the entry function of the resulting program. An optional dictionary specifying parse options may be given as second argument. Both functions return `null` on invalid arguments and throw an exception in case of compilation errors. The `call()` function allows invoking a given function value with a different `this` context and/or a different global environment. Finally refactor the existing `uc_require_ucode()` implementation to reuse the new `uc_loadfile()` and `uc_call()` implementations and adjust as well as simplify affected testcases. Signed-off-by: Jo-Philipp Wich --- tests/custom/03_stdlib/63_call | 102 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 tests/custom/03_stdlib/63_call (limited to 'tests/custom/03_stdlib/63_call') diff --git a/tests/custom/03_stdlib/63_call b/tests/custom/03_stdlib/63_call new file mode 100644 index 0000000..41064eb --- /dev/null +++ b/tests/custom/03_stdlib/63_call @@ -0,0 +1,102 @@ +The `call()` function allows invoking functions with a modified `this` context +and global environment. It's main use case is binding global variables for +dynamiclly loaded code at runtime. + +Returns `null` if the given function value is not callable. +Returns the value returned by the invoked function in all other cases. + + +Test modifying `this` context + +-- Testcase -- +{% + let o1 = { + name: "Object #1", + func: function() { + print(`This is ${this.name}\n`); + } + }; + + let o2 = { + name: "Object #2" + }; + + o1.func(); + call(o1.func, o2); +%} +-- End -- + +-- Expect stdout -- +This is Object #1 +This is Object #2 +-- End -- + + +Test modifying environment + +-- Testcase -- +{% + function fn() { + print("Hello world\n"); + } + + fn(); + call(fn, null, { print: (s) => printf("Overridden print(): %s", s) }); +%} +-- End -- + +-- Expect stdout -- +Hello world +Overridden print(): Hello world +-- End -- + + +Test isolating environment + +-- Testcase -- +{% + function fn() { + print("Hello world\n"); + } + + fn(); + call(fn, null, proto({}, {})); // should fail due to unavailable print +%} +-- End -- + +-- Expect stdout -- +Hello world +-- End -- + +-- Expect stderr -- +Type error: left-hand side is not a function +In fn(), line 3, byte 24: + called from function call ([C]) + called from anonymous function ([stdin]:7:30) + + ` print("Hello world\n");` + Near here -------------------^ + + +-- End -- + + +Test passing through arguments + +-- Testcase -- +{% + function fn(a, b) { + printf("The product of %d * %d is %d\n", a, b, a * b); + } + + fn(3, 4); + call(fn, null, null, 5, 6); + call((...args) => printf("Args: %J\n", args), null, null, 1, 2, 3, 4, 5, 6); +%} +-- End -- + +-- Expect stdout -- +The product of 3 * 4 is 12 +The product of 5 * 6 is 30 +Args: [ 1, 2, 3, 4, 5, 6 ] +-- End -- -- cgit v1.2.3