summaryrefslogtreecommitdiffhomepage
path: root/vm.c
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2024-03-13 21:05:04 +0100
committerJo-Philipp Wich <jo@mein.io>2024-03-13 23:08:37 +0100
commitbe767ae197babd656d4f5d9c2d5013e39ddbe656 (patch)
tree37b790dae8f45a02da5b5cbe7266875e9bc1f540 /vm.c
parentba3855ae3775197f3594fc2615cac539075bd2fb (diff)
vm: rework `in` operator semantics
- Ensure that testing for array membership does strict equality tests - Ensure that `(NaN in [ NaN ]) == true` - Do not perform implicit value conversion when testing for object keys, to avoid nonsensical results such as `([] in { "[ ]": true }) == true` - Add test cases for the `in` operator Fixes: #193 Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'vm.c')
-rw-r--r--vm.c49
1 files changed, 34 insertions, 15 deletions
diff --git a/vm.c b/vm.c
index 4642380..bb6dc2f 100644
--- a/vm.c
+++ b/vm.c
@@ -993,6 +993,37 @@ uc_vm_raise_exception(uc_vm_t *vm, uc_exception_type_t type, const char *fmt, ..
vm->exception.stacktrace = uc_vm_get_error_context(vm);
}
+static bool
+uc_vm_test_strict_equality(uc_value_t *v1, uc_value_t *v2, bool nan_equal)
+{
+ uc_type_t t1 = ucv_type(v1);
+ uc_type_t t2 = ucv_type(v2);
+ double d1, d2;
+
+ if (t1 != t2)
+ return false;
+
+ switch (t1) {
+ case UC_DOUBLE:
+ d1 = ((uc_double_t *)v1)->dbl;
+ d2 = ((uc_double_t *)v2)->dbl;
+
+ if (isnan(d1) && isnan(d2))
+ return nan_equal;
+
+ return (d1 == d2);
+
+ case UC_NULL:
+ case UC_BOOLEAN:
+ case UC_INTEGER:
+ case UC_STRING:
+ return ucv_is_equal(v1, v2);
+
+ default:
+ return (v1 == v2);
+ }
+}
+
static void
uc_vm_insn_load(uc_vm_t *vm, uc_vm_insn_t insn)
@@ -2066,7 +2097,6 @@ uc_vm_insn_in(uc_vm_t *vm, uc_vm_insn_t insn)
uc_value_t *item;
size_t arrlen, arridx;
bool found = false;
- char *key;
switch (ucv_type(r2)) {
case UC_ARRAY:
@@ -2074,7 +2104,7 @@ uc_vm_insn_in(uc_vm_t *vm, uc_vm_insn_t insn)
arridx < arrlen; arridx++) {
item = ucv_array_get(r2, arridx);
- if (ucv_compare(I_EQ, r1, item, NULL)) {
+ if (uc_vm_test_strict_equality(r1, item, true)) {
found = true;
break;
}
@@ -2083,14 +2113,8 @@ uc_vm_insn_in(uc_vm_t *vm, uc_vm_insn_t insn)
break;
case UC_OBJECT:
- if (ucv_type(r1) == UC_STRING) {
+ if (ucv_type(r1) == UC_STRING)
ucv_object_get(r2, ucv_string_get(r1), &found);
- }
- else {
- key = ucv_to_string(vm, r1);
- ucv_object_get(r2, key, &found);
- free(key);
- }
break;
@@ -2109,12 +2133,7 @@ uc_vm_insn_equality(uc_vm_t *vm, uc_vm_insn_t insn)
{
uc_value_t *r2 = uc_vm_stack_pop(vm);
uc_value_t *r1 = uc_vm_stack_pop(vm);
- bool equal;
-
- if (ucv_is_scalar(r1) && ucv_is_scalar(r2))
- equal = ucv_is_equal(r1, r2);
- else
- equal = (r1 == r2);
+ bool equal = uc_vm_test_strict_equality(r1, r2, false);
ucv_put(r1);
ucv_put(r2);