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"
226
227
228
229
	,NULL
};
#endif

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

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

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

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

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

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

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

    return(JS_TRUE);
}

269
270
271
272
273
274
275
276
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
};

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

290
291
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

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

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

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

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

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

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

	JS_DestroyContext(cx);

    return(JS_TRUE);
}

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

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

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

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

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

357
	cb->gc_attempts++;
358

359
360
361
	return(JS_TRUE);
}

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

368
369
370
371
372
373
374
375
	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);
	}
376

377
378
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

	return(JS_TRUE);
}
384

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

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

398
	if(glob==scope) {
399
400
401
402
403
404
405
		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 {
406
		list=(str_list_t)JS_GetPrivate(cx,scope);
407
408
		if(list==NULL) {
			list=strListInit();
409
			JS_SetPrivate(cx,scope,list);
410
411
		}
	}
412

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

	return(JS_TRUE);
}

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

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

	return(JS_TRUE);
}

445
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
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);
}

474
static jsSyncMethodSpec js_functions[] = {
rswindell's avatar
rswindell committed
475
	{"eval",            js_eval,            0,	JSTYPE_UNDEF,	JSDOCSTR("script")
476
	,JSDOCSTR("evaluate a JavaScript string in its own (secure) context, returning the result")
477
	,311
478
	},		
rswindell's avatar
rswindell committed
479
	{"gc",				js_gc,				0,	JSTYPE_VOID,	JSDOCSTR("forced=<tt>true</tt>")
480
481
482
	,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")
483
	,311
484
	},
rswindell's avatar
rswindell committed
485
486
	{"on_exit",			js_on_exit,			1,	JSTYPE_VOID,	JSDOCSTR("to_eval")
	,JSDOCSTR("add a string to evaluate/execute (LIFO stack) upon script's termination")
487
	,313
488
	},
rswindell's avatar
rswindell committed
489
	{"report_error",	js_report_error,	1,	JSTYPE_VOID,	JSDOCSTR("error [,fatal=<tt>false</tt>]")
490
491
	,JSDOCSTR("report an error using the standard JavaScript error reporting mechanism "
	"(including script filename and line number), "
492
493
	"if <i>fatal</i> is <i>true</i>, immediately terminates script")
	,313
494
	},
rswindell's avatar
rswindell committed
495
	{"get_parent",		js_get_parent,		1,	JSTYPE_OBJECT,	JSDOCSTR("object")
496
	,JSDOCSTR("return the parent of the specified object")
rswindell's avatar
rswindell committed
497
	,314
498
	},
499
500
501
502
503
504
505
506
	{"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
	},
507
508
509
	{0}
};

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

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

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

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

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		*/
};

549
void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_callback_t* cb)
550
551
552
{
	char*	p;
	jsval	rval;
553
	JSObject* script;
554
	BOOL	auto_terminate=cb->auto_terminate;
555
556
557
558
559
560
561
562
563
564
	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);
565

566
	cb->auto_terminate=FALSE;
567

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

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

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

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

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

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

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

		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);

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

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

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

	return(obj);
}
629

630
631
632
633
634
635
636
637
638
639
#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

640
void DLLCALL js_PrepareToExecute(JSContext *cx, JSObject *obj, const char *filename, const char* startup_dir, JSObject *scope)
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
{
	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);
		}
665
666
667
668
669
		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);
670
671
		JS_DefineProperty(cx, js, "scope", OBJECT_TO_JSVAL(scope)
			,NULL,NULL,JSPROP_ENUMERATE|JSPROP_READONLY);
672
	}
673
674
	JS_DefineProperty(cx, scope, "exit_code", JSVAL_NULL
		,NULL,NULL,JSPROP_ENUMERATE|JSPROP_PERMANENT);
675
676
677
#if defined(_MSC_VER)
	_set_invalid_parameter_handler(msvc_invalid_parameter_handler);
#endif
678
}