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

Contents of /trunk/js/jsscript.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 460 - (show annotations)
Sat Sep 26 23:15:22 2009 UTC (12 years, 7 months ago) by siliconforks
File size: 58412 byte(s)
Upgrade to SpiderMonkey from Firefox 3.5.3.

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 "jsstddef.h"
45 #include <string.h>
46 #include "jstypes.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 #if JS_HAS_SCRIPT_OBJECT
69
70 static const char js_script_exec_str[] = "Script.prototype.exec";
71 static const char js_script_compile_str[] = "Script.prototype.compile";
72
73 /*
74 * This routine requires that obj has been locked previously.
75 */
76 static jsint
77 GetScriptExecDepth(JSContext *cx, JSObject *obj)
78 {
79 jsval v;
80
81 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
82 v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass));
83 return JSVAL_TO_INT(v);
84 }
85
86 static void
87 AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta)
88 {
89 jsint execDepth;
90
91 JS_LOCK_OBJ(cx, obj);
92 execDepth = GetScriptExecDepth(cx, obj);
93 LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass),
94 INT_TO_JSVAL(execDepth + delta));
95 JS_UNLOCK_OBJ(cx, obj);
96 }
97
98 #if JS_HAS_TOSOURCE
99 static JSBool
100 script_toSource(JSContext *cx, uintN argc, jsval *vp)
101 {
102 JSObject *obj;
103 uint32 indent;
104 JSScript *script;
105 size_t i, j, k, n;
106 char buf[16];
107 jschar *s, *t;
108 JSString *str;
109
110 obj = JS_THIS_OBJECT(cx, vp);
111 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
112 return JS_FALSE;
113
114 indent = 0;
115 if (argc != 0) {
116 indent = js_ValueToECMAUint32(cx, &vp[2]);
117 if (JSVAL_IS_NULL(vp[2]))
118 return JS_FALSE;
119 }
120
121 script = (JSScript *) JS_GetPrivate(cx, obj);
122
123 /* Let n count the source string length, j the "front porch" length. */
124 j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
125 n = j + 2;
126 if (!script) {
127 /* Let k count the constructor argument string length. */
128 k = 0;
129 s = NULL; /* quell GCC overwarning */
130 } else {
131 str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
132 (uintN)indent);
133 if (!str)
134 return JS_FALSE;
135 str = js_QuoteString(cx, str, '\'');
136 if (!str)
137 return JS_FALSE;
138 JSSTRING_CHARS_AND_LENGTH(str, s, k);
139 n += k;
140 }
141
142 /* Allocate the source string and copy into it. */
143 t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
144 if (!t)
145 return JS_FALSE;
146 for (i = 0; i < j; i++)
147 t[i] = buf[i];
148 for (j = 0; j < k; i++, j++)
149 t[i] = s[j];
150 t[i++] = ')';
151 t[i++] = ')';
152 t[i] = 0;
153
154 /* Create and return a JS string for t. */
155 str = JS_NewUCString(cx, t, n);
156 if (!str) {
157 JS_free(cx, t);
158 return JS_FALSE;
159 }
160 *vp = STRING_TO_JSVAL(str);
161 return JS_TRUE;
162 }
163 #endif /* JS_HAS_TOSOURCE */
164
165 static JSBool
166 script_toString(JSContext *cx, uintN argc, jsval *vp)
167 {
168 uint32 indent;
169 JSObject *obj;
170 JSScript *script;
171 JSString *str;
172
173 indent = 0;
174 if (argc != 0) {
175 indent = js_ValueToECMAUint32(cx, &vp[2]);
176 if (JSVAL_IS_NULL(vp[2]))
177 return JS_FALSE;
178 }
179
180 obj = JS_THIS_OBJECT(cx, vp);
181 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, vp + 2))
182 return JS_FALSE;
183 script = (JSScript *) JS_GetPrivate(cx, obj);
184 if (!script) {
185 *vp = STRING_TO_JSVAL(cx->runtime->emptyString);
186 return JS_TRUE;
187 }
188
189 str = JS_DecompileScript(cx, script, "Script.prototype.toString",
190 (uintN)indent);
191 if (!str)
192 return JS_FALSE;
193 *vp = STRING_TO_JSVAL(str);
194 return JS_TRUE;
195 }
196
197 static JSBool
198 script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
199 jsval *rval)
200 {
201 JSString *str;
202 JSObject *scopeobj;
203 jsval v;
204 JSScript *script, *oldscript;
205 JSStackFrame *caller;
206 const char *file;
207 uintN line;
208 JSPrincipals *principals;
209 uint32 tcflags;
210 jsint execDepth;
211
212 /* Make sure obj is a Script object. */
213 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
214 return JS_FALSE;
215
216 /* If no args, leave private undefined and return early. */
217 if (argc == 0)
218 goto out;
219
220 /* Otherwise, the first arg is the script source to compile. */
221 str = js_ValueToString(cx, argv[0]);
222 if (!str)
223 return JS_FALSE;
224 argv[0] = STRING_TO_JSVAL(str);
225
226 scopeobj = NULL;
227 if (argc >= 2) {
228 if (!js_ValueToObject(cx, argv[1], &scopeobj))
229 return JS_FALSE;
230 argv[1] = OBJECT_TO_JSVAL(scopeobj);
231 }
232
233 /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
234 caller = js_GetScriptedCaller(cx, NULL);
235 JS_ASSERT(!caller || cx->fp->scopeChain == caller->scopeChain);
236
237 if (caller) {
238 if (!scopeobj) {
239 scopeobj = js_GetScopeChain(cx, caller);
240 if (!scopeobj)
241 return JS_FALSE;
242 }
243
244 principals = JS_EvalFramePrincipals(cx, cx->fp, caller);
245 file = js_ComputeFilename(cx, caller, principals, &line);
246 } else {
247 file = NULL;
248 line = 0;
249 principals = NULL;
250 }
251
252 /* Ensure we compile this script with the right (inner) principals. */
253 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile_str);
254 if (!scopeobj)
255 return JS_FALSE;
256
257 /*
258 * Compile the new script using the caller's scope chain, a la eval().
259 * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in
260 * tcflags and use NULL for the callerFrame argument, because compilation
261 * is here separated from execution, and the run-time scope chain may not
262 * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
263 * jsparse.c to optimize based on identity of run- and compile-time scope.
264 */
265 tcflags = 0;
266 script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags,
267 JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
268 NULL, file, line);
269 if (!script)
270 return JS_FALSE;
271
272 JS_LOCK_OBJ(cx, obj);
273 execDepth = GetScriptExecDepth(cx, obj);
274
275 /*
276 * execDepth must be 0 to allow compilation here, otherwise the JSScript
277 * struct can be released while running.
278 */
279 if (execDepth > 0) {
280 JS_UNLOCK_OBJ(cx, obj);
281 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
282 JSMSG_COMPILE_EXECED_SCRIPT);
283 return JS_FALSE;
284 }
285
286 /* Swap script for obj's old script, if any. */
287 v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
288 oldscript = (JSScript*) (!JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL);
289 LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(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 *) JS_GetPrivate(cx, obj);
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 = PTRDIFF(script->main, script->code, jsbytecode);
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(script);
465 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
466 continue;
467 nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
468 nsrcnotes++; /* room for the terminator */
469
470 if (script->objectsOffset != 0)
471 nobjects = JS_SCRIPT_OBJECTS(script)->length;
472 if (script->upvarsOffset != 0)
473 nupvars = JS_SCRIPT_UPVARS(script)->length;
474 if (script->regexpsOffset != 0)
475 nregexps = JS_SCRIPT_REGEXPS(script)->length;
476 if (script->trynotesOffset != 0)
477 ntrynotes = JS_SCRIPT_TRYNOTES(script)->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(script);
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 JS_free(cx, 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 JS_free(cx, (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 = &JS_SCRIPT_OBJECTS(script)->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, &JS_SCRIPT_UPVARS(script)->vector[i]))
621 goto error;
622 }
623 for (i = 0; i != nregexps; ++i) {
624 if (!js_XDRRegExpObject(xdr, &JS_SCRIPT_REGEXPS(script)->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 = JS_SCRIPT_TRYNOTES(script)->vector;
639 JS_ASSERT(JS_SCRIPT_TRYNOTES(script)->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 JS_free(cx, (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 *) JS_GetPrivate(cx, obj);
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 JSSTRING_CHARS_AND_LENGTH(str, 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 *) JS_malloc(cx, 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 v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
824 oldscript = !JSVAL_IS_VOID(v) ? JSVAL_TO_PRIVATE(v) : NULL;
825 LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
826 JS_UNLOCK_OBJ(cx, obj);
827
828 if (oldscript)
829 js_DestroyScript(cx, oldscript);
830
831 script->u.object = obj;
832 js_CallNewScriptHook(cx, script, NULL);
833
834 out:
835 /*
836 * We reset the buffer to be NULL so that it doesn't free the chars
837 * memory owned by str (vp[2]).
838 */
839 JS_XDRMemSetData(xdr, NULL, 0);
840 JS_XDRDestroy(xdr);
841 #if IS_BIG_ENDIAN
842 JS_free(cx, buf);
843 #endif
844 *vp = JSVAL_TRUE;
845 return ok;
846 }
847
848 static const char js_thaw_str[] = "thaw";
849
850 #endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */
851 #endif /* JS_HAS_XDR */
852
853 #if JS_HAS_SCRIPT_OBJECT
854
855 static JSFunctionSpec script_methods[] = {
856 #if JS_HAS_TOSOURCE
857 JS_FN(js_toSource_str, script_toSource, 0,0),
858 #endif
859 JS_FN(js_toString_str, script_toString, 0,0),
860 JS_FN("compile", script_compile, 2,0),
861 JS_FN("exec", script_exec, 1,0),
862 #if JS_HAS_XDR_FREEZE_THAW
863 JS_FN("freeze", script_freeze, 0,0),
864 JS_FN(js_thaw_str, script_thaw, 1,0),
865 #endif /* JS_HAS_XDR_FREEZE_THAW */
866 JS_FS_END
867 };
868
869 #endif /* JS_HAS_SCRIPT_OBJECT */
870
871 static void
872 script_finalize(JSContext *cx, JSObject *obj)
873 {
874 JSScript *script;
875
876 script = (JSScript *) JS_GetPrivate(cx, obj);
877 if (script)
878 js_DestroyScript(cx, script);
879 }
880
881 static JSBool
882 script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
883 {
884 #if JS_HAS_SCRIPT_OBJECT
885 return script_exec_sub(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
886 #else
887 return JS_FALSE;
888 #endif
889 }
890
891 static void
892 script_trace(JSTracer *trc, JSObject *obj)
893 {
894 JSScript *script;
895
896 script = (JSScript *) JS_GetPrivate(trc->context, obj);
897 if (script)
898 js_TraceScript(trc, script);
899 }
900
901 #if !JS_HAS_SCRIPT_OBJECT
902 #define JSProto_Script JSProto_Object
903 #endif
904
905 JS_FRIEND_DATA(JSClass) js_ScriptClass = {
906 js_Script_str,
907 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
908 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script),
909 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
910 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize,
911 NULL, NULL, script_call, NULL,/*XXXbe xdr*/
912 NULL, NULL, JS_CLASS_TRACE(script_trace), NULL
913 };
914
915 #if JS_HAS_SCRIPT_OBJECT
916
917 static JSBool
918 Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
919 {
920 /* If not constructing, replace obj with a new Script object. */
921 if (!JS_IsConstructing(cx)) {
922 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL, 0);
923 if (!obj)
924 return JS_FALSE;
925
926 /*
927 * script_compile_sub does not use rval to root its temporaries so we
928 * can use it to root obj.
929 */
930 *rval = OBJECT_TO_JSVAL(obj);
931 }
932
933 if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0)))
934 return JS_FALSE;
935
936 return script_compile_sub(cx, obj, argc, argv, rval);
937 }
938
939 #if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
940
941 static JSBool
942 script_static_thaw(JSContext *cx, uintN argc, jsval *vp)
943 {
944 JSObject *obj;
945
946 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
947 if (!obj)
948 return JS_FALSE;
949 vp[1] = OBJECT_TO_JSVAL(obj);
950 if (!script_thaw(cx, argc, vp))
951 return JS_FALSE;
952 *vp = OBJECT_TO_JSVAL(obj);
953 return JS_TRUE;
954 }
955
956 static JSFunctionSpec script_static_methods[] = {
957 JS_FN(js_thaw_str, script_static_thaw, 1,0),
958 JS_FS_END
959 };
960
961 #else /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
962
963 #define script_static_methods NULL
964
965 #endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
966
967 JSObject *
968 js_InitScriptClass(JSContext *cx, JSObject *obj)
969 {
970 return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
971 NULL, script_methods, NULL, script_static_methods);
972 }
973
974 #endif /* JS_HAS_SCRIPT_OBJECT */
975
976 /*
977 * Shared script filename management.
978 */
979 static int
980 js_compare_strings(const void *k1, const void *k2)
981 {
982 return strcmp((const char *) k1, (const char *) k2) == 0;
983 }
984
985 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
986 typedef struct ScriptFilenameEntry {
987 JSHashEntry *next; /* hash chain linkage */
988 JSHashNumber keyHash; /* key hash function result */
989 const void *key; /* ptr to filename, below */
990 uint32 flags; /* user-defined filename prefix flags */
991 JSPackedBool mark; /* GC mark flag */
992 char filename[3]; /* two or more bytes, NUL-terminated */
993 } ScriptFilenameEntry;
994
995 static void *
996 js_alloc_table_space(void *priv, size_t size)
997 {
998 return malloc(size);
999 }
1000
1001 static void
1002 js_free_table_space(void *priv, void *item, size_t size)
1003 {
1004 free(item);
1005 }
1006
1007 static JSHashEntry *
1008 js_alloc_sftbl_entry(void *priv, const void *key)
1009 {
1010 size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
1011 strlen((const char *) key) + 1;
1012
1013 return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
1014 }
1015
1016 static void
1017 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
1018 {
1019 if (flag != HT_FREE_ENTRY)
1020 return;
1021 free(he);
1022 }
1023
1024 static JSHashAllocOps sftbl_alloc_ops = {
1025 js_alloc_table_space, js_free_table_space,
1026 js_alloc_sftbl_entry, js_free_sftbl_entry
1027 };
1028
1029 JSBool
1030 js_InitRuntimeScriptState(JSRuntime *rt)
1031 {
1032 #ifdef JS_THREADSAFE
1033 JS_ASSERT(!rt->scriptFilenameTableLock);
1034 rt->scriptFilenameTableLock = JS_NEW_LOCK();
1035 if (!rt->scriptFilenameTableLock)
1036 return JS_FALSE;
1037 #endif
1038 JS_ASSERT(!rt->scriptFilenameTable);
1039 rt->scriptFilenameTable =
1040 JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
1041 &sftbl_alloc_ops, NULL);
1042 if (!rt->scriptFilenameTable) {
1043 js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */
1044 return JS_FALSE;
1045 }
1046 JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
1047 return JS_TRUE;
1048 }
1049
1050 typedef struct ScriptFilenamePrefix {
1051 JSCList links; /* circular list linkage for easy deletion */
1052 const char *name; /* pointer to pinned ScriptFilenameEntry string */
1053 size_t length; /* prefix string length, precomputed */
1054 uint32 flags; /* user-defined flags to inherit from this prefix */
1055 } ScriptFilenamePrefix;
1056
1057 void
1058 js_FinishRuntimeScriptState(JSRuntime *rt)
1059 {
1060 if (rt->scriptFilenameTable) {
1061 JS_HashTableDestroy(rt->scriptFilenameTable);
1062 rt->scriptFilenameTable = NULL;
1063 }
1064 #ifdef JS_THREADSAFE
1065 if (rt->scriptFilenameTableLock) {
1066 JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
1067 rt->scriptFilenameTableLock = NULL;
1068 }
1069 #endif
1070 }
1071
1072 void
1073 js_FreeRuntimeScriptState(JSRuntime *rt)
1074 {
1075 ScriptFilenamePrefix *sfp;
1076
1077 if (!rt->scriptFilenameTable)
1078 return;
1079
1080 while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
1081 sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next;
1082 JS_REMOVE_LINK(&sfp->links);
1083 free(sfp);
1084 }
1085 js_FinishRuntimeScriptState(rt);
1086 }
1087
1088 #ifdef DEBUG_brendan
1089 #define DEBUG_SFTBL
1090 #endif
1091 #ifdef DEBUG_SFTBL
1092 size_t sftbl_savings = 0;
1093 #endif
1094
1095 static ScriptFilenameEntry *
1096 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
1097 {
1098 JSHashTable *table;
1099 JSHashNumber hash;
1100 JSHashEntry **hep;
1101 ScriptFilenameEntry *sfe;
1102 size_t length;
1103 JSCList *head, *link;
1104 ScriptFilenamePrefix *sfp;
1105
1106 table = rt->scriptFilenameTable;
1107 hash = JS_HashString(filename);
1108 hep = JS_HashTableRawLookup(table, hash, filename);
1109 sfe = (ScriptFilenameEntry *) *hep;
1110 #ifdef DEBUG_SFTBL
1111 if (sfe)
1112 sftbl_savings += strlen(sfe->filename);
1113 #endif
1114
1115 if (!sfe) {
1116 sfe = (ScriptFilenameEntry *)
1117 JS_HashTableRawAdd(table, hep, hash, filename, NULL);
1118 if (!sfe)
1119 return NULL;
1120 sfe->key = strcpy(sfe->filename, filename);
1121 sfe->flags = 0;
1122 sfe->mark = JS_FALSE;
1123 }
1124
1125 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1126 if (flags != 0) {
1127 /* Search in case filename was saved already; we must be idempotent. */
1128 sfp = NULL;
1129 length = strlen(filename);
1130 for (head = link = &rt->scriptFilenamePrefixes;
1131 link->next != head;
1132 link = link->next) {
1133 /* Lag link behind sfp to insert in non-increasing length order. */
1134 sfp = (ScriptFilenamePrefix *) link->next;
1135 if (!strcmp(sfp->name, filename))
1136 break;
1137 if (sfp->length <= length) {
1138 sfp = NULL;
1139 break;
1140 }
1141 sfp = NULL;
1142 }
1143
1144 if (!sfp) {
1145 /* No such prefix: add one now. */
1146 sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix));
1147 if (!sfp)
1148 return NULL;
1149 JS_INSERT_AFTER(&sfp->links, link);
1150 sfp->name = sfe->filename;
1151 sfp->length = length;
1152 sfp->flags = 0;
1153 }
1154
1155 /*
1156 * Accumulate flags in both sfe and sfp: sfe for later access from the
1157 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1158 * filename entries can inherit by prefix.
1159 */
1160 sfe->flags |= flags;
1161 sfp->flags |= flags;
1162 }
1163
1164 #ifdef JS_FUNCTION_METERING
1165 size_t len = strlen(sfe->filename);
1166 if (len >= sizeof rt->lastScriptFilename)
1167 len = sizeof rt->lastScriptFilename - 1;
1168 memcpy(rt->lastScriptFilename, sfe->filename, len);
1169 rt->lastScriptFilename[len] = '\0';
1170 #endif
1171
1172 return sfe;
1173 }
1174
1175 const char *
1176 js_SaveScriptFilename(JSContext *cx, const char *filename)
1177 {
1178 JSRuntime *rt;
1179 ScriptFilenameEntry *sfe;
1180 JSCList *head, *link;
1181 ScriptFilenamePrefix *sfp;
1182
1183 rt = cx->runtime;
1184 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1185 sfe = SaveScriptFilename(rt, filename, 0);
1186 if (!sfe) {
1187 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1188 JS_ReportOutOfMemory(cx);
1189 return NULL;
1190 }
1191
1192 /*
1193 * Try to inherit flags by prefix. We assume there won't be more than a
1194 * few (dozen! ;-) prefixes, so linear search is tolerable.
1195 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1196 */
1197 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1198 link != head;
1199 link = link->next) {
1200 sfp = (ScriptFilenamePrefix *) link;
1201 if (!strncmp(sfp->name, filename, sfp->length)) {
1202 sfe->flags |= sfp->flags;
1203 break;
1204 }
1205 }
1206 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1207 return sfe->filename;
1208 }
1209
1210 const char *
1211 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
1212 {
1213 ScriptFilenameEntry *sfe;
1214
1215 /* This may be called very early, via the jsdbgapi.h entry point. */
1216 if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
1217 return NULL;
1218
1219 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1220 sfe = SaveScriptFilename(rt, filename, flags);
1221 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1222 if (!sfe)
1223 return NULL;
1224
1225 return sfe->filename;
1226 }
1227
1228 /*
1229 * Back up from a saved filename by its offset within its hash table entry.
1230 */
1231 #define FILENAME_TO_SFE(fn) \
1232 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1233
1234 /*
1235 * The sfe->key member, redundant given sfe->filename but required by the old
1236 * jshash.c code, here gives us a useful sanity check. This assertion will
1237 * very likely botch if someone tries to mark a string that wasn't allocated
1238 * as an sfe->filename.
1239 */
1240 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
1241
1242 uint32
1243 js_GetScriptFilenameFlags(const char *filename)
1244 {
1245 ScriptFilenameEntry *sfe;
1246
1247 sfe = FILENAME_TO_SFE(filename);
1248 ASSERT_VALID_SFE(sfe);
1249 return sfe->flags;
1250 }
1251
1252 void
1253 js_MarkScriptFilename(const char *filename)
1254 {
1255 ScriptFilenameEntry *sfe;
1256
1257 sfe = FILENAME_TO_SFE(filename);
1258 ASSERT_VALID_SFE(sfe);
1259 sfe->mark = JS_TRUE;
1260 }
1261
1262 static intN
1263 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
1264 {
1265 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1266
1267 sfe->mark = JS_TRUE;
1268 return HT_ENUMERATE_NEXT;
1269 }
1270
1271 void
1272 js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
1273 {
1274 JSCList *head, *link;
1275 ScriptFilenamePrefix *sfp;
1276
1277 if (!rt->scriptFilenameTable)
1278 return;
1279
1280 if (keepAtoms) {
1281 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1282 js_script_filename_marker,
1283 rt);
1284 }
1285 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1286 link != head;
1287 link = link->next) {
1288 sfp = (ScriptFilenamePrefix *) link;
1289 js_MarkScriptFilename(sfp->name);
1290 }
1291 }
1292
1293 static intN
1294 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
1295 {
1296 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1297
1298 if (!sfe->mark)
1299 return HT_ENUMERATE_REMOVE;
1300 sfe->mark = JS_FALSE;
1301 return HT_ENUMERATE_NEXT;
1302 }
1303
1304 void
1305 js_SweepScriptFilenames(JSRuntime *rt)
1306 {
1307 if (!rt->scriptFilenameTable)
1308 return;
1309
1310 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1311 js_script_filename_sweeper,
1312 rt);
1313 #ifdef DEBUG_notme
1314 #ifdef DEBUG_SFTBL
1315 printf("script filename table savings so far: %u\n", sftbl_savings);
1316 #endif
1317 #endif
1318 }
1319
1320 /*
1321 * JSScript data structures memory alignment:
1322 *
1323 * JSScript
1324 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
1325 * use JS_SCRIPT_OBJECTS(script) macro to access it.
1326 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
1327 * use JS_SCRIPT_REGEXPS(script) macro to access it.
1328 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
1329 * != 0, use JS_SCRIPT_TRYNOTES(script) macro to access it.
1330 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
1331 * JSScript.atomMap.vector if any.
1332 * JSObject *o[] array of JS_SCRIPT_OBJECTS(script)->length objects if any
1333 * pointed by JS_SCRIPT_OBJECTS(script)->vector.
1334 * JSObject *r[] array of JS_SCRIPT_REGEXPS(script)->length regexps if any
1335 * pointed by JS_SCRIPT_REGEXPS(script)->vector.
1336 * JSTryNote t[] array of JS_SCRIPT_TRYNOTES(script)->length try notes if any
1337 * pointed by JS_SCRIPT_TRYNOTES(script)->vector.
1338 * jsbytecode b[] script bytecode pointed by JSScript.code.
1339 * jssrcnote s[] script source notes, use SCRIPT_NOTES(script) to access it
1340 *
1341 * The alignment avoids gaps between entries as alignment requirement for each
1342 * subsequent structure or array is the same or divides the alignment
1343 * requirement for the previous one.
1344 *
1345 * The followings asserts checks that assuming that the alignment requirement
1346 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
1347 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
1348 */
1349 JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0);
1350 JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0);
1351 JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray));
1352 JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *));
1353 JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0);
1354 JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32));
1355 JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0);
1356 JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
1357
1358 /*
1359 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
1360 * sufficient.
1361 */
1362 JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
1363 sizeof(JSUpvarArray) < JS_BIT(8));
1364
1365 JSScript *
1366 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
1367 uint32 nobjects, uint32 nupvars, uint32 nregexps,
1368 uint32 ntrynotes)
1369 {
1370 size_t size, vectorSize;
1371 JSScript *script;
1372 uint8 *cursor;
1373
1374 size = sizeof(JSScript) +
1375 sizeof(JSAtom *) * natoms +
1376 length * sizeof(jsbytecode) +
1377 nsrcnotes * sizeof(jssrcnote);
1378 if (nobjects != 0)
1379 size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
1380 if (nupvars != 0)
1381 size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
1382 if (nregexps != 0)
1383 size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
1384 if (ntrynotes != 0)
1385 size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
1386
1387 script = (JSScript *) JS_malloc(cx, size);
1388 if (!script)
1389 return NULL;
1390 memset(script, 0, sizeof(JSScript));
1391 script->length = length;
1392 script->version = cx->version;
1393
1394 cursor = (uint8 *)script + sizeof(JSScript);
1395 if (nobjects != 0) {
1396 script->objectsOffset = (uint8)(cursor - (uint8 *)script);
1397 cursor += sizeof(JSObjectArray);
1398 }
1399 if (nupvars != 0) {
1400 script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
1401 cursor += sizeof(JSUpvarArray);
1402 }
1403 if (nregexps != 0) {
1404 script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
1405 cursor += sizeof(JSObjectArray);
1406 }
1407 if (ntrynotes != 0) {
1408 script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
1409 cursor += sizeof(JSTryNoteArray);
1410 }
1411
1412 if (natoms != 0) {
1413 script->atomMap.length = natoms;
1414 script->atomMap.vector = (JSAtom **)cursor;
1415 vectorSize = natoms * sizeof(script->atomMap.vector[0]);
1416
1417 /*
1418 * Clear object map's vector so the GC tracing can run when not yet
1419 * all atoms are copied to the array.
1420 */
1421 memset(cursor, 0, vectorSize);
1422 cursor += vectorSize;
1423 }
1424
1425 if (nobjects != 0) {
1426 JS_SCRIPT_OBJECTS(script)->length = nobjects;
1427 JS_SCRIPT_OBJECTS(script)->vector = (JSObject **)cursor;
1428 vectorSize = nobjects * sizeof(JS_SCRIPT_OBJECTS(script)->vector[0]);
1429 memset(cursor, 0, vectorSize);
1430 cursor += vectorSize;
1431 }
1432
1433 if (nupvars != 0) {
1434 JS_SCRIPT_UPVARS(script)->length = nupvars;
1435 JS_SCRIPT_UPVARS(script)->vector = (uint32 *)cursor;
1436 vectorSize = nupvars * sizeof(JS_SCRIPT_UPVARS(script)->vector[0]);
1437 memset(cursor, 0, vectorSize);
1438 cursor += vectorSize;
1439 }
1440
1441 if (nregexps != 0) {
1442 JS_SCRIPT_REGEXPS(script)->length = nregexps;
1443 JS_SCRIPT_REGEXPS(script)->vector = (JSObject **)cursor;
1444 vectorSize = nregexps * sizeof(JS_SCRIPT_REGEXPS(script)->vector[0]);
1445 memset(cursor, 0, vectorSize);
1446 cursor += vectorSize;
1447 }
1448
1449 if (ntrynotes != 0) {
1450 JS_SCRIPT_TRYNOTES(script)->length = ntrynotes;
1451 JS_SCRIPT_TRYNOTES(script)->vector = (JSTryNote *)cursor;
1452 vectorSize = ntrynotes * sizeof(JS_SCRIPT_TRYNOTES(script)->vector[0]);
1453 #ifdef DEBUG
1454 memset(cursor, 0, vectorSize);
1455 #endif
1456 cursor += vectorSize;
1457 }
1458
1459 script->code = script->main = (jsbytecode *)cursor;
1460 JS_ASSERT(cursor +
1461 length * sizeof(jsbytecode) +
1462 nsrcnotes * sizeof(jssrcnote) ==
1463 (uint8 *)script + size);
1464
1465 #ifdef CHECK_SCRIPT_OWNER
1466 script->owner = cx->thread;
1467 #endif
1468 return script;
1469 }
1470
1471 JSScript *
1472 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
1473 {
1474 uint32 mainLength, prologLength, nsrcnotes, nfixed;
1475 JSScript *script;
1476 const char *filename;
1477 JSFunction *fun;
1478
1479 /* The counts of indexed things must be checked during code generation. */
1480 JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
1481 JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
1482 JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
1483
1484 mainLength = CG_OFFSET(cg);
1485 prologLength = CG_PROLOG_OFFSET(cg);
1486 CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1487 script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
1488 cg->atomList.count, cg->objectList.length,
1489 cg->upvarList.count, cg->regexpList.length,
1490 cg->ntrynotes);
1491 if (!script)
1492 return NULL;
1493
1494 /* Now that we have script, error control flow must go to label bad. */
1495 script->main += prologLength;
1496 memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1497 memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1498 nfixed = (cg->flags & TCF_IN_FUNCTION)
1499 ? cg->fun->u.i.nvars
1500 : cg->ngvars + cg->regexpList.length;
1501 JS_ASSERT(nfixed < SLOTNO_LIMIT);
1502 script->nfixed = (uint16) nfixed;
1503 js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
1504
1505 filename = cg->compiler->tokenStream.filename;
1506 if (filename) {
1507 script->filename = js_SaveScriptFilename(cx, filename);
1508 if (!script->filename)
1509 goto bad;
1510 }
1511 script->lineno = cg->firstLine;
1512 if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
1513 js_ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR,
1514 JSMSG_NEED_DIET, "script");
1515 goto bad;
1516 }
1517 script->nslots = script->nfixed + cg->maxStackDepth;
1518 script->staticLevel = cg->staticLevel;
1519 script->principals = cg->compiler->principals;
1520 if (script->principals)
1521 JSPRINCIPALS_HOLD(cx, script->principals);
1522
1523 if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script)))
1524 goto bad;
1525 if (cg->ntrynotes != 0)
1526 js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script));
1527 if (cg->objectList.length != 0)
1528 cg->objectList.finish(JS_SCRIPT_OBJECTS(script));
1529 if (cg->regexpList.length != 0)
1530 cg->regexpList.finish(JS_SCRIPT_REGEXPS(script));
1531 if (cg->flags & TCF_NO_SCRIPT_RVAL)
1532 script->flags |= JSSF_NO_SCRIPT_RVAL;
1533
1534 if (cg->upvarList.count != 0) {
1535 JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
1536 memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector,
1537 cg->upvarList.count * sizeof(uint32));
1538 cg->upvarList.clear();
1539 JS_free(cx, cg->upvarMap.vector);
1540 cg->upvarMap.vector = NULL;
1541 }
1542
1543 /*
1544 * We initialize fun->u.script to be the script constructed above
1545 * so that the debugger has a valid FUN_SCRIPT(fun).
1546 */
1547 fun = NULL;
1548 if (cg->flags & TCF_IN_FUNCTION) {
1549 fun = cg->fun;
1550 JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
1551 JS_ASSERT_IF(script->upvarsOffset != 0,
1552 JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars);
1553
1554 js_FreezeLocalNames(cx, fun);
1555 fun->u.i.script = script;
1556 #ifdef CHECK_SCRIPT_OWNER
1557 script->owner = NULL;
1558 #endif
1559 if (cg->flags & TCF_FUN_HEAVYWEIGHT)
1560 fun->flags |= JSFUN_HEAVYWEIGHT;
1561 }
1562
1563 /* Tell the debugger about this compiled script. */
1564 js_CallNewScriptHook(cx, script, fun);
1565 return script;
1566
1567 bad:
1568 js_DestroyScript(cx, script);
1569 return NULL;
1570 }
1571
1572 JS_FRIEND_API(void)
1573 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1574 {
1575 JSNewScriptHook hook;
1576
1577 hook = cx->debugHooks->newScriptHook;
1578 if (hook) {
1579 JS_KEEP_ATOMS(cx->runtime);
1580 hook(cx, script->filename, script->lineno, script, fun,
1581 cx->debugHooks->newScriptHookData);
1582 JS_UNKEEP_ATOMS(cx->runtime);
1583 }
1584 }
1585
1586 JS_FRIEND_API(void)
1587 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1588 {
1589 JSDestroyScriptHook hook;
1590
1591 hook = cx->debugHooks->destroyScriptHook;
1592 if (hook)
1593 hook(cx, script, cx->debugHooks->destroyScriptHookData);
1594 }
1595
1596 void
1597 js_DestroyScript(JSContext *cx, JSScript *script)
1598 {
1599 js_CallDestroyScriptHook(cx, script);
1600 JS_ClearScriptTraps(cx, script);
1601
1602 if (script->principals)
1603 JSPRINCIPALS_DROP(cx, script->principals);
1604
1605 if (JS_GSN_CACHE(cx).code == script->code)
1606 JS_PURGE_GSN_CACHE(cx);
1607
1608 /*
1609 * The GC flushes all property caches, so no need to purge just the
1610 * entries for this script.
1611 *
1612 * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
1613 * current thread's property cache, so a script not owned by a function
1614 * or object, which hands off lifetime management for that script to the
1615 * GC, must be used by only one thread over its lifetime.
1616 *
1617 * This should be an API-compatible change, since a script is never safe
1618 * against premature GC if shared among threads without a rooted object
1619 * wrapping it to protect the script's mapped atoms against GC. We use
1620 * script->owner to enforce this requirement via assertions.
1621 */
1622 #ifdef CHECK_SCRIPT_OWNER
1623 JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
1624 #endif
1625
1626 if (!cx->runtime->gcRunning) {
1627 JSStackFrame *fp = js_GetTopStackFrame(cx);
1628
1629 if (!(fp && (fp->flags & JSFRAME_EVAL))) {
1630 #ifdef CHECK_SCRIPT_OWNER
1631 JS_ASSERT(script->owner == cx->thread);
1632 #endif
1633 js_PurgePropertyCacheForScript(cx, script);
1634 #ifdef JS_TRACER
1635 js_PurgeScriptFragments(cx, script);
1636 #endif
1637 }
1638 }
1639
1640 JS_free(cx, script);
1641 }
1642
1643 void
1644 js_TraceScript(JSTracer *trc, JSScript *script)
1645 {
1646 JSAtomMap *map;
1647 uintN i, length;
1648 JSAtom **vector;
1649 jsval v;
1650 JSObjectArray *objarray;
1651
1652 map = &script->atomMap;
1653 length = map->length;
1654 vector = map->vector;
1655 for (i = 0; i < length; i++) {
1656 v = ATOM_KEY(vector[i]);
1657 if (JSVAL_IS_TRACEABLE(v)) {
1658 JS_SET_TRACING_INDEX(trc, "atomMap", i);
1659 JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
1660 }
1661 }
1662
1663 if (script->objectsOffset != 0) {
1664 objarray = JS_SCRIPT_OBJECTS(script);
1665 i = objarray->length;
1666 do {
1667 --i;
1668 if (objarray->vector[i]) {
1669 JS_SET_TRACING_INDEX(trc, "objects", i);
1670 JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1671 }
1672 } while (i != 0);
1673 }
1674
1675 if (script->regexpsOffset != 0) {
1676 objarray = JS_SCRIPT_REGEXPS(script);
1677 i = objarray->length;
1678 do {
1679 --i;
1680 if (objarray->vector[i]) {
1681 JS_SET_TRACING_INDEX(trc, "regexps", i);
1682 JS_CallTracer(trc, objarray->vector[i], JSTRACE_OBJECT);
1683 }
1684 } while (i != 0);
1685 }
1686
1687 if (script->u.object) {
1688 JS_SET_TRACING_NAME(trc, "object");
1689 JS_CallTracer(trc, script->u.object, JSTRACE_OBJECT);
1690 }
1691
1692 if (IS_GC_MARKING_TRACER(trc) && script->filename)
1693 js_MarkScriptFilename(script->filename);
1694 }
1695
1696 typedef struct GSNCacheEntry {
1697 JSDHashEntryHdr hdr;
1698 jsbytecode *pc;
1699 jssrcnote *sn;
1700 } GSNCacheEntry;
1701
1702 #define GSN_CACHE_THRESHOLD 100
1703
1704 void
1705 js_PurgeGSNCache(JSGSNCache *cache)
1706 {
1707 cache->code = NULL;
1708 if (cache->table.ops) {
1709 JS_DHashTableFinish(&cache->table);
1710 cache->table.ops = NULL;
1711 }
1712 GSN_CACHE_METER(cache, purges);
1713 }
1714
1715 jssrcnote *
1716 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1717 {
1718 ptrdiff_t target, offset;
1719 GSNCacheEntry *entry;
1720 jssrcnote *sn, *result;
1721 uintN nsrcnotes;
1722
1723
1724 target = PTRDIFF(pc, script->code, jsbytecode);
1725 if ((uint32)target >= script->length)
1726 return NULL;
1727
1728 if (JS_GSN_CACHE(cx).code == script->code) {
1729 JS_METER_GSN_CACHE(cx, hits);
1730 entry = (GSNCacheEntry *)
1731 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1732 JS_DHASH_LOOKUP);
1733 return entry->sn;
1734 }
1735
1736 JS_METER_GSN_CACHE(cx, misses);
1737 offset = 0;
1738 for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) {
1739 if (SN_IS_TERMINATOR(sn)) {
1740 result = NULL;
1741 break;
1742 }
1743 offset += SN_DELTA(sn);
1744 if (offset == target && SN_IS_GETTABLE(sn)) {
1745 result = sn;
1746 break;
1747 }
1748 }
1749
1750 if (JS_GSN_CACHE(cx).code != script->code &&
1751 script->length >= GSN_CACHE_THRESHOLD) {
1752 JS_PURGE_GSN_CACHE(cx);
1753 nsrcnotes = 0;
1754 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1755 sn = SN_NEXT(sn)) {
1756 if (SN_IS_GETTABLE(sn))
1757 ++nsrcnotes;
1758 }
1759 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
1760 NULL, sizeof(GSNCacheEntry),
1761 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) {
1762 JS_GSN_CACHE(cx).table.ops = NULL;
1763 } else {
1764 pc = script->code;
1765 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1766 sn = SN_NEXT(sn)) {
1767 pc += SN_DELTA(sn);
1768 if (SN_IS_GETTABLE(sn)) {
1769 entry = (GSNCacheEntry *)
1770 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1771 JS_DHASH_ADD);
1772 entry->pc = pc;
1773 entry->sn = sn;
1774 }
1775 }
1776 JS_GSN_CACHE(cx).code = script->code;
1777 JS_METER_GSN_CACHE(cx, fills);
1778 }
1779 }
1780
1781 return result;
1782 }
1783
1784 uintN
1785 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
1786 {
1787 return js_PCToLineNumber(cx, fp->script, fp->imacpc ? fp->imacpc : fp->regs->pc);
1788 }
1789
1790 uintN
1791 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1792 {
1793 JSOp op;
1794 JSFunction *fun;
1795 uintN lineno;
1796 ptrdiff_t offset, target;
1797 jssrcnote *sn;
1798 JSSrcNoteType type;
1799
1800 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1801 if (!pc)
1802 return 0;
1803
1804 /*
1805 * Special case: function definition needs no line number note because
1806 * the function's script contains its starting line number.
1807 */
1808 op = js_GetOpcode(cx, script, pc);
1809 if (js_CodeSpec[op].format & JOF_INDEXBASE)
1810 pc += js_CodeSpec[op].length;
1811 if (*pc == JSOP_DEFFUN) {
1812 GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun);
1813 return fun->u.i.script->lineno;
1814 }
1815
1816 /*
1817 * General case: walk through source notes accumulating their deltas,
1818 * keeping track of line-number notes, until we pass the note for pc's
1819 * offset within script->code.
1820 */
1821 lineno = script->lineno;
1822 offset = 0;
1823 target = PTRDIFF(pc, script->code, jsbytecode);
1824 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1825 offset += SN_DELTA(sn);
1826 type = (JSSrcNoteType) SN_TYPE(sn);
1827 if (type == SRC_SETLINE) {
1828 if (offset <= target)
1829 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1830 } else if (type == SRC_NEWLINE) {
1831 if (offset <= target)
1832 lineno++;
1833 }
1834 if (offset > target)
1835 break;
1836 }
1837 return lineno;
1838 }
1839
1840 /* The line number limit is the same as the jssrcnote offset limit. */
1841 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1842
1843 jsbytecode *
1844 js_LineNumberToPC(JSScript *script, uintN target)
1845 {
1846 ptrdiff_t offset, best;
1847 uintN lineno, bestdiff, diff;
1848 jssrcnote *sn;
1849 JSSrcNoteType type;
1850
1851 offset = 0;
1852 best = -1;
1853 lineno = script->lineno;
1854 bestdiff = SN_LINE_LIMIT;
1855 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1856 /*
1857 * Exact-match only if offset is not in the prolog; otherwise use
1858 * nearest greater-or-equal line number match.
1859 */
1860 if (lineno == target && script->code + offset >= script->main)
1861 goto out;
1862 if (lineno >= target) {
1863 diff = lineno - target;
1864 if (diff < bestdiff) {
1865 bestdiff = diff;
1866 best = offset;
1867 }
1868 }
1869 offset += SN_DELTA(sn);
1870 type = (JSSrcNoteType) SN_TYPE(sn);
1871 if (type == SRC_SETLINE) {
1872 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1873 } else if (type == SRC_NEWLINE) {
1874 lineno++;
1875 }
1876 }
1877 if (best >= 0)
1878 offset = best;
1879 out:
1880 return script->code + offset;
1881 }
1882
1883 JS_FRIEND_API(uintN)
1884 js_GetScriptLineExtent(JSScript *script)
1885 {
1886 uintN lineno;
1887 jssrcnote *sn;
1888 JSSrcNoteType type;
1889
1890 lineno = script->lineno;
1891 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1892 type = (JSSrcNoteType) SN_TYPE(sn);
1893 if (type == SRC_SETLINE) {
1894 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1895 } else if (type == SRC_NEWLINE) {
1896 lineno++;
1897 }
1898 }
1899 return 1 + lineno - script->lineno;
1900 }
1901
1902 #if JS_HAS_GENERATORS
1903
1904 JSBool
1905 js_IsInsideTryWithFinally(JSScript *script, jsbytecode *pc)
1906 {
1907 JSTryNoteArray *tarray;
1908 JSTryNote *tn, *tnlimit;
1909 uint32 off;
1910
1911 JS_ASSERT(script->code <= pc);
1912 JS_ASSERT(pc < script->code + script->length);
1913
1914 if (!script->trynotesOffset != 0)
1915 return JS_FALSE;
1916 tarray = JS_SCRIPT_TRYNOTES(script);
1917 JS_ASSERT(tarray->length != 0);
1918
1919 tn = tarray->vector;
1920 tnlimit = tn + tarray->length;
1921 off = (uint32)(pc - script->main);
1922 do {
1923 if (off - tn->start < tn->length) {
1924 if (tn->kind == JSTRY_FINALLY)
1925 return JS_TRUE;
1926 JS_ASSERT(tn->kind == JSTRY_CATCH);
1927 }
1928 } while (++tn != tnlimit);
1929 return JS_FALSE;
1930 }
1931
1932 #endif

  ViewVC Help
Powered by ViewVC 1.1.24