/[jscoverage]/trunk/js/jsscript.cpp
ViewVC logotype

Contents of /trunk/js/jsscript.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 507 - (show annotations)
Sun Jan 10 07:23:34 2010 UTC (9 years, 8 months ago) by siliconforks
File size: 57822 byte(s)
Update SpiderMonkey from Firefox 3.6rc1.

1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
24 *
25 * Contributor(s):
26 *
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS script operations.
43 */
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jsprf.h"
49 #include "jsapi.h"
50 #include "jsatom.h"
51 #include "jscntxt.h"
52 #include "jsversion.h"
53 #include "jsdbgapi.h"
54 #include "jsemit.h"
55 #include "jsfun.h"
56 #include "jsinterp.h"
57 #include "jslock.h"
58 #include "jsnum.h"
59 #include "jsopcode.h"
60 #include "jsparse.h"
61 #include "jsscope.h"
62 #include "jsscript.h"
63 #include "jstracer.h"
64 #if JS_HAS_XDR
65 #include "jsxdrapi.h"
66 #endif
67
68 #include "jsscriptinlines.h"
69
70 #if JS_HAS_SCRIPT_OBJECT
71
72 static const char js_script_exec_str[] = "Script.prototype.exec";
73 static const char js_script_compile_str[] = "Script.prototype.compile";
74
75 /*
76 * This routine requires that obj has been locked previously.
77 */
78 static jsint
79 GetScriptExecDepth(JSContext *cx, JSObject *obj)
80 {
81 jsval v;
82
83 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
84 v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass));
85 return JSVAL_TO_INT(v);
86 }
87
88 static void
89 AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta)
90 {
91 jsint execDepth;
92
93 JS_LOCK_OBJ(cx, obj);
94 execDepth = GetScriptExecDepth(cx, obj);
95 LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass),
96 INT_TO_JSVAL(execDepth + delta));
97 JS_UNLOCK_OBJ(cx, obj);
98 }
99
100 #if JS_HAS_TOSOURCE
101 static JSBool
102 script_toSource(JSContext *cx, uintN argc, jsval *vp)
103 {
104 JSObject *obj;
105 uint32 indent;
106 JSScript *script;
107 size_t i, j, k, n;
108 char buf[16];
109 jschar *s, *t;
110 JSString *str;
111
112 obj = JS_THIS_OBJECT(cx, vp);
113 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
114 return JS_FALSE;
115
116 indent = 0;
117 if (argc != 0) {
118 indent = js_ValueToECMAUint32(cx, &vp[2]);
119 if (JSVAL_IS_NULL(vp[2]))
120 return JS_FALSE;
121 }
122
123 script = (JSScript *) obj->getPrivate();
124
125 /* Let n count the source string length, j the "front porch" length. */
126 j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
127 n = j + 2;
128 if (!script) {
129 /* Let k count the constructor argument string length. */
130 k = 0;
131 s = NULL; /* quell GCC overwarning */
132 } else {
133 str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
134 (uintN)indent);
135 if (!str)
136 return JS_FALSE;
137 str = js_QuoteString(cx, str, '\'');
138 if (!str)
139 return JS_FALSE;
140 str->getCharsAndLength(s, k);
141 n += k;
142 }
143
144 /* Allocate the source string and copy into it. */
145 t = (jschar *) cx->malloc((n + 1) * sizeof(jschar));
146 if (!t)
147 return JS_FALSE;
148 for (i = 0; i < j; i++)
149 t[i] = buf[i];
150 for (j = 0; j < k; i++, j++)
151 t[i] = s[j];
152 t[i++] = ')';
153 t[i++] = ')';
154 t[i] = 0;
155
156 /* Create and return a JS string for t. */
157 str = JS_NewUCString(cx, t, n);
158 if (!str) {
159 cx->free(t);
160 return JS_FALSE;
161 }
162 *vp = STRING_TO_JSVAL(str);
163 return JS_TRUE;
164 }
165 #endif /* JS_HAS_TOSOURCE */
166
167 static JSBool
168 script_toString(JSContext *cx, uintN argc, jsval *vp)
169 {
170 uint32 indent;
171 JSObject *obj;
172 JSScript *script;
173 JSString *str;
174
175 indent = 0;
176 if (argc != 0) {
177 indent = js_ValueToECMAUint32(cx, &vp[2]);
178 if (JSVAL_IS_NULL(vp[2]))
179 return JS_FALSE;
180 }
181
182 obj = JS_THIS_OBJECT(cx, vp);
183 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
184 return JS_FALSE;
185 script = (JSScript *) obj->getPrivate();
186 if (!script) {
187 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
188 return JS_TRUE;
189 }
190
191 str = JS_DecompileScript(cx, script, "Script.prototype.toString",
192 (uintN)indent);
193 if (!str)
194 return JS_FALSE;
195 *vp = STRING_TO_JSVAL(str);
196 return JS_TRUE;
197 }
198
199 static JSBool
200 script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
201 jsval *rval)
202 {
203 JSString *str;
204 JSObject *scopeobj;
205 JSScript *script, *oldscript;
206 JSStackFrame *caller;
207 const char *file;
208 uintN line;
209 JSPrincipals *principals;
210 uint32 tcflags;
211 jsint execDepth;
212
213 /* Make sure obj is a Script object. */
214 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
215 return JS_FALSE;
216
217 /* If no args, leave private undefined and return early. */
218 if (argc == 0)
219 goto out;
220
221 /* Otherwise, the first arg is the script source to compile. */
222 str = js_ValueToString(cx, argv[0]);
223 if (!str)
224 return JS_FALSE;
225 argv[0] = STRING_TO_JSVAL(str);
226
227 scopeobj = NULL;
228 if (argc >= 2) {
229 if (!js_ValueToObject(cx, argv[1], &scopeobj))
230 return JS_FALSE;
231 argv[1] = OBJECT_TO_JSVAL(scopeobj);
232 }
233
234 /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
235 caller = js_GetScriptedCaller(cx, NULL);
236 JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain);
237
238 if (caller) {
239 if (!scopeobj) {
240 scopeobj = js_GetScopeChain(cx, caller);
241 if (!scopeobj)
242 return JS_FALSE;
243 }
244
245 principals = JS_EvalFramePrincipals(cx, cx->fp, caller);
246 file = js_ComputeFilename(cx, caller, principals, &line);
247 } else {
248 file = NULL;
249 line = 0;
250 principals = NULL;
251 }
252
253 /* Ensure we compile this script with the right (inner) principals. */
254 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str);
255 if (!scopeobj)
256 return JS_FALSE;
257
258 /*
259 * Compile the new script using the caller's scope chain, a la eval().
260 * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in
261 * tcflags and use NULL for the callerFrame argument, because compilation
262 * is here separated from execution, and the run-time scope chain may not
263 * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
264 * jsparse.c to optimize based on identity of run- and compile-time scope.
265 */
266 tcflags = 0;
267 script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags,
268 str->chars(), str->length(),
269 NULL, file, line);
270 if (!script)
271 return JS_FALSE;
272
273 JS_LOCK_OBJ(cx, obj);
274 execDepth = GetScriptExecDepth(cx, obj);
275
276 /*
277 * execDepth must be 0 to allow compilation here, otherwise the JSScript
278 * struct can be released while running.
279 */
280 if (execDepth > 0) {
281 JS_UNLOCK_OBJ(cx, obj);
282 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
283 JSMSG_COMPILE_EXECED_SCRIPT);
284 return JS_FALSE;
285 }
286
287 /* Swap script for obj's old script, if any. */
288 oldscript = (JSScript*) obj->getPrivate();
289 obj->setPrivate(script);
290 JS_UNLOCK_OBJ(cx, obj);
291
292 if (oldscript)
293 js_DestroyScript(cx, oldscript);
294
295 script->u.object = obj;
296 js_CallNewScriptHook(cx, script, NULL);
297
298 out:
299 /* Return the object. */
300 *rval = OBJECT_TO_JSVAL(obj);
301 return JS_TRUE;
302 }
303
304 static JSBool
305 script_compile(JSContext *cx, uintN argc, jsval *vp)
306 {
307 return script_compile_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp);
308 }
309
310 static JSBool
311 script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
312 jsval *rval)
313 {
314 JSObject *scopeobj;
315 JSStackFrame *caller;
316 JSPrincipals *principals;
317 JSScript *script;
318 JSBool ok;
319
320 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
321 return JS_FALSE;
322
323 scopeobj = NULL;
324 if (argc != 0) {
325 if (!js_ValueToObject(cx, argv[0], &scopeobj))
326 return JS_FALSE;
327 argv[0] = OBJECT_TO_JSVAL(scopeobj);
328 }
329
330 /*
331 * Emulate eval() by using caller's this, var object, sharp array, etc.,
332 * all propagated by js_Execute via a non-null fourth (down) argument to
333 * js_Execute. If there is no scripted caller, js_Execute uses its second
334 * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
335 *
336 * Unlike eval, which the compiler detects, Script.prototype.exec may be
337 * called from a lightweight function, or even from native code (in which
338 * case fp->varobj and fp->scopeChain are null). If exec is called from
339 * a lightweight function, we will need to get a Call object representing
340 * its frame, to act as the var object and scope chain head.
341 */
342 caller = js_GetScriptedCaller(cx, NULL);
343 if (caller && !caller->varobj) {
344 /* Called from a lightweight function. */
345 JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags));
346
347 /* Scope chain links from Call object to caller's scope chain. */
348 if (!js_GetCallObject(cx, caller))
349 return JS_FALSE;
350 }
351
352 if (!scopeobj) {
353 /* No scope object passed in: try to use the caller's scope chain. */
354 if (caller) {
355 /*
356 * Load caller->scopeChain after the conditional js_GetCallObject
357 * call above, which resets scopeChain as well as varobj.
358 */
359 scopeobj = js_GetScopeChain(cx, caller);
360 if (!scopeobj)
361 return JS_FALSE;
362 } else {
363 /*
364 * Called from native code, so we don't know what scope object to
365 * use. We could use the caller's scope chain (see above), but Script.prototype.exec
366 * might be a shared/sealed "superglobal" method. A more general
367 * approach would use cx->globalObject, which will be the same as
368 * exec.__parent__ in the non-superglobal case. In the superglobal
369 * case it's the right object: the global, not the superglobal.
370 */
371 scopeobj = cx->globalObject;
372 }
373 }
374
375 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec_str);
376 if (!scopeobj)
377 return JS_FALSE;
378
379 /* Keep track of nesting depth for the script. */
380 AdjustScriptExecDepth(cx, obj, 1);
381
382 /* Must get to out label after this */
383 script = (JSScript *) obj->getPrivate();
384 if (!script) {
385 ok = JS_FALSE;
386 goto out;
387 }
388
389 /* Belt-and-braces: check that this script object has access to scopeobj. */
390 principals = script->principals;
391 ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
392 CLASS_ATOM(cx, Script));
393 if (!ok)
394 goto out;
395
396 ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
397
398 out:
399 AdjustScriptExecDepth(cx, obj, -1);
400 return ok;
401 }
402
403 static JSBool
404 script_exec(JSContext *cx, uintN argc, jsval *vp)
405 {
406 return script_exec_sub(cx, JS_THIS_OBJECT(cx, vp), argc, vp + 2, vp);
407 }
408
409 #endif /* JS_HAS_SCRIPT_OBJECT */
410
411 #if JS_HAS_XDR
412
413 JSBool
414 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
415 {
416 JSContext *cx;
417 JSScript *script, *oldscript;
418 JSBool ok;
419 jsbytecode *code;
420 uint32 length, lineno, nslots, magic;
421 uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, i;
422 uint32 prologLength, version;
423 JSTempValueRooter tvr;
424 JSPrincipals *principals;
425 uint32 encodeable;
426 JSBool filenameWasSaved;
427 jssrcnote *notes, *sn;
428 JSSecurityCallbacks *callbacks;
429
430 cx = xdr->cx;
431 script = *scriptp;
432 nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = 0;
433 filenameWasSaved = JS_FALSE;
434 notes = NULL;
435
436 if (xdr->mode == JSXDR_ENCODE)
437 magic = JSXDR_MAGIC_SCRIPT_CURRENT;
438 if (!JS_XDRUint32(xdr, &magic))
439 return JS_FALSE;
440 if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) {
441 /* We do not provide binary compatibility with older scripts. */
442 if (!hasMagic) {
443 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
444 JSMSG_BAD_SCRIPT_MAGIC);
445 return JS_FALSE;
446 }
447 *hasMagic = JS_FALSE;
448 return JS_TRUE;
449 }
450 if (hasMagic)
451 *hasMagic = JS_TRUE;
452
453 if (xdr->mode == JSXDR_ENCODE) {
454 length = script->length;
455 prologLength = script->main - script->code;
456 JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
457 version = (uint32)script->version | (script->nfixed << 16);
458 lineno = (uint32)script->lineno;
459 nslots = (uint32)script->nslots;
460 nslots = (uint32)((script->staticLevel << 16) | script->nslots);
461 natoms = (uint32)script->atomMap.length;
462
463 /* Count the srcnotes, keeping notes pointing at the first one. */
464 notes = script->notes();
465 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
466 continue;
467 nsrcnotes = sn - notes;
468 nsrcnotes++; /* room for the terminator */
469
470 if (script->objectsOffset != 0)
471 nobjects = script->objects()->length;
472 if (script->upvarsOffset != 0)
473 nupvars = script->upvars()->length;
474 if (script->regexpsOffset != 0)
475 nregexps = script->regexps()->length;
476 if (script->trynotesOffset != 0)
477 ntrynotes = script->trynotes()->length;
478 }
479
480 if (!JS_XDRUint32(xdr, &length))
481 return JS_FALSE;
482 if (!JS_XDRUint32(xdr, &prologLength))
483 return JS_FALSE;
484 if (!JS_XDRUint32(xdr, &version))
485 return JS_FALSE;
486
487 /*
488 * To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
489 * and trynote counts early.
490 */
491 if (!JS_XDRUint32(xdr, &natoms))
492 return JS_FALSE;
493 if (!JS_XDRUint32(xdr, &nsrcnotes))
494 return JS_FALSE;
495 if (!JS_XDRUint32(xdr, &ntrynotes))
496 return JS_FALSE;
497 if (!JS_XDRUint32(xdr, &nobjects))
498 return JS_FALSE;
499 if (!JS_XDRUint32(xdr, &nupvars))
500 return JS_FALSE;
501 if (!JS_XDRUint32(xdr, &nregexps))
502 return JS_FALSE;
503
504 if (xdr->mode == JSXDR_DECODE) {
505 script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
506 nregexps, ntrynotes);
507 if (!script)
508 return JS_FALSE;
509
510 script->main += prologLength;
511 script->version = JSVersion(version & 0xffff);
512 script->nfixed = uint16(version >> 16);
513
514 /* If we know nsrcnotes, we allocated space for notes in script. */
515 notes = script->notes();
516 *scriptp = script;
517 JS_PUSH_TEMP_ROOT_SCRIPT(cx, script, &tvr);
518 }
519
520 /*
521 * Control hereafter must goto error on failure, in order for the
522 * DECODE case to destroy script.
523 */
524 oldscript = xdr->script;
525 code = script->code;
526 if (xdr->mode == JSXDR_ENCODE) {
527 code = js_UntrapScriptCode(cx, script);
528 if (!code)
529 goto error;
530 }
531
532 xdr->script = script;
533 ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode));
534
535 if (code != script->code)
536 cx->free(code);
537
538 if (!ok)
539 goto error;
540
541 if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
542 !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
543 !JS_XDRUint32(xdr, &lineno) ||
544 !JS_XDRUint32(xdr, &nslots)) {
545 goto error;
546 }
547
548 callbacks = JS_GetSecurityCallbacks(cx);
549 if (xdr->mode == JSXDR_ENCODE) {
550 principals = script->principals;
551 encodeable = callbacks && callbacks->principalsTranscoder;
552 if (!JS_XDRUint32(xdr, &encodeable))
553 goto error;
554 if (encodeable &&
555 !callbacks->principalsTranscoder(xdr, &principals)) {
556 goto error;
557 }
558 } else {
559 if (!JS_XDRUint32(xdr, &encodeable))
560 goto error;
561 if (encodeable) {
562 if (!(callbacks && callbacks->principalsTranscoder)) {
563 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
564 JSMSG_CANT_DECODE_PRINCIPALS);
565 goto error;
566 }
567 if (!callbacks->principalsTranscoder(xdr, &principals))
568 goto error;
569 script->principals = principals;
570 }
571 }
572
573 if (xdr->mode == JSXDR_DECODE) {
574 const char *filename = script->filename;
575 if (filename) {
576 filename = js_SaveScriptFilename(cx, filename);
577 if (!filename)
578 goto error;
579 cx->free((void *) script->filename);
580 script->filename = filename;
581 filenameWasSaved = JS_TRUE;
582 }
583 script->lineno = (uintN)lineno;
584 script->nslots = (uint16)nslots;
585 script->staticLevel = (uint16)(nslots >> 16);
586 }
587
588 for (i = 0; i != natoms; ++i) {
589 if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
590 goto error;
591 }
592
593 /*
594 * Here looping from 0-to-length to xdr objects is essential. It ensures
595 * that block objects from the script->objects array will be written and
596 * restored in the outer-to-inner order. js_XDRBlockObject relies on this
597 * to restore the parent chain.
598 */
599 for (i = 0; i != nobjects; ++i) {
600 JSObject **objp = &script->objects()->vector[i];
601 uint32 isBlock;
602 if (xdr->mode == JSXDR_ENCODE) {
603 JSClass *clasp = STOBJ_GET_CLASS(*objp);
604 JS_ASSERT(clasp == &js_FunctionClass ||
605 clasp == &js_BlockClass);
606 isBlock = (clasp == &js_BlockClass) ? 1 : 0;
607 }
608 if (!JS_XDRUint32(xdr, &isBlock))
609 goto error;
610 if (isBlock == 0) {
611 if (!js_XDRFunctionObject(xdr, objp))
612 goto error;
613 } else {
614 JS_ASSERT(isBlock == 1);
615 if (!js_XDRBlockObject(xdr, objp))
616 goto error;
617 }
618 }
619 for (i = 0; i != nupvars; ++i) {
620 if (!JS_XDRUint32(xdr, &script->upvars()->vector[i]))
621 goto error;
622 }
623 for (i = 0; i != nregexps; ++i) {
624 if (!js_XDRRegExpObject(xdr, &script->regexps()->vector[i]))
625 goto error;
626 }
627
628 if (ntrynotes != 0) {
629 /*
630 * We combine tn->kind and tn->stackDepth when serializing as XDR is not
631 * efficient when serializing small integer types.
632 */
633 JSTryNote *tn, *tnfirst;
634 uint32 kindAndDepth;
635 JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8));
636 JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16));
637
638 tnfirst = script->trynotes()->vector;
639 JS_ASSERT(script->trynotes()->length == ntrynotes);
640 tn = tnfirst + ntrynotes;
641 do {
642 --tn;
643 if (xdr->mode == JSXDR_ENCODE) {
644 kindAndDepth = ((uint32)tn->kind << 16)
645 | (uint32)tn->stackDepth;
646 }
647 if (!JS_XDRUint32(xdr, &kindAndDepth) ||
648 !JS_XDRUint32(xdr, &tn->start) ||
649 !JS_XDRUint32(xdr, &tn->length)) {
650 goto error;
651 }
652 if (xdr->mode == JSXDR_DECODE) {
653 tn->kind = (uint8)(kindAndDepth >> 16);
654 tn->stackDepth = (uint16)kindAndDepth;
655 }
656 } while (tn != tnfirst);
657 }
658
659 xdr->script = oldscript;
660 if (xdr->mode == JSXDR_DECODE)
661 JS_POP_TEMP_ROOT(cx, &tvr);
662 return JS_TRUE;
663
664 error:
665 if (xdr->mode == JSXDR_DECODE) {
666 JS_POP_TEMP_ROOT(cx, &tvr);
667 if (script->filename && !filenameWasSaved) {
668 cx->free((void *) script->filename);
669 script->filename = NULL;
670 }
671 js_DestroyScript(cx, script);
672 *scriptp = NULL;
673 }
674 xdr->script = oldscript;
675 return JS_FALSE;
676 }
677
678 #if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
679 /*
680 * These cannot be exposed to web content, and chrome does not need them, so
681 * we take them out of the Mozilla client altogether. Fortunately, there is
682 * no way to serialize a native function (see fun_xdrObject in jsfun.c).
683 */
684
685 static JSBool
686 script_freeze(JSContext *cx, uintN argc, jsval *vp)
687 {
688 JSObject *obj;
689 JSXDRState *xdr;
690 JSScript *script;
691 JSBool ok, hasMagic;
692 uint32 len;
693 void *buf;
694 JSString *str;
695
696 obj = JS_THIS_OBJECT(cx, vp);
697 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
698 return JS_FALSE;
699 script = (JSScript *) obj->getPrivate();
700 if (!script)
701 return JS_TRUE;
702
703 /* create new XDR */
704 xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
705 if (!xdr)
706 return JS_FALSE;
707
708 /* write */
709 ok = js_XDRScript(xdr, &script, &hasMagic);
710 if (!ok)
711 goto out;
712 if (!hasMagic) {
713 *vp = JSVAL_VOID;
714 goto out;
715 }
716
717 buf = JS_XDRMemGetData(xdr, &len);
718 if (!buf) {
719 ok = JS_FALSE;
720 goto out;
721 }
722
723 JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
724 len /= sizeof(jschar);
725 #if IS_BIG_ENDIAN
726 {
727 jschar *chars;
728 uint32 i;
729
730 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
731 chars = (jschar *)buf;
732 for (i = 0; i < len; i++)
733 chars[i] = JSXDR_SWAB16(chars[i]);
734 }
735 #endif
736 str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
737 if (!str) {
738 ok = JS_FALSE;
739 goto out;
740 }
741
742 *vp = STRING_TO_JSVAL(str);
743
744 out:
745 JS_XDRDestroy(xdr);
746 return ok;
747 }
748
749 static JSBool
750 script_thaw(JSContext *cx, uintN argc, jsval *vp)
751 {
752 JSObject *obj;
753 JSXDRState *xdr;
754 JSString *str;
755 void *buf;
756 uint32 len;
757 jsval v;
758 JSScript *script, *oldscript;
759 JSBool ok, hasMagic;
760 jsint execDepth;
761
762 obj = JS_THIS_OBJECT(cx, vp);
763 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
764 return JS_FALSE;
765
766 if (argc == 0)
767 return JS_TRUE;
768 str = js_ValueToString(cx, vp[2]);
769 if (!str)
770 return JS_FALSE;
771 vp[2] = STRING_TO_JSVAL(str);
772
773 /* create new XDR */
774 xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
775 if (!xdr)
776 return JS_FALSE;
777
778 str->getCharsAndLength(buf, len);
779 #if IS_BIG_ENDIAN
780 {
781 jschar *from, *to;
782 uint32 i;
783
784 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
785 from = (jschar *)buf;
786 to = (jschar *) cx->malloc(len * sizeof(jschar));
787 if (!to) {
788 JS_XDRDestroy(xdr);
789 return JS_FALSE;
790 }
791 for (i = 0; i < len; i++)
792 to[i] = JSXDR_SWAB16(from[i]);
793 buf = (char *)to;
794 }
795 #endif
796 len *= sizeof(jschar);
797 JS_XDRMemSetData(xdr, buf, len);
798
799 /* XXXbe should magic mismatch be error, or false return value? */
800 ok = js_XDRScript(xdr, &script, &hasMagic);
801 if (!ok)
802 goto out;
803 if (!hasMagic) {
804 *vp = JSVAL_FALSE;
805 goto out;
806 }
807
808 JS_LOCK_OBJ(cx, obj);
809 execDepth = GetScriptExecDepth(cx, obj);
810
811 /*
812 * execDepth must be 0 to allow compilation here, otherwise the JSScript
813 * struct can be released while running.
814 */
815 if (execDepth > 0) {
816 JS_UNLOCK_OBJ(cx, obj);
817 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
818 JSMSG_COMPILE_EXECED_SCRIPT);
819 goto out;
820 }
821
822 /* Swap script for obj's old script, if any. */
823 oldscript = (JSScript *) obj->getPrivate();
824 obj->setPrivate(script);
825 JS_UNLOCK_OBJ(cx, obj);
826
827 if (oldscript)
828 js_DestroyScript(cx, oldscript);
829
830 script->u.object = obj;
831 js_CallNewScriptHook(cx, script, NULL);
832
833 out:
834 /*
835 * We reset the buffer to be NULL so that it doesn't free the chars
836 * memory owned by str (vp[2]).
837 */
838 JS_XDRMemSetData(xdr, NULL, 0);
839 JS_XDRDestroy(xdr);
840 #if IS_BIG_ENDIAN
841 cx->free(buf);
842 #endif
843 *vp = JSVAL_TRUE;
844 return ok;
845 }
846
847 static const char js_thaw_str[] = "thaw";
848
849 #endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */
850 #endif /* JS_HAS_XDR */
851
852 #if JS_HAS_SCRIPT_OBJECT
853
854 static JSFunctionSpec script_methods[] = {
855 #if JS_HAS_TOSOURCE
856 JS_FN(js_toSource_str, script_toSource, 0,0),
857 #endif
858 JS_FN(js_toString_str, script_toString, 0,0),
859 JS_FN("compile", script_compile, 2,0),
860 JS_FN("exec", script_exec, 1,0),
861 #if JS_HAS_XDR_FREEZE_THAW
862 JS_FN("freeze", script_freeze, 0,0),
863 JS_FN(js_thaw_str, script_thaw, 1,0),
864 #endif /* JS_HAS_XDR_FREEZE_THAW */
865 JS_FS_END
866 };
867
868 #endif /* JS_HAS_SCRIPT_OBJECT */
869
870 static void
871 script_finalize(JSContext *cx, JSObject *obj)
872 {
873 JSScript *script = (JSScript *) obj->getPrivate();
874 if (script)
875 js_DestroyScript(cx, script);
876 }
877
878 static JSBool
879 script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
880 {
881 #if JS_HAS_SCRIPT_OBJECT
882 return script_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
883 #else
884 return JS_FALSE;
885 #endif
886 }
887
888 static void
889 script_trace(JSTracer *trc, JSObject *obj)
890 {
891 JSScript *script = (JSScript *) obj->getPrivate();
892 if (script)
893 js_TraceScript(trc, script);
894 }
895
896 #if !JS_HAS_SCRIPT_OBJECT
897 #define JSProto_Script JSProto_Object
898 #endif
899
900 JS_FRIEND_DATA(JSClass) js_ScriptClass = {
901 js_Script_str,
902 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
903 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script),
904 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
905 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize,
906 NULL, NULL, script_call, NULL,/*XXXbe xdr*/
907 NULL, NULL, JS_CLASS_TRACE(script_trace), NULL
908 };
909
910 #if JS_HAS_SCRIPT_OBJECT
911
912 static JSBool
913 Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
914 {
915 /* If not constructing, replace obj with a new Script object. */
916 if (!JS_IsConstructing(cx)) {
917 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
918 if (!obj)
919 return JS_FALSE;
920
921 /*
922 * script_compile_sub does not use rval to root its temporaries so we
923 * can use it to root obj.
924 */
925 *rval = OBJECT_TO_JSVAL(obj);
926 }
927
928 if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0)))
929 return JS_FALSE;
930
931 return script_compile_sub(cx, obj, argc, argv, rval);
932 }
933
934 #if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
935
936 static JSBool
937 script_static_thaw(JSContext *cx, uintN argc, jsval *vp)
938 {
939 JSObject *obj;
940
941 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
942 if (!obj)
943 return JS_FALSE;
944 vp[1] = OBJECT_TO_JSVAL(obj);
945 if (!script_thaw(cx, argc, vp))
946 return JS_FALSE;
947 *vp = OBJECT_TO_JSVAL(obj);
948 return JS_TRUE;
949 }
950
951 static JSFunctionSpec script_static_methods[] = {
952 JS_FN(js_thaw_str, script_static_thaw, 1,0),
953 JS_FS_END
954 };
955
956 #else /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
957
958 #define script_static_methods NULL
959
960 #endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
961
962 JSObject *
963 js_InitScriptClass(JSContext *cx, JSObject *obj)
964 {
965 return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
966 NULL, script_methods, NULL, script_static_methods);
967 }
968
969 #endif /* JS_HAS_SCRIPT_OBJECT */
970
971 /*
972 * Shared script filename management.
973 */
974 static int
975 js_compare_strings(const void *k1, const void *k2)
976 {
977 return strcmp((const char *) k1, (const char *) k2) == 0;
978 }
979
980 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
981 typedef struct ScriptFilenameEntry {
982 JSHashEntry *next; /* hash chain linkage */
983 JSHashNumber keyHash; /* key hash function result */
984 const void *key; /* ptr to filename, below */
985 uint32 flags; /* user-defined filename prefix flags */
986 JSPackedBool mark; /* GC mark flag */
987 char filename[3]; /* two or more bytes, NUL-terminated */
988 } ScriptFilenameEntry;
989
990 static void *
991 js_alloc_table_space(void *priv, size_t size)
992 {
993 return js_malloc(size);
994 }
995
996 static void
997 js_free_table_space(void *priv, void *item, size_t size)
998 {
999 js_free(item);
1000 }
1001
1002 static JSHashEntry *
1003 js_alloc_sftbl_entry(void *priv, const void *key)
1004 {
1005 size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
1006 strlen((const char *) key) + 1;
1007
1008 return (JSHashEntry *) js_malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
1009 }
1010
1011 static void
1012 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
1013 {
1014 if (flag != HT_FREE_ENTRY)
1015 return;
1016 js_free(he);
1017 }
1018
1019 static JSHashAllocOps sftbl_alloc_ops = {
1020 js_alloc_table_space, js_free_table_space,
1021 js_alloc_sftbl_entry, js_free_sftbl_entry
1022 };
1023
1024 static void
1025 FinishRuntimeScriptState(JSRuntime *rt)
1026 {
1027 if (rt->scriptFilenameTable) {
1028 JS_HashTableDestroy(rt->scriptFilenameTable);
1029 rt->scriptFilenameTable = NULL;
1030 }
1031 #ifdef JS_THREADSAFE
1032 if (rt->scriptFilenameTableLock) {
1033 JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
1034 rt->scriptFilenameTableLock = NULL;
1035 }
1036 #endif
1037 }
1038
1039 JSBool
1040 js_InitRuntimeScriptState(JSRuntime *rt)
1041 {
1042 #ifdef JS_THREADSAFE
1043 JS_ASSERT(!rt->scriptFilenameTableLock);
1044 rt->scriptFilenameTableLock = JS_NEW_LOCK();
1045 if (!rt->scriptFilenameTableLock)
1046 return JS_FALSE;
1047 #endif
1048 JS_ASSERT(!rt->scriptFilenameTable);
1049 rt->scriptFilenameTable =
1050 JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
1051 &sftbl_alloc_ops, NULL);
1052 if (!rt->scriptFilenameTable) {
1053 FinishRuntimeScriptState(rt); /* free lock if threadsafe */
1054 return JS_FALSE;
1055 }
1056 JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
1057 return JS_TRUE;
1058 }
1059
1060 typedef struct ScriptFilenamePrefix {
1061 JSCList links; /* circular list linkage for easy deletion */
1062 const char *name; /* pointer to pinned ScriptFilenameEntry string */
1063 size_t length; /* prefix string length, precomputed */
1064 uint32 flags; /* user-defined flags to inherit from this prefix */
1065 } ScriptFilenamePrefix;
1066
1067 void
1068 js_FreeRuntimeScriptState(JSRuntime *rt)
1069 {
1070 if (!rt->scriptFilenameTable)
1071 return;
1072
1073 while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
1074 ScriptFilenamePrefix *sfp = (ScriptFilenamePrefix *)
1075 rt->scriptFilenamePrefixes.next;
1076 JS_REMOVE_LINK(&sfp->links);
1077 js_free(sfp);
1078 }
1079 FinishRuntimeScriptState(rt);
1080 }
1081
1082 #ifdef DEBUG_brendan
1083 #define DEBUG_SFTBL
1084 #endif
1085 #ifdef DEBUG_SFTBL
1086 size_t sftbl_savings = 0;
1087 #endif
1088
1089 static ScriptFilenameEntry *
1090 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
1091 {
1092 JSHashTable *table;
1093 JSHashNumber hash;
1094 JSHashEntry **hep;
1095 ScriptFilenameEntry *sfe;
1096 size_t length;
1097 JSCList *head, *link;
1098 ScriptFilenamePrefix *sfp;
1099
1100 table = rt->scriptFilenameTable;
1101 hash = JS_HashString(filename);
1102 hep = JS_HashTableRawLookup(table, hash, filename);
1103 sfe = (ScriptFilenameEntry *) *hep;
1104 #ifdef DEBUG_SFTBL
1105 if (sfe)
1106 sftbl_savings += strlen(sfe->filename);
1107 #endif
1108
1109 if (!sfe) {
1110 sfe = (ScriptFilenameEntry *)
1111 JS_HashTableRawAdd(table, hep, hash, filename, NULL);
1112 if (!sfe)
1113 return NULL;
1114 sfe->key = strcpy(sfe->filename, filename);
1115 sfe->flags = 0;
1116 sfe->mark = JS_FALSE;
1117 }
1118
1119 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1120 if (flags != 0) {
1121 /* Search in case filename was saved already; we must be idempotent. */
1122 sfp = NULL;
1123 length = strlen(filename);
1124 for (head = link = &rt->scriptFilenamePrefixes;
1125 link->next != head;
1126 link = link->next) {
1127 /* Lag link behind sfp to insert in non-increasing length order. */
1128 sfp = (ScriptFilenamePrefix *) link->next;
1129 if (!strcmp(sfp->name, filename))
1130 break;
1131 if (sfp->length <= length) {
1132 sfp = NULL;
1133 break;
1134 }
1135 sfp = NULL;
1136 }
1137
1138 if (!sfp) {
1139 /* No such prefix: add one now. */
1140 sfp = (ScriptFilenamePrefix *) js_malloc(sizeof(ScriptFilenamePrefix));
1141 if (!sfp)
1142 return NULL;
1143 JS_INSERT_AFTER(&sfp->links, link);
1144 sfp->name = sfe->filename;
1145 sfp->length = length;
1146 sfp->flags = 0;
1147 }
1148
1149 /*
1150 * Accumulate flags in both sfe and sfp: sfe for later access from the
1151 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1152 * filename entries can inherit by prefix.
1153 */
1154 sfe->flags |= flags;
1155 sfp->flags |= flags;
1156 }
1157
1158 #ifdef JS_FUNCTION_METERING
1159 size_t len = strlen(sfe->filename);
1160 if (len >= sizeof rt->lastScriptFilename)
1161 len = sizeof rt->lastScriptFilename - 1;
1162 memcpy(rt->lastScriptFilename, sfe->filename, len);
1163 rt->lastScriptFilename[len] = '\0';
1164 #endif
1165
1166 return sfe;
1167 }
1168
1169 const char *
1170 js_SaveScriptFilename(JSContext *cx, const char *filename)
1171 {
1172 JSRuntime *rt;
1173 ScriptFilenameEntry *sfe;
1174 JSCList *head, *link;
1175 ScriptFilenamePrefix *sfp;
1176
1177 rt = cx->runtime;
1178 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1179 sfe = SaveScriptFilename(rt, filename, 0);
1180 if (!sfe) {
1181 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1182 JS_ReportOutOfMemory(cx);
1183 return NULL;
1184 }
1185
1186 /*
1187 * Try to inherit flags by prefix. We assume there won't be more than a
1188 * few (dozen! ;-) prefixes, so linear search is tolerable.
1189 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1190 */
1191 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1192 link != head;
1193 link = link->next) {
1194 sfp = (ScriptFilenamePrefix *) link;
1195 if (!strncmp(sfp->name, filename, sfp->length)) {
1196 sfe->flags |= sfp->flags;
1197 break;
1198 }
1199 }
1200 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1201 return sfe->filename;
1202 }
1203
1204 const char *
1205 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
1206 {
1207 ScriptFilenameEntry *sfe;
1208
1209 /* This may be called very early, via the jsdbgapi.h entry point. */
1210 if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
1211 return NULL;
1212
1213 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1214 sfe = SaveScriptFilename(rt, filename, flags);
1215 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1216 if (!sfe)
1217 return NULL;
1218
1219 return sfe->filename;
1220 }
1221
1222 /*
1223 * Back up from a saved filename by its offset within its hash table entry.
1224 */
1225 #define FILENAME_TO_SFE(fn) \
1226 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1227
1228 /*
1229 * The sfe->key member, redundant given sfe->filename but required by the old
1230 * jshash.c code, here gives us a useful sanity check. This assertion will
1231 * very likely botch if someone tries to mark a string that wasn't allocated
1232 * as an sfe->filename.
1233 */
1234 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
1235
1236 uint32
1237 js_GetScriptFilenameFlags(const char *filename)
1238 {
1239 ScriptFilenameEntry *sfe;
1240
1241 sfe = FILENAME_TO_SFE(filename);
1242 ASSERT_VALID_SFE(sfe);
1243 return sfe->flags;
1244 }
1245
1246 void
1247 js_MarkScriptFilename(const char *filename)
1248 {
1249 ScriptFilenameEntry *sfe;
1250
1251 sfe = FILENAME_TO_SFE(filename);
1252 ASSERT_VALID_SFE(sfe);
1253 sfe->mark = JS_TRUE;
1254 }
1255
1256 static intN
1257 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
1258 {
1259 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1260
1261 sfe->mark = JS_TRUE;
1262 return HT_ENUMERATE_NEXT;
1263 }
1264
1265 void
1266 js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
1267 {
1268 JSCList *head, *link;
1269 ScriptFilenamePrefix *sfp;
1270
1271 if (!rt->scriptFilenameTable)
1272 return;
1273
1274 if (keepAtoms) {
1275 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1276 js_script_filename_marker,
1277 rt);
1278 }
1279 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1280 link != head;
1281 link = link->next) {
1282 sfp = (ScriptFilenamePrefix *) link;
1283 js_MarkScriptFilename(sfp->name);
1284 }
1285 }
1286
1287 static intN
1288 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
1289 {
1290 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1291
1292 if (!sfe->mark)
1293 return HT_ENUMERATE_REMOVE;
1294 sfe->mark = JS_FALSE;
1295 return HT_ENUMERATE_NEXT;
1296 }
1297
1298 void
1299 js_SweepScriptFilenames(JSRuntime *rt)
1300 {
1301 if (!rt->scriptFilenameTable)
1302 return;
1303
1304 /*
1305 * JS_HashTableEnumerateEntries shrinks the table if many entries are
1306 * removed preventing wasting memory on a too sparse table.
1307 */
1308 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1309 js_script_filename_sweeper,
1310 rt);
1311 #ifdef DEBUG_notme
1312 #ifdef DEBUG_SFTBL
1313 printf("script filename table savings so far: %u\n", sftbl_savings);
1314 #endif
1315 #endif
1316 }
1317
1318 /*
1319 * JSScript data structures memory alignment:
1320 *
1321 * JSScript
1322 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
1323 * use script->objects() to access it.
1324 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
1325 * use script->regexps() to access it.
1326 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
1327 * != 0, use script->trynotes() to access it.
1328 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
1329 * JSScript.atomMap.vector if any.
1330 * JSObject *o[] array of script->objects()->length objects if any
1331 * pointed by script->objects()->vector.
1332 * JSObject *r[] array of script->regexps()->length regexps if any
1333 * pointed by script->regexps()->vector.
1334 * JSTryNote t[] array of script->trynotes()->length try notes if any
1335 * pointed by script->trynotes()->vector.
1336 * jsbytecode b[] script bytecode pointed by JSScript.code.
1337 * jssrcnote s[] script source notes, use script->notes() to access it
1338 *
1339 * The alignment avoids gaps between entries as alignment requirement for each
1340 * subsequent structure or array is the same or divides the alignment
1341 * requirement for the previous one.
1342 *
1343 * The followings asserts checks that assuming that the alignment requirement
1344 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
1345 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
1346 */
1347 JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0);
1348 JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0);
1349 JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray));
1350 JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *));
1351 JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0);
1352 JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32));
1353 JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0);
1354 JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
1355
1356 /*
1357 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
1358 * sufficient.
1359 */
1360 JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
1361 sizeof(JSUpvarArray) < JS_BIT(8));
1362
1363 JSScript *
1364 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
1365 uint32 nobjects, uint32 nupvars, uint32 nregexps,
1366 uint32 ntrynotes)
1367 {
1368 size_t size, vectorSize;
1369 JSScript *script;
1370 uint8 *cursor;
1371
1372 size = sizeof(JSScript) +
1373 sizeof(JSAtom *) * natoms +
1374 length * sizeof(jsbytecode) +
1375 nsrcnotes * sizeof(jssrcnote);
1376 if (nobjects != 0)
1377 size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
1378 if (nupvars != 0)
1379 size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
1380 if (nregexps != 0)
1381 size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
1382 if (ntrynotes != 0)
1383 size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
1384
1385 script = (JSScript *) cx->malloc(size);
1386 if (!script)
1387 return NULL;
1388 memset(script, 0, sizeof(JSScript));
1389 script->length = length;
1390 script->version = cx->version;
1391
1392 cursor = (uint8 *)script + sizeof(JSScript);
1393 if (nobjects != 0) {
1394 script->objectsOffset = (uint8)(cursor - (uint8 *)script);
1395 cursor += sizeof(JSObjectArray);
1396 }
1397 if (nupvars != 0) {
1398 script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
1399 cursor += sizeof(JSUpvarArray);
1400 }
1401 if (nregexps != 0) {
1402 script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
1403 cursor += sizeof(JSObjectArray);
1404 }
1405 if (ntrynotes != 0) {
1406 script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
1407 cursor += sizeof(JSTryNoteArray);
1408 }
1409
1410 if (natoms != 0) {
1411 script->atomMap.length = natoms;
1412 script->atomMap.vector = (JSAtom **)cursor;
1413 vectorSize = natoms * sizeof(script->atomMap.vector[0]);
1414
1415 /*
1416 * Clear object map's vector so the GC tracing can run when not yet
1417 * all atoms are copied to the array.
1418 */
1419 memset(cursor, 0, vectorSize);
1420 cursor += vectorSize;
1421 }
1422
1423 if (nobjects != 0) {
1424 script->objects()->length = nobjects;
1425 script->objects()->vector = (JSObject **)cursor;
1426 vectorSize = nobjects * sizeof(script->objects()->vector[0]);
1427 memset(cursor, 0, vectorSize);
1428 cursor += vectorSize;
1429 }
1430
1431 if (nregexps != 0) {
1432 script->regexps()->length = nregexps;
1433 script->regexps()->vector = (JSObject **)cursor;
1434 vectorSize = nregexps * sizeof(script->regexps()->vector[0]);
1435 memset(cursor, 0, vectorSize);
1436 cursor += vectorSize;
1437 }
1438
1439 if (ntrynotes != 0) {
1440 script->trynotes()->length = ntrynotes;
1441 script->trynotes()->vector = (JSTryNote *)cursor;
1442 vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
1443 #ifdef DEBUG
1444 memset(cursor, 0, vectorSize);
1445 #endif
1446 cursor += vectorSize;
1447 }
1448
1449 /*
1450 * NB: We allocate the vector of uint32 upvar cookies after all vectors of
1451 * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
1452 */
1453 if (nupvars != 0) {
1454 script->upvars()->length = nupvars;
1455 script->upvars()->vector = (uint32 *)cursor;
1456 vectorSize = nupvars * sizeof(script->upvars()->vector[0]);
1457 memset(cursor, 0, vectorSize);
1458 cursor += vectorSize;
1459 }
1460
1461 script->code = script->main = (jsbytecode *)cursor;
1462 JS_ASSERT(cursor +
1463 length * sizeof(jsbytecode) +
1464 nsrcnotes * sizeof(jssrcnote) ==
1465 (uint8 *)script + size);
1466
1467 #ifdef CHECK_SCRIPT_OWNER
1468 script->owner = cx->thread;
1469 #endif
1470 return script;
1471 }
1472
1473 JSScript *
1474 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
1475 {
1476 uint32 mainLength, prologLength, nsrcnotes, nfixed;
1477 JSScript *script;
1478 const char *filename;
1479 JSFunction *fun;
1480
1481 /* The counts of indexed things must be checked during code generation. */
1482 JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
1483 JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
1484 JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
1485
1486 mainLength = CG_OFFSET(cg);
1487 prologLength = CG_PROLOG_OFFSET(cg);
1488 CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1489 script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
1490 cg->atomList.count, cg->objectList.length,
1491 cg->upvarList.count, cg->regexpList.length,
1492 cg->ntrynotes);
1493 if (!script)
1494 return NULL;
1495
1496 /* Now that we have script, error control flow must go to label bad. */
1497 script->main += prologLength;
1498 memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1499 memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1500 nfixed = (cg->flags & TCF_IN_FUNCTION)
1501 ? cg->fun->u.i.nvars
1502 : cg->ngvars + cg->regexpList.length;
1503 JS_ASSERT(nfixed < SLOTNO_LIMIT);
1504 script->nfixed = (uint16) nfixed;
1505 js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
1506
1507 filename = cg->compiler->tokenStream.filename;
1508 if (filename) {
1509 script->filename = js_SaveScriptFilename(cx, filename);
1510 if (!script->filename)
1511 goto bad;
1512 }
1513 script->lineno = cg->firstLine;
1514 if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
1515 js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR,
1516 JSMSG_NEED_DIET, "script");
1517 goto bad;
1518 }
1519 script->nslots = script->nfixed + cg->maxStackDepth;
1520 script->staticLevel = cg->staticLevel;
1521 script->principals = cg->compiler->principals;
1522 if (script->principals)
1523 JSPRINCIPALS_HOLD(cx, script->principals);
1524
1525 if (!js_FinishTakingSrcNotes(cx, cg, script->notes()))
1526 goto bad;
1527 if (cg->ntrynotes != 0)
1528 js_FinishTakingTryNotes(cg, script->trynotes());
1529 if (cg->objectList.length != 0)
1530 cg->objectList.finish(script->objects());
1531 if (cg->regexpList.length != 0)
1532 cg->regexpList.finish(script->regexps());
1533 if (cg->flags & TCF_NO_SCRIPT_RVAL)
1534 script->flags |= JSSF_NO_SCRIPT_RVAL;
1535
1536 if (cg->upvarList.count != 0) {
1537 JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
1538 memcpy(script->upvars()->vector, cg->upvarMap.vector,
1539 cg->upvarList.count * sizeof(uint32));
1540 cg->upvarList.clear();
1541 cx->free(cg->upvarMap.vector);
1542 cg->upvarMap.vector = NULL;
1543 }
1544
1545 /*
1546 * We initialize fun->u.script to be the script constructed above
1547 * so that the debugger has a valid FUN_SCRIPT(fun).
1548 */
1549 fun = NULL;
1550 if (cg->flags & TCF_IN_FUNCTION) {
1551 fun = cg->fun;
1552 JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
1553 JS_ASSERT_IF(script->upvarsOffset != 0,
1554 script->upvars()->length == fun->u.i.nupvars);
1555
1556 js_FreezeLocalNames(cx, fun);
1557 fun->u.i.script = script;
1558 #ifdef CHECK_SCRIPT_OWNER
1559 script->owner = NULL;
1560 #endif
1561 if (cg->flags & TCF_FUN_HEAVYWEIGHT)
1562 fun->flags |= JSFUN_HEAVYWEIGHT;
1563 }
1564
1565 /* Tell the debugger about this compiled script. */
1566 js_CallNewScriptHook(cx, script, fun);
1567 return script;
1568
1569 bad:
1570 js_DestroyScript(cx, script);
1571 return NULL;
1572 }
1573
1574 JS_FRIEND_API(void)
1575 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1576 {
1577 JSNewScriptHook hook;
1578
1579 hook = cx->debugHooks->newScriptHook;
1580 if (hook) {
1581 JS_KEEP_ATOMS(cx->runtime);
1582 hook(cx, script->filename, script->lineno, script, fun,
1583 cx->debugHooks->newScriptHookData);
1584 JS_UNKEEP_ATOMS(cx->runtime);
1585 }
1586 }
1587
1588 JS_FRIEND_API(void)
1589 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1590 {
1591 JSDestroyScriptHook hook;
1592
1593 hook = cx->debugHooks->destroyScriptHook;
1594 if (hook)
1595 hook(cx, script, cx->debugHooks->destroyScriptHookData);
1596 }
1597
1598 void
1599 js_DestroyScript(JSContext *cx, JSScript *script)
1600 {
1601 js_CallDestroyScriptHook(cx, script);
1602 JS_ClearScriptTraps(cx, script);
1603
1604 if (script->principals)
1605 JSPRINCIPALS_DROP(cx, script->principals);
1606
1607 if (JS_GSN_CACHE(cx).code == script->code)
1608 JS_PURGE_GSN_CACHE(cx);
1609
1610 /*
1611 * Worry about purging the property cache and any compiled traces related
1612 * to its bytecode if this script is being destroyed from JS_DestroyScript
1613 * or equivalent according to a mandatory "New/Destroy" protocol.
1614 *
1615 * The GC purges all property caches when regenerating shapes upon shape
1616 * generator overflow, so no need in that event to purge just the entries
1617 * for this script.
1618 *
1619 * The GC purges trace-JITted code on every GC activation, not just when
1620 * regenerating shapes, so we don't have to purge fragments if the GC is
1621 * currently running.
1622 *
1623 * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
1624 * current thread's property cache, so a script not owned by a function
1625 * or object, which hands off lifetime management for that script to the
1626 * GC, must be used by only one thread over its lifetime.
1627 *
1628 * This should be an API-compatible change, since a script is never safe
1629 * against premature GC if shared among threads without a rooted object
1630 * wrapping it to protect the script's mapped atoms against GC. We use
1631 * script->owner to enforce this requirement via assertions.
1632 */
1633 #ifdef CHECK_SCRIPT_OWNER
1634 JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
1635 #endif
1636
1637 /* FIXME: bug 506341; would like to do this only if regenerating shapes. */
1638 if (!cx->runtime->gcRunning) {
1639 JSStackFrame *fp = js_GetTopStackFrame(cx);
1640
1641 if (!(fp && (fp->flags & JSFRAME_EVAL))) {
1642 js_PurgePropertyCacheForScript(cx, script);
1643
1644 #ifdef CHECK_SCRIPT_OWNER
1645 JS_ASSERT(script->owner == cx->thread);
1646 #endif
1647 }
1648 }
1649
1650 #ifdef JS_TRACER
1651 js_PurgeScriptFragments(cx, script);
1652 #endif
1653
1654 cx->free(script);
1655 }
1656
1657 void
1658 js_TraceScript(JSTracer *trc, JSScript *script)
1659 {
1660 JSAtomMap *map;
1661 uintN i, length;
1662 JSAtom **vector;
1663 jsval v;
1664 JSObjectArray *objarray;
1665
1666 map = &script->atomMap;
1667 length = map->length;
1668 vector = map->vector;
1669 for (i = 0; i < length; i++) {
1670 v = ATOM_KEY(vector[i]);
1671 if (JSVAL_IS_TRACEABLE(v)) {
1672 JS_SET_TRACING_INDEX(trc, "atomMap", i);
1673 JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1674 }
1675 }
1676
1677 if (script->objectsOffset != 0) {
1678 objarray = script->objects();
1679 i = objarray->length;
1680 do {
1681 --i;
1682 if (objarray->vector[i]) {
1683 JS_SET_TRACING_INDEX(trc, "objects", i);
1684 JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1685 }
1686 } while (i != 0);
1687 }
1688
1689 if (script->regexpsOffset != 0) {
1690 objarray = script->regexps();
1691 i = objarray->length;
1692 do {
1693 --i;
1694 if (objarray->vector[i]) {
1695 JS_SET_TRACING_INDEX(trc, "regexps", i);
1696 JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1697 }
1698 } while (i != 0);
1699 }
1700
1701 if (script->u.object) {
1702 JS_SET_TRACING_NAME(trc, "object");
1703 JS_CallTracer(trc, script->u.object, JSTRACE_OBJECT);
1704 }
1705
1706 if (IS_GC_MARKING_TRACER(trc) && script->filename)
1707 js_MarkScriptFilename(script->filename);
1708 }
1709
1710 typedef struct GSNCacheEntry {
1711 JSDHashEntryHdr hdr;
1712 jsbytecode *pc;
1713 jssrcnote *sn;
1714 } GSNCacheEntry;
1715
1716 #define GSN_CACHE_THRESHOLD 100
1717
1718 void
1719 js_PurgeGSNCache(JSGSNCache *cache)
1720 {
1721 cache->code = NULL;
1722 if (cache->table.ops) {
1723 JS_DHashTableFinish(&cache->table);
1724 cache->table.ops = NULL;
1725 }
1726 GSN_CACHE_METER(cache, purges);
1727 }
1728
1729 jssrcnote *
1730 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1731 {
1732 ptrdiff_t target, offset;
1733 GSNCacheEntry *entry;
1734 jssrcnote *sn, *result;
1735 uintN nsrcnotes;
1736
1737
1738 target = pc - script->code;
1739 if ((uint32)target >= script->length)
1740 return NULL;
1741
1742 if (JS_GSN_CACHE(cx).code == script->code) {
1743 JS_METER_GSN_CACHE(cx, hits);
1744 entry = (GSNCacheEntry *)
1745 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1746 JS_DHASH_LOOKUP);
1747 return entry->sn;
1748 }
1749
1750 JS_METER_GSN_CACHE(cx, misses);
1751 offset = 0;
1752 for (sn = script->notes(); ; sn = SN_NEXT(sn)) {
1753 if (SN_IS_TERMINATOR(sn)) {
1754 result = NULL;
1755 break;
1756 }
1757 offset += SN_DELTA(sn);
1758 if (offset == target && SN_IS_GETTABLE(sn)) {
1759 result = sn;
1760 break;
1761 }
1762 }
1763
1764 if (JS_GSN_CACHE(cx).code != script->code &&
1765 script->length >= GSN_CACHE_THRESHOLD) {
1766 JS_PURGE_GSN_CACHE(cx);
1767 nsrcnotes = 0;
1768 for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
1769 sn = SN_NEXT(sn)) {
1770 if (SN_IS_GETTABLE(sn))
1771 ++nsrcnotes;
1772 }
1773 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
1774 NULL, sizeof(GSNCacheEntry),
1775 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) {
1776 JS_GSN_CACHE(cx).table.ops = NULL;
1777 } else {
1778 pc = script->code;
1779 for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
1780 sn = SN_NEXT(sn)) {
1781 pc += SN_DELTA(sn);
1782 if (SN_IS_GETTABLE(sn)) {
1783 entry = (GSNCacheEntry *)
1784 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1785 JS_DHASH_ADD);
1786 entry->pc = pc;
1787 entry->sn = sn;
1788 }
1789 }
1790 JS_GSN_CACHE(cx).code = script->code;
1791 JS_METER_GSN_CACHE(cx, fills);
1792 }
1793 }
1794
1795 return result;
1796 }
1797
1798 uintN
1799 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
1800 {
1801 return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc);
1802 }
1803
1804 uintN
1805 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1806 {
1807 JSOp op;
1808 JSFunction *fun;
1809 uintN lineno;
1810 ptrdiff_t offset, target;
1811 jssrcnote *sn;
1812 JSSrcNoteType type;
1813
1814 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1815 if (!pc)
1816 return 0;
1817
1818 /*
1819 * Special case: function definition needs no line number note because
1820 * the function's script contains its starting line number.
1821 */
1822 op = js_GetOpcode(cx, script, pc);
1823 if (js_CodeSpec[op].format & JOF_INDEXBASE)
1824 pc += js_CodeSpec[op].length;
1825 if (*pc == JSOP_DEFFUN) {
1826 GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun);
1827 return fun->u.i.script->lineno;
1828 }
1829
1830 /*
1831 * General case: walk through source notes accumulating their deltas,
1832 * keeping track of line-number notes, until we pass the note for pc's
1833 * offset within script->code.
1834 */
1835 lineno = script->lineno;
1836 offset = 0;
1837 target = pc - script->code;
1838 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1839 offset += SN_DELTA(sn);
1840 type = (JSSrcNoteType) SN_TYPE(sn);
1841 if (type == SRC_SETLINE) {
1842 if (offset <= target)
1843 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1844 } else if (type == SRC_NEWLINE) {
1845 if (offset <= target)
1846 lineno++;
1847 }
1848 if (offset > target)
1849 break;
1850 }
1851 return lineno;
1852 }
1853
1854 /* The line number limit is the same as the jssrcnote offset limit. */
1855 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1856
1857 jsbytecode *
1858 js_LineNumberToPC(JSScript *script, uintN target)
1859 {
1860 ptrdiff_t offset, best;
1861 uintN lineno, bestdiff, diff;
1862 jssrcnote *sn;
1863 JSSrcNoteType type;
1864
1865 offset = 0;
1866 best = -1;
1867 lineno = script->lineno;
1868 bestdiff = SN_LINE_LIMIT;
1869 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1870 /*
1871 * Exact-match only if offset is not in the prolog; otherwise use
1872 * nearest greater-or-equal line number match.
1873 */
1874 if (lineno == target && script->code + offset >= script->main)
1875 goto out;
1876 if (lineno >= target) {
1877 diff = lineno - target;
1878 if (diff < bestdiff) {
1879 bestdiff = diff;
1880 best = offset;
1881 }
1882 }
1883 offset += SN_DELTA(sn);
1884 type = (JSSrcNoteType) SN_TYPE(sn);
1885 if (type == SRC_SETLINE) {
1886 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1887 } else if (type == SRC_NEWLINE) {
1888 lineno++;
1889 }
1890 }
1891 if (best >= 0)
1892 offset = best;
1893 out:
1894 return script->code + offset;
1895 }
1896
1897 JS_FRIEND_API(uintN)
1898 js_GetScriptLineExtent(JSScript *script)
1899 {
1900 uintN lineno;
1901 jssrcnote *sn;
1902 JSSrcNoteType type;
1903
1904 lineno = script->lineno;
1905 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1906 type = (JSSrcNoteType) SN_TYPE(sn);
1907 if (type == SRC_SETLINE) {
1908 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1909 } else if (type == SRC_NEWLINE) {
1910 lineno++;
1911 }
1912 }
1913 return 1 + lineno - script->lineno;
1914 }

  ViewVC Help
Powered by ViewVC 1.1.24