Skip to content
Merged
4 changes: 2 additions & 2 deletions tests/custom/00_syntax/26_exponentiation
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ the power of the second operand.
4,
9223372036854775808,
-9223372036854775808,
-0.25,
0.25,
2.7556759606311
]
-- End --
Expand All @@ -36,7 +36,7 @@ power value in the rhs expression.
-- Expect stdout --
[
4,
-0.25,
0.25,
2.7556759606311
]
-- End --
Expand Down
17 changes: 17 additions & 0 deletions tests/custom/99_bugs/53_int64_min_division_sigfpe
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
Ensure that dividing INT64_MIN by -1 does not trap with SIGFPE due to
signed integer division overflow but yields the mathematically correct
unsigned result, consistent with multiplication and subtraction.

-- Testcase --
{%
let min = -9223372036854775807 - 1;

print(min / -1, "\n");
print(min % -1, "\n");
%}
-- End --

-- Expect stdout --
9223372036854775808
0
-- End --
22 changes: 22 additions & 0 deletions tests/custom/99_bugs/55_strict_undeclared_assignment
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
In strict mode, assigning to an undeclared variable raised the expected
reference error but still performed the assignment, so the variable came
into existence when the exception was caught. Ensure the assignment and
compound update are suppressed when the exception is raised.

-- Testcase --
{%
'use strict';

try { undeclared_a = 1; } catch (e) { print(e.type, ": ", e.message, "\n"); }
try { print(undeclared_a, "\n"); } catch (e) { print(e.type, ": ", e.message, "\n"); }
try { undeclared_b += 2; } catch (e) { print(e.type, ": ", e.message, "\n"); }
try { print(undeclared_b, "\n"); } catch (e) { print(e.type, ": ", e.message, "\n"); }
%}
-- End --

-- Expect stdout --
Reference error: access to undeclared variable undeclared_a
Reference error: access to undeclared variable undeclared_a
Reference error: access to undeclared variable undeclared_b
Reference error: access to undeclared variable undeclared_b
-- End --
31 changes: 31 additions & 0 deletions tests/custom/99_bugs/56_negative_base_exponentiation
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Integer exponentiation with a negative base value applied a negative
sign to the result unconditionally instead of respecting the parity of
the exponent, e.g. (-2) ** 2 yielded -4 and (-2) ** 0 yielded -1.

-- Testcase --
{%
printf("%.J\n", [
(-2) ** 2,
(-2) ** 3,
(-3) ** 4,
(-2) ** 0,
(-2) ** -2,
(-2) ** -3,
(-1) ** 1000000,
(-9223372036854775807 - 1) ** 1
]);
%}
-- End --

-- Expect stdout --
[
4,
-8,
81,
1,
0.25,
-0.125,
1,
-9223372036854775808
]
-- End --
87 changes: 71 additions & 16 deletions vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,6 @@ void uc_vm_free(uc_vm_t *vm)
vm->open_upvals = ref;
}

for (i = 0; i < vm->restypes.count; i++)
ucv_put(vm->restypes.entries[i]->proto);

uc_vm_reset_callframes(vm);
uc_vm_reset_stack(vm);
uc_vector_clear(&vm->stack);
Expand All @@ -286,8 +283,14 @@ void uc_vm_free(uc_vm_t *vm)

ucv_freeall(vm);

for (i = 0; i < vm->restypes.count; i++)
/* Type prototypes are not on the GC value list, so a ucv_put() during
* ucv_freeall()'s retain phase would only mark them UC_NULL and leak
* them. Release them here, after the final GC, so the last reference a
* GC value may hold (e.g. via proto()) is already gone. */
for (i = 0; i < vm->restypes.count; i++) {
ucv_put(vm->restypes.entries[i]->proto);
free(vm->restypes.entries[i]);
}

uc_vector_clear(&vm->restypes);

Expand Down Expand Up @@ -473,6 +476,7 @@ static uc_value_t *
uc_vm_resolve_upval(uc_vm_t *vm, uc_value_t *value)
{
uc_upvalref_t *ref;
uc_value_t *rv;

#ifdef __clang_analyzer__
/* Clang static analyzer does not understand that ucv_type(NULL) can't
Expand All @@ -485,9 +489,13 @@ uc_vm_resolve_upval(uc_vm_t *vm, uc_value_t *value)
ref = (uc_upvalref_t *)value;

if (ref->closed)
return ucv_get(ref->value);
rv = ucv_get(ref->value);
else
return ucv_get(vm->stack.entries[ref->slot]);
rv = ucv_get(vm->stack.entries[ref->slot]);

ucv_put(value);

return rv;
}

return value;
Expand Down Expand Up @@ -1013,6 +1021,9 @@ uc_vm_get_error_context(uc_vm_t *vm)
uc_stringbuf_t *buf;
uc_chunk_t *chunk;

if (vm->callframes.count == 0)
return NULL;

/* skip to first non-native function call frame */
for (i = vm->callframes.count; i > 1; i--)
if (vm->callframes.entries[i - 1].closure)
Expand Down Expand Up @@ -1384,6 +1395,10 @@ uc_vm_insn_store_var(uc_vm_t *vm, uc_vm_insn_t insn)
uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
"access to undeclared variable %s",
ucv_string_get(name));
ucv_put(name);
ucv_put(v);

return;
}

break;
Expand Down Expand Up @@ -1424,8 +1439,17 @@ uc_vm_insn_store_val(uc_vm_t *vm, uc_vm_insn_t insn)
switch (ucv_type(o)) {
case UC_OBJECT:
case UC_ARRAY:
if (assert_mutable_value(vm, o))
uc_vm_stack_push(vm, ucv_key_set(vm, o, k, v));
if (assert_mutable_value(vm, o)) {
uc_value_t *rv = ucv_key_set(vm, o, k, v);

/* on success rv is a reference to the stored value that gets
* pushed onto the stack; clear v so the cleanup below does not
* release the reference now owned by the stack */
if (rv)
v = NULL;

uc_vm_stack_push(vm, rv);
}

break;

Expand All @@ -1435,6 +1459,7 @@ uc_vm_insn_store_val(uc_vm_t *vm, uc_vm_insn_t insn)
ucv_typename(o));
}

ucv_put(v);
ucv_put(o);
ucv_put(k);
}
Expand Down Expand Up @@ -1766,6 +1791,11 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
if (n2 == 0) {
rv = ucv_double_new(INFINITY);
}
else if (n1 == INT64_MIN && n2 == -1) {
/* the mathematical result 2^63 only fits into uint64_t;
* the signed division would trap with SIGFPE */
rv = ucv_uint64_new((uint64_t)INT64_MAX + 1);
}
else if (n1 < 0 || n2 < 0) {
rv = ucv_int64_new(n1 / n2);
}
Expand All @@ -1782,6 +1812,10 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val
if (n2 == 0) {
rv = ucv_double_new(NAN);
}
else if (n1 == INT64_MIN && n2 == -1) {
/* the signed modulo would trap with SIGFPE */
rv = ucv_int64_new(0);
}
else if (n1 < 0 || n2 < 0) {
rv = ucv_int64_new(n1 % n2);
}
Expand All @@ -1796,12 +1830,15 @@ uc_vm_value_arith(uc_vm_t *vm, uc_vm_insn_t operation, uc_value_t *value, uc_val

case I_EXP:
if (n1 < 0 || n2 < 0) {
if (n1 < 0 && n2 < 0)
rv = ucv_double_new(-(1.0 / (double)upow64(abs64(n1), abs64(n2))));
else if (n2 < 0)
rv = ucv_double_new(1.0 / (double)upow64(abs64(n1), abs64(n2)));
/* the result is negative iff the base is negative and the
* exponent is odd */
if (n2 < 0)
rv = ucv_double_new(((n1 < 0 && (n2 & 1)) ? -1.0 : 1.0) /
(double)upow64(abs64(n1), abs64(n2)));
else if (n2 & 1)
rv = ucv_int64_new((int64_t)-upow64(abs64(n1), abs64(n2)));
else
rv = ucv_int64_new(-upow64(abs64(n1), abs64(n2)));
rv = ucv_uint64_new(upow64(abs64(n1), abs64(n2)));
}
else {
if (!u1) u1 = (uint64_t)n1;
Expand Down Expand Up @@ -1851,6 +1888,10 @@ uc_vm_insn_update_var(uc_vm_t *vm, uc_vm_insn_t insn)
uc_vm_raise_exception(vm, EXCEPTION_REFERENCE,
"access to undeclared variable %s",
ucv_string_get(name));
ucv_put(name);
ucv_put(inc);

return;
}

break;
Expand Down Expand Up @@ -1880,8 +1921,19 @@ uc_vm_insn_update_val(uc_vm_t *vm, uc_vm_insn_t insn)
case UC_OBJECT:
case UC_ARRAY:
if (assert_mutable_value(vm, v)) {
uc_value_t *nv, *rv;

val = ucv_key_get(vm, v, k);
uc_vm_stack_push(vm, ucv_key_set(vm, v, k, uc_vm_value_arith(vm, vm->arg.u8, val, inc)));
nv = uc_vm_value_arith(vm, vm->arg.u8, val, inc);
rv = ucv_key_set(vm, v, k, nv);

/* on success rv is a reference to the stored value that gets
* pushed onto the stack; on failure nv was not stored, so
* release it here */
if (!rv)
ucv_put(nv);

uc_vm_stack_push(vm, rv);
}

break;
Expand Down Expand Up @@ -2032,7 +2084,7 @@ uc_vm_insn_mobj(uc_vm_t *vm, uc_vm_insn_t insn)
ucv_put(src);
break;

case json_type_array:
case UC_ARRAY:
for (i = 0; i < ucv_array_length(src); i++) {
xasprintf(&s, "%zu", i);
ucv_object_add(dst, s, ucv_get(ucv_array_get(src, i)));
Expand Down Expand Up @@ -2590,7 +2642,7 @@ uc_vm_insn_import(uc_vm_t *vm, uc_vm_insn_t insn)
}

/* module export available, patch into upvalue */
else if (from <= prog->exports.count && prog->exports.entries[from]) {
else if (from < prog->exports.count && prog->exports.entries[from]) {
frame->closure->upvals[to] = prog->exports.entries[from];
ucv_get(&prog->exports.entries[from]->header);
}
Expand Down Expand Up @@ -2668,6 +2720,7 @@ uc_vm_insn_dynload(uc_vm_t *vm, uc_vm_insn_t insn)
ucv_string_get(name));

ucv_put(name);
ucv_put(modscope);

return;
}
Expand All @@ -2682,6 +2735,8 @@ uc_vm_insn_dynload(uc_vm_t *vm, uc_vm_insn_t insn)
to++;
}
}

ucv_put(modscope);
}

static void
Expand Down
Loading