nota leaks fixed

This commit is contained in:
John Alanbrook 2024-01-31 08:42:15 +00:00
parent 46b86a8e92
commit 262cd50e1a
13 changed files with 217 additions and 162 deletions

View file

@ -91,14 +91,14 @@
*/
//#define DUMP_BYTECODE (1)
/* dump the occurence of the automatic GC */
//#define DUMP_GC
#define DUMP_GC
/* dump objects freed by the garbage collector */
//#define DUMP_GC_FREE
/* dump objects leaking when freeing the runtime */
//#define DUMP_LEAKS 1
#define DUMP_LEAKS 1
/* dump memory usage before running the garbage collector */
//#define DUMP_MEM
//#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
#define DUMP_MEM
#define DUMP_OBJECTS /* dump objects in JS_FreeContext */
//#define DUMP_ATOMS /* dump atoms in JS_FreeContext */
//#define DUMP_SHAPES /* dump shapes in JS_FreeContext */
//#define DUMP_MODULE_RESOLVE

View file

@ -562,7 +562,7 @@ var editor = {
} else {
var path = sub.replaceAll('.', '/') + ".json";
var saveobj = obj.json_obj();
IO.slurpwrite(JSON.stringify(saveobj,null,1), path);
IO.slurpwrite(path, JSON.stringify(saveobj,null,1));
if (obj === editor.edit_level) {
if (obj === editor.desktop) {
@ -841,7 +841,7 @@ editor.inputs['C-s'] = function() {
if (savejs.objects) saveobj.__proto__.objects = savejs.objects;
var path = prototypes.ur_stem(saveobj.ur.toString()) + ".json";
IO.slurpwrite(JSON.stringify(saveobj.__proto__,null,1), path);
IO.slurpwrite(path, JSON.stringify(saveobj.__proto__,null,1));
Log.warn(`Wrote to file ${path}`);
Object.values(saveobj.objects).forEach(function(x) { x.check_dirty(); });

View file

@ -760,7 +760,6 @@ gameobject.doc = {
var prototypes = {};
prototypes.ur_ext = ".jso";
prototypes.ur = {};
prototypes.save_gameobjects = function() { slurpwrite(JSON.stringify(gameobjects,null,2), "proto.json"); };
/* Makes a new ur-type from disk. If the ur doesn't exist, it searches on the disk to create it. */
prototypes.from_file = function(file)

View file

@ -1,4 +1,3 @@
var Sound = {
bus: {},
samplerate() { return cmd(198); },
@ -108,9 +107,10 @@ Object.mixin(cmd(180).__proto__, {
set volume(x) { this.gain = x; },
});
Object.mixin(DSP.source().__proto__, {
/*Object.mixin(DSP.source().__proto__, {
frames() { return cmd(197,this); },
length() { return this.frames()/Sound.samplerate(); },
time() { return this.frame/Sound.samplerate(); },
pct() { return this.time()/this.length(); },
});
*/

View file

@ -6,6 +6,12 @@ function compile_env(str, env, file)
function fcompile_env(file, env) { return compile_env(IO.slurp(file), env, file); }
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join(' ');
}
var OS = {};
OS.cwd = function() { return cmd(144); }
OS.exec = function(s) { cmd(143, s); }
@ -140,7 +146,15 @@ var IO = {
else
throw new Error(`File ${file} does not exist; can't slurp`);
},
slurpwrite(str, file) { return cmd(39, str, file); },
slurpbytes(file) {
return cmd(81, file);
},
slurpwrite(file, data) {
if (data.byteLength)
cmd(60, data, file);
else
return cmd(39, data, file);
},
extensions(ext) {
var paths = IO.ls();
paths = paths.filter(function(str) { return str.ext() === ext; });
@ -312,7 +326,6 @@ Cmdline.register_cmd("cjson", function(json) {
}
Log.say(j);
// IO.slurpwrite(JSON.stringify(j,undefined,2), f);
STD.exit(0);
}, "Clean up a jso file.");

View file

@ -19,7 +19,6 @@ HMM_Vec2 mouse_delta = {0, 0};
struct joystick {
int id;
//GLFWgamepadstate state;
};
static int *downkeys = NULL;

View file

@ -162,8 +162,8 @@ JSValue js_getpropidx(JSValue v, uint32_t i)
static inline cpBody *js2body(JSValue v) { return js2gameobject(v)->body; }
uint64_t js2uint64(JSValue v) {
uint64_t i;
int64_t js2int64(JSValue v) {
int64_t i;
JS_ToInt64(js, &i, v);
return i;
}
@ -247,6 +247,8 @@ char *js_nota_decode(JSValue *tmp, char *nota)
case NOTA_TEXT:
nota = nota_read_text(&str, nota);
*tmp = str2js(str);
/* TODO: Avoid malloc and free here */
free(str);
break;
case NOTA_ARR:
nota = nota_read_array(&n, nota);
@ -258,14 +260,12 @@ char *js_nota_decode(JSValue *tmp, char *nota)
break;
case NOTA_REC:
nota = nota_read_record(&n, nota);
printf("nota object with %d elements\n", n);
*tmp = JS_NewObject(js);
for (int i = 0; i < n; i++) {
nota = nota_read_text(&str, nota);
printf("looking at property %d named %s\n", i, str);
nota = js_nota_decode(&ret2, nota);
JS_SetPropertyStr(js, *tmp, str, ret2);
free(str);
}
break;
case NOTA_INT:
@ -273,8 +273,10 @@ char *js_nota_decode(JSValue *tmp, char *nota)
*tmp = int2js(n);
break;
case NOTA_SYM:
nota = nota_read_bool(&b, nota);
*tmp = bool2js(b);
nota = nota_read_sym(&b, nota);
if (b == NOTA_NULL) *tmp = JS_UNDEFINED;
else
*tmp = bool2js(b);
break;
default:
case NOTA_FLOAT:
@ -297,42 +299,44 @@ char *js_nota_encode(JSValue v, char *nota)
switch(tag) {
case JS_TAG_FLOAT64:
// printf("encode float\n");
return nota_write_float(JS_VALUE_GET_FLOAT64(v), nota);
case JS_TAG_INT:
// printf("encode int\n");
return nota_write_int(JS_VALUE_GET_INT(v), nota);
case JS_TAG_STRING:
// printf("encode string\n");
str = js2str(v);
nota = nota_write_text(str, nota);
JS_FreeCString(js, str);
return nota;
case JS_TAG_BOOL:
// printf("encode bool\n");
return nota_write_bool(JS_VALUE_GET_BOOL(v), nota);
return nota_write_sym(JS_VALUE_GET_BOOL(v), nota);
case JS_TAG_UNDEFINED:
return nota_write_sym(NOTA_NULL, nota);
case JS_TAG_NULL:
return nota_write_sym(NOTA_NULL, nota);
case JS_TAG_OBJECT:
if (JS_IsArray(js, v)) {
// printf("encode array\n");
int n = js_arrlen(v);
nota = nota_write_array(n, nota);
for (int i = 0; i < js_arrlen(v); i++)
for (int i = 0; i < n; i++)
nota = js_nota_encode(js_arridx(v, i), nota);
return nota;
}
// printf("encode object\n");
n = JS_GetOwnPropertyNames(js, &ptab, &plen, v, JS_GPN_ENUM_ONLY | JS_GPN_STRING_MASK);
nota = nota_write_record(plen, nota);
for (int i = 0; i < plen; i++) {
val = JS_GetProperty(js,v,ptab[i].atom);
if (JS_IsUndefined(val) || JS_IsNull(val)) continue;
/* todo: slower than atomtocstring */
str = JS_AtomToCString(js, ptab[i].atom);
// printf("encoding object entry %s\n", str);
JS_FreeAtom(js, ptab[i].atom);
nota = nota_write_text(str, nota);
nota = js_nota_encode(JS_GetProperty(js, v, ptab[i].atom), nota);
JS_FreeCString(js, str);
nota = js_nota_encode(val, nota);
JS_FreeValue(js,val);
}
js_free(js, ptab);
return nota;
}
}
@ -666,6 +670,7 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
int *intids = NULL;
gameobject *go = NULL;
JSValue ret = JS_UNDEFINED;
size_t plen = 0;
switch (cmd) {
case 0:
@ -807,9 +812,9 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 39:
str = JS_ToCString(js, argv[1]);
str = JS_ToCStringLen(js, &plen, argv[1]);
str2 = JS_ToCString(js, argv[2]);
ret = JS_NewInt64(js, slurp_write(str, str2));
ret = JS_NewInt64(js, slurp_write(str, str2, plen));
break;
case 40:
@ -883,6 +888,10 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 60:
str = JS_GetArrayBuffer(js, &plen, argv[1]);
str2 = JS_ToCString(js, argv[2]);
ret = JS_NewInt64(js, slurp_write(str, str2, plen));
str = NULL;
break;
case 61:
@ -961,6 +970,12 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
arrfree(ids);
break;
case 81:
str = JS_ToCString(js, argv[1]);
d1 = slurp_file(str, &plen);
ret = JS_NewArrayBufferCopy(js, d1, plen);
break;
case 82:
gameobject_draw_debug(js2gameobject(argv[1]));
break;
@ -1101,15 +1116,15 @@ JSValue duk_cmd(JSContext *js, JSValueConst this, int argc, JSValueConst *argv)
break;
case 128:
ret = JS_NewFloat64(js, stm_ns(js2uint64(argv[1])));
ret = JS_NewFloat64(js, stm_ns(js2int64(argv[1])));
break;
case 129:
ret = JS_NewFloat64(js, stm_us(js2uint64(argv[1])));
ret = JS_NewFloat64(js, stm_us(js2int64(argv[1])));
break;
case 130:
ret = JS_NewFloat64(js, stm_ms(js2uint64(argv[1])));
ret = JS_NewFloat64(js, stm_ms(js2int64(argv[1])));
break;
case 131:
@ -2041,7 +2056,7 @@ JSValue nota_encode(JSContext *js, JSValueConst this, int argc, JSValueConst *ar
if (argc < 1) return JS_UNDEFINED;
JSValue obj = argv[0];
char nota[100000];
char nota[1024*1024]; // 1MB
char *e = js_nota_encode(obj, nota);
return JS_NewArrayBufferCopy(js, nota, e-nota);
@ -2107,6 +2122,7 @@ void ffi_load() {
sound_proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, sound_proto, js_sound_funcs, countof(js_sound_funcs));
JS_SetPrototype(js, sound_proto, dsp_node_proto);
JS_FreeValue(js, sound_proto);
QJSCLASSPREP_FUNCS(emitter);
QJSCLASSPREP_FUNCS(warp_gravity);

View file

@ -1,2 +1,101 @@
#include "kim.h"
#define KIM_CONT 0x80
#define KIM_DATA 0x7f
#define CONTINUE(CHAR) (CHAR>>7)
int utf8_bytes(char *s)
{
int bytes = __builtin_clz(~(*s));
if (!bytes) return 1;
return bytes-24;
}
int utf8_count(char *s)
{
int count = 0;
char *p = s;
while(*s) {
count++;
s += utf8_bytes(s);
}
return count;
}
/* decode and advance s, returning the character cde */
int decode_utf8(char **s) {
int k = **s ? __builtin_clz(~(**s << 24)) : 0; // Count # of leading 1 bits.
int mask = (1 << (8 - k)) - 1; // All 1's with k leading 0's.
int value = **s & mask;
for (++(*s), --k; k > 0 && **s; --k, ++(*s)) { // Note that k = #total bytes, or 0.
value <<= 6;
value += (**s & 0x3F);
}
return value;
}
/* Write and advance s with code in utf-8 */
void encode_utf8(char **s, int code) {
char val[4];
int lead_byte_max = 0x7F;
int val_index = 0;
while (code > lead_byte_max) {
val[val_index++] = (code & 0x3F) | 0x80;
code >>= 6;
lead_byte_max >>= (val_index == 1 ? 2 : 1);
}
val[val_index++] = (code & lead_byte_max) | (~lead_byte_max << 1);
while (val_index--) {
**s = val[val_index];
(*s)++;
}
}
/* write and advance s with code in kim */
void encode_kim(char **s, int code)
{
if (code < KIM_CONT) {
**s = 0 | (KIM_DATA & code);
(*s)++;
return;
}
int bits = ((32 - __builtin_clz(code) + 6) / 7) * 7;
while (bits > 7) {
bits -= 7;
**s = KIM_CONT | KIM_DATA & (code >> bits);
(*s)++;
}
**s = KIM_DATA & code;
(*s)++;
}
/* decode and advance s, returning the character code */
int decode_kim(char **s)
{
int rune = **s & KIM_DATA;
while (CONTINUE(**s)) {
rune <<= 7;
(*s)++;
rune |= **s & KIM_DATA;
}
(*s)++;
return rune;
}
/* write a null-terminated utf8 stream into a kim string */
void utf8_to_kim(char **utf, char **kim)
{
while (**utf)
encode_kim(kim, decode_utf8(utf));
}
/* write number of runes from a kim stream int a utf8 stream */
void kim_to_utf8(char **kim, char **utf, int runes)
{
for (int i = 0; i < runes; i++)
encode_utf8(utf, decode_kim(kim));
}

View file

@ -1,6 +1,13 @@
#ifndef KIM_H
#define KIM_H
void write_kim_char(char c, char *into);
int utf8_bytes(char *s);
int utf8_count(char *s);
int decode_utf8(char **s);
void encode_utf8(char **s, int code);
void encode_kim(char **s, int code);
int decode_kim(char **s);
void utf8_to_kim(char **utf, char **kim);
void kim_to_utf8(char **kim, char **utf, int runes);
#endif

View file

@ -4,6 +4,7 @@
#include "string.h"
#include "stdlib.h"
#include "limits.h"
#include "kim.h"
#define NOTA_CONT 0x80
#define NOTA_DATA 0x7f
@ -15,13 +16,11 @@
#define NOTA_HEAD_DATA 0x0f
#define CONTINUE(CHAR) (CHAR>>7)
#define NOTA_FALSE 0x00
#define NOTA_TRUE 0x01
#define NOTA_PRIVATE 0x08
#define NOTA_SYSTEM 0x09
#define UTF8_DATA 0x3f
/* define this to use native string instead of kim. Bytes are encoded instead of runes */
#define NOTA_UTF8
int nota_type(char *nota) { return *nota & NOTA_TYPE; }
char *nota_skip(char *nota)
@ -240,136 +239,52 @@ char *nota_write_record(unsigned long long n, char *nota)
return nota_continue_num(n, nota, 4);
}
/* kim is 7, 14, then 21 */
int utf8_bytes(char *s)
char *nota_write_sym(int sym, char *nota)
{
int bytes = __builtin_clz(~(*s));
if (!bytes) return 1;
return bytes-24;
*nota = NOTA_SYM | sym;
return nota+1;
}
int utf8_count(char *s)
char *nota_read_sym(int *sym, char *nota)
{
int count = 0;
char *p = s;
while(*s) {
count++;
s += utf8_bytes(s);
}
return count;
}
int decode_utf8(char **s) {
int k = **s ? __builtin_clz(~(**s << 24)) : 0; // Count # of leading 1 bits.
int mask = (1 << (8 - k)) - 1; // All 1's with k leading 0's.
int value = **s & mask;
for (++(*s), --k; k > 0 && **s; --k, ++(*s)) { // Note that k = #total bytes, or 0.
value <<= 6;
value += (**s & 0x3F);
}
return value;
}
void encode_utf8(char **s, char *end, int code) {
if (code < 255) {
**s = code;
(*s)++;
return;
}
char val[4];
int lead_byte_max = 0x7F;
int val_index = 0;
while (code > lead_byte_max) {
val[val_index++] = (code & 0x3F) | 0x80;
code >>= 6;
lead_byte_max >>= (val_index == 1 ? 2 : 1);
}
val[val_index++] = (code & lead_byte_max) | (~lead_byte_max << 1);
while (val_index-- && *s < end) {
**s = val[val_index];
(*s)++;
}
}
void encode_kim(char **s, char *end, int code)
{
if (code < 255) {
**s = 0 | (NOTA_DATA & code);
(*s)++;
return;
}
int bits = ((32 - __builtin_clz(code) + 6) / 7) * 7;
while (bits > 7) {
bits -= 7;
**s = NOTA_CONT | NOTA_DATA & (code >> bits);
(*s)++;
}
**s = NOTA_DATA & code;
(*s)++;
}
int decode_kim(char **s)
{
int rune = **s & NOTA_DATA;
while (CONTINUE(**s)) {
rune <<= 7;
(*s)++;
rune |= **s & NOTA_DATA;
}
(*s)++;
return rune;
}
char *utf8_to_kim(char *utf, char *kim)
{
while (*utf)
encode_kim(&kim, NULL, decode_utf8(&utf));
return kim;
}
void kim_to_utf8(char *kim, char *utf, int runes)
{
for (int i = 0; i < runes; i++)
encode_utf8(&utf, utf+4, decode_kim(&kim));
*utf = 0;
if (*sym) *sym = (*nota) & 0x0f;
return nota+1;
}
char *nota_read_text(char **text, char *nota)
{
long long chars;
nota = nota_read_num(&chars, nota);
#ifdef NOTA_UTF8
*text = calloc(chars+1,1);
memcpy(*text, nota, chars);
nota += chars;
#else
char utf[chars*4];
kim_to_utf8(nota, utf, chars);
*text = strdup(utf);
char *pp = utf;
kim_to_utf8(&nota, &pp, chars);
*pp = 0;
*text = strdup(utf);
#endif
return nota;
}
char *nota_write_bool(int b, char *nota)
{
*nota = NOTA_SYM | (b ? NOTA_TRUE : NOTA_FALSE);
return nota+1;
}
char *nota_read_bool(int *b, char *nota)
{
if (b) *b = (*nota) & 0x0f;
return nota+1;
}
char *nota_write_text(char *s, char *nota)
{
char *start = nota;
nota[0] = NOTA_TEXT;
long long n = utf8_count(s);
#ifdef NOTA_UTF8
long long n = strlen(s);
nota = nota_continue_num(n,nota,4);
return utf8_to_kim(s, nota);
memcpy(nota, s, n);
return nota+n;
#else
long long n = utf8_count(s);
nota = nota_continue_num(n,nota,4);
utf8_to_kim(&s, &nota);
return nota;
#endif
}

View file

@ -9,6 +9,12 @@
#define NOTA_INT 0x60
#define NOTA_SYM 0x70
#define NOTA_FALSE 0x00
#define NOTA_TRUE 0x01
#define NOTA_PRIVATE 0x08
#define NOTA_SYSTEM 0x09
#define NOTA_NULL 0x02
typedef struct NOTA {
char *head;
} NOTA;
@ -21,7 +27,7 @@ char *nota_read_array(long long *len, char *nota);
char *nota_read_record(long long *len, char *nota);
char *nota_read_float(double *d, char *nota);
char *nota_read_int(long long *l, char *nota);
char *nota_read_bool(int *b, char *nota);
char *nota_read_sym(int *sym, char *nota);
void print_nota_hex(char *nota);
@ -31,6 +37,6 @@ char *nota_write_array(unsigned long long n, char *nota);
char *nota_write_record(unsigned long long n, char *nota);
char *nota_write_float(double n, char *nota);
char *nota_write_int(long long n, char *nota);
char *nota_write_bool(int b, char *nota);
char *nota_write_sym(int sym, char *nota);
#endif

View file

@ -291,11 +291,12 @@ FILE *fopen_mkdir(const char *path, const char *mode) {
return fopen(path,mode);
}
int slurp_write(const char *txt, const char *filename) {
int slurp_write(const char *txt, const char *filename, size_t len) {
FILE *f = fopen_mkdir(filename, "w");
if (!f) return 1;
fputs(txt, f);
if (len < 0) len = strlen(txt);
fwrite(txt, len, 1, f);
fclose(f);
return 0;
}

View file

@ -24,7 +24,7 @@ char *dirname(const char *path);
void *slurp_file(const char *filename, size_t *size);
char *slurp_text(const char *filename, size_t *size);
int slurp_write(const char *txt, const char *filename);
int slurp_write(const char *txt, const char *filename, size_t len);
char *seprint(char *fmt, ...);