1 #include "jsi.h"
2 #include "jsparse.h"
3 #include "jscompile.h"
4 #include "jsvalue.h"
5 #include "jsbuiltin.h"
6 
jsB_Function(js_State * J)7 static void jsB_Function(js_State *J)
8 {
9 	int i, top = js_gettop(J);
10 	js_Buffer *sb = NULL;
11 	const char *body;
12 	js_Ast *parse;
13 	js_Function *fun;
14 
15 	if (js_try(J)) {
16 		js_free(J, sb);
17 		jsP_freeparse(J);
18 		js_throw(J);
19 	}
20 
21 	/* p1, p2, ..., pn */
22 	if (top > 2) {
23 		for (i = 1; i < top - 1; ++i) {
24 			if (i > 1)
25 				js_putc(J, &sb, ',');
26 			js_puts(J, &sb, js_tostring(J, i));
27 		}
28 		js_putc(J, &sb, ')');
29 		js_putc(J, &sb, 0);
30 	}
31 
32 	/* body */
33 	body = js_isdefined(J, top - 1) ? js_tostring(J, top - 1) : "";
34 
35 	parse = jsP_parsefunction(J, "[string]", sb ? sb->s : NULL, body);
36 	fun = jsC_compilefunction(J, parse);
37 
38 	js_endtry(J);
39 	js_free(J, sb);
40 	jsP_freeparse(J);
41 
42 	js_newfunction(J, fun, J->GE);
43 }
44 
jsB_Function_prototype(js_State * J)45 static void jsB_Function_prototype(js_State *J)
46 {
47 	js_pushundefined(J);
48 }
49 
Fp_toString(js_State * J)50 static void Fp_toString(js_State *J)
51 {
52 	js_Object *self = js_toobject(J, 0);
53 	js_Buffer *sb = NULL;
54 	int i;
55 
56 	if (!js_iscallable(J, 0))
57 		js_typeerror(J, "not a function");
58 
59 	if (self->type == JS_CFUNCTION || self->type == JS_CSCRIPT) {
60 		js_Function *F = self->u.f.function;
61 
62 		if (js_try(J)) {
63 			js_free(J, sb);
64 			js_throw(J);
65 		}
66 
67 		js_puts(J, &sb, "function ");
68 		js_puts(J, &sb, F->name);
69 		js_putc(J, &sb, '(');
70 		for (i = 0; i < F->numparams; ++i) {
71 			if (i > 0) js_putc(J, &sb, ',');
72 			js_puts(J, &sb, F->vartab[i]);
73 		}
74 		js_puts(J, &sb, ") { ... }");
75 		js_putc(J, &sb, 0);
76 
77 		js_pushstring(J, sb->s);
78 		js_endtry(J);
79 		js_free(J, sb);
80 	} else if (self->type == JS_CCFUNCTION) {
81 		if (js_try(J)) {
82 			js_free(J, sb);
83 			js_throw(J);
84 		}
85 
86 		js_puts(J, &sb, "function ");
87 		js_puts(J, &sb, self->u.c.name);
88 		js_puts(J, &sb, "() { ... }");
89 		js_putc(J, &sb, 0);
90 
91 		js_pushstring(J, sb->s);
92 		js_endtry(J);
93 		js_free(J, sb);
94 	} else {
95 		js_pushliteral(J, "function () { ... }");
96 	}
97 }
98 
Fp_apply(js_State * J)99 static void Fp_apply(js_State *J)
100 {
101 	int i, n;
102 
103 	if (!js_iscallable(J, 0))
104 		js_typeerror(J, "not a function");
105 
106 	js_copy(J, 0);
107 	js_copy(J, 1);
108 
109 	if (js_isnull(J, 2) || js_isundefined(J, 2)) {
110 		n = 0;
111 	} else {
112 		n = js_getlength(J, 2);
113 		for (i = 0; i < n; ++i)
114 			js_getindex(J, 2, i);
115 	}
116 
117 	js_call(J, n);
118 }
119 
Fp_call(js_State * J)120 static void Fp_call(js_State *J)
121 {
122 	int i, top = js_gettop(J);
123 
124 	if (!js_iscallable(J, 0))
125 		js_typeerror(J, "not a function");
126 
127 	for (i = 0; i < top; ++i)
128 		js_copy(J, i);
129 
130 	js_call(J, top - 2);
131 }
132 
callbound(js_State * J)133 static void callbound(js_State *J)
134 {
135 	int top = js_gettop(J);
136 	int i, fun, args, n;
137 
138 	fun = js_gettop(J);
139 	js_currentfunction(J);
140 	js_getproperty(J, fun, "__TargetFunction__");
141 	js_getproperty(J, fun, "__BoundThis__");
142 
143 	args = js_gettop(J);
144 	js_getproperty(J, fun, "__BoundArguments__");
145 	n = js_getlength(J, args);
146 	for (i = 0; i < n; ++i)
147 		js_getindex(J, args, i);
148 	js_remove(J, args);
149 
150 	for (i = 1; i < top; ++i)
151 		js_copy(J, i);
152 
153 	js_call(J, n + top - 1);
154 }
155 
constructbound(js_State * J)156 static void constructbound(js_State *J)
157 {
158 	int top = js_gettop(J);
159 	int i, fun, args, n;
160 
161 	fun = js_gettop(J);
162 	js_currentfunction(J);
163 	js_getproperty(J, fun, "__TargetFunction__");
164 
165 	args = js_gettop(J);
166 	js_getproperty(J, fun, "__BoundArguments__");
167 	n = js_getlength(J, args);
168 	for (i = 0; i < n; ++i)
169 		js_getindex(J, args, i);
170 	js_remove(J, args);
171 
172 	for (i = 1; i < top; ++i)
173 		js_copy(J, i);
174 
175 	js_construct(J, n + top - 1);
176 }
177 
Fp_bind(js_State * J)178 static void Fp_bind(js_State *J)
179 {
180 	int i, top = js_gettop(J);
181 	int n;
182 
183 	if (!js_iscallable(J, 0))
184 		js_typeerror(J, "not a function");
185 
186 	n = js_getlength(J, 0);
187 	if (n > top - 2)
188 		n -= top - 2;
189 	else
190 		n = 0;
191 
192 	/* Reuse target function's prototype for HasInstance check. */
193 	js_getproperty(J, 0, "prototype");
194 	js_newcconstructor(J, callbound, constructbound, "[bind]", n);
195 
196 	/* target function */
197 	js_copy(J, 0);
198 	js_defproperty(J, -2, "__TargetFunction__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
199 
200 	/* bound this */
201 	js_copy(J, 1);
202 	js_defproperty(J, -2, "__BoundThis__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
203 
204 	/* bound arguments */
205 	js_newarray(J);
206 	for (i = 2; i < top; ++i) {
207 		js_copy(J, i);
208 		js_setindex(J, -2, i - 2);
209 	}
210 	js_defproperty(J, -2, "__BoundArguments__", JS_READONLY | JS_DONTENUM | JS_DONTCONF);
211 }
212 
jsB_initfunction(js_State * J)213 void jsB_initfunction(js_State *J)
214 {
215 	J->Function_prototype->u.c.name = "Function.prototype";
216 	J->Function_prototype->u.c.function = jsB_Function_prototype;
217 	J->Function_prototype->u.c.constructor = NULL;
218 	J->Function_prototype->u.c.length = 0;
219 
220 	js_pushobject(J, J->Function_prototype);
221 	{
222 		jsB_propf(J, "Function.prototype.toString", Fp_toString, 2);
223 		jsB_propf(J, "Function.prototype.apply", Fp_apply, 2);
224 		jsB_propf(J, "Function.prototype.call", Fp_call, 1);
225 		jsB_propf(J, "Function.prototype.bind", Fp_bind, 1);
226 	}
227 	js_newcconstructor(J, jsB_Function, jsB_Function, "Function", 1);
228 	js_defglobal(J, "Function", JS_DONTENUM);
229 }
230