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.

optimize arena memory shrink logic for better performance

+86 -74
+7
examples/test262/index.js
··· 214 214 lines.push(''); 215 215 } 216 216 217 + if (mem.intern) { 218 + lines.push(`${c.cyan}Intern Table${c.reset}`); 219 + lines.push(` Strings: ${c.bold}${mem.intern.count}${c.reset}`); 220 + lines.push(` Bytes: ${c.bold}${fmt(mem.intern.bytes)}${c.reset}`); 221 + lines.push(''); 222 + } 223 + 217 224 lines.push(`${c.cyan}Process${c.reset}`); 218 225 lines.push(` RSS: ${c.bold}${fmt(mem.rss)}${c.reset}`); 219 226 if (mem.virtualSize) {
+4
include/arena.h
··· 104 104 size_t decommit_size = old_pages - new_pages; 105 105 106 106 if (mprotect(decommit_start, decommit_size, PROT_NONE) == 0) return 0; 107 + #ifdef __APPLE__ 108 + madvise(decommit_start, decommit_size, MADV_FREE); 109 + #else 107 110 madvise(decommit_start, decommit_size, MADV_DONTNEED); 111 + #endif 108 112 return 0; 109 113 } 110 114
+8
include/internal.h
··· 71 71 int for_let_stack_len; 72 72 int for_let_stack_cap; 73 73 74 + jsval_t length_str; 74 75 jsval_t *gc_roots; 75 76 jshdl_t gc_roots_len; 76 77 jshdl_t gc_roots_cap; ··· 88 89 size_t len; 89 90 bool needs_free; 90 91 } js_cstr_t; 92 + 93 + typedef struct { 94 + size_t count; 95 + size_t bytes; 96 + } js_intern_stats_t; 91 97 92 98 enum { 93 99 T_OBJ, T_PROP, T_STR, T_UNDEF, T_NULL, T_NUM, T_BOOL, T_FUNC, ··· 130 136 131 137 jsval_t coerce_to_str(struct js *js, jsval_t v); 132 138 jsval_t coerce_to_str_concat(struct js *js, jsval_t v); 139 + 140 + js_intern_stats_t js_intern_stats(void); 133 141 js_cstr_t js_to_cstr(struct js *js, jsval_t value, char *stack_buf, size_t stack_size); 134 142 135 143 jsoff_t lkp(struct js *js, jsval_t obj, const char *buf, size_t len);
+54 -70
src/ant.c
··· 168 168 struct interned_string *next; 169 169 } interned_string_t; 170 170 171 + static size_t intern_count = 0; 172 + static size_t intern_bytes = 0; 171 173 static interned_string_t *intern_buckets[ANT_LIMIT_SIZE_CACHE]; 172 174 173 175 typedef struct { ··· 1965 1967 if (e->hash == h && e->len == len && memcmp(e->str, str, len) == 0) return e->str; 1966 1968 } 1967 1969 1968 - interned_string_t *entry = (interned_string_t *)ant_calloc(sizeof(interned_string_t) + len + 1); 1970 + size_t alloc_size = sizeof(interned_string_t) + len + 1; 1971 + interned_string_t *entry = (interned_string_t *)ant_calloc(alloc_size); 1969 1972 if (!entry) return NULL; 1970 1973 1971 1974 entry->str = (char *)(entry + 1); ··· 1976 1979 entry->next = intern_buckets[bucket]; 1977 1980 intern_buckets[bucket] = entry; 1978 1981 1982 + intern_count++; 1983 + intern_bytes += alloc_size; 1984 + 1979 1985 return entry->str; 1986 + } 1987 + 1988 + js_intern_stats_t js_intern_stats(void) { 1989 + return (js_intern_stats_t){ 1990 + .count = intern_count, 1991 + .bytes = intern_bytes 1992 + }; 1980 1993 } 1981 1994 1982 1995 bool is_internal_prop(const char *key, jsoff_t klen) { ··· 3607 3620 } 3608 3621 if (update_idx < cur_len) goto done_update; 3609 3622 3610 - jsval_t len_key = js_mkstr(js, "length", 6); 3611 3623 jsval_t new_len = tov((double)(update_idx + 1)); 3612 3624 if (len_off != 0) saveval(js, len_off + sizeof(jsoff_t) * 2, new_len); else { 3613 - mkprop(js, obj, len_key, new_len, 0); 3625 + mkprop(js, obj, js->length_str, new_len, 0); 3614 3626 } 3615 3627 3616 3628 done_update: ··· 3653 3665 jsval_t result = mkprop(js, obj, k, v, 0); 3654 3666 if (need_length_update) { 3655 3667 jsoff_t inner_len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); 3656 - jsval_t inner_len_key = js_mkstr(js, "length", 6); 3657 3668 jsval_t inner_new_len = tov((double)(idx + 1)); 3658 3669 if (inner_len_off != 0) saveval(js, inner_len_off + sizeof(jsoff_t) * 2, inner_new_len); else { 3659 - mkprop(js, obj, inner_len_key, inner_new_len, 0); 3670 + mkprop(js, obj, js->length_str, inner_new_len, 0); 3660 3671 } 3661 3672 } 3662 3673 ··· 6119 6130 const char *str_data = (const char *)&js->mem[str_off]; 6120 6131 return tov(D(utf16_strlen(str_data, byte_len))); 6121 6132 } 6122 - if (vtype(obj) == T_ARR) { 6123 - jsval_t key = js_mkstr(js, "length", 6); 6124 - return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(key)); 6125 - } 6133 + if (vtype(obj) == T_ARR) return mkpropref((jsoff_t)vdata(obj), (jsoff_t)vdata(js->length_str)); 6126 6134 } 6127 6135 if (vtype(obj) == T_STR) { 6128 6136 double idx_d = JS_NAN; ··· 6253 6261 } 6254 6262 6255 6263 if (t == T_ARR && streq(ptr, plen, "length", 6)) { 6256 - jsval_t key = js_mkstr(js, "length", 6); 6257 - return mkpropref((jsoff_t)vdata(l), (jsoff_t)vdata(key)); 6264 + return mkpropref((jsoff_t)vdata(l), (jsoff_t)vdata(js->length_str)); 6258 6265 } 6259 - 6266 + 6260 6267 if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) { 6261 6268 jsval_t key = js_mkstr(js, ptr, plen); 6262 6269 return mkprim_propref(l, (jsoff_t)vdata(key)); ··· 8930 8937 if (is_err(func_obj)) return func_obj; 8931 8938 set_func_code(js, func_obj, &js->code[pos], js->pos - pos); 8932 8939 8933 - jsval_t len_key = js_mkstr(js, "length", 6); 8934 - if (is_err(len_key)) return len_key; 8935 - jsval_t res_len = js_setprop(js, func_obj, len_key, tov(param_count)); 8940 + jsval_t res_len = js_setprop(js, func_obj, js->length_str, tov(param_count)); 8936 8941 if (is_err(res_len)) return res_len; 8937 8942 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 8938 8943 ··· 10654 10659 jsval_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); 10655 10660 if (vtype(func_proto) == T_FUNC) set_proto(js, func_obj, func_proto); 10656 10661 10657 - jsval_t len_key = js_mkstr(js, "length", 6); 10658 - if (is_err(len_key)) return len_key; 10659 - jsval_t res_len = js_setprop(js, func_obj, len_key, tov(param_count)); 10662 + jsval_t res_len = js_setprop(js, func_obj, js->length_str, tov(param_count)); 10660 10663 if (is_err(res_len)) return res_len; 10661 10664 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 10662 10665 jsval_t name_key = js_mkstr(js, "name", 4); ··· 10722 10725 set_slot(js, func_obj, SLOT_ASYNC, js_true); 10723 10726 jsval_t async_proto = get_slot(js, js_glob(js), SLOT_ASYNC_PROTO); 10724 10727 if (vtype(async_proto) == T_FUNC) set_proto(js, func_obj, async_proto); 10725 - jsval_t len_key = js_mkstr(js, "length", 6); 10726 - if (is_err(len_key)) return len_key; 10727 - jsval_t res_len = js_setprop(js, func_obj, len_key, tov(0)); 10728 + jsval_t res_len = js_setprop(js, func_obj, js->length_str, tov(0)); 10728 10729 if (is_err(res_len)) return res_len; 10729 10730 js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); 10730 10731 jsval_t name_key = js_mkstr(js, "name", 4); ··· 13088 13089 set_slot(js, js->this_val, SLOT_PRIMITIVE, sval); 13089 13090 jsoff_t slen; 13090 13091 vstr(js, sval, &slen); 13091 - js_setprop(js, js->this_val, js_mkstr(js, "length", 6), tov((double)slen)); 13092 + js_setprop(js, js->this_val, js->length_str, tov((double)slen)); 13092 13093 js_set_descriptor(js, js->this_val, "length", 6, 0); 13093 13094 } 13094 13095 return sval; ··· 13613 13614 if (vtype(func_proto) == T_FUNC) set_proto(js, bound_func, func_proto); 13614 13615 13615 13616 jsval_t bound = mkval(T_FUNC, (unsigned long) vdata(bound_func)); 13616 - js_setprop(js, bound_func, js_mkstr(js, "length", 6), tov((double) bound_length)); 13617 + js_setprop(js, bound_func, js->length_str, tov((double) bound_length)); 13617 13618 13618 13619 jsval_t proto_setup = setup_func_prototype(js, bound); 13619 13620 if (is_err(proto_setup)) return proto_setup; ··· 13665 13666 set_slot(js, bound_func, SLOT_BOUND_ARGS, bound_arr); 13666 13667 } 13667 13668 13668 - js_setprop(js, bound_func, js_mkstr(js, "length", 6), tov((double) bound_length)); 13669 + js_setprop(js, bound_func, js->length_str, tov((double) bound_length)); 13669 13670 13670 13671 jsval_t bound = mkval(T_FUNC, (unsigned long) vdata(bound_func)); 13671 13672 jsval_t proto_setup = setup_func_prototype(js, bound); ··· 15867 15868 } next = next_prop(header); 15868 15869 } 15869 15870 15870 - if (is_arr_obj) arr_set(js, arr, idx++, js_mkstr(js, "length", 6)); 15871 - 15871 + if (is_arr_obj) arr_set(js, arr, idx++, js->length_str); 15872 15872 return mkval(T_ARR, vdata(arr)); 15873 15873 } 15874 15874 ··· 16147 16147 js_setprop(js, arr, key, args[i]); 16148 16148 len++; 16149 16149 } 16150 - jsval_t len_key = js_mkstr(js, "length", 6); 16150 + 16151 16151 jsval_t len_val = tov((double) len); 16152 - js_setprop(js, arr, len_key, len_val); 16152 + js_setprop(js, arr, js->length_str, len_val); 16153 16153 return len_val; 16154 16154 } 16155 16155 ··· 16227 16227 if (is_proxy(js, arr)) { 16228 16228 jsoff_t len = proxy_aware_length(js, arr); 16229 16229 if (len == 0) { 16230 - jsval_t len_key = js_mkstr(js, "length", 6); 16231 - js_setprop(js, arr, len_key, tov(0.0)); 16230 + js_setprop(js, arr, js->length_str, tov(0.0)); 16232 16231 return js_mkundef(); 16233 16232 } 16234 16233 len--; 16235 16234 char idxstr[16]; 16236 16235 size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); 16237 16236 jsval_t result = proxy_aware_get_elem(js, arr, idxstr, idxlen); 16238 - jsval_t len_key = js_mkstr(js, "length", 6); 16239 - js_setprop(js, arr, len_key, tov((double) len)); 16237 + js_setprop(js, arr, js->length_str, tov((double) len)); 16240 16238 js->needs_gc = true; 16241 16239 return result; 16242 16240 } ··· 16285 16283 16286 16284 if (off != 0) { 16287 16285 saveval(js, off + sizeof(jsoff_t) * 2, tov((double) len)); 16288 - } else { 16289 - jsval_t len_key = js_mkstr(js, "length", 6); 16290 - js_setprop(js, arr, len_key, tov((double) len)); 16291 - } 16286 + } else js_setprop(js, arr, js->length_str, tov((double) len)); 16292 16287 16293 16288 js->needs_gc = true; 16294 16289 return result; ··· 17090 17085 } 17091 17086 } 17092 17087 17093 - jsval_t len_key = js_mkstr(js, "length", 6); 17094 - js_setprop(js, arr, len_key, tov((double)(len - 1))); 17088 + js_setprop(js, arr, js->length_str, tov((double)(len - 1))); 17095 17089 js->needs_gc = true; 17090 + 17096 17091 return first; 17097 17092 } 17098 17093 ··· 17140 17135 js_setprop(js, arr, key, args[i]); 17141 17136 } 17142 17137 17143 - jsval_t len_key = js_mkstr(js, "length", 6); 17144 17138 jsoff_t new_len = len + nargs; 17145 - js_setprop(js, arr, len_key, tov((double) new_len)); 17139 + js_setprop(js, arr, js->length_str, tov((double) new_len)); 17140 + 17146 17141 return tov((double) new_len); 17147 17142 } 17148 17143 ··· 17394 17389 js_setprop(js, removed, key, elem); 17395 17390 } 17396 17391 } 17397 - jsval_t rem_len_key = js_mkstr(js, "length", 6); 17398 - js_setprop(js, removed, rem_len_key, tov((double) deleteCount)); 17399 17392 17393 + js_setprop(js, removed, js->length_str, tov((double) deleteCount)); 17400 17394 int shift = insertCount - deleteCount; 17395 + 17401 17396 if (shift > 0) { 17402 17397 for (int i = (int)len - 1; i >= start + deleteCount; i--) { 17403 17398 char src[16], dst[16]; ··· 17427 17422 js_setprop(js, arr, key, args[2 + i]); 17428 17423 } 17429 17424 17430 - jsval_t len_key = js_mkstr(js, "length", 6); 17431 - js_setprop(js, arr, len_key, tov((double)((int)len + shift))); 17425 + js_setprop(js, arr, js->length_str, tov((double)((int)len + shift))); 17432 17426 if (deleteCount > 0) js->needs_gc = true; 17427 + 17433 17428 return mkval(T_ARR, vdata(removed)); 17434 17429 } 17435 17430 ··· 17750 17745 js_setprop(js, write_target, js_mkstr(js, idxstr, idxlen), elem); 17751 17746 } 17752 17747 } 17753 - if (vtype(result) != T_ARR) { 17754 - jsval_t len_key = js_mkstr(js, "length", 6); 17755 - js_setprop(js, result, len_key, tov((double) str_len)); 17756 - } 17748 + if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double) str_len)); 17757 17749 } else if (vtype(src) == T_ARR || vtype(src) == T_OBJ) { 17758 17750 jsoff_t len = get_array_length(js, src); 17759 17751 for (jsoff_t i = 0; i < len; i++) { ··· 17769 17761 js_setprop(js, write_target, js_mkstr(js, idxstr, idxlen), elem); 17770 17762 } 17771 17763 } 17772 - if (vtype(result) != T_ARR) { 17773 - jsval_t len_key = js_mkstr(js, "length", 6); 17774 - js_setprop(js, result, len_key, tov((double) len)); 17775 - } 17764 + if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double) len)); 17776 17765 } 17777 17766 17778 17767 if (!use_ctor) return mkval(T_ARR, vdata(result)); ··· 17803 17792 js_setprop(js, write_target, js_mkstr(js, idxstr, idxlen), args[i]); 17804 17793 } 17805 17794 } 17806 - if (vtype(arr) != T_ARR) { 17807 - jsval_t len_key = js_mkstr(js, "length", 6); 17808 - js_setprop(js, arr, len_key, tov((double) nargs)); 17809 - } 17810 - 17795 + 17796 + if (vtype(arr) != T_ARR) js_setprop(js, arr, js->length_str, tov((double) nargs)); 17811 17797 if (!use_ctor) return mkval(T_ARR, vdata(arr)); 17798 + 17812 17799 return arr; 17813 17800 } 17814 17801 ··· 20605 20592 if (vtype(r) == T_ARR) { 20606 20593 unsigned long idx; 20607 20594 jsoff_t arr_len = get_array_length(js, r); 20608 - if (parse_array_index(prop_name, prop_len, arr_len, &idx)) { 20609 - return mkval(T_BOOL, arr_has(js, r, (jsoff_t)idx) ? 1 : 0); 20610 - } 20611 - if (prop_len == 6 && memcmp(prop_name, "length", 6) == 0) { 20612 - return mkval(T_BOOL, 1); 20613 - } 20595 + if (parse_array_index(prop_name, prop_len, arr_len, &idx)) return mkval(T_BOOL, arr_has(js, r, (jsoff_t)idx) ? 1 : 0); 20596 + if (prop_len == 6 && memcmp(prop_name, "length", 6) == 0) return mkval(T_BOOL, 1); 20614 20597 } 20615 20598 20616 20599 jsoff_t found = lkp_proto(js, r, prop_name, prop_len); ··· 21833 21816 js->errmsg_size = 4096; 21834 21817 js->errmsg = (char *)malloc(js->errmsg_size); 21835 21818 if (js->errmsg) js->errmsg[0] = '\0'; 21836 - 21819 + js->length_str = ANT_STRING("length"); 21820 + 21837 21821 #ifdef _WIN32 21838 21822 js->stack_limit = 512 * 1024; 21839 21823 #else ··· 22120 22104 set_proto(js, func_ctor_obj, function_proto); 22121 22105 set_slot(js, func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Function)); 22122 22106 js_setprop_nonconfigurable(js, func_ctor_obj, "prototype", 9, function_proto); 22123 - js_setprop(js, func_ctor_obj, js_mkstr(js, "length", 6), tov(1.0)); 22107 + js_setprop(js, func_ctor_obj, js->length_str, tov(1.0)); 22124 22108 js_set_descriptor(js, func_ctor_obj, "length", 6, JS_DESC_C); 22125 22109 js_setprop(js, func_ctor_obj, ANT_STRING("name"), ANT_STRING("Function")); 22126 22110 js_setprop(js, glob, js_mkstr(js, "Function", 8), mkval(T_FUNC, vdata(func_ctor_obj))); ··· 22135 22119 set_proto(js, async_func_ctor_obj, function_proto); 22136 22120 set_slot(js, async_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_AsyncFunction)); 22137 22121 js_setprop_nonconfigurable(js, async_func_ctor_obj, "prototype", 9, async_func_proto); 22138 - js_setprop(js, async_func_ctor_obj, js_mkstr(js, "length", 6), tov(1.0)); 22122 + js_setprop(js, async_func_ctor_obj, js->length_str, tov(1.0)); 22139 22123 js_set_descriptor(js, async_func_ctor_obj, "length", 6, JS_DESC_C); 22140 22124 js_setprop(js, async_func_ctor_obj, ANT_STRING("name"), ANT_STRING("AsyncFunction")); 22141 22125 jsval_t async_func_ctor = mkval(T_FUNC, vdata(async_func_ctor_obj)); ··· 22190 22174 js_setprop(js, arr_ctor_obj, js_mkstr(js, "isArray", 7), js_mkfun(builtin_Array_isArray)); 22191 22175 js_setprop(js, arr_ctor_obj, js_mkstr(js, "from", 4), js_mkfun(builtin_Array_from)); 22192 22176 js_setprop(js, arr_ctor_obj, js_mkstr(js, "of", 2), js_mkfun(builtin_Array_of)); 22193 - js_setprop(js, arr_ctor_obj, js_mkstr(js, "length", 6), tov(1.0)); 22177 + js_setprop(js, arr_ctor_obj, js->length_str, tov(1.0)); 22194 22178 js_set_descriptor(js, arr_ctor_obj, "length", 6, JS_DESC_C); 22195 22179 js_setprop(js, arr_ctor_obj, ANT_STRING("name"), ANT_STRING("Array")); 22196 22180 js_setprop(js, glob, js_mkstr(js, "Array", 5), mkval(T_FUNC, vdata(arr_ctor_obj))); ··· 22752 22736 op_val(c, &c->js->module_ns); 22753 22737 op_val(c, &c->js->current_func); 22754 22738 op_val(c, &c->js->thrown_value); 22755 - 22739 + op_val(c, &c->js->length_str); 22740 + 22756 22741 for (jshdl_t i = 0; i < c->js->gc_roots_len; i++) op_val(c, &c->js->gc_roots[i]); 22757 22742 if (c->js->ascii_cache_init) for (int i = 0; i < 128; i++) op_val(c, &c->js->ascii_char_cache[i]); 22758 22743 } ··· 23109 23094 idx++; 23110 23095 arg_idx++; 23111 23096 } 23112 - jsval_t len_key = js_mkstr(js, "length", 6); 23113 - js_setprop(js, rest_array, len_key, tov((double) idx)); 23097 + js_setprop(js, rest_array, js->length_str, tov((double) idx)); 23114 23098 rest_array = mkval(T_ARR, vdata(rest_array)); 23115 23099 js_setprop(js, js->scope, js_mkstr(js, &fn[pf->rest_param_start], pf->rest_param_len), rest_array); 23116 23100 }
+4 -2
src/gc.c
··· 44 44 45 45 #ifdef _WIN32 46 46 #define RELEASE_PAGES(p, sz) VirtualAlloc(p, sz, MEM_RESET, PAGE_READWRITE) 47 + #elif defined(__APPLE__) 48 + #define RELEASE_PAGES(p, sz) madvise(p, sz, MADV_FREE) 47 49 #else 48 50 #define RELEASE_PAGES(p, sz) madvise(p, sz, MADV_DONTNEED) 49 51 #endif ··· 788 790 size_t new_brk = ctx.new_brk; 789 791 size_t old_size = js->size; 790 792 791 - if (new_brk < old_size / 2 && old_size > ARENA_GROW_INCREMENT) { 792 - size_t target = ((new_brk * 2 + ARENA_GROW_INCREMENT - 1) / ARENA_GROW_INCREMENT) * ARENA_GROW_INCREMENT; 793 + if (new_brk < old_size * 3 / 4 && old_size > ARENA_GROW_INCREMENT) { 794 + size_t target = ((new_brk * 3 / 2 + ARENA_GROW_INCREMENT - 1) / ARENA_GROW_INCREMENT) * ARENA_GROW_INCREMENT; 793 795 if (target < ARENA_GROW_INCREMENT) target = ARENA_GROW_INCREMENT; 794 796 if (target < old_size) { ant_arena_decommit(js->mem, old_size, target); js->size = (jsoff_t)target; } 795 797 }
+7
src/modules/builtin.c
··· 114 114 js_set(js, ext, "total", js_mknum((double)external_total)); 115 115 js_set(js, result, "external", ext); 116 116 117 + js_intern_stats_t intern_stats = js_intern_stats(); 118 + jsval_t intern = js_newobj(js); 119 + 120 + js_set(js, intern, "count", js_mknum((double)intern_stats.count)); 121 + js_set(js, intern, "bytes", js_mknum((double)intern_stats.bytes)); 122 + js_set(js, result, "intern", intern); 123 + 117 124 if (js->cstk != NULL) { 118 125 volatile char marker; 119 126 uintptr_t base = (uintptr_t)js->cstk;
+2 -2
src/modules/collections.c
··· 680 680 js_setprop(js, entry, js_mkstr(js, idx, idx_len), held_value); 681 681 idx_len = uint_to_str(idx, sizeof(idx), 2); 682 682 js_setprop(js, entry, js_mkstr(js, idx, idx_len), unregister_token); 683 - js_setprop(js, entry, js_mkstr(js, "length", 6), tov(3.0)); 683 + js_setprop(js, entry, js->length_str, tov(3.0)); 684 684 685 685 idx_len = uint_to_str(idx, sizeof(idx), len); 686 686 js_setprop(js, registrations, js_mkstr(js, idx, idx_len), entry); 687 - js_setprop(js, registrations, js_mkstr(js, "length", 6), tov((double)(len + 1))); 687 + js_setprop(js, registrations, js->length_str, tov((double)(len + 1))); 688 688 689 689 return js_mkundef(); 690 690 }