fix increment operators on objects
This commit is contained in:
2
parse.cm
2
parse.cm
@@ -1644,6 +1644,8 @@ var parse = function(tokens, src, filename, tokenizer) {
|
||||
operand.level = -1
|
||||
}
|
||||
}
|
||||
} else if (operand != null) {
|
||||
sem_check_assign_target(scope, operand)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
BIN
parse.mach
BIN
parse.mach
Binary file not shown.
@@ -843,19 +843,83 @@ static int mach_compile_expr(MachCompState *cs, cJSON *node, int dest) {
|
||||
int slot = mach_find_var(cs, name);
|
||||
if (slot >= 0) {
|
||||
if (is_postfix) {
|
||||
/* Return old value, then increment */
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
|
||||
mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0));
|
||||
} else {
|
||||
/* Increment, then return new value */
|
||||
mach_emit(cs, MACH_ABC(inc_op, slot, slot, 0));
|
||||
if (dest != slot)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, slot, 0));
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
} else if (level > 0 && name) {
|
||||
/* Closure variable */
|
||||
int save = cs->freereg;
|
||||
MachCompState *target = cs;
|
||||
for (int i = 0; i < level; i++) target = target->parent;
|
||||
int slot = mach_find_var(target, name);
|
||||
int val_r = mach_reserve_reg(cs);
|
||||
mach_emit(cs, MACH_ABC(MACH_GETUP, val_r, level, slot));
|
||||
if (is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0));
|
||||
if (!is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(MACH_SETUP, val_r, level, slot));
|
||||
mach_free_reg_to(cs, save);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
/* Property access: obj.prop++ */
|
||||
if (op_kind && strcmp(op_kind, ".") == 0) {
|
||||
int save = cs->freereg;
|
||||
cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression");
|
||||
if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left");
|
||||
cJSON *prop = cJSON_GetObjectItemCaseSensitive(operand, "name");
|
||||
if (!prop) prop = cJSON_GetObjectItemCaseSensitive(operand, "right");
|
||||
const char *prop_name = NULL;
|
||||
if (cJSON_IsString(prop)) prop_name = cJSON_GetStringValue(prop);
|
||||
else if (prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "value"));
|
||||
if (!prop_name && prop) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(prop, "name"));
|
||||
if (!prop_name) prop_name = cJSON_GetStringValue(cJSON_GetObjectItemCaseSensitive(operand, "value"));
|
||||
if (prop_name) {
|
||||
int obj_r = mach_compile_expr(cs, obj_expr, -1);
|
||||
if (cs->freereg <= obj_r) cs->freereg = obj_r + 1;
|
||||
int ki = mach_cpool_add_str(cs, prop_name);
|
||||
int val_r = mach_reserve_reg(cs);
|
||||
mach_emit(cs, MACH_ABC(MACH_GETFIELD, val_r, obj_r, ki));
|
||||
if (is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0));
|
||||
if (!is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(MACH_SETFIELD, obj_r, ki, val_r));
|
||||
mach_free_reg_to(cs, save);
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
/* Computed property access: obj[idx]++ */
|
||||
if (op_kind && strcmp(op_kind, "[") == 0) {
|
||||
int save = cs->freereg;
|
||||
cJSON *obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "expression");
|
||||
if (!obj_expr) obj_expr = cJSON_GetObjectItemCaseSensitive(operand, "left");
|
||||
cJSON *idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "index");
|
||||
if (!idx_expr) idx_expr = cJSON_GetObjectItemCaseSensitive(operand, "right");
|
||||
int obj_r = mach_compile_expr(cs, obj_expr, -1);
|
||||
if (cs->freereg <= obj_r) cs->freereg = obj_r + 1;
|
||||
int idx_r = mach_compile_expr(cs, idx_expr, -1);
|
||||
if (cs->freereg <= idx_r) cs->freereg = idx_r + 1;
|
||||
int val_r = mach_reserve_reg(cs);
|
||||
mach_emit(cs, MACH_ABC(MACH_GETINDEX, val_r, obj_r, idx_r));
|
||||
if (is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(inc_op, val_r, val_r, 0));
|
||||
if (!is_postfix)
|
||||
mach_emit(cs, MACH_ABC(MACH_MOVE, dest, val_r, 0));
|
||||
mach_emit(cs, MACH_ABC(MACH_SETINDEX, obj_r, idx_r, val_r));
|
||||
mach_free_reg_to(cs, save);
|
||||
return dest;
|
||||
}
|
||||
/* Fallback: just compile operand */
|
||||
return mach_compile_expr(cs, operand, dest);
|
||||
}
|
||||
|
||||
135
vm_suite.ce
135
vm_suite.ce
@@ -1271,6 +1271,141 @@ run("prefix decrement on array element", function() {
|
||||
if (arr[0] != 9) fail("side effect")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// POSTFIX INCREMENT/DECREMENT ON PROPERTIES (Bug: never worked)
|
||||
// ============================================================================
|
||||
|
||||
run("postfix increment on property", function() {
|
||||
var obj = {x: 5}
|
||||
obj.x++
|
||||
assert_eq(obj.x, 6, "obj.x should be 6 after obj.x++")
|
||||
})
|
||||
|
||||
run("postfix decrement on property", function() {
|
||||
var obj = {x: 5}
|
||||
obj.x--
|
||||
assert_eq(obj.x, 4, "obj.x should be 4 after obj.x--")
|
||||
})
|
||||
|
||||
run("postfix increment on property return value", function() {
|
||||
var obj = {x: 5}
|
||||
var y = obj.x++
|
||||
assert_eq(y, 5, "return value should be old value")
|
||||
assert_eq(obj.x, 6, "property should be incremented")
|
||||
})
|
||||
|
||||
run("postfix decrement on property return value", function() {
|
||||
var obj = {x: 5}
|
||||
var y = obj.x--
|
||||
assert_eq(y, 5, "return value should be old value")
|
||||
assert_eq(obj.x, 4, "property should be decremented")
|
||||
})
|
||||
|
||||
run("postfix increment on array element", function() {
|
||||
var arr = [10, 20, 30]
|
||||
arr[1]++
|
||||
assert_eq(arr[1], 21, "arr[1] should be 21 after arr[1]++")
|
||||
})
|
||||
|
||||
run("postfix decrement on array element", function() {
|
||||
var arr = [10, 20, 30]
|
||||
arr[1]--
|
||||
assert_eq(arr[1], 19, "arr[1] should be 19 after arr[1]--")
|
||||
})
|
||||
|
||||
run("postfix increment on array element return value", function() {
|
||||
var arr = [10]
|
||||
var y = arr[0]++
|
||||
assert_eq(y, 10, "return value should be old value")
|
||||
assert_eq(arr[0], 11, "array element should be incremented")
|
||||
})
|
||||
|
||||
run("postfix increment on computed property", function() {
|
||||
var obj = {a: 10}
|
||||
var key = "a"
|
||||
obj[key]++
|
||||
assert_eq(obj.a, 11, "computed property should be incremented")
|
||||
})
|
||||
|
||||
run("postfix increment on nested property", function() {
|
||||
var obj = {inner: {val: 10}}
|
||||
obj.inner.val++
|
||||
assert_eq(obj.inner.val, 11, "nested property should be incremented")
|
||||
})
|
||||
|
||||
run("postfix increment property in loop", function() {
|
||||
var obj = {count: 0}
|
||||
var i = 0
|
||||
for (i = 0; i < 5; i++) {
|
||||
obj.count++
|
||||
}
|
||||
assert_eq(obj.count, 5, "property should be 5 after 5 increments")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// POSTFIX INCREMENT ON CLOSURE PROPERTIES (Original reported bug)
|
||||
// ============================================================================
|
||||
|
||||
run("postfix increment closure property", function() {
|
||||
var obj = {x: 0}
|
||||
var fn = function() {
|
||||
obj.x++
|
||||
}
|
||||
fn()
|
||||
assert_eq(obj.x, 1, "closure property should be incremented")
|
||||
})
|
||||
|
||||
run("postfix decrement closure property", function() {
|
||||
var obj = {x: 5}
|
||||
var fn = function() {
|
||||
obj.x--
|
||||
}
|
||||
fn()
|
||||
assert_eq(obj.x, 4, "closure property should be decremented")
|
||||
})
|
||||
|
||||
run("postfix increment closure counter pattern", function() {
|
||||
var counter = {passed: 0, failed: 0}
|
||||
var pass = function() { counter.passed++ }
|
||||
var fail_fn = function() { counter.failed++ }
|
||||
pass()
|
||||
pass()
|
||||
pass()
|
||||
fail_fn()
|
||||
assert_eq(counter.passed, 3, "passed count")
|
||||
assert_eq(counter.failed, 1, "failed count")
|
||||
})
|
||||
|
||||
run("postfix increment deep closure property", function() {
|
||||
var obj = {x: 0}
|
||||
var fn = function() {
|
||||
var inner = function() {
|
||||
obj.x++
|
||||
}
|
||||
inner()
|
||||
}
|
||||
fn()
|
||||
assert_eq(obj.x, 1, "deep closure property should be incremented")
|
||||
})
|
||||
|
||||
run("postfix increment closure array element", function() {
|
||||
var arr = [10]
|
||||
var fn = function() {
|
||||
arr[0]++
|
||||
}
|
||||
fn()
|
||||
assert_eq(arr[0], 11, "closure array element should be incremented")
|
||||
})
|
||||
|
||||
run("postfix increment on closure variable", function() {
|
||||
var x = 5
|
||||
var fn = function() {
|
||||
x++
|
||||
}
|
||||
fn()
|
||||
assert_eq(x, 6, "closure variable should be incremented by postfix ++")
|
||||
})
|
||||
|
||||
// ============================================================================
|
||||
// INCREMENT/DECREMENT IN LOOPS AND EXPRESSIONS
|
||||
// ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user