diff options
author | Jo-Philipp Wich <jo@mein.io> | 2021-09-25 19:34:14 +0200 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2021-10-11 09:39:12 +0200 |
commit | 4ee06d8138a107908a9fb45220fea32055b3c48a (patch) | |
tree | 2f0bd421931b2dd2daf504719beb63cc2885d23a /vm.c | |
parent | e43b751aab997c5e74a0712f7569d90bd3d6b429 (diff) |
syntax: introduce optional chaining operators
Introduce new operators `?.`, `?.[…]` and `?.(…)` to simplify looking up
deeply nested property chain in a secure manner.
The `?.` operator behaves like the `.` property access operator but yields
`null` if the left hand side is `null` or not an object.
Like `?.`, the `?.[…]` operator behaves like the `[…]` computed property
access but yields `null` if the left hand side is `null` or neither an
object or array.
Finally the `?.(…)` operator behaves like the function call operator `(…)`
but yields `null` if the left hand side is `null` or not a callable
function.
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'vm.c')
-rw-r--r-- | vm.c | 40 |
1 files changed, 36 insertions, 4 deletions
@@ -67,7 +67,9 @@ static const int8_t insn_operand_bytes[__I_MAX] = { [I_COPY] = 1, [I_CALL] = 4, - [I_MCALL] = 4 + [I_MCALL] = 4, + [I_QCALL] = 4, + [I_QMCALL] = 4, }; static const char *exception_type_strings[] = { @@ -1020,9 +1022,12 @@ uc_vm_insn_load_val(uc_vm_t *vm, uc_vm_insn_t insn) break; default: - uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, - "left-hand side expression is %s", - v ? "not an array or object" : "null"); + if (insn == I_QLVAL) + uc_vm_stack_push(vm, NULL); + else + uc_vm_raise_exception(vm, EXCEPTION_REFERENCE, + "left-hand side expression is %s", + v ? "not an array or object" : "null"); break; } @@ -1889,11 +1894,32 @@ uc_vm_insn_close_upval(uc_vm_t *vm, uc_vm_insn_t insn) } static void +uc_vm_skip_call(uc_vm_t *vm, bool mcall) +{ + uc_callframe_t *frame = uc_vm_current_frame(vm); + size_t i; + + /* pop all function arguments, the function itself and the associated + * function context off the stack */ + for (i = 0; i < 1 + mcall + (vm->arg.u32 & 0xffff); i++) + ucv_put(uc_vm_stack_pop(vm)); + + /* skip all encoded spread value indexes */ + for (i = 0; i < (vm->arg.u32 >> 16); i++) + frame->ip += 2; + + uc_vm_stack_push(vm, NULL); +} + +static void uc_vm_insn_call(uc_vm_t *vm, uc_vm_insn_t insn) { uc_value_t *fno = ucv_get(uc_vm_stack_peek(vm, vm->arg.u32 & 0xffff)); uc_value_t *ctx = NULL; + if (!ucv_is_callable(fno) && insn == I_QCALL) + return uc_vm_skip_call(vm, false); + if (!ucv_is_arrowfn(fno)) ctx = NULL; else if (vm->callframes.count > 0) @@ -1910,6 +1936,9 @@ uc_vm_insn_mcall(uc_vm_t *vm, uc_vm_insn_t insn) uc_value_t *key = vm->stack.entries[key_slot]; uc_value_t *fno = ucv_key_get(vm, ctx, key); + if (!ucv_is_callable(fno) && insn == I_QMCALL) + return uc_vm_skip_call(vm, true); + uc_vm_stack_set(vm, key_slot, fno); /* arrow functions as method calls inherit the parent ctx */ @@ -2075,6 +2104,7 @@ uc_vm_execute_chunk(uc_vm_t *vm) break; case I_LVAL: + case I_QLVAL: uc_vm_insn_load_val(vm, insn); break; @@ -2220,12 +2250,14 @@ uc_vm_execute_chunk(uc_vm_t *vm) break; case I_CALL: + case I_QCALL: uc_vm_insn_call(vm, insn); frame = uc_vm_current_frame(vm); chunk = frame->closure ? uc_vm_frame_chunk(frame) : NULL; break; case I_MCALL: + case I_QMCALL: uc_vm_insn_mcall(vm, insn); frame = uc_vm_current_frame(vm); chunk = frame->closure ? uc_vm_frame_chunk(frame) : NULL; |