MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

add getter and setter accessor support to property lookup

+239 -85
+1 -1
meson.build
··· 79 79 build_date = run_command('date', '+%Y-%m-%d', check: true).stdout().strip() 80 80 81 81 version_conf = configuration_data() 82 - version_conf.set('ANT_VERSION', '0.2.2.21') 82 + version_conf.set('ANT_VERSION', '0.2.2.22') 83 83 version_conf.set('ANT_GIT_HASH', git_hash) 84 84 version_conf.set('ANT_BUILD_DATE', build_date) 85 85
+128 -84
src/ant.c
··· 4169 4169 return lkp_inline(js, obj, buf, len); 4170 4170 } 4171 4171 4172 + static jsoff_t lkp_with_getter(struct js *js, jsval_t obj, const char *buf, size_t len, jsval_t *getter_out, bool *has_getter_out) { 4173 + *has_getter_out = false; 4174 + *getter_out = js_mkundef(); 4175 + 4176 + jsval_t current = obj; 4177 + while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) { 4178 + jsoff_t current_off = (jsoff_t)vdata(current); 4179 + descriptor_entry_t *desc = lookup_descriptor(current_off, buf, len); 4180 + 4181 + if (desc && desc->has_getter) { 4182 + *getter_out = desc->getter; 4183 + *has_getter_out = true; 4184 + return current_off; 4185 + } 4186 + 4187 + jsoff_t prop_off = lkp_interned(js, current, intern_string(buf, len), len); 4188 + if (prop_off != 0) return prop_off; 4189 + 4190 + jsval_t proto = get_proto(js, current); 4191 + if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; 4192 + current = proto; 4193 + } 4194 + 4195 + return 0; 4196 + } 4197 + 4198 + static jsoff_t lkp_with_setter(struct js *js, jsval_t obj, const char *buf, size_t len, jsval_t *setter_out, bool *has_setter_out) { 4199 + *has_setter_out = false; 4200 + *setter_out = js_mkundef(); 4201 + 4202 + jsval_t current = obj; 4203 + while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) { 4204 + jsoff_t current_off = (jsoff_t)vdata(current); 4205 + descriptor_entry_t *desc = lookup_descriptor(current_off, buf, len); 4206 + 4207 + if (desc && desc->has_setter) { 4208 + *setter_out = desc->setter; 4209 + *has_setter_out = true; 4210 + return current_off; 4211 + } 4212 + 4213 + jsoff_t prop_off = lkp_interned(js, current, intern_string(buf, len), len); 4214 + if (prop_off != 0) return prop_off; 4215 + 4216 + jsval_t proto = get_proto(js, current); 4217 + if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; 4218 + current = proto; 4219 + } 4220 + 4221 + return 0; 4222 + } 4223 + 4172 4224 jsval_t js_get_proto(struct js *js, jsval_t obj) { 4173 4225 uint8_t t = vtype(obj); 4174 4226 ··· 4380 4432 } 4381 4433 4382 4434 static bool try_accessor_getter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t *out) { 4383 - jsval_t current = obj; 4384 - while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) { 4385 - jsoff_t current_off = (jsoff_t)vdata(current); 4386 - descriptor_entry_t *desc = lookup_descriptor(current_off, key, key_len); 4387 - 4388 - if (desc && desc->has_getter) { 4389 - jsval_t getter = desc->getter; 4390 - if (vtype(getter) != T_FUNC && vtype(getter) != T_CFUNC) return false; 4391 - 4392 - js_parse_state_t saved; 4393 - JS_SAVE_STATE(js, saved); 4394 - uint8_t saved_flags = js->flags; 4395 - jsoff_t saved_toff = js->toff; 4396 - jsoff_t saved_tlen = js->tlen; 4397 - 4398 - jsval_t saved_this = js->this_val; 4399 - js->this_val = obj; 4400 - push_this(obj); 4401 - *out = call_js_with_args(js, getter, NULL, 0); 4402 - 4403 - pop_this(); 4404 - js->this_val = saved_this; 4405 - 4406 - JS_RESTORE_STATE(js, saved); 4407 - js->flags = saved_flags; 4408 - js->toff = saved_toff; 4409 - js->tlen = saved_tlen; 4410 - 4411 - return true; 4412 - } 4413 - 4414 - jsval_t proto = get_proto(js, current); 4415 - if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; 4416 - current = proto; 4417 - } 4435 + jsval_t getter = js_mkundef(); 4436 + bool has_getter = false; 4437 + lkp_with_getter(js, obj, key, key_len, &getter, &has_getter); 4438 + 4439 + if (!has_getter) return false; 4440 + if (vtype(getter) != T_FUNC && vtype(getter) != T_CFUNC) return false; 4441 + 4442 + js_parse_state_t saved; 4443 + JS_SAVE_STATE(js, saved); 4444 + uint8_t saved_flags = js->flags; 4445 + jsoff_t saved_toff = js->toff; 4446 + jsoff_t saved_tlen = js->tlen; 4447 + 4448 + jsval_t saved_this = js->this_val; 4449 + js->this_val = obj; 4450 + push_this(obj); 4451 + *out = call_js_with_args(js, getter, NULL, 0); 4418 4452 4419 - return false; 4453 + pop_this(); 4454 + js->this_val = saved_this; 4455 + 4456 + JS_RESTORE_STATE(js, saved); 4457 + js->flags = saved_flags; 4458 + js->toff = saved_toff; 4459 + js->tlen = saved_tlen; 4460 + 4461 + return true; 4420 4462 } 4421 4463 4422 4464 static jsval_t resolveprop(struct js *js, jsval_t v) { ··· 4463 4505 } 4464 4506 4465 4507 static bool try_accessor_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t val, jsval_t *out) { 4466 - jsval_t current = obj; 4467 - while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) { 4468 - jsoff_t current_off = (jsoff_t)vdata(current); 4469 - descriptor_entry_t *desc = lookup_descriptor(current_off, key, key_len); 4470 - 4471 - if (desc && desc->has_setter) { 4472 - jsval_t setter = desc->setter; 4473 - if (vtype(setter) != T_FUNC && vtype(setter) != T_CFUNC) return false; 4474 - 4475 - js_parse_state_t saved; 4476 - JS_SAVE_STATE(js, saved); 4477 - uint8_t saved_flags = js->flags; 4478 - jsoff_t saved_toff = js->toff; 4479 - jsoff_t saved_tlen = js->tlen; 4480 - 4481 - jsval_t saved_this = js->this_val; 4482 - js->this_val = obj; 4483 - push_this(obj); 4484 - jsval_t result = call_js_with_args(js, setter, &val, 1); 4485 - pop_this(); 4486 - js->this_val = saved_this; 4487 - 4488 - JS_RESTORE_STATE(js, saved); 4489 - js->flags = saved_flags; 4490 - js->toff = saved_toff; 4491 - js->tlen = saved_tlen; 4492 - 4493 - *out = is_err(result) ? result : val; 4494 - return true; 4495 - } 4496 - 4497 - jsval_t proto = get_proto(js, current); 4498 - if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; 4499 - current = proto; 4500 - } 4508 + jsval_t setter = js_mkundef(); 4509 + bool has_setter = false; 4510 + lkp_with_setter(js, obj, key, key_len, &setter, &has_setter); 4511 + 4512 + if (!has_setter) return false; 4513 + if (vtype(setter) != T_FUNC && vtype(setter) != T_CFUNC) return false; 4514 + 4515 + js_parse_state_t saved; 4516 + JS_SAVE_STATE(js, saved); 4517 + uint8_t saved_flags = js->flags; 4518 + jsoff_t saved_toff = js->toff; 4519 + jsoff_t saved_tlen = js->tlen; 4520 + 4521 + jsval_t saved_this = js->this_val; 4522 + js->this_val = obj; 4523 + push_this(obj); 4524 + jsval_t result = call_js_with_args(js, setter, &val, 1); 4525 + pop_this(); 4526 + js->this_val = saved_this; 4527 + 4528 + JS_RESTORE_STATE(js, saved); 4529 + js->flags = saved_flags; 4530 + js->toff = saved_toff; 4531 + js->tlen = saved_tlen; 4501 4532 4502 - return false; 4533 + *out = is_err(result) ? result : val; 4534 + return true; 4503 4535 } 4504 4536 4505 4537 static jsval_t assign(struct js *js, jsval_t lhs, jsval_t val) { ··· 4508 4540 jsoff_t key_off = propref_key(lhs); 4509 4541 jsval_t obj = mkval(is_arr_off(js, obj_off) ? T_ARR : T_OBJ, obj_off); 4510 4542 jsval_t key = mkval(T_STR, key_off); 4543 + 4544 + jsoff_t key_len; 4545 + const char *key_str = (const char *)&js->mem[vstr(js, key, &key_len)]; 4546 + 4547 + jsval_t setter_result; 4548 + if (try_accessor_setter(js, obj, key_str, key_len, val, &setter_result)) { 4549 + return setter_result; 4550 + } 4551 + 4511 4552 return setprop(js, obj, key, val); 4512 4553 } 4513 4554 ··· 4700 4741 return js_mkerr_typed(js, JS_ERR_TYPE, "'%.*s' not allowed on strict arguments", (int)keylen, keystr); 4701 4742 } 4702 4743 4703 - jsoff_t own_off = lkp(js, obj, keystr, keylen); 4704 - if (own_off != 0) { 4705 - jsoff_t obj_off = (jsoff_t)vdata(obj); 4706 - descriptor_entry_t *desc = lookup_descriptor(obj_off, keystr, keylen); 4707 - if (desc && (desc->has_getter || desc->has_setter)) { 4708 - jsval_t key = js_mkstr(js, keystr, keylen); 4709 - return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 4710 - } 4711 - return mkval(T_PROP, own_off); 4744 + jsval_t getter = js_mkundef(); 4745 + bool has_getter = false; 4746 + jsoff_t prop_off = lkp_with_getter(js, obj, keystr, keylen, &getter, &has_getter); 4747 + 4748 + jsval_t setter = js_mkundef(); 4749 + bool has_setter = false; 4750 + if (!has_getter) { 4751 + lkp_with_setter(js, obj, keystr, keylen, &setter, &has_setter); 4752 + } 4753 + 4754 + if (has_getter || has_setter) { 4755 + jsval_t key = js_mkstr(js, keystr, keylen); 4756 + return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 4712 4757 } 4713 4758 4714 - jsval_t accessor_result; 4715 - if (try_accessor_getter(js, obj, keystr, keylen, &accessor_result)) { 4716 - return accessor_result; 4759 + if (prop_off != 0) { 4760 + return mkval(T_PROP, prop_off); 4717 4761 } 4718 4762 4719 4763 jsval_t dyn_result = try_dynamic_getter(js, obj, keystr, keylen);
+14
tests/test_bracket_getter.js
··· 1 + class Test { 2 + constructor() { 3 + this._value = "initial"; 4 + } 5 + 6 + get value() { 7 + console.log("getter called"); 8 + return this._value; 9 + } 10 + } 11 + 12 + const t = new Test(); 13 + console.log("1. Get via dot:", t.value); 14 + console.log("2. Get via bracket:", t["value"]);
+23
tests/test_bracket_setter.js
··· 1 + class Test { 2 + constructor() { 3 + this._value = "initial"; 4 + } 5 + 6 + get value() { 7 + return this._value; 8 + } 9 + 10 + set value(v) { 11 + console.log("setter called with:", v); 12 + this._value = v; 13 + } 14 + } 15 + 16 + const t = new Test(); 17 + console.log("1. Get via dot:", t.value); 18 + t.value = "changed via dot"; 19 + console.log("2. After dot set:", t.value); 20 + 21 + console.log("3. Get via bracket:", t["value"]); 22 + t["value"] = "changed via bracket"; 23 + console.log("4. After bracket set:", t["value"]);
+56
tests/test_getters_setters.js
··· 1 + class Person { 2 + constructor(name) { 3 + this._name = name; 4 + } 5 + 6 + get name() { 7 + console.log("Getting name"); 8 + return this._name; 9 + } 10 + 11 + set name(value) { 12 + console.log("Setting name to:", value); 13 + this._name = value; 14 + } 15 + 16 + get greeting() { 17 + return "Hello, " + this._name; 18 + } 19 + } 20 + 21 + const person = new Person("Alice"); 22 + console.log("1. Initial name:", person.name); 23 + console.log("2. Greeting:", person.greeting); 24 + 25 + person.name = "Bob"; 26 + console.log("3. After setting name:", person.name); 27 + console.log("4. New greeting:", person.greeting); 28 + 29 + // Test with multiple getters 30 + class Rectangle { 31 + constructor(width, height) { 32 + this._width = width; 33 + this._height = height; 34 + } 35 + 36 + get area() { 37 + return this._width * this._height; 38 + } 39 + 40 + get perimeter() { 41 + return 2 * (this._width + this._height); 42 + } 43 + 44 + set width(w) { 45 + console.log("Setting width to:", w); 46 + this._width = w; 47 + } 48 + 49 + get width() { 50 + return this._width; 51 + } 52 + } 53 + 54 + const rect = new Rectangle(5, 10); 55 + console.log("5. Rectangle area:", rect.area); 56 + console.log("6. Rectangle perimeter:", rect.perimeter);
+17
tests/test_setter_direct.js
··· 1 + const obj = {}; 2 + 3 + Object.defineProperty(obj, 'value', { 4 + get() { 5 + console.log("getter called"); 6 + return this._value; 7 + }, 8 + set(v) { 9 + console.log("setter called with:", v); 10 + this._value = v; 11 + } 12 + }); 13 + 14 + obj._value = "initial"; 15 + console.log("1. Get:", obj.value); 16 + obj.value = "changed"; 17 + console.log("2. Get after set:", obj.value);