Back to index

lightning-sunbird  0.9+nobinonly
jsscript.c
Go to the documentation of this file.
00001 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
00002  * vim: set ts=8 sw=4 et tw=78:
00003  *
00004  * ***** BEGIN LICENSE BLOCK *****
00005  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006  *
00007  * The contents of this file are subject to the Mozilla Public License Version
00008  * 1.1 (the "License"); you may not use this file except in compliance with
00009  * the License. You may obtain a copy of the License at
00010  * http://www.mozilla.org/MPL/
00011  *
00012  * Software distributed under the License is distributed on an "AS IS" basis,
00013  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014  * for the specific language governing rights and limitations under the
00015  * License.
00016  *
00017  * The Original Code is Mozilla Communicator client code, released
00018  * March 31, 1998.
00019  *
00020  * The Initial Developer of the Original Code is
00021  * Netscape Communications Corporation.
00022  * Portions created by the Initial Developer are Copyright (C) 1998
00023  * the Initial Developer. All Rights Reserved.
00024  *
00025  * Contributor(s):
00026  *
00027  * Alternatively, the contents of this file may be used under the terms of
00028  * either of the GNU General Public License Version 2 or later (the "GPL"),
00029  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030  * in which case the provisions of the GPL or the LGPL are applicable instead
00031  * of those above. If you wish to allow use of your version of this file only
00032  * under the terms of either the GPL or the LGPL, and not to allow others to
00033  * use your version of this file under the terms of the MPL, indicate your
00034  * decision by deleting the provisions above and replace them with the notice
00035  * and other provisions required by the GPL or the LGPL. If you do not delete
00036  * the provisions above, a recipient may use your version of this file under
00037  * the terms of any one of the MPL, the GPL or the LGPL.
00038  *
00039  * ***** END LICENSE BLOCK ***** */
00040 
00041 /*
00042  * JS script operations.
00043  */
00044 #include "jsstddef.h"
00045 #include <string.h>
00046 #include "jstypes.h"
00047 #include "jsutil.h" /* Added by JSIFY */
00048 #include "jsprf.h"
00049 #include "jsapi.h"
00050 #include "jsatom.h"
00051 #include "jscntxt.h"
00052 #include "jsconfig.h"
00053 #include "jsdbgapi.h"
00054 #include "jsemit.h"
00055 #include "jsfun.h"
00056 #include "jsinterp.h"
00057 #include "jslock.h"
00058 #include "jsnum.h"
00059 #include "jsopcode.h"
00060 #include "jsscript.h"
00061 #if JS_HAS_XDR
00062 #include "jsxdrapi.h"
00063 #endif
00064 
00065 #if JS_HAS_SCRIPT_OBJECT
00066 
00067 static const char js_script_exec[] = "Script.prototype.exec";
00068 static const char js_script_compile[] = "Script.prototype.compile";
00069 
00070 /*
00071  * This routine requires that obj has been locked previously.
00072  */
00073 static jsint
00074 GetScriptExecDepth(JSContext *cx, JSObject *obj)
00075 {
00076     jsval v;
00077 
00078     JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
00079     v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass));
00080     return JSVAL_TO_INT(v);
00081 }
00082 
00083 static void
00084 AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta)
00085 {
00086     jsint execDepth;
00087 
00088     JS_LOCK_OBJ(cx, obj);
00089     execDepth = GetScriptExecDepth(cx, obj);
00090     LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass),
00091                         INT_TO_JSVAL(execDepth + delta));
00092     JS_UNLOCK_OBJ(cx, obj);
00093 }
00094 
00095 #if JS_HAS_TOSOURCE
00096 static JSBool
00097 script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00098                 jsval *rval)
00099 {
00100     uint32 indent;
00101     JSScript *script;
00102     size_t i, j, k, n;
00103     char buf[16];
00104     jschar *s, *t;
00105     JSString *str;
00106 
00107     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00108         return JS_FALSE;
00109 
00110     indent = 0;
00111     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
00112         return JS_FALSE;
00113 
00114     script = (JSScript *) JS_GetPrivate(cx, obj);
00115 
00116     /* Let n count the source string length, j the "front porch" length. */
00117     j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
00118     n = j + 2;
00119     if (!script) {
00120         /* Let k count the constructor argument string length. */
00121         k = 0;
00122         s = NULL;               /* quell GCC overwarning */
00123     } else {
00124         str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
00125                                  (uintN)indent);
00126         if (!str)
00127             return JS_FALSE;
00128         str = js_QuoteString(cx, str, '\'');
00129         if (!str)
00130             return JS_FALSE;
00131         s = JSSTRING_CHARS(str);
00132         k = JSSTRING_LENGTH(str);
00133         n += k;
00134     }
00135 
00136     /* Allocate the source string and copy into it. */
00137     t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
00138     if (!t)
00139         return JS_FALSE;
00140     for (i = 0; i < j; i++)
00141         t[i] = buf[i];
00142     for (j = 0; j < k; i++, j++)
00143         t[i] = s[j];
00144     t[i++] = ')';
00145     t[i++] = ')';
00146     t[i] = 0;
00147 
00148     /* Create and return a JS string for t. */
00149     str = JS_NewUCString(cx, t, n);
00150     if (!str) {
00151         JS_free(cx, t);
00152         return JS_FALSE;
00153     }
00154     *rval = STRING_TO_JSVAL(str);
00155     return JS_TRUE;
00156 }
00157 #endif /* JS_HAS_TOSOURCE */
00158 
00159 static JSBool
00160 script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00161                 jsval *rval)
00162 {
00163     uint32 indent;
00164     JSScript *script;
00165     JSString *str;
00166 
00167     indent = 0;
00168     if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
00169         return JS_FALSE;
00170 
00171     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00172         return JS_FALSE;
00173     script = (JSScript *) JS_GetPrivate(cx, obj);
00174     if (!script) {
00175         *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
00176         return JS_TRUE;
00177     }
00178 
00179     str = JS_DecompileScript(cx, script, "Script.prototype.toString",
00180                              (uintN)indent);
00181     if (!str)
00182         return JS_FALSE;
00183     *rval = STRING_TO_JSVAL(str);
00184     return JS_TRUE;
00185 }
00186 
00187 static JSBool
00188 script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00189                jsval *rval)
00190 {
00191     JSString *str;
00192     JSObject *scopeobj;
00193     jsval v;
00194     JSScript *script, *oldscript;
00195     JSStackFrame *fp, *caller;
00196     const char *file;
00197     uintN line;
00198     JSPrincipals *principals;
00199     jsint execDepth;
00200 
00201     /* Make sure obj is a Script object. */
00202     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00203         return JS_FALSE;
00204 
00205     /* If no args, leave private undefined and return early. */
00206     if (argc == 0)
00207         goto out;
00208 
00209     /* Otherwise, the first arg is the script source to compile. */
00210     str = js_ValueToString(cx, argv[0]);
00211     if (!str)
00212         return JS_FALSE;
00213     argv[0] = STRING_TO_JSVAL(str);
00214 
00215     scopeobj = NULL;
00216     if (argc >= 2) {
00217         if (!js_ValueToObject(cx, argv[1], &scopeobj))
00218             return JS_FALSE;
00219         argv[1] = OBJECT_TO_JSVAL(scopeobj);
00220     }
00221 
00222     /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
00223     fp = cx->fp;
00224     caller = JS_GetScriptedCaller(cx, fp);
00225     JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain);
00226 
00227     if (caller) {
00228         if (!scopeobj) {
00229             scopeobj = js_GetScopeChain(cx, caller);
00230             if (!scopeobj)
00231                 return JS_FALSE;
00232             fp->scopeChain = scopeobj;  /* for the compiler's benefit */
00233         }
00234 
00235         principals = JS_EvalFramePrincipals(cx, fp, caller);
00236         file = js_ComputeFilename(cx, caller, principals, &line);
00237     } else {
00238         file = NULL;
00239         line = 0;
00240         principals = NULL;
00241     }
00242 
00243     /* Ensure we compile this script with the right (inner) principals. */
00244     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile);
00245     if (!scopeobj)
00246         return JS_FALSE;
00247 
00248     /*
00249      * Compile the new script using the caller's scope chain, a la eval().
00250      * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's
00251      * flags, because compilation is here separated from execution, and the
00252      * run-time scope chain may not match the compile-time.  JSFRAME_EVAL is
00253      * tested in jsemit.c and jsscan.c to optimize based on identity of run-
00254      * and compile-time scope.
00255      */
00256     fp->flags |= JSFRAME_SCRIPT_OBJECT;
00257     script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
00258                                              JSSTRING_CHARS(str),
00259                                              JSSTRING_LENGTH(str),
00260                                              file, line);
00261     if (!script)
00262         return JS_FALSE;
00263 
00264     JS_LOCK_OBJ(cx, obj);
00265     execDepth = GetScriptExecDepth(cx, obj);
00266 
00267     /*
00268      * execDepth must be 0 to allow compilation here, otherwise the JSScript
00269      * struct can be released while running.
00270      */
00271     if (execDepth > 0) {
00272         JS_UNLOCK_OBJ(cx, obj);
00273         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00274                              JSMSG_COMPILE_EXECED_SCRIPT);
00275         return JS_FALSE;
00276     }
00277 
00278     /* Swap script for obj's old script, if any. */
00279     v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PRIVATE);
00280     oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL;
00281     LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
00282     JS_UNLOCK_OBJ(cx, obj);
00283 
00284     if (oldscript)
00285         js_DestroyScript(cx, oldscript);
00286 
00287     script->object = obj;
00288     js_CallNewScriptHook(cx, script, NULL);
00289 
00290 out:
00291     /* Return the object. */
00292     *rval = OBJECT_TO_JSVAL(obj);
00293     return JS_TRUE;
00294 }
00295 
00296 static JSBool
00297 script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00298 {
00299     JSObject *scopeobj, *parent;
00300     JSStackFrame *fp, *caller;
00301     JSScript *script;
00302     JSBool ok;
00303 
00304     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00305         return JS_FALSE;
00306 
00307     scopeobj = NULL;
00308     if (argc) {
00309         if (!js_ValueToObject(cx, argv[0], &scopeobj))
00310             return JS_FALSE;
00311         argv[0] = OBJECT_TO_JSVAL(scopeobj);
00312     }
00313 
00314     /*
00315      * Emulate eval() by using caller's this, var object, sharp array, etc.,
00316      * all propagated by js_Execute via a non-null fourth (down) argument to
00317      * js_Execute.  If there is no scripted caller, js_Execute uses its second
00318      * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
00319      *
00320      * Unlike eval, which the compiler detects, Script.prototype.exec may be
00321      * called from a lightweight function, or even from native code (in which
00322      * case fp->varobj and fp->scopeChain are null).  If exec is called from
00323      * a lightweight function, we will need to get a Call object representing
00324      * its frame, to act as the var object and scope chain head.
00325      */
00326     fp = cx->fp;
00327     caller = JS_GetScriptedCaller(cx, fp);
00328     if (caller && !caller->varobj) {
00329         /* Called from a lightweight function. */
00330         JS_ASSERT(caller->fun && !JSFUN_HEAVYWEIGHT_TEST(caller->fun->flags));
00331 
00332         /* Scope chain links from Call object to callee's parent. */
00333         parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2]));
00334         if (!js_GetCallObject(cx, caller, parent))
00335             return JS_FALSE;
00336     }
00337 
00338     if (!scopeobj) {
00339         /* No scope object passed in: try to use the caller's scope chain. */
00340         if (caller) {
00341             /*
00342              * Load caller->scopeChain after the conditional js_GetCallObject
00343              * call above, which resets scopeChain as well as varobj.
00344              */
00345             scopeobj = js_GetScopeChain(cx, caller);
00346             if (!scopeobj)
00347                 return JS_FALSE;
00348         } else {
00349             /*
00350              * Called from native code, so we don't know what scope object to
00351              * use.  We could use parent (see above), but Script.prototype.exec
00352              * might be a shared/sealed "superglobal" method.  A more general
00353              * approach would use cx->globalObject, which will be the same as
00354              * exec.__parent__ in the non-superglobal case.  In the superglobal
00355              * case it's the right object: the global, not the superglobal.
00356              */
00357             scopeobj = cx->globalObject;
00358         }
00359     }
00360 
00361     scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec);
00362     if (!scopeobj)
00363         return JS_FALSE;
00364 
00365     /* Keep track of nesting depth for the script. */
00366     AdjustScriptExecDepth(cx, obj, 1);
00367 
00368     /* Must get to out label after this */
00369     script = (JSScript *) JS_GetPrivate(cx, obj);
00370     if (!script) {
00371         ok = JS_FALSE;
00372         goto out;
00373     }
00374 
00375     /* Belt-and-braces: check that this script object has access to scopeobj. */
00376     ok = js_CheckPrincipalsAccess(cx, scopeobj, script->principals,
00377                                   CLASS_ATOM(cx, Script));
00378     if (!ok)
00379         goto out;
00380 
00381     ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
00382    
00383 out:
00384     AdjustScriptExecDepth(cx, obj, -1); 
00385     return ok;
00386 }
00387 
00388 #if JS_HAS_XDR
00389 
00390 static JSBool
00391 XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)
00392 {
00393     JSContext *cx;
00394     uint32 natoms, i, index;
00395     JSAtom **atoms;
00396 
00397     cx = xdr->cx;
00398 
00399     if (xdr->mode == JSXDR_ENCODE)
00400         natoms = (uint32)map->length;
00401 
00402     if (!JS_XDRUint32(xdr, &natoms))
00403         return JS_FALSE;
00404 
00405     if (xdr->mode == JSXDR_ENCODE) {
00406         atoms = map->vector;
00407     } else {
00408         if (natoms == 0) {
00409             atoms = NULL;
00410         } else {
00411             atoms = (JSAtom **) JS_malloc(cx, (size_t)natoms * sizeof *atoms);
00412             if (!atoms)
00413                 return JS_FALSE;
00414 #ifdef DEBUG
00415             memset(atoms, 0, (size_t)natoms * sizeof *atoms);
00416 #endif
00417         }
00418 
00419         map->vector = atoms;
00420         map->length = natoms;
00421     }
00422 
00423     for (i = 0; i != natoms; ++i) {
00424         if (xdr->mode == JSXDR_ENCODE)
00425             index = i;
00426         if (!JS_XDRUint32(xdr, &index))
00427             goto bad;
00428 
00429         /*
00430          * Assert that, when decoding, the read index is valid and points to
00431          * an unoccupied element of atoms array.
00432          */
00433         JS_ASSERT(index < natoms);
00434         JS_ASSERT(xdr->mode == JSXDR_ENCODE || !atoms[index]);
00435         if (!js_XDRAtom(xdr, &atoms[index]))
00436             goto bad;
00437     }
00438 
00439     return JS_TRUE;
00440 
00441   bad:
00442     if (xdr->mode == JSXDR_DECODE) {
00443         JS_free(cx, atoms);
00444         map->vector = NULL;
00445         map->length = 0;
00446     }
00447 
00448     return JS_FALSE;
00449 }
00450 
00451 JSBool
00452 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
00453 {
00454     JSContext *cx;
00455     JSScript *script, *newscript, *oldscript;
00456     uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes;
00457     uint32 prologLength, version;
00458     JSBool filenameWasSaved;
00459     jssrcnote *notes, *sn;
00460 
00461     cx = xdr->cx;
00462     script = *scriptp;
00463     nsrcnotes = ntrynotes = 0;
00464     filenameWasSaved = JS_FALSE;
00465     notes = NULL;
00466 
00467     /*
00468      * Encode prologLength and version after script->length (_2 or greater),
00469      * but decode both new (>= _2) and old, prolog&version-free (_1) scripts.
00470      * Version _3 supports principals serialization.  Version _4 reorders the
00471      * nsrcnotes and ntrynotes fields to come before everything except magic,
00472      * length, prologLength, and version, so that srcnote and trynote storage
00473      * can be allocated as part of the JSScript (along with bytecode storage).
00474      *
00475      * So far, the magic number has not changed for every jsopcode.tbl change.
00476      * We stipulate forward compatibility by requiring old bytecodes never to
00477      * change or go away (modulo a few exceptions before the XDR interfaces
00478      * evolved, and a few exceptions during active trunk development).  With
00479      * the addition of JSOP_STOP to support JS_THREADED_INTERP, we make a new
00480      * magic number (_5) so that we know to append JSOP_STOP to old scripts
00481      * when deserializing.
00482      */
00483     if (xdr->mode == JSXDR_ENCODE)
00484         magic = JSXDR_MAGIC_SCRIPT_CURRENT;
00485     if (!JS_XDRUint32(xdr, &magic))
00486         return JS_FALSE;
00487     JS_ASSERT((uint32)JSXDR_MAGIC_SCRIPT_5 - (uint32)JSXDR_MAGIC_SCRIPT_1 == 4);
00488     if (magic - (uint32)JSXDR_MAGIC_SCRIPT_1 > 4) {
00489         if (!hasMagic) {
00490             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00491                                  JSMSG_BAD_SCRIPT_MAGIC);
00492             return JS_FALSE;
00493         }
00494         *hasMagic = JS_FALSE;
00495         return JS_TRUE;
00496     }
00497     if (hasMagic)
00498         *hasMagic = JS_TRUE;
00499 
00500     if (xdr->mode == JSXDR_ENCODE) {
00501         length = script->length;
00502         prologLength = PTRDIFF(script->main, script->code, jsbytecode);
00503         JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
00504         version = (uint32)script->version | (script->numGlobalVars << 16);
00505         lineno = (uint32)script->lineno;
00506         depth = (uint32)script->depth;
00507 
00508         /* Count the srcnotes, keeping notes pointing at the first one. */
00509         notes = SCRIPT_NOTES(script);
00510         for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
00511             continue;
00512         nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
00513         nsrcnotes++;            /* room for the terminator */
00514 
00515         /* Count the trynotes. */
00516         if (script->trynotes) {
00517             while (script->trynotes[ntrynotes].catchStart)
00518                 ntrynotes++;
00519             ntrynotes++;        /* room for the end marker */
00520         }
00521     }
00522 
00523     if (!JS_XDRUint32(xdr, &length))
00524         return JS_FALSE;
00525     if (magic >= JSXDR_MAGIC_SCRIPT_2) {
00526         if (!JS_XDRUint32(xdr, &prologLength))
00527             return JS_FALSE;
00528         if (!JS_XDRUint32(xdr, &version))
00529             return JS_FALSE;
00530 
00531         /* To fuse allocations, we need srcnote and trynote counts early. */
00532         if (magic >= JSXDR_MAGIC_SCRIPT_4) {
00533             if (!JS_XDRUint32(xdr, &nsrcnotes))
00534                 return JS_FALSE;
00535             if (!JS_XDRUint32(xdr, &ntrynotes))
00536                 return JS_FALSE;
00537         }
00538     }
00539 
00540     if (xdr->mode == JSXDR_DECODE) {
00541         size_t alloclength = length;
00542         if (magic < JSXDR_MAGIC_SCRIPT_5)
00543             ++alloclength;      /* add a byte for JSOP_STOP */
00544 
00545         script = js_NewScript(cx, alloclength, nsrcnotes, ntrynotes);
00546         if (!script)
00547             return JS_FALSE;
00548         if (magic >= JSXDR_MAGIC_SCRIPT_2) {
00549             script->main += prologLength;
00550             script->version = (JSVersion) (version & 0xffff);
00551             script->numGlobalVars = (uint16) (version >> 16);
00552 
00553             /* If we know nsrcnotes, we allocated space for notes in script. */
00554             if (magic >= JSXDR_MAGIC_SCRIPT_4)
00555                 notes = SCRIPT_NOTES(script);
00556         }
00557         *scriptp = script;
00558     }
00559 
00560     /*
00561      * Control hereafter must goto error on failure, in order for the DECODE
00562      * case to destroy script and conditionally free notes, which if non-null
00563      * in the (DECODE and magic < _4) case must point at a temporary vector
00564      * allocated just below.
00565      */
00566     oldscript = xdr->script;
00567     xdr->script = script;
00568     if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) ||
00569         !XDRAtomMap(xdr, &script->atomMap)) {
00570         goto error;
00571     }
00572 
00573     if (magic < JSXDR_MAGIC_SCRIPT_5) {
00574         if (xdr->mode == JSXDR_DECODE) {
00575             /*
00576              * Append JSOP_STOP to old scripts, to relieve the interpreter
00577              * from having to bounds-check pc.  Also take care to increment
00578              * length, as it is used below and must count all bytecode.
00579              */
00580             script->code[length++] = JSOP_STOP;
00581         }
00582 
00583         if (magic < JSXDR_MAGIC_SCRIPT_4) {
00584             if (!JS_XDRUint32(xdr, &nsrcnotes))
00585                 goto error;
00586             if (xdr->mode == JSXDR_DECODE) {
00587                 notes = (jssrcnote *)
00588                         JS_malloc(cx, nsrcnotes * sizeof(jssrcnote));
00589                 if (!notes)
00590                     goto error;
00591             }
00592         }
00593     }
00594 
00595     if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
00596         !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
00597         !JS_XDRUint32(xdr, &lineno) ||
00598         !JS_XDRUint32(xdr, &depth) ||
00599         (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) {
00600         goto error;
00601     }
00602 
00603     /* Script principals transcoding support comes with versions >= _3. */
00604     if (magic >= JSXDR_MAGIC_SCRIPT_3) {
00605         JSPrincipals *principals;
00606         uint32 encodeable;
00607 
00608         if (xdr->mode == JSXDR_ENCODE) {
00609             principals = script->principals;
00610             encodeable = (cx->runtime->principalsTranscoder != NULL);
00611             if (!JS_XDRUint32(xdr, &encodeable))
00612                 goto error;
00613             if (encodeable &&
00614                 !cx->runtime->principalsTranscoder(xdr, &principals)) {
00615                 goto error;
00616             }
00617         } else {
00618             if (!JS_XDRUint32(xdr, &encodeable))
00619                 goto error;
00620             if (encodeable) {
00621                 if (!cx->runtime->principalsTranscoder) {
00622                     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00623                                          JSMSG_CANT_DECODE_PRINCIPALS);
00624                     goto error;
00625                 }
00626                 if (!cx->runtime->principalsTranscoder(xdr, &principals))
00627                     goto error;
00628                 script->principals = principals;
00629             }
00630         }
00631     }
00632 
00633     if (xdr->mode == JSXDR_DECODE) {
00634         const char *filename = script->filename;
00635         if (filename) {
00636             filename = js_SaveScriptFilename(cx, filename);
00637             if (!filename)
00638                 goto error;
00639             JS_free(cx, (void *) script->filename);
00640             script->filename = filename;
00641             filenameWasSaved = JS_TRUE;
00642         }
00643         script->lineno = (uintN)lineno;
00644         script->depth = (uintN)depth;
00645 
00646         if (magic < JSXDR_MAGIC_SCRIPT_4) {
00647             /*
00648              * Argh, we have to reallocate script, copy notes into the extra
00649              * space after the bytecodes, and free the temporary notes vector.
00650              * First, add enough slop to nsrcnotes so we can align the address
00651              * after the srcnotes of the first trynote.
00652              */
00653             uint32 osrcnotes = nsrcnotes;
00654 
00655             if (ntrynotes)
00656                 nsrcnotes += JSTRYNOTE_ALIGNMASK;
00657             newscript = (JSScript *) JS_realloc(cx, script,
00658                                                 sizeof(JSScript) +
00659                                                 length * sizeof(jsbytecode) +
00660                                                 nsrcnotes * sizeof(jssrcnote) +
00661                                                 ntrynotes * sizeof(JSTryNote));
00662             if (!newscript)
00663                 goto error;
00664 
00665             *scriptp = script = newscript;
00666             script->code = (jsbytecode *)(script + 1);
00667             script->main = script->code + prologLength;
00668             memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote));
00669             JS_free(cx, (void *) notes);
00670             notes = NULL;
00671             if (ntrynotes) {
00672                 script->trynotes = (JSTryNote *)
00673                                    ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
00674                                     ~(jsword)JSTRYNOTE_ALIGNMASK);
00675                 memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
00676             }
00677         }
00678     }
00679 
00680     while (ntrynotes) {
00681         JSTryNote *tn = &script->trynotes[--ntrynotes];
00682         uint32 start = (uint32) tn->start,
00683                catchLength = (uint32) tn->length,
00684                catchStart = (uint32) tn->catchStart;
00685 
00686         if (!JS_XDRUint32(xdr, &start) ||
00687             !JS_XDRUint32(xdr, &catchLength) ||
00688             !JS_XDRUint32(xdr, &catchStart)) {
00689             goto error;
00690         }
00691         tn->start = (ptrdiff_t) start;
00692         tn->length = (ptrdiff_t) catchLength;
00693         tn->catchStart = (ptrdiff_t) catchStart;
00694     }
00695 
00696     xdr->script = oldscript;
00697     return JS_TRUE;
00698 
00699   error:
00700     if (xdr->mode == JSXDR_DECODE) {
00701         if (script->filename && !filenameWasSaved) {
00702             JS_free(cx, (void *) script->filename);
00703             script->filename = NULL;
00704         }
00705         if (notes && magic < JSXDR_MAGIC_SCRIPT_4)
00706             JS_free(cx, (void *) notes);
00707         js_DestroyScript(cx, script);
00708         *scriptp = NULL;
00709     }
00710     return JS_FALSE;
00711 }
00712 
00713 #if JS_HAS_XDR_FREEZE_THAW
00714 /*
00715  * These cannot be exposed to web content, and chrome does not need them, so
00716  * we take them out of the Mozilla client altogether.  Fortunately, there is
00717  * no way to serialize a native function (see fun_xdrObject in jsfun.c).
00718  */
00719 
00720 static JSBool
00721 script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00722               jsval *rval)
00723 {
00724     JSXDRState *xdr;
00725     JSScript *script;
00726     JSBool ok, hasMagic;
00727     uint32 len;
00728     void *buf;
00729     JSString *str;
00730 
00731     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00732         return JS_FALSE;
00733     script = (JSScript *) JS_GetPrivate(cx, obj);
00734     if (!script)
00735         return JS_TRUE;
00736 
00737     /* create new XDR */
00738     xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
00739     if (!xdr)
00740         return JS_FALSE;
00741 
00742     /* write  */
00743     ok = js_XDRScript(xdr, &script, &hasMagic);
00744     if (!ok)
00745         goto out;
00746     if (!hasMagic) {
00747         *rval = JSVAL_VOID;
00748         goto out;
00749     }
00750 
00751     buf = JS_XDRMemGetData(xdr, &len);
00752     if (!buf) {
00753         ok = JS_FALSE;
00754         goto out;
00755     }
00756 
00757     JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
00758     len /= sizeof(jschar);
00759     str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
00760     if (!str) {
00761         ok = JS_FALSE;
00762         goto out;
00763     }
00764 
00765 #if IS_BIG_ENDIAN
00766   {
00767     jschar *chars;
00768     uint32 i;
00769 
00770     /* Swap bytes in Unichars to keep frozen strings machine-independent. */
00771     chars = JS_GetStringChars(str);
00772     for (i = 0; i < len; i++)
00773         chars[i] = JSXDR_SWAB16(chars[i]);
00774   }
00775 #endif
00776     *rval = STRING_TO_JSVAL(str);
00777 
00778 out:
00779     JS_XDRDestroy(xdr);
00780     return ok;
00781 }
00782 
00783 static JSBool
00784 script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00785             jsval *rval)
00786 {
00787     JSXDRState *xdr;
00788     JSString *str;
00789     void *buf;
00790     uint32 len;
00791     jsval v;
00792     JSScript *script, *oldscript;
00793     JSBool ok, hasMagic;
00794 
00795     if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
00796         return JS_FALSE;
00797 
00798     if (argc == 0)
00799         return JS_TRUE;
00800     str = js_ValueToString(cx, argv[0]);
00801     if (!str)
00802         return JS_FALSE;
00803     argv[0] = STRING_TO_JSVAL(str);
00804 
00805     /* create new XDR */
00806     xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
00807     if (!xdr)
00808         return JS_FALSE;
00809 
00810     buf = JS_GetStringChars(str);
00811     len = JS_GetStringLength(str);
00812 #if IS_BIG_ENDIAN
00813   {
00814     jschar *from, *to;
00815     uint32 i;
00816 
00817     /* Swap bytes in Unichars to keep frozen strings machine-independent. */
00818     from = (jschar *)buf;
00819     to = (jschar *) JS_malloc(cx, len * sizeof(jschar));
00820     if (!to) {
00821         JS_XDRDestroy(xdr);
00822         return JS_FALSE;
00823     }
00824     for (i = 0; i < len; i++)
00825         to[i] = JSXDR_SWAB16(from[i]);
00826     buf = (char *)to;
00827   }
00828 #endif
00829     len *= sizeof(jschar);
00830     JS_XDRMemSetData(xdr, buf, len);
00831 
00832     /* XXXbe should magic mismatch be error, or false return value? */
00833     ok = js_XDRScript(xdr, &script, &hasMagic);
00834     if (!ok)
00835         goto out;
00836     if (!hasMagic) {
00837         *rval = JSVAL_FALSE;
00838         goto out;
00839     }
00840 
00841     JS_LOCK_OBJ(cx, obj);
00842     execDepth = GetScriptExecDepth(cx, obj);
00843 
00844     /*
00845      * execDepth must be 0 to allow compilation here, otherwise the JSScript
00846      * struct can be released while running.
00847      */
00848     if (execDepth > 0) {
00849         JS_UNLOCK_OBJ(cx, obj);
00850         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
00851                              JSMSG_COMPILE_EXECED_SCRIPT);
00852         goto out;
00853     }
00854 
00855     /* Swap script for obj's old script, if any. */
00856     v = LOCKED_OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
00857     oldscript = !JSVAL_IS_VOID(v) ? (JSScript *) JSVAL_TO_PRIVATE(v) : NULL;
00858     LOCKED_OBJ_SET_SLOT(cx, obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(script));
00859     JS_UNLOCK_OBJ(cx, obj);
00860 
00861     if (oldscript)
00862         js_DestroyScript(cx, oldscript);
00863 
00864     script->object = obj;
00865     js_CallNewScriptHook(cx, script, NULL);
00866 
00867 out:
00868     /*
00869      * We reset the buffer to be NULL so that it doesn't free the chars
00870      * memory owned by str (argv[0]).
00871      */
00872     JS_XDRMemSetData(xdr, NULL, 0);
00873     JS_XDRDestroy(xdr);
00874 #if IS_BIG_ENDIAN
00875     JS_free(cx, buf);
00876 #endif
00877     *rval = JSVAL_TRUE;
00878     return ok;
00879 }
00880 
00881 static const char js_thaw_str[] = "thaw";
00882 
00883 #endif /* JS_HAS_XDR_FREEZE_THAW */
00884 #endif /* JS_HAS_XDR */
00885 
00886 static JSFunctionSpec script_methods[] = {
00887 #if JS_HAS_TOSOURCE
00888     {js_toSource_str,   script_toSource,        0,0,0},
00889 #endif
00890     {js_toString_str,   script_toString,        0,0,0},
00891     {"compile",         script_compile,         2,0,0},
00892     {"exec",            script_exec,            1,0,0},
00893 #if JS_HAS_XDR_FREEZE_THAW
00894     {"freeze",          script_freeze,          0,0,0},
00895     {js_thaw_str,       script_thaw,            1,0,0},
00896 #endif /* JS_HAS_XDR_FREEZE_THAW */
00897     {0,0,0,0,0}
00898 };
00899 
00900 #endif /* JS_HAS_SCRIPT_OBJECT */
00901 
00902 static void
00903 script_finalize(JSContext *cx, JSObject *obj)
00904 {
00905     JSScript *script;
00906 
00907     script = (JSScript *) JS_GetPrivate(cx, obj);
00908     if (script)
00909         js_DestroyScript(cx, script);
00910 }
00911 
00912 static JSBool
00913 script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00914 {
00915 #if JS_HAS_SCRIPT_OBJECT
00916     return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
00917 #else
00918     return JS_FALSE;
00919 #endif
00920 }
00921 
00922 static uint32
00923 script_mark(JSContext *cx, JSObject *obj, void *arg)
00924 {
00925     JSScript *script;
00926 
00927     script = (JSScript *) JS_GetPrivate(cx, obj);
00928     if (script)
00929         js_MarkScript(cx, script);
00930     return 0;
00931 }
00932 
00933 #if !JS_HAS_SCRIPT_OBJECT
00934 const char js_Script_str[] = "Script";
00935 
00936 #define JSProto_Script  JSProto_Object
00937 #endif
00938 
00939 JS_FRIEND_DATA(JSClass) js_ScriptClass = {
00940     js_Script_str,
00941     JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script) |
00942     JSCLASS_HAS_RESERVED_SLOTS(1),
00943     JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,  JS_PropertyStub,
00944     JS_EnumerateStub, JS_ResolveStub,   JS_ConvertStub,   script_finalize,
00945     NULL,             NULL,             script_call,      NULL,/*XXXbe xdr*/
00946     NULL,             NULL,             script_mark,      0
00947 };
00948 
00949 #if JS_HAS_SCRIPT_OBJECT
00950 
00951 static JSBool
00952 Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
00953 {
00954     /* If not constructing, replace obj with a new Script object. */
00955     if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
00956         obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
00957         if (!obj)
00958             return JS_FALSE;
00959 
00960         /*
00961          * script_compile does not use rval to root its temporaries
00962          * so we can use it to root obj.
00963          */
00964         *rval = OBJECT_TO_JSVAL(obj);
00965     }
00966 
00967     if (!JS_SetReservedSlot(cx, obj, 0, INT_TO_JSVAL(0)))
00968         return JS_FALSE;
00969 
00970     return script_compile(cx, obj, argc, argv, rval);
00971 }
00972 
00973 #if JS_HAS_XDR_FREEZE_THAW
00974 
00975 static JSBool
00976 script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
00977                    jsval *rval)
00978 {
00979     obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
00980     if (!obj)
00981         return JS_FALSE;
00982     if (!script_thaw(cx, obj, argc, argv, rval))
00983         return JS_FALSE;
00984     *rval = OBJECT_TO_JSVAL(obj);
00985     return JS_TRUE;
00986 }
00987 
00988 static JSFunctionSpec script_static_methods[] = {
00989     {js_thaw_str,       script_static_thaw,     1,0,0},
00990     {0,0,0,0,0}
00991 };
00992 
00993 #else  /* !JS_HAS_XDR_FREEZE_THAW */
00994 
00995 #define script_static_methods   NULL
00996 
00997 #endif /* !JS_HAS_XDR_FREEZE_THAW */
00998 
00999 JSObject *
01000 js_InitScriptClass(JSContext *cx, JSObject *obj)
01001 {
01002     return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
01003                         NULL, script_methods, NULL, script_static_methods);
01004 }
01005 
01006 #endif /* JS_HAS_SCRIPT_OBJECT */
01007 
01008 /*
01009  * Shared script filename management.
01010  */
01011 JS_STATIC_DLL_CALLBACK(int)
01012 js_compare_strings(const void *k1, const void *k2)
01013 {
01014     return strcmp(k1, k2) == 0;
01015 }
01016 
01017 /* Shared with jsatom.c to save code space. */
01018 extern void * JS_DLL_CALLBACK
01019 js_alloc_table_space(void *priv, size_t size);
01020 
01021 extern void JS_DLL_CALLBACK
01022 js_free_table_space(void *priv, void *item);
01023 
01024 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
01025 typedef struct ScriptFilenameEntry {
01026     JSHashEntry         *next;          /* hash chain linkage */
01027     JSHashNumber        keyHash;        /* key hash function result */
01028     const void          *key;           /* ptr to filename, below */
01029     uint32              flags;          /* user-defined filename prefix flags */
01030     JSPackedBool        mark;           /* GC mark flag */
01031     char                filename[3];    /* two or more bytes, NUL-terminated */
01032 } ScriptFilenameEntry;
01033 
01034 JS_STATIC_DLL_CALLBACK(JSHashEntry *)
01035 js_alloc_sftbl_entry(void *priv, const void *key)
01036 {
01037     size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1;
01038 
01039     return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
01040 }
01041 
01042 JS_STATIC_DLL_CALLBACK(void)
01043 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
01044 {
01045     if (flag != HT_FREE_ENTRY)
01046         return;
01047     free(he);
01048 }
01049 
01050 static JSHashAllocOps sftbl_alloc_ops = {
01051     js_alloc_table_space,   js_free_table_space,
01052     js_alloc_sftbl_entry,   js_free_sftbl_entry
01053 };
01054 
01055 JSBool
01056 js_InitRuntimeScriptState(JSRuntime *rt)
01057 {
01058 #ifdef JS_THREADSAFE
01059     JS_ASSERT(!rt->scriptFilenameTableLock);
01060     rt->scriptFilenameTableLock = JS_NEW_LOCK();
01061     if (!rt->scriptFilenameTableLock)
01062         return JS_FALSE;
01063 #endif
01064     JS_ASSERT(!rt->scriptFilenameTable);
01065     rt->scriptFilenameTable =
01066         JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
01067                         &sftbl_alloc_ops, NULL);
01068     if (!rt->scriptFilenameTable) {
01069         js_FinishRuntimeScriptState(rt);    /* free lock if threadsafe */
01070         return JS_FALSE;
01071     }
01072     JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
01073     return JS_TRUE;
01074 }
01075 
01076 typedef struct ScriptFilenamePrefix {
01077     JSCList     links;      /* circular list linkage for easy deletion */
01078     const char  *name;      /* pointer to pinned ScriptFilenameEntry string */
01079     size_t      length;     /* prefix string length, precomputed */
01080     uint32      flags;      /* user-defined flags to inherit from this prefix */
01081 } ScriptFilenamePrefix;
01082 
01083 void
01084 js_FinishRuntimeScriptState(JSRuntime *rt)
01085 {
01086     if (rt->scriptFilenameTable) {
01087         JS_HashTableDestroy(rt->scriptFilenameTable);
01088         rt->scriptFilenameTable = NULL;
01089     }
01090 #ifdef JS_THREADSAFE
01091     if (rt->scriptFilenameTableLock) {
01092         JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
01093         rt->scriptFilenameTableLock = NULL;
01094     }
01095 #endif
01096 }
01097 
01098 void
01099 js_FreeRuntimeScriptState(JSRuntime *rt)
01100 {
01101     ScriptFilenamePrefix *sfp;
01102 
01103     if (!rt->scriptFilenameTable)
01104         return;
01105 
01106     while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
01107         sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next;
01108         JS_REMOVE_LINK(&sfp->links);
01109         free(sfp);
01110     }
01111     js_FinishRuntimeScriptState(rt);
01112 }
01113 
01114 #ifdef DEBUG_brendan
01115 #define DEBUG_SFTBL
01116 #endif
01117 #ifdef DEBUG_SFTBL
01118 size_t sftbl_savings = 0;
01119 #endif
01120 
01121 static ScriptFilenameEntry *
01122 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
01123 {
01124     JSHashTable *table;
01125     JSHashNumber hash;
01126     JSHashEntry **hep;
01127     ScriptFilenameEntry *sfe;
01128     size_t length;
01129     JSCList *head, *link;
01130     ScriptFilenamePrefix *sfp;
01131 
01132     table = rt->scriptFilenameTable;
01133     hash = JS_HashString(filename);
01134     hep = JS_HashTableRawLookup(table, hash, filename);
01135     sfe = (ScriptFilenameEntry *) *hep;
01136 #ifdef DEBUG_SFTBL
01137     if (sfe)
01138         sftbl_savings += strlen(sfe->filename);
01139 #endif
01140 
01141     if (!sfe) {
01142         sfe = (ScriptFilenameEntry *)
01143               JS_HashTableRawAdd(table, hep, hash, filename, NULL);
01144         if (!sfe)
01145             return NULL;
01146         sfe->key = strcpy(sfe->filename, filename);
01147         sfe->flags = 0;
01148         sfe->mark = JS_FALSE;
01149     }
01150 
01151     /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
01152     if (flags != 0) {
01153         /* Search in case filename was saved already; we must be idempotent. */
01154         sfp = NULL;
01155         length = strlen(filename);
01156         for (head = link = &rt->scriptFilenamePrefixes;
01157              link->next != head;
01158              link = link->next) {
01159             /* Lag link behind sfp to insert in non-increasing length order. */
01160             sfp = (ScriptFilenamePrefix *) link->next;
01161             if (!strcmp(sfp->name, filename))
01162                 break;
01163             if (sfp->length <= length) {
01164                 sfp = NULL;
01165                 break;
01166             }
01167             sfp = NULL;
01168         }
01169 
01170         if (!sfp) {
01171             /* No such prefix: add one now. */
01172             sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix));
01173             if (!sfp)
01174                 return NULL;
01175             JS_INSERT_AFTER(&sfp->links, link);
01176             sfp->name = sfe->filename;
01177             sfp->length = length;
01178             sfp->flags = 0;
01179         }
01180 
01181         /*
01182          * Accumulate flags in both sfe and sfp: sfe for later access from the
01183          * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
01184          * filename entries can inherit by prefix.
01185          */
01186         sfe->flags |= flags;
01187         sfp->flags |= flags;
01188     }
01189 
01190     return sfe;
01191 }
01192 
01193 const char *
01194 js_SaveScriptFilename(JSContext *cx, const char *filename)
01195 {
01196     JSRuntime *rt;
01197     ScriptFilenameEntry *sfe;
01198     JSCList *head, *link;
01199     ScriptFilenamePrefix *sfp;
01200 
01201     rt = cx->runtime;
01202     JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
01203     sfe = SaveScriptFilename(rt, filename, 0);
01204     if (!sfe) {
01205         JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
01206         JS_ReportOutOfMemory(cx);
01207         return NULL;
01208     }
01209 
01210     /*
01211      * Try to inherit flags by prefix.  We assume there won't be more than a
01212      * few (dozen! ;-) prefixes, so linear search is tolerable.
01213      * XXXbe every time I've assumed that in the JS engine, I've been wrong!
01214      */
01215     for (head = &rt->scriptFilenamePrefixes, link = head->next;
01216          link != head;
01217          link = link->next) {
01218         sfp = (ScriptFilenamePrefix *) link;
01219         if (!strncmp(sfp->name, filename, sfp->length)) {
01220             sfe->flags |= sfp->flags;
01221             break;
01222         }
01223     }
01224     JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
01225     return sfe->filename;
01226 }
01227 
01228 const char *
01229 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
01230 {
01231     ScriptFilenameEntry *sfe;
01232 
01233     /* This may be called very early, via the jsdbgapi.h entry point. */
01234     if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
01235         return NULL;
01236 
01237     JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
01238     sfe = SaveScriptFilename(rt, filename, flags);
01239     JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
01240     if (!sfe)
01241         return NULL;
01242 
01243     return sfe->filename;
01244 }
01245 
01246 /*
01247  * Back up from a saved filename by its offset within its hash table entry.
01248  */
01249 #define FILENAME_TO_SFE(fn) \
01250     ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
01251 
01252 /*
01253  * The sfe->key member, redundant given sfe->filename but required by the old
01254  * jshash.c code, here gives us a useful sanity check.  This assertion will
01255  * very likely botch if someone tries to mark a string that wasn't allocated
01256  * as an sfe->filename.
01257  */
01258 #define ASSERT_VALID_SFE(sfe)   JS_ASSERT((sfe)->key == (sfe)->filename)
01259 
01260 uint32
01261 js_GetScriptFilenameFlags(const char *filename)
01262 {
01263     ScriptFilenameEntry *sfe;
01264 
01265     sfe = FILENAME_TO_SFE(filename);
01266     ASSERT_VALID_SFE(sfe);
01267     return sfe->flags;
01268 }
01269 
01270 void
01271 js_MarkScriptFilename(const char *filename)
01272 {
01273     ScriptFilenameEntry *sfe;
01274 
01275     sfe = FILENAME_TO_SFE(filename);
01276     ASSERT_VALID_SFE(sfe);
01277     sfe->mark = JS_TRUE;
01278 }
01279 
01280 JS_STATIC_DLL_CALLBACK(intN)
01281 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
01282 {
01283     ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
01284 
01285     sfe->mark = JS_TRUE;
01286     return HT_ENUMERATE_NEXT;
01287 }
01288 
01289 void
01290 js_MarkScriptFilenames(JSRuntime *rt, JSBool keepAtoms)
01291 {
01292     JSCList *head, *link;
01293     ScriptFilenamePrefix *sfp;
01294 
01295     if (!rt->scriptFilenameTable)
01296         return;
01297 
01298     if (keepAtoms) {
01299         JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
01300                                      js_script_filename_marker,
01301                                      rt);
01302     }
01303     for (head = &rt->scriptFilenamePrefixes, link = head->next;
01304          link != head;
01305          link = link->next) {
01306         sfp = (ScriptFilenamePrefix *) link;
01307         js_MarkScriptFilename(sfp->name);
01308     }
01309 }
01310 
01311 JS_STATIC_DLL_CALLBACK(intN)
01312 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
01313 {
01314     ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
01315 
01316     if (!sfe->mark)
01317         return HT_ENUMERATE_REMOVE;
01318     sfe->mark = JS_FALSE;
01319     return HT_ENUMERATE_NEXT;
01320 }
01321 
01322 void
01323 js_SweepScriptFilenames(JSRuntime *rt)
01324 {
01325     if (!rt->scriptFilenameTable)
01326         return;
01327 
01328     JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
01329                                  js_script_filename_sweeper,
01330                                  rt);
01331 #ifdef DEBUG_notme
01332 #ifdef DEBUG_SFTBL
01333     printf("script filename table savings so far: %u\n", sftbl_savings);
01334 #endif
01335 #endif
01336 }
01337 
01338 JSScript *
01339 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes)
01340 {
01341     JSScript *script;
01342 
01343     /* Round up source note count to align script->trynotes for its type. */
01344     if (ntrynotes)
01345         nsrcnotes += JSTRYNOTE_ALIGNMASK;
01346     script = (JSScript *) JS_malloc(cx,
01347                                     sizeof(JSScript) +
01348                                     length * sizeof(jsbytecode) +
01349                                     nsrcnotes * sizeof(jssrcnote) +
01350                                     ntrynotes * sizeof(JSTryNote));
01351     if (!script)
01352         return NULL;
01353     memset(script, 0, sizeof(JSScript));
01354     script->code = script->main = (jsbytecode *)(script + 1);
01355     script->length = length;
01356     script->version = cx->version;
01357     if (ntrynotes) {
01358         script->trynotes = (JSTryNote *)
01359                            ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
01360                             ~(jsword)JSTRYNOTE_ALIGNMASK);
01361         memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
01362     }
01363     return script;
01364 }
01365 
01366 JS_FRIEND_API(JSScript *)
01367 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
01368 {
01369     uint32 mainLength, prologLength, nsrcnotes, ntrynotes;
01370     JSScript *script;
01371     const char *filename;
01372 
01373     mainLength = CG_OFFSET(cg);
01374     prologLength = CG_PROLOG_OFFSET(cg);
01375     CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
01376     CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes);
01377     script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes);
01378     if (!script)
01379         return NULL;
01380 
01381     /* Now that we have script, error control flow must go to label bad. */
01382     script->main += prologLength;
01383     memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
01384     memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
01385     script->numGlobalVars = cg->treeContext.numGlobalVars;
01386     if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList))
01387         goto bad;
01388 
01389     filename = cg->filename;
01390     if (filename) {
01391         script->filename = js_SaveScriptFilename(cx, filename);
01392         if (!script->filename)
01393             goto bad;
01394     }
01395     script->lineno = cg->firstLine;
01396     script->depth = cg->maxStackDepth;
01397     if (cg->principals) {
01398         script->principals = cg->principals;
01399         JSPRINCIPALS_HOLD(cx, script->principals);
01400     }
01401 
01402     if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script)))
01403         goto bad;
01404     if (script->trynotes)
01405         js_FinishTakingTryNotes(cx, cg, script->trynotes);
01406 
01407     /*
01408      * We initialize fun->u.script to be the script constructed above
01409      * so that the debugger has a valid FUN_SCRIPT(fun).
01410      */
01411     if (fun) {
01412         JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
01413         fun->u.i.script = script;
01414         if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
01415             fun->flags |= JSFUN_HEAVYWEIGHT;
01416     }
01417 
01418     /* Tell the debugger about this compiled script. */
01419     js_CallNewScriptHook(cx, script, fun);
01420     return script;
01421 
01422 bad:
01423     js_DestroyScript(cx, script);
01424     return NULL;
01425 }
01426 
01427 JS_FRIEND_API(void)
01428 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
01429 {
01430     JSRuntime *rt;
01431     JSNewScriptHook hook;
01432 
01433     rt = cx->runtime;
01434     hook = rt->newScriptHook;
01435     if (hook) {
01436         JS_KEEP_ATOMS(rt);
01437         hook(cx, script->filename, script->lineno, script, fun,
01438              rt->newScriptHookData);
01439         JS_UNKEEP_ATOMS(rt);
01440     }
01441 }
01442 
01443 JS_FRIEND_API(void)
01444 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
01445 {
01446     JSRuntime *rt;
01447     JSDestroyScriptHook hook;
01448 
01449     rt = cx->runtime;
01450     hook = rt->destroyScriptHook;
01451     if (hook)
01452         hook(cx, script, rt->destroyScriptHookData);
01453 }
01454 
01455 void
01456 js_DestroyScript(JSContext *cx, JSScript *script)
01457 {
01458     js_CallDestroyScriptHook(cx, script);
01459 
01460     JS_ClearScriptTraps(cx, script);
01461     js_FreeAtomMap(cx, &script->atomMap);
01462     if (script->principals)
01463         JSPRINCIPALS_DROP(cx, script->principals);
01464     if (JS_GSN_CACHE(cx).script == script)
01465         JS_CLEAR_GSN_CACHE(cx);
01466     JS_free(cx, script);
01467 }
01468 
01469 void
01470 js_MarkScript(JSContext *cx, JSScript *script)
01471 {
01472     JSAtomMap *map;
01473     uintN i, length;
01474     JSAtom **vector;
01475 
01476     map = &script->atomMap;
01477     length = map->length;
01478     vector = map->vector;
01479     for (i = 0; i < length; i++)
01480         GC_MARK_ATOM(cx, vector[i]);
01481 
01482     if (script->filename)
01483         js_MarkScriptFilename(script->filename);
01484 }
01485 
01486 typedef struct GSNCacheEntry {
01487     JSDHashEntryHdr     hdr;
01488     jsbytecode          *pc;
01489     jssrcnote           *sn;
01490 } GSNCacheEntry;
01491 
01492 #define GSN_CACHE_THRESHOLD     100
01493 
01494 jssrcnote *
01495 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
01496 {
01497     ptrdiff_t target, offset;
01498     GSNCacheEntry *entry;
01499     jssrcnote *sn, *result;
01500     uintN nsrcnotes;
01501 
01502 
01503     target = PTRDIFF(pc, script->code, jsbytecode);
01504     if ((uint32)target >= script->length)
01505         return NULL;
01506 
01507     if (JS_GSN_CACHE(cx).script == script) {
01508         JS_METER_GSN_CACHE(cx, hits);
01509         entry = (GSNCacheEntry *)
01510                 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
01511                                      JS_DHASH_LOOKUP);
01512         return entry->sn;
01513     }
01514 
01515     JS_METER_GSN_CACHE(cx, misses);
01516     offset = 0;
01517     for (sn = SCRIPT_NOTES(script); ; sn = SN_NEXT(sn)) {
01518         if (SN_IS_TERMINATOR(sn)) {
01519             result = NULL;
01520             break;
01521         }
01522         offset += SN_DELTA(sn);
01523         if (offset == target && SN_IS_GETTABLE(sn)) {
01524             result = sn;
01525             break;
01526         }
01527     }
01528 
01529     if (JS_GSN_CACHE(cx).script != script &&
01530         script->length >= GSN_CACHE_THRESHOLD) {
01531         JS_CLEAR_GSN_CACHE(cx);
01532         nsrcnotes = 0;
01533         for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
01534              sn = SN_NEXT(sn)) {
01535             if (SN_IS_GETTABLE(sn))
01536                 ++nsrcnotes;
01537         }
01538         if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
01539                                NULL, sizeof(GSNCacheEntry), nsrcnotes)) {
01540             JS_GSN_CACHE(cx).table.ops = NULL;
01541         } else {
01542             pc = script->code;
01543             for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn);
01544                  sn = SN_NEXT(sn)) {
01545                 pc += SN_DELTA(sn);
01546                 if (SN_IS_GETTABLE(sn)) {
01547                     entry = (GSNCacheEntry *)
01548                             JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
01549                                                  JS_DHASH_ADD);
01550                     entry->pc = pc;
01551                     entry->sn = sn;
01552                 }
01553             }
01554             JS_GSN_CACHE(cx).script = script;
01555             JS_METER_GSN_CACHE(cx, fills);
01556         }
01557     }
01558 
01559     return result;
01560 }
01561 
01562 uintN
01563 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
01564 {
01565     JSAtom *atom;
01566     JSFunction *fun;
01567     uintN lineno;
01568     ptrdiff_t offset, target;
01569     jssrcnote *sn;
01570     JSSrcNoteType type;
01571 
01572     /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
01573     if (!pc)
01574         return 0;
01575 
01576     /*
01577      * Special case: function definition needs no line number note because
01578      * the function's script contains its starting line number.
01579      */
01580     if (*pc == JSOP_DEFFUN ||
01581         (*pc == JSOP_LITOPX && pc[1 + LITERAL_INDEX_LEN] == JSOP_DEFFUN)) {
01582         atom = js_GetAtom(cx, &script->atomMap,
01583                           (*pc == JSOP_DEFFUN)
01584                           ? GET_ATOM_INDEX(pc)
01585                           : GET_LITERAL_INDEX(pc));
01586         fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
01587         JS_ASSERT(FUN_INTERPRETED(fun));
01588         return fun->u.i.script->lineno;
01589     }
01590 
01591     /*
01592      * General case: walk through source notes accumulating their deltas,
01593      * keeping track of line-number notes, until we pass the note for pc's
01594      * offset within script->code.
01595      */
01596     lineno = script->lineno;
01597     offset = 0;
01598     target = PTRDIFF(pc, script->code, jsbytecode);
01599     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
01600         offset += SN_DELTA(sn);
01601         type = (JSSrcNoteType) SN_TYPE(sn);
01602         if (type == SRC_SETLINE) {
01603             if (offset <= target)
01604                 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
01605         } else if (type == SRC_NEWLINE) {
01606             if (offset <= target)
01607                 lineno++;
01608         }
01609         if (offset > target)
01610             break;
01611     }
01612     return lineno;
01613 }
01614 
01615 /* The line number limit is the same as the jssrcnote offset limit. */
01616 #define SN_LINE_LIMIT   (SN_3BYTE_OFFSET_FLAG << 16)
01617 
01618 jsbytecode *
01619 js_LineNumberToPC(JSScript *script, uintN target)
01620 {
01621     ptrdiff_t offset, best;
01622     uintN lineno, bestdiff, diff;
01623     jssrcnote *sn;
01624     JSSrcNoteType type;
01625 
01626     offset = 0;
01627     best = -1;
01628     lineno = script->lineno;
01629     bestdiff = SN_LINE_LIMIT;
01630     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
01631         if (lineno == target)
01632             goto out;
01633         if (lineno > target) {
01634             diff = lineno - target;
01635             if (diff < bestdiff) {
01636                 bestdiff = diff;
01637                 best = offset;
01638             }
01639         }
01640         offset += SN_DELTA(sn);
01641         type = (JSSrcNoteType) SN_TYPE(sn);
01642         if (type == SRC_SETLINE) {
01643             lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
01644         } else if (type == SRC_NEWLINE) {
01645             lineno++;
01646         }
01647     }
01648     if (best >= 0)
01649         offset = best;
01650 out:
01651     return script->code + offset;
01652 }
01653 
01654 JS_FRIEND_API(uintN)
01655 js_GetScriptLineExtent(JSScript *script)
01656 {
01657     uintN lineno;
01658     jssrcnote *sn;
01659     JSSrcNoteType type;
01660 
01661     lineno = script->lineno;
01662     for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
01663         type = (JSSrcNoteType) SN_TYPE(sn);
01664         if (type == SRC_SETLINE) {
01665             lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
01666         } else if (type == SRC_NEWLINE) {
01667             lineno++;
01668         }
01669     }
01670     return 1 + lineno - script->lineno;
01671 }
01672 
01673 #if JS_HAS_GENERATORS
01674 
01675 jsbytecode *
01676 js_FindFinallyHandler(JSScript *script, jsbytecode *pc)
01677 {
01678     JSTryNote *tn;
01679     ptrdiff_t off;
01680     JSOp op2;
01681 
01682     tn = script->trynotes;
01683     if (!tn)
01684         return NULL;
01685 
01686     off = pc - script->main;
01687     if (off < 0)
01688         return NULL;
01689 
01690     JS_ASSERT(tn->catchStart != 0);
01691     do {
01692         if ((jsuword)(off - tn->start) < (jsuword)tn->length) {
01693             /*
01694              * We have a handler: is it the finally one, or a catch handler?
01695              *
01696              * Catch bytecode begins with:   JSOP_SETSP JSOP_ENTERBLOCK
01697              * Finally bytecode begins with: JSOP_SETSP JSOP_(GOSUB|EXCEPTION)
01698              */
01699             pc = script->main + tn->catchStart;
01700             JS_ASSERT(*pc == JSOP_SETSP);
01701             op2 = pc[JSOP_SETSP_LENGTH];
01702             if (op2 != JSOP_ENTERBLOCK) {
01703                 JS_ASSERT(op2 == JSOP_GOSUB || op2 == JSOP_EXCEPTION);
01704                 return pc;
01705             }
01706         }
01707     } while ((++tn)->catchStart != 0);
01708     return NULL;
01709 }
01710 
01711 #endif