From 2c7f3bc699f32ec7f017b82e7f2ae8bf03c6e893 Mon Sep 17 00:00:00 2001 From: Rob Swindell <rob@synchro.net> Date: Sat, 6 Mar 2021 13:49:04 -0800 Subject: [PATCH] Allow fine-grained control over JavaScript compiler options via *.ini MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit JavaScriptOptions bit-field can be set in sbbs.ini and jsexec.ini to over-ride the default JS compiler options which have been changed from 0 to (options previously only used by JSDoor): JIT | METHODJIT | COMPILE_N_GO | PROFILING * JIT - TraceMonkey * METHODJIT - JägerMonkey * COMPILE_N_GO - compile-time scope chain resolution of consts * PROFILING - Choose between TraceMonkey and JägerMonkey at compile-time based on profiling results Other options available but not enabled by default: * STRICT - warn on debious practice (i.e. similar to "use strict") * WERROR - convert warnings to errors * VAROBJFIX - use last object on scope chain as the ECMA 'variables object' * RELIMIT - Throw exception on any regular expression which backtracks more than n^3 times, where n is length of the input string * ANONFUNFIX - Disallow function () {} in statement context per ECMA-262 Edition 3. * METHODJIT_ALWAYS - Always whole-method JIT, don't tune at run-time. Also: - Fixed JS warning string formatting (missing space separator). - Removed an extraneous new-line in lprintf() call in mailsrvr. - Added basic assertEq() global method to jsexec, required when running SpiderMonkey test scripts. --- src/sbbs3/js_global.c | 16 +--------------- src/sbbs3/jsexec.c | 33 +++++++++++++++++++++++++++++---- src/sbbs3/mailsrvr.c | 3 ++- src/sbbs3/main.cpp | 5 +++-- src/sbbs3/sbbs_ini.c | 4 ++++ src/sbbs3/sbbsdefs.h | 1 + src/sbbs3/services.c | 1 + src/sbbs3/startup.h | 17 +++++++++++++++++ src/sbbs3/websrvr.c | 4 +++- 9 files changed, 61 insertions(+), 23 deletions(-) diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c index 9384c900c4..1401886058 100644 --- a/src/sbbs3/js_global.c +++ b/src/sbbs3/js_global.c @@ -1,8 +1,5 @@ /* Synchronet JavaScript "global" object properties/methods for all servers */ -/* $Id: js_global.c,v 1.409 2020/08/09 01:53:52 rswindell Exp $ */ -// vi: tabstop=4 - /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * @@ -16,21 +13,9 @@ * See the GNU General Public License for more details: gpl.txt or * * http://www.fsf.org/copyleft/gpl.html * * * - * Anonymous FTP access to the most recent released source is available at * - * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net * - * * - * Anonymous CVS access to the development source and modification history * - * is available at cvs.synchro.net:/cvsroot/sbbs, example: * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login * - * (just hit return, no password is necessary) * - * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src * - * * * For Synchronet coding style and modification guidelines, see * * http://www.synchro.net/source.html * * * - * You are encouraged to submit any modifications (preferably in Unix diff * - * format) via e-mail to mods@synchro.net * - * * Note: If this box doesn't appear square, then you need to fix your tabs. * ****************************************************************************/ @@ -333,6 +318,7 @@ js_load(JSContext *cx, uintN argc, jsval *arglist) JS_RESUMEREQUEST(cx, rc); return(JS_FALSE); } + JS_SetOptions(bg->cx, p->startup->options); JS_BEGINREQUEST(bg->cx); if(!js_CreateCommonObjects(bg->cx diff --git a/src/sbbs3/jsexec.c b/src/sbbs3/jsexec.c index 51c7c82c2b..5f2dd6e778 100644 --- a/src/sbbs3/jsexec.c +++ b/src/sbbs3/jsexec.c @@ -29,6 +29,7 @@ #include <termios.h> #endif +#define STARTUP_INI_BITDESC_TABLES #include "sbbs.h" #include "ciolib.h" #include "ini_file.h" @@ -45,6 +46,7 @@ static const char* strJavaScriptMaxBytes ="JavaScriptMaxBytes"; static const char* strJavaScriptTimeLimit ="JavaScriptTimeLimit"; static const char* strJavaScriptGcInterval ="JavaScriptGcInterval"; static const char* strJavaScriptYieldInterval ="JavaScriptYieldInterval"; +static const char* strJavaScriptOptions ="JavaScriptOptions"; js_startup_t startup; JSRuntime* js_runtime; @@ -54,6 +56,7 @@ js_callback_t cb; scfg_t scfg; char* text[TOTAL_TEXT]; ulong js_max_bytes=JAVASCRIPT_MAX_BYTES; +ulong js_opts = JAVASCRIPT_OPTIONS; FILE* confp; FILE* errfp; FILE* nulfp; @@ -700,6 +703,28 @@ js_putenv(JSContext *cx, uintN argc, jsval *arglist) return(JS_TRUE); } +// Forked from mozilla/js/src/shell/js.cpp: AssertEq() +static JSBool +js_AssertEq(JSContext *cx, uintN argc, jsval *vp) +{ + if (!(argc == 2 || (argc == 3 && JSVAL_IS_STRING(JS_ARGV(cx, vp)[2])))) { + JS_ReportError(cx, "assertEq: missing or invalid args"); + return JS_FALSE; + } + + jsval *argv = JS_ARGV(cx, vp); + JSBool same; + if (!JS_SameValue(cx, argv[0], argv[1], &same)) + return JS_FALSE; + if (!same) { + JS_ReportError(cx, "not equal"); + return JS_FALSE; + } + JS_SET_RVAL(cx, vp, JSVAL_VOID); + return JS_TRUE; +} + + static jsSyncMethodSpec js_global_functions[] = { {"log", js_log, 1}, {"read", js_read, 1}, @@ -710,13 +735,14 @@ static jsSyncMethodSpec js_global_functions[] = { {"write", js_write, 0}, {"writeln", js_writeln, 0}, {"print", js_writeln, 0}, - {"printf", jse_printf, 1}, + {"printf", jse_printf, 1}, {"alert", js_alert, 1}, {"prompt", js_prompt, 1}, {"confirm", js_confirm, 1}, {"deny", js_deny, 1}, {"chdir", js_chdir, 1}, {"putenv", js_putenv, 1}, + {"assertEq", js_AssertEq, 2}, {0} }; @@ -817,9 +843,7 @@ static BOOL js_init(char** env) if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) return(FALSE); -#ifdef JSDOOR - JS_SetOptions(js_cx, JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_COMPILE_N_GO | JSOPTION_PROFILING); -#endif + JS_SetOptions(js_cx, js_opts); JS_BEGINREQUEST(js_cx); JS_SetErrorReporter(js_cx, js_ErrorReporter); @@ -1138,6 +1162,7 @@ void get_ini_values(str_list_t ini, const char* section, js_callback_t* cb) cb->gc_interval = iniGetInteger(ini, section, strJavaScriptGcInterval , cb->gc_interval); cb->yield_interval = iniGetInteger(ini, section, strJavaScriptYieldInterval , cb->yield_interval); cb->auto_terminate = iniGetBool(ini, section, "AutoTerminate" , cb->auto_terminate); + js_opts = iniGetBitField(ini, section, strJavaScriptOptions , js_options, js_opts); } /*********************/ diff --git a/src/sbbs3/mailsrvr.c b/src/sbbs3/mailsrvr.c index f2eef48ad2..7b2b95cb08 100644 --- a/src/sbbs3/mailsrvr.c +++ b/src/sbbs3/mailsrvr.c @@ -2257,7 +2257,7 @@ js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailpr *result = 0; do { if(*js_runtime==NULL) { - lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes\n" + lprintf(LOG_DEBUG,"%04d %s JavaScript: Creating runtime: %lu bytes" ,sock, log_prefix, startup->js.max_bytes); if((*js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL) @@ -2267,6 +2267,7 @@ js_mailproc(SOCKET sock, client_t* client, user_t* user, struct mailproc* mailpr if(*js_cx==NULL) { if((*js_cx = JS_NewContext(*js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) return FALSE; + JS_SetOptions(*js_cx, startup->js.options); } JS_BEGINREQUEST(*js_cx); diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp index 0f733712d4..a462f3ee8e 100644 --- a/src/sbbs3/main.cpp +++ b/src/sbbs3/main.cpp @@ -1247,9 +1247,9 @@ js_ErrorReporter(JSContext *cx, const char *message, JSErrorReport *report) if(JSREPORT_IS_WARNING(report->flags)) { if(JSREPORT_IS_STRICT(report->flags)) - warning="strict warning"; + warning="strict warning "; else - warning="warning"; + warning="warning "; log_level = LOG_WARNING; } else { warning=nulstr; @@ -1276,6 +1276,7 @@ JSContext* sbbs_t::js_init(JSRuntime** runtime, JSObject** glob, const char* des if((js_cx = JS_NewContext(*runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) return NULL; + JS_SetOptions(js_cx, startup->js.options); JS_BEGINREQUEST(js_cx); memset(&js_callback,0,sizeof(js_callback)); diff --git a/src/sbbs3/sbbs_ini.c b/src/sbbs3/sbbs_ini.c index 74184c2368..24765d3330 100644 --- a/src/sbbs3/sbbs_ini.c +++ b/src/sbbs3/sbbs_ini.c @@ -58,6 +58,7 @@ static const char* strJavaScriptTimeLimit ="JavaScriptTimeLimit"; static const char* strJavaScriptGcInterval ="JavaScriptGcInterval"; static const char* strJavaScriptYieldInterval ="JavaScriptYieldInterval"; static const char* strJavaScriptLoadPath ="JavaScriptLoadPath"; +static const char* strJavaScriptOptions ="JavaScriptOptions"; static const char* strSemFileCheckFrequency ="SemFileCheckFrequency"; #define DEFAULT_LOG_LEVEL LOG_DEBUG @@ -104,6 +105,7 @@ void sbbs_get_js_settings( js->time_limit = iniGetInteger(list,section,strJavaScriptTimeLimit ,defaults->time_limit); js->gc_interval = iniGetInteger(list,section,strJavaScriptGcInterval ,defaults->gc_interval); js->yield_interval = iniGetInteger(list,section,strJavaScriptYieldInterval ,defaults->yield_interval); + js->options = iniGetBitField(list, section, strJavaScriptOptions ,js_options, defaults->options); /* Get JavaScriptLoadPath, use default if key is missing, use blank if key value is blank */ if((p=iniGetExistingString(list, section, strJavaScriptLoadPath, nulstr, value)) == NULL) { @@ -128,6 +130,7 @@ BOOL sbbs_set_js_settings( ,JAVASCRIPT_TIME_LIMIT ,JAVASCRIPT_GC_INTERVAL ,JAVASCRIPT_YIELD_INTERVAL + ,JAVASCRIPT_OPTIONS ,JAVASCRIPT_LOAD_PATH }; SAFECOPY(global_defaults.load_path, JAVASCRIPT_LOAD_PATH); @@ -226,6 +229,7 @@ static void get_ini_globals(str_list_t list, global_startup_t* global) global->js.time_limit = JAVASCRIPT_TIME_LIMIT; global->js.gc_interval = JAVASCRIPT_GC_INTERVAL; global->js.yield_interval = JAVASCRIPT_YIELD_INTERVAL; + global->js.options = JAVASCRIPT_OPTIONS; SAFECOPY(global->js.load_path, JAVASCRIPT_LOAD_PATH); /* Read .ini values here */ diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h index 290043f294..0742417fcf 100644 --- a/src/sbbs3/sbbsdefs.h +++ b/src/sbbs3/sbbsdefs.h @@ -66,6 +66,7 @@ #define JAVASCRIPT_GC_INTERVAL 100 #define JAVASCRIPT_LOAD_PATH "load" #define JAVASCRIPT_LOAD_PATH_LIST "load_path_list" +#define JAVASCRIPT_OPTIONS 0xC810 // JSOPTION_JIT | JSOPTION_METHODJIT | JSOPTION_COMPILE_N_GO | JSOPTION_PROFILING struct js_callback; typedef struct js_callback { diff --git a/src/sbbs3/services.c b/src/sbbs3/services.c index bd8716e2e1..0efdd14f82 100644 --- a/src/sbbs3/services.c +++ b/src/sbbs3/services.c @@ -754,6 +754,7 @@ js_initcx(JSRuntime* js_runtime, SOCKET sock, service_client_t* service_client, if((js_cx = JS_NewContext(js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) return(NULL); + JS_SetOptions(js_cx, startup->js.options); JS_BEGINREQUEST(js_cx); JS_SetErrorReporter(js_cx, js_ErrorReporter); diff --git a/src/sbbs3/startup.h b/src/sbbs3/startup.h index e4febaaa63..7f0c0498fb 100644 --- a/src/sbbs3/startup.h +++ b/src/sbbs3/startup.h @@ -38,6 +38,7 @@ typedef struct { ulong time_limit; /* maximum number of ticks (for infinite loop detection) */ ulong gc_interval; /* number of ticks between garbage collection attempts */ ulong yield_interval; /* number of ticks between time-slice yields */ + ulong options; char load_path[INI_MAX_VALUE_LEN]; /* additional (comma-separated) directories to search for load()ed scripts */ } js_startup_t; @@ -211,6 +212,22 @@ static ini_bitdesc_t bbs_options[] = { { 0 ,NULL } }; +static ini_bitdesc_t js_options[] = { + + { 1<<0 ,"STRICT" }, + { 1<<1 ,"WERROR" }, + { 1<<2 ,"VAROBJFIX" }, + { 1<<4 ,"COMPILE_N_GO" }, + { 1<<9 ,"RELIMIT" }, + { 1<<10 ,"ANONFUNFIX" }, + { 1<<11 ,"JIT" }, + { 1<<14 ,"METHODJIT" }, + { 1<<15 ,"PROFILING" }, + { 1<<16 ,"METHODJIT_ALWAYS" }, + /* terminator */ + { 0 ,NULL } +}; + #endif #ifdef __cplusplus diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c index cbe35e5346..c3efaee5f0 100644 --- a/src/sbbs3/websrvr.c +++ b/src/sbbs3/websrvr.c @@ -5754,9 +5754,11 @@ js_initcx(http_session_t *session) if((js_cx = JS_NewContext(session->js_runtime, JAVASCRIPT_CONTEXT_STACK))==NULL) return(NULL); + JS_SetOptions(js_cx, startup->js.options); JS_BEGINREQUEST(js_cx); - lprintf(LOG_DEBUG,"%04d JavaScript: Context created",session->socket); + lprintf(LOG_DEBUG,"%04d JavaScript: Context created with options: %lx" + ,session->socket, (long)startup->js.options); JS_SetErrorReporter(js_cx, js_ErrorReporter); -- GitLab