shithub: libmujs

Download patch

ref: 40a12fba0dd5bef0dfcdf3067ea021d3a221faf4
parent: 8aa062755760b61f9131a0411e9125bafddc0a4f
author: Tor Andersson <[email protected]>
date: Mon Jan 20 11:13:09 EST 2014

Split header into js.h public and jsi.h private. Start cleaning up
private function prefixes.

--- a/js.h
+++ b/js.h
@@ -1,86 +1,93 @@
 #ifndef js_h
 #define js_h
 
-#include <stdio.h>
-#include <stdlib.h>
-#include <stddef.h>
-#include <stdarg.h>
-#include <string.h>
-#include <setjmp.h>
-#include <math.h>
-#include <float.h>
+#include "jsconf.h"
 
-/* noreturn is a GCC extension */
-#ifdef __GNUC__
-#define JS_NORETURN __attribute__((noreturn))
-#else
-#ifdef _MSC_VER
-#define JS_NORETURN __declspec(noreturn)
-#else
-#define JS_NORETURN
-#endif
-#endif
-
-/* GCC can do type checking of printf strings */
-#ifndef __printflike
-#if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7
-#define __printflike(fmtarg, firstvararg) \
-	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
-#else
-#define __printflike(fmtarg, firstvararg)
-#endif
-#endif
-
 typedef struct js_State js_State;
+typedef int (*js_CFunction)(js_State *J, int argc);
 
-#define JS_REGEXP_G 1
-#define JS_REGEXP_I 2
-#define JS_REGEXP_M 4
+/* Basic functions */
 
 js_State *js_newstate(void);
-void js_close(js_State *J);
+void js_freestate(js_State *J);
 
-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);
 
 void js_gc(js_State *J, int report);
 
-/* binding API: TODO: move from jsrun.h */
+const char *js_intern(js_State *J, const char *s);
 
-typedef int (*js_CFunction)(js_State *J, int argc);
+/* Push a new Error object with the formatted message and throw it */
+JS_NORETURN void js_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
 
-/* private */
+/* Property attribute flags */
+enum {
+	JS_READONLY = 1,
+	JS_DONTENUM = 2,
+	JS_DONTDELETE = 4,
+};
 
-typedef struct js_Ast js_Ast;
-typedef struct js_Environment js_Environment;
-typedef struct js_Function js_Function;
-typedef struct js_Object js_Object;
-typedef struct js_StringNode js_StringNode;
+JS_NORETURN void js_throw(js_State *J);
 
-const char *js_intern(js_State *J, const char *s);
-void js_printstrings(js_State *J);
-void js_freestrings(js_State *J);
+void js_loadstring(js_State *J, const char *filename, const char *source);
+void js_loadfile(js_State *J, const char *filename);
 
-void jsB_initobject(js_State *J);
-void jsB_initarray(js_State *J);
-void jsB_initfunction(js_State *J);
-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);
+void js_call(js_State *J, int n);
+void js_construct(js_State *J, int n);
 
-JS_NORETURN void js_throw(js_State *J);
-JS_NORETURN void js_error(js_State *J, const char *fmt, ...) __printflike(2,3);
+void js_getglobal(js_State *J, const char *name);
+void js_setglobal(js_State *J, const char *name);
 
-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);
+void js_getownproperty(js_State *J, int idx, const char *name);
+void js_getproperty(js_State *J, int idx, const char *name);
+void js_setproperty(js_State *J, int idx, const char *name);
+void js_cfgproperty(js_State *J, int idx, const char *name, int atts);
+void js_delproperty(js_State *J, int idx, const char *name);
+int js_nextproperty(js_State *J, int idx);
+
+void js_pushglobal(js_State *J);
+void js_pushundefined(js_State *J);
+void js_pushnull(js_State *J);
+void js_pushboolean(js_State *J, int v);
+void js_pushnumber(js_State *J, double v);
+void js_pushstring(js_State *J, const char *v);
+void js_pushliteral(js_State *J, const char *v);
+
+void js_newobject(js_State *J);
+void js_newarray(js_State *J);
+void js_newcfunction(js_State *J, js_CFunction fun, int length);
+
+int js_isundefined(js_State *J, int idx);
+int js_isnull(js_State *J, int idx);
+int js_isboolean(js_State *J, int idx);
+int js_isnumber(js_State *J, int idx);
+int js_isstring(js_State *J, int idx);
+int js_isprimitive(js_State *J, int idx);
+int js_isobject(js_State *J, int idx);
+int js_iscallable(js_State *J, int idx);
+
+int js_toboolean(js_State *J, int idx);
+double js_tonumber(js_State *J, int idx);
+const char *js_tostring(js_State *J, int idx);
+
+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);
+
+int js_gettop(js_State *J);
+void js_settop(js_State *J, int idx);
+void js_pop(js_State *J, int n);
+void js_copy(js_State *J, int idx);
+void js_remove(js_State *J, int idx);
+void js_insert(js_State *J, int idx);
+void js_replace(js_State* J, int idx);
+
+void js_concat(js_State *J);
+int js_compare(js_State *J);
+int js_equal(js_State *J);
+int js_strictequal(js_State *J);
 
 #endif
--- a/jsbarray.c
+++ b/jsbarray.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_Array(js_State *J, int n) { return 0; }
 static int jsB_new_Array(js_State *J, int n) { return 0; }
--- a/jsbboolean.c
+++ b/jsbboolean.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_new_Boolean(js_State *J, int n)
 {
--- a/jsberror.c
+++ b/jsberror.c
@@ -1,15 +1,14 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int Ep_toString(js_State *J, int n)
 {
 	js_getproperty(J, 0, "name");
 	js_pushliteral(J, ": ");
-	jsR_concat(J);
+	js_concat(J);
 	js_getproperty(J, 0, "message");
-	jsR_concat(J);
+	js_concat(J);
 	return 1;
 }
 
--- a/jsbfunction.c
+++ b/jsbfunction.c
@@ -1,8 +1,7 @@
-#include "js.h"
+#include "jsi.h"
 #include "jscompile.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_new_Function(js_State *J, int n) { return 0; }
 static int jsB_Function(js_State *J, int n) { return 0; }
--- a/jsbmath.c
+++ b/jsbmath.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int Math_abs(js_State *J, int nargs) {
 	return js_pushnumber(J, abs(js_tonumber(J, 1))), 1;
--- a/jsbnumber.c
+++ b/jsbnumber.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_new_Number(js_State *J, int n)
 {
--- a/jsbobject.c
+++ b/jsbobject.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_new_Object(js_State *J, int n)
 {
--- a/jsbstring.c
+++ b/jsbstring.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 #include "jsutf.h"
 
 static int jsB_new_String(js_State *J, int n)
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
 static int jsB_print(js_State *J, int argc)
 {
@@ -26,7 +25,7 @@
 {
 	if (!js_isstring(J, -1))
 		return 1;
-	jsR_loadscript(J, "(eval)", js_tostring(J, -1));
+	js_loadstring(J, "(eval)", js_tostring(J, -1));
 	js_copy(J, 0);
 	js_call(J, 0);
 	return 1;
--- /dev/null
+++ b/jsbuiltin.h
@@ -1,0 +1,18 @@
+#ifndef js_builtin_h
+#define js_builtin_h
+
+void jsB_init(js_State *J);
+void jsB_initobject(js_State *J);
+void jsB_initarray(js_State *J);
+void jsB_initfunction(js_State *J);
+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);
+
+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);
+
+#endif
--- a/jscompile.c
+++ b/jscompile.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsparse.h"
 #include "jscompile.h"
-#include "jsstate.h"
 
 #define cexp js_cexp /* collision with math.h */
 
--- /dev/null
+++ b/jsconf.h
@@ -1,0 +1,32 @@
+#ifndef js_conf_h
+#define js_conf_h
+
+#define JS_STACKSIZE 256	/* value stack size */
+#define JS_MINSTACK 20		/* at least this much available when entering a function */
+#define JS_TRYLIMIT 64		/* exception stack size */
+#define JS_GCLIMIT 10000	/* run gc cycle every N allocations */
+
+/* noreturn is a GCC extension */
+#ifdef __GNUC__
+#define JS_NORETURN __attribute__((noreturn))
+#else
+#ifdef _MSC_VER
+#define JS_NORETURN __declspec(noreturn)
+#else
+#define JS_NORETURN
+#endif
+#endif
+
+/* GCC can do type checking of printf strings */
+#ifdef __printflike
+#define JS_PRINTFLIKE __printflike
+#else
+#if __GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7
+#define JS_PRINTFLIKE(fmtarg, firstvararg) \
+	__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+#else
+#define JS_PRINTFLIKE(fmtarg, firstvararg)
+#endif
+#endif
+
+#endif
--- a/jsdump.c
+++ b/jsdump.c
@@ -1,4 +1,4 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsparse.h"
 #include "jscompile.h"
 #include "jsobject.h"
--- a/jsgc.c
+++ b/jsgc.c
@@ -1,8 +1,7 @@
-#include "js.h"
+#include "jsi.h"
 #include "jscompile.h"
 #include "jsobject.h"
 #include "jsrun.h"
-#include "jsstate.h"
 
 static void jsG_markobject(js_State *J, int mark, js_Object *obj);
 
@@ -147,7 +146,7 @@
 			genv, nenv, gfun, nfun, gobj, nobj);
 }
 
-void js_close(js_State *J)
+void js_freestate(js_State *J)
 {
 	js_Function *fun, *nextfun;
 	js_Object *obj, *nextobj;
@@ -160,8 +159,9 @@
 	for (obj = J->gcobj; obj; obj = nextobj)
 		nextobj = obj->gcnext, jsG_freeobject(J, obj);
 
-	js_freestrings(J);
+	jsS_freestrings(J);
 
-	free(J->buf.text);
+	free(J->lexbuf.text);
+	free(J->stack);
 	free(J);
 }
--- /dev/null
+++ b/jsi.h
@@ -1,0 +1,135 @@
+#ifndef jsi_h
+#define jsi_h
+
+#include "js.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdarg.h>
+#include <string.h>
+#include <setjmp.h>
+#include <math.h>
+#include <float.h>
+
+enum { JS_REGEXP_G = 1, JS_REGEXP_I = 2, JS_REGEXP_M = 4 }; /* RegExp flags */
+
+enum { JS_HNONE, JS_HNUMBER, JS_HSTRING }; /* Hint to ToPrimitive() */
+
+typedef struct js_Value js_Value;
+typedef struct js_Object js_Object;
+
+typedef struct js_Ast js_Ast;
+typedef struct js_Function js_Function;
+typedef struct js_Environment js_Environment;
+
+typedef struct js_StringNode js_StringNode;
+void jsS_dumpstrings(js_State *J);
+void jsS_freestrings(js_State *J);
+
+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);
+
+js_Object *jsR_newcconstructor(js_State *J, js_CFunction cfunction, js_CFunction cconstructor);
+
+const char *jsR_stringfromnumber(js_State *J, double number);
+double jsR_numberfromstring(js_State *J, const char *string);
+
+void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
+void js_newscript(js_State *J, js_Function *function);
+void js_dup(js_State *J);
+void js_rot(js_State *J, int n);
+void js_rot2(js_State *J);
+void js_rot3(js_State *J);
+
+/* Exception handling */
+
+typedef struct js_Jumpbuf js_Jumpbuf;
+
+struct js_Jumpbuf
+{
+	jmp_buf buf;
+	js_Environment *E;
+	int top, bot;
+	short *pc;
+};
+
+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)
+
+/* State struct */
+
+struct js_State
+{
+	js_StringNode *strings;
+
+	/* parser input source */
+	const char *filename;
+	const char *source;
+	int line;
+
+	/* lexer state */
+	struct { char *text; size_t len, cap; } lexbuf;
+	int lexline;
+	int lexchar;
+	int lasttoken;
+	int newline;
+
+	/* parser state */
+	int lookahead;
+	const char *text;
+	double number;
+	js_Ast *gcast; /* list of allocated nodes to free after parsing */
+
+	/* compiler state */
+	int strict;
+
+	/* runtime environment */
+	js_Object *Object_prototype;
+	js_Object *Array_prototype;
+	js_Object *Function_prototype;
+	js_Object *Boolean_prototype;
+	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;
+
+	/* execution stack */
+	int top, bot;
+	js_Value *stack;
+
+	/* garbage collector list */
+	int gcmark;
+	int gccounter;
+	js_Environment *gcenv;
+	js_Function *gcfun;
+	js_Object *gcobj;
+
+	/* exception stack */
+	int trylen;
+	js_Jumpbuf trybuf[JS_TRYLIMIT];
+};
+
+#endif
--- a/jsintern.c
+++ b/jsintern.c
@@ -1,5 +1,4 @@
-#include "js.h"
-#include "jsstate.h"
+#include "jsi.h"
 
 /* Use an AA-tree to quickly look up interned strings. */
 
@@ -66,26 +65,26 @@
 	return newstringnode(string, result);
 }
 
-static void printstringnode(js_StringNode *node, int level)
+static void dumpstringnode(js_StringNode *node, int level)
 {
 	int i;
 	if (node->left != &sentinel)
-		printstringnode(node->left, level + 1);
+		dumpstringnode(node->left, level + 1);
 	printf("%d: ", node->level);
 	for (i = 0; i < level; ++i)
 		putchar('\t');
 	printf("'%s'\n", node->string);
 	if (node->right != &sentinel)
-		printstringnode(node->right, level + 1);
+		dumpstringnode(node->right, level + 1);
 }
 
-void js_printstrings(js_State *J)
+void jsS_dumpstrings(js_State *J)
 {
 	js_StringNode *root = J->strings;
-	printf("--- string dump ---\n");
+	printf("interned strings {\n");
 	if (root && root != &sentinel)
-		printstringnode(root, 0);
-	printf("---\n");
+		dumpstringnode(root, 1);
+	printf("}\n");
 }
 
 static void js_freestringnode(js_State *J, js_StringNode *node)
@@ -95,7 +94,7 @@
 	free(node);
 }
 
-void js_freestrings(js_State *J)
+void jsS_freestrings(js_State *J)
 {
 	if (J->strings && J->strings != &sentinel)
 		js_freestringnode(J, J->strings);
--- a/jslex.c
+++ b/jslex.c
@@ -1,10 +1,27 @@
-#include "js.h"
+#include "jsi.h"
 #include "jslex.h"
-#include "jsstate.h"
 #include "jsutf.h"
 
 #define nelem(a) (sizeof (a) / sizeof (a)[0])
 
+JS_NORETURN static int jsY_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+
+static int jsY_error(js_State *J, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[512];
+	char msgbuf[256];
+
+	va_start(ap, fmt);
+	vsnprintf(msgbuf, 256, fmt, ap);
+	va_end(ap);
+
+	snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
+	strcat(buf, msgbuf);
+
+	jsR_throwSyntaxError(J, buf);
+}
+
 static const char *tokenstring[] = {
 	"(end-of-file)",
 	"'\\x01'", "'\\x02'", "'\\x03'", "'\\x04'", "'\\x05'", "'\\x06'", "'\\x07'",
@@ -48,7 +65,7 @@
 	"'void'", "'while'", "'with'",
 };
 
-const char *jsP_tokenstring(int token)
+const char *jsY_tokenstring(int token)
 {
 	if (token >= 0 && token < nelem(tokenstring))
 		if (tokenstring[token])
@@ -135,7 +152,7 @@
 #define PEEK (J->lexchar)
 #define NEXT() next(J)
 #define ACCEPT(x) (PEEK == x ? (NEXT(), 1) : 0)
-#define EXPECT(x) (ACCEPT(x) || (jsP_error(J, "expected '%c'", x), 0))
+#define EXPECT(x) (ACCEPT(x) || (jsY_error(J, "expected '%c'", x), 0))
 
 static void next(js_State *J)
 {
@@ -164,33 +181,33 @@
 			return;
 		}
 error:
-		jsP_error(J, "unexpected escape sequence");
+		jsY_error(J, "unexpected escape sequence");
 	}
 }
 
 static void textinit(js_State *J)
 {
-	if (!J->buf.text) {
-		J->buf.cap = 4096;
-		J->buf.text = malloc(J->buf.cap);
+	if (!J->lexbuf.text) {
+		J->lexbuf.cap = 4096;
+		J->lexbuf.text = malloc(J->lexbuf.cap);
 	}
-	J->buf.len = 0;
+	J->lexbuf.len = 0;
 }
 
 static inline void textpush(js_State *J, Rune c)
 {
 	int n = runelen(c);
-	if (J->buf.len + n > J->buf.cap) {
-		J->buf.cap = J->buf.cap * 2;
-		J->buf.text = realloc(J->buf.text, J->buf.cap);
+	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->buf.len += runetochar(J->buf.text + J->buf.len, &c);
+	J->lexbuf.len += runetochar(J->lexbuf.text + J->lexbuf.len, &c);
 }
 
 static inline char *textend(js_State *J)
 {
 	textpush(J, 0);
-	return J->buf.text;
+	return J->lexbuf.text;
 }
 
 static inline void lexlinecomment(js_State *J)
@@ -218,7 +235,7 @@
 {
 	double n = 0;
 	if (!ishex(PEEK))
-		return jsP_error(J, "malformed hexadecimal number");
+		return jsY_error(J, "malformed hexadecimal number");
 	while (ishex(PEEK)) {
 		n = n * 16 + tohex(PEEK);
 		NEXT();
@@ -230,7 +247,7 @@
 {
 	double n = 0;
 	if (!isdec(PEEK))
-		return jsP_error(J, "malformed number");
+		return jsY_error(J, "malformed number");
 	while (isdec(PEEK)) {
 		n = n * 10 + (PEEK - '0');
 		NEXT();
@@ -272,7 +289,7 @@
 			return TK_NUMBER;
 		}
 		if (isdec(PEEK))
-			return jsP_error(J, "number with leading zero");
+			return jsY_error(J, "number with leading zero");
 		n = 0;
 		if (ACCEPT('.'))
 			n += lexfraction(J);
@@ -290,7 +307,7 @@
 	}
 
 	if (isidentifierstart(PEEK))
-		return jsP_error(J, "number with letter suffix");
+		return jsY_error(J, "number with letter suffix");
 
 	J->number = n;
 	return TK_NUMBER;
@@ -346,10 +363,10 @@
 
 	while (PEEK != q) {
 		if (PEEK == 0 || PEEK == '\n')
-			return jsP_error(J, "string not terminated");
+			return jsY_error(J, "string not terminated");
 		if (ACCEPT('\\')) {
 			if (lexescape(J))
-				return jsP_error(J, "malformed escape sequence");
+				return jsY_error(J, "malformed escape sequence");
 		} else {
 			textpush(J, PEEK);
 			NEXT();
@@ -387,7 +404,6 @@
 {
 	const char *s;
 	int g, m, i;
-	int c;
 
 	/* already consumed initial '/' */
 
@@ -396,11 +412,11 @@
 	/* regexp body */
 	while (PEEK != '/') {
 		if (PEEK == 0 || PEEK == '\n') {
-			return jsP_error(J, "regular expression not terminated");
+			return jsY_error(J, "regular expression not terminated");
 		} else if (ACCEPT('\\')) {
 			textpush(J, '\\');
 			if (PEEK == 0 || PEEK == '\n')
-				return jsP_error(J, "regular expression not terminated");
+				return jsY_error(J, "regular expression not terminated");
 			textpush(J, PEEK);
 			NEXT();
 		} else {
@@ -415,16 +431,15 @@
 	/* regexp flags */
 	g = i = m = 0;
 
-	c = PEEK;
 	while (isidentifierpart(PEEK)) {
 		if (ACCEPT('g')) ++g;
 		else if (ACCEPT('i')) ++i;
 		else if (ACCEPT('m')) ++m;
-		else return jsP_error(J, "illegal flag in regular expression: %c", PEEK);
+		else return jsY_error(J, "illegal flag in regular expression: %c", PEEK);
 	}
 
 	if (g > 1 || i > 1 || m > 1)
-		return jsP_error(J, "duplicated flag in regular expression");
+		return jsY_error(J, "duplicated flag in regular expression");
 
 	J->text = js_intern(J, s);
 	J->number = 0;
@@ -471,7 +486,7 @@
 				continue;
 			} else if (ACCEPT('*')) {
 				if (lexcomment(J))
-					return jsP_error(J, "multi-line comment not terminated");
+					return jsY_error(J, "multi-line comment not terminated");
 				continue;
 			} else if (isregexpcontext(J->lasttoken)) {
 				return lexregexp(J);
@@ -621,16 +636,16 @@
 
 			textend(J);
 
-			return findkeyword(J, J->buf.text);
+			return findkeyword(J, J->lexbuf.text);
 		}
 
 		if (PEEK >= 0x20 && PEEK <= 0x7E)
-			return jsP_error(J, "unexpected character: '%c'", PEEK);
-		return jsP_error(J, "unexpected character: \\u%04X", PEEK);
+			return jsY_error(J, "unexpected character: '%c'", PEEK);
+		return jsY_error(J, "unexpected character: \\u%04X", PEEK);
 	}
 }
 
-void jsP_initlex(js_State *J, const char *filename, const char *source)
+void jsY_initlex(js_State *J, const char *filename, const char *source)
 {
 	J->filename = filename;
 	J->source = source;
@@ -639,7 +654,7 @@
 	next(J); /* load first lookahead character */
 }
 
-int jsP_lex(js_State *J)
+int jsY_lex(js_State *J)
 {
 	return J->lasttoken = lex(J);
 }
--- a/jslex.h
+++ b/jslex.h
@@ -66,12 +66,9 @@
 	TK_WITH,
 };
 
-const char *jsP_tokenstring(int token);
+const char *jsY_tokenstring(int token);
 
-void jsP_initlex(js_State *J, const char *filename, const char *source);
-int jsP_lex(js_State *J);
-
-JS_NORETURN int jsP_error(js_State *J, const char *fmt, ...);
-void jsP_warning(js_State *J, const char *fmt, ...);
+void jsY_initlex(js_State *J, const char *filename, const char *source);
+int jsY_lex(js_State *J);
 
 #endif
--- a/jsobject.c
+++ b/jsobject.c
@@ -1,8 +1,7 @@
-#include "js.h"
+#include "jsi.h"
 #include "jscompile.h"
 #include "jsobject.h"
 #include "jsrun.h"
-#include "jsstate.h"
 #include "jsutf.h"
 
 static js_Object *jsR_newfunction(js_State *J, js_Function *function, js_Environment *scope)
--- a/jsobject.h
+++ b/jsobject.h
@@ -2,9 +2,8 @@
 #define js_object_h
 
 typedef enum js_Type js_Type;
-typedef struct js_Value js_Value;
-
 typedef enum js_Class js_Class;
+
 typedef struct js_Property js_Property;
 
 enum js_Type {
@@ -31,12 +30,6 @@
 	JS_CMATH,
 };
 
-enum {
-	JS_HNONE,
-	JS_HNUMBER,
-	JS_HSTRING,
-};
-
 struct js_Value
 {
 	js_Type type;
@@ -79,6 +72,13 @@
 	js_Value value;
 };
 
+js_Value js_tovalue(js_State *J, int idx);
+js_Value js_toprimitive(js_State *J, int idx, int hint);
+js_Object *js_toobject(js_State *J, int idx);
+
+void js_pushvalue(js_State *J, js_Value v);
+void js_pushobject(js_State *J, js_Object *v);
+
 /* jsvalue.c */
 int jsR_toboolean(js_State *J, const js_Value *v);
 double jsR_tonumber(js_State *J, const js_Value *v);
@@ -104,7 +104,5 @@
 
 void js_dumpobject(js_State *J, js_Object *obj);
 void js_dumpvalue(js_State *J, js_Value v);
-
-JS_NORETURN void jsR_error(js_State *J, const char *fmt, ...);
 
 #endif
--- a/jsoptim.c
+++ b/jsoptim.c
@@ -1,4 +1,4 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsparse.h"
 
 static inline int i32(double d)
--- a/jsparse.c
+++ b/jsparse.c
@@ -1,7 +1,6 @@
-#include "js.h"
+#include "jsi.h"
 #include "jslex.h"
 #include "jsparse.h"
-#include "jsstate.h"
 
 #define nelem(a) (sizeof (a) / sizeof (a)[0])
 
@@ -18,7 +17,7 @@
 #define STM3(x,a,b,c)	jsP_newnode(J, STM_ ## x, a, b, c, 0)
 #define STM4(x,a,b,c,d)	jsP_newnode(J, STM_ ## x, a, b, c, d)
 
-#define TOKSTR		jsP_tokenstring(J->lookahead)
+#define TOKSTR		jsY_tokenstring(J->lookahead)
 
 static js_Ast *expression(js_State *J, int notin);
 static js_Ast *assignment(js_State *J, int notin);
@@ -26,6 +25,34 @@
 static js_Ast *statement(js_State *J);
 static js_Ast *funbody(js_State *J);
 
+JS_NORETURN static void jsP_error(js_State *J, const char *fmt, ...) JS_PRINTFLIKE(2,3);
+
+static void jsP_error(js_State *J, const char *fmt, ...)
+{
+	va_list ap;
+	char buf[512];
+	char msgbuf[256];
+
+	va_start(ap, fmt);
+	vsnprintf(msgbuf, 256, fmt, ap);
+	va_end(ap);
+
+	snprintf(buf, 256, "%s:%d: ", J->filename, J->lexline);
+	strcat(buf, msgbuf);
+
+	jsR_throwSyntaxError(J, buf);
+}
+
+static void jsP_warning(js_State *J, const char *fmt, ...)
+{
+	va_list ap;
+	fprintf(stderr, "%s:%d: warning: ", J->filename, J->lexline);
+	va_start(ap, fmt);
+	vfprintf(stderr, fmt, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
 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));
@@ -74,7 +101,7 @@
 
 static inline void next(js_State *J)
 {
-	J->lookahead = jsP_lex(J);
+	J->lookahead = jsY_lex(J);
 }
 
 static inline int accept(js_State *J, int t)
@@ -90,7 +117,7 @@
 {
 	if (accept(J, t))
 		return;
-	jsP_error(J, "unexpected token: %s (expected %s)", TOKSTR, jsP_tokenstring(t));
+	jsP_error(J, "unexpected token: %s (expected %s)", TOKSTR, jsY_tokenstring(t));
 }
 
 static void semicolon(js_State *J)
@@ -838,38 +865,11 @@
 	return a;
 }
 
-void jsP_warning(js_State *J, const char *fmt, ...)
-{
-	va_list ap;
-
-	fprintf(stderr, "%s:%d: warning: ", J->filename, J->lexline);
-	va_start(ap, fmt);
-	vfprintf(stderr, fmt, ap);
-	va_end(ap);
-	fprintf(stderr, "\n");
-}
-
-int jsP_error(js_State *J, const char *fmt, ...)
-{
-	va_list ap;
-	char buf[512];
-	char msgbuf[256];
-
-	va_start(ap, fmt);
-	vsnprintf(msgbuf, 256, fmt, ap);
-	va_end(ap);
-
-	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)
 {
 	js_Ast *p, *last;
 
-	jsP_initlex(J, filename, source);
+	jsY_initlex(J, filename, source);
 
 	next(J);
 	p = script(J);
--- a/jsparse.h
+++ b/jsparse.h
@@ -1,7 +1,9 @@
 #ifndef js_parse_h
 #define js_parse_h
 
-enum
+typedef enum js_AstType js_AstType;
+
+enum js_AstType
 {
 	AST_LIST,
 	AST_FUNDEC,
--- a/jsproperty.c
+++ b/jsproperty.c
@@ -1,6 +1,5 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsstate.h"
 
 /*
 	Use an AA-tree to quickly look up properties in objects:
--- a/jsrun.c
+++ b/jsrun.c
@@ -1,9 +1,7 @@
-#include "js.h"
-#include "jsobject.h"
-#include "jsparse.h"
+#include "jsi.h"
 #include "jscompile.h"
+#include "jsobject.h"
 #include "jsrun.h"
-#include "jsstate.h"
 
 static void jsR_run(js_State *J, js_Function *F);
 
@@ -42,7 +40,7 @@
 #define TOP (J->top)
 #define BOT (J->bot)
 
-static void js_pushvalue(js_State *J, js_Value v)
+void js_pushvalue(js_State *J, js_Value v)
 {
 	STACK[TOP] = v;
 	++TOP;
@@ -127,7 +125,7 @@
 	return 0;
 }
 
-const char *js_typeof(js_State *J, int idx)
+static const char *js_typeof(js_State *J, int idx)
 {
 	switch (stackidx(J, idx)->type) {
 	case JS_TUNDEFINED: return "undefined";
@@ -708,7 +706,7 @@
 		/* Additive operators */
 
 		case OP_ADD:
-			jsR_concat(J);
+			js_concat(J);
 			break;
 
 		case OP_SUB:
@@ -743,19 +741,19 @@
 
 		/* Relational operators */
 
-		case OP_LT: b = jsR_compare(J); js_pushboolean(J, b < 0); break;
-		case OP_GT: b = jsR_compare(J); js_pushboolean(J, b > 0); break;
-		case OP_LE: b = jsR_compare(J); js_pushboolean(J, b <= 0); break;
-		case OP_GE: b = jsR_compare(J); js_pushboolean(J, b >= 0); break;
+		case OP_LT: b = js_compare(J); js_pushboolean(J, b < 0); break;
+		case OP_GT: b = js_compare(J); js_pushboolean(J, b > 0); break;
+		case OP_LE: b = js_compare(J); js_pushboolean(J, b <= 0); break;
+		case OP_GE: b = js_compare(J); js_pushboolean(J, b >= 0); break;
 
 		// OP_INSTANCEOF
 
 		/* Equality */
 
-		case OP_EQ: b = jsR_equal(J); js_pushboolean(J, b); break;
-		case OP_NE: b = jsR_equal(J); js_pushboolean(J, !b); break;
-		case OP_STRICTEQ: b = jsR_strictequal(J); js_pushboolean(J, b); break;
-		case OP_STRICTNE: b = jsR_strictequal(J); js_pushboolean(J, !b); break;
+		case OP_EQ: b = js_equal(J); js_pushboolean(J, b); break;
+		case OP_NE: b = js_equal(J); js_pushboolean(J, !b); break;
+		case OP_STRICTEQ: b = js_strictequal(J); js_pushboolean(J, b); break;
+		case OP_STRICTNE: b = js_strictequal(J); js_pushboolean(J, !b); break;
 
 		/* Binary bitwise operators */
 
@@ -813,23 +811,4 @@
 			js_error(J, "illegal instruction: %d (pc=%d)", opcode, (int)(pc - F->code - 1));
 		}
 	}
-}
-
-void jsR_loadscript(js_State *J, const char *filename, const char *source)
-{
-	js_Ast *P;
-	js_Function *F;
-
-	if (js_try(J)) {
-		jsP_freeparse(J);
-		js_throw(J);
-	}
-
-	P = jsP_parse(J, filename, source);
-	jsP_optimize(J, P);
-	F = jsC_compile(J, P);
-	jsP_freeparse(J);
-	js_newscript(J, F);
-
-	js_endtry(J);
 }
--- a/jsrun.h
+++ b/jsrun.h
@@ -1,6 +1,8 @@
 #ifndef js_run_h
 #define js_run_h
 
+js_Environment *jsR_newenvironment(js_State *J, js_Object *variables, js_Environment *outer);
+
 struct js_Environment
 {
 	js_Environment *outer;
@@ -9,80 +11,5 @@
 	js_Environment *gcnext;
 	int gcmark;
 };
-
-/* private */
-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);
-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);
-js_Value js_toprimitive(js_State *J, int idx, int hint);
-js_Value js_tovalue(js_State *J, int idx);
-void jsR_concat(js_State *J);
-int jsR_compare(js_State *J);
-int jsR_equal(js_State *J);
-int jsR_strictequal(js_State *J);
-
-const char *jsR_stringfromnumber(js_State *J, double number);
-double jsR_numberfromstring(js_State *J, const char *string);
-
-/* public */
-
-void js_call(js_State *J, int n);
-void js_construct(js_State *J, int n);
-
-void js_getglobal(js_State *J, const char *name);
-void js_setglobal(js_State *J, const char *name);
-void js_getownproperty(js_State *J, int idx, const char *name);
-void js_getproperty(js_State *J, int idx, const char *name);
-void js_setproperty(js_State *J, int idx, const char *name);
-int js_nextproperty(js_State *J, int idx);
-
-void js_pushglobal(js_State *J);
-void js_pushundefined(js_State *J);
-void js_pushnull(js_State *J);
-void js_pushboolean(js_State *J, int v);
-void js_pushnumber(js_State *J, double v);
-void js_pushliteral(js_State *J, const char *v);
-void js_pushstring(js_State *J, const char *v);
-
-void js_newobject(js_State *J);
-void js_newarray(js_State *J);
-void js_newfunction(js_State *J, js_Function *function, js_Environment *scope);
-void js_newscript(js_State *J, js_Function *function);
-void js_newcfunction(js_State *J, js_CFunction fun, int length);
-
-const char *js_typeof(js_State *J, int idx);
-int js_isundefined(js_State *J, int idx);
-int js_isnull(js_State *J, int idx);
-int js_isboolean(js_State *J, int idx);
-int js_isnumber(js_State *J, int idx);
-int js_isstring(js_State *J, int idx);
-int js_isprimitive(js_State *J, int idx);
-int js_isobject(js_State *J, int idx);
-int js_iscallable(js_State *J, int idx);
-
-int js_toboolean(js_State *J, int idx);
-double js_tonumber(js_State *J, int idx);
-const char *js_tostring(js_State *J, int idx);
-
-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);
-void js_copy(js_State *J, int idx);
-void js_rot(js_State *J, int n);
-void js_rot2(js_State *J);
-void js_rot3(js_State *J);
 
 #endif
--- a/jsstate.c
+++ b/jsstate.c
@@ -1,11 +1,27 @@
-#include "js.h"
+#include "jsi.h"
+#include "jsparse.h"
+#include "jscompile.h"
 #include "jsobject.h"
 #include "jsrun.h"
-#include "jsstate.h"
+#include "jsbuiltin.h"
 
-void js_loadstring(js_State *J, const char *source)
+void js_loadstring(js_State *J, const char *filename, const char *source)
 {
-	jsR_loadscript(J, "(string)", source);
+	js_Ast *P;
+	js_Function *F;
+
+	if (js_try(J)) {
+		jsP_freeparse(J);
+		js_throw(J);
+	}
+
+	P = jsP_parse(J, filename, source);
+	jsP_optimize(J, P);
+	F = jsC_compile(J, P);
+	jsP_freeparse(J);
+	js_newscript(J, F);
+
+	js_endtry(J);
 }
 
 void js_loadfile(js_State *J, const char *filename)
@@ -47,7 +63,7 @@
 		js_throw(J);
 	}
 
-	jsR_loadscript(J, filename, s);
+	js_loadstring(J, filename, s);
 
 	free(s);
 	fclose(f);
@@ -60,7 +76,7 @@
 		fprintf(stderr, "libjs: %s\n", js_tostring(J, -1));
 		return 1;
 	}
-	js_loadstring(J, source);
+	js_loadstring(J, "(string)", source);
 	js_pushglobal(J);
 	js_call(J, 0);
 	js_pop(J, 1);
@@ -86,6 +102,8 @@
 {
 	js_State *J = malloc(sizeof *J);
 	memset(J, 0, sizeof(*J));
+
+	J->stack = malloc(JS_STACKSIZE * sizeof *J->stack);
 
 	J->gcmark = 1;
 
--- a/jsstate.h
+++ /dev/null
@@ -1,91 +1,0 @@
-#ifndef js_state_h
-#define js_state_h
-
-#include "jsobject.h" /* for js_Value */
-
-#define JS_STACKSIZE 256
-#define JS_TRYLIMIT 64
-#define JS_GCLIMIT 10000 /* run gc cycle every N allocations */
-
-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 buf;
-	js_Environment *E;
-	int top, bot;
-	short *pc;
-};
-
-struct js_State
-{
-	js_StringNode *strings;
-
-	/* input */
-	const char *filename;
-	const char *source;
-	int line;
-
-	/* lexer */
-	struct { char *text; size_t len, cap; } buf;
-	int lexline;
-	int lexchar;
-	int lasttoken;
-	int newline;
-
-	/* parser */
-	int lookahead;
-	const char *text;
-	double number;
-	js_Ast *gcast; /* list of allocated nodes to free after parsing */
-
-	/* compiler */
-	int strict;
-
-	/* runtime environment */
-	js_Object *Object_prototype;
-	js_Object *Array_prototype;
-	js_Object *Function_prototype;
-	js_Object *Boolean_prototype;
-	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;
-
-	/* garbage collector list */
-	int gcmark;
-	int gccounter;
-	js_Environment *gcenv;
-	js_Function *gcfun;
-	js_Object *gcobj;
-
-	/* exception stack */
-	int trylen;
-	js_Jumpbuf trybuf[JS_TRYLIMIT];
-
-	/* execution stack */
-	int top, bot;
-	js_Value stack[JS_STACKSIZE];
-};
-
-#endif
--- a/jsutf.c
+++ b/jsutf.c
@@ -11,10 +11,9 @@
  * ANY REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
  * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
  */
-#include <stdarg.h>
+#include <stdlib.h>
 #include <string.h>
 
-#include "js.h"
 #include "jsutf.h"
 
 #define uchar jsU_uchar
--- a/jsutftype.c
+++ b/jsutftype.c
@@ -1,4 +1,3 @@
-#include "js.h"
 #include "jsutf.h"
 
 #define bsearch jsU_bsearch
--- a/jsvalue.c
+++ b/jsvalue.c
@@ -1,6 +1,5 @@
-#include "js.h"
+#include "jsi.h"
 #include "jsobject.h"
-#include "jsrun.h"
 
 const char *jsR_stringfromnumber(js_State *J, double n)
 {
@@ -139,7 +138,7 @@
 	jsR_throwTypeError(J, "cannot convert value to object");
 }
 
-void jsR_concat(js_State *J)
+void js_concat(js_State *J)
 {
 	js_Value va = js_toprimitive(J, -2, JS_HNONE);
 	js_Value vb = js_toprimitive(J, -1, JS_HNONE);
@@ -160,7 +159,7 @@
 	}
 }
 
-int jsR_compare(js_State *J)
+int js_compare(js_State *J)
 {
 	js_Value va = js_toprimitive(J, -2, JS_HNUMBER);
 	js_Value vb = js_toprimitive(J, -1, JS_HNUMBER);
@@ -174,7 +173,7 @@
 	}
 }
 
-int jsR_equal(js_State *J)
+int js_equal(js_State *J)
 {
 	js_Value va = js_tovalue(J, -2);
 	js_Value vb = js_tovalue(J, -1);
@@ -211,7 +210,7 @@
 	return 0;
 }
 
-int jsR_strictequal(js_State *J)
+int js_strictequal(js_State *J)
 {
 	js_Value va = js_tovalue(J, -2);
 	js_Value vb = js_tovalue(J, -1);
--- a/main.c
+++ b/main.c
@@ -13,7 +13,7 @@
 		js_gc(J, 1);
 	}
 
-	js_close(J);
+	js_freestate(J);
 
 	return 0;
 }