js_internal.c 18.7 KB
Newer Older
1
2
/* js_internal.c */

3
/* Synchronet "js" object, for internal JavaScript callback and GC control */
4
5
6
7
8
9
10

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
11
 * Copyright 2013 Rob Swindell - http://www.synchro.net/copyright.html		*
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 *																			*
 * This program is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU General Public License				*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * 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.	*
 ****************************************************************************/

#include "sbbs.h"
39
#include "js_request.h"
40

41
42
43
/* SpiderMonkey: */
#include <jsdbgapi.h>

44
enum {
45
46
	 PROP_VERSION
	,PROP_TERMINATED
47
	,PROP_AUTO_TERMINATE
48
49
	,PROP_COUNTER
	,PROP_TIME_LIMIT
50
51
	,PROP_YIELD_INTERVAL
	,PROP_GC_INTERVAL
52
	,PROP_GC_ATTEMPTS
53
54
#ifdef jscntxt_h___
	,PROP_GC_COUNTER
55
56
57
	,PROP_GC_LASTBYTES
	,PROP_BYTES
	,PROP_MAXBYTES
58
#endif
59
	,PROP_GLOBAL
60
61
};

62
static JSBool js_get(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
63
{
64
	jsval idval;
65
    jsint			tiny;
66
	js_callback_t*	cb;
67

68
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
69
70
		return(JS_FALSE);

71
72
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
73
74

	switch(tiny) {
75
76
77
		case PROP_VERSION:
			*vp=STRING_TO_JSVAL(JS_NewStringCopyZ(cx,(char *)JS_GetImplementationVersion()));
			break;
78
		case PROP_TERMINATED:
79
			if(cb->terminated==NULL)
80
81
				*vp=JSVAL_FALSE;
			else
82
				*vp=BOOLEAN_TO_JSVAL(*cb->terminated);
83
			break;
84
		case PROP_AUTO_TERMINATE:
85
			*vp=BOOLEAN_TO_JSVAL(cb->auto_terminate);
86
			break;
87
88
		case PROP_COUNTER:
			*vp=DOUBLE_TO_JSVAL((double)cb->counter);
89
			break;
90
91
		case PROP_TIME_LIMIT:
			*vp=DOUBLE_TO_JSVAL(cb->limit);
92
93
			break;
		case PROP_YIELD_INTERVAL:
94
			*vp=DOUBLE_TO_JSVAL((double)cb->yield_interval);
95
96
			break;
		case PROP_GC_INTERVAL:
97
			*vp=DOUBLE_TO_JSVAL((double)cb->gc_interval);
98
			break;
99
		case PROP_GC_ATTEMPTS:
100
			*vp=DOUBLE_TO_JSVAL((double)cb->gc_attempts);
101
			break;
102
#ifdef jscntxt_h___
103
		case PROP_GC_COUNTER:
104
			*vp=UINT_TO_JSVAL(cx->runtime->gcNumber);
105
106
			break;
		case PROP_GC_LASTBYTES:
107
			*vp=DOUBLE_TO_JSVAL((double)cx->runtime->gcLastBytes);
108
			break;
109
		case PROP_BYTES:
110
			*vp=DOUBLE_TO_JSVAL((double)cx->runtime->gcBytes);
111
			break;
112
		case PROP_MAXBYTES:
113
			*vp=DOUBLE_TO_JSVAL((double)cx->runtime->gcMaxBytes);
114
			break;
115
#endif
116
		case PROP_GLOBAL:
117
			*vp = OBJECT_TO_JSVAL(JS_GetGlobalObject(cx));	
118
			break;
119
120
121
122
123
	}

	return(JS_TRUE);
}

124
static JSBool js_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
125
{
126
	jsval idval;
127
    jsint			tiny;
128
	js_callback_t*	cb;
129

130
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
131
132
		return(JS_FALSE);

133
134
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
135
136

	switch(tiny) {
137
		case PROP_TERMINATED:
138
139
			if(cb->terminated!=NULL)
				JS_ValueToBoolean(cx, *vp, (int *)cb->terminated);
140
			break;
141
		case PROP_AUTO_TERMINATE:
142
			JS_ValueToBoolean(cx,*vp,&cb->auto_terminate);
143
			break;
144
145
		case PROP_COUNTER:
			if(!JS_ValueToInt32(cx, *vp, (int32*)&cb->counter))
146
				return JS_FALSE;
147
			break;
148
149
		case PROP_TIME_LIMIT:
			if(!JS_ValueToInt32(cx, *vp, (int32*)&cb->limit))
150
				return JS_FALSE;
151
152
			break;
		case PROP_GC_INTERVAL:
153
			if(!JS_ValueToInt32(cx, *vp, (int32*)&cb->gc_interval))
154
				return JS_FALSE;
155
156
			break;
		case PROP_YIELD_INTERVAL:
157
			if(!JS_ValueToInt32(cx, *vp, (int32*)&cb->yield_interval))
158
				return JS_FALSE;
159
			break;
160
#ifdef jscntxt_h___
161
		case PROP_MAXBYTES:
162
163
			if(!JS_ValueToInt32(cx, *vp, (int32*)&cx->runtime->gcMaxBytes))
				return JS_FALSE;
164
			break;
165
#endif
166
167
	}

168
	return(JS_TRUE);
169
170
}

171
#define PROP_FLAGS	JSPROP_ENUMERATE|JSPROP_READONLY
172

173
174
175
static jsSyncPropertySpec js_properties[] = {
/*		 name,				tinyid,						flags,		ver	*/

176
	{	"version",			PROP_VERSION,		PROP_FLAGS,			311 },
177
	{	"auto_terminate",	PROP_AUTO_TERMINATE,JSPROP_ENUMERATE,	311 },
178
	{	"terminated",		PROP_TERMINATED,	JSPROP_ENUMERATE,	311 },
179
180
181
182
	{	"branch_counter",	PROP_COUNTER,		0,					311 },
	{	"counter",			PROP_COUNTER,		JSPROP_ENUMERATE,	316 },
	{	"branch_limit",		PROP_TIME_LIMIT,	0,					311 },
	{	"time_limit",		PROP_TIME_LIMIT,	JSPROP_ENUMERATE,	316 },
183
184
	{	"yield_interval",	PROP_YIELD_INTERVAL,JSPROP_ENUMERATE,	311 },
	{	"gc_interval",		PROP_GC_INTERVAL,	JSPROP_ENUMERATE,	311 },
185
	{	"gc_attempts",		PROP_GC_ATTEMPTS,	PROP_FLAGS,			311 },
186
#ifdef jscntxt_h___
187
188
189
	{	"gc_counter",		PROP_GC_COUNTER,	PROP_FLAGS,			311 },
	{	"gc_last_bytes",	PROP_GC_LASTBYTES,	PROP_FLAGS,			311 },
	{	"bytes",			PROP_BYTES,			PROP_FLAGS,			311 },
190
	{	"max_bytes",		PROP_MAXBYTES,		JSPROP_ENUMERATE,	311 },
191
#endif
rswindell's avatar
rswindell committed
192
	{	"global",			PROP_GLOBAL,		PROP_FLAGS,			314 },
193
194
195
	{0}
};

196
#ifdef BUILD_JSDOCS
197
static char* prop_desc[] = {
198
199
	 "JavaScript engine version information (AKA system.js_version)"
	,"set to <i>false</i> to disable the automatic termination of the script upon external request"
200
	,"termination has been requested (stop execution as soon as possible)"
201
202
	,"number of operation callbacks performed in this runtime"
	,"maximum number of operation callbacks, used for infinite-loop detection (0=disabled)"
203
	,"interval of periodic time-slice yields (lower number=higher frequency, 0=disabled)"
204
	,"interval of periodic garbage collection attempts (lower number=higher frequency, 0=disabled)"
205
	,"number of garbage collections attempted in this runtime - <small>READ ONLY</small>"
206
#ifdef jscntxt_h___
207
208
209
	,"number of garbage collections performed in this runtime - <small>READ ONLY</small>"
	,"number of heap bytes in use after last garbage collection - <small>READ ONLY</small>"
	,"number of heap bytes currently in use - <small>READ ONLY</small>"
210
	,"maximum number of bytes available for heap"
211
#endif
212
	,"global (top level) object - <small>READ ONLY</small>"
deuce's avatar
deuce committed
213
214
215
216
217
218
219
220
221
222
223
	/* New properties go here... */
	,"load() search path array.<br>For relative load paths (e.g. not beginning with '/' or '\'), "
		"the path is assumed to be a sub-directory of the (configurable) mods or exec directories "
		"and is searched accordingly. "
		"So, by default, load(\"somefile.js\") will search in this order:<br>"
		"mods/load/somefile.js<br>"
		"exec/load/somefile.js<br>"
		"mods/somefile.js<br>"
		"exec/somefile.js<br>"
	,"full path and filename of JS file executed"
	,"JS filename executed (with no path)"
deuce's avatar
deuce committed
224
	,"directory of executed JS file"
225
	,"Either the configured startup directory in SCFG (for externals) or the cwd when jsexec is started"
deuce's avatar
deuce committed
226
	,"global scope for this script"
227
228
229
230
	,NULL
};
#endif

231
JSBool DLLCALL
232
js_CommonOperationCallback(JSContext *cx, js_callback_t* cb)
233
{
234
	cb->counter++;
235

236
	/* Terminated? */
237
238
	if(cb->auto_terminate &&
		(cb->terminated!=NULL && *cb->terminated)) {
239
		JS_ReportWarning(cx,"Terminated");
240
		cb->counter=0;
241
242
243
		return(JS_FALSE);
	}

244
	/* Infinite loop? */
245
	if(cb->limit && cb->counter > cb->limit) {
246
		JS_ReportError(cx,"Infinite loop (%lu operation callbacks) detected",cb->counter);
247
		cb->counter=0;
248
249
		return(JS_FALSE);
	}
250

251
	/* Give up timeslices every once in a while */
252
	if(cb->yield_interval && (cb->counter%cb->yield_interval)==0) {
deuce's avatar
deuce committed
253
254
		jsrefcount	rc;

255
		rc=JS_SUSPENDREQUEST(cx);
256
		YIELD();
257
		JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
258
	}
259

260
261
262
	/* Permit other contexts to run GC */
	JS_YieldRequest(cx);

263
	/* Periodic Garbage Collection */
264
265
	if(cb->gc_interval && (cb->counter%cb->gc_interval)==0)
		JS_MaybeGC(cx), cb->gc_attempts++;
266
267
268
269

    return(JS_TRUE);
}

270
271
272
273
274
275
276
277
static JSClass eval_class = {
    "Global",  /* name */
    JSCLASS_GLOBAL_FLAGS,  /* flags */
    JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub,
    JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
    JSCLASS_NO_OPTIONAL_MEMBERS
};

278
279
/* Execute a string in its own context (away from Synchronet objects) */
static JSBool
280
js_eval(JSContext *parent_cx, uintN argc, jsval *arglist)
281
{
282
	jsval *argv=JS_ARGV(parent_cx, arglist);
283
	char*			buf;
284
285
	size_t			buflen;
	JSString*		str;
286
    JSObject*		script;
287
288
	JSContext*		cx;
	JSObject*		obj;
289
	JSErrorReporter	reporter;
290

291
292
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

293
294
295
	if(argc<1)
		return(JS_TRUE);

296
	if((str=JS_ValueToString(parent_cx, argv[0]))==NULL)
297
		return(JS_FALSE);
298
299
	JSSTRING_TO_MSTRING(parent_cx, str, buf, &buflen);
	HANDLE_PENDING(parent_cx);
deuce's avatar
deuce committed
300
	if(buf==NULL)
301
		return(JS_TRUE);
302

303
304
	if((cx=JS_NewContext(JS_GetRuntime(parent_cx),JAVASCRIPT_CONTEXT_STACK))==NULL) {
		free(buf);
305
		return(JS_FALSE);
306
	}
307

308
	/* Use the error reporter from the parent context */
309
310
	reporter=JS_SetErrorReporter(parent_cx,NULL);
	JS_SetErrorReporter(parent_cx,reporter);
311
	JS_SetErrorReporter(cx,reporter);
312

313
	/* Use the operation callback from the parent context */
314
	JS_SetContextPrivate(cx, JS_GetContextPrivate(parent_cx));
deuce's avatar
deuce committed
315
	JS_SetOperationCallback(cx, JS_GetOperationCallback(parent_cx));
316

317
	if((obj=JS_NewCompartmentAndGlobalObject(cx, &eval_class, NULL))==NULL
318
319
		|| !JS_InitStandardClasses(cx,obj)) {
		JS_DestroyContext(cx);
320
		free(buf);
321
322
		return(JS_FALSE);
	}
323

324
	if((script=JS_CompileScript(cx, obj, buf, buflen, NULL, 0))!=NULL) {
deuce's avatar
deuce committed
325
326
327
328
		jsval	rval;

		JS_ExecuteScript(cx, obj, script, &rval);
		JS_SET_RVAL(cx, arglist, rval);
329
	}
330
	free(buf);
331
332
333
334
335
336

	JS_DestroyContext(cx);

    return(JS_TRUE);
}

337
static JSBool
338
js_gc(JSContext *cx, uintN argc, jsval *arglist)
339
{
340
341
	JSObject *obj=JS_THIS_OBJECT(cx, arglist);
	jsval *argv=JS_ARGV(cx, arglist);
342
	JSBool			forced=JS_TRUE;
343
	js_callback_t*	cb;
344

deuce's avatar
deuce committed
345
346
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

347
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
348
		return(JS_FALSE);
349
350
351
352
353
354
355
356
357

	if(argc)
		JS_ValueToBoolean(cx,argv[0],&forced);

	if(forced)
		JS_GC(cx);
	else
		JS_MaybeGC(cx);

358
	cb->gc_attempts++;
359

360
361
362
	return(JS_TRUE);
}

363
static JSBool
364
js_report_error(JSContext *cx, uintN argc, jsval *arglist)
365
{
366
	jsval *argv=JS_ARGV(cx, arglist);
deuce's avatar
deuce committed
367
368
	char	*p;

369
370
371
372
373
374
375
376
	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
	HANDLE_PENDING(cx);
	if(p==NULL)
		JS_ReportError(cx,"NULL");
	else {
		JS_ReportError(cx,"%s",p);
		free(p);
	}
377

378
379
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

380
381
382
383
384
	if(argc>1 && argv[1]==JSVAL_TRUE)
		return(JS_FALSE);	/* fatal */

	return(JS_TRUE);
}
385

386
static JSBool
387
js_on_exit(JSContext *cx, uintN argc, jsval *arglist)
388
{
389
	JSObject *scope=JS_GetScopeChain(cx);
390
	JSObject *glob=JS_GetGlobalObject(cx);
391
	jsval *argv=JS_ARGV(cx, arglist);
392
393
	global_private_t*	pd;
	str_list_t	list;
394
	str_list_t	oldlist;
deuce's avatar
deuce committed
395
	char		*p;
396

deuce's avatar
deuce committed
397
398
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

399
	if(glob==scope) {
400
401
402
403
404
405
406
		if((pd=(global_private_t*)JS_GetPrivate(cx,glob))==NULL)
			return(JS_FALSE);
		if(pd->exit_func==NULL)
			pd->exit_func=strListInit();
		list=pd->exit_func;
	}
	else {
407
		list=(str_list_t)JS_GetPrivate(cx,scope);
408
409
		if(list==NULL) {
			list=strListInit();
410
			JS_SetPrivate(cx,scope,list);
411
412
		}
	}
413

414
415
416
417
	JSVALUE_TO_MSTRING(cx, argv[0], p, NULL);
	HANDLE_PENDING(cx);
	if(!p)
		return JS_TRUE;
deuce's avatar
deuce committed
418
	oldlist=list;
419
	strListPush(&list,p);
420
	free(p);
deuce's avatar
deuce committed
421
422
423
424
425
426
	if(oldlist != list) {
		if(glob==scope)
			pd->exit_func=list;
		else
			JS_SetPrivate(cx,scope,list);
	}
427
428
429
430

	return(JS_TRUE);
}

431
static JSBool
432
js_get_parent(JSContext *cx, uintN argc, jsval *arglist)
433
{
434
	jsval *argv=JS_ARGV(cx, arglist);
435
436
437
438
439
440
	JSObject* child=NULL;
	JSObject* parent;

	if(JS_ValueToObject(cx, argv[0], &child)
		&& child!=NULL
		&& (parent=JS_GetParent(cx,child))!=NULL)
441
		JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(parent));
442
443
444
445

	return(JS_TRUE);
}

446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
static JSBool js_getsize(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval	*argv=JS_ARGV(cx, arglist);
	JSObject* tmp_obj;

	if(!JSVAL_IS_OBJECT(argv[0])) {
		JS_ReportError(cx, "get_size() error!  Parameter is not an object.");
		return(JS_FALSE);
	}
	tmp_obj=JSVAL_TO_OBJECT(argv[0]);
	if(!tmp_obj)
		return(JS_FALSE);
	JS_SET_RVAL(cx, arglist, DOUBLE_TO_JSVAL(JS_GetObjectTotalSize(cx, tmp_obj)));
	return(JS_TRUE);
}

static JSBool js_flatten(JSContext *cx, uintN argc, jsval *arglist)
{
	jsval	*argv=JS_ARGV(cx, arglist);

	if(!JSVAL_IS_STRING(argv[0])) {
		JS_ReportError(cx, "get_size() error!  Parameter is not a string.");
		return(JS_FALSE);
	}
	JS_FlattenString(cx, JSVAL_TO_STRING(argv[0]));
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);
	return(JS_TRUE);
}

475
static jsSyncMethodSpec js_functions[] = {
rswindell's avatar
rswindell committed
476
	{"eval",            js_eval,            0,	JSTYPE_UNDEF,	JSDOCSTR("script")
477
	,JSDOCSTR("evaluate a JavaScript string in its own (secure) context, returning the result")
478
	,311
479
	},		
rswindell's avatar
rswindell committed
480
	{"gc",				js_gc,				0,	JSTYPE_VOID,	JSDOCSTR("forced=<tt>true</tt>")
481
482
483
	,JSDOCSTR("perform a garbage collection operation (freeing memory for unused allocated objects), "
		"if <i>forced</i> is <i>true</i> (the default) a garbage collection is always performed, "
		"otherwise it is only performed if deemed appropriate by the JavaScript engine")
484
	,311
485
	},
rswindell's avatar
rswindell committed
486
487
	{"on_exit",			js_on_exit,			1,	JSTYPE_VOID,	JSDOCSTR("to_eval")
	,JSDOCSTR("add a string to evaluate/execute (LIFO stack) upon script's termination")
488
	,313
489
	},
rswindell's avatar
rswindell committed
490
	{"report_error",	js_report_error,	1,	JSTYPE_VOID,	JSDOCSTR("error [,fatal=<tt>false</tt>]")
491
492
	,JSDOCSTR("report an error using the standard JavaScript error reporting mechanism "
	"(including script filename and line number), "
493
494
	"if <i>fatal</i> is <i>true</i>, immediately terminates script")
	,313
495
	},
rswindell's avatar
rswindell committed
496
	{"get_parent",		js_get_parent,		1,	JSTYPE_OBJECT,	JSDOCSTR("object")
497
	,JSDOCSTR("return the parent of the specified object")
rswindell's avatar
rswindell committed
498
	,314
499
	},
500
501
502
503
504
505
506
507
	{"get_size",		js_getsize,			1,	JSTYPE_NUMBER,	JSDOCSTR("[object]")
	,JSDOCSTR("return the size in bytes the object uses in memory (forces GC) ")
	,316
	},
	{"flatten_string",	js_flatten,			1,	JSTYPE_VOID,	JSDOCSTR("[string]")
	,JSDOCSTR("flattens a string, optimizing allocated memory used for concatenated strings")
	,316
	},
508
509
510
	{0}
};

511
static JSBool js_internal_resolve(JSContext *cx, JSObject *obj, jsid id)
deuce's avatar
deuce committed
512
513
{
	char*			name=NULL;
514
	JSBool			ret;
deuce's avatar
deuce committed
515

deuce's avatar
deuce committed
516
517
518
519
	if(id != JSID_VOID && id != JSID_EMPTY) {
		jsval idval;
		
		JS_IdToValue(cx, id, &idval);
520
521
522
523
		if(JSVAL_IS_STRING(idval)) {
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx);
		}
deuce's avatar
deuce committed
524
	}
deuce's avatar
deuce committed
525

526
527
528
529
	ret=js_SyncResolve(cx, obj, name, js_properties, js_functions, NULL, 0);
	if(name)
		free(name);
	return(ret);
deuce's avatar
deuce committed
530
531
532
533
}

static JSBool js_internal_enumerate(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed
534
	return(js_internal_resolve(cx, obj, JSID_VOID));
deuce's avatar
deuce committed
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
}

static JSClass js_internal_class = {
     "JsInternal"				/* name			*/
    ,JSCLASS_HAS_PRIVATE	/* flags		*/
	,JS_PropertyStub		/* addProperty	*/
	,JS_PropertyStub		/* delProperty	*/
	,js_get					/* getProperty	*/
	,js_set					/* setProperty	*/
	,js_internal_enumerate	/* enumerate	*/
	,js_internal_resolve	/* resolve		*/
	,JS_ConvertStub			/* convert		*/
	,JS_FinalizeStub		/* finalize		*/
};

550
void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_callback_t* cb)
551
552
553
{
	char*	p;
	jsval	rval;
554
	JSObject* script;
555
	BOOL	auto_terminate=cb->auto_terminate;
556
557
558
559
560
561
562
563
564
565
	JSObject	*glob=JS_GetGlobalObject(cx);
	global_private_t *pt;
	str_list_t	list;

	if(glob==obj) {
		pt=(global_private_t *)JS_GetPrivate(cx,JS_GetGlobalObject(cx));		
		list=pt->exit_func;
	}
	else
		list=JS_GetPrivate(cx,obj);
566

567
	cb->auto_terminate=FALSE;
568

569
	while((p=strListPop(&list))!=NULL) {
570
571
572
		if((script=JS_CompileScript(cx, obj, p, strlen(p), NULL, 0))!=NULL) {
			JS_ExecuteScript(cx, obj, script, &rval);
		}
deuce's avatar
deuce committed
573
		free(p);
574
575
	}

576
	strListFree(&list);
577
578
579
580
	if(glob != obj)
		JS_SetPrivate(cx,obj,NULL);
	else
		pt->exit_func=NULL;
581

582
	if(auto_terminate)
583
		cb->auto_terminate = TRUE;
584
585
}

586
JSObject* DLLCALL js_CreateInternalJsObject(JSContext* cx, JSObject* parent, js_callback_t* cb, js_startup_t* startup)
587
588
589
590
591
592
593
{
	JSObject*	obj;

	if((obj = JS_DefineObject(cx, parent, "js", &js_internal_class, NULL
		,JSPROP_ENUMERATE|JSPROP_READONLY))==NULL)
		return(NULL);

594
	if(!JS_SetPrivate(cx, obj, cb))	/* Store a pointer to js_callback_t */
595
596
		return(NULL);

597
598
599
	if(startup!=NULL) {
		JSObject*	load_path_list;
		jsval		val;
600
		str_list_t	load_path;
601
602
603
604
605
606
607

		if((load_path_list=JS_NewArrayObject(cx, 0, NULL))==NULL) 
			return(NULL);
		val=OBJECT_TO_JSVAL(load_path_list);
		if(!JS_SetProperty(cx, obj, JAVASCRIPT_LOAD_PATH_LIST, &val)) 
			return(NULL);

608
		if((load_path=strListSplitCopy(NULL, startup->load_path, ",")) != NULL) {
609
610
611
			JSString*	js_str;
			unsigned	i;

612
613
614
			for(i=0; load_path[i]!=NULL; i++) {
				if((js_str=JS_NewStringCopyZ(cx, load_path[i]))==NULL)
					break;
615
616
				val=STRING_TO_JSVAL(js_str);
				if(!JS_SetElement(cx, load_path_list, i, &val))
617
					break;
618
			}
619
			strListFree(&load_path);
620
621
622
		}
	}

623
#ifdef BUILD_JSDOCS
624
	js_DescribeSyncObject(cx,obj,"JavaScript engine internal control object",311);
625
626
627
628
629
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", prop_desc, JSPROP_READONLY);
#endif

	return(obj);
}
630

631
632
633
634
635
636
637
638
639
640
#if defined(_MSC_VER)
void msvc_invalid_parameter_handler(const wchar_t* expression,
   const wchar_t* function, 
   const wchar_t* file, 
   unsigned int line, 
   uintptr_t pReserved)
{
}
#endif

641
void DLLCALL js_PrepareToExecute(JSContext *cx, JSObject *obj, const char *filename, const char* startup_dir, JSObject *scope)
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
{
	JSString*	str;
	jsval		val;

	if(JS_GetProperty(cx, obj, "js", &val) && JSVAL_IS_OBJECT(val)) {
		JSObject* js = JSVAL_TO_OBJECT(val);
		char	dir[MAX_PATH+1];

		if(filename!=NULL) {
			char* p;

			if((str=JS_NewStringCopyZ(cx, filename)) != NULL)
				JS_DefineProperty(cx, js, "exec_path", STRING_TO_JSVAL(str)
					,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
			if((str=JS_NewStringCopyZ(cx, getfname(filename))) != NULL)
				JS_DefineProperty(cx, js, "exec_file", STRING_TO_JSVAL(str)
					,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
			SAFECOPY(dir,filename);
			p=getfname(dir);
			*p=0;
			if((str=JS_NewStringCopyZ(cx, dir)) != NULL)
				JS_DefineProperty(cx, js, "exec_dir", STRING_TO_JSVAL(str)
					,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
		}
666
667
668
669
670
		if(startup_dir==NULL)
			startup_dir="";
		if((str=JS_NewStringCopyZ(cx, startup_dir)) != NULL)
			JS_DefineProperty(cx, js, "startup_dir", STRING_TO_JSVAL(str)
				,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
671
672
		JS_DefineProperty(cx, js, "scope", OBJECT_TO_JSVAL(scope)
			,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
673
	}
674
675
	JS_DefineProperty(cx, scope, "exit_code", JSVAL_NULL
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_PERMANENT);
676
677
678
#if defined(_MSC_VER)
	_set_invalid_parameter_handler(msvc_invalid_parameter_handler);
#endif
679
}