|
|
1.1 root 1: /* js_internal.c */
2:
3: /* Synchronet "js" object, for internal JavaScript branch and GC control */
4:
1.1.1.2 ! root 5: /* $Id: js_internal.c,v 1.55 2011/08/31 22:01:27 rswindell Exp $ */
1.1 root 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: * *
1.1.1.2 ! root 11: * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html *
1.1 root 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"
1.1.1.2 ! root 39: #include "js_request.h"
1.1 root 40:
1.1.1.2 ! root 41: #ifdef _DEBUG
! 42: #include <jscntxt.h> /* Needed for Context-private data structure */
! 43: #endif
1.1 root 44:
45: enum {
46: PROP_VERSION
47: ,PROP_TERMINATED
48: ,PROP_AUTO_TERMINATE
49: ,PROP_BRANCH_COUNTER
50: ,PROP_BRANCH_LIMIT
51: ,PROP_YIELD_INTERVAL
52: ,PROP_GC_INTERVAL
53: ,PROP_GC_ATTEMPTS
54: #ifdef jscntxt_h___
55: ,PROP_GC_COUNTER
56: ,PROP_GC_LASTBYTES
57: ,PROP_BYTES
58: ,PROP_MAXBYTES
59: #endif
60: ,PROP_GLOBAL
61: };
62:
63: static JSBool js_get(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
64: {
65: jsint tiny;
66: js_branch_t* branch;
67:
68: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
69: return(JS_FALSE);
70:
71: tiny = JSVAL_TO_INT(id);
72:
73: switch(tiny) {
74: case PROP_VERSION:
75: *vp=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,(char *)JS_GetImplementationVersion()));
76: break;
77: case PROP_TERMINATED:
78: if(branch->terminated==NULL)
79: *vp=JSVAL_FALSE;
80: else
81: *vp=BOOLEAN_TO_JSVAL(*branch->terminated);
82: break;
83: case PROP_AUTO_TERMINATE:
84: *vp=BOOLEAN_TO_JSVAL(branch->auto_terminate);
85: break;
86: case PROP_BRANCH_COUNTER:
87: JS_NewNumberValue(cx,branch->counter,vp);
88: break;
89: case PROP_BRANCH_LIMIT:
90: JS_NewNumberValue(cx,branch->limit,vp);
91: break;
92: case PROP_YIELD_INTERVAL:
93: JS_NewNumberValue(cx,branch->yield_interval,vp);
94: break;
95: case PROP_GC_INTERVAL:
96: JS_NewNumberValue(cx,branch->gc_interval,vp);
97: break;
98: case PROP_GC_ATTEMPTS:
99: JS_NewNumberValue(cx,branch->gc_attempts,vp);
100: break;
101: #ifdef jscntxt_h___
102: case PROP_GC_COUNTER:
103: JS_NewNumberValue(cx,cx->runtime->gcNumber,vp);
104: break;
105: case PROP_GC_LASTBYTES:
106: JS_NewNumberValue(cx,cx->runtime->gcLastBytes,vp);
107: break;
108: case PROP_BYTES:
109: JS_NewNumberValue(cx,cx->runtime->gcBytes,vp);
110: break;
111: case PROP_MAXBYTES:
112: JS_NewNumberValue(cx,cx->runtime->gcMaxBytes,vp);
113: break;
1.1.1.2 ! root 114: #endif
1.1 root 115: case PROP_GLOBAL:
1.1.1.2 ! root 116: *vp = OBJECT_TO_JSVAL(JS_GetGlobalObject(cx));
1.1 root 117: break;
118: }
119:
120: return(JS_TRUE);
121: }
122:
123: static JSBool js_set(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
124: {
125: jsint tiny;
126: js_branch_t* branch;
127:
128: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
129: return(JS_FALSE);
130:
131: tiny = JSVAL_TO_INT(id);
132:
133: switch(tiny) {
134: case PROP_TERMINATED:
135: if(branch->terminated!=NULL)
1.1.1.2 ! root 136: JS_ValueToBoolean(cx, *vp, (int *)branch->terminated);
1.1 root 137: break;
138: case PROP_AUTO_TERMINATE:
139: JS_ValueToBoolean(cx,*vp,&branch->auto_terminate);
140: break;
141: case PROP_BRANCH_COUNTER:
142: JS_ValueToInt32(cx, *vp, (int32*)&branch->counter);
143: break;
144: case PROP_BRANCH_LIMIT:
145: JS_ValueToInt32(cx, *vp, (int32*)&branch->limit);
146: break;
147: case PROP_GC_INTERVAL:
148: JS_ValueToInt32(cx, *vp, (int32*)&branch->gc_interval);
149: break;
150: case PROP_YIELD_INTERVAL:
151: JS_ValueToInt32(cx, *vp, (int32*)&branch->yield_interval);
152: break;
153: #ifdef jscntxt_h___
154: case PROP_MAXBYTES:
155: JS_ValueToInt32(cx, *vp, (int32*)&cx->runtime->gcMaxBytes);
156: break;
157: #endif
158: }
159:
160: return(JS_TRUE);
161: }
162:
163: #define PROP_FLAGS JSPROP_ENUMERATE|JSPROP_READONLY
164:
165: static jsSyncPropertySpec js_properties[] = {
166: /* name, tinyid, flags, ver */
167:
168: { "version", PROP_VERSION, PROP_FLAGS, 311 },
169: { "auto_terminate", PROP_AUTO_TERMINATE,JSPROP_ENUMERATE, 311 },
170: { "terminated", PROP_TERMINATED, JSPROP_ENUMERATE, 311 },
171: { "branch_counter", PROP_BRANCH_COUNTER,JSPROP_ENUMERATE, 311 },
172: { "branch_limit", PROP_BRANCH_LIMIT, JSPROP_ENUMERATE, 311 },
173: { "yield_interval", PROP_YIELD_INTERVAL,JSPROP_ENUMERATE, 311 },
174: { "gc_interval", PROP_GC_INTERVAL, JSPROP_ENUMERATE, 311 },
175: { "gc_attempts", PROP_GC_ATTEMPTS, PROP_FLAGS, 311 },
176: #ifdef jscntxt_h___
177: { "gc_counter", PROP_GC_COUNTER, PROP_FLAGS, 311 },
178: { "gc_last_bytes", PROP_GC_LASTBYTES, PROP_FLAGS, 311 },
179: { "bytes", PROP_BYTES, PROP_FLAGS, 311 },
180: { "max_bytes", PROP_MAXBYTES, JSPROP_ENUMERATE, 311 },
181: #endif
182: { "global", PROP_GLOBAL, PROP_FLAGS, 314 },
183: {0}
184: };
185:
186: #ifdef BUILD_JSDOCS
187: static char* prop_desc[] = {
188: "JavaScript engine version information (AKA system.js_version)"
189: ,"set to <i>false</i> to disable the automatic termination of the script upon external request"
190: ,"termination has been requested (stop execution as soon as possible)"
191: ,"number of branch operations performed in this runtime"
192: ,"maximum number of branches, used for infinite-loop detection (0=disabled)"
193: ,"interval of periodic time-slice yields (lower number=higher frequency, 0=disabled)"
194: ,"interval of periodic garbage collection attempts (lower number=higher frequency, 0=disabled)"
195: ,"number of garbage collections attempted in this runtime - <small>READ ONLY</small>"
196: #ifdef jscntxt_h___
197: ,"number of garbage collections performed in this runtime - <small>READ ONLY</small>"
198: ,"number of heap bytes in use after last garbage collection - <small>READ ONLY</small>"
199: ,"number of heap bytes currently in use - <small>READ ONLY</small>"
200: ,"maximum number of bytes available for heap"
201: #endif
202: ,"global (top level) object - <small>READ ONLY</small>"
203: ,NULL
204: };
205: #endif
206:
207: JSBool DLLCALL
208: js_CommonBranchCallback(JSContext *cx, js_branch_t* branch)
209: {
210: branch->counter++;
211:
212: /* Terminated? */
213: if(branch->auto_terminate &&
214: (branch->terminated!=NULL && *branch->terminated)) {
1.1.1.2 ! root 215: JS_ReportWarning(cx,"Terminated");
1.1 root 216: branch->counter=0;
217: return(JS_FALSE);
218: }
219:
220: /* Infinite loop? */
221: if(branch->limit && branch->counter > branch->limit) {
222: JS_ReportError(cx,"Infinite loop (%lu branches) detected",branch->counter);
223: branch->counter=0;
224: return(JS_FALSE);
225: }
226:
1.1.1.2 ! root 227: #ifndef USE_JS_OPERATION_CALLBACK
1.1 root 228: /* Give up timeslices every once in a while */
1.1.1.2 ! root 229: if(branch->yield_interval && (branch->counter%branch->yield_interval)==0) {
! 230: jsrefcount rc;
! 231:
! 232: rc=JS_SUSPENDREQUEST(cx);
1.1 root 233: YIELD();
1.1.1.2 ! root 234: JS_RESUMEREQUEST(cx, rc);
! 235: }
1.1 root 236:
237: /* Periodic Garbage Collection */
238: if(branch->gc_interval && (branch->counter%branch->gc_interval)==0)
239: JS_MaybeGC(cx), branch->gc_attempts++;
1.1.1.2 ! root 240: #endif
1.1 root 241:
242: return(JS_TRUE);
243: }
244:
245: /* Execute a string in its own context (away from Synchronet objects) */
246: static JSBool
247: js_eval(JSContext *parent_cx, JSObject *parent_obj, uintN argc, jsval *argv, jsval *rval)
248: {
249: char* buf;
250: size_t buflen;
251: JSString* str;
252: JSScript* script;
253: JSContext* cx;
254: JSObject* obj;
255: JSErrorReporter reporter;
256: #ifndef EVAL_BRANCH_CALLBACK
1.1.1.2 ! root 257: #ifndef USE_JS_OPERATION_CALLBACK
1.1 root 258: JSBranchCallback callback;
259: #endif
1.1.1.2 ! root 260: #endif
1.1 root 261:
262: if(argc<1)
263: return(JS_TRUE);
264:
265: if((str=JS_ValueToString(parent_cx, argv[0]))==NULL)
266: return(JS_FALSE);
267: if((buf=JS_GetStringBytes(str))==NULL)
268: return(JS_FALSE);
269: buflen=JS_GetStringLength(str);
270:
271: if((cx=JS_NewContext(JS_GetRuntime(parent_cx),JAVASCRIPT_CONTEXT_STACK))==NULL)
272: return(JS_FALSE);
273:
274: /* Use the error reporter from the parent context */
275: reporter=JS_SetErrorReporter(parent_cx,NULL);
276: JS_SetErrorReporter(parent_cx,reporter);
277: JS_SetErrorReporter(cx,reporter);
278:
279: #ifdef EVAL_BRANCH_CALLBACK
280: JS_SetContextPrivate(cx, JS_GetPrivate(parent_cx, parent_obj));
1.1.1.2 ! root 281: #ifdef USE_JS_OPERATION_CALLBACK
! 282: JS_SetOperationCallback(cx, js_OperationCallback);
! 283: #else
1.1 root 284: JS_SetBranchCallback(cx, js_BranchCallback);
1.1.1.2 ! root 285: #endif
1.1 root 286: #else /* Use the branch callback from the parent context */
287: JS_SetContextPrivate(cx, JS_GetContextPrivate(parent_cx));
1.1.1.2 ! root 288: #ifdef USE_JS_OPERATION_CALLBACK
! 289: JS_SetOperationCallback(cx, JS_GetOperationCallback(parent_cx));
! 290: #else
1.1 root 291: callback=JS_SetBranchCallback(parent_cx,NULL);
292: JS_SetBranchCallback(parent_cx, callback);
293: JS_SetBranchCallback(cx, callback);
294: #endif
1.1.1.2 ! root 295: #endif
1.1 root 296:
297: if((obj=JS_NewObject(cx, NULL, NULL, NULL))==NULL
298: || !JS_InitStandardClasses(cx,obj)) {
299: JS_DestroyContext(cx);
300: return(JS_FALSE);
301: }
302:
303: if((script=JS_CompileScript(cx, obj, buf, buflen, NULL, 0))!=NULL) {
304: JS_ExecuteScript(cx, obj, script, rval);
305: JS_DestroyScript(cx, script);
306: }
307:
308: JS_DestroyContext(cx);
309:
310: return(JS_TRUE);
311: }
312:
313: static JSBool
314: js_gc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
315: {
316: JSBool forced=JS_TRUE;
317: js_branch_t* branch;
318:
319: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
320: return(JS_FALSE);
321:
322: if(argc)
323: JS_ValueToBoolean(cx,argv[0],&forced);
324:
325: if(forced)
326: JS_GC(cx);
327: else
328: JS_MaybeGC(cx);
329:
330: branch->gc_attempts++;
331:
332: return(JS_TRUE);
333: }
334:
335: static JSBool
336: js_report_error(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
337: {
338: JS_ReportError(cx,"%s",JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
339:
340: if(argc>1 && argv[1]==JSVAL_TRUE)
341: return(JS_FALSE); /* fatal */
342:
343: return(JS_TRUE);
344: }
345:
346: static JSBool
347: js_on_exit(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
348: {
349: js_branch_t* branch;
350:
351: if((branch=(js_branch_t*)JS_GetPrivate(cx,obj))==NULL)
352: return(JS_FALSE);
353:
354: if(branch->exit_func==NULL)
355: branch->exit_func=strListInit();
356:
357: strListPush(&branch->exit_func,JS_GetStringBytes(JS_ValueToString(cx, argv[0])));
358:
359: return(JS_TRUE);
360: }
361:
362: static JSBool
363: js_get_parent(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
364: {
365: JSObject* child=NULL;
366: JSObject* parent;
367:
368: if(JS_ValueToObject(cx, argv[0], &child)
369: && child!=NULL
370: && (parent=JS_GetParent(cx,child))!=NULL)
371: *rval = OBJECT_TO_JSVAL(parent);
372:
373: return(JS_TRUE);
374: }
375:
376: static jsSyncMethodSpec js_functions[] = {
377: {"eval", js_eval, 0, JSTYPE_UNDEF, JSDOCSTR("script")
378: ,JSDOCSTR("evaluate a JavaScript string in its own (secure) context, returning the result")
379: ,311
380: },
381: {"gc", js_gc, 0, JSTYPE_VOID, JSDOCSTR("forced=<tt>true</tt>")
382: ,JSDOCSTR("perform a garbage collection operation (freeing memory for unused allocated objects), "
383: "if <i>forced</i> is <i>true</i> (the default) a garbage collection is always performed, "
384: "otherwise it is only performed if deemed appropriate by the JavaScript engine")
385: ,311
386: },
387: {"on_exit", js_on_exit, 1, JSTYPE_VOID, JSDOCSTR("to_eval")
388: ,JSDOCSTR("add a string to evaluate/execute (LIFO stack) upon script's termination")
389: ,313
390: },
391: {"report_error", js_report_error, 1, JSTYPE_VOID, JSDOCSTR("error [,fatal=<tt>false</tt>]")
392: ,JSDOCSTR("report an error using the standard JavaScript error reporting mechanism "
393: "(including script filename and line number), "
394: "if <i>fatal</i> is <i>true</i>, immediately terminates script")
395: ,313
396: },
397: {"get_parent", js_get_parent, 1, JSTYPE_OBJECT, JSDOCSTR("object")
398: ,JSDOCSTR("return the parent of the specified object")
399: ,314
400: },
401: {0}
402: };
403:
1.1.1.2 ! root 404: static JSBool js_internal_resolve(JSContext *cx, JSObject *obj, jsval id)
! 405: {
! 406: char* name=NULL;
! 407:
! 408: if(id != JSVAL_NULL)
! 409: name=JS_GetStringBytes(JSVAL_TO_STRING(id));
! 410:
! 411: return(js_SyncResolve(cx, obj, name, js_properties, js_functions, NULL, 0));
! 412: }
! 413:
! 414: static JSBool js_internal_enumerate(JSContext *cx, JSObject *obj)
! 415: {
! 416: return(js_internal_resolve(cx, obj, JSVAL_NULL));
! 417: }
! 418:
! 419: static JSClass js_internal_class = {
! 420: "JsInternal" /* name */
! 421: ,JSCLASS_HAS_PRIVATE /* flags */
! 422: ,JS_PropertyStub /* addProperty */
! 423: ,JS_PropertyStub /* delProperty */
! 424: ,js_get /* getProperty */
! 425: ,js_set /* setProperty */
! 426: ,js_internal_enumerate /* enumerate */
! 427: ,js_internal_resolve /* resolve */
! 428: ,JS_ConvertStub /* convert */
! 429: ,JS_FinalizeStub /* finalize */
! 430: };
! 431:
1.1 root 432: void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_branch_t* branch)
433: {
434: char* p;
435: jsval rval;
436: JSScript* script;
1.1.1.2 ! root 437: BOOL auto_terminate=branch->auto_terminate;
! 438:
! 439: branch->auto_terminate=FALSE;
1.1 root 440:
441: while((p=strListPop(&branch->exit_func))!=NULL) {
442: if((script=JS_CompileScript(cx, obj, p, strlen(p), NULL, 0))!=NULL) {
443: JS_ExecuteScript(cx, obj, script, &rval);
444: JS_DestroyScript(cx, script);
445: }
1.1.1.2 ! root 446: free(p);
1.1 root 447: }
448:
449: strListFree(&branch->exit_func);
1.1.1.2 ! root 450:
! 451: if(auto_terminate)
! 452: branch->auto_terminate = TRUE;
1.1 root 453: }
454:
1.1.1.2 ! root 455: JSObject* DLLCALL js_CreateInternalJsObject(JSContext* cx, JSObject* parent, js_branch_t* branch, js_startup_t* startup)
1.1 root 456: {
457: JSObject* obj;
458:
459: if((obj = JS_DefineObject(cx, parent, "js", &js_internal_class, NULL
460: ,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
461: return(NULL);
462:
463: if(!JS_SetPrivate(cx, obj, branch)) /* Store a pointer to js_branch_t */
464: return(NULL);
465:
1.1.1.2 ! root 466: if(startup!=NULL) {
! 467: JSObject* load_path_list;
! 468: jsval val;
! 469: str_list_t load_path;
! 470:
! 471: if((load_path_list=JS_NewArrayObject(cx, 0, NULL))==NULL)
! 472: return(NULL);
! 473: val=OBJECT_TO_JSVAL(load_path_list);
! 474: if(!JS_SetProperty(cx, obj, JAVASCRIPT_LOAD_PATH_LIST, &val))
! 475: return(NULL);
! 476:
! 477: if((load_path=strListSplitCopy(NULL, startup->load_path, ",")) != NULL) {
! 478: JSString* js_str;
! 479: unsigned i;
! 480:
! 481: for(i=0; load_path[i]!=NULL; i++) {
! 482: if((js_str=JS_NewStringCopyZ(cx, load_path[i]))==NULL)
! 483: break;
! 484: val=STRING_TO_JSVAL(js_str);
! 485: if(!JS_SetElement(cx, load_path_list, i, &val))
! 486: break;
! 487: }
! 488: strListFree(&load_path);
! 489: }
! 490: }
1.1 root 491:
492: #ifdef BUILD_JSDOCS
493: js_DescribeSyncObject(cx,obj,"JavaScript execution and garbage collection control object",311);
494: js_CreateArrayOfStrings(cx, obj, "_property_desc_list", prop_desc, JSPROP_READONLY);
495: #endif
496:
497: return(obj);
498: }
1.1.1.2 ! root 499:
! 500: void DLLCALL js_PrepareToExecute(JSContext *cx, JSObject *obj, const char *filename, const char* startup_dir)
! 501: {
! 502: JSString* str;
! 503: jsval val;
! 504:
! 505: if(JS_GetProperty(cx, obj, "js", &val) && JSVAL_IS_OBJECT(val)) {
! 506: JSObject* js = JSVAL_TO_OBJECT(val);
! 507: char dir[MAX_PATH+1];
! 508:
! 509: if(filename!=NULL) {
! 510: char* p;
! 511:
! 512: if((str=JS_NewStringCopyZ(cx, filename)) != NULL)
! 513: JS_DefineProperty(cx, js, "exec_path", STRING_TO_JSVAL(str)
! 514: ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
! 515: if((str=JS_NewStringCopyZ(cx, getfname(filename))) != NULL)
! 516: JS_DefineProperty(cx, js, "exec_file", STRING_TO_JSVAL(str)
! 517: ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
! 518: SAFECOPY(dir,filename);
! 519: p=getfname(dir);
! 520: *p=0;
! 521: if((str=JS_NewStringCopyZ(cx, dir)) != NULL)
! 522: JS_DefineProperty(cx, js, "exec_dir", STRING_TO_JSVAL(str)
! 523: ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
! 524: }
! 525: if(startup_dir==NULL)
! 526: startup_dir="";
! 527: if((str=JS_NewStringCopyZ(cx, startup_dir)) != NULL)
! 528: JS_DefineProperty(cx, js, "startup_dir", STRING_TO_JSVAL(str)
! 529: ,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
! 530: }
! 531: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.