|
|
1.1 root 1: /* js_internal.c */
2:
3: /* Synchronet "js" object, for internal JavaScript branch and GC control */
4:
5: /* $Id: js_internal.c,v 1.34 2006/12/27 23:25:17 rswindell Exp $ */
6:
7: /****************************************************************************
8: * @format.tab-size 4 (Plain Text/Source Code File Header) *
9: * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
10: * *
11: * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html *
12: * *
13: * This program is free software; you can redistribute it and/or *
14: * modify it under the terms of the GNU General Public License *
15: * as published by the Free Software Foundation; either version 2 *
16: * of the License, or (at your option) any later version. *
17: * See the GNU General Public License for more details: gpl.txt or *
18: * http://www.fsf.org/copyleft/gpl.html *
19: * *
20: * Anonymous FTP access to the most recent released source is available at *
21: * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net *
22: * *
23: * Anonymous CVS access to the development source and modification history *
24: * is available at cvs.synchro.net:/cvsroot/sbbs, example: *
25: * cvs -d :pserver:[email protected]:/cvsroot/sbbs login *
26: * (just hit return, no password is necessary) *
27: * cvs -d :pserver:[email protected]:/cvsroot/sbbs checkout src *
28: * *
29: * For Synchronet coding style and modification guidelines, see *
30: * http://www.synchro.net/source.html *
31: * *
32: * You are encouraged to submit any modifications (preferably in Unix diff *
33: * format) via e-mail to [email protected] *
34: * *
35: * Note: If this box doesn't appear square, then you need to fix your tabs. *
36: ****************************************************************************/
37:
38: #include "sbbs.h"
39:
40: #include <jscntxt.h> /* Needed for Context-private data structure */
41:
42: enum {
43: PROP_VERSION
44: ,PROP_TERMINATED
45: ,PROP_AUTO_TERMINATE
46: ,PROP_BRANCH_COUNTER
47: ,PROP_BRANCH_LIMIT
48: ,PROP_YIELD_INTERVAL
49: ,PROP_GC_INTERVAL
50: ,PROP_GC_ATTEMPTS
51: #ifdef jscntxt_h___
52: ,PROP_GC_COUNTER
53: ,PROP_GC_LASTBYTES
54: ,PROP_BYTES
55: ,PROP_MAXBYTES
56: #endif
57: ,PROP_GLOBAL
58: };
59:
60: static JSBool js_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
61: {
62: jsint tiny;
63: js_branch_t* branch;
64:
65: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
66: return(JS_FALSE);
67:
68: tiny = JSVAL_TO_INT(id);
69:
70: switch(tiny) {
71: case PROP_VERSION:
72: *vp=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,(char *)JS_GetImplementationVersion()));
73: break;
74: case PROP_TERMINATED:
75: if(branch->terminated==NULL)
76: *vp=JSVAL_FALSE;
77: else
78: *vp=BOOLEAN_TO_JSVAL(*branch->terminated);
79: break;
80: case PROP_AUTO_TERMINATE:
81: *vp=BOOLEAN_TO_JSVAL(branch->auto_terminate);
82: break;
83: case PROP_BRANCH_COUNTER:
84: JS_NewNumberValue(cx,branch->counter,vp);
85: break;
86: case PROP_BRANCH_LIMIT:
87: JS_NewNumberValue(cx,branch->limit,vp);
88: break;
89: case PROP_YIELD_INTERVAL:
90: JS_NewNumberValue(cx,branch->yield_interval,vp);
91: break;
92: case PROP_GC_INTERVAL:
93: JS_NewNumberValue(cx,branch->gc_interval,vp);
94: break;
95: case PROP_GC_ATTEMPTS:
96: JS_NewNumberValue(cx,branch->gc_attempts,vp);
97: break;
98: #ifdef jscntxt_h___
99: case PROP_GC_COUNTER:
100: JS_NewNumberValue(cx,cx->runtime->gcNumber,vp);
101: break;
102: case PROP_GC_LASTBYTES:
103: JS_NewNumberValue(cx,cx->runtime->gcLastBytes,vp);
104: break;
105: case PROP_BYTES:
106: JS_NewNumberValue(cx,cx->runtime->gcBytes,vp);
107: break;
108: case PROP_MAXBYTES:
109: JS_NewNumberValue(cx,cx->runtime->gcMaxBytes,vp);
110: break;
111: case PROP_GLOBAL:
112: *vp = OBJECT_TO_JSVAL(cx->globalObject);
113: break;
114: #else
115: case PROP_GLOBAL:
116: *vp = OBJECT_TO_JSVAL(JS_GetParent(cx,obj));
117: break;
118: #endif
119: }
120:
121: return(JS_TRUE);
122: }
123:
124: static JSBool js_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
125: {
126: jsint tiny;
127: js_branch_t* branch;
128:
129: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
130: return(JS_FALSE);
131:
132: tiny = JSVAL_TO_INT(id);
133:
134: switch(tiny) {
135: case PROP_TERMINATED:
136: if(branch->terminated!=NULL)
137: JS_ValueToBoolean(cx, *vp, branch->terminated);
138: break;
139: case PROP_AUTO_TERMINATE:
140: JS_ValueToBoolean(cx,*vp,&branch->auto_terminate);
141: break;
142: case PROP_BRANCH_COUNTER:
143: JS_ValueToInt32(cx, *vp, (int32*)&branch->counter);
144: break;
145: case PROP_BRANCH_LIMIT:
146: JS_ValueToInt32(cx, *vp, (int32*)&branch->limit);
147: break;
148: case PROP_GC_INTERVAL:
149: JS_ValueToInt32(cx, *vp, (int32*)&branch->gc_interval);
150: break;
151: case PROP_YIELD_INTERVAL:
152: JS_ValueToInt32(cx, *vp, (int32*)&branch->yield_interval);
153: break;
154: #ifdef jscntxt_h___
155: case PROP_MAXBYTES:
156: JS_ValueToInt32(cx, *vp, (int32*)&cx->runtime->gcMaxBytes);
157: break;
158: #endif
159: }
160:
161: return(JS_TRUE);
162: }
163:
164: #define PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
165:
166: static jsSyncPropertySpec js_properties[] = {
167: /* name, tinyid, flags, ver */
168:
169: { "version", PROP_VERSION, PROP_FLAGS, 311 },
170: { "auto_terminate", PROP_AUTO_TERMINATE,JSPROP_ENUMERATE, 311 },
171: { "terminated", PROP_TERMINATED, JSPROP_ENUMERATE, 311 },
172: { "branch_counter", PROP_BRANCH_COUNTER,JSPROP_ENUMERATE, 311 },
173: { "branch_limit", PROP_BRANCH_LIMIT, JSPROP_ENUMERATE, 311 },
174: { "yield_interval", PROP_YIELD_INTERVAL,JSPROP_ENUMERATE, 311 },
175: { "gc_interval", PROP_GC_INTERVAL, JSPROP_ENUMERATE, 311 },
176: { "gc_attempts", PROP_GC_ATTEMPTS, PROP_FLAGS, 311 },
177: #ifdef jscntxt_h___
178: { "gc_counter", PROP_GC_COUNTER, PROP_FLAGS, 311 },
179: { "gc_last_bytes", PROP_GC_LASTBYTES, PROP_FLAGS, 311 },
180: { "bytes", PROP_BYTES, PROP_FLAGS, 311 },
181: { "max_bytes", PROP_MAXBYTES, JSPROP_ENUMERATE, 311 },
182: #endif
183: { "global", PROP_GLOBAL, PROP_FLAGS, 314 },
184: {0}
185: };
186:
187: #ifdef BUILD_JSDOCS
188: static char* prop_desc[] = {
189: "JavaScript engine version information (AKA system.js_version)"
190: ,"set to <i>false</i> to disable the automatic termination of the script upon external request"
191: ,"termination has been requested (stop execution as soon as possible)"
192: ,"number of branch operations performed in this runtime"
193: ,"maximum number of branches, used for infinite-loop detection (0=disabled)"
194: ,"interval of periodic time-slice yields (lower number=higher frequency, 0=disabled)"
195: ,"interval of periodic garbage collection attempts (lower number=higher frequency, 0=disabled)"
196: ,"number of garbage collections attempted in this runtime - <small>READ ONLY</small>"
197: #ifdef jscntxt_h___
198: ,"number of garbage collections performed in this runtime - <small>READ ONLY</small>"
199: ,"number of heap bytes in use after last garbage collection - <small>READ ONLY</small>"
200: ,"number of heap bytes currently in use - <small>READ ONLY</small>"
201: ,"maximum number of bytes available for heap"
202: #endif
203: ,"global (top level) object - <small>READ ONLY</small>"
204: ,NULL
205: };
206: #endif
207:
208: JSBool DLLCALL
209: js_CommonBranchCallback(JSContext *cx, js_branch_t* branch)
210: {
211: branch->counter++;
212:
213: /* Terminated? */
214: if(branch->auto_terminate &&
215: (branch->terminated!=NULL && *branch->terminated)) {
216: JS_ReportError(cx,"Terminated");
217: branch->counter=0;
218: return(JS_FALSE);
219: }
220:
221: /* Infinite loop? */
222: if(branch->limit && branch->counter > branch->limit) {
223: JS_ReportError(cx,"Infinite loop (%lu branches) detected",branch->counter);
224: branch->counter=0;
225: return(JS_FALSE);
226: }
227:
228: /* Give up timeslices every once in a while */
229: if(branch->yield_interval && (branch->counter%branch->yield_interval)==0)
230: YIELD();
231:
232: /* Periodic Garbage Collection */
233: if(branch->gc_interval && (branch->counter%branch->gc_interval)==0)
234: JS_MaybeGC(cx), branch->gc_attempts++;
235:
236: return(JS_TRUE);
237: }
238:
239: /* Execute a string in its own context (away from Synchronet objects) */
240: static JSBool
241: js_eval(JSContext *parent_cx, JSObject *parent_obj, uintN argc, jsval *argv, jsval *rval)
242: {
243: char* buf;
244: size_t buflen;
245: JSString* str;
246: JSScript* script;
247: JSContext* cx;
248: JSObject* obj;
249: JSErrorReporter reporter;
250: #ifndef EVAL_BRANCH_CALLBACK
251: JSBranchCallback callback;
252: #endif
253:
254: if(argc<1)
255: return(JS_TRUE);
256:
257: if((str=JS_ValueToString(parent_cx, argv[0]))==NULL)
258: return(JS_FALSE);
259: if((buf=JS_GetStringBytes(str))==NULL)
260: return(JS_FALSE);
261: buflen=JS_GetStringLength(str);
262:
263: if((cx=JS_NewContext(JS_GetRuntime(parent_cx),JAVASCRIPT_CONTEXT_STACK))==NULL)
264: return(JS_FALSE);
265:
266: /* Use the error reporter from the parent context */
267: reporter=JS_SetErrorReporter(parent_cx,NULL);
268: JS_SetErrorReporter(parent_cx,reporter);
269: JS_SetErrorReporter(cx,reporter);
270:
271: #ifdef EVAL_BRANCH_CALLBACK
272: JS_SetContextPrivate(cx, JS_GetPrivate(parent_cx, parent_obj));
273: JS_SetBranchCallback(cx, js_BranchCallback);
274: #else /* Use the branch callback from the parent context */
275: JS_SetContextPrivate(cx, JS_GetContextPrivate(parent_cx));
276: callback=JS_SetBranchCallback(parent_cx,NULL);
277: JS_SetBranchCallback(parent_cx, callback);
278: JS_SetBranchCallback(cx, callback);
279: #endif
280:
281: if((obj=JS_NewObject(cx, NULL, NULL, NULL))==NULL
282: || !JS_InitStandardClasses(cx,obj)) {
283: JS_DestroyContext(cx);
284: return(JS_FALSE);
285: }
286:
287: if((script=JS_CompileScript(cx, obj, buf, buflen, NULL, 0))!=NULL) {
288: JS_ExecuteScript(cx, obj, script, rval);
289: JS_DestroyScript(cx, script);
290: }
291:
292: JS_DestroyContext(cx);
293:
294: return(JS_TRUE);
295: }
296:
297: static JSBool
298: js_gc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
299: {
300: JSBool forced=JS_TRUE;
301: js_branch_t* branch;
302:
303: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
304: return(JS_FALSE);
305:
306: if(argc)
307: JS_ValueToBoolean(cx,argv[0],&forced);
308:
309: if(forced)
310: JS_GC(cx);
311: else
312: JS_MaybeGC(cx);
313:
314: branch->gc_attempts++;
315:
316: return(JS_TRUE);
317: }
318:
319: static JSBool
320: js_report_error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
321: {
322: JS_ReportError(cx,"%s",JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
323:
324: if(argc>1 && argv[1]==JSVAL_TRUE)
325: return(JS_FALSE); /* fatal */
326:
327: return(JS_TRUE);
328: }
329:
330: static JSBool
331: js_on_exit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
332: {
333: js_branch_t* branch;
334:
335: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
336: return(JS_FALSE);
337:
338: if(branch->exit_func==NULL)
339: branch->exit_func=strListInit();
340:
341: strListPush(&branch->exit_func,JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
342:
343: return(JS_TRUE);
344: }
345:
346: static JSBool
347: js_get_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
348: {
349: JSObject* child=NULL;
350: JSObject* parent;
351:
352: if(JS_ValueToObject(cx, argv[0], &child)
353: && child!=NULL
354: && (parent=JS_GetParent(cx,child))!=NULL)
355: *rval = OBJECT_TO_JSVAL(parent);
356:
357: return(JS_TRUE);
358: }
359:
360: static JSClass js_internal_class = {
361: "JsInternal" /* name */
362: ,JSCLASS_HAS_PRIVATE /* flags */
363: ,JS_PropertyStub /* addProperty */
364: ,JS_PropertyStub /* delProperty */
365: ,js_get /* getProperty */
366: ,js_set /* setProperty */
367: ,JS_EnumerateStub /* enumerate */
368: ,JS_ResolveStub /* resolve */
369: ,JS_ConvertStub /* convert */
370: ,JS_FinalizeStub /* finalize */
371: };
372:
373: static jsSyncMethodSpec js_functions[] = {
374: {"eval", js_eval, 0, JSTYPE_UNDEF, JSDOCSTR("script")
375: ,JSDOCSTR("evaluate a JavaScript string in its own (secure) context, returning the result")
376: ,311
377: },
378: {"gc", js_gc, 0, JSTYPE_VOID, JSDOCSTR("forced=<tt>true</tt>")
379: ,JSDOCSTR("perform a garbage collection operation (freeing memory for unused allocated objects), "
380: "if <i>forced</i> is <i>true</i> (the default) a garbage collection is always performed, "
381: "otherwise it is only performed if deemed appropriate by the JavaScript engine")
382: ,311
383: },
384: {"on_exit", js_on_exit, 1, JSTYPE_VOID, JSDOCSTR("to_eval")
385: ,JSDOCSTR("add a string to evaluate/execute (LIFO stack) upon script's termination")
386: ,313
387: },
388: {"report_error", js_report_error, 1, JSTYPE_VOID, JSDOCSTR("error [,fatal=<tt>false</tt>]")
389: ,JSDOCSTR("report an error using the standard JavaScript error reporting mechanism "
390: "(including script filename and line number), "
391: "if <i>fatal</i> is <i>true</i>, immediately terminates script")
392: ,313
393: },
394: {"get_parent", js_get_parent, 1, JSTYPE_OBJECT, JSDOCSTR("object")
395: ,JSDOCSTR("return the parent of the specified object")
396: ,314
397: },
398: {0}
399: };
400:
401: void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_branch_t* branch)
402: {
403: char* p;
404: jsval rval;
405: JSScript* script;
406:
407: while((p=strListPop(&branch->exit_func))!=NULL) {
408: if((script=JS_CompileScript(cx, obj, p, strlen(p), NULL, 0))!=NULL) {
409: JS_ExecuteScript(cx, obj, script, &rval);
410: JS_DestroyScript(cx, script);
411: }
412: }
413:
414: strListFree(&branch->exit_func);
415: }
416:
417: JSObject* DLLCALL js_CreateInternalJsObject(JSContext* cx, JSObject* parent, js_branch_t* branch)
418: {
419: JSObject* obj;
420:
421: if((obj = JS_DefineObject(cx, parent, "js", &js_internal_class, NULL
422: ,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
423: return(NULL);
424:
425: if(!JS_SetPrivate(cx, obj, branch)) /* Store a pointer to js_branch_t */
426: return(NULL);
427:
428: if(!js_DefineSyncProperties(cx, obj, js_properties)) /* expose them */
429: return(NULL);
430:
431: if(!js_DefineSyncMethods(cx, obj, js_functions, /* append? */ FALSE))
432: return(NULL);
433:
434: #ifdef BUILD_JSDOCS
435: js_DescribeSyncObject(cx,obj,"JavaScript execution and garbage collection control object",311);
436: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", prop_desc, JSPROP_READONLY);
437: #endif
438:
439: return(obj);
440: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.