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 private fields and methods to classes

+423 -144
+96
examples/spec/classes.js
··· 151 151 test('static property', Static.value, 42); 152 152 test('static method', Static.method(), 'static'); 153 153 154 + class BankAccount { 155 + #balance = 0; 156 + 157 + constructor(initialBalance) { 158 + this.#balance = initialBalance; 159 + } 160 + 161 + deposit(amount) { 162 + this.#balance = this.#balance + amount; 163 + return this.#balance; 164 + } 165 + 166 + withdraw(amount) { 167 + this.#balance = this.#balance - amount; 168 + return this.#balance; 169 + } 170 + 171 + getBalance() { 172 + return this.#balance; 173 + } 174 + } 175 + 176 + let account = new BankAccount(100); 177 + test('private field initializer', account.getBalance(), 100); 178 + test('private field deposit', account.deposit(50), 150); 179 + test('private field withdraw', account.withdraw(30), 120); 180 + 181 + class Vehicle { 182 + #speed = 0; 183 + 184 + constructor(initialSpeed) { 185 + this.#speed = initialSpeed; 186 + } 187 + 188 + getSpeed() { 189 + return this.#speed; 190 + } 191 + 192 + accelerate(amount) { 193 + this.#speed = this.#speed + amount; 194 + } 195 + } 196 + 197 + class Car extends Vehicle { 198 + #model; 199 + 200 + constructor(model, speed) { 201 + super(speed); 202 + this.#model = model; 203 + } 204 + 205 + getModel() { 206 + return this.#model; 207 + } 208 + } 209 + 210 + let car = new Car('Tesla', 0); 211 + test('inherited private field', car.getSpeed(), 0); 212 + car.accelerate(50); 213 + test('inherited method with private field', car.getSpeed(), 50); 214 + test('own private field', car.getModel(), 'Tesla'); 215 + 216 + class Accumulator { 217 + count = 0; 218 + 219 + increment() { 220 + this.count = this.count + 1; 221 + return this.count; 222 + } 223 + } 224 + 225 + let accumulator = new Accumulator(); 226 + test('field initializer', accumulator.count, 0); 227 + test('method with initialized field', accumulator.increment(), 1); 228 + 229 + class Temperature { 230 + #celsius = 0; 231 + 232 + constructor(celsius) { 233 + this.#celsius = celsius; 234 + } 235 + 236 + get fahrenheit() { 237 + return (this.#celsius * 9) / 5 + 32; 238 + } 239 + 240 + set fahrenheit(f) { 241 + this.#celsius = ((f - 32) * 5) / 9; 242 + } 243 + } 244 + 245 + let temp = new Temperature(0); 246 + test('getter', temp.fahrenheit, 32); 247 + temp.fahrenheit = 212; 248 + test('setter', Math.round(temp.fahrenheit), 212); 249 + 154 250 summary();
+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.19') 82 + version_conf.set('ANT_VERSION', '0.2.2.20') 83 83 version_conf.set('ANT_GIT_HASH', git_hash) 84 84 version_conf.set('ANT_BUILD_DATE', build_date) 85 85
+199 -143
src/ant.c
··· 424 424 TOK_COLON, TOK_Q, TOK_ASSIGN, TOK_PLUS_ASSIGN, TOK_MINUS_ASSIGN, 425 425 TOK_MUL_ASSIGN, TOK_DIV_ASSIGN, TOK_REM_ASSIGN, TOK_SHL_ASSIGN, 426 426 TOK_SHR_ASSIGN, TOK_ZSHR_ASSIGN, TOK_AND_ASSIGN, TOK_XOR_ASSIGN, 427 - TOK_OR_ASSIGN, TOK_COMMA, TOK_TEMPLATE, TOK_ARROW, 427 + TOK_OR_ASSIGN, TOK_COMMA, TOK_TEMPLATE, TOK_ARROW, TOK_HASH, 428 428 TOK_MAX 429 429 }; 430 430 ··· 3819 3819 case '<': if (LOOK(1, '<') && LOOK(2, '=')) TOK(TOK_SHL_ASSIGN, 3); if (LOOK(1, '<')) TOK(TOK_SHL, 2); if (LOOK(1, '=')) TOK(TOK_LE, 2); TOK(TOK_LT, 1); 3820 3820 case '>': if (LOOK(1, '>') && LOOK(2, '>') && LOOK(3, '=')) TOK(TOK_ZSHR_ASSIGN, 4); if (LOOK(1, '>') && LOOK(2, '>')) TOK(TOK_ZSHR, 3); if (LOOK(1, '>') && LOOK(2, '=')) TOK(TOK_SHR_ASSIGN, 3); if (LOOK(1, '>')) TOK(TOK_SHR, 2); if (LOOK(1, '=')) TOK(TOK_GE, 2); TOK(TOK_GT, 1); 3821 3821 case '^': if (LOOK(1, '=')) TOK(TOK_XOR_ASSIGN, 2); TOK(TOK_XOR, 1); 3822 + case '#': TOK(TOK_HASH, 1); 3822 3823 case '`': 3823 3824 js->tlen++; 3824 3825 while (js->toff + js->tlen < js->clen && buf[js->tlen] != '`') { ··· 4379 4380 } 4380 4381 4381 4382 static bool try_accessor_getter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t *out) { 4382 - jsoff_t obj_off = (jsoff_t)vdata(obj); 4383 - descriptor_entry_t *desc = lookup_descriptor(obj_off, key, key_len); 4384 - 4385 - if (!desc || !desc->has_getter) return false; 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 + } 4386 4418 4387 - jsval_t getter = desc->getter; 4388 - if (vtype(getter) != T_FUNC && vtype(getter) != T_CFUNC) return false; 4389 - 4390 - js_parse_state_t saved; 4391 - JS_SAVE_STATE(js, saved); 4392 - uint8_t saved_flags = js->flags; 4393 - jsoff_t saved_toff = js->toff; 4394 - jsoff_t saved_tlen = js->tlen; 4395 - 4396 - jsval_t saved_this = js->this_val; 4397 - js->this_val = obj; 4398 - push_this(obj); 4399 - *out = call_js_with_args(js, getter, NULL, 0); 4400 - 4401 - pop_this(); 4402 - js->this_val = saved_this; 4403 - 4404 - JS_RESTORE_STATE(js, saved); 4405 - js->flags = saved_flags; 4406 - js->toff = saved_toff; 4407 - js->tlen = saved_tlen; 4408 - 4409 - return true; 4419 + return false; 4410 4420 } 4411 4421 4412 4422 static jsval_t resolveprop(struct js *js, jsval_t v) { ··· 4453 4463 } 4454 4464 4455 4465 static bool try_accessor_setter(struct js *js, jsval_t obj, const char *key, size_t key_len, jsval_t val, jsval_t *out) { 4456 - jsoff_t obj_off = (jsoff_t)vdata(obj); 4457 - descriptor_entry_t *desc = lookup_descriptor(obj_off, key, key_len); 4458 - 4459 - if (!desc || !desc->has_setter) return false; 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 + } 4460 4501 4461 - jsval_t setter = desc->setter; 4462 - if (vtype(setter) != T_FUNC && vtype(setter) != T_CFUNC) return false; 4463 - 4464 - js_parse_state_t saved; 4465 - JS_SAVE_STATE(js, saved); 4466 - uint8_t saved_flags = js->flags; 4467 - jsoff_t saved_toff = js->toff; 4468 - jsoff_t saved_tlen = js->tlen; 4469 - 4470 - jsval_t saved_this = js->this_val; 4471 - js->this_val = obj; 4472 - push_this(obj); 4473 - jsval_t result = call_js_with_args(js, setter, &val, 1); 4474 - pop_this(); 4475 - js->this_val = saved_this; 4476 - 4477 - JS_RESTORE_STATE(js, saved); 4478 - js->flags = saved_flags; 4479 - js->toff = saved_toff; 4480 - js->tlen = saved_tlen; 4481 - 4482 - *out = is_err(result) ? result : val; 4483 - return true; 4502 + return false; 4484 4503 } 4485 4504 4486 4505 static jsval_t assign(struct js *js, jsval_t lhs, jsval_t val) { ··· 7817 7836 } 7818 7837 jsval_t prop_name; 7819 7838 uint8_t nxt = next(js); 7820 - if (nxt == TOK_IDENTIFIER || is_keyword_propname(nxt)) { 7839 + if (nxt == TOK_HASH) { 7840 + js->consumed = 1; 7841 + if (next(js) != TOK_IDENTIFIER) { 7842 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "private field name expected"); 7843 + } 7844 + js->consumed = 1; 7845 + prop_name = mkcoderef((jsoff_t) js->toff, (jsoff_t) js->tlen); 7846 + } else if (nxt == TOK_IDENTIFIER || is_keyword_propname(nxt)) { 7821 7847 js->consumed = 1; 7822 7848 prop_name = mkcoderef((jsoff_t) js->toff, (jsoff_t) js->tlen); 7823 7849 } else { ··· 9560 9586 mkprop(js, js->scope, var_key, loop_var_val, false); 9561 9587 } 9562 9588 9563 - js->pos = pos3, js->consumed = 1, js->flags |= F_LOOP; 9589 + js->flags |= F_LOOP; 9590 + js->pos = pos3; 9591 + js->consumed = 1; 9564 9592 v = js_block_or_stmt(js); 9565 9593 if (is_err2(&v, &res)) { 9566 9594 if (vtype(iter_scope) != T_UNDEF) delscope(js); ··· 10505 10533 bool is_async; 10506 10534 bool is_static; 10507 10535 bool is_field; 10536 + bool is_private; 10508 10537 bool is_getter; 10509 10538 bool is_setter; 10510 10539 jsoff_t field_start, field_end; 10540 + jsoff_t param_start; 10511 10541 } MethodInfo; 10512 10542 10513 - MethodInfo methods[32]; 10514 - int method_count = 0; 10543 + static const UT_icd method_info_icd = { 10544 + .sz = sizeof(MethodInfo), 10545 + .init = NULL, 10546 + .copy = NULL, 10547 + .dtor = NULL, 10548 + }; 10549 + 10550 + UT_array *methods = NULL; 10551 + utarray_new(methods, &method_info_icd); 10515 10552 10516 10553 uint8_t class_tok; 10517 - while ((class_tok = next(js)) != TOK_RBRACE && class_tok != TOK_EOF && method_count < 32) { 10554 + while ((class_tok = next(js)) != TOK_RBRACE && class_tok != TOK_EOF) { 10518 10555 bool is_async_method = false; 10519 10556 bool is_static_member = false; 10520 10557 bool is_getter_method = false; ··· 10529 10566 js->consumed = 1; 10530 10567 } 10531 10568 10569 + bool is_private_member = false; 10570 + if (next(js) == TOK_HASH) { 10571 + js->consumed = 1; 10572 + if (next(js) == TOK_IDENTIFIER) { is_private_member = true; } else { 10573 + js->flags = save_flags; 10574 + return js_mkerr_typed(js, JS_ERR_SYNTAX, "private field name expected"); 10575 + } 10576 + } 10577 + 10532 10578 if (next(js) == TOK_IDENTIFIER) { 10533 10579 bool is_get = (js->tlen == 3 && memcmp(&js->code[js->toff], "get", 3) == 0); 10534 10580 bool is_set = (js->tlen == 3 && memcmp(&js->code[js->toff], "set", 3) == 0); 10535 - if (is_get || is_set) { 10581 + if (!is_private_member && (is_get || is_set)) { 10536 10582 jsoff_t saved_pos = js->pos; 10537 10583 jsoff_t saved_toff = js->toff; 10538 10584 jsoff_t saved_tlen = js->tlen; ··· 10588 10634 jsoff_t field_end = js->pos; 10589 10635 if (next(js) == TOK_SEMICOLON) js->consumed = 1; 10590 10636 10591 - methods[method_count].name_off = method_name_off; 10592 - methods[method_count].name_len = method_name_len; 10593 - methods[method_count].is_static = is_static_member; 10594 - methods[method_count].is_async = false; 10595 - methods[method_count].is_field = true; 10596 - methods[method_count].field_start = field_start; 10597 - methods[method_count].field_end = field_end; 10598 - methods[method_count].fn_start = 0; 10599 - methods[method_count].fn_end = 0; 10600 - method_count++; 10637 + MethodInfo field_method = { 10638 + .name_off = method_name_off, 10639 + .name_len = method_name_len, 10640 + .is_static = is_static_member, 10641 + .is_async = false, 10642 + .is_field = true, 10643 + .is_private = is_private_member, 10644 + .field_start = field_start, 10645 + .field_end = field_end, 10646 + .fn_start = 0, 10647 + .fn_end = 0, 10648 + }; 10649 + utarray_push_back(methods, &field_method); 10601 10650 continue; 10602 10651 } 10603 10652 10604 10653 if (next(js) == TOK_SEMICOLON || (next(js) != TOK_LPAREN && next(js) == TOK_IDENTIFIER)) { 10605 10654 if (next(js) == TOK_SEMICOLON) js->consumed = 1; 10606 - methods[method_count].name_off = method_name_off; 10607 - methods[method_count].name_len = method_name_len; 10608 - methods[method_count].is_static = is_static_member; 10609 - methods[method_count].is_async = false; 10610 - methods[method_count].is_field = true; 10611 - methods[method_count].field_start = 0; 10612 - methods[method_count].field_end = 0; 10613 - methods[method_count].fn_start = 0; 10614 - methods[method_count].fn_end = 0; 10615 - method_count++; 10655 + MethodInfo bare_method = { 10656 + .name_off = method_name_off, 10657 + .name_len = method_name_len, 10658 + .is_static = is_static_member, 10659 + .is_async = false, 10660 + .is_field = true, 10661 + .is_private = is_private_member, 10662 + .field_start = 0, 10663 + .field_end = 0, 10664 + .fn_start = 0, 10665 + .fn_end = 0, 10666 + }; 10667 + utarray_push_back(methods, &bare_method); 10616 10668 continue; 10617 10669 } 10618 10670 ··· 10637 10689 constructor_body_start = method_body_start + 1; 10638 10690 constructor_body_end = method_body_end; 10639 10691 } else { 10640 - methods[method_count].name_off = method_name_off; 10641 - methods[method_count].name_len = method_name_len; 10642 - methods[method_count].fn_start = method_params_start; 10643 - methods[method_count].fn_end = method_body_end; 10644 - methods[method_count].is_async = is_async_method; 10645 - methods[method_count].is_static = is_static_member; 10646 - methods[method_count].is_field = false; 10647 - methods[method_count].is_getter = is_getter_method; 10648 - methods[method_count].is_setter = is_setter_method; 10649 - methods[method_count].field_start = 0; 10650 - methods[method_count].field_end = 0; 10651 - method_count++; 10652 - } 10692 + MethodInfo func_method = { 10693 + .name_off = method_name_off, 10694 + .name_len = method_name_len, 10695 + .fn_start = method_params_start, 10696 + .fn_end = method_body_end, 10697 + .param_start = method_params_start, 10698 + .is_async = is_async_method, 10699 + .is_static = is_static_member, 10700 + .is_field = false, 10701 + .is_private = is_private_member, 10702 + .is_getter = is_getter_method, 10703 + .is_setter = is_setter_method, 10704 + .field_start = 0, 10705 + .field_end = 0, 10706 + }; 10707 + utarray_push_back(methods, &func_method); 10708 + } 10653 10709 js->consumed = 1; 10654 10710 } 10655 10711 ··· 10691 10747 if (is_err(res_super)) return res_super; 10692 10748 } 10693 10749 10694 - for (int i = 0; i < method_count; i++) { 10695 - if (methods[i].is_static) continue; 10696 - if (methods[i].is_field) continue; 10750 + for (unsigned int i = 0; i < utarray_len(methods); i++) { 10751 + MethodInfo *m = (MethodInfo *)utarray_eltptr(methods, i); 10752 + if (m->is_static) continue; 10753 + if (m->is_field) continue; 10697 10754 10698 - jsval_t method_name = js_mkstr(js, &js->code[methods[i].name_off], methods[i].name_len); 10755 + jsval_t method_name = js_mkstr(js, &js->code[m->name_off], m->name_len); 10699 10756 if (is_err(method_name)) return method_name; 10700 10757 10701 - jsoff_t mlen = methods[i].fn_end - methods[i].fn_start; 10702 - jsval_t method_code = js_mkstr(js, &js->code[methods[i].fn_start], mlen); 10758 + jsoff_t mlen = m->fn_end - m->fn_start; 10759 + jsval_t method_code = js_mkstr(js, &js->code[m->fn_start], mlen); 10703 10760 if (is_err(method_code)) return method_code; 10704 10761 10705 10762 jsval_t method_obj = mkobj(js, 0); ··· 10710 10767 jsval_t mres = setprop(js, method_obj, mcode_key, method_code); 10711 10768 if (is_err(mres)) return mres; 10712 10769 10713 - if (methods[i].is_async) { 10770 + if (m->is_async) { 10714 10771 jsval_t async_key = js_mkstr(js, "__async", 7); 10715 10772 if (is_err(async_key)) return async_key; 10716 10773 jsval_t async_val = mkval(T_BOOL, 1); ··· 10725 10782 10726 10783 jsval_t method_func = mkval(T_FUNC, (unsigned long) vdata(method_obj)); 10727 10784 10728 - if (methods[i].is_getter || methods[i].is_setter) { 10785 + if (m->is_getter || m->is_setter) { 10729 10786 jsoff_t name_len; 10730 10787 const char *name_str = (const char *)&js->mem[vstr(js, method_name, &name_len)]; 10731 10788 10732 - if (methods[i].is_getter) { 10789 + if (m->is_getter) { 10733 10790 js_set_getter_desc(js, proto, name_str, name_len, method_func, JS_DESC_C); 10734 10791 } else { 10735 10792 js_set_setter_desc(js, proto, name_str, name_len, method_func, JS_DESC_C); ··· 10743 10800 jsval_t func_obj = mkobj(js, 0); 10744 10801 if (is_err(func_obj)) return func_obj; 10745 10802 10803 + jsval_t code_key = js_mkstr(js, "__code", 6); 10804 + if (is_err(code_key)) return code_key; 10805 + 10806 + jsval_t ctor_str; 10746 10807 if (constructor_params_start > 0 && constructor_body_start > 0) { 10747 10808 jsoff_t code_len = constructor_body_end - constructor_params_start; 10748 - 10749 - jsval_t code_key = js_mkstr(js, "__code", 6); 10750 - if (is_err(code_key)) return code_key; 10751 - jsval_t ctor_str = js_mkstr(js, &js->code[constructor_params_start], code_len); 10752 - if (is_err(ctor_str)) return ctor_str; 10753 - 10754 - jsval_t res2 = setprop(js, func_obj, code_key, ctor_str); 10755 - if (is_err(res2)) return res2; 10809 + ctor_str = js_mkstr(js, &js->code[constructor_params_start], code_len); 10756 10810 } else { 10757 - jsval_t code_key = js_mkstr(js, "__code", 6); 10758 - if (is_err(code_key)) return code_key; 10759 - jsval_t default_ctor; 10760 10811 if (super_len > 0) { 10761 - default_ctor = js_mkstr(js, "(...args){super(...args);}", 26); 10762 - } else { 10763 - default_ctor = js_mkstr(js, "(){}", 4); 10764 - } 10765 - if (is_err(default_ctor)) return default_ctor; 10766 - jsval_t res2 = setprop(js, func_obj, code_key, default_ctor); 10767 - if (is_err(res2)) return res2; 10812 + ctor_str = js_mkstr(js, "(...args){super(...args);}", 26); 10813 + } else { ctor_str = js_mkstr(js, "(){}", 4); } 10768 10814 } 10815 + if (is_err(ctor_str)) return ctor_str; 10769 10816 10770 - int field_count = 0; 10771 - for (int i = 0; i < method_count; i++) { 10772 - if (methods[i].is_static) continue; 10773 - if (methods[i].is_field) field_count++; 10817 + jsval_t res2 = setprop(js, func_obj, code_key, ctor_str); 10818 + if (is_err(res2)) return res2; 10819 + 10820 + int instance_field_count = 0; 10821 + for (unsigned int i = 0; i < utarray_len(methods); i++) { 10822 + MethodInfo *m = (MethodInfo *)utarray_eltptr(methods, i); 10823 + if (m->is_static) continue; 10824 + if (!m->is_field) continue; 10825 + instance_field_count++; 10774 10826 } 10775 10827 10776 - if (field_count > 0) { 10777 - size_t metadata_size = field_count * sizeof(jsoff_t) * 4; 10828 + if (instance_field_count > 0) { 10829 + size_t metadata_size = instance_field_count * sizeof(jsoff_t) * 4; 10778 10830 jsoff_t meta_len = (jsoff_t) (metadata_size + 1); 10779 10831 jsoff_t meta_header = (jsoff_t) ((meta_len << 2) | T_STR); 10780 10832 jsoff_t meta_off = js_alloc(js, meta_len + sizeof(meta_header)); ··· 10784 10836 jsoff_t *metadata = (jsoff_t *)(&js->mem[meta_off + sizeof(meta_header)]); 10785 10837 10786 10838 int meta_idx = 0; 10787 - for (int i = 0; i < method_count; i++) { 10788 - if (methods[i].is_static) continue; 10789 - if (!methods[i].is_field) continue; 10839 + for (unsigned int i = 0; i < utarray_len(methods); i++) { 10840 + MethodInfo *m = (MethodInfo *)utarray_eltptr(methods, i); 10841 + if (m->is_static) continue; 10842 + if (!m->is_field) continue; 10790 10843 10791 - metadata[meta_idx * 4 + 0] = methods[i].name_off; 10792 - metadata[meta_idx * 4 + 1] = methods[i].name_len; 10793 - metadata[meta_idx * 4 + 2] = methods[i].field_start; 10794 - metadata[meta_idx * 4 + 3] = methods[i].field_end; 10844 + metadata[meta_idx * 4 + 0] = m->name_off; 10845 + metadata[meta_idx * 4 + 1] = m->name_len; 10846 + metadata[meta_idx * 4 + 2] = m->field_start; 10847 + metadata[meta_idx * 4 + 3] = m->field_end; 10795 10848 meta_idx++; 10796 10849 } 10797 10850 ··· 10805 10858 10806 10859 jsval_t count_key = js_mkstr(js, "__field_count", 13); 10807 10860 if (is_err(count_key)) return count_key; 10808 - jsval_t res_count = setprop(js, func_obj, count_key, tov((double)field_count)); 10861 + jsval_t res_count = setprop(js, func_obj, count_key, tov((double)instance_field_count)); 10809 10862 if (is_err(res_count)) return res_count; 10810 10863 10811 10864 jsval_t src_key = js_mkstr(js, "__source", 8); ··· 10843 10896 if (is_err(x)) return x; 10844 10897 } 10845 10898 10846 - for (int i = 0; i < method_count; i++) { 10847 - if (!methods[i].is_static) continue; 10899 + for (unsigned int i = 0; i < utarray_len(methods); i++) { 10900 + MethodInfo *m = (MethodInfo *)utarray_eltptr(methods, i); 10901 + if (!m->is_static) continue; 10848 10902 10849 - jsval_t member_name = js_mkstr(js, &js->code[methods[i].name_off], methods[i].name_len); 10903 + jsval_t member_name = js_mkstr(js, &js->code[m->name_off], m->name_len); 10850 10904 if (is_err(member_name)) return member_name; 10851 10905 10852 - if (methods[i].is_field) { 10906 + if (m->is_field) { 10853 10907 jsval_t field_val = js_mkundef(); 10854 - if (methods[i].field_start > 0 && methods[i].field_end > methods[i].field_start) { 10855 - field_val = js_eval_slice(js, methods[i].field_start, methods[i].field_end - methods[i].field_start); 10908 + if (m->field_start > 0 && m->field_end > m->field_start) { 10909 + field_val = js_eval_slice(js, m->field_start, m->field_end - m->field_start); 10856 10910 field_val = resolveprop(js, field_val); 10857 10911 } 10858 10912 jsval_t set_res = setprop(js, func_obj, member_name, field_val); 10859 10913 if (is_err(set_res)) return set_res; 10860 10914 } else { 10861 - jsoff_t mlen = methods[i].fn_end - methods[i].fn_start; 10862 - jsval_t method_code = js_mkstr(js, &js->code[methods[i].fn_start], mlen); 10915 + jsoff_t mlen = m->fn_end - m->fn_start; 10916 + jsval_t method_code = js_mkstr(js, &js->code[m->fn_start], mlen); 10863 10917 if (is_err(method_code)) return method_code; 10864 10918 10865 10919 jsval_t method_obj = mkobj(js, 0); ··· 10882 10936 } 10883 10937 10884 10938 (void) class_body_start; 10939 + utarray_free(methods); 10885 10940 return constructor; 10886 10941 } 10887 10942 10888 10943 (void) class_body_start; 10944 + utarray_free(methods); 10889 10945 return js_mkundef(); 10890 10946 } 10891 10947
+127
tests/private_fields.js
··· 1 + // Test private fields in classes 2 + 3 + // Basic private field access 4 + class Rectangle { 5 + #width; 6 + #height; 7 + 8 + constructor(w, h) { 9 + this.#width = w; 10 + this.#height = h; 11 + } 12 + 13 + area() { 14 + return this.#width * this.#height; 15 + } 16 + } 17 + 18 + const rect = new Rectangle(5, 10); 19 + console.assert(rect.area() === 50, "Rectangle area should be 50"); 20 + 21 + // Private field with getter/setter 22 + class Person { 23 + #name; 24 + #age; 25 + 26 + constructor(name, age) { 27 + this.#name = name; 28 + this.#age = age; 29 + } 30 + 31 + getName() { 32 + return this.#name; 33 + } 34 + 35 + getAge() { 36 + return this.#age; 37 + } 38 + 39 + setAge(newAge) { 40 + this.#age = newAge; 41 + } 42 + 43 + describe() { 44 + return this.#name + " is " + this.#age; 45 + } 46 + } 47 + 48 + const person = new Person("Alice", 30); 49 + console.assert(person.getName() === "Alice", "getName should return Alice"); 50 + console.assert(person.getAge() === 30, "getAge should return 30"); 51 + console.assert(person.describe() === "Alice is 30", "describe should work"); 52 + person.setAge(31); 53 + console.assert(person.describe() === "Alice is 31", "setAge should work"); 54 + 55 + // Private fields with inheritance 56 + class Animal { 57 + #species; 58 + 59 + constructor(species) { 60 + this.#species = species; 61 + } 62 + 63 + getSpecies() { 64 + return this.#species; 65 + } 66 + } 67 + 68 + class Dog extends Animal { 69 + #name; 70 + 71 + constructor(name, species) { 72 + super(species); 73 + this.#name = name; 74 + } 75 + 76 + getName() { 77 + return this.#name; 78 + } 79 + } 80 + 81 + const dog = new Dog("Rex", "Canis familiaris"); 82 + console.assert(dog.getName() === "Rex", "Dog getName should return Rex"); 83 + console.assert(dog.getSpecies() === "Canis familiaris", "Dog getSpecies should work"); 84 + 85 + // Private field with initializer 86 + class Counter { 87 + #count = 0; 88 + 89 + constructor(start) { 90 + this.#count = start; 91 + } 92 + 93 + increment() { 94 + this.#count = this.#count + 1; 95 + return this.#count; 96 + } 97 + 98 + getCount() { 99 + return this.#count; 100 + } 101 + } 102 + 103 + const counter = new Counter(10); 104 + console.assert(counter.increment() === 11, "Counter increment should return 11"); 105 + console.assert(counter.increment() === 12, "Counter increment should return 12"); 106 + console.assert(counter.getCount() === 12, "Counter getCount should return 12"); 107 + 108 + // Performance test: tight loop with private fields and initializer 109 + class FastCounter { 110 + #n = 0; 111 + 112 + increment() { 113 + this.#n = this.#n + 1; 114 + } 115 + 116 + get() { 117 + return this.#n; 118 + } 119 + } 120 + 121 + const fast = new FastCounter(); 122 + for (let i = 0; i < 1000; i = i + 1) { 123 + fast.increment(); 124 + } 125 + console.assert(fast.get() === 1000, "FastCounter should reach 1000"); 126 + 127 + console.log("All private fields tests passed!");