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 "jsconfig.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 "jsscript.h"
61 #if JS_HAS_XDR
62 #include "jsxdrapi.h"
63 #endif
64 
65 #if JS_HAS_SCRIPT_OBJECT
66 
67 static const char js_script_exec[] = "Script.prototype.exec";
68 static const char js_script_compile[] = "Script.prototype.compile";
69 
70 /*
71  * This routine requires that obj has been locked previously.
72  */
73 static jsint
GetScriptExecDepth(JSContext * cx,JSObject * obj)74 GetScriptExecDepth(JSContext *cx, JSObject *obj)
75 {
76     jsval v;
77 
78     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
79     v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass));
80     return JSVAL_TO_INT(v);
81 }
82 
83 static void
AdjustScriptExecDepth(JSContext * cx,JSObject * obj,jsint delta)84 AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta)
85 {
86     jsint execDepth;
87 
88     JS_LOCK_OBJ(cx, obj);
89     execDepth = GetScriptExecDepth(cx, obj);
90     LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass),
91                         INT_TO_JSVAL(execDepth + delta));
92     JS_UNLOCK_OBJ(cx, obj);
93 }
94 
95 #if JS_HAS_TOSOURCE
96 static JSBool
script_toSource(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)97 script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
98                 jsval *rval)
99 {
100     uint32 indent;
101     JSScript *script;
102     size_t i, j, k, n;
103     char buf[16];
104     jschar *s, *t;
105     JSString *str;
106 
107     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
108         return JS_FALSE;
109 
110     indent = 0;
111     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
112         return JS_FALSE;
113 
114     script = (JSScript *) JS_GetPrivate(cx, obj);
115 
116     /* Let n count the source string length, j the "front porch" length. */
117     j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
118     n = j + 2;
119     if (!script) {
120         /* Let k count the constructor argument string length. */
121         k = 0;
122         s = NULL;               /* quell GCC overwarning */
123     } else {
124         str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
125                                  (uintN)indent);
126         if (!str)
127             return JS_FALSE;
128         str = js_QuoteString(cx, str, '\'');
129         if (!str)
130             return JS_FALSE;
131         s = JSSTRING_CHARS(str);
132         k = JSSTRING_LENGTH(str);
133         n += k;
134     }
135 
136     /* Allocate the source string and copy into it. */
137     t = (jschar *) JS_malloc(cx, (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         JS_free(cx, t);
152         return JS_FALSE;
153     }
154     *rval = STRING_TO_JSVAL(str);
155     return JS_TRUE;
156 }
157 #endif /* JS_HAS_TOSOURCE */
158 
159 static JSBool
script_toString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)160 script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
161                 jsval *rval)
162 {
163     uint32 indent;
164     JSScript *script;
165     JSString *str;
166 
167     indent = 0;
168     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
169         return JS_FALSE;
170 
171     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
172         return JS_FALSE;
173     script = (JSScript *) JS_GetPrivate(cx, obj);
174     if (!script) {
175         *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
176         return JS_TRUE;
177     }
178 
179     str = JS_DecompileScript(cx, script, "Script.prototype.toString",
180                              (uintN)indent);
181     if (!str)
182         return JS_FALSE;
183     *rval = STRING_TO_JSVAL(str);
184     return JS_TRUE;
185 }
186 
187 static JSBool
script_compile(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)188 script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
189                jsval *rval)
190 {
191     JSString *str;
192     JSObject *scopeobj;
193     jsval v;
194     JSScript *script, *oldscript;
195     JSStackFrame *fp, *caller;
196     const char *file;
197     uintN line;
198     JSPrincipals *principals;
199     jsint execDepth;
200 
201     /* Make sure obj is a Script object. */
202     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
203         return JS_FALSE;
204 
205     /* If no args, leave private undefined and return early. */
206     if (argc == 0)
207         goto out;
208 
209     /* Otherwise, the first arg is the script source to compile. */
210     str = js_ValueToString(cx, argv[0]);
211     if (!str)
212         return JS_FALSE;
213     argv[0] = STRING_TO_JSVAL(str);
214 
215     scopeobj = NULL;
216     if (argc >= 2) {
217         if (!js_ValueToObject(cx, argv[1], &scopeobj))
218             return JS_FALSE;
219         argv[1] = OBJECT_TO_JSVAL(scopeobj);
220     }
221 
222     /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
223     fp = cx->fp;
224     caller = JS_GetScriptedCaller(cx, fp);
225     JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain);
226 
227     if (caller) {
228         if (!scopeobj) {
229             scopeobj = js_GetScopeChain(cx, caller);
230             if (!scopeobj)
231                 return JS_FALSE;
232             fp->scopeChain = scopeobj;  /* for the compiler's benefit */
233         }
234 
235         principals = JS_EvalFramePrincipals(cx, fp, caller);
236         if (principals == caller->script->principals) {
237             file = caller->script->filename;
238             line = js_PCToLineNumber(cx, caller->script, caller->pc);
239         } else {
240             file = principals->codebase;
241             line = 0;
242         }
243     } else {
244         file = NULL;
245         line = 0;
246         principals = NULL;
247     }
248 
249     /* Ensure we compile this script with the right (inner) principals. */
250     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile);
251     if (!scopeobj)
252         return JS_FALSE;
253 
254     /*
255      * Compile the new script using the caller's scope chain, a la eval().
256      * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's
257      * flags, because compilation is here separated from execution, and the
258      * run-time scope chain may not match the compile-time.  JSFRAME_EVAL is
259      * tested in jsemit.c and jsscan.c to optimize based on identity of run-
260      * and compile-time scope.
261      */
262     fp->flags |= JSFRAME_SCRIPT_OBJECT;
263     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
264                                              JSSTRING_CHARS(str),
265                                              JSSTRING_LENGTH(str),
266                                              file, line);
267     if (!script)
268         return JS_FALSE;
269 
270     JS_LOCK_OBJ(cx, obj);
271     execDepth = GetScriptExecDepth(cx, obj);
272 
273     /*
274      * execDepth must be 0 to allow compilation here, otherwise the JSScript
275      * struct can be released while running.
276      */
277     if (execDepth > 0) {
278         JS_UNLOCK_OBJ(cx, obj);
279         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
280                              JSMSG_COMPILE_EXECED_SCRIPT);
281         return JS_FALSE;
282     }
283 
284     /* Swap script for obj's old script, if any. */
285     v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
286     oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL;
287     LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
288     JS_UNLOCK_OBJ(cx, obj);
289 
290     if (oldscript)
291         js_DestroyScript(cx, oldscript);
292 
293     script->object = obj;
294     js_CallNewScriptHook(cx, script, NULL);
295 
296 out:
297     /* Return the object. */
298     *rval = OBJECT_TO_JSVAL(obj);
299     return JS_TRUE;
300 }
301 
302 static JSBool
script_exec(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)303 script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
304 {
305     JSObject *scopeobj, *parent;
306     JSStackFrame *fp, *caller;
307     JSScript *script;
308     JSBool ok;
309 
310     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
311         return JS_FALSE;
312 
313     scopeobj = NULL;
314     if (argc) {
315         if (!js_ValueToObject(cx, argv[0], &scopeobj))
316             return JS_FALSE;
317         argv[0] = OBJECT_TO_JSVAL(scopeobj);
318     }
319 
320     /*
321      * Emulate eval() by using caller's this, var object, sharp array, etc.,
322      * all propagated by js_Execute via a non-null fourth (down) argument to
323      * js_Execute.  If there is no scripted caller, js_Execute uses its second
324      * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
325      *
326      * Unlike eval, which the compiler detects, Script.prototype.exec may be
327      * called from a lightweight function, or even from native code (in which
328      * case fp->varobj and fp->scopeChain are null).  If exec is called from
329      * a lightweight function, we will need to get a Call object representing
330      * its frame, to act as the var object and scope chain head.
331      */
332     fp = cx->fp;
333     caller = JS_GetScriptedCaller(cx, fp);
334     if (caller && !caller->varobj) {
335         /* Called from a lightweight function. */
336         JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags));
337 
338         /* Scope chain links from Call object to callee's parent. */
339         parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2]));
340         if (!js_GetCallObject(cx, caller, parent))
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 parent (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);
368     if (!scopeobj)
369         return JS_FALSE;
370 
371     /* Keep track of nesting depth for the script. */
372     AdjustScriptExecDepth(cx, obj, 1);
373 
374     /* Must get to out label after this */
375     script = (JSScript *) JS_GetPrivate(cx, obj);
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     ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals,
383                                   CLASS_ATOM(cx, Script));
384     if (!ok)
385         goto out;
386 
387     ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
388 
389 out:
390     AdjustScriptExecDepth(cx, obj, -1);
391     return ok;
392 }
393 
394 #if JS_HAS_XDR
395 
396 static JSBool
XDRAtomMap(JSXDRState * xdr,JSAtomMap * map)397 XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)
398 {
399     JSContext *cx;
400     uint32 natoms, i, index;
401     JSAtom **atoms;
402 
403     cx = xdr->cx;
404 
405     if (xdr->mode == JSXDR_ENCODE)
406         natoms = (uint32)map->length;
407 
408     if (!JS_XDRUint32(xdr, &natoms))
409         return JS_FALSE;
410 
411     if (xdr->mode == JSXDR_ENCODE) {
412         atoms = map->vector;
413     } else {
414         if (natoms == 0) {
415             atoms = NULL;
416         } else {
417             atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms);
418             if (!atoms)
419                 return JS_FALSE;
420 #ifdef DEBUG
421             memset(atoms, 0, (size_t)natoms * sizeof *atoms);
422 #endif
423         }
424 
425         map->vector = atoms;
426         map->length = natoms;
427     }
428 
429     for (i = 0; i != natoms; ++i) {
430         if (xdr->mode == JSXDR_ENCODE)
431             index = i;
432         if (!JS_XDRUint32(xdr, &index))
433             goto bad;
434 
435         /*
436          * Assert that, when decoding, the read index is valid and points to
437          * an unoccupied element of atoms array.
438          */
439         JS_ASSERT(index < natoms);
440         JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]);
441         if (!js_XDRAtom(xdr, &atoms[index]))
442             goto bad;
443     }
444 
445     return JS_TRUE;
446 
447   bad:
448     if (xdr->mode == JSXDR_DECODE) {
449         JS_free(cx, atoms);
450         map->vector = NULL;
451         map->length = 0;
452     }
453 
454     return JS_FALSE;
455 }
456 
457 JSBool
js_XDRScript(JSXDRState * xdr,JSScript ** scriptp,JSBool * hasMagic)458 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
459 {
460     JSContext *cx;
461     JSScript *script, *newscript, *oldscript;
462     uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes;
463     uint32 prologLength, version;
464     JSBool filenameWasSaved;
465     jssrcnote *notes, *sn;
466 
467     cx = xdr->cx;
468     script = *scriptp;
469     nsrcnotes = ntrynotes = 0;
470     filenameWasSaved = JS_FALSE;
471     notes = NULL;
472 
473     /*
474      * Encode prologLength and version after script->length (_2 or greater),
475      * but decode both new (>= _2) and old, prolog&version-free (_1) scripts.
476      * Version _3 supports principals serialization.  Version _4 reorders the
477      * nsrcnotes and ntrynotes fields to come before everything except magic,
478      * length, prologLength, and version, so that srcnote and trynote storage
479      * can be allocated as part of the JSScript (along with bytecode storage).
480      *
481      * So far, the magic number has not changed for every jsopcode.tbl change.
482      * We stipulate forward compatibility by requiring old bytecodes never to
483      * change or go away (modulo a few exceptions before the XDR interfaces
484      * evolved, and a few exceptions during active trunk development).  With
485      * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new
486      * magic number (_5) so that we know to append JSOP_STOP to old scripts
487      * when deserializing.
488      */
489     if (xdr->mode == JSXDR_ENCODE)
490         magic = JSXDR_MAGIC_SCRIPT_CURRENT;
491     if (!JS_XDRUint32(xdr, &magic))
492         return JS_FALSE;
493     JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4);
494     if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) {
495         if (!hasMagic) {
496             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
497                                  JSMSG_BAD_SCRIPT_MAGIC);
498             return JS_FALSE;
499         }
500         *hasMagic = JS_FALSE;
501         return JS_TRUE;
502     }
503     if (hasMagic)
504         *hasMagic = JS_TRUE;
505 
506     if (xdr->mode == JSXDR_ENCODE) {
507         length = script->length;
508         prologLength = PTRDIFF(script->main, script->code, jsbytecode);
509         JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
510         version = (uint32)script->version | (script->numGlobalVars << 16);
511         lineno = (uint32)script->lineno;
512         depth = (uint32)script->depth;
513 
514         /* Count the srcnotes, keeping notes pointing at the first one. */
515         notes = SCRIPT_NOTES(script);
516         for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
517             continue;
518         nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
519         nsrcnotes++;            /* room for the terminator */
520 
521         /* Count the trynotes. */
522         if (script->trynotes) {
523             while (script->trynotes[ntrynotes].catchStart)
524                 ntrynotes++;
525             ntrynotes++;        /* room for the end marker */
526         }
527     }
528 
529     if (!JS_XDRUint32(xdr, &length))
530         return JS_FALSE;
531     if (magic >= JSXDR_MAGIC_SCRIPT_2) {
532         if (!JS_XDRUint32(xdr, &prologLength))
533             return JS_FALSE;
534         if (!JS_XDRUint32(xdr, &version))
535             return JS_FALSE;
536 
537         /* To fuse allocations, we need srcnote and trynote counts early. */
538         if (magic >= JSXDR_MAGIC_SCRIPT_4) {
539             if (!JS_XDRUint32(xdr, &nsrcnotes))
540                 return JS_FALSE;
541             if (!JS_XDRUint32(xdr, &ntrynotes))
542                 return JS_FALSE;
543         }
544     }
545 
546     if (xdr->mode == JSXDR_DECODE) {
547         size_t alloclength = length;
548         if (magic < JSXDR_MAGIC_SCRIPT_5)
549             ++alloclength;      /* add a byte for JSOP_STOP */
550 
551         script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes);
552         if (!script)
553             return JS_FALSE;
554         if (magic >= JSXDR_MAGIC_SCRIPT_2) {
555             script->main += prologLength;
556             script->version = (JSVersion) (version & 0xffff);
557             script->numGlobalVars = (uint16) (version >> 16);
558 
559             /* If we know nsrcnotes, we allocated space for notes in script. */
560             if (magic >= JSXDR_MAGIC_SCRIPT_4)
561                 notes = SCRIPT_NOTES(script);
562         }
563         *scriptp = script;
564     }
565 
566     /*
567      * Control hereafter must goto error on failure, in order for the DECODE
568      * case to destroy script and conditionally free notes, which if non-null
569      * in the (DECODE and magic < _4) case must point at a temporary vector
570      * allocated just below.
571      */
572     oldscript = xdr->script;
573     xdr->script = script;
574     if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) ||
575         !XDRAtomMap(xdr, &script->atomMap)) {
576         goto error;
577     }
578 
579     if (magic < JSXDR_MAGIC_SCRIPT_5) {
580         if (xdr->mode == JSXDR_DECODE) {
581             /*
582              * Append JSOP_STOP to old scripts, to relieve the interpreter
583              * from having to bounds-check pc.  Also take care to increment
584              * length, as it is used below and must count all bytecode.
585              */
586             script->code[length++] = JSOP_STOP;
587         }
588 
589         if (magic < JSXDR_MAGIC_SCRIPT_4) {
590             if (!JS_XDRUint32(xdr, &nsrcnotes))
591                 goto error;
592             if (xdr->mode == JSXDR_DECODE) {
593                 notes = (jssrcnote *)
594                         JS_malloc(cx, nsrcnotes * sizeof(jssrcnote));
595                 if (!notes)
596                     goto error;
597             }
598         }
599     }
600 
601     if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
602         !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
603         !JS_XDRUint32(xdr, &lineno) ||
604         !JS_XDRUint32(xdr, &depth) ||
605         (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) {
606         goto error;
607     }
608 
609     /* Script principals transcoding support comes with versions >= _3. */
610     if (magic >= JSXDR_MAGIC_SCRIPT_3) {
611         JSPrincipals *principals;
612         uint32 encodeable;
613 
614         if (xdr->mode == JSXDR_ENCODE) {
615             principals = script->principals;
616             encodeable = (cx->runtime->principalsTranscoder != NULL);
617             if (!JS_XDRUint32(xdr, &encodeable))
618                 goto error;
619             if (encodeable &&
620                 !cx->runtime->principalsTranscoder(xdr, &principals)) {
621                 goto error;
622             }
623         } else {
624             if (!JS_XDRUint32(xdr, &encodeable))
625                 goto error;
626             if (encodeable) {
627                 if (!cx->runtime->principalsTranscoder) {
628                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
629                                          JSMSG_CANT_DECODE_PRINCIPALS);
630                     goto error;
631                 }
632                 if (!cx->runtime->principalsTranscoder(xdr, &principals))
633                     goto error;
634                 script->principals = principals;
635             }
636         }
637     }
638 
639     if (xdr->mode == JSXDR_DECODE) {
640         const char *filename = script->filename;
641         if (filename) {
642             filename = js_SaveScriptFilename(cx, filename);
643             if (!filename)
644                 goto error;
645             JS_free(cx, (void *) script->filename);
646             script->filename = filename;
647             filenameWasSaved = JS_TRUE;
648         }
649         script->lineno = (uintN)lineno;
650         script->depth = (uintN)depth;
651 
652         if (magic < JSXDR_MAGIC_SCRIPT_4) {
653             /*
654              * Argh, we have to reallocate script, copy notes into the extra
655              * space after the bytecodes, and free the temporary notes vector.
656              * First, add enough slop to nsrcnotes so we can align the address
657              * after the srcnotes of the first trynote.
658              */
659             uint32 osrcnotes = nsrcnotes;
660 
661             if (ntrynotes)
662                 nsrcnotes += JSTRYNOTE_ALIGNMASK;
663             newscript = (JSScript *) JS_realloc(cx, script,
664                                                 sizeof(JSScript) +
665                                                 length * sizeof(jsbytecode) +
666                                                 nsrcnotes * sizeof(jssrcnote) +
667                                                 ntrynotes * sizeof(JSTryNote));
668             if (!newscript)
669                 goto error;
670 
671             *scriptp = script = newscript;
672             script->code = (jsbytecode *)(script + 1);
673             script->main = script->code + prologLength;
674             memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote));
675             JS_free(cx, (void *) notes);
676             notes = NULL;
677             if (ntrynotes) {
678                 script->trynotes = (JSTryNote *)
679                                    ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
680                                     ~(jsword)JSTRYNOTE_ALIGNMASK);
681                 memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
682             }
683         }
684     }
685 
686     while (ntrynotes) {
687         JSTryNote *tn = &script->trynotes[--ntrynotes];
688         uint32 start = (uint32) tn->start,
689                catchLength = (uint32) tn->length,
690                catchStart = (uint32) tn->catchStart;
691 
692         if (!JS_XDRUint32(xdr, &start) ||
693             !JS_XDRUint32(xdr, &catchLength) ||
694             !JS_XDRUint32(xdr, &catchStart)) {
695             goto error;
696         }
697         tn->start = (ptrdiff_t) start;
698         tn->length = (ptrdiff_t) catchLength;
699         tn->catchStart = (ptrdiff_t) catchStart;
700     }
701 
702     xdr->script = oldscript;
703     return JS_TRUE;
704 
705   error:
706     if (xdr->mode == JSXDR_DECODE) {
707         if (script->filename && !filenameWasSaved) {
708             JS_free(cx, (void *) script->filename);
709             script->filename = NULL;
710         }
711         if (notes && magic < JSXDR_MAGIC_SCRIPT_4)
712             JS_free(cx, (void *) notes);
713         js_DestroyScript(cx, script);
714         *scriptp = NULL;
715     }
716     return JS_FALSE;
717 }
718 
719 #if JS_HAS_XDR_FREEZE_THAW
720 /*
721  * These cannot be exposed to web content, and chrome does not need them, so
722  * we take them out of the Mozilla client altogether.  Fortunately, there is
723  * no way to serialize a native function (see fun_xdrObject in jsfun.c).
724  */
725 
726 static JSBool
script_freeze(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)727 script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
728               jsval *rval)
729 {
730     JSXDRState *xdr;
731     JSScript *script;
732     JSBool ok, hasMagic;
733     uint32 len;
734     void *buf;
735     JSString *str;
736 
737     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
738         return JS_FALSE;
739     script = (JSScript *) JS_GetPrivate(cx, obj);
740     if (!script)
741         return JS_TRUE;
742 
743     /* create new XDR */
744     xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
745     if (!xdr)
746         return JS_FALSE;
747 
748     /* write  */
749     ok = js_XDRScript(xdr, &script, &hasMagic);
750     if (!ok)
751         goto out;
752     if (!hasMagic) {
753         *rval = JSVAL_VOID;
754         goto out;
755     }
756 
757     buf = JS_XDRMemGetData(xdr, &len);
758     if (!buf) {
759         ok = JS_FALSE;
760         goto out;
761     }
762 
763     JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
764     len /= sizeof(jschar);
765     str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
766     if (!str) {
767         ok = JS_FALSE;
768         goto out;
769     }
770 
771 #if IS_BIG_ENDIAN
772   {
773     jschar *chars;
774     uint32 i;
775 
776     /* Swap bytes in Unichars to keep frozen strings machine-independent. */
777     chars = JS_GetStringChars(str);
778     for (i = 0; i < len; i++)
779         chars[i] = JSXDR_SWAB16(chars[i]);
780   }
781 #endif
782     *rval = STRING_TO_JSVAL(str);
783 
784 out:
785     JS_XDRDestroy(xdr);
786     return ok;
787 }
788 
789 static JSBool
script_thaw(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)790 script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
791             jsval *rval)
792 {
793     JSXDRState *xdr;
794     JSString *str;
795     void *buf;
796     uint32 len;
797     jsval v;
798     JSScript *script, *oldscript;
799     JSBool ok, hasMagic;
800 
801     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
802         return JS_FALSE;
803 
804     if (argc == 0)
805         return JS_TRUE;
806     str = js_ValueToString(cx, argv[0]);
807     if (!str)
808         return JS_FALSE;
809     argv[0] = STRING_TO_JSVAL(str);
810 
811     /* create new XDR */
812     xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
813     if (!xdr)
814         return JS_FALSE;
815 
816     buf = JS_GetStringChars(str);
817     len = JS_GetStringLength(str);
818 #if IS_BIG_ENDIAN
819   {
820     jschar *from, *to;
821     uint32 i;
822 
823     /* Swap bytes in Unichars to keep frozen strings machine-independent. */
824     from = (jschar *)buf;
825     to = (jschar *) JS_malloc(cx, len * sizeof(jschar));
826     if (!to) {
827         JS_XDRDestroy(xdr);
828         return JS_FALSE;
829     }
830     for (i = 0; i < len; i++)
831         to[i] = JSXDR_SWAB16(from[i]);
832     buf = (char *)to;
833   }
834 #endif
835     len *= sizeof(jschar);
836     JS_XDRMemSetData(xdr, buf, len);
837 
838     /* XXXbe should magic mismatch be error, or false return value? */
839     ok = js_XDRScript(xdr, &script, &hasMagic);
840     if (!ok)
841         goto out;
842     if (!hasMagic) {
843         *rval = JSVAL_FALSE;
844         goto out;
845     }
846 
847     JS_LOCK_OBJ(cx, obj);
848     execDepth = GetScriptExecDepth(cx, obj);
849 
850     /*
851      * execDepth must be 0 to allow compilation here, otherwise the JSScript
852      * struct can be released while running.
853      */
854     if (execDepth > 0) {
855         JS_UNLOCK_OBJ(cx, obj);
856         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
857                              JSMSG_COMPILE_EXECED_SCRIPT);
858         goto out;
859     }
860 
861     /* Swap script for obj's old script, if any. */
862     v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
863     oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL;
864     LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
865     JS_UNLOCK_OBJ(cx, obj);
866 
867     if (oldscript)
868         js_DestroyScript(cx, oldscript);
869 
870     script->object = obj;
871     js_CallNewScriptHook(cx, script, NULL);
872 
873 out:
874     /*
875      * We reset the buffer to be NULL so that it doesn't free the chars
876      * memory owned by str (argv[0]).
877      */
878     JS_XDRMemSetData(xdr, NULL, 0);
879     JS_XDRDestroy(xdr);
880 #if IS_BIG_ENDIAN
881     JS_free(cx, buf);
882 #endif
883     *rval = JSVAL_TRUE;
884     return ok;
885 }
886 
887 static const char js_thaw_str[] = "thaw";
888 
889 #endif /* JS_HAS_XDR_FREEZE_THAW */
890 #endif /* JS_HAS_XDR */
891 
892 static JSFunctionSpec script_methods[] = {
893 #if JS_HAS_TOSOURCE
894     {js_toSource_str,   script_toSource,        0,0,0},
895 #endif
896     {js_toString_str,   script_toString,        0,0,0},
897     {"compile",         script_compile,         2,0,0},
898     {"exec",            script_exec,            1,0,0},
899 #if JS_HAS_XDR_FREEZE_THAW
900     {"freeze",          script_freeze,          0,0,0},
901     {js_thaw_str,       script_thaw,            1,0,0},
902 #endif /* JS_HAS_XDR_FREEZE_THAW */
903     {0,0,0,0,0}
904 };
905 
906 #endif /* JS_HAS_SCRIPT_OBJECT */
907 
908 static void
script_finalize(JSContext * cx,JSObject * obj)909 script_finalize(JSContext *cx, JSObject *obj)
910 {
911     JSScript *script;
912 
913     script = (JSScript *) JS_GetPrivate(cx, obj);
914     if (script)
915         js_DestroyScript(cx, script);
916 }
917 
918 static JSBool
script_call(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)919 script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
920 {
921 #if JS_HAS_SCRIPT_OBJECT
922     return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
923 #else
924     return JS_FALSE;
925 #endif
926 }
927 
928 static uint32
script_mark(JSContext * cx,JSObject * obj,void * arg)929 script_mark(JSContext *cx, JSObject *obj, void *arg)
930 {
931     JSScript *script;
932 
933     script = (JSScript *) JS_GetPrivate(cx, obj);
934     if (script)
935         js_MarkScript(cx, script);
936     return 0;
937 }
938 
939 #if !JS_HAS_SCRIPT_OBJECT
940 const char js_Script_str[] = "Script";
941 
942 #define JSProto_Script  JSProto_Object
943 #endif
944 
945 JS_FRIEND_DATA(JSClass) js_ScriptClass = {
946     js_Script_str,
947     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) |
948     JSCLASS_HAS_RESERVED_SLOTS(1),
949     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
950     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   script_finalize,
951     NULL,             NULL,             script_call,      NULL,/*XXXbe xdr*/
952     NULL,             NULL,             script_mark,      0
953 };
954 
955 #if JS_HAS_SCRIPT_OBJECT
956 
957 static JSBool
Script(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)958 Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
959 {
960     /* If not constructing, replace obj with a new Script object. */
961     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
962         obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
963         if (!obj)
964             return JS_FALSE;
965 
966         /*
967          * script_compile does not use rval to root its temporaries
968          * so we can use it to root obj.
969          */
970         *rval = OBJECT_TO_JSVAL(obj);
971     }
972 
973     if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0)))
974         return JS_FALSE;
975 
976     return script_compile(cx, obj, argc, argv, rval);
977 }
978 
979 #if JS_HAS_XDR_FREEZE_THAW
980 
981 static JSBool
script_static_thaw(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)982 script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
983                    jsval *rval)
984 {
985     obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
986     if (!obj)
987         return JS_FALSE;
988     if (!script_thaw(cx, obj, argc, argv, rval))
989         return JS_FALSE;
990     *rval = OBJECT_TO_JSVAL(obj);
991     return JS_TRUE;
992 }
993 
994 static JSFunctionSpec script_static_methods[] = {
995     {js_thaw_str,       script_static_thaw,     1,0,0},
996     {0,0,0,0,0}
997 };
998 
999 #else  /* !JS_HAS_XDR_FREEZE_THAW */
1000 
1001 #define script_static_methods   NULL
1002 
1003 #endif /* !JS_HAS_XDR_FREEZE_THAW */
1004 
1005 JSObject *
js_InitScriptClass(JSContext * cx,JSObject * obj)1006 js_InitScriptClass(JSContext *cx, JSObject *obj)
1007 {
1008     return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
1009                         NULL, script_methods, NULL, script_static_methods);
1010 }
1011 
1012 #endif /* JS_HAS_SCRIPT_OBJECT */
1013 
1014 /*
1015  * Shared script filename management.
1016  */
1017 JS_STATIC_DLL_CALLBACK(int)
js_compare_strings(const void * k1,const void * k2)1018 js_compare_strings(const void *k1, const void *k2)
1019 {
1020     return strcmp(k1, k2) == 0;
1021 }
1022 
1023 /* Shared with jsatom.c to save code space. */
1024 extern void * JS_DLL_CALLBACK
1025 js_alloc_table_space(void *priv, size_t size);
1026 
1027 extern void JS_DLL_CALLBACK
1028 js_free_table_space(void *priv, void *item);
1029 
1030 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
1031 typedef struct ScriptFilenameEntry {
1032     JSHashEntry         *next;          /* hash chain linkage */
1033     JSHashNumber        keyHash;        /* key hash function result */
1034     const void          *key;           /* ptr to filename, below */
1035     uint32              flags;          /* user-defined filename prefix flags */
1036     JSPackedBool        mark;           /* GC mark flag */
1037     char                filename[3];    /* two or more bytes, NUL-terminated */
1038 } ScriptFilenameEntry;
1039 
1040 JS_STATIC_DLL_CALLBACK(JSHashEntry *)
js_alloc_sftbl_entry(void * priv,const void * key)1041 js_alloc_sftbl_entry(void *priv, const void *key)
1042 {
1043     size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1;
1044 
1045     return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
1046 }
1047 
1048 JS_STATIC_DLL_CALLBACK(void)
js_free_sftbl_entry(void * priv,JSHashEntry * he,uintN flag)1049 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
1050 {
1051     if (flag != HT_FREE_ENTRY)
1052         return;
1053     free(he);
1054 }
1055 
1056 static JSHashAllocOps sftbl_alloc_ops = {
1057     js_alloc_table_space,   js_free_table_space,
1058     js_alloc_sftbl_entry,   js_free_sftbl_entry
1059 };
1060 
1061 JSBool
js_InitRuntimeScriptState(JSRuntime * rt)1062 js_InitRuntimeScriptState(JSRuntime *rt)
1063 {
1064 #ifdef JS_THREADSAFE
1065     JS_ASSERT(!rt->scriptFilenameTableLock);
1066     rt->scriptFilenameTableLock = JS_NEW_LOCK();
1067     if (!rt->scriptFilenameTableLock)
1068         return JS_FALSE;
1069 #endif
1070     JS_ASSERT(!rt->scriptFilenameTable);
1071     rt->scriptFilenameTable =
1072         JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
1073                         &sftbl_alloc_ops, NULL);
1074     if (!rt->scriptFilenameTable) {
1075         js_FinishRuntimeScriptState(rt);    /* free lock if threadsafe */
1076         return JS_FALSE;
1077     }
1078     JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
1079     return JS_TRUE;
1080 }
1081 
1082 typedef struct ScriptFilenamePrefix {
1083     JSCList     links;      /* circular list linkage for easy deletion */
1084     const char  *name;      /* pointer to pinned ScriptFilenameEntry string */
1085     size_t      length;     /* prefix string length, precomputed */
1086     uint32      flags;      /* user-defined flags to inherit from this prefix */
1087 } ScriptFilenamePrefix;
1088 
1089 void
js_FinishRuntimeScriptState(JSRuntime * rt)1090 js_FinishRuntimeScriptState(JSRuntime *rt)
1091 {
1092     if (rt->scriptFilenameTable) {
1093         JS_HashTableDestroy(rt->scriptFilenameTable);
1094         rt->scriptFilenameTable = NULL;
1095     }
1096 #ifdef JS_THREADSAFE
1097     if (rt->scriptFilenameTableLock) {
1098         JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
1099         rt->scriptFilenameTableLock = NULL;
1100     }
1101 #endif
1102 }
1103 
1104 void
js_FreeRuntimeScriptState(JSRuntime * rt)1105 js_FreeRuntimeScriptState(JSRuntime *rt)
1106 {
1107     ScriptFilenamePrefix *sfp;
1108 
1109     if (!rt->scriptFilenameTable)
1110         return;
1111 
1112     while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
1113         sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next;
1114         JS_REMOVE_LINK(&sfp->links);
1115         free(sfp);
1116     }
1117     js_FinishRuntimeScriptState(rt);
1118 }
1119 
1120 #ifdef DEBUG_brendan
1121 #define DEBUG_SFTBL
1122 #endif
1123 #ifdef DEBUG_SFTBL
1124 size_t sftbl_savings = 0;
1125 #endif
1126 
1127 static ScriptFilenameEntry *
SaveScriptFilename(JSRuntime * rt,const char * filename,uint32 flags)1128 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
1129 {
1130     JSHashTable *table;
1131     JSHashNumber hash;
1132     JSHashEntry **hep;
1133     ScriptFilenameEntry *sfe;
1134     size_t length;
1135     JSCList *head, *link;
1136     ScriptFilenamePrefix *sfp;
1137 
1138     table = rt->scriptFilenameTable;
1139     hash = JS_HashString(filename);
1140     hep = JS_HashTableRawLookup(table, hash, filename);
1141     sfe = (ScriptFilenameEntry *) *hep;
1142 #ifdef DEBUG_SFTBL
1143     if (sfe)
1144         sftbl_savings += strlen(sfe->filename);
1145 #endif
1146 
1147     if (!sfe) {
1148         sfe = (ScriptFilenameEntry *)
1149               JS_HashTableRawAdd(table, hep, hash, filename, NULL);
1150         if (!sfe)
1151             return NULL;
1152         sfe->key = strcpy(sfe->filename, filename);
1153         sfe->flags = 0;
1154         sfe->mark = JS_FALSE;
1155     }
1156 
1157     /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1158     if (flags != 0) {
1159         /* Search in case filename was saved already; we must be idempotent. */
1160         sfp = NULL;
1161         length = strlen(filename);
1162         for (head = link = &rt->scriptFilenamePrefixes;
1163              link->next != head;
1164              link = link->next) {
1165             /* Lag link behind sfp to insert in non-increasing length order. */
1166             sfp = (ScriptFilenamePrefix *) link->next;
1167             if (!strcmp(sfp->name, filename))
1168                 break;
1169             if (sfp->length <= length) {
1170                 sfp = NULL;
1171                 break;
1172             }
1173             sfp = NULL;
1174         }
1175 
1176         if (!sfp) {
1177             /* No such prefix: add one now. */
1178             sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix));
1179             if (!sfp)
1180                 return NULL;
1181             JS_INSERT_AFTER(&sfp->links, link);
1182             sfp->name = sfe->filename;
1183             sfp->length = length;
1184             sfp->flags = 0;
1185         }
1186 
1187         /*
1188          * Accumulate flags in both sfe and sfp: sfe for later access from the
1189          * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1190          * filename entries can inherit by prefix.
1191          */
1192         sfe->flags |= flags;
1193         sfp->flags |= flags;
1194     }
1195 
1196     return sfe;
1197 }
1198 
1199 const char *
js_SaveScriptFilename(JSContext * cx,const char * filename)1200 js_SaveScriptFilename(JSContext *cx, const char *filename)
1201 {
1202     JSRuntime *rt;
1203     ScriptFilenameEntry *sfe;
1204     JSCList *head, *link;
1205     ScriptFilenamePrefix *sfp;
1206 
1207     rt = cx->runtime;
1208     JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1209     sfe = SaveScriptFilename(rt, filename, 0);
1210     if (!sfe) {
1211         JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1212         JS_ReportOutOfMemory(cx);
1213         return NULL;
1214     }
1215 
1216     /*
1217      * Try to inherit flags by prefix.  We assume there won't be more than a
1218      * few (dozen! ;-) prefixes, so linear search is tolerable.
1219      * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1220      */
1221     for (head = &rt->scriptFilenamePrefixes, link = head->next;
1222          link != head;
1223          link = link->next) {
1224         sfp = (ScriptFilenamePrefix *) link;
1225         if (!strncmp(sfp->name, filename, sfp->length)) {
1226             sfe->flags |= sfp->flags;
1227             break;
1228         }
1229     }
1230     JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1231     return sfe->filename;
1232 }
1233 
1234 const char *
js_SaveScriptFilenameRT(JSRuntime * rt,const char * filename,uint32 flags)1235 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
1236 {
1237     ScriptFilenameEntry *sfe;
1238 
1239     /* This may be called very early, via the jsdbgapi.h entry point. */
1240     if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
1241         return NULL;
1242 
1243     JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1244     sfe = SaveScriptFilename(rt, filename, flags);
1245     JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1246     if (!sfe)
1247         return NULL;
1248 
1249     return sfe->filename;
1250 }
1251 
1252 /*
1253  * Back up from a saved filename by its offset within its hash table entry.
1254  */
1255 #define FILENAME_TO_SFE(fn) \
1256     ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1257 
1258 /*
1259  * The sfe->key member, redundant given sfe->filename but required by the old
1260  * jshash.c code, here gives us a useful sanity check.  This assertion will
1261  * very likely botch if someone tries to mark a string that wasn't allocated
1262  * as an sfe->filename.
1263  */
1264 #define ASSERT_VALID_SFE(sfe)   JS_ASSERT((sfe)->key == (sfe)->filename)
1265 
1266 uint32
js_GetScriptFilenameFlags(const char * filename)1267 js_GetScriptFilenameFlags(const char *filename)
1268 {
1269     ScriptFilenameEntry *sfe;
1270 
1271     sfe = FILENAME_TO_SFE(filename);
1272     ASSERT_VALID_SFE(sfe);
1273     return sfe->flags;
1274 }
1275 
1276 void
js_MarkScriptFilename(const char * filename)1277 js_MarkScriptFilename(const char *filename)
1278 {
1279     ScriptFilenameEntry *sfe;
1280 
1281     sfe = FILENAME_TO_SFE(filename);
1282     ASSERT_VALID_SFE(sfe);
1283     sfe->mark = JS_TRUE;
1284 }
1285 
1286 JS_STATIC_DLL_CALLBACK(intN)
js_script_filename_marker(JSHashEntry * he,intN i,void * arg)1287 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
1288 {
1289     ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1290 
1291     sfe->mark = JS_TRUE;
1292     return HT_ENUMERATE_NEXT;
1293 }
1294 
1295 void
js_MarkScriptFilenames(JSRuntime * rt,JSBool keepAtoms)1296 js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
1297 {
1298     JSCList *head, *link;
1299     ScriptFilenamePrefix *sfp;
1300 
1301     if (!rt->scriptFilenameTable)
1302         return;
1303 
1304     if (keepAtoms) {
1305         JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1306                                      js_script_filename_marker,
1307                                      rt);
1308     }
1309     for (head = &rt->scriptFilenamePrefixes, link = head->next;
1310          link != head;
1311          link = link->next) {
1312         sfp = (ScriptFilenamePrefix *) link;
1313         js_MarkScriptFilename(sfp->name);
1314     }
1315 }
1316 
1317 JS_STATIC_DLL_CALLBACK(intN)
js_script_filename_sweeper(JSHashEntry * he,intN i,void * arg)1318 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
1319 {
1320     ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1321 
1322     if (!sfe->mark)
1323         return HT_ENUMERATE_REMOVE;
1324     sfe->mark = JS_FALSE;
1325     return HT_ENUMERATE_NEXT;
1326 }
1327 
1328 void
js_SweepScriptFilenames(JSRuntime * rt)1329 js_SweepScriptFilenames(JSRuntime *rt)
1330 {
1331     if (!rt->scriptFilenameTable)
1332         return;
1333 
1334     JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1335                                  js_script_filename_sweeper,
1336                                  rt);
1337 #ifdef DEBUG_notme
1338 #ifdef DEBUG_SFTBL
1339     printf("script filename table savings so far: %u\n", sftbl_savings);
1340 #endif
1341 #endif
1342 }
1343 
1344 JSScript *
js_NewScript(JSContext * cx,uint32 length,uint32 nsrcnotes,uint32 ntrynotes)1345 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes)
1346 {
1347     JSScript *script;
1348 
1349     /* Round up source note count to align script->trynotes for its type. */
1350     if (ntrynotes)
1351         nsrcnotes += JSTRYNOTE_ALIGNMASK;
1352     script = (JSScript *) JS_malloc(cx,
1353                                     sizeof(JSScript) +
1354                                     length * sizeof(jsbytecode) +
1355                                     nsrcnotes * sizeof(jssrcnote) +
1356                                     ntrynotes * sizeof(JSTryNote));
1357     if (!script)
1358         return NULL;
1359     memset(script, 0, sizeof(JSScript));
1360     script->code = script->main = (jsbytecode *)(script + 1);
1361     script->length = length;
1362     script->version = cx->version;
1363     if (ntrynotes) {
1364         script->trynotes = (JSTryNote *)
1365                            ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
1366                             ~(jsword)JSTRYNOTE_ALIGNMASK);
1367         memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
1368     }
1369     return script;
1370 }
1371 
1372 JS_FRIEND_API(JSScript *)
js_NewScriptFromCG(JSContext * cx,JSCodeGenerator * cg,JSFunction * fun)1373 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
1374 {
1375     uint32 mainLength, prologLength, nsrcnotes, ntrynotes;
1376     JSScript *script;
1377     const char *filename;
1378 
1379     mainLength = CG_OFFSET(cg);
1380     prologLength = CG_PROLOG_OFFSET(cg);
1381     CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1382     CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes);
1383     script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes);
1384     if (!script)
1385         return NULL;
1386 
1387     /* Now that we have script, error control flow must go to label bad. */
1388     script->main += prologLength;
1389     memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1390     memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1391     script->numGlobalVars = cg->treeContext.numGlobalVars;
1392     if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList))
1393         goto bad;
1394 
1395     filename = cg->filename;
1396     if (filename) {
1397         script->filename = js_SaveScriptFilename(cx, filename);
1398         if (!script->filename)
1399             goto bad;
1400     }
1401     script->lineno = cg->firstLine;
1402     script->depth = cg->maxStackDepth;
1403     if (cg->principals) {
1404         script->principals = cg->principals;
1405         JSPRINCIPALS_HOLD(cx, script->principals);
1406     }
1407 
1408     if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script)))
1409         goto bad;
1410     if (script->trynotes)
1411         js_FinishTakingTryNotes(cx, cg, script->trynotes);
1412 
1413     /*
1414      * We initialize fun->u.script to be the script constructed above
1415      * so that the debugger has a valid FUN_SCRIPT(fun).
1416      */
1417     if (fun) {
1418         JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
1419         fun->u.i.script = script;
1420         if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
1421             fun->flags |= JSFUN_HEAVYWEIGHT;
1422     }
1423 
1424     /* Tell the debugger about this compiled script. */
1425     js_CallNewScriptHook(cx, script, fun);
1426     return script;
1427 
1428 bad:
1429     js_DestroyScript(cx, script);
1430     return NULL;
1431 }
1432 
1433 JS_FRIEND_API(void)
js_CallNewScriptHook(JSContext * cx,JSScript * script,JSFunction * fun)1434 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1435 {
1436     JSRuntime *rt;
1437     JSNewScriptHook hook;
1438 
1439     rt = cx->runtime;
1440     hook = rt->newScriptHook;
1441     if (hook) {
1442         JS_KEEP_ATOMS(rt);
1443         hook(cx, script->filename, script->lineno, script, fun,
1444              rt->newScriptHookData);
1445         JS_UNKEEP_ATOMS(rt);
1446     }
1447 }
1448 
1449 JS_FRIEND_API(void)
js_CallDestroyScriptHook(JSContext * cx,JSScript * script)1450 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1451 {
1452     JSRuntime *rt;
1453     JSDestroyScriptHook hook;
1454 
1455     rt = cx->runtime;
1456     hook = rt->destroyScriptHook;
1457     if (hook)
1458         hook(cx, script, rt->destroyScriptHookData);
1459 }
1460 
1461 void
js_DestroyScript(JSContext * cx,JSScript * script)1462 js_DestroyScript(JSContext *cx, JSScript *script)
1463 {
1464     js_CallDestroyScriptHook(cx, script);
1465 
1466     JS_ClearScriptTraps(cx, script);
1467     js_FreeAtomMap(cx, &script->atomMap);
1468     if (script->principals)
1469         JSPRINCIPALS_DROP(cx, script->principals);
1470     if (JS_GSN_CACHE(cx).script == script)
1471         JS_CLEAR_GSN_CACHE(cx);
1472     JS_free(cx, script);
1473 }
1474 
1475 void
js_MarkScript(JSContext * cx,JSScript * script)1476 js_MarkScript(JSContext *cx, JSScript *script)
1477 {
1478     JSAtomMap *map;
1479     uintN i, length;
1480     JSAtom **vector;
1481 
1482     map = &script->atomMap;
1483     length = map->length;
1484     vector = map->vector;
1485     for (i = 0; i < length; i++)
1486         GC_MARK_ATOM(cx, vector[i]);
1487 
1488     if (script->filename)
1489         js_MarkScriptFilename(script->filename);
1490 }
1491 
1492 typedef struct GSNCacheEntry {
1493     JSDHashEntryHdr     hdr;
1494     jsbytecode          *pc;
1495     jssrcnote           *sn;
1496 } GSNCacheEntry;
1497 
1498 #define GSN_CACHE_THRESHOLD     100
1499 
1500 jssrcnote *
js_GetSrcNoteCached(JSContext * cx,JSScript * script,jsbytecode * pc)1501 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1502 {
1503     ptrdiff_t target, offset;
1504     GSNCacheEntry *entry;
1505     jssrcnote *sn, *result;
1506     uintN nsrcnotes;
1507 
1508 
1509     target = PTRDIFF(pc, script->code, jsbytecode);
1510     if ((uint32)target >= script->length)
1511         return NULL;
1512 
1513     if (JS_GSN_CACHE(cx).script == script) {
1514         JS_METER_GSN_CACHE(cx, hits);
1515         entry = (GSNCacheEntry *)
1516                 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1517                                      JS_DHASH_LOOKUP);
1518         return entry->sn;
1519     }
1520 
1521     JS_METER_GSN_CACHE(cx, misses);
1522     offset = 0;
1523     for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) {
1524         if (SN_IS_TERMINATOR(sn)) {
1525             result = NULL;
1526             break;
1527         }
1528         offset += SN_DELTA(sn);
1529         if (offset == target && SN_IS_GETTABLE(sn)) {
1530             result = sn;
1531             break;
1532         }
1533     }
1534 
1535     if (JS_GSN_CACHE(cx).script != script &&
1536         script->length >= GSN_CACHE_THRESHOLD) {
1537         JS_CLEAR_GSN_CACHE(cx);
1538         nsrcnotes = 0;
1539         for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1540              sn = SN_NEXT(sn)) {
1541             if (SN_IS_GETTABLE(sn))
1542                 ++nsrcnotes;
1543         }
1544         if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
1545                                NULL, sizeof(GSNCacheEntry), nsrcnotes)) {
1546             JS_GSN_CACHE(cx).table.ops = NULL;
1547         } else {
1548             pc = script->code;
1549             for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
1550                  sn = SN_NEXT(sn)) {
1551                 pc += SN_DELTA(sn);
1552                 if (SN_IS_GETTABLE(sn)) {
1553                     entry = (GSNCacheEntry *)
1554                             JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1555                                                  JS_DHASH_ADD);
1556                     entry->pc = pc;
1557                     entry->sn = sn;
1558                 }
1559             }
1560             JS_GSN_CACHE(cx).script = script;
1561             JS_METER_GSN_CACHE(cx, fills);
1562         }
1563     }
1564 
1565     return result;
1566 }
1567 
1568 uintN
js_PCToLineNumber(JSContext * cx,JSScript * script,jsbytecode * pc)1569 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1570 {
1571     JSAtom *atom;
1572     JSFunction *fun;
1573     uintN lineno;
1574     ptrdiff_t offset, target;
1575     jssrcnote *sn;
1576     JSSrcNoteType type;
1577 
1578     /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1579     if (!pc)
1580         return 0;
1581 
1582     /*
1583      * Special case: function definition needs no line number note because
1584      * the function's script contains its starting line number.
1585      */
1586     if (*pc == JSOP_DEFFUN ||
1587         (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) {
1588         atom = js_GetAtom(cx, &script->atomMap,
1589                           (*pc == JSOP_DEFFUN)
1590                           ? GET_ATOM_INDEX(pc)
1591                           : GET_LITERAL_INDEX(pc));
1592         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
1593         JS_ASSERT(FUN_INTERPRETED(fun));
1594         return fun->u.i.script->lineno;
1595     }
1596 
1597     /*
1598      * General case: walk through source notes accumulating their deltas,
1599      * keeping track of line-number notes, until we pass the note for pc's
1600      * offset within script->code.
1601      */
1602     lineno = script->lineno;
1603     offset = 0;
1604     target = PTRDIFF(pc, script->code, jsbytecode);
1605     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1606         offset += SN_DELTA(sn);
1607         type = (JSSrcNoteType) SN_TYPE(sn);
1608         if (type == SRC_SETLINE) {
1609             if (offset <= target)
1610                 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1611         } else if (type == SRC_NEWLINE) {
1612             if (offset <= target)
1613                 lineno++;
1614         }
1615         if (offset > target)
1616             break;
1617     }
1618     return lineno;
1619 }
1620 
1621 /* The line number limit is the same as the jssrcnote offset limit. */
1622 #define SN_LINE_LIMIT   (SN_3BYTE_OFFSET_FLAG << 16)
1623 
1624 jsbytecode *
js_LineNumberToPC(JSScript * script,uintN target)1625 js_LineNumberToPC(JSScript *script, uintN target)
1626 {
1627     ptrdiff_t offset, best;
1628     uintN lineno, bestdiff, diff;
1629     jssrcnote *sn;
1630     JSSrcNoteType type;
1631 
1632     offset = 0;
1633     best = -1;
1634     lineno = script->lineno;
1635     bestdiff = SN_LINE_LIMIT;
1636     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1637         if (lineno == target)
1638             goto out;
1639         if (lineno > target) {
1640             diff = lineno - target;
1641             if (diff < bestdiff) {
1642                 bestdiff = diff;
1643                 best = offset;
1644             }
1645         }
1646         offset += SN_DELTA(sn);
1647         type = (JSSrcNoteType) SN_TYPE(sn);
1648         if (type == SRC_SETLINE) {
1649             lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1650         } else if (type == SRC_NEWLINE) {
1651             lineno++;
1652         }
1653     }
1654     if (best >= 0)
1655         offset = best;
1656 out:
1657     return script->code + offset;
1658 }
1659 
1660 JS_FRIEND_API(uintN)
js_GetScriptLineExtent(JSScript * script)1661 js_GetScriptLineExtent(JSScript *script)
1662 {
1663     uintN lineno;
1664     jssrcnote *sn;
1665     JSSrcNoteType type;
1666 
1667     lineno = script->lineno;
1668     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1669         type = (JSSrcNoteType) SN_TYPE(sn);
1670         if (type == SRC_SETLINE) {
1671             lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1672         } else if (type == SRC_NEWLINE) {
1673             lineno++;
1674         }
1675     }
1676     return 1 + lineno - script->lineno;
1677 }
1678 
1679 #if JS_HAS_GENERATORS
1680 
1681 jsbytecode *
js_FindFinallyHandler(JSScript * script,jsbytecode * pc)1682 js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
1683 {
1684     JSTryNote *tn;
1685     ptrdiff_t off;
1686     JSOp op2;
1687 
1688     tn = script->trynotes;
1689     if (!tn)
1690         return NULL;
1691 
1692     off = pc - script->main;
1693     if (off < 0)
1694         return NULL;
1695 
1696     JS_ASSERT(tn->catchStart != 0);
1697     do {
1698         if ((jsuword)(off - tn->start) < (jsuword)tn->length) {
1699             /*
1700              * We have a handler: is it the finally one, or a catch handler?
1701              *
1702              * Catch bytecode begins with:   JSOP_SETSP JSOP_ENTERBLOCK
1703              * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION)
1704              */
1705             pc = script->main + tn->catchStart;
1706             JS_ASSERT(*pc == JSOP_SETSP);
1707             op2 = pc[JSOP_SETSP_LENGTH];
1708             if (op2 != JSOP_ENTERBLOCK) {
1709                 JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION);
1710                 return pc;
1711             }
1712         }
1713     } while ((++tn)->catchStart != 0);
1714     return NULL;
1715 }
1716 
1717 #endif
1718