shithub: libmujs

Download patch

ref: 8aa062755760b61f9131a0411e9125bafddc0a4f
parent: 3c58fa90c8fd063c7ccb00a85a7e238ae2788a73
author: Tor Andersson <tor@ccxvii.net>
date: Sun Jan 19 19:13:47 EST 2014

Add Error builtin objects and exception stack.

--- a/js.h
+++ b/js.h
@@ -40,10 +40,8 @@
 js_State *js_newstate(void);
 void js_close(js_State *J);
 
-int js_error(js_State *J, const char *fmt, ...);
-
-int js_loadstring(js_State *J, const char *source);
-int js_loadfile(js_State *J, const char *filename);
+void js_loadstring(js_State *J, const char *source);
+void js_loadfile(js_State *J, const char *filename);
 int js_dostring(js_State *J, const char *source);
 int js_dofile(js_State *J, const char *filename);
 
@@ -71,6 +69,18 @@
 void jsB_initboolean(js_State *J);
 void jsB_initnumber(js_State *J);
 void jsB_initstring(js_State *J);
+void jsB_initerror(js_State *J);
 void jsB_initmath(js_State *J);
+
+JS_NORETURN void js_throw(js_State *J);
+JS_NORETURN void js_error(js_State *J, const char *fmt, ...) __printflike(2,3);
+
+JS_NORETURN void jsR_throwError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwEvalError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwRangeError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwReferenceError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwSyntaxError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwTypeError(js_State *J, const char *message);
+JS_NORETURN void jsR_throwURIError(js_State *J, const char *message);
 
 #endif
--- a/jsbboolean.c
+++ b/jsbboolean.c
@@ -18,7 +18,7 @@
 static int Bp_toString(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CBOOLEAN) jsR_error(J, "TypeError");
+	if (self->type != JS_CBOOLEAN) jsR_throwTypeError(J, "not a boolean");
 	js_pushliteral(J, self->u.boolean ? "true" : "false");
 	return 1;
 }
@@ -26,7 +26,7 @@
 static int Bp_valueOf(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CBOOLEAN) jsR_error(J, "TypeError");
+	if (self->type != JS_CBOOLEAN) jsR_throwTypeError(J, "not a boolean");
 	js_pushboolean(J, self->u.boolean);
 	return 1;
 }
--- /dev/null
+++ b/jsberror.c
@@ -1,0 +1,86 @@
+#include "js.h"
+#include "jsobject.h"
+#include "jsrun.h"
+#include "jsstate.h"
+
+static int Ep_toString(js_State *J, int n)
+{
+	js_getproperty(J, 0, "name");
+	js_pushliteral(J, ": ");
+	jsR_concat(J);
+	js_getproperty(J, 0, "message");
+	jsR_concat(J);
+	return 1;
+}
+
+#define STRSTR(X) #X
+#define STR(X) STRSTR(X)
+
+#define DECL(NAME) \
+	static int jsB_new_##NAME(js_State *J, int n) { \
+		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		if (n > 0) { \
+			js_pushstring(J, js_tostring(J, 0)); \
+			js_setproperty(J, -2, "message"); \
+		} \
+		return 1; \
+	} \
+	static int jsB_##NAME(js_State *J, int n) { \
+		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		if (n > 1) { \
+			js_pushstring(J, js_tostring(J, 1)); \
+			js_setproperty(J, -2, "message"); \
+		} \
+		return 1; \
+	} \
+	static void jsB_init##NAME(js_State *J) { \
+		js_pushobject(J, jsR_newcconstructor(J, jsB_##NAME, jsB_new_##NAME)); \
+		{ \
+			jsB_propn(J, "length", 1); \
+			js_pushobject(J, J->NAME##_prototype); \
+			{ \
+				js_copy(J, -2); \
+				js_setproperty(J, -2, "constructor"); \
+				jsB_props(J, "name", STR(NAME)); \
+				jsB_props(J, "message", "an error has occurred"); \
+				jsB_propf(J, "toString", Ep_toString, 0); \
+			} \
+			js_setproperty(J, -2, "prototype"); \
+		} \
+		js_setglobal(J, STR(NAME)); \
+	} \
+	void jsR_throw##NAME(js_State *J, const char *message) { \
+		js_pushobject(J, jsR_newobject(J, JS_CERROR, J->NAME##_prototype)); \
+		js_pushstring(J, message); \
+		js_setproperty(J, -2, "message"); \
+		js_throw(J); \
+	} \
+
+DECL(Error);
+DECL(EvalError);
+DECL(RangeError);
+DECL(ReferenceError);
+DECL(SyntaxError);
+DECL(TypeError);
+DECL(URIError);
+
+void jsB_initerror(js_State *J)
+{
+	jsB_initError(J);
+	jsB_initEvalError(J);
+	jsB_initRangeError(J);
+	jsB_initReferenceError(J);
+	jsB_initSyntaxError(J);
+	jsB_initTypeError(J);
+	jsB_initURIError(J);
+}
+
+void js_error(js_State *J, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[256];
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof buf, fmt, ap);
+	va_end(ap);
+	jsR_throwError(J, buf);
+}
--- a/jsbfunction.c
+++ b/jsbfunction.c
@@ -20,7 +20,7 @@
 	int i, n;
 
 	if (!js_iscallable(J, 0))
-		jsR_error(J, "TypeError");
+		jsR_throwTypeError(J, "not a function");
 
 	if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) {
 		js_Function *F = self->u.f.function;
@@ -52,7 +52,7 @@
 	char name[20];
 
 	if (!js_iscallable(J, 0))
-		jsR_error(J, "TypeError: not a function");
+		jsR_throwTypeError(J, "not a function");
 
 	js_copy(J, 0);
 	js_copy(J, 1);
@@ -75,7 +75,7 @@
 	int i;
 
 	if (!js_iscallable(J, 0))
-		jsR_error(J, "TypeError: not a function");
+		jsR_throwTypeError(J, "not a function");
 
 	js_copy(J, 0);
 	js_copy(J, 1);
--- a/jsbnumber.c
+++ b/jsbnumber.c
@@ -18,7 +18,7 @@
 static int Np_valueOf(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CNUMBER) jsR_error(J, "TypeError");
+	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
 	js_pushnumber(J, self->u.number);
 	return 1;
 }
@@ -26,7 +26,7 @@
 static int Np_toString(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CNUMBER) jsR_error(J, "TypeError");
+	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
 	js_pushliteral(J, jsR_stringfromnumber(J, self->u.number));
 	return 1;
 }
@@ -36,7 +36,7 @@
 	char buf[40];
 	js_Object *self = js_toobject(J, 0);
 	int width = js_tonumber(J, 1);
-	if (self->type != JS_CNUMBER) jsR_error(J, "TypeError");
+	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
 	sprintf(buf, "%.*f", width, self->u.number);
 	js_pushstring(J, buf);
 	return 1;
@@ -47,7 +47,7 @@
 	char buf[40];
 	js_Object *self = js_toobject(J, 0);
 	int width = js_tonumber(J, 1);
-	if (self->type != JS_CNUMBER) jsR_error(J, "TypeError");
+	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
 	sprintf(buf, "%.*e", width, self->u.number);
 	js_pushstring(J, buf);
 	return 1;
@@ -58,7 +58,7 @@
 	char buf[40];
 	js_Object *self = js_toobject(J, 0);
 	int width = js_tonumber(J, 1);
-	if (self->type != JS_CNUMBER) jsR_error(J, "TypeError");
+	if (self->type != JS_CNUMBER) jsR_throwTypeError(J, "not a number");
 	sprintf(buf, "%.*g", width, self->u.number);
 	js_pushstring(J, buf);
 	return 1;
--- a/jsbstring.c
+++ b/jsbstring.c
@@ -19,7 +19,7 @@
 static int Sp_toString(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CSTRING) jsR_error(J, "TypeError");
+	if (self->type != JS_CSTRING) jsR_throwTypeError(J, "not a string");
 	js_pushliteral(J, self->u.string);
 	return 1;
 }
@@ -27,7 +27,7 @@
 static int Sp_valueOf(js_State *J, int n)
 {
 	js_Object *self = js_toobject(J, 0);
-	if (self->type != JS_CSTRING) jsR_error(J, "TypeError");
+	if (self->type != JS_CSTRING) jsR_throwTypeError(J, "not a string");
 	js_pushliteral(J, self->u.string);
 	return 1;
 }
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -24,16 +24,10 @@
 
 static int jsB_eval(js_State *J, int argc)
 {
-	const char *s;
-
 	if (!js_isstring(J, -1))
 		return 1;
-
-	s = js_tostring(J, -1);
-	if (jsR_loadscript(J, "(eval)", s))
-		jsR_error(J, "SyntaxError (eval)");
-
-	js_copy(J, 0); /* copy this */
+	jsR_loadscript(J, "(eval)", js_tostring(J, -1));
+	js_copy(J, 0);
 	js_call(J, 0);
 	return 1;
 }
@@ -85,6 +79,12 @@
 	js_setproperty(J, -2, name);
 }
 
+void jsB_props(js_State *J, const char *name, const char *string)
+{
+	js_pushliteral(J, string);
+	js_setproperty(J, -2, name);
+}
+
 void jsB_init(js_State *J)
 {
 	/* Create the prototype objects here, before the constructors */
@@ -95,6 +95,15 @@
 	J->Number_prototype = jsR_newobject(J, JS_CNUMBER, J->Object_prototype);
 	J->String_prototype = jsR_newobject(J, JS_CSTRING, J->Object_prototype);
 
+	/* All the native error types */
+	J->Error_prototype = jsR_newobject(J, JS_CERROR, J->Object_prototype);
+	J->EvalError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->RangeError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->ReferenceError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->SyntaxError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->TypeError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+	J->URIError_prototype = jsR_newobject(J, JS_CERROR, J->Error_prototype);
+
 	/* Create the constructors and fill out the prototype objects */
 	jsB_initobject(J);
 	jsB_initarray(J);
@@ -102,6 +111,7 @@
 	jsB_initboolean(J);
 	jsB_initnumber(J);
 	jsB_initstring(J);
+	jsB_initerror(J);
 
 	jsB_initmath(J);
 
--- a/jscompile.c
+++ b/jscompile.c
@@ -742,25 +742,20 @@
 int jsC_error(js_State *J, js_Ast *node, const char *fmt, ...)
 {
 	va_list ap;
+	char buf[512];
+	char msgbuf[256];
 
-	fprintf(stderr, "%s:%d: error: ", J->filename, node->line);
 	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
+	vsnprintf(msgbuf, 256, fmt, ap);
 	va_end(ap);
-	fprintf(stderr, "\n");
 
-	longjmp(J->jb, 1);
+	snprintf(buf, 256, "%s:%d: ", J->filename, node->line);
+	strcat(buf, msgbuf);
+
+	jsR_throwSyntaxError(J, buf);
 }
 
 js_Function *jsC_compile(js_State *J, js_Ast *prog)
 {
-	js_Function *F;
-
-	if (setjmp(J->jb))
-		return NULL;
-
-	F = newfun(J, NULL, NULL, prog);
-
-	J->fun = NULL;
-	return F;
+	return newfun(J, NULL, NULL, prog);
 }
--- a/jsdump.c
+++ b/jsdump.c
@@ -683,6 +683,7 @@
 		case JS_CBOOLEAN: printf("boolean(%d)", v.u.object->u.boolean); break;
 		case JS_CNUMBER: printf("number(%g)", v.u.object->u.number); break;
 		case JS_CSTRING: printf("string('%s')", v.u.object->u.string); break;
+		case JS_CERROR: printf("error()"); break;
 		default: printf("<unknown %p>", v.u.object); break;
 		}
 		break;
--- a/jsparse.c
+++ b/jsparse.c
@@ -852,14 +852,17 @@
 int jsP_error(js_State *J, const char *fmt, ...)
 {
 	va_list ap;
+	char buf[512];
+	char msgbuf[256];
 
-	fprintf(stderr, "%s:%d: error: ", J->filename, J->lexline);
 	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
+	vsnprintf(msgbuf, 256, fmt, ap);
 	va_end(ap);
-	fprintf(stderr, "\n");
 
-	longjmp(J->jb, 1);
+	snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
+	strcat(buf, msgbuf);
+
+	jsR_throwSyntaxError(J, buf);
 }
 
 js_Ast *jsP_parse(js_State *J, const char *filename, const char *source)
@@ -867,11 +870,6 @@
 	js_Ast *p, *last;
 
 	jsP_initlex(J, filename, source);
-
-	if (setjmp(J->jb)) {
-		jsP_freeparse(J);
-		return NULL;
-	}
 
 	next(J);
 	p = script(J);
--- a/jsparse.h
+++ b/jsparse.h
@@ -121,6 +121,7 @@
 	const char *string;
 	js_Ast *gcnext; /* next in alloc list */
 };
+
 js_Ast *jsP_parse(js_State *J, const char *filename, const char *source);
 void jsP_optimize(js_State *J, js_Ast *prog);
 void jsP_freeparse(js_State *J);
--- a/jsrun.c
+++ b/jsrun.c
@@ -394,10 +394,10 @@
 	int rv = F(J, n + 1);
 	if (rv) {
 		js_Value v = js_tovalue(J, -1);
-		js_pop(J, TOP - BOT + 1);
+		TOP = --BOT; /* pop down to below function */
 		js_pushvalue(J, v);
 	} else {
-		js_pop(J, TOP - BOT + 1);
+		TOP = --BOT; /* pop down to below function */
 		js_pushundefined(J);
 	}
 }
@@ -414,7 +414,7 @@
 	else if (obj->type == JS_CCFUNCTION)
 		jsR_callcfunction(J, n, obj->u.c.function);
 	else
-		jsR_error(J, "TypeError (not a function)");
+		jsR_throwTypeError(J, "not a function");
 	BOT = savebot;
 }
 
@@ -449,6 +449,33 @@
 	js_call(J, n);
 }
 
+/* Exceptions */
+
+void js_savetry(js_State *J, short *pc)
+{
+	if (J->trylen == JS_TRYLIMIT)
+		js_error(J, "exception stack overflow");
+	J->trybuf[J->trylen].E = J->E;
+	J->trybuf[J->trylen].top = J->top;
+	J->trybuf[J->trylen].bot = J->bot;
+	J->trybuf[J->trylen].pc = pc;
+}
+
+void js_throw(js_State *J)
+{
+	if (J->trylen > 0) {
+		js_Value v = js_tovalue(J, -1);
+		--J->trylen;
+		J->E = J->trybuf[J->trylen].E;
+		J->top = J->trybuf[J->trylen].top;
+		J->bot = J->trybuf[J->trylen].bot;
+		js_pushvalue(J, v);
+		longjmp(J->trybuf[J->trylen].buf, 1);
+	}
+	fprintf(stderr, "libjs: uncaught exception!\n");
+	abort();
+}
+
 /* Main interpreter loop */
 
 void jsR_dumpstack(js_State *J)
@@ -546,7 +573,7 @@
 			if (ref)
 				js_pushvalue(J, ref->value);
 			else
-				jsR_error(J, "ReferenceError (%s)", str);
+				jsR_throwReferenceError(J, str);
 			break;
 
 		case OP_SETVAR:
@@ -783,40 +810,26 @@
 			return;
 
 		default:
-			fprintf(stderr, "illegal instruction: %d (pc=%d)\n", opcode, (int)(pc - F->code - 1));
-			return;
+			js_error(J, "illegal instruction: %d (pc=%d)", opcode, (int)(pc - F->code - 1));
 		}
 	}
 }
 
-int jsR_loadscript(js_State *J, const char *filename, const char *source)
+void jsR_loadscript(js_State *J, const char *filename, const char *source)
 {
 	js_Ast *P;
 	js_Function *F;
 
-	// TODO: push exception stack
+	if (js_try(J)) {
+		jsP_freeparse(J);
+		js_throw(J);
+	}
 
 	P = jsP_parse(J, filename, source);
-	if (!P) return 1;
 	jsP_optimize(J, P);
 	F = jsC_compile(J, P);
-
 	jsP_freeparse(J);
-	if (!F) return 1;
-
 	js_newscript(J, F);
-	return 0;
-}
 
-void jsR_error(js_State *J, const char *fmt, ...)
-{
-	va_list ap;
-
-	fprintf(stderr, "runtime error: ");
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-	fprintf(stderr, "\n");
-
-	longjmp(J->jb, 1);
+	js_endtry(J);
 }
--- a/jsrun.h
+++ b/jsrun.h
@@ -14,10 +14,11 @@
 void jsB_init(js_State *J);
 void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n);
 void jsB_propn(js_State *J, const char *name, double number);
+void jsB_props(js_State *J, const char *name, const char *string);
 
 js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer);
 js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor);
-int jsR_loadscript(js_State *J, const char *filename, const char *source);
+void jsR_loadscript(js_State *J, const char *filename, const char *source);
 void jsR_error(js_State *J, const char *fmt, ...);
 void js_pushobject(js_State *J, js_Object *v);
 js_Object *js_toobject(js_State *J, int idx);
@@ -74,6 +75,8 @@
 double js_tointeger(js_State *J, int idx);
 int js_toint32(js_State *J, int idx);
 unsigned int js_touint32(js_State *J, int idx);
+short js_toint16(js_State *J, int idx);
+unsigned short js_touint16(js_State *J, int idx);
 
 void js_pop(js_State *J, int n);
 void js_dup(js_State *J);
--- a/jsstate.c
+++ b/jsstate.c
@@ -3,12 +3,12 @@
 #include "jsrun.h"
 #include "jsstate.h"
 
-int js_loadstring(js_State *J, const char *source)
+void js_loadstring(js_State *J, const char *source)
 {
-	return jsR_loadscript(J, "(string)", source);
+	jsR_loadscript(J, "(string)", source);
 }
 
-int js_loadfile(js_State *J, const char *filename)
+void js_loadfile(js_State *J, const char *filename)
 {
 	FILE *f;
 	char *s;
@@ -16,12 +16,12 @@
 
 	f = fopen(filename, "r");
 	if (!f) {
-		return js_error(J, "cannot open file: '%s'", filename);
+		js_error(J, "cannot open file: '%s'", filename);
 	}
 
 	if (fseek(f, 0, SEEK_END) < 0) {
 		fclose(f);
-		return js_error(J, "cannot seek in file: '%s'", filename);
+		js_error(J, "cannot seek in file: '%s'", filename);
 	}
 	n = ftell(f);
 	fseek(f, 0, SEEK_SET);
@@ -29,7 +29,7 @@
 	s = malloc(n + 1); /* add space for string terminator */
 	if (!s) {
 		fclose(f);
-		return js_error(J, "cannot allocate storage for file contents: '%s'", filename);
+		js_error(J, "cannot allocate storage for file contents: '%s'", filename);
 	}
 
 	t = fread(s, 1, n, f);
@@ -36,42 +36,50 @@
 	if (t != n) {
 		free(s);
 		fclose(f);
-		return js_error(J, "cannot read data from file: '%s'", filename);
+		js_error(J, "cannot read data from file: '%s'", filename);
 	}
 
 	s[n] = 0; /* zero-terminate string containing file data */
 
-	t = jsR_loadscript(J, filename, s);
+	if (js_try(J)) {
+		free(s);
+		fclose(f);
+		js_throw(J);
+	}
 
+	jsR_loadscript(J, filename, s);
+
 	free(s);
 	fclose(f);
-	return t;
+	js_endtry(J);
 }
 
 int js_dostring(js_State *J, const char *source)
 {
-	int rv = js_loadstring(J, source);
-	if (!rv) {
-		if (setjmp(J->jb))
-			return 1;
-		js_pushglobal(J);
-		js_call(J, 0);
-		js_pop(J, 1);
+	if (js_try(J)) {
+		fprintf(stderr, "libjs: %s\n", js_tostring(J, -1));
+		return 1;
 	}
-	return rv;
+	js_loadstring(J, source);
+	js_pushglobal(J);
+	js_call(J, 0);
+	js_pop(J, 1);
+	js_endtry(J);
+	return 0;
 }
 
 int js_dofile(js_State *J, const char *filename)
 {
-	int rv = js_loadfile(J, filename);
-	if (!rv) {
-		if (setjmp(J->jb))
-			return 1;
-		js_pushglobal(J);
-		js_call(J, 0);
-		js_pop(J, 1);
+	if (js_try(J)) {
+		fprintf(stderr, "libjs: %s\n", js_tostring(J, -1));
+		return 1;
 	}
-	return rv;
+	js_loadfile(J, filename);
+	js_pushglobal(J);
+	js_call(J, 0);
+	js_pop(J, 1);
+	js_endtry(J);
+	return 0;
 }
 
 js_State *js_newstate(void)
@@ -87,19 +95,4 @@
 	jsB_init(J);
 
 	return J;
-}
-
-int js_error(js_State *J, const char *fmt, ...)
-{
-	va_list ap;
-
-	fprintf(stderr, "error: ");
-
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-
-	fprintf(stderr, "\n");
-
-	return 0;
 }
--- a/jsstate.h
+++ b/jsstate.h
@@ -4,12 +4,32 @@
 #include "jsobject.h" /* for js_Value */
 
 #define JS_STACKSIZE 256
+#define JS_TRYLIMIT 64
 #define JS_GCLIMIT 10000 /* run gc cycle every N allocations */
 
-struct js_State
+void js_savetry(js_State *J, short *pc);
+
+#define js_trypc(J, PC) \
+	(js_savetry(J, PC), setjmp(J->trybuf[J->trylen++].buf))
+
+#define js_try(J) \
+	(js_savetry(J, NULL), setjmp(J->trybuf[J->trylen++].buf))
+
+#define js_endtry(J) \
+	(--J->trylen)
+
+typedef struct js_Jumpbuf js_Jumpbuf;
+
+struct js_Jumpbuf
 {
-	jmp_buf jb; /* setjmp buffer for error handling in parser */
+	jmp_buf buf;
+	js_Environment *E;
+	int top, bot;
+	short *pc;
+};
 
+struct js_State
+{
 	js_StringNode *strings;
 
 	/* input */
@@ -31,11 +51,9 @@
 	js_Ast *gcast; /* list of allocated nodes to free after parsing */
 
 	/* compiler */
-	js_Function *fun; /* list of allocated functions to free on errors */
-
 	int strict;
 
-	/* runtime */
+	/* runtime environment */
 	js_Object *Object_prototype;
 	js_Object *Array_prototype;
 	js_Object *Function_prototype;
@@ -43,6 +61,14 @@
 	js_Object *Number_prototype;
 	js_Object *String_prototype;
 
+	js_Object *Error_prototype;
+	js_Object *EvalError_prototype;
+	js_Object *RangeError_prototype;
+	js_Object *ReferenceError_prototype;
+	js_Object *SyntaxError_prototype;
+	js_Object *TypeError_prototype;
+	js_Object *URIError_prototype;
+
 	js_Object *G;
 	js_Environment *E;
 
@@ -52,6 +78,10 @@
 	js_Environment *gcenv;
 	js_Function *gcfun;
 	js_Object *gcobj;
+
+	/* exception stack */
+	int trylen;
+	js_Jumpbuf trybuf[JS_TRYLIMIT];
 
 	/* execution stack */
 	int top, bot;
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -76,7 +76,7 @@
 			return vv;
 		}
 	}
-	jsR_error(J, "TypeError (ToPrimitive)");
+	jsR_throwTypeError(J, "cannot convert object to primitive");
 }
 
 int jsR_toboolean(js_State *J, const js_Value *v)
@@ -129,14 +129,14 @@
 js_Object *jsR_toobject(js_State *J, const js_Value *v)
 {
 	switch (v->type) {
-	case JS_TUNDEFINED: jsR_error(J, "TypeError (ToObject(undefined))");
-	case JS_TNULL: jsR_error(J, "TypeError (ToObject(null))");
+	case JS_TUNDEFINED: jsR_throwTypeError(J, "cannot convert undefined to object");
+	case JS_TNULL: jsR_throwTypeError(J, "cannot convert null to object");
 	case JS_TBOOLEAN: return jsR_newboolean(J, v->u.boolean);
 	case JS_TNUMBER: return jsR_newnumber(J, v->u.number);
 	case JS_TSTRING: return jsR_newstring(J, v->u.string);
 	case JS_TOBJECT: return v->u.object;
 	}
-	jsR_error(J, "TypeError (ToObject)");
+	jsR_throwTypeError(J, "cannot convert value to object");
 }
 
 void jsR_concat(js_State *J)