Age | Commit message (Collapse) | Author |
|
- Change command line flags to be align better with those of other
interpreters and with the gcc compiler, e.g. `-D` and `-U` to
define and undefine globals, `-e` to execute script expression etc.
- Pass only excess CLI arguments as `ARGV` to scripts, e.g.
`ucode -e 'print("Hello world")' -- -x -y` would pass only
`[ "-x", "-y" ]` as ARGV contents
- Default to raw mode and introduce flag to enable template mode
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
lib: fix potential integer underflow on empty render output
|
|
The current `uc_render()` implementation uses a `fseek()` call on the
`open_memstream()` provided `FILE *` stream to reserve headroom for the
`uc_string_t` header. The `fseek()` call alone does not guarantee that
the underlying buffer length is updated on all libc implementations though.
This may lead to an integer underflow later on when the `uc_string_t`
header length is substracted from the buffer length after invoking a
template that did not produce any output write operations. In such a
case, a very large value is assigned to `ustr->length` leading to
uninitialized or out-of-bounds memory accesses later on.
Solve this issue by writing the header structure as data using `fwrite()`
which should yield the expected behaviour on all libc environments.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
vm: fix crash on object literals with non-string computed properties
|
|
When executing an object literal declaration using non-string computed
property name values, the VM crashed caused by an attempt to use a NULL
pointer (result of ucv_string_get() on a non-string value) as hash table
key.
Fix this issue by using the `ucv_key_set()` infrastructure which deals
with the implicit stringification of non-string key values.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
ubus: add object publishing, notify and subscribe support
|
|
syntax: support add new operators
|
|
- Support ES2016 exponentiation (**) and exponentiation assignment (**=)
- Support ES2020 nullish coalescing (??) and logical nullish assignment (??=)
- Support ES2021 logical and assignment (&&=) and logical or assignment (||=)
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Extend the ubus binding to cover ubus event handling APIs.
Instantiating ubus event listener:
listener = conn.listener(
"event.type.*",
function (type, data) {
...event callback...
}
);
listener.remove();
Broadcasting events:
conn.event("event.type.foo", { ...event data... });
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
- Add more detailled error messages
- Introduce helpers for fetching and validating function call arguments
- Get rid of some uneeded blob->jso->ucv conversions
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Extend the ubus binding to cover ubus object publishing, notifications on
objects, as well as subscriber APIs.
Instantiating ubus objects:
obj = conn.publish("objname", {
methodname: {
args: { ...argspec... },
call: function(request) { ...method handler... }
},
...
}, function() { ...subscription status change handler... });
obj.notify(...);
obj.remove();
Emitting notifications:
obj.notify("notificationtype", { ...notification data... },
function(type, data) { ...data callback... },
function(idx, ret) { ...status callback... },
function() { ...completion callback... },
100 /* timeout */
);
Instantiating subscribers:
sub = conn.subscriber(
function(notify) { ...notification handler... },
function(id) { ...object gone handler... }
);
sub.subscribe("objname");
sub.unsubscribe("objname");
sub.remove();
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
In some cases, errno contains stale values from prior function invocations
which might lead to random failures in uc_uloop_run(), uc_uloop_timer_set()
and uc_uloop_timer().
Solve this issue by explicitly initializing errno to 0.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Mark reachable resource type prototype objects during incremental GC steps
in order to avoid freeing them prematurely.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
lib: introduce uloop binding
|
|
The uloop module allows controlling the uloop event loop and supports adding
timeouts, processes and file descriptors.
Example:
#!ucode -RS
let fs = require("fs");
let uloop = require("uloop");
let fd1 = fs.popen("echo 1; sleep 1; echo 2; sleep 1; echo 3", "r");
let fd2 = fs.popen("echo 4; sleep 1; echo 5; sleep 1; echo 6", "r");
function fd_read_callback(flags) {
if (flags & uloop.ULOOP_READ) {
let line = this.handle().read("line");
printf("Line from fd <%s/%d>: <%s>\n",
this, this.fileno(),
trim(line));
}
}
uloop.init();
uloop.timer(1500, function() {
printf("Timeout after 1500ms\n");
});
uloop.handle(fd1, fd_read_callback, uloop.ULOOP_READ);
uloop.handle(fd2, fd_read_callback, uloop.ULOOP_READ);
uloop.process("date", [ "+%s" ], { LC_ALL: "C" }, function(exitcode) {
printf("Date command exited with code %d\n", exitcode);
});
uloop.run();
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
When attempting to invoke a non-function value as method or when the the
internal recursion limit was exceeded, `uc_vm_call_function()` emitted
and internal runtime exception and freed the function value but not the
`this` context associated with the method call.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Fixes: 4ce69a8 ("fs: implement access(), mkstemp(), file.flush() and proc.flush()")
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The `access()` function allows testing the path given in the first argument
for accessibility according to the permissions specified in the second
mode string argument. The mode string characters may be `r`, `w`, `x` or
`f` which correspond to `R_OK` - path is readable, `W_OK` - path is
writable, `X_OK` - path is executable or `F_OK` - path exists respectively.
The `mkstemp()` function creates a secure temporary file, unlinks it and
returns the open file handle. The temporary path is constructed based on
the optional template argument. If the template argument contains a slash,
the path is taken as-is, if it contains no slashes, `/tmp/` is prepended.
If the template does not end with `XXXXXX`, a `.XXXXXX` suffix is appended
to the path. If the template is omitted, `/tmp/XXXXXX` is used.
The `file.flush()` and `proc.flush()` functions call `fflush()` on the
underlying file handle respectively. They take no arguments.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
When compiling a switch statement with duplicate `default` cases or a switch
statement with syntax errors before the body block, two error handling cases
were hit in the code that prematurely returned from the function without
resetting the compiler's patchlist pointer away from the on-stack patchlist
that had been set up for the switch statement.
Upon processing a subsequent break or continue control statement, a realloc
was performed on the then invalid patchlist contents, triggering a
segmentation fault or libc assert.
Solve this issue by not returning from the function but breaking the switch
body parsing loop.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The most common usecase is extracting the value of a single byte at a
specific offset, e.g. to scan a string char-by-char to construct a hash.
Furthermore, constructing an array which contains the results of multiple
`ord()` invocations is trivial while efficiently extracting a single byte
value without the overhead of an intermediate array is not.
Due to that, change `ord()` to always return a single integer byte value
at the offset specified as second argument or at offset 0 in case no
argument was supplied.
That means that `ord("Abc", 0, 1, 2)` will now return `65` instead of the
former `[ 65, 98, 99 ]` result.
Code relying on the former behaviour should either perform multiple calls
to `ord()`, passing different offsets each time or switch to the `struct`
module which allows efficient unpacking of string data.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Due to using signed byte values when writing/reading short strings
to/from pointer addresses, 8 bit characters where incorrectly clamped
to `-1` (`255`).
Fix this issue by treating the input string as `uint8_t` array.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
When patching jump targets for break statments while compiling for-loop
statments, we need jump beyond the instructions popping intermediate loop
variables off the stack but before the pop instructions removing local
loop body variables to prevent a stack position mismatch between compiler
and vm.
Before that change, local loop body variables remained on the stack,
breaking the expected stack layout.
Fixes: b3d758b compiler: ("fix for/break miscompilation")
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
treewide: rework function memory model
|
|
Instead of implementing a custom limited refcount logic, turn uc_source_t
instances into proper uc_value_t objects.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
- Instead of treating individual program functions as managed ucode types,
demote uc_function_t values to pointers into a uc_program_t entity
- Promote uc_program_t to a managed type
- Let uc_closure_t claim references to the owning program of the enclosed
uc_function_t
- Redefine public APIs uc_compile() and uc_vm_execute() APIs to return and
expect an uc_program_t object respectively
- Remove vallist indirection for function loading and let the compiler
emit the function id directly when producing function construction code
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The regular expression `/()/` will match the empty string, causing the
match loop to never advance. Add extra logic to deal with this case,
similar to the empty separator string logic.
Apply a similar exception to replacements of empty search strings, those
should yield the same result as empty regexp matches.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The regular expression `/()/` will match the empty string, causing the
match loop to never advance. Add extra logic to deal with this case.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The regular expression `/()/` will match the empty string, causing the
match loop to never advance. Add extra logic to deal with this case,
similar to the empty separator string logic.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Clang emits code which evaluates function call argument expressions in a
different order, causing `uc_dump_insn()` to receive the instruction pointer
address after decoding the instruction, not before.
Avoid that problem by explicitly caching the pre-decode address in a
temporary variable which is then passed to `uc_dump_insn()`.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The internal uc_vm_call_function() helper may fail in different ways before
the stack frame has been set up, e.g. if the provided function value was not
actually a callable function. In such cases an exception is raised but the
actual function value is leaked since there's not yet a stackframe referring
to it.
Solve the issue by freeing the function value explicitly in these exit cases.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Ensure that that the testcase files are executed within the temporary
testcase work directory to simplify testing relative path resolution.
Also fixup the duplicate resource regression test breaking due to that.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Bytecode fixes
|
|
Extend source objects with a `runpath` field which contains the original
path of the source being executed by the VM.
When instantiating source objects from file paths, the `runpath` will be
set to the `filename`. When instantiating source buffers using
`uc_source_new_buffer()`, the runpath is initially unset.
A new function `uc_source_runpath_set()` can be used to adjust the runtime
path being associated with a source object.
Extend bytecode loading logic to set the source buffer runtime path to the
precompiled bytecode file path being loaded and executed. This is required
for `sourcepath()` and relative paths in `include()` to function correctly
when executing precompiled programs.
Finally rename `uc_program_from_file()` and `uc_program_to_file()` to
`uc_program_load()` and `uc_program_write()` respectively since the load
part now operates on an `uc_source_t` input buffer instead of a plain
`FILE *` handle.
Adjust users of these API functions accordingly.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
We need to release the compiled module function after we executed it in
our VM context.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
A performance shortcut in `ucv_is_equal()` incorrectly led to `NaN === NaN`
being true. Fix the issue by only comparing pointers when the involved
types are not doubles.
Due to fixing `NaN !== NaN`, the `uniq()` function now requires a special
case to treat multiple NaNs equal for the sake of generating an array of
unique values.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Various fixes
|
|
Fixes: b605dbf ("treewide: rework numeric value handling")
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The current implementation incorrectly yielded `true` for `0 == null` but
only `null` must be equal to `null`.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The current implementation incorrectly returned `false` which got treated
as `NULL` instead of a boolean `false` value.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
- Rename 03_bugs to 04_bugs
- Rename 26_invalid_sparse_array_set to 27_invalid_sparse_array_set
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
The `touch` command result incorrectly shadowed the testcase exit code.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
|
|
|