js_internal.c 17.1 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 2011 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

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

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

65
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
66
67
		return(JS_FALSE);

68
69
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
70
71

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

	return(JS_TRUE);
}

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

127
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
128
129
		return(JS_FALSE);

130
131
    JS_IdToValue(cx, id, &idval);
    tiny = JSVAL_TO_INT(idval);
132
133

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

165
	return(JS_TRUE);
166
167
}

168
#define PROP_FLAGS	JSPROP_ENUMERATE|JSPROP_READONLY
169

170
171
172
static jsSyncPropertySpec js_properties[] = {
/*		 name,				tinyid,						flags,		ver	*/

173
	{	"version",			PROP_VERSION,		PROP_FLAGS,			311 },
174
	{	"auto_terminate",	PROP_AUTO_TERMINATE,JSPROP_ENUMERATE,	311 },
175
	{	"terminated",		PROP_TERMINATED,	JSPROP_ENUMERATE,	311 },
176
177
178
179
	{	"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 },
180
181
	{	"yield_interval",	PROP_YIELD_INTERVAL,JSPROP_ENUMERATE,	311 },
	{	"gc_interval",		PROP_GC_INTERVAL,	JSPROP_ENUMERATE,	311 },
182
	{	"gc_attempts",		PROP_GC_ATTEMPTS,	PROP_FLAGS,			311 },
183
#ifdef jscntxt_h___
184
185
186
	{	"gc_counter",		PROP_GC_COUNTER,	PROP_FLAGS,			311 },
	{	"gc_last_bytes",	PROP_GC_LASTBYTES,	PROP_FLAGS,			311 },
	{	"bytes",			PROP_BYTES,			PROP_FLAGS,			311 },
187
	{	"max_bytes",		PROP_MAXBYTES,		JSPROP_ENUMERATE,	311 },
188
#endif
rswindell's avatar
rswindell committed
189
	{	"global",			PROP_GLOBAL,		PROP_FLAGS,			314 },
190
191
192
	{0}
};

193
#ifdef BUILD_JSDOCS
194
static char* prop_desc[] = {
195
196
	 "JavaScript engine version information (AKA system.js_version)"
	,"set to <i>false</i> to disable the automatic termination of the script upon external request"
197
	,"termination has been requested (stop execution as soon as possible)"
198
	,"number of branch operations performed in this runtime"
199
200
	,"maximum number of branches, used for infinite-loop detection (0=disabled)"
	,"interval of periodic time-slice yields (lower number=higher frequency, 0=disabled)"
201
	,"interval of periodic garbage collection attempts (lower number=higher frequency, 0=disabled)"
202
	,"number of garbage collections attempted in this runtime - <small>READ ONLY</small>"
203
#ifdef jscntxt_h___
204
205
206
	,"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>"
207
	,"maximum number of bytes available for heap"
208
#endif
209
	,"global (top level) object - <small>READ ONLY</small>"
deuce's avatar
deuce committed
210
211
212
213
214
215
216
217
218
219
220
221
222
	/* 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"
	,"directory of executed JS file"
	,"JS filename executed (with no path)"
	,"Either the configure startup directory in SCFG (for externals) or the cwd when jsexec is started"
223
224
225
226
	,NULL
};
#endif

227
JSBool DLLCALL
228
js_CommonOperationCallback(JSContext *cx, js_callback_t* cb)
229
{
230
	cb->counter++;
231

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

240
	/* Infinite loop? */
241
242
243
	if(cb->limit && cb->counter > cb->limit) {
		JS_ReportError(cx,"Infinite loop (%lu branches) detected",cb->counter);
		cb->counter=0;
244
245
		return(JS_FALSE);
	}
246

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

251
		rc=JS_SUSPENDREQUEST(cx);
252
		YIELD();
253
		JS_RESUMEREQUEST(cx, rc);
deuce's avatar
deuce committed
254
	}
255

256
257
258
	/* Permit other contexts to run GC */
	JS_YieldRequest(cx);

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

    return(JS_TRUE);
}

266
267
268
269
270
271
272
273
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
};

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

287
288
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

289
290
291
	if(argc<1)
		return(JS_TRUE);

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

299
300
	if((cx=JS_NewContext(JS_GetRuntime(parent_cx),JAVASCRIPT_CONTEXT_STACK))==NULL) {
		free(buf);
301
		return(JS_FALSE);
302
	}
303

304
	/* Use the error reporter from the parent context */
305
306
	reporter=JS_SetErrorReporter(parent_cx,NULL);
	JS_SetErrorReporter(parent_cx,reporter);
307
	JS_SetErrorReporter(cx,reporter);
308

309
	/* Use the branch callback from the parent context */
310
	JS_SetContextPrivate(cx, JS_GetContextPrivate(parent_cx));
deuce's avatar
deuce committed
311
	JS_SetOperationCallback(cx, JS_GetOperationCallback(parent_cx));
312

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

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

		JS_ExecuteScript(cx, obj, script, &rval);
		JS_SET_RVAL(cx, arglist, rval);
325
	}
326
	free(buf);
327
328
329
330
331
332

	JS_DestroyContext(cx);

    return(JS_TRUE);
}

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

deuce's avatar
deuce committed
341
342
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

343
	if((cb=(js_callback_t*)JS_GetPrivate(cx,obj))==NULL)
344
		return(JS_FALSE);
345
346
347
348
349
350
351
352
353

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

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

354
	cb->gc_attempts++;
355

356
357
358
	return(JS_TRUE);
}

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

365
366
367
368
369
370
371
372
	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);
	}
373

374
375
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

376
377
378
379
380
	if(argc>1 && argv[1]==JSVAL_TRUE)
		return(JS_FALSE);	/* fatal */

	return(JS_TRUE);
}
381

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

deuce's avatar
deuce committed
393
394
	JS_SET_RVAL(cx, arglist, JSVAL_VOID);

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

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

	return(JS_TRUE);
}

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

	if(JS_ValueToObject(cx, argv[0], &child)
		&& child!=NULL
		&& (parent=JS_GetParent(cx,child))!=NULL)
437
		JS_SET_RVAL(cx, arglist, OBJECT_TO_JSVAL(parent));
438
439
440
441

	return(JS_TRUE);
}

442
static jsSyncMethodSpec js_functions[] = {
rswindell's avatar
rswindell committed
443
	{"eval",            js_eval,            0,	JSTYPE_UNDEF,	JSDOCSTR("script")
444
	,JSDOCSTR("evaluate a JavaScript string in its own (secure) context, returning the result")
445
	,311
446
	},		
rswindell's avatar
rswindell committed
447
	{"gc",				js_gc,				0,	JSTYPE_VOID,	JSDOCSTR("forced=<tt>true</tt>")
448
449
450
	,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")
451
	,311
452
	},
rswindell's avatar
rswindell committed
453
454
	{"on_exit",			js_on_exit,			1,	JSTYPE_VOID,	JSDOCSTR("to_eval")
	,JSDOCSTR("add a string to evaluate/execute (LIFO stack) upon script's termination")
455
	,313
456
	},
rswindell's avatar
rswindell committed
457
	{"report_error",	js_report_error,	1,	JSTYPE_VOID,	JSDOCSTR("error [,fatal=<tt>false</tt>]")
458
459
	,JSDOCSTR("report an error using the standard JavaScript error reporting mechanism "
	"(including script filename and line number), "
460
461
	"if <i>fatal</i> is <i>true</i>, immediately terminates script")
	,313
462
	},
rswindell's avatar
rswindell committed
463
	{"get_parent",		js_get_parent,		1,	JSTYPE_OBJECT,	JSDOCSTR("object")
464
	,JSDOCSTR("return the parent of the specified object")
rswindell's avatar
rswindell committed
465
	,314
466
	},
467
468
469
	{0}
};

470
static JSBool js_internal_resolve(JSContext *cx, JSObject *obj, jsid id)
deuce's avatar
deuce committed
471
472
{
	char*			name=NULL;
473
	JSBool			ret;
deuce's avatar
deuce committed
474

deuce's avatar
deuce committed
475
476
477
478
	if(id != JSID_VOID && id != JSID_EMPTY) {
		jsval idval;
		
		JS_IdToValue(cx, id, &idval);
479
480
481
482
		if(JSVAL_IS_STRING(idval)) {
			JSSTRING_TO_MSTRING(cx, JSVAL_TO_STRING(idval), name, NULL);
			HANDLE_PENDING(cx);
		}
deuce's avatar
deuce committed
483
	}
deuce's avatar
deuce committed
484

485
486
487
488
	ret=js_SyncResolve(cx, obj, name, js_properties, js_functions, NULL, 0);
	if(name)
		free(name);
	return(ret);
deuce's avatar
deuce committed
489
490
491
492
}

static JSBool js_internal_enumerate(JSContext *cx, JSObject *obj)
{
deuce's avatar
deuce committed
493
	return(js_internal_resolve(cx, obj, JSID_VOID));
deuce's avatar
deuce committed
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
}

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

509
void DLLCALL js_EvalOnExit(JSContext *cx, JSObject *obj, js_callback_t* cb)
510
511
512
{
	char*	p;
	jsval	rval;
513
	JSObject* script;
514
	BOOL	auto_terminate=cb->auto_terminate;
515
516
517
518
519
520
521
522
523
524
	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);
525

526
	cb->auto_terminate=FALSE;
527

528
	while((p=strListPop(&list))!=NULL) {
529
530
531
		if((script=JS_CompileScript(cx, obj, p, strlen(p), NULL, 0))!=NULL) {
			JS_ExecuteScript(cx, obj, script, &rval);
		}
deuce's avatar
deuce committed
532
		free(p);
533
534
	}

535
	strListFree(&list);
536
537
538
539
	if(glob != obj)
		JS_SetPrivate(cx,obj,NULL);
	else
		pt->exit_func=NULL;
540

541
	if(auto_terminate)
542
		cb->auto_terminate = TRUE;
543
544
}

545
JSObject* DLLCALL js_CreateInternalJsObject(JSContext* cx, JSObject* parent, js_callback_t* cb, js_startup_t* startup)
546
547
548
549
550
551
552
{
	JSObject*	obj;

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

553
	if(!JS_SetPrivate(cx, obj, cb))	/* Store a pointer to js_callback_t */
554
555
		return(NULL);

556
557
558
	if(startup!=NULL) {
		JSObject*	load_path_list;
		jsval		val;
559
		str_list_t	load_path;
560
561
562
563
564
565
566

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

567
		if((load_path=strListSplitCopy(NULL, startup->load_path, ",")) != NULL) {
568
569
570
			JSString*	js_str;
			unsigned	i;

571
572
573
			for(i=0; load_path[i]!=NULL; i++) {
				if((js_str=JS_NewStringCopyZ(cx, load_path[i]))==NULL)
					break;
574
575
				val=STRING_TO_JSVAL(js_str);
				if(!JS_SetElement(cx, load_path_list, i, &val))
576
					break;
577
			}
578
			strListFree(&load_path);
579
580
581
		}
	}

582
#ifdef BUILD_JSDOCS
583
	js_DescribeSyncObject(cx,obj,"JavaScript execution and garbage collection control object",311);
584
585
586
587
588
	js_CreateArrayOfStrings(cx, obj, "_property_desc_list", prop_desc, JSPROP_READONLY);
#endif

	return(obj);
}
589

590
void DLLCALL js_PrepareToExecute(JSContext *cx, JSObject *obj, const char *filename, const char* startup_dir)
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
{
	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);
		}
615
616
617
618
619
		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);
620
621
	}
}