Bug 588735 - Mirror glass caption buttons for rtl windows. r=roc, a=blocking-betaN.
[mozilla-central.git] / js / src / jsscript.cpp
blob4ce4f64bec920b85d04642701ae3f2e54901f94a
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=78:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
39 * ***** END LICENSE BLOCK ***** */
42 * JS script operations.
44 #include <string.h>
45 #include "jstypes.h"
46 #include "jsstdint.h"
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jsprf.h"
49 #include "jsapi.h"
50 #include "jsatom.h"
51 #include "jscntxt.h"
52 #include "jsversion.h"
53 #include "jsdbgapi.h"
54 #include "jsemit.h"
55 #include "jsfun.h"
56 #include "jsinterp.h"
57 #include "jslock.h"
58 #include "jsnum.h"
59 #include "jsopcode.h"
60 #include "jsparse.h"
61 #include "jsscope.h"
62 #include "jsscript.h"
63 #include "jstracer.h"
64 #if JS_HAS_XDR
65 #include "jsxdrapi.h"
66 #endif
68 #include "jsobjinlines.h"
69 #include "jsscriptinlines.h"
71 using namespace js;
73 static const jsbytecode emptyScriptCode[] = {JSOP_STOP, SRC_NULL};
75 /* static */ const JSScript JSScript::emptyScriptConst = {
76 const_cast<jsbytecode*>(emptyScriptCode),
77 1, JSVERSION_DEFAULT, 0, 0, 0, 0, 0, 0, true, false, false, false, true,
78 const_cast<jsbytecode*>(emptyScriptCode),
79 {0, NULL}, NULL, 0, 0, 0, NULL
82 #if JS_HAS_XDR
84 JSBool
85 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, bool needMutableScript,
86 JSBool *hasMagic)
88 JSContext *cx;
89 JSScript *script, *oldscript;
90 JSBool ok;
91 jsbytecode *code;
92 uint32 length, lineno, nslots, magic;
93 uint32 natoms, nsrcnotes, ntrynotes, nobjects, nupvars, nregexps, nconsts, i;
94 uint32 prologLength, version;
95 JSPrincipals *principals;
96 uint32 encodeable;
97 JSBool filenameWasSaved;
98 jssrcnote *notes, *sn;
99 JSSecurityCallbacks *callbacks;
101 cx = xdr->cx;
102 script = *scriptp;
103 nsrcnotes = ntrynotes = natoms = nobjects = nupvars = nregexps = nconsts = 0;
104 filenameWasSaved = JS_FALSE;
105 notes = NULL;
107 if (xdr->mode == JSXDR_ENCODE)
108 magic = JSXDR_MAGIC_SCRIPT_CURRENT;
109 if (!JS_XDRUint32(xdr, &magic))
110 return JS_FALSE;
111 if (magic != JSXDR_MAGIC_SCRIPT_CURRENT) {
112 /* We do not provide binary compatibility with older scripts. */
113 if (!hasMagic) {
114 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
115 JSMSG_BAD_SCRIPT_MAGIC);
116 return JS_FALSE;
118 *hasMagic = JS_FALSE;
119 return JS_TRUE;
121 if (hasMagic)
122 *hasMagic = JS_TRUE;
125 * Since the shortest possible script has JSOP_STOP as its only bytecode,
126 * encode only the length 0 for the emptyScript singleton, and return the
127 * emptyScript instead of a new script when decoding a script of length 0.
129 if (xdr->mode == JSXDR_ENCODE)
130 length = (script == JSScript::emptyScript()) ? 0 : script->length;
131 if (!JS_XDRUint32(xdr, &length))
132 return JS_FALSE;
133 if (length == 0) {
134 if (xdr->mode == JSXDR_ENCODE) {
135 JS_ASSERT(*scriptp == JSScript::emptyScript());
136 return JS_TRUE;
139 /* Decoding: check whether we need a mutable empty script. */
140 if (cx->debugHooks->newScriptHook)
141 needMutableScript = true;
142 if (needMutableScript) {
144 * We need a mutable empty script but the encoder serialized only
145 * the shorthand (0 length word) for us. Make a new mutable empty
146 * script here and return it immediately.
148 script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0, 0);
149 if (!script)
150 return JS_FALSE;
152 script->version = JSVERSION_DEFAULT;
153 script->noScriptRval = true;
154 script->code[0] = JSOP_STOP;
155 script->code[1] = SRC_NULL;
156 *scriptp = script;
157 return JS_TRUE;
160 *scriptp = JSScript::emptyScript();
161 return JS_TRUE;
164 if (xdr->mode == JSXDR_ENCODE) {
165 prologLength = script->main - script->code;
166 JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
167 version = (uint32)script->version | (script->nfixed << 16);
168 lineno = (uint32)script->lineno;
169 nslots = (uint32)script->nslots;
170 nslots = (uint32)((script->staticLevel << 16) | script->nslots);
171 natoms = (uint32)script->atomMap.length;
173 /* Count the srcnotes, keeping notes pointing at the first one. */
174 notes = script->notes();
175 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
176 continue;
177 nsrcnotes = sn - notes;
178 nsrcnotes++; /* room for the terminator */
180 if (script->objectsOffset != 0)
181 nobjects = script->objects()->length;
182 if (script->upvarsOffset != 0)
183 nupvars = script->upvars()->length;
184 if (script->regexpsOffset != 0)
185 nregexps = script->regexps()->length;
186 if (script->trynotesOffset != 0)
187 ntrynotes = script->trynotes()->length;
188 if (script->constOffset != 0)
189 nconsts = script->consts()->length;
192 if (!JS_XDRUint32(xdr, &prologLength))
193 return JS_FALSE;
194 if (!JS_XDRUint32(xdr, &version))
195 return JS_FALSE;
198 * To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
199 * and trynote counts early.
201 if (!JS_XDRUint32(xdr, &natoms))
202 return JS_FALSE;
203 if (!JS_XDRUint32(xdr, &nsrcnotes))
204 return JS_FALSE;
205 if (!JS_XDRUint32(xdr, &ntrynotes))
206 return JS_FALSE;
207 if (!JS_XDRUint32(xdr, &nobjects))
208 return JS_FALSE;
209 if (!JS_XDRUint32(xdr, &nupvars))
210 return JS_FALSE;
211 if (!JS_XDRUint32(xdr, &nregexps))
212 return JS_FALSE;
213 if (!JS_XDRUint32(xdr, &nconsts))
214 return JS_FALSE;
216 AutoScriptRooter tvr(cx, NULL);
218 if (xdr->mode == JSXDR_DECODE) {
219 script = js_NewScript(cx, length, nsrcnotes, natoms, nobjects, nupvars,
220 nregexps, ntrynotes, nconsts);
221 if (!script)
222 return JS_FALSE;
224 script->main += prologLength;
225 script->version = JSVersion(version & 0xffff);
226 script->nfixed = uint16(version >> 16);
228 /* If we know nsrcnotes, we allocated space for notes in script. */
229 notes = script->notes();
230 *scriptp = script;
231 tvr.setScript(script);
235 * Control hereafter must goto error on failure, in order for the
236 * DECODE case to destroy script.
238 oldscript = xdr->script;
239 code = script->code;
240 if (xdr->mode == JSXDR_ENCODE) {
241 code = js_UntrapScriptCode(cx, script);
242 if (!code)
243 goto error;
246 xdr->script = script;
247 ok = JS_XDRBytes(xdr, (char *) code, length * sizeof(jsbytecode));
249 if (code != script->code)
250 cx->free(code);
252 if (!ok)
253 goto error;
255 if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
256 !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
257 !JS_XDRUint32(xdr, &lineno) ||
258 !JS_XDRUint32(xdr, &nslots)) {
259 goto error;
262 callbacks = JS_GetSecurityCallbacks(cx);
263 if (xdr->mode == JSXDR_ENCODE) {
264 principals = script->principals;
265 encodeable = callbacks && callbacks->principalsTranscoder;
266 if (!JS_XDRUint32(xdr, &encodeable))
267 goto error;
268 if (encodeable &&
269 !callbacks->principalsTranscoder(xdr, &principals)) {
270 goto error;
272 } else {
273 if (!JS_XDRUint32(xdr, &encodeable))
274 goto error;
275 if (encodeable) {
276 if (!(callbacks && callbacks->principalsTranscoder)) {
277 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
278 JSMSG_CANT_DECODE_PRINCIPALS);
279 goto error;
281 if (!callbacks->principalsTranscoder(xdr, &principals))
282 goto error;
283 script->principals = principals;
287 if (xdr->mode == JSXDR_DECODE) {
288 const char *filename = script->filename;
289 if (filename) {
290 filename = js_SaveScriptFilename(cx, filename);
291 if (!filename)
292 goto error;
293 cx->free((void *) script->filename);
294 script->filename = filename;
295 filenameWasSaved = JS_TRUE;
297 script->lineno = (uintN)lineno;
298 script->nslots = (uint16)nslots;
299 script->staticLevel = (uint16)(nslots >> 16);
302 for (i = 0; i != natoms; ++i) {
303 if (!js_XDRAtom(xdr, &script->atomMap.vector[i]))
304 goto error;
308 * Here looping from 0-to-length to xdr objects is essential. It ensures
309 * that block objects from the script->objects array will be written and
310 * restored in the outer-to-inner order. js_XDRBlockObject relies on this
311 * to restore the parent chain.
313 for (i = 0; i != nobjects; ++i) {
314 JSObject **objp = &script->objects()->vector[i];
315 uint32 isBlock;
316 if (xdr->mode == JSXDR_ENCODE) {
317 Class *clasp = (*objp)->getClass();
318 JS_ASSERT(clasp == &js_FunctionClass ||
319 clasp == &js_BlockClass);
320 isBlock = (clasp == &js_BlockClass) ? 1 : 0;
322 if (!JS_XDRUint32(xdr, &isBlock))
323 goto error;
324 if (isBlock == 0) {
325 if (!js_XDRFunctionObject(xdr, objp))
326 goto error;
327 } else {
328 JS_ASSERT(isBlock == 1);
329 if (!js_XDRBlockObject(xdr, objp))
330 goto error;
333 for (i = 0; i != nupvars; ++i) {
334 if (!JS_XDRUint32(xdr, reinterpret_cast<uint32 *>(&script->upvars()->vector[i])))
335 goto error;
337 for (i = 0; i != nregexps; ++i) {
338 if (!js_XDRRegExpObject(xdr, &script->regexps()->vector[i]))
339 goto error;
342 if (ntrynotes != 0) {
344 * We combine tn->kind and tn->stackDepth when serializing as XDR is not
345 * efficient when serializing small integer types.
347 JSTryNote *tn, *tnfirst;
348 uint32 kindAndDepth;
349 JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8));
350 JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16));
352 tnfirst = script->trynotes()->vector;
353 JS_ASSERT(script->trynotes()->length == ntrynotes);
354 tn = tnfirst + ntrynotes;
355 do {
356 --tn;
357 if (xdr->mode == JSXDR_ENCODE) {
358 kindAndDepth = ((uint32)tn->kind << 16)
359 | (uint32)tn->stackDepth;
361 if (!JS_XDRUint32(xdr, &kindAndDepth) ||
362 !JS_XDRUint32(xdr, &tn->start) ||
363 !JS_XDRUint32(xdr, &tn->length)) {
364 goto error;
366 if (xdr->mode == JSXDR_DECODE) {
367 tn->kind = (uint8)(kindAndDepth >> 16);
368 tn->stackDepth = (uint16)kindAndDepth;
370 } while (tn != tnfirst);
373 for (i = 0; i != nconsts; ++i) {
374 if (!JS_XDRValue(xdr, Jsvalify(&script->consts()->vector[i])))
375 goto error;
378 xdr->script = oldscript;
379 return JS_TRUE;
381 error:
382 if (xdr->mode == JSXDR_DECODE) {
383 if (script->filename && !filenameWasSaved) {
384 cx->free((void *) script->filename);
385 script->filename = NULL;
387 js_DestroyScript(cx, script);
388 *scriptp = NULL;
390 xdr->script = oldscript;
391 return JS_FALSE;
394 #endif /* JS_HAS_XDR */
396 static void
397 script_finalize(JSContext *cx, JSObject *obj)
399 JSScript *script = (JSScript *) obj->getPrivate();
400 if (script)
401 js_DestroyScript(cx, script);
404 static void
405 script_trace(JSTracer *trc, JSObject *obj)
407 JSScript *script = (JSScript *) obj->getPrivate();
408 if (script)
409 js_TraceScript(trc, script);
412 Class js_ScriptClass = {
413 "Script",
414 JSCLASS_HAS_PRIVATE |
415 JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
416 PropertyStub, /* addProperty */
417 PropertyStub, /* delProperty */
418 PropertyStub, /* getProperty */
419 PropertyStub, /* setProperty */
420 EnumerateStub,
421 ResolveStub,
422 ConvertStub,
423 script_finalize,
424 NULL, /* reserved0 */
425 NULL, /* checkAccess */
426 NULL, /* call */
427 NULL, /* construct */
428 NULL, /* xdrObject */
429 NULL, /* hasInstance */
430 JS_CLASS_TRACE(script_trace)
434 * Shared script filename management.
436 static int
437 js_compare_strings(const void *k1, const void *k2)
439 return strcmp((const char *) k1, (const char *) k2) == 0;
442 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
443 typedef struct ScriptFilenameEntry {
444 JSHashEntry *next; /* hash chain linkage */
445 JSHashNumber keyHash; /* key hash function result */
446 const void *key; /* ptr to filename, below */
447 uint32 flags; /* user-defined filename prefix flags */
448 JSPackedBool mark; /* GC mark flag */
449 char filename[3]; /* two or more bytes, NUL-terminated */
450 } ScriptFilenameEntry;
452 static void *
453 js_alloc_table_space(void *priv, size_t size)
455 return js_malloc(size);
458 static void
459 js_free_table_space(void *priv, void *item, size_t size)
461 js_free(item);
464 static JSHashEntry *
465 js_alloc_sftbl_entry(void *priv, const void *key)
467 size_t nbytes = offsetof(ScriptFilenameEntry, filename) +
468 strlen((const char *) key) + 1;
470 return (JSHashEntry *) js_malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
473 static void
474 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
476 if (flag != HT_FREE_ENTRY)
477 return;
478 js_free(he);
481 static JSHashAllocOps sftbl_alloc_ops = {
482 js_alloc_table_space, js_free_table_space,
483 js_alloc_sftbl_entry, js_free_sftbl_entry
486 static void
487 FinishRuntimeScriptState(JSRuntime *rt)
489 if (rt->scriptFilenameTable) {
490 JS_HashTableDestroy(rt->scriptFilenameTable);
491 rt->scriptFilenameTable = NULL;
493 #ifdef JS_THREADSAFE
494 if (rt->scriptFilenameTableLock) {
495 JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
496 rt->scriptFilenameTableLock = NULL;
498 #endif
501 JSBool
502 js_InitRuntimeScriptState(JSRuntime *rt)
504 #ifdef JS_THREADSAFE
505 JS_ASSERT(!rt->scriptFilenameTableLock);
506 rt->scriptFilenameTableLock = JS_NEW_LOCK();
507 if (!rt->scriptFilenameTableLock)
508 return JS_FALSE;
509 #endif
510 JS_ASSERT(!rt->scriptFilenameTable);
511 rt->scriptFilenameTable =
512 JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
513 &sftbl_alloc_ops, NULL);
514 if (!rt->scriptFilenameTable) {
515 FinishRuntimeScriptState(rt); /* free lock if threadsafe */
516 return JS_FALSE;
518 JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
519 return JS_TRUE;
522 typedef struct ScriptFilenamePrefix {
523 JSCList links; /* circular list linkage for easy deletion */
524 const char *name; /* pointer to pinned ScriptFilenameEntry string */
525 size_t length; /* prefix string length, precomputed */
526 uint32 flags; /* user-defined flags to inherit from this prefix */
527 } ScriptFilenamePrefix;
529 void
530 js_FreeRuntimeScriptState(JSRuntime *rt)
532 if (!rt->scriptFilenameTable)
533 return;
535 while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
536 ScriptFilenamePrefix *sfp = (ScriptFilenamePrefix *)
537 rt->scriptFilenamePrefixes.next;
538 JS_REMOVE_LINK(&sfp->links);
539 js_free(sfp);
541 FinishRuntimeScriptState(rt);
544 #ifdef DEBUG_brendan
545 #define DEBUG_SFTBL
546 #endif
547 #ifdef DEBUG_SFTBL
548 size_t sftbl_savings = 0;
549 #endif
551 static ScriptFilenameEntry *
552 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
554 JSHashTable *table;
555 JSHashNumber hash;
556 JSHashEntry **hep;
557 ScriptFilenameEntry *sfe;
558 size_t length;
559 JSCList *head, *link;
560 ScriptFilenamePrefix *sfp;
562 table = rt->scriptFilenameTable;
563 hash = JS_HashString(filename);
564 hep = JS_HashTableRawLookup(table, hash, filename);
565 sfe = (ScriptFilenameEntry *) *hep;
566 #ifdef DEBUG_SFTBL
567 if (sfe)
568 sftbl_savings += strlen(sfe->filename);
569 #endif
571 if (!sfe) {
572 sfe = (ScriptFilenameEntry *)
573 JS_HashTableRawAdd(table, hep, hash, filename, NULL);
574 if (!sfe)
575 return NULL;
576 sfe->key = strcpy(sfe->filename, filename);
577 sfe->flags = 0;
578 sfe->mark = JS_FALSE;
581 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
582 if (flags != 0) {
583 /* Search in case filename was saved already; we must be idempotent. */
584 sfp = NULL;
585 length = strlen(filename);
586 for (head = link = &rt->scriptFilenamePrefixes;
587 link->next != head;
588 link = link->next) {
589 /* Lag link behind sfp to insert in non-increasing length order. */
590 sfp = (ScriptFilenamePrefix *) link->next;
591 if (!strcmp(sfp->name, filename))
592 break;
593 if (sfp->length <= length) {
594 sfp = NULL;
595 break;
597 sfp = NULL;
600 if (!sfp) {
601 /* No such prefix: add one now. */
602 sfp = (ScriptFilenamePrefix *) js_malloc(sizeof(ScriptFilenamePrefix));
603 if (!sfp)
604 return NULL;
605 JS_INSERT_AFTER(&sfp->links, link);
606 sfp->name = sfe->filename;
607 sfp->length = length;
608 sfp->flags = 0;
612 * Accumulate flags in both sfe and sfp: sfe for later access from the
613 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
614 * filename entries can inherit by prefix.
616 sfe->flags |= flags;
617 sfp->flags |= flags;
620 #ifdef DEBUG
621 if (rt->functionMeterFilename) {
622 size_t len = strlen(sfe->filename);
623 if (len >= sizeof rt->lastScriptFilename)
624 len = sizeof rt->lastScriptFilename - 1;
625 memcpy(rt->lastScriptFilename, sfe->filename, len);
626 rt->lastScriptFilename[len] = '\0';
628 #endif
630 return sfe;
633 const char *
634 js_SaveScriptFilename(JSContext *cx, const char *filename)
636 JSRuntime *rt;
637 ScriptFilenameEntry *sfe;
638 JSCList *head, *link;
639 ScriptFilenamePrefix *sfp;
641 rt = cx->runtime;
642 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
643 sfe = SaveScriptFilename(rt, filename, 0);
644 if (!sfe) {
645 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
646 JS_ReportOutOfMemory(cx);
647 return NULL;
651 * Try to inherit flags by prefix. We assume there won't be more than a
652 * few (dozen! ;-) prefixes, so linear search is tolerable.
653 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
655 for (head = &rt->scriptFilenamePrefixes, link = head->next;
656 link != head;
657 link = link->next) {
658 sfp = (ScriptFilenamePrefix *) link;
659 if (!strncmp(sfp->name, filename, sfp->length)) {
660 sfe->flags |= sfp->flags;
661 break;
664 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
665 return sfe->filename;
668 const char *
669 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
671 ScriptFilenameEntry *sfe;
673 /* This may be called very early, via the jsdbgapi.h entry point. */
674 if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
675 return NULL;
677 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
678 sfe = SaveScriptFilename(rt, filename, flags);
679 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
680 if (!sfe)
681 return NULL;
683 return sfe->filename;
687 * Back up from a saved filename by its offset within its hash table entry.
689 #define FILENAME_TO_SFE(fn) \
690 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
693 * The sfe->key member, redundant given sfe->filename but required by the old
694 * jshash.c code, here gives us a useful sanity check. This assertion will
695 * very likely botch if someone tries to mark a string that wasn't allocated
696 * as an sfe->filename.
698 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
700 uint32
701 js_GetScriptFilenameFlags(const char *filename)
703 ScriptFilenameEntry *sfe;
705 sfe = FILENAME_TO_SFE(filename);
706 ASSERT_VALID_SFE(sfe);
707 return sfe->flags;
710 void
711 js_MarkScriptFilename(const char *filename)
713 ScriptFilenameEntry *sfe;
715 sfe = FILENAME_TO_SFE(filename);
716 ASSERT_VALID_SFE(sfe);
717 sfe->mark = JS_TRUE;
720 static intN
721 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
723 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
725 sfe->mark = JS_TRUE;
726 return HT_ENUMERATE_NEXT;
729 void
730 js_MarkScriptFilenames(JSRuntime *rt)
732 JSCList *head, *link;
733 ScriptFilenamePrefix *sfp;
735 if (!rt->scriptFilenameTable)
736 return;
738 if (rt->gcKeepAtoms) {
739 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
740 js_script_filename_marker,
741 rt);
743 for (head = &rt->scriptFilenamePrefixes, link = head->next;
744 link != head;
745 link = link->next) {
746 sfp = (ScriptFilenamePrefix *) link;
747 js_MarkScriptFilename(sfp->name);
751 static intN
752 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
754 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
756 if (!sfe->mark)
757 return HT_ENUMERATE_REMOVE;
758 sfe->mark = JS_FALSE;
759 return HT_ENUMERATE_NEXT;
762 void
763 js_SweepScriptFilenames(JSRuntime *rt)
765 if (!rt->scriptFilenameTable)
766 return;
769 * JS_HashTableEnumerateEntries shrinks the table if many entries are
770 * removed preventing wasting memory on a too sparse table.
772 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
773 js_script_filename_sweeper,
774 rt);
775 #ifdef DEBUG_notme
776 #ifdef DEBUG_SFTBL
777 printf("script filename table savings so far: %u\n", sftbl_savings);
778 #endif
779 #endif
783 * JSScript data structures memory alignment:
785 * JSScript
786 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
787 * use script->objects() to access it.
788 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
789 * use script->regexps() to access it.
790 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
791 * != 0, use script->trynotes() to access it.
792 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
793 * JSScript.atomMap.vector if any.
794 * JSObject *o[] array of script->objects()->length objects if any
795 * pointed by script->objects()->vector.
796 * JSObject *r[] array of script->regexps()->length regexps if any
797 * pointed by script->regexps()->vector.
798 * JSTryNote t[] array of script->trynotes()->length try notes if any
799 * pointed by script->trynotes()->vector.
800 * jsbytecode b[] script bytecode pointed by JSScript.code.
801 * jssrcnote s[] script source notes, use script->notes() to access it
803 * The alignment avoids gaps between entries as alignment requirement for each
804 * subsequent structure or array is the same or divides the alignment
805 * requirement for the previous one.
807 * The followings asserts checks that assuming that the alignment requirement
808 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
809 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
811 JS_STATIC_ASSERT(sizeof(JSScript) % sizeof(void *) == 0);
812 JS_STATIC_ASSERT(sizeof(JSObjectArray) % sizeof(void *) == 0);
813 JS_STATIC_ASSERT(sizeof(JSTryNoteArray) == sizeof(JSObjectArray));
814 JS_STATIC_ASSERT(sizeof(JSAtom *) == sizeof(JSObject *));
815 JS_STATIC_ASSERT(sizeof(JSObject *) % sizeof(uint32) == 0);
816 JS_STATIC_ASSERT(sizeof(JSTryNote) == 3 * sizeof(uint32));
817 JS_STATIC_ASSERT(sizeof(uint32) % sizeof(jsbytecode) == 0);
818 JS_STATIC_ASSERT(sizeof(jsbytecode) % sizeof(jssrcnote) == 0);
821 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
822 * sufficient.
824 JS_STATIC_ASSERT(sizeof(JSScript) + 2 * sizeof(JSObjectArray) +
825 sizeof(JSUpvarArray) < JS_BIT(8));
827 JSScript *
828 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms,
829 uint32 nobjects, uint32 nupvars, uint32 nregexps,
830 uint32 ntrynotes, uint32 nconsts)
832 size_t size, vectorSize;
833 JSScript *script;
834 uint8 *cursor;
835 unsigned constPadding = 0;
837 size = sizeof(JSScript) +
838 sizeof(JSAtom *) * natoms;
840 if (nobjects != 0)
841 size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
842 if (nupvars != 0)
843 size += sizeof(JSUpvarArray) + nupvars * sizeof(uint32);
844 if (nregexps != 0)
845 size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
846 if (ntrynotes != 0)
847 size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
849 if (nconsts != 0) {
850 size += sizeof(JSConstArray);
852 * Calculate padding assuming that consts go after the other arrays,
853 * but before the bytecode and source notes.
855 constPadding = (8 - (size % 8)) % 8;
856 size += constPadding + nconsts * sizeof(Value);
859 size += length * sizeof(jsbytecode) +
860 nsrcnotes * sizeof(jssrcnote);
862 script = (JSScript *) cx->malloc(size);
863 if (!script)
864 return NULL;
865 PodZero(script);
866 script->length = length;
867 script->version = cx->version;
869 cursor = (uint8 *)script + sizeof(JSScript);
870 if (nobjects != 0) {
871 script->objectsOffset = (uint8)(cursor - (uint8 *)script);
872 cursor += sizeof(JSObjectArray);
874 if (nupvars != 0) {
875 script->upvarsOffset = (uint8)(cursor - (uint8 *)script);
876 cursor += sizeof(JSUpvarArray);
878 if (nregexps != 0) {
879 script->regexpsOffset = (uint8)(cursor - (uint8 *)script);
880 cursor += sizeof(JSObjectArray);
882 if (ntrynotes != 0) {
883 script->trynotesOffset = (uint8)(cursor - (uint8 *)script);
884 cursor += sizeof(JSTryNoteArray);
886 if (nconsts != 0) {
887 script->constOffset = (uint8)(cursor - (uint8 *)script);
888 cursor += sizeof(JSConstArray);
891 if (natoms != 0) {
892 script->atomMap.length = natoms;
893 script->atomMap.vector = (JSAtom **)cursor;
894 vectorSize = natoms * sizeof(script->atomMap.vector[0]);
897 * Clear object map's vector so the GC tracing can run when not yet
898 * all atoms are copied to the array.
900 memset(cursor, 0, vectorSize);
901 cursor += vectorSize;
904 if (nobjects != 0) {
905 script->objects()->length = nobjects;
906 script->objects()->vector = (JSObject **)cursor;
907 vectorSize = nobjects * sizeof(script->objects()->vector[0]);
908 memset(cursor, 0, vectorSize);
909 cursor += vectorSize;
912 if (nregexps != 0) {
913 script->regexps()->length = nregexps;
914 script->regexps()->vector = (JSObject **)cursor;
915 vectorSize = nregexps * sizeof(script->regexps()->vector[0]);
916 memset(cursor, 0, vectorSize);
917 cursor += vectorSize;
920 if (ntrynotes != 0) {
921 script->trynotes()->length = ntrynotes;
922 script->trynotes()->vector = (JSTryNote *)cursor;
923 vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
924 #ifdef DEBUG
925 memset(cursor, 0, vectorSize);
926 #endif
927 cursor += vectorSize;
931 * NB: We allocate the vector of uint32 upvar cookies after all vectors of
932 * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
934 if (nupvars != 0) {
935 script->upvars()->length = nupvars;
936 script->upvars()->vector = reinterpret_cast<UpvarCookie *>(cursor);
937 vectorSize = nupvars * sizeof(script->upvars()->vector[0]);
938 memset(cursor, 0, vectorSize);
939 cursor += vectorSize;
942 /* Must go after other arrays; see constPadding definition. */
943 if (nconsts != 0) {
944 cursor += constPadding;
945 script->consts()->length = nconsts;
946 script->consts()->vector = (Value *)cursor;
947 JS_ASSERT((size_t)cursor % sizeof(double) == 0);
948 vectorSize = nconsts * sizeof(script->consts()->vector[0]);
949 memset(cursor, 0, vectorSize);
950 cursor += vectorSize;
953 script->code = script->main = (jsbytecode *)cursor;
954 JS_ASSERT(cursor +
955 length * sizeof(jsbytecode) +
956 nsrcnotes * sizeof(jssrcnote) ==
957 (uint8 *)script + size);
959 #ifdef CHECK_SCRIPT_OWNER
960 script->owner = cx->thread;
961 #endif
962 return script;
965 JSScript *
966 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
968 uint32 mainLength, prologLength, nsrcnotes, nfixed;
969 JSScript *script;
970 const char *filename;
971 JSFunction *fun;
973 /* The counts of indexed things must be checked during code generation. */
974 JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
975 JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
976 JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
978 mainLength = CG_OFFSET(cg);
979 prologLength = CG_PROLOG_OFFSET(cg);
981 if (prologLength + mainLength <= 3) {
983 * Check very short scripts to see whether they are "empty" and return
984 * the const empty-script singleton if so. We are deliberately flexible
985 * about whether JSOP_TRACE is in the prolog.
987 jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg);
989 if (JSOp(*pc) == JSOP_TRACE) {
990 ++pc;
991 if (pc == CG_PROLOG_BASE(cg) + prologLength)
992 pc = CG_BASE(cg);
994 if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE)
995 ++pc;
997 if (JSOp(*pc) == JSOP_STOP &&
998 !cx->debugHooks->newScriptHook &&
999 !(cg->flags & TCF_NEED_MUTABLE_SCRIPT))
1002 * We can probably use the immutable empty script singleton, just
1003 * two hard cases (nupvars != 0, strict mode code) may stand in our
1004 * way.
1006 JSScript *empty = JSScript::emptyScript();
1008 if (cg->flags & TCF_IN_FUNCTION) {
1009 fun = cg->fun;
1010 JS_ASSERT(fun->isInterpreted() && !FUN_SCRIPT(fun));
1011 if (cg->flags & TCF_STRICT_MODE_CODE) {
1013 * We can't use a script singleton for empty strict mode
1014 * functions because they have poison-pill caller and
1015 * arguments properties:
1017 * function strict() { "use strict"; }
1018 * strict.caller; // calls [[ThrowTypeError]] function
1020 goto skip_empty;
1022 if (fun->u.i.nupvars != 0) {
1024 * FIXME: upvar uses that were all optimized away may leave
1025 * fun->u.i.nupvars non-zero, and since that count is added
1026 * into fun->countLocalNames() in order to discriminate the
1027 * fun->u.i.names union, we cannot force fun->u.i.nupvars
1028 * to 0 to match JSScript::emptyScript()->upvars()->length.
1029 * So we skip the empty script optimization.
1031 * Fixing this requires the compiler to track upvar uses as
1032 * it analyzes and optimizes closures, and subsequently as
1033 * the emitter performs useless expression elimination.
1035 goto skip_empty;
1037 js_FreezeLocalNames(cx, fun);
1038 fun->u.i.script = empty;
1041 JS_RUNTIME_METER(cx->runtime, liveEmptyScripts);
1042 JS_RUNTIME_METER(cx->runtime, totalEmptyScripts);
1043 return empty;
1047 skip_empty:
1048 CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1049 script = js_NewScript(cx, prologLength + mainLength, nsrcnotes,
1050 cg->atomList.count, cg->objectList.length,
1051 cg->upvarList.count, cg->regexpList.length,
1052 cg->ntrynotes, cg->constList.length());
1053 if (!script)
1054 return NULL;
1056 /* Now that we have script, error control flow must go to label bad. */
1057 script->main += prologLength;
1058 memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1059 memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1060 nfixed = (cg->flags & TCF_IN_FUNCTION)
1061 ? cg->fun->u.i.nvars
1062 : cg->ngvars + cg->sharpSlots();
1063 JS_ASSERT(nfixed < SLOTNO_LIMIT);
1064 script->nfixed = (uint16) nfixed;
1065 js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
1067 filename = cg->parser->tokenStream.getFilename();
1068 if (filename) {
1069 script->filename = js_SaveScriptFilename(cx, filename);
1070 if (!script->filename)
1071 goto bad;
1073 script->lineno = cg->firstLine;
1074 if (script->nfixed + cg->maxStackDepth >= JS_BIT(16)) {
1075 ReportCompileErrorNumber(cx, CG_TS(cg), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET, "script");
1076 goto bad;
1078 script->nslots = script->nfixed + cg->maxStackDepth;
1079 script->staticLevel = uint16(cg->staticLevel);
1080 script->principals = cg->parser->principals;
1081 if (script->principals)
1082 JSPRINCIPALS_HOLD(cx, script->principals);
1084 if (!js_FinishTakingSrcNotes(cx, cg, script->notes()))
1085 goto bad;
1086 if (cg->ntrynotes != 0)
1087 js_FinishTakingTryNotes(cg, script->trynotes());
1088 if (cg->objectList.length != 0)
1089 cg->objectList.finish(script->objects());
1090 if (cg->regexpList.length != 0)
1091 cg->regexpList.finish(script->regexps());
1092 if (cg->constList.length() != 0)
1093 cg->constList.finish(script->consts());
1094 if (cg->flags & TCF_NO_SCRIPT_RVAL)
1095 script->noScriptRval = true;
1096 if (cg->hasSharps())
1097 script->hasSharps = true;
1098 if (cg->flags & TCF_STRICT_MODE_CODE)
1099 script->strictModeCode = true;
1101 if (cg->upvarList.count != 0) {
1102 JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
1103 memcpy(script->upvars()->vector, cg->upvarMap.vector,
1104 cg->upvarList.count * sizeof(uint32));
1105 cg->upvarList.clear();
1106 cx->free(cg->upvarMap.vector);
1107 cg->upvarMap.vector = NULL;
1111 * We initialize fun->u.script to be the script constructed above
1112 * so that the debugger has a valid FUN_SCRIPT(fun).
1114 fun = NULL;
1115 if (cg->flags & TCF_IN_FUNCTION) {
1116 fun = cg->fun;
1117 JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
1118 if (script->upvarsOffset != 0)
1119 JS_ASSERT(script->upvars()->length == fun->u.i.nupvars);
1120 else
1121 fun->u.i.nupvars = 0;
1123 js_FreezeLocalNames(cx, fun);
1124 fun->u.i.script = script;
1125 #ifdef CHECK_SCRIPT_OWNER
1126 script->owner = NULL;
1127 #endif
1128 if (cg->flags & TCF_FUN_HEAVYWEIGHT)
1129 fun->flags |= JSFUN_HEAVYWEIGHT;
1132 /* Tell the debugger about this compiled script. */
1133 js_CallNewScriptHook(cx, script, fun);
1134 JS_RUNTIME_METER(cx->runtime, liveScripts);
1135 JS_RUNTIME_METER(cx->runtime, totalScripts);
1136 return script;
1138 bad:
1139 js_DestroyScript(cx, script);
1140 return NULL;
1143 JS_FRIEND_API(void)
1144 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1146 JS_ASSERT(script != JSScript::emptyScript());
1148 JSNewScriptHook hook;
1150 hook = cx->debugHooks->newScriptHook;
1151 if (hook) {
1152 AutoKeepAtoms keep(cx->runtime);
1153 hook(cx, script->filename, script->lineno, script, fun,
1154 cx->debugHooks->newScriptHookData);
1158 JS_FRIEND_API(void)
1159 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1161 JS_ASSERT(script != JSScript::emptyScript());
1163 JSDestroyScriptHook hook;
1165 hook = cx->debugHooks->destroyScriptHook;
1166 if (hook)
1167 hook(cx, script, cx->debugHooks->destroyScriptHookData);
1170 void
1171 js_DestroyScript(JSContext *cx, JSScript *script)
1173 if (script == JSScript::emptyScript()) {
1174 JS_RUNTIME_UNMETER(cx->runtime, liveEmptyScripts);
1175 return;
1178 js_CallDestroyScriptHook(cx, script);
1179 JS_ClearScriptTraps(cx, script);
1181 if (script->principals)
1182 JSPRINCIPALS_DROP(cx, script->principals);
1184 if (JS_GSN_CACHE(cx).code == script->code)
1185 JS_PURGE_GSN_CACHE(cx);
1188 * Worry about purging the property cache and any compiled traces related
1189 * to its bytecode if this script is being destroyed from JS_DestroyScript
1190 * or equivalent according to a mandatory "New/Destroy" protocol.
1192 * The GC purges all property caches when regenerating shapes upon shape
1193 * generator overflow, so no need in that event to purge just the entries
1194 * for this script.
1196 * The GC purges trace-JITted code on every GC activation, not just when
1197 * regenerating shapes, so we don't have to purge fragments if the GC is
1198 * currently running.
1200 * JS_THREADSAFE note: The code below purges only the current thread's
1201 * property cache, so a script not owned by a function or object, which
1202 * hands off lifetime management for that script to the GC, must be used by
1203 * only one thread over its lifetime.
1205 * This should be an API-compatible change, since a script is never safe
1206 * against premature GC if shared among threads without a rooted object
1207 * wrapping it to protect the script's mapped atoms against GC. We use
1208 * script->owner to enforce this requirement via assertions.
1210 #ifdef CHECK_SCRIPT_OWNER
1211 JS_ASSERT_IF(cx->runtime->gcRunning, !script->owner);
1212 #endif
1214 /* FIXME: bug 506341; would like to do this only if regenerating shapes. */
1215 if (!cx->runtime->gcRunning) {
1216 JSStackFrame *fp = js_GetTopStackFrame(cx);
1218 if (!(fp && (fp->flags & JSFRAME_EVAL))) {
1219 JS_PROPERTY_CACHE(cx).purgeForScript(script);
1221 #ifdef CHECK_SCRIPT_OWNER
1222 JS_ASSERT(script->owner == cx->thread);
1223 #endif
1227 #ifdef JS_TRACER
1228 PurgeScriptFragments(cx, script);
1229 #endif
1231 cx->free(script);
1233 JS_RUNTIME_UNMETER(cx->runtime, liveScripts);
1236 void
1237 js_TraceScript(JSTracer *trc, JSScript *script)
1239 JSAtomMap *map = &script->atomMap;
1240 MarkAtomRange(trc, map->length, map->vector, "atomMap");
1242 if (script->objectsOffset != 0) {
1243 JSObjectArray *objarray = script->objects();
1244 uintN i = objarray->length;
1245 do {
1246 --i;
1247 if (objarray->vector[i]) {
1248 JS_SET_TRACING_INDEX(trc, "objects", i);
1249 Mark(trc, objarray->vector[i], JSTRACE_OBJECT);
1251 } while (i != 0);
1254 if (script->regexpsOffset != 0) {
1255 JSObjectArray *objarray = script->regexps();
1256 uintN i = objarray->length;
1257 do {
1258 --i;
1259 if (objarray->vector[i]) {
1260 JS_SET_TRACING_INDEX(trc, "regexps", i);
1261 Mark(trc, objarray->vector[i], JSTRACE_OBJECT);
1263 } while (i != 0);
1266 if (script->constOffset != 0) {
1267 JSConstArray *constarray = script->consts();
1268 MarkValueRange(trc, constarray->length, constarray->vector, "consts");
1271 if (script->u.object) {
1272 JS_SET_TRACING_NAME(trc, "object");
1273 Mark(trc, script->u.object, JSTRACE_OBJECT);
1276 if (IS_GC_MARKING_TRACER(trc) && script->filename)
1277 js_MarkScriptFilename(script->filename);
1280 JSBool
1281 js_NewScriptObject(JSContext *cx, JSScript *script)
1283 AutoScriptRooter root(cx, script);
1285 JS_ASSERT(!script->u.object);
1286 JS_ASSERT(script != JSScript::emptyScript());
1288 JSObject *obj = NewNonFunction<WithProto::Class>(cx, &js_ScriptClass, NULL, NULL);
1289 if (!obj)
1290 return JS_FALSE;
1291 obj->setPrivate(script);
1292 script->u.object = obj;
1295 * Clear the object's proto, to avoid entraining stuff. Once we no longer use the parent
1296 * for security checks, then we can clear the parent, too.
1298 obj->clearProto();
1300 #ifdef CHECK_SCRIPT_OWNER
1301 script->owner = NULL;
1302 #endif
1304 return JS_TRUE;
1307 typedef struct GSNCacheEntry {
1308 JSDHashEntryHdr hdr;
1309 jsbytecode *pc;
1310 jssrcnote *sn;
1311 } GSNCacheEntry;
1313 #define GSN_CACHE_THRESHOLD 100
1315 void
1316 js_PurgeGSNCache(JSGSNCache *cache)
1318 cache->code = NULL;
1319 if (cache->table.ops) {
1320 JS_DHashTableFinish(&cache->table);
1321 cache->table.ops = NULL;
1323 GSN_CACHE_METER(cache, purges);
1326 jssrcnote *
1327 js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1329 ptrdiff_t target, offset;
1330 GSNCacheEntry *entry;
1331 jssrcnote *sn, *result;
1332 uintN nsrcnotes;
1335 target = pc - script->code;
1336 if ((uint32)target >= script->length)
1337 return NULL;
1339 if (JS_GSN_CACHE(cx).code == script->code) {
1340 JS_METER_GSN_CACHE(cx, hits);
1341 entry = (GSNCacheEntry *)
1342 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1343 JS_DHASH_LOOKUP);
1344 return entry->sn;
1347 JS_METER_GSN_CACHE(cx, misses);
1348 offset = 0;
1349 for (sn = script->notes(); ; sn = SN_NEXT(sn)) {
1350 if (SN_IS_TERMINATOR(sn)) {
1351 result = NULL;
1352 break;
1354 offset += SN_DELTA(sn);
1355 if (offset == target && SN_IS_GETTABLE(sn)) {
1356 result = sn;
1357 break;
1361 if (JS_GSN_CACHE(cx).code != script->code &&
1362 script->length >= GSN_CACHE_THRESHOLD) {
1363 JS_PURGE_GSN_CACHE(cx);
1364 nsrcnotes = 0;
1365 for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
1366 sn = SN_NEXT(sn)) {
1367 if (SN_IS_GETTABLE(sn))
1368 ++nsrcnotes;
1370 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx).table, JS_DHashGetStubOps(),
1371 NULL, sizeof(GSNCacheEntry),
1372 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes))) {
1373 JS_GSN_CACHE(cx).table.ops = NULL;
1374 } else {
1375 pc = script->code;
1376 for (sn = script->notes(); !SN_IS_TERMINATOR(sn);
1377 sn = SN_NEXT(sn)) {
1378 pc += SN_DELTA(sn);
1379 if (SN_IS_GETTABLE(sn)) {
1380 entry = (GSNCacheEntry *)
1381 JS_DHashTableOperate(&JS_GSN_CACHE(cx).table, pc,
1382 JS_DHASH_ADD);
1383 entry->pc = pc;
1384 entry->sn = sn;
1387 JS_GSN_CACHE(cx).code = script->code;
1388 JS_METER_GSN_CACHE(cx, fills);
1392 return result;
1395 uintN
1396 js_FramePCToLineNumber(JSContext *cx, JSStackFrame *fp)
1398 return js_PCToLineNumber(cx, fp->getScript(),
1399 fp->hasIMacroPC() ? fp->getIMacroPC() : fp->pc(cx));
1402 uintN
1403 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1405 JSOp op;
1406 JSFunction *fun;
1407 uintN lineno;
1408 ptrdiff_t offset, target;
1409 jssrcnote *sn;
1410 JSSrcNoteType type;
1412 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1413 if (!pc)
1414 return 0;
1417 * Special case: function definition needs no line number note because
1418 * the function's script contains its starting line number.
1420 op = js_GetOpcode(cx, script, pc);
1421 if (js_CodeSpec[op].format & JOF_INDEXBASE)
1422 pc += js_CodeSpec[op].length;
1423 if (*pc == JSOP_DEFFUN) {
1424 GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun);
1425 return fun->u.i.script->lineno;
1429 * General case: walk through source notes accumulating their deltas,
1430 * keeping track of line-number notes, until we pass the note for pc's
1431 * offset within script->code.
1433 lineno = script->lineno;
1434 offset = 0;
1435 target = pc - script->code;
1436 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1437 offset += SN_DELTA(sn);
1438 type = (JSSrcNoteType) SN_TYPE(sn);
1439 if (type == SRC_SETLINE) {
1440 if (offset <= target)
1441 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1442 } else if (type == SRC_NEWLINE) {
1443 if (offset <= target)
1444 lineno++;
1446 if (offset > target)
1447 break;
1449 return lineno;
1452 /* The line number limit is the same as the jssrcnote offset limit. */
1453 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1455 jsbytecode *
1456 js_LineNumberToPC(JSScript *script, uintN target)
1458 ptrdiff_t offset, best;
1459 uintN lineno, bestdiff, diff;
1460 jssrcnote *sn;
1461 JSSrcNoteType type;
1463 offset = 0;
1464 best = -1;
1465 lineno = script->lineno;
1466 bestdiff = SN_LINE_LIMIT;
1467 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1469 * Exact-match only if offset is not in the prolog; otherwise use
1470 * nearest greater-or-equal line number match.
1472 if (lineno == target && script->code + offset >= script->main)
1473 goto out;
1474 if (lineno >= target) {
1475 diff = lineno - target;
1476 if (diff < bestdiff) {
1477 bestdiff = diff;
1478 best = offset;
1481 offset += SN_DELTA(sn);
1482 type = (JSSrcNoteType) SN_TYPE(sn);
1483 if (type == SRC_SETLINE) {
1484 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1485 } else if (type == SRC_NEWLINE) {
1486 lineno++;
1489 if (best >= 0)
1490 offset = best;
1491 out:
1492 return script->code + offset;
1495 JS_FRIEND_API(uintN)
1496 js_GetScriptLineExtent(JSScript *script)
1498 uintN lineno;
1499 jssrcnote *sn;
1500 JSSrcNoteType type;
1502 lineno = script->lineno;
1503 for (sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1504 type = (JSSrcNoteType) SN_TYPE(sn);
1505 if (type == SRC_SETLINE) {
1506 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1507 } else if (type == SRC_NEWLINE) {
1508 lineno++;
1511 return 1 + lineno - script->lineno;