ref: 07610bd0ac03c3c3473cb1965aed6db08d6120c5
parent: 05275e2e5de3231e0e2278e3792bb077db6a33c1
author: Tor Andersson <tor@ccxvii.net>
date: Sat Jan 18 15:16:28 EST 2014
Refactor ToString and ToNumber functions. Add ToPrimitive.
--- a/jsrun.c
+++ b/jsrun.c
@@ -119,6 +119,14 @@
int js_isprimitive(js_State *J, int idx) { return stackidx(J, idx)->type != JS_TOBJECT; }
int js_isobject(js_State *J, int idx) { return stackidx(J, idx)->type == JS_TOBJECT; }
+int js_iscallable(js_State *J, int idx)
+{
+ const js_Value *v = stackidx(J, idx);
+ if (v->type == JS_TOBJECT)
+ return v->u.object->type == JS_CFUNCTION || v->u.object->type == JS_CCFUNCTION;
+ return 0;
+}
+
const char *js_typeof(js_State *J, int idx)
{
switch (stackidx(J, idx)->type) {
--- a/jsrun.h
+++ b/jsrun.h
@@ -24,6 +24,9 @@
int jsR_equal(js_State *J);
int jsR_strictequal(js_State *J);
+const char *jsR_numbertostring(js_State *J, double number);
+double jsR_stringtonumber(js_State *J, const char *string);
+
/* public */
void js_call(js_State *J, int n);
@@ -41,6 +44,7 @@
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);
@@ -58,6 +62,7 @@
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);
@@ -67,5 +72,8 @@
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/jsvalue.c
+++ b/jsvalue.c
@@ -2,8 +2,57 @@
#include "jsobject.h"
#include "jsrun.h"
+const char *jsR_numbertostring(js_State *J, double n)
+{
+ char buf[32];
+ if (isnan(n)) return "NaN";
+ if (isinf(n)) return n < 0 ? "-Infinity" : "Infinity";
+ if (n == 0) return "0";
+ sprintf(buf, "%.17g", n); /* DBL_DECIMAL_DIG == 17 */
+ return js_intern(J, buf);
+}
+
+double jsR_stringtonumber(js_State *J, const char *s)
+{
+ /* TODO: use lexer to parse string grammar */
+ return strtod(s, NULL);
+}
+
+static int jsR_toString(js_State *J, js_Object *obj)
+{
+ js_pushobject(J, obj);
+ js_getproperty(J, -1, "toString");
+ if (js_iscallable(J, -1)) {
+ js_rot2(J);
+ js_call(J, 0);
+ if (js_isprimitive(J, -1))
+ return 1;
+ js_pop(J, 1);
+ return 0;
+ }
+ js_pop(J, 2);
+ return 0;
+}
+
+static int jsR_valueOf(js_State *J, js_Object *obj)
+{
+ js_pushobject(J, obj);
+ js_getproperty(J, -1, "valueOf");
+ if (js_iscallable(J, -1)) {
+ js_rot2(J);
+ js_call(J, 0);
+ if (js_isprimitive(J, -1))
+ return 1;
+ js_pop(J, 1);
+ return 0;
+ }
+ js_pop(J, 2);
+ return 0;
+}
+
js_Value jsR_toprimitive(js_State *J, const js_Value *v, int preferred)
{
+ js_Value vv;
js_Object *obj;
if (v->type != JS_TOBJECT)
@@ -15,15 +64,17 @@
preferred = obj->type == JS_CDATE ? JS_HSTRING : JS_HNUMBER;
if (preferred == JS_HSTRING) {
- // try "toString"
- // if result is primitive, return result
- // try "valueOf"
- // if result is primitive, return result
+ if (jsR_toString(J, obj) || jsR_valueOf(J, obj)) {
+ vv = js_tovalue(J, -1);
+ js_pop(J, 1);
+ return vv;
+ }
} else {
- // try "toString"
- // if result is primitive, return result
- // try "valueOf"
- // if result is primitive, return result
+ if (jsR_valueOf(J, obj) || jsR_toString(J, obj)) {
+ vv = js_tovalue(J, -1);
+ js_pop(J, 1);
+ return vv;
+ }
}
jsR_error(J, "TypeError (ToPrimitive)");
}
@@ -48,11 +99,7 @@
case JS_TNULL: return 0;
case JS_TBOOLEAN: return v->u.boolean;
case JS_TNUMBER: return v->u.number;
- case JS_TSTRING:
- {
- /* TODO: use lexer to parse string grammar */
- return strtod(v->u.string, NULL);
- }
+ case JS_TSTRING: return jsR_stringtonumber(J, v->u.string);
case JS_TOBJECT:
{
js_Value vv = jsR_toprimitive(J, v, JS_HNUMBER);
@@ -68,16 +115,7 @@
case JS_TUNDEFINED: return "undefined";
case JS_TNULL: return "null";
case JS_TBOOLEAN: return v->u.boolean ? "true" : "false";
- case JS_TNUMBER:
- {
- char buf[32];
- double n = v->u.number;
- if (isnan(n)) return "NaN";
- if (isinf(n)) return n < 0 ? "-Infinity" : "Infinity";
- if (n == 0) return "0";
- sprintf(buf, "%.17g", n); /* DBL_DECIMAL_DIG == 17 */
- return js_intern(J, buf);
- }
+ case JS_TNUMBER: return jsR_numbertostring(J, v->u.number);
case JS_TSTRING: return v->u.string;
case JS_TOBJECT:
{