shithub: libmujs

Download patch

ref: 1237388b8e825ef032e81694a983b1eac2c6954a
parent: a592f61f36e18ffcafeb186ee133cd37b7f5295c
author: Tor Andersson <tor@ccxvii.net>
date: Sat Jan 18 20:50:41 EST 2014

More builtins. Fix prototype chains for builtin classes and prototypes.

--- a/jsbarray.c
+++ b/jsbarray.c
@@ -4,12 +4,35 @@
 #include "jsstate.h"
 
 static int jsB_Array(js_State *J, int n) { return 0; }
+static int jsB_new_Array(js_State *J, int n) { return 0; }
 
+static int A_isArray(js_State *J, int n)
+{
+	if (js_isobject(J, 1)) {
+		js_Object *T = js_toobject(J, 1);
+		js_pushboolean(J, T->type == JS_CARRAY);
+		return 1;
+	}
+	js_pushboolean(J, 0);
+	return 0;
+}
+
 void jsB_initarray(js_State *J)
 {
-	J->Array_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	js_newcfunction(J, jsB_Array);
-	js_pushobject(J, J->Array_prototype);
-	js_setproperty(J, -2, "prototype");
+	js_pushobject(J, jsR_newcconstructor(J, jsB_Array, jsB_new_Array));
+	{
+		jsB_propn(J, "length", 1);
+
+		js_pushobject(J, J->Array_prototype);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+			jsB_propn(J, "length", 0);
+		}
+		js_setproperty(J, -2, "prototype");
+
+		/* ECMA-262-5 */
+		jsB_propf(J, "isArray", A_isArray, 1);
+	}
 	js_setglobal(J, "Array");
 }
--- a/jsbboolean.c
+++ b/jsbboolean.c
@@ -3,13 +3,49 @@
 #include "jsrun.h"
 #include "jsstate.h"
 
-static int jsB_Boolean(js_State *J, int n) { return 0; }
+static int jsB_new_Boolean(js_State *J, int n)
+{
+	js_pushobject(J, jsR_newboolean(J, js_toboolean(J, 0)));
+	return 1;
+}
 
+static int jsB_Boolean(js_State *J, int n)
+{
+	js_pushboolean(J, js_toboolean(J, 1));
+	return 1;
+}
+
+static int Bp_toString(js_State *J, int n)
+{
+	js_Object *T = js_toobject(J, 0);
+	if (T->type != JS_CBOOLEAN) jsR_error(J, "TypeError");
+	js_pushliteral(J, T->primitive.boolean ? "true" : "false");
+	return 1;
+}
+
+static int Bp_valueOf(js_State *J, int n)
+{
+	js_Object *T = js_toobject(J, 0);
+	if (T->type != JS_CBOOLEAN) jsR_error(J, "TypeError");
+	js_pushboolean(J, T->primitive.boolean);
+	return 1;
+}
+
 void jsB_initboolean(js_State *J)
 {
-	J->Boolean_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	js_newcfunction(J, jsB_Boolean);
-	js_pushobject(J, J->Boolean_prototype);
-	js_setproperty(J, -2, "prototype");
+	J->Boolean_prototype->primitive.boolean = 0;
+
+	js_pushobject(J, jsR_newcconstructor(J, jsB_Boolean, jsB_new_Boolean));
+	{
+		jsB_propn(J, "length", 1);
+		js_pushobject(J, J->Boolean_prototype);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+			jsB_propf(J, "toString", Bp_toString, 0);
+			jsB_propf(J, "valueOf", Bp_valueOf, 0);
+		}
+		js_setproperty(J, -2, "prototype");
+	}
 	js_setglobal(J, "Boolean");
 }
--- a/jsbfunction.c
+++ b/jsbfunction.c
@@ -3,13 +3,53 @@
 #include "jsrun.h"
 #include "jsstate.h"
 
+static int jsB_new_Function(js_State *J, int n) { return 0; }
 static int jsB_Function(js_State *J, int n) { return 0; }
 
+static int jsB_Function_prototype(js_State *J, int n)
+{
+	js_pushundefined(J);
+	return 1;
+}
+
+static int Fp_call(js_State *J, int n)
+{
+	int i;
+
+	if (!js_iscallable(J, 0))
+		jsR_error(J, "TypeError");
+	js_copy(J, 0);
+
+	if (js_isundefined(J, 1) || js_isnull(J, 1))
+		js_pushglobal(J);
+	else
+		js_copy(J, 1);
+
+	for (i = 1; i < n; ++i)
+		js_copy(J, i + 1);
+
+	js_call(J, n - 1);
+	return 1;
+}
+
 void jsB_initfunction(js_State *J)
 {
-	J->Function_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	js_newcfunction(J, jsB_Function);
-	js_pushobject(J, J->Function_prototype);
-	js_setproperty(J, -2, "prototype");
+	J->Function_prototype->cfunction = jsB_Function_prototype;
+	J->Function_prototype->cconstructor = NULL;
+
+	js_pushobject(J, jsR_newcconstructor(J, jsB_Function, jsB_new_Function));
+	{
+		jsB_propn(J, "length", 1);
+
+		js_pushobject(J, J->Function_prototype);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+			// jsB_propf(J, "toString", Fp_toString, 2);
+			// jsB_propf(J, "apply", Fp_apply, 2);
+			jsB_propf(J, "call", Fp_call, 1);
+		}
+		js_setproperty(J, -2, "prototype");
+	}
 	js_setglobal(J, "Function");
 }
--- a/jsbnumber.c
+++ b/jsbnumber.c
@@ -15,7 +15,7 @@
 	return 1;
 }
 
-static int jsB_Number_p_valueOf(js_State *J, int n)
+static int Np_valueOf(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	if (T->type != JS_CNUMBER) jsR_error(J, "TypeError");
@@ -23,7 +23,7 @@
 	return 1;
 }
 
-static int jsB_Number_p_toString(js_State *J, int n)
+static int Np_toString(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	if (T->type != JS_CNUMBER) jsR_error(J, "TypeError");
@@ -31,7 +31,7 @@
 	return 1;
 }
 
-static int jsB_Number_p_toFixed(js_State *J, int n)
+static int Np_toFixed(js_State *J, int n)
 {
 	char buf[40];
 	js_Object *T = js_toobject(J, 0);
@@ -42,7 +42,7 @@
 	return 1;
 }
 
-static int jsB_Number_p_toExponential(js_State *J, int n)
+static int Np_toExponential(js_State *J, int n)
 {
 	char buf[40];
 	js_Object *T = js_toobject(J, 0);
@@ -53,7 +53,7 @@
 	return 1;
 }
 
-static int jsB_Number_p_toPrecision(js_State *J, int n)
+static int Np_toPrecision(js_State *J, int n)
 {
 	char buf[40];
 	js_Object *T = js_toobject(J, 0);
@@ -66,40 +66,30 @@
 
 void jsB_initnumber(js_State *J)
 {
-	J->Number_prototype = jsR_newobject(J, JS_CNUMBER, J->Object_prototype);
 	J->Number_prototype->primitive.number = 0;
 
 	js_pushobject(J, jsR_newcconstructor(J, jsB_Number, jsB_new_Number));
 	{
+		jsB_propn(J, "length", 1);
+
 		js_pushobject(J, J->Number_prototype);
 		{
 			js_copy(J, -2);
 			js_setproperty(J, -2, "constructor");
-			js_newcfunction(J, jsB_Number_p_valueOf);
-			js_setproperty(J, -2, "valueOf");
-			js_newcfunction(J, jsB_Number_p_toString);
-			js_dup(J);
-			js_setproperty(J, -3, "toString");
-			js_setproperty(J, -2, "toLocaleString");
-			js_newcfunction(J, jsB_Number_p_toFixed);
-			js_setproperty(J, -2, "toFixed");
-			js_newcfunction(J, jsB_Number_p_toExponential);
-			js_setproperty(J, -2, "toExponential");
-			js_newcfunction(J, jsB_Number_p_toPrecision);
-			js_setproperty(J, -2, "toPrecision");
+			jsB_propf(J, "valueOf", Np_valueOf, 0);
+			jsB_propf(J, "toString", Np_toString, 0);
+			jsB_propf(J, "toLocaleString", Np_toString, 0);
+			jsB_propf(J, "toFixed", Np_toFixed, 1);
+			jsB_propf(J, "toExponential", Np_toExponential, 1);
+			jsB_propf(J, "toPrecision", Np_toPrecision, 1);
 		}
 		js_setproperty(J, -2, "prototype");
 
-		js_pushnumber(J, DBL_MAX);
-		js_setproperty(J, -2, "MAX_VALUE");
-		js_pushnumber(J, DBL_MIN);
-		js_setproperty(J, -2, "MIN_VALUE");
-		js_pushnumber(J, NAN);
-		js_setproperty(J, -2, "NaN");
-		js_pushnumber(J, -INFINITY);
-		js_setproperty(J, -2, "NEGATIVE_INFINITY");
-		js_pushnumber(J, INFINITY);
-		js_setproperty(J, -2, "POSITIVE_INFINITY");
+		jsB_propn(J, "MAX_VALUE", DBL_MAX);
+		jsB_propn(J, "MIN_VALUE", DBL_MIN);
+		jsB_propn(J, "NaN", NAN);
+		jsB_propn(J, "NEGATIVE_INFINITY", -INFINITY);
+		jsB_propn(J, "POSITIVE_INFINITY", INFINITY);
 	}
 	js_setglobal(J, "Number");
 }
--- a/jsbobject.c
+++ b/jsbobject.c
@@ -3,7 +3,8 @@
 #include "jsrun.h"
 #include "jsstate.h"
 
-static int jsB_new_Object(js_State *J, int n) {
+static int jsB_new_Object(js_State *J, int n)
+{
 	if (n == 0 || js_isundefined(J, 0) || js_isnull(J, 0))
 		js_newobject(J);
 	else
@@ -11,7 +12,8 @@
 	return 1;
 }
 
-static int jsB_Object(js_State *J, int n) {
+static int jsB_Object(js_State *J, int n)
+{
 	if (n == 0 || js_isundefined(J, 1) || js_isnull(J, 1))
 		js_newobject(J);
 	else
@@ -19,7 +21,7 @@
 	return 1;
 }
 
-static int jsB_Object_p_toString(js_State *J, int n)
+static int Op_toString(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	switch (T->type) {
@@ -40,13 +42,13 @@
 	return 1;
 }
 
-static int jsB_Object_p_valueOf(js_State *J, int n)
+static int Op_valueOf(js_State *J, int n)
 {
 	/* return the 'this' object */
 	return 1;
 }
 
-static int jsB_Object_p_hasOwnProperty(js_State *J, int n)
+static int Op_hasOwnProperty(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	const char *name = js_tostring(J, 1);
@@ -55,7 +57,7 @@
 	return 1;
 }
 
-static int jsB_Object_p_isPrototypeOf(js_State *J, int n)
+static int Op_isPrototypeOf(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	if (js_isobject(J, 1)) {
@@ -72,7 +74,7 @@
 	return 1;
 }
 
-static int jsB_Object_p_propertyIsEnumerable(js_State *J, int n)
+static int Op_propertyIsEnumerable(js_State *J, int n)
 {
 	js_Object *T = js_toobject(J, 0);
 	const char *name = js_tostring(J, 1);
@@ -83,25 +85,19 @@
 
 void jsB_initobject(js_State *J)
 {
-	J->Object_prototype = jsR_newobject(J, JS_COBJECT, NULL);
 	js_pushobject(J, jsR_newcconstructor(J, jsB_Object, jsB_new_Object));
 	{
+		jsB_propn(J, "length", 1);
 		js_pushobject(J, J->Object_prototype);
 		{
 			js_copy(J, -2);
 			js_setproperty(J, -2, "constructor");
-			js_newcfunction(J, jsB_Object_p_toString);
-			js_dup(J);
-			js_setproperty(J, -3, "toString");
-			js_setproperty(J, -2, "toLocaleString");
-			js_newcfunction(J, jsB_Object_p_valueOf);
-			js_setproperty(J, -2, "valueOf");
-			js_newcfunction(J, jsB_Object_p_hasOwnProperty);
-			js_setproperty(J, -2, "hasOwnProperty");
-			js_newcfunction(J, jsB_Object_p_isPrototypeOf);
-			js_setproperty(J, -2, "isPrototypeOf");
-			js_newcfunction(J, jsB_Object_p_propertyIsEnumerable);
-			js_setproperty(J, -2, "propertyIsEnumerable");
+			jsB_propf(J, "toString", Op_toString, 0);
+			jsB_propf(J, "toLocaleString", Op_toString, 0);
+			jsB_propf(J, "valueOf", Op_valueOf, 0);
+			jsB_propf(J, "hasOwnProperty", Op_hasOwnProperty, 1);
+			jsB_propf(J, "isPrototypeOf", Op_isPrototypeOf, 1);
+			jsB_propf(J, "propertyIsEnumerable", Op_propertyIsEnumerable, 1);
 		}
 		js_setproperty(J, -2, "prototype");
 	}
--- a/jsbstring.c
+++ b/jsbstring.c
@@ -2,14 +2,70 @@
 #include "jsobject.h"
 #include "jsrun.h"
 #include "jsstate.h"
+#include "jsutf.h"
 
-static int jsB_String(js_State *J, int n) { return 0; }
+static int jsB_new_String(js_State *J, int n)
+{
+	js_pushobject(J, jsR_newstring(J, n > 0 ? js_tostring(J, 0) : ""));
+	return 1;
+}
 
+static int jsB_String(js_State *J, int n)
+{
+	js_pushliteral(J, n > 0 ? js_tostring(J, 1) : "");
+	return 1;
+}
+
+static int Sp_toString(js_State *J, int n)
+{
+	js_Object *T = js_toobject(J, 0);
+	if (T->type != JS_CSTRING) jsR_error(J, "TypeError");
+	js_pushliteral(J, T->primitive.string);
+	return 1;
+}
+
+static int Sp_valueOf(js_State *J, int n)
+{
+	js_Object *T = js_toobject(J, 0);
+	if (T->type != JS_CSTRING) jsR_error(J, "TypeError");
+	js_pushliteral(J, T->primitive.string);
+	return 1;
+}
+
+static int S_fromCharCode(js_State *J, int n)
+{
+	int i;
+	Rune c;
+	char *s = malloc(n * UTFmax + 1), *p = s;
+	// TODO: guard malloc with try/catch
+	for (i = 0; i < n; i++) {
+		c = js_tonumber(J, i + 1); // TODO: ToUInt16()
+		p += runetochar(p, &c);
+	}
+	*p = 0;
+	js_pushstring(J, s);
+	free(s);
+	return 1;
+}
+
 void jsB_initstring(js_State *J)
 {
-	J->String_prototype = jsR_newobject(J, JS_COBJECT, NULL);
-	js_newcfunction(J, jsB_String);
-	js_pushobject(J, J->String_prototype);
-	js_setproperty(J, -2, "prototype");
+	J->String_prototype->primitive.string = "";
+
+	js_pushobject(J, jsR_newcconstructor(J, jsB_String, jsB_new_String));
+	{
+		jsB_propn(J, "length", 1);
+
+		js_pushobject(J, J->String_prototype);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+			jsB_propf(J, "toString", Sp_toString, 0);
+			jsB_propf(J, "valueOf", Sp_valueOf, 0);
+		}
+		js_setproperty(J, -2, "prototype");
+
+		jsB_propf(J, "fromCharCode", S_fromCharCode, 1);
+	}
 	js_setglobal(J, "String");
 }
--- a/jsbuiltin.c
+++ b/jsbuiltin.c
@@ -67,14 +67,35 @@
 	return 1;
 }
 
-static void jsB_register(js_State *J, const char *name, js_CFunction cfun)
+static void jsB_globalf(js_State *J, const char *name, js_CFunction cfun, int n)
 {
-	js_newcfunction(J, cfun);
+	js_newcfunction(J, cfun, n);
 	js_setglobal(J, name);
 }
 
+void jsB_propf(js_State *J, const char *name, js_CFunction cfun, int n)
+{
+	js_newcfunction(J, cfun, n);
+	js_setproperty(J, -2, name);
+}
+
+void jsB_propn(js_State *J, const char *name, double number)
+{
+	js_pushnumber(J, number);
+	js_setproperty(J, -2, name);
+}
+
 void jsB_init(js_State *J)
 {
+	/* Create the prototype objects here, before the constructors */
+	J->Object_prototype = jsR_newobject(J, JS_COBJECT, NULL);
+	J->Array_prototype = jsR_newobject(J, JS_CARRAY, J->Object_prototype);
+	J->Function_prototype = jsR_newobject(J, JS_CCFUNCTION, J->Object_prototype);
+	J->Boolean_prototype = jsR_newobject(J, JS_CBOOLEAN, J->Object_prototype);
+	J->Number_prototype = jsR_newobject(J, JS_CNUMBER, J->Object_prototype);
+	J->String_prototype = jsR_newobject(J, JS_CSTRING, J->Object_prototype);
+
+	/* Create the constructors and fill out the prototype objects */
 	jsB_initobject(J);
 	jsB_initarray(J);
 	jsB_initfunction(J);
@@ -82,6 +103,7 @@
 	jsB_initnumber(J);
 	jsB_initstring(J);
 
+	/* Initialize the global object */
 	js_pushnumber(J, NAN);
 	js_setglobal(J, "NaN");
 
@@ -91,12 +113,12 @@
 	js_pushundefined(J);
 	js_setglobal(J, "undefined");
 
-	jsB_register(J, "eval", jsB_eval);
-	jsB_register(J, "parseInt", jsB_parseInt);
-	jsB_register(J, "parseFloat", jsB_parseFloat);
-	jsB_register(J, "isNaN", jsB_isNaN);
-	jsB_register(J, "isFinite", jsB_isFinite);
+	jsB_globalf(J, "eval", jsB_eval, 1);
+	jsB_globalf(J, "parseInt", jsB_parseInt, 1);
+	jsB_globalf(J, "parseFloat", jsB_parseFloat, 1);
+	jsB_globalf(J, "isNaN", jsB_isNaN, 1);
+	jsB_globalf(J, "isFinite", jsB_isFinite, 1);
 
-	jsB_register(J, "collectGarbage", jsB_collectGarbage);
-	jsB_register(J, "print", jsB_print);
+	jsB_globalf(J, "collectGarbage", jsB_collectGarbage, 0);
+	jsB_globalf(J, "print", jsB_print, 0);
 }
--- a/jsobject.c
+++ b/jsobject.c
@@ -69,12 +69,16 @@
 void js_newfunction(js_State *J, js_Function *F, js_Environment *scope)
 {
 	js_pushobject(J, jsR_newfunction(J, F, scope));
-	js_pushnumber(J, F->numparams);
-	js_setproperty(J, -2, "length");
-	js_newobject(J);
-	js_copy(J, -2);
-	js_setproperty(J, -2, "constructor");
-	js_setproperty(J, -2, "prototype");
+	{
+		js_pushnumber(J, F->numparams);
+		js_setproperty(J, -2, "length");
+		js_newobject(J);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+		}
+		js_setproperty(J, -2, "prototype");
+	}
 }
 
 void js_newscript(js_State *J, js_Function *F)
@@ -82,13 +86,17 @@
 	js_pushobject(J, jsR_newscript(J, F));
 }
 
-void js_newcfunction(js_State *J, js_CFunction fun)
+void js_newcfunction(js_State *J, js_CFunction fun, int length)
 {
 	js_pushobject(J, jsR_newcfunction(J, fun));
-	js_newobject(J);
 	{
-		js_copy(J, -2);
-		js_setproperty(J, -2, "constructor");
+		js_pushnumber(J, length);
+		js_setproperty(J, -2, "length");
+		js_newobject(J);
+		{
+			js_copy(J, -2);
+			js_setproperty(J, -2, "constructor");
+		}
+		js_setproperty(J, -2, "prototype");
 	}
-	js_setproperty(J, -2, "prototype");
 }
--- a/jsrun.h
+++ b/jsrun.h
@@ -12,6 +12,9 @@
 
 /* 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);
+
 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);
@@ -52,7 +55,7 @@
 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);
+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);