|
|
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.