shithub: libmujs

Download patch

ref: 67b33c5a86ad6656744f6eb51b3d9ed0686fc4f2
parent: 598de57d76b9b3ff421d1d737eca0b88e0d020a3
author: Tor Andersson <tor@ccxvii.net>
date: Fri Feb 28 09:24:13 EST 2014

Handle malloc failure by throwing exceptions.

--- a/jsarray.c
+++ b/jsarray.c
@@ -119,7 +119,7 @@
 	}
 
 	if (js_try(J)) {
-		free(out);
+		js_free(J, out);
 		js_throw(J);
 	}
 
@@ -133,7 +133,7 @@
 		n += strlen(r);
 
 		if (k == 0) {
-			out = malloc(n);
+			out = js_malloc(J, n);
 			strcpy(out, r);
 		} else {
 			n += seplen;
@@ -147,7 +147,7 @@
 
 	js_pushstring(J, out);
 	js_endtry(J);
-	free(out);
+	js_free(J, out);
 }
 
 static void Ap_pop(js_State *J, unsigned int argc)
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -103,22 +103,22 @@
 	while (*str) {
 		int c = (unsigned char) *str++;
 		if (strchr(unescaped, c))
-			sb_putc(&sb, c);
+			js_putc(J, &sb, c);
 		else {
-			sb_putc(&sb, '%');
-			sb_putc(&sb, HEX[(c >> 4) & 0xf]);
-			sb_putc(&sb, HEX[c & 0xf]);
+			js_putc(J, &sb, '%');
+			js_putc(J, &sb, HEX[(c >> 4) & 0xf]);
+			js_putc(J, &sb, HEX[c & 0xf]);
 		}
 	}
-	sb_putc(&sb, 0);
+	js_putc(J, &sb, 0);
 
 	if (js_try(J)) {
-		free(sb);
+		js_free(J, sb);
 		js_throw(J);
 	}
 	js_pushstring(J, sb ? sb->s : "");
 	js_endtry(J);
-	free(sb);
+	js_free(J, sb);
 }
 
 static void Decode(js_State *J, const char *str, const char *reserved)
@@ -129,7 +129,7 @@
 	while (*str) {
 		int c = (unsigned char) *str++;
 		if (c != '%')
-			sb_putc(&sb, c);
+			js_putc(J, &sb, c);
 		else {
 			if (!str[0] || !str[1])
 				js_urierror(J, "truncated escape sequence");
@@ -139,23 +139,23 @@
 				js_urierror(J, "invalid escape sequence");
 			c = jsY_tohex(a) << 4 | jsY_tohex(b);
 			if (!strchr(reserved, c))
-				sb_putc(&sb, c);
+				js_putc(J, &sb, c);
 			else {
-				sb_putc(&sb, '%');
-				sb_putc(&sb, a);
-				sb_putc(&sb, b);
+				js_putc(J, &sb, '%');
+				js_putc(J, &sb, a);
+				js_putc(J, &sb, b);
 			}
 		}
 	}
-	sb_putc(&sb, 0);
+	js_putc(J, &sb, 0);
 
 	if (js_try(J)) {
-		free(sb);
+		js_free(J, sb);
 		js_throw(J);
 	}
 	js_pushstring(J, sb ? sb->s : "");
 	js_endtry(J);
-	free(sb);
+	js_free(J, sb);
 }
 
 #define URIRESERVED ";/?:@&=+$,"
--- a/jsbuiltin.h
+++ b/jsbuiltin.h
@@ -20,31 +20,31 @@
 
 typedef struct js_Buffer { unsigned int n, m; char s[64]; } js_Buffer;
 
-static void sb_putc(js_Buffer **sbp, int c)
+static void js_putc(js_State *J, js_Buffer **sbp, int c)
 {
 	js_Buffer *sb = *sbp;
 	if (!sb) {
-		sb = malloc(sizeof *sb);
+		sb = js_malloc(J, sizeof *sb);
 		sb->n = 0;
 		sb->m = sizeof sb->s;
 		*sbp = sb;
 	} else if (sb->n == sb->m) {
-		sb = realloc(sb, (sb->m *= 2) + offsetof(js_Buffer, s));
+		sb = js_realloc(J, sb, (sb->m *= 2) + offsetof(js_Buffer, s));
 		*sbp = sb;
 	}
 	sb->s[sb->n++] = c;
 }
 
-static inline void sb_puts(js_Buffer **sb, const char *s)
+static inline void js_puts(js_State *J, js_Buffer **sb, const char *s)
 {
 	while (*s)
-		sb_putc(sb, *s++);
+		js_putc(J, sb, *s++);
 }
 
-static inline void sb_putm(js_Buffer **sb, const char *s, const char *e)
+static inline void js_putm(js_State *J, js_Buffer **sb, const char *s, const char *e)
 {
 	while (s < e)
-		sb_putc(sb, *s++);
+		js_putc(J, sb, *s++);
 }
 
 #endif
--- a/jscompile.c
+++ b/jscompile.c
@@ -32,7 +32,7 @@
 
 static js_Function *newfun(js_State *J, js_Ast *name, js_Ast *params, js_Ast *body, int script)
 {
-	js_Function *F = malloc(sizeof *F);
+	js_Function *F = js_malloc(J, sizeof *F);
 	memset(F, 0, sizeof *F);
 	F->gcmark = 0;
 	F->gcnext = J->gcfun;
@@ -619,7 +619,7 @@
 
 static void addjump(JF, enum js_AstType type, js_Ast *target, int inst)
 {
-	js_JumpList *jump = malloc(sizeof *jump);
+	js_JumpList *jump = js_malloc(J, sizeof *jump);
 	jump->type = type;
 	jump->inst = inst;
 	jump->next = target->jumps;
--- a/jsfunction.c
+++ b/jsfunction.c
@@ -19,15 +19,15 @@
 		sb = NULL;
 		if (argc > 1) {
 			for (i = 1; i < argc; ++i) {
-				if (i > 1) sb_putc(&sb, ',');
-				sb_puts(&sb, js_tostring(J, i));
+				if (i > 1) js_putc(J, &sb, ',');
+				js_puts(J, &sb, js_tostring(J, i));
 			}
-			sb_putc(&sb, ')');
+			js_putc(J, &sb, ')');
 		}
 	}
 
 	if (js_try(J)) {
-		free(sb);
+		js_free(J, sb);
 		jsP_freeparse(J);
 		js_throw(J);
 	}
@@ -36,7 +36,7 @@
 	fun = jsC_compilefunction(J, parse);
 
 	js_endtry(J);
-	free(sb);
+	js_free(J, sb);
 	jsP_freeparse(J);
 
 	js_newfunction(J, fun, J->GE);
@@ -62,7 +62,7 @@
 		n += strlen(F->name);
 		for (i = 0; i < F->numparams; ++i)
 			n += strlen(F->vartab[i]) + 1;
-		s = malloc(n);
+		s = js_malloc(J, n);
 		strcpy(s, "function ");
 		strcat(s, F->name);
 		strcat(s, "(");
@@ -71,8 +71,12 @@
 			strcat(s, F->vartab[i]);
 		}
 		strcat(s, ") { ... }");
+		if (js_try(J)) {
+			js_free(J, s);
+			js_throw(J);
+		}
 		js_pushstring(J, s);
-		free(s);
+		js_free(J, s);
 	} else {
 		js_pushliteral(J, "function () { ... }");
 	}
--- a/jsgc.c
+++ b/jsgc.c
@@ -9,17 +9,17 @@
 
 static void jsG_freeenvironment(js_State *J, js_Environment *env)
 {
-	free(env);
+	js_free(J, env);
 }
 
 static void jsG_freefunction(js_State *J, js_Function *fun)
 {
-	free(fun->funtab);
-	free(fun->numtab);
-	free(fun->strtab);
-	free(fun->vartab);
-	free(fun->code);
-	free(fun);
+	js_free(J, fun->funtab);
+	js_free(J, fun->numtab);
+	js_free(J, fun->strtab);
+	js_free(J, fun->vartab);
+	js_free(J, fun->code);
+	js_free(J, fun);
 }
 
 static void jsG_freeproperty(js_State *J, js_Property *node)
@@ -26,7 +26,7 @@
 {
 	while (node) {
 		js_Property *next = node->next;
-		free(node);
+		js_free(J, node);
 		node = next;
 	}
 }
@@ -35,7 +35,7 @@
 {
 	while (node) {
 		js_Iterator *next = node->next;
-		free(node);
+		js_free(J, node);
 		node = next;
 	}
 }
@@ -48,7 +48,7 @@
 		js_regfree(obj->u.r.prog);
 	if (obj->type == JS_CITERATOR)
 		jsG_freeiterator(J, obj->u.iter.head);
-	free(obj);
+	js_free(J, obj);
 }
 
 static void jsG_markfunction(js_State *J, int mark, js_Function *fun)
@@ -203,7 +203,7 @@
 
 	jsS_freestrings(J);
 
-	free(J->lexbuf.text);
+	js_free(J, J->lexbuf.text);
 	free(J->stack);
 	free(J);
 }
--- a/jsi.h
+++ b/jsi.h
@@ -29,6 +29,10 @@
 
 #define nelem(a) (sizeof (a) / sizeof (a)[0])
 
+void *js_malloc(js_State *J, size_t size);
+void *js_realloc(js_State *J, void *ptr, size_t size);
+void js_free(js_State *J, void *ptr);
+
 typedef struct js_Regexp js_Regexp;
 typedef struct js_Value js_Value;
 typedef struct js_Object js_Object;
--- a/jsintern.c
+++ b/jsintern.c
@@ -11,10 +11,10 @@
 
 static js_StringNode jsS_sentinel = { &jsS_sentinel, &jsS_sentinel, 0, ""};
 
-static js_StringNode *jsS_newstringnode(const char *string, const char **result)
+static js_StringNode *jsS_newstringnode(js_State *J, const char *string, const char **result)
 {
 	int n = strlen(string);
-	js_StringNode *node = malloc(offsetof(js_StringNode, string) + n + 1);
+	js_StringNode *node = js_malloc(J, offsetof(js_StringNode, string) + n + 1);
 	node->left = node->right = &jsS_sentinel;
 	node->level = 1;
 	strcpy(node->string, string);
@@ -44,14 +44,14 @@
 	return node;
 }
 
-static js_StringNode *jsS_insert(js_StringNode *node, const char *string, const char **result)
+static js_StringNode *jsS_insert(js_State *J, js_StringNode *node, const char *string, const char **result)
 {
 	if (node != &jsS_sentinel) {
 		int c = strcmp(string, node->string);
 		if (c < 0)
-			node->left = jsS_insert(node->left, string, result);
+			node->left = jsS_insert(J, node->left, string, result);
 		else if (c > 0)
-			node->right = jsS_insert(node->right, string, result);
+			node->right = jsS_insert(J, node->right, string, result);
 		else
 			return *result = node->string, node;
 		node = jsS_skew(node);
@@ -58,7 +58,7 @@
 		node = jsS_split(node);
 		return node;
 	}
-	return jsS_newstringnode(string, result);
+	return jsS_newstringnode(J, string, result);
 }
 
 static void dumpstringnode(js_StringNode *node, int level)
@@ -87,7 +87,7 @@
 {
 	if (node->left != &jsS_sentinel) jsS_freestringnode(J, node->left);
 	if (node->right != &jsS_sentinel) jsS_freestringnode(J, node->right);
-	free(node);
+	js_free(J, node);
 }
 
 void jsS_freestrings(js_State *J)
@@ -101,6 +101,6 @@
 	const char *result;
 	if (!J->strings)
 		J->strings = &jsS_sentinel;
-	J->strings = jsS_insert(J->strings, s, &result);
+	J->strings = jsS_insert(J, J->strings, s, &result);
 	return result;
 }
--- a/jslex.c
+++ b/jslex.c
@@ -185,7 +185,7 @@
 {
 	if (!J->lexbuf.text) {
 		J->lexbuf.cap = 4096;
-		J->lexbuf.text = malloc(J->lexbuf.cap);
+		J->lexbuf.text = js_malloc(J, J->lexbuf.cap);
 	}
 	J->lexbuf.len = 0;
 }
@@ -195,7 +195,7 @@
 	int n = runelen(c);
 	if (J->lexbuf.len + n > J->lexbuf.cap) {
 		J->lexbuf.cap = J->lexbuf.cap * 2;
-		J->lexbuf.text = realloc(J->lexbuf.text, J->lexbuf.cap);
+		J->lexbuf.text = js_realloc(J, J->lexbuf.text, J->lexbuf.cap);
 	}
 	J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c);
 }
--- a/json.c
+++ b/json.c
@@ -101,46 +101,46 @@
 	// TODO: reviver Walk()
 }
 
-static void fmtnum(js_Buffer **sb, double n)
+static void fmtnum(js_State *J, js_Buffer **sb, double n)
 {
-	if (isnan(n)) sb_puts(sb, "NaN");
-	else if (isinf(n)) sb_puts(sb, n < 0 ? "-Infinity" : "Infinity");
-	else if (n == 0) sb_puts(sb, "0");
+	if (isnan(n)) js_puts(J, sb, "NaN");
+	else if (isinf(n)) js_puts(J, sb, n < 0 ? "-Infinity" : "Infinity");
+	else if (n == 0) js_puts(J, sb, "0");
 	else {
 		char buf[40];
 		sprintf(buf, "%.17g", n);
-		sb_puts(sb, buf);
+		js_puts(J, sb, buf);
 	}
 }
 
-static void fmtstr(js_Buffer **sb, const char *s)
+static void fmtstr(js_State *J, js_Buffer **sb, const char *s)
 {
 	static const char *HEX = "0123456789ABCDEF";
 	Rune c;
-	sb_putc(sb, '"');
+	js_putc(J, sb, '"');
 	while (*s) {
 		s += chartorune(&c, s);
 		switch (c) {
-		case '"': sb_puts(sb, "\\\""); break;
-		case '\\': sb_puts(sb, "\\\\"); break;
-		case '\b': sb_puts(sb, "\\b"); break;
-		case '\f': sb_puts(sb, "\\f"); break;
-		case '\n': sb_puts(sb, "\\n"); break;
-		case '\r': sb_puts(sb, "\\r"); break;
-		case '\t': sb_puts(sb, "\\t"); break;
+		case '"': js_puts(J, sb, "\\\""); break;
+		case '\\': js_puts(J, sb, "\\\\"); break;
+		case '\b': js_puts(J, sb, "\\b"); break;
+		case '\f': js_puts(J, sb, "\\f"); break;
+		case '\n': js_puts(J, sb, "\\n"); break;
+		case '\r': js_puts(J, sb, "\\r"); break;
+		case '\t': js_puts(J, sb, "\\t"); break;
 		default:
 			if (c < ' ') {
-				sb_puts(sb, "\\u");
-				sb_putc(sb, HEX[(c>>12)&15]);
-				sb_putc(sb, HEX[(c>>8)&15]);
-				sb_putc(sb, HEX[(c>>4)&15]);
-				sb_putc(sb, HEX[c&15]);
+				js_puts(J, sb, "\\u");
+				js_putc(J, sb, HEX[(c>>12)&15]);
+				js_putc(J, sb, HEX[(c>>8)&15]);
+				js_putc(J, sb, HEX[(c>>4)&15]);
+				js_putc(J, sb, HEX[c&15]);
 			} else {
-				sb_putc(sb, c); break;
+				js_putc(J, sb, c); break;
 			}
 		}
 	}
-	sb_putc(sb, '"');
+	js_putc(J, sb, '"');
 }
 
 static int fmtvalue(js_State *J, js_Buffer **sb, const char *key);
@@ -151,14 +151,14 @@
 	int save;
 	int n = 0;
 
-	sb_putc(sb, '{');
+	js_putc(J, sb, '{');
 	for (ref = obj->head; ref; ref = ref->next) {
 		if (ref->atts & JS_DONTENUM)
 			continue;
 		save = (*sb)->n;
-		if (n) sb_putc(sb, ',');
-		fmtstr(sb, ref->name);
-		sb_putc(sb, ':');
+		if (n) js_putc(J, sb, ',');
+		fmtstr(J, sb, ref->name);
+		js_putc(J, sb, ':');
 		js_pushvalue(J, ref->value);
 		if (!fmtvalue(J, sb, ref->name))
 			(*sb)->n = save;
@@ -166,7 +166,7 @@
 			++n;
 		js_pop(J, 1);
 	}
-	sb_putc(sb, '}');
+	js_putc(J, sb, '}');
 }
 
 static void fmtarray(js_State *J, js_Buffer **sb)
@@ -178,22 +178,22 @@
 	len = js_touint32(J, -1);
 	js_pop(J, 1);
 
-	sb_putc(sb, '[');
+	js_putc(J, sb, '[');
 	for (k = 0; k < len; ++k) {
-		if (k) sb_putc(sb, ',');
+		if (k) js_putc(J, sb, ',');
 		sprintf(buf, "%u", k);
 		js_getproperty(J, -1, buf);
 		if (!fmtvalue(J, sb, buf))
-			sb_puts(sb, "null");
+			js_puts(J, sb, "null");
 		js_pop(J, 1);
 	}
-	sb_putc(sb, ']');
+	js_putc(J, sb, ']');
 }
 
 static int fmtvalue(js_State *J, js_Buffer **sb, const char *key)
 {
 	if (js_try(J)) {
-		free(*sb);
+		js_free(J, *sb);
 		js_throw(J);
 	}
 	if (js_isobject(J, -1)) {
@@ -215,21 +215,21 @@
 	if (js_isobject(J, -1) && !js_iscallable(J, -1)) {
 		js_Object *obj = js_toobject(J, -1);
 		switch (obj->type) {
-		case JS_CNUMBER: fmtnum(sb, obj->u.number); break;
-		case JS_CSTRING: fmtstr(sb, obj->u.s.string); break;
-		case JS_CBOOLEAN: sb_puts(sb, obj->u.boolean ? "true" : "false"); break;
+		case JS_CNUMBER: fmtnum(J, sb, obj->u.number); break;
+		case JS_CSTRING: fmtstr(J, sb, obj->u.s.string); break;
+		case JS_CBOOLEAN: js_puts(J, sb, obj->u.boolean ? "true" : "false"); break;
 		case JS_CARRAY: fmtarray(J, sb); break;
 		default: fmtobject(J, sb, obj); break;
 		}
 	}
 	else if (js_isboolean(J, -1))
-		sb_puts(sb, js_toboolean(J, -1) ? "true" : "false");
+		js_puts(J, sb, js_toboolean(J, -1) ? "true" : "false");
 	else if (js_isnumber(J, -1))
-		fmtnum(sb, js_tonumber(J, -1));
+		fmtnum(J, sb, js_tonumber(J, -1));
 	else if (js_isstring(J, -1))
-		fmtstr(sb, js_tostring(J, -1));
+		fmtstr(J, sb, js_tostring(J, -1));
 	else if (js_isnull(J, -1))
-		sb_puts(sb, "null");
+		js_puts(J, sb, "null");
 	else
 		return 0;
 
@@ -242,14 +242,14 @@
 	if (argc > 0) {
 		js_copy(J, 1);
 		if (fmtvalue(J, &sb, "")) {
-			sb_putc(&sb, 0);
+			js_putc(J, &sb, 0);
 			if (js_try(J)) {
-				free(sb);
+				js_free(J, sb);
 				js_throw(J);
 			}
 			js_pushstring(J, sb ? sb->s : "");
 			js_endtry(J);
-			free(sb);
+			js_free(J, sb);
 		}
 	} else {
 		js_pushundefined(J);
--- a/jsparse.c
+++ b/jsparse.c
@@ -54,7 +54,7 @@
 
 static js_Ast *jsP_newnode(js_State *J, int type, js_Ast *a, js_Ast *b, js_Ast *c, js_Ast *d)
 {
-	js_Ast *node = malloc(sizeof(js_Ast));
+	js_Ast *node = js_malloc(J, sizeof *node);
 
 	node->type = type;
 	node->line = J->lexline;
@@ -109,7 +109,7 @@
 {
 	while (node) {
 		js_JumpList *next = node->next;
-		free(node);
+		js_free(J, node);
 		node = next;
 	}
 }
@@ -120,7 +120,7 @@
 	while (node) {
 		js_Ast *next = node->gcnext;
 		jsP_freejumps(J, node->jumps);
-		free(node);
+		js_free(J, node);
 		node = next;
 	}
 	J->gcast = NULL;
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -29,7 +29,7 @@
 
 static js_Property *newproperty(js_State *J, const char *name)
 {
-	js_Property *node = malloc(sizeof(js_Property));
+	js_Property *node = js_malloc(J, sizeof *node);
 	node->name = js_intern(J, name);
 	node->left = node->right = &sentinel;
 	node->prevp = NULL;
@@ -97,12 +97,12 @@
 	return *result = newproperty(J, name);
 }
 
-static void freenode(js_State *J, js_Property *node)
+static void freeproperty(js_State *J, js_Property *node)
 {
 	if (node->next)
 		node->next->prevp = node->prevp;
 	*node->prevp = node->next;
-	free(node);
+	js_free(J, node);
 }
 
 static js_Property *delete(js_State *J, js_Property *node, const char *name)
@@ -119,11 +119,11 @@
 			if (node->left == &sentinel) {
 				temp = node;
 				node = node->right;
-				freenode(J, temp);
+				freeproperty(J, temp);
 			} else if (node->right == &sentinel) {
 				temp = node;
 				node = node->left;
-				freenode(J, temp);
+				freeproperty(J, temp);
 			} else {
 				succ = node->right;
 				while (succ->left != &sentinel)
@@ -153,7 +153,8 @@
 
 js_Object *jsV_newobject(js_State *J, enum js_Class type, js_Object *prototype)
 {
-	js_Object *obj = calloc(sizeof(js_Object), 1);
+	js_Object *obj = js_malloc(J, sizeof *obj);
+	memset(obj, 0, sizeof *obj);
 	obj->gcmark = 0;
 	obj->gcnext = J->gcobj;
 	J->gcobj = obj;
@@ -246,7 +247,7 @@
 	unsigned int k;
 
 #define ITADD(x) \
-	js_Iterator *node = malloc(sizeof *node); \
+	js_Iterator *node = js_malloc(J, sizeof *node); \
 	node->name = x; \
 	node->next = NULL; \
 	if (!tail) { \
@@ -297,7 +298,7 @@
 	while (io->u.iter.head) {
 		js_Iterator *next = io->u.iter.head->next;
 		const char *name = io->u.iter.head->name;
-		free(io->u.iter.head);
+		js_free(J, io->u.iter.head);
 		io->u.iter.head = next;
 		if (jsV_getproperty(J, io->u.iter.target, name))
 			return name;
--- a/jsregexp.c
+++ b/jsregexp.c
@@ -154,7 +154,7 @@
 
 	re = js_toregexp(J, 0);
 
-	out = malloc(strlen(re->source) + 6); /* extra space for //gim */
+	out = js_malloc(J, strlen(re->source) + 6); /* extra space for //gim */
 	strcpy(out, "/");
 	strcat(out, re->source);
 	strcat(out, "/");
@@ -163,13 +163,13 @@
 	if (re->flags & JS_REGEXP_M) strcat(out, "m");
 
 	if (js_try(J)) {
-		free(out);
+		js_free(J, out);
 		js_throw(J);
 	}
 	js_pop(J, 0);
 	js_pushstring(J, out);
 	js_endtry(J);
-	free(out);
+	js_free(J, out);
 }
 
 static void Rp_exec(js_State *J, unsigned int argc)
--- a/jsrun.c
+++ b/jsrun.c
@@ -21,6 +21,35 @@
 	js_throw(J);
 }
 
+static void js_outofmemory(js_State *J)
+{
+	STACK[TOP].type = JS_TSTRING;
+	STACK[TOP].u.string = "out of memory";
+	++TOP;
+	js_throw(J);
+}
+
+void *js_malloc(js_State *J, size_t size)
+{
+	void *ptr = malloc(size);
+	if (!ptr)
+		js_outofmemory(J);
+	return ptr;
+}
+
+void *js_realloc(js_State *J, void *ptr, size_t size)
+{
+	ptr = realloc(ptr, size);
+	if (!ptr)
+		js_outofmemory(J);
+	return ptr;
+}
+
+void js_free(js_State *J, void *ptr)
+{
+	free(ptr);
+}
+
 #define CHECKSTACK(n) if (TOP + n >= JS_STACKSIZE) js_stackoverflow(J)
 
 void js_pushvalue(js_State *J, js_Value v)
@@ -76,16 +105,16 @@
 		buf[n] = 0;
 		js_pushstring(J, buf);
 	} else {
-		char *s = malloc(n + 1);
+		char *s = js_malloc(J, n + 1);
 		memcpy(s, v, n);
 		s[n] = 0;
 		if (js_try(J)) {
-			free(s);
+			js_free(J, s);
 			js_throw(J);
 		}
 		js_pushstring(J, s);
 		js_endtry(J);
-		free(s);
+		js_free(J, s);
 	}
 }
 
@@ -680,7 +709,7 @@
 
 js_Environment *jsR_newenvironment(js_State *J, js_Object *vars, js_Environment *outer)
 {
-	js_Environment *E = malloc(sizeof *E);
+	js_Environment *E = js_malloc(J, sizeof *E);
 	E->gcmark = 0;
 	E->gcnext = J->gcenv;
 	J->gcenv = E;
--- a/jsstate.c
+++ b/jsstate.c
@@ -41,7 +41,7 @@
 	n = ftell(f);
 	fseek(f, 0, SEEK_SET);
 
-	s = malloc(n + 1); /* add space for string terminator */
+	s = js_malloc(J, n + 1); /* add space for string terminator */
 	if (!s) {
 		fclose(f);
 		js_error(J, "cannot allocate storage for file contents: '%s'", filename);
@@ -49,7 +49,7 @@
 
 	t = fread(s, 1, n, f);
 	if (t != n) {
-		free(s);
+		js_free(J, s);
 		fclose(f);
 		js_error(J, "cannot read data from file: '%s'", filename);
 	}
@@ -57,7 +57,7 @@
 	s[n] = 0; /* zero-terminate string containing file data */
 
 	if (js_try(J)) {
-		free(s);
+		js_free(J, s);
 		fclose(f);
 		js_throw(J);
 	}
@@ -64,7 +64,7 @@
 
 	js_loadstring(J, filename, s);
 
-	free(s);
+	js_free(J, s);
 	fclose(f);
 	js_endtry(J);
 }
@@ -104,9 +104,15 @@
 js_State *js_newstate(void)
 {
 	js_State *J = malloc(sizeof *J);
+	if (!J)
+		return NULL;
 	memset(J, 0, sizeof(*J));
 
 	J->stack = malloc(JS_STACKSIZE * sizeof *J->stack);
+	if (!J->stack) {
+		free(J);
+		return NULL;
+	}
 
 	J->gcmark = 1;
 	J->nextref = 0;
--- a/jsstring.c
+++ b/jsstring.c
@@ -108,11 +108,11 @@
 
 	s = js_tostring(J, 0);
 	n = strlen(s);
-	out = malloc(n + 1);
+	out = js_malloc(J, n + 1);
 	strcpy(out, s);
 
 	if (js_try(J)) {
-		free(out);
+		js_free(J, out);
 		js_throw(J);
 	}
 
@@ -125,7 +125,7 @@
 
 	js_pushstring(J, out);
 	js_endtry(J);
-	free(out);
+	js_free(J, out);
 }
 
 static void Sp_indexOf(js_State *J, unsigned int argc)
@@ -221,7 +221,7 @@
 static void Sp_toLowerCase(js_State *J, unsigned int argc)
 {
 	const char *src = js_tostring(J, 0);
-	char *dst = malloc(UTFmax * strlen(src) + 1);
+	char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
 	const char *s = src;
 	char *d = dst;
 	Rune rune;
@@ -232,18 +232,18 @@
 	}
 	*d = 0;
 	if (js_try(J)) {
-		free(dst);
+		js_free(J, dst);
 		js_throw(J);
 	}
 	js_pushstring(J, dst);
 	js_endtry(J);
-	free(dst);
+	js_free(J, dst);
 }
 
 static void Sp_toUpperCase(js_State *J, unsigned int argc)
 {
 	const char *src = js_tostring(J, 0);
-	char *dst = malloc(UTFmax * strlen(src) + 1);
+	char *dst = js_malloc(J, UTFmax * strlen(src) + 1);
 	const char *s = src;
 	char *d = dst;
 	Rune rune;
@@ -254,12 +254,12 @@
 	}
 	*d = 0;
 	if (js_try(J)) {
-		free(dst);
+		js_free(J, dst);
 		js_throw(J);
 	}
 	js_pushstring(J, dst);
 	js_endtry(J);
-	free(dst);
+	js_free(J, dst);
 }
 
 static int istrim(int c)
@@ -286,10 +286,10 @@
 	Rune c;
 	char *s, *p;
 
-	s = p = malloc(argc * UTFmax + 1);
+	s = p = js_malloc(J, argc * UTFmax + 1);
 
 	if (js_try(J)) {
-		free(s);
+		js_free(J, s);
 		js_throw(J);
 	}
 
@@ -301,7 +301,7 @@
 	js_pushstring(J, s);
 
 	js_endtry(J);
-	free(s);
+	js_free(J, s);
 }
 
 static void Sp_match(js_State *J, unsigned int argc)
@@ -404,20 +404,20 @@
 		js_copy(J, 0); /* arg x+3: search string */
 		js_call(J, 2 + x);
 		r = js_tostring(J, -1);
-		sb_putm(&sb, source, s);
-		sb_puts(&sb, r);
+		js_putm(J, &sb, source, s);
+		js_puts(J, &sb, r);
 		js_pop(J, 1);
 	} else {
 		r = js_tostring(J, 2);
-		sb_putm(&sb, source, s);
+		js_putm(J, &sb, source, s);
 		while (*r) {
 			if (*r == '$') {
 				switch (*(++r)) {
-				case '$': sb_putc(&sb, '$'); break;
-				case '`': sb_putm(&sb, source, s); break;
-				case '\'': sb_puts(&sb, s + n); break;
+				case '$': js_putc(J, &sb, '$'); break;
+				case '`': js_putm(J, &sb, source, s); break;
+				case '\'': js_puts(J, &sb, s + n); break;
 				case '&':
-					sb_putm(&sb, s, s + n);
+					js_putm(J, &sb, s, s + n);
 					break;
 				case '0': case '1': case '2': case '3': case '4':
 				case '5': case '6': case '7': case '8': case '9':
@@ -426,25 +426,25 @@
 						x = x * 10 + *(++r) - '0';
 					// TODO: use prog->nsub somehow
 					if (x > 0 && x < m.nsub) {
-						sb_putm(&sb, m.sub[x].sp, m.sub[x].ep);
+						js_putm(J, &sb, m.sub[x].sp, m.sub[x].ep);
 					} else {
-						sb_putc(&sb, '$');
+						js_putc(J, &sb, '$');
 						if (x > 10) {
-							sb_putc(&sb, '0' + x / 10);
-							sb_putc(&sb, '0' + x % 10);
+							js_putc(J, &sb, '0' + x / 10);
+							js_putc(J, &sb, '0' + x % 10);
 						} else {
-							sb_putc(&sb, '0' + x);
+							js_putc(J, &sb, '0' + x);
 						}
 					}
 					break;
 				default:
-					sb_putc(&sb, '$');
-					sb_putc(&sb, *r);
+					js_putc(J, &sb, '$');
+					js_putc(J, &sb, *r);
 					break;
 				}
 				++r;
 			} else {
-				sb_putc(&sb, *r++);
+				js_putc(J, &sb, *r++);
 			}
 		}
 	}
@@ -453,7 +453,7 @@
 		source = m.sub[0].ep;
 		if (n == 0) {
 			if (*source)
-				sb_putc(&sb, *source++);
+				js_putc(J, &sb, *source++);
 			else
 				goto end;
 		}
@@ -462,16 +462,16 @@
 	}
 
 end:
-	sb_puts(&sb, s + n);
-	sb_putc(&sb, 0);
+	js_puts(J, &sb, s + n);
+	js_putc(J, &sb, 0);
 
 	if (js_try(J)) {
-		free(sb);
+		js_free(J, sb);
 		js_throw(J);
 	}
 	js_pushstring(J, sb ? sb->s : "");
 	js_endtry(J);
-	free(sb);
+	js_free(J, sb);
 }
 
 static void Sp_replace_string(js_State *J, unsigned int argc)
@@ -498,39 +498,39 @@
 		js_copy(J, 0); /* arg 3: search string */
 		js_call(J, 3);
 		r = js_tostring(J, -1);
-		sb_putm(&sb, source, s);
-		sb_puts(&sb, r);
-		sb_puts(&sb, s + n);
-		sb_putc(&sb, 0);
+		js_putm(J, &sb, source, s);
+		js_puts(J, &sb, r);
+		js_puts(J, &sb, s + n);
+		js_putc(J, &sb, 0);
 		js_pop(J, 1);
 	} else {
 		r = js_tostring(J, 2);
-		sb_putm(&sb, source, s);
+		js_putm(J, &sb, source, s);
 		while (*r) {
 			if (*r == '$') {
 				switch (*(++r)) {
-				case '$': sb_putc(&sb, '$'); break;
-				case '&': sb_putm(&sb, s, s + n); break;
-				case '`': sb_putm(&sb, source, s); break;
-				case '\'': sb_puts(&sb, s + n); break;
-				default: sb_putc(&sb, '$'); sb_putc(&sb, *r); break;
+				case '$': js_putc(J, &sb, '$'); break;
+				case '&': js_putm(J, &sb, s, s + n); break;
+				case '`': js_putm(J, &sb, source, s); break;
+				case '\'': js_puts(J, &sb, s + n); break;
+				default: js_putc(J, &sb, '$'); js_putc(J, &sb, *r); break;
 				}
 				++r;
 			} else {
-				sb_putc(&sb, *r++);
+				js_putc(J, &sb, *r++);
 			}
 		}
-		sb_puts(&sb, s + n);
-		sb_putc(&sb, 0);
+		js_puts(J, &sb, s + n);
+		js_putc(J, &sb, 0);
 	}
 
 	if (js_try(J)) {
-		free(sb);
+		js_free(J, sb);
 		js_throw(J);
 	}
 	js_pushstring(J, sb ? sb->s : "");
 	js_endtry(J);
-	free(sb);
+	js_free(J, sb);
 }
 
 static void Sp_replace(js_State *J, unsigned int argc)
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -378,17 +378,17 @@
 	if (va.type == JS_TSTRING || vb.type == JS_TSTRING) {
 		const char *sa = jsV_tostring(J, &va);
 		const char *sb = jsV_tostring(J, &vb);
-		char *sab = malloc(strlen(sa) + strlen(sb) + 1);
+		char *sab = js_malloc(J, strlen(sa) + strlen(sb) + 1);
 		strcpy(sab, sa);
 		strcat(sab, sb);
 		if (js_try(J)) {
-			free(sab);
+			js_free(J, sab);
 			js_throw(J);
 		}
 		js_pop(J, 2);
 		js_pushstring(J, sab);
 		js_endtry(J);
-		free(sab);
+		js_free(J, sab);
 	} else {
 		double x = jsV_tonumber(J, &va);
 		double y = jsV_tonumber(J, &vb);