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

Contents of /trunk/js/jsscript.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (9 years, 1 month ago) by siliconforks
File size: 57640 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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

  ViewVC Help
Powered by ViewVC 1.1.24