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
17 * The Original Code is Mozilla Communicator client code, released
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.
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.
47 #include "jsutil.h" /* Added by JSIFY */
52 #include "jsversion.h"
68 #include "jsscriptinlines.h"
72 const uint32 JSSLOT_EXEC_DEPTH
= JSSLOT_PRIVATE
+ 1;
73 const uint32 JSSCRIPT_RESERVED_SLOTS
= 1;
75 static const jsbytecode emptyScriptCode
[] = {JSOP_STOP
, SRC_NULL
};
77 /* static */ const JSScript
JSScript::emptyScriptConst
= {
78 const_cast<jsbytecode
*>(emptyScriptCode
),
79 1, JSVERSION_DEFAULT
, 0, 0, 0, 0, 0, true, false, false, false,
80 const_cast<jsbytecode
*>(emptyScriptCode
),
81 {0, NULL
}, NULL
, 0, 0, 0, NULL
87 js_XDRScript(JSXDRState
*xdr
, JSScript
**scriptp
, bool needMutableScript
,
91 JSScript
*script
, *oldscript
;
94 uint32 length
, lineno
, nslots
, magic
;
95 uint32 natoms
, nsrcnotes
, ntrynotes
, nobjects
, nupvars
, nregexps
, i
;
96 uint32 prologLength
, version
;
97 JSPrincipals
*principals
;
99 JSBool filenameWasSaved
;
100 jssrcnote
*notes
, *sn
;
101 JSSecurityCallbacks
*callbacks
;
105 nsrcnotes
= ntrynotes
= natoms
= nobjects
= nupvars
= nregexps
= 0;
106 filenameWasSaved
= JS_FALSE
;
109 if (xdr
->mode
== JSXDR_ENCODE
)
110 magic
= JSXDR_MAGIC_SCRIPT_CURRENT
;
111 if (!JS_XDRUint32(xdr
, &magic
))
113 if (magic
!= JSXDR_MAGIC_SCRIPT_CURRENT
) {
114 /* We do not provide binary compatibility with older scripts. */
116 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
117 JSMSG_BAD_SCRIPT_MAGIC
);
120 *hasMagic
= JS_FALSE
;
127 * Since the shortest possible script has JSOP_STOP as its only bytecode,
128 * encode only the length 0 for the emptyScript singleton, and return the
129 * emptyScript instead of a new script when decoding a script of length 0.
131 if (xdr
->mode
== JSXDR_ENCODE
)
132 length
= (script
== JSScript::emptyScript()) ? 0 : script
->length
;
133 if (!JS_XDRUint32(xdr
, &length
))
136 if (xdr
->mode
== JSXDR_ENCODE
) {
137 JS_ASSERT(*scriptp
== JSScript::emptyScript());
141 /* Decoding: check whether we need a mutable empty script. */
142 if (cx
->debugHooks
->newScriptHook
)
143 needMutableScript
= true;
144 if (needMutableScript
) {
146 * We need a mutable empty script but the encoder serialized only
147 * the shorthand (0 length word) for us. Make a new mutable empty
148 * script here and return it immediately.
150 script
= js_NewScript(cx
, 1, 1, 0, 0, 0, 0, 0);
154 script
->version
= JSVERSION_DEFAULT
;
155 script
->noScriptRval
= true;
156 script
->code
[0] = JSOP_STOP
;
157 script
->code
[1] = SRC_NULL
;
162 *scriptp
= JSScript::emptyScript();
166 if (xdr
->mode
== JSXDR_ENCODE
) {
167 prologLength
= script
->main
- script
->code
;
168 JS_ASSERT((int16
)script
->version
!= JSVERSION_UNKNOWN
);
169 version
= (uint32
)script
->version
| (script
->nfixed
<< 16);
170 lineno
= (uint32
)script
->lineno
;
171 nslots
= (uint32
)script
->nslots
;
172 nslots
= (uint32
)((script
->staticLevel
<< 16) | script
->nslots
);
173 natoms
= (uint32
)script
->atomMap
.length
;
175 /* Count the srcnotes, keeping notes pointing at the first one. */
176 notes
= script
->notes();
177 for (sn
= notes
; !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
))
179 nsrcnotes
= sn
- notes
;
180 nsrcnotes
++; /* room for the terminator */
182 if (script
->objectsOffset
!= 0)
183 nobjects
= script
->objects()->length
;
184 if (script
->upvarsOffset
!= 0)
185 nupvars
= script
->upvars()->length
;
186 if (script
->regexpsOffset
!= 0)
187 nregexps
= script
->regexps()->length
;
188 if (script
->trynotesOffset
!= 0)
189 ntrynotes
= script
->trynotes()->length
;
192 if (!JS_XDRUint32(xdr
, &prologLength
))
194 if (!JS_XDRUint32(xdr
, &version
))
198 * To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
199 * and trynote counts early.
201 if (!JS_XDRUint32(xdr
, &natoms
))
203 if (!JS_XDRUint32(xdr
, &nsrcnotes
))
205 if (!JS_XDRUint32(xdr
, &ntrynotes
))
207 if (!JS_XDRUint32(xdr
, &nobjects
))
209 if (!JS_XDRUint32(xdr
, &nupvars
))
211 if (!JS_XDRUint32(xdr
, &nregexps
))
214 AutoScriptRooter
tvr(cx
, NULL
);
216 if (xdr
->mode
== JSXDR_DECODE
) {
217 script
= js_NewScript(cx
, length
, nsrcnotes
, natoms
, nobjects
, nupvars
,
218 nregexps
, ntrynotes
);
222 script
->main
+= prologLength
;
223 script
->version
= JSVersion(version
& 0xffff);
224 script
->nfixed
= uint16(version
>> 16);
226 /* If we know nsrcnotes, we allocated space for notes in script. */
227 notes
= script
->notes();
229 tvr
.setScript(script
);
233 * Control hereafter must goto error on failure, in order for the
234 * DECODE case to destroy script.
236 oldscript
= xdr
->script
;
238 if (xdr
->mode
== JSXDR_ENCODE
) {
239 code
= js_UntrapScriptCode(cx
, script
);
244 xdr
->script
= script
;
245 ok
= JS_XDRBytes(xdr
, (char *) code
, length
* sizeof(jsbytecode
));
247 if (code
!= script
->code
)
253 if (!JS_XDRBytes(xdr
, (char *)notes
, nsrcnotes
* sizeof(jssrcnote
)) ||
254 !JS_XDRCStringOrNull(xdr
, (char **)&script
->filename
) ||
255 !JS_XDRUint32(xdr
, &lineno
) ||
256 !JS_XDRUint32(xdr
, &nslots
)) {
260 callbacks
= JS_GetSecurityCallbacks(cx
);
261 if (xdr
->mode
== JSXDR_ENCODE
) {
262 principals
= script
->principals
;
263 encodeable
= callbacks
&& callbacks
->principalsTranscoder
;
264 if (!JS_XDRUint32(xdr
, &encodeable
))
267 !callbacks
->principalsTranscoder(xdr
, &principals
)) {
271 if (!JS_XDRUint32(xdr
, &encodeable
))
274 if (!(callbacks
&& callbacks
->principalsTranscoder
)) {
275 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
276 JSMSG_CANT_DECODE_PRINCIPALS
);
279 if (!callbacks
->principalsTranscoder(xdr
, &principals
))
281 script
->principals
= principals
;
285 if (xdr
->mode
== JSXDR_DECODE
) {
286 const char *filename
= script
->filename
;
288 filename
= js_SaveScriptFilename(cx
, filename
);
291 cx
->free((void *) script
->filename
);
292 script
->filename
= filename
;
293 filenameWasSaved
= JS_TRUE
;
295 script
->lineno
= (uintN
)lineno
;
296 script
->nslots
= (uint16
)nslots
;
297 script
->staticLevel
= (uint16
)(nslots
>> 16);
300 for (i
= 0; i
!= natoms
; ++i
) {
301 if (!js_XDRAtom(xdr
, &script
->atomMap
.vector
[i
]))
306 * Here looping from 0-to-length to xdr objects is essential. It ensures
307 * that block objects from the script->objects array will be written and
308 * restored in the outer-to-inner order. js_XDRBlockObject relies on this
309 * to restore the parent chain.
311 for (i
= 0; i
!= nobjects
; ++i
) {
312 JSObject
**objp
= &script
->objects()->vector
[i
];
314 if (xdr
->mode
== JSXDR_ENCODE
) {
315 JSClass
*clasp
= STOBJ_GET_CLASS(*objp
);
316 JS_ASSERT(clasp
== &js_FunctionClass
||
317 clasp
== &js_BlockClass
);
318 isBlock
= (clasp
== &js_BlockClass
) ? 1 : 0;
320 if (!JS_XDRUint32(xdr
, &isBlock
))
323 if (!js_XDRFunctionObject(xdr
, objp
))
326 JS_ASSERT(isBlock
== 1);
327 if (!js_XDRBlockObject(xdr
, objp
))
331 for (i
= 0; i
!= nupvars
; ++i
) {
332 if (!JS_XDRUint32(xdr
, &script
->upvars()->vector
[i
]))
335 for (i
= 0; i
!= nregexps
; ++i
) {
336 if (!js_XDRRegExpObject(xdr
, &script
->regexps()->vector
[i
]))
340 if (ntrynotes
!= 0) {
342 * We combine tn->kind and tn->stackDepth when serializing as XDR is not
343 * efficient when serializing small integer types.
345 JSTryNote
*tn
, *tnfirst
;
347 JS_STATIC_ASSERT(sizeof(tn
->kind
) == sizeof(uint8
));
348 JS_STATIC_ASSERT(sizeof(tn
->stackDepth
) == sizeof(uint16
));
350 tnfirst
= script
->trynotes()->vector
;
351 JS_ASSERT(script
->trynotes()->length
== ntrynotes
);
352 tn
= tnfirst
+ ntrynotes
;
355 if (xdr
->mode
== JSXDR_ENCODE
) {
356 kindAndDepth
= ((uint32
)tn
->kind
<< 16)
357 | (uint32
)tn
->stackDepth
;
359 if (!JS_XDRUint32(xdr
, &kindAndDepth
) ||
360 !JS_XDRUint32(xdr
, &tn
->start
) ||
361 !JS_XDRUint32(xdr
, &tn
->length
)) {
364 if (xdr
->mode
== JSXDR_DECODE
) {
365 tn
->kind
= (uint8
)(kindAndDepth
>> 16);
366 tn
->stackDepth
= (uint16
)kindAndDepth
;
368 } while (tn
!= tnfirst
);
371 xdr
->script
= oldscript
;
375 if (xdr
->mode
== JSXDR_DECODE
) {
376 if (script
->filename
&& !filenameWasSaved
) {
377 cx
->free((void *) script
->filename
);
378 script
->filename
= NULL
;
380 js_DestroyScript(cx
, script
);
383 xdr
->script
= oldscript
;
387 #endif /* JS_HAS_XDR */
390 script_finalize(JSContext
*cx
, JSObject
*obj
)
392 JSScript
*script
= (JSScript
*) obj
->getPrivate();
394 js_DestroyScript(cx
, script
);
398 script_trace(JSTracer
*trc
, JSObject
*obj
)
400 JSScript
*script
= (JSScript
*) obj
->getPrivate();
402 js_TraceScript(trc
, script
);
405 JSClass js_ScriptClass
= {
407 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(JSSCRIPT_RESERVED_SLOTS
) |
408 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
409 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
410 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, script_finalize
,
411 NULL
, NULL
, NULL
, NULL
,/*XXXbe xdr*/
412 NULL
, NULL
, JS_CLASS_TRACE(script_trace
), NULL
416 * Shared script filename management.
419 js_compare_strings(const void *k1
, const void *k2
)
421 return strcmp((const char *) k1
, (const char *) k2
) == 0;
424 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
425 typedef struct ScriptFilenameEntry
{
426 JSHashEntry
*next
; /* hash chain linkage */
427 JSHashNumber keyHash
; /* key hash function result */
428 const void *key
; /* ptr to filename, below */
429 uint32 flags
; /* user-defined filename prefix flags */
430 JSPackedBool mark
; /* GC mark flag */
431 char filename
[3]; /* two or more bytes, NUL-terminated */
432 } ScriptFilenameEntry
;
435 js_alloc_table_space(void *priv
, size_t size
)
437 return js_malloc(size
);
441 js_free_table_space(void *priv
, void *item
, size_t size
)
447 js_alloc_sftbl_entry(void *priv
, const void *key
)
449 size_t nbytes
= offsetof(ScriptFilenameEntry
, filename
) +
450 strlen((const char *) key
) + 1;
452 return (JSHashEntry
*) js_malloc(JS_MAX(nbytes
, sizeof(JSHashEntry
)));
456 js_free_sftbl_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
458 if (flag
!= HT_FREE_ENTRY
)
463 static JSHashAllocOps sftbl_alloc_ops
= {
464 js_alloc_table_space
, js_free_table_space
,
465 js_alloc_sftbl_entry
, js_free_sftbl_entry
469 FinishRuntimeScriptState(JSRuntime
*rt
)
471 if (rt
->scriptFilenameTable
) {
472 JS_HashTableDestroy(rt
->scriptFilenameTable
);
473 rt
->scriptFilenameTable
= NULL
;
476 if (rt
->scriptFilenameTableLock
) {
477 JS_DESTROY_LOCK(rt
->scriptFilenameTableLock
);
478 rt
->scriptFilenameTableLock
= NULL
;
484 js_InitRuntimeScriptState(JSRuntime
*rt
)
487 JS_ASSERT(!rt
->scriptFilenameTableLock
);
488 rt
->scriptFilenameTableLock
= JS_NEW_LOCK();
489 if (!rt
->scriptFilenameTableLock
)
492 JS_ASSERT(!rt
->scriptFilenameTable
);
493 rt
->scriptFilenameTable
=
494 JS_NewHashTable(16, JS_HashString
, js_compare_strings
, NULL
,
495 &sftbl_alloc_ops
, NULL
);
496 if (!rt
->scriptFilenameTable
) {
497 FinishRuntimeScriptState(rt
); /* free lock if threadsafe */
500 JS_INIT_CLIST(&rt
->scriptFilenamePrefixes
);
504 typedef struct ScriptFilenamePrefix
{
505 JSCList links
; /* circular list linkage for easy deletion */
506 const char *name
; /* pointer to pinned ScriptFilenameEntry string */
507 size_t length
; /* prefix string length, precomputed */
508 uint32 flags
; /* user-defined flags to inherit from this prefix */
509 } ScriptFilenamePrefix
;
512 js_FreeRuntimeScriptState(JSRuntime
*rt
)
514 if (!rt
->scriptFilenameTable
)
517 while (!JS_CLIST_IS_EMPTY(&rt
->scriptFilenamePrefixes
)) {
518 ScriptFilenamePrefix
*sfp
= (ScriptFilenamePrefix
*)
519 rt
->scriptFilenamePrefixes
.next
;
520 JS_REMOVE_LINK(&sfp
->links
);
523 FinishRuntimeScriptState(rt
);
530 size_t sftbl_savings
= 0;
533 static ScriptFilenameEntry
*
534 SaveScriptFilename(JSRuntime
*rt
, const char *filename
, uint32 flags
)
539 ScriptFilenameEntry
*sfe
;
541 JSCList
*head
, *link
;
542 ScriptFilenamePrefix
*sfp
;
544 table
= rt
->scriptFilenameTable
;
545 hash
= JS_HashString(filename
);
546 hep
= JS_HashTableRawLookup(table
, hash
, filename
);
547 sfe
= (ScriptFilenameEntry
*) *hep
;
550 sftbl_savings
+= strlen(sfe
->filename
);
554 sfe
= (ScriptFilenameEntry
*)
555 JS_HashTableRawAdd(table
, hep
, hash
, filename
, NULL
);
558 sfe
->key
= strcpy(sfe
->filename
, filename
);
560 sfe
->mark
= JS_FALSE
;
563 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
565 /* Search in case filename was saved already; we must be idempotent. */
567 length
= strlen(filename
);
568 for (head
= link
= &rt
->scriptFilenamePrefixes
;
571 /* Lag link behind sfp to insert in non-increasing length order. */
572 sfp
= (ScriptFilenamePrefix
*) link
->next
;
573 if (!strcmp(sfp
->name
, filename
))
575 if (sfp
->length
<= length
) {
583 /* No such prefix: add one now. */
584 sfp
= (ScriptFilenamePrefix
*) js_malloc(sizeof(ScriptFilenamePrefix
));
587 JS_INSERT_AFTER(&sfp
->links
, link
);
588 sfp
->name
= sfe
->filename
;
589 sfp
->length
= length
;
594 * Accumulate flags in both sfe and sfp: sfe for later access from the
595 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
596 * filename entries can inherit by prefix.
602 #ifdef JS_FUNCTION_METERING
603 size_t len
= strlen(sfe
->filename
);
604 if (len
>= sizeof rt
->lastScriptFilename
)
605 len
= sizeof rt
->lastScriptFilename
- 1;
606 memcpy(rt
->lastScriptFilename
, sfe
->filename
, len
);
607 rt
->lastScriptFilename
[len
] = '\0';
614 js_SaveScriptFilename(JSContext
*cx
, const char *filename
)
617 ScriptFilenameEntry
*sfe
;
618 JSCList
*head
, *link
;
619 ScriptFilenamePrefix
*sfp
;
622 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
623 sfe
= SaveScriptFilename(rt
, filename
, 0);
625 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
626 JS_ReportOutOfMemory(cx
);
631 * Try to inherit flags by prefix. We assume there won't be more than a
632 * few (dozen! ;-) prefixes, so linear search is tolerable.
633 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
635 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
638 sfp
= (ScriptFilenamePrefix
*) link
;
639 if (!strncmp(sfp
->name
, filename
, sfp
->length
)) {
640 sfe
->flags
|= sfp
->flags
;
644 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
645 return sfe
->filename
;
649 js_SaveScriptFilenameRT(JSRuntime
*rt
, const char *filename
, uint32 flags
)
651 ScriptFilenameEntry
*sfe
;
653 /* This may be called very early, via the jsdbgapi.h entry point. */
654 if (!rt
->scriptFilenameTable
&& !js_InitRuntimeScriptState(rt
))
657 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
658 sfe
= SaveScriptFilename(rt
, filename
, flags
);
659 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
663 return sfe
->filename
;
667 * Back up from a saved filename by its offset within its hash table entry.
669 #define FILENAME_TO_SFE(fn) \
670 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
673 * The sfe->key member, redundant given sfe->filename but required by the old
674 * jshash.c code, here gives us a useful sanity check. This assertion will
675 * very likely botch if someone tries to mark a string that wasn't allocated
676 * as an sfe->filename.
678 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
681 js_GetScriptFilenameFlags(const char *filename
)
683 ScriptFilenameEntry
*sfe
;
685 sfe
= FILENAME_TO_SFE(filename
);
686 ASSERT_VALID_SFE(sfe
);
691 js_MarkScriptFilename(const char *filename
)
693 ScriptFilenameEntry
*sfe
;
695 sfe
= FILENAME_TO_SFE(filename
);
696 ASSERT_VALID_SFE(sfe
);
701 js_script_filename_marker(JSHashEntry
*he
, intN i
, void *arg
)
703 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
706 return HT_ENUMERATE_NEXT
;
710 js_MarkScriptFilenames(JSRuntime
*rt
, JSBool keepAtoms
)
712 JSCList
*head
, *link
;
713 ScriptFilenamePrefix
*sfp
;
715 if (!rt
->scriptFilenameTable
)
719 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
720 js_script_filename_marker
,
723 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
726 sfp
= (ScriptFilenamePrefix
*) link
;
727 js_MarkScriptFilename(sfp
->name
);
732 js_script_filename_sweeper(JSHashEntry
*he
, intN i
, void *arg
)
734 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
737 return HT_ENUMERATE_REMOVE
;
738 sfe
->mark
= JS_FALSE
;
739 return HT_ENUMERATE_NEXT
;
743 js_SweepScriptFilenames(JSRuntime
*rt
)
745 if (!rt
->scriptFilenameTable
)
749 * JS_HashTableEnumerateEntries shrinks the table if many entries are
750 * removed preventing wasting memory on a too sparse table.
752 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
753 js_script_filename_sweeper
,
757 printf("script filename table savings so far: %u\n", sftbl_savings
);
763 * JSScript data structures memory alignment:
766 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
767 * use script->objects() to access it.
768 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
769 * use script->regexps() to access it.
770 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
771 * != 0, use script->trynotes() to access it.
772 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
773 * JSScript.atomMap.vector if any.
774 * JSObject *o[] array of script->objects()->length objects if any
775 * pointed by script->objects()->vector.
776 * JSObject *r[] array of script->regexps()->length regexps if any
777 * pointed by script->regexps()->vector.
778 * JSTryNote t[] array of script->trynotes()->length try notes if any
779 * pointed by script->trynotes()->vector.
780 * jsbytecode b[] script bytecode pointed by JSScript.code.
781 * jssrcnote s[] script source notes, use script->notes() to access it
783 * The alignment avoids gaps between entries as alignment requirement for each
784 * subsequent structure or array is the same or divides the alignment
785 * requirement for the previous one.
787 * The followings asserts checks that assuming that the alignment requirement
788 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
789 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
791 JS_STATIC_ASSERT(sizeof(JSScript
) % sizeof(void *) == 0);
792 JS_STATIC_ASSERT(sizeof(JSObjectArray
) % sizeof(void *) == 0);
793 JS_STATIC_ASSERT(sizeof(JSTryNoteArray
) == sizeof(JSObjectArray
));
794 JS_STATIC_ASSERT(sizeof(JSAtom
*) == sizeof(JSObject
*));
795 JS_STATIC_ASSERT(sizeof(JSObject
*) % sizeof(uint32
) == 0);
796 JS_STATIC_ASSERT(sizeof(JSTryNote
) == 3 * sizeof(uint32
));
797 JS_STATIC_ASSERT(sizeof(uint32
) % sizeof(jsbytecode
) == 0);
798 JS_STATIC_ASSERT(sizeof(jsbytecode
) % sizeof(jssrcnote
) == 0);
801 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
804 JS_STATIC_ASSERT(sizeof(JSScript
) + 2 * sizeof(JSObjectArray
) +
805 sizeof(JSUpvarArray
) < JS_BIT(8));
808 js_NewScript(JSContext
*cx
, uint32 length
, uint32 nsrcnotes
, uint32 natoms
,
809 uint32 nobjects
, uint32 nupvars
, uint32 nregexps
,
812 size_t size
, vectorSize
;
816 size
= sizeof(JSScript
) +
817 sizeof(JSAtom
*) * natoms
+
818 length
* sizeof(jsbytecode
) +
819 nsrcnotes
* sizeof(jssrcnote
);
821 size
+= sizeof(JSObjectArray
) + nobjects
* sizeof(JSObject
*);
823 size
+= sizeof(JSUpvarArray
) + nupvars
* sizeof(uint32
);
825 size
+= sizeof(JSObjectArray
) + nregexps
* sizeof(JSObject
*);
827 size
+= sizeof(JSTryNoteArray
) + ntrynotes
* sizeof(JSTryNote
);
829 script
= (JSScript
*) cx
->malloc(size
);
833 script
->length
= length
;
834 script
->version
= cx
->version
;
836 cursor
= (uint8
*)script
+ sizeof(JSScript
);
838 script
->objectsOffset
= (uint8
)(cursor
- (uint8
*)script
);
839 cursor
+= sizeof(JSObjectArray
);
842 script
->upvarsOffset
= (uint8
)(cursor
- (uint8
*)script
);
843 cursor
+= sizeof(JSUpvarArray
);
846 script
->regexpsOffset
= (uint8
)(cursor
- (uint8
*)script
);
847 cursor
+= sizeof(JSObjectArray
);
849 if (ntrynotes
!= 0) {
850 script
->trynotesOffset
= (uint8
)(cursor
- (uint8
*)script
);
851 cursor
+= sizeof(JSTryNoteArray
);
855 script
->atomMap
.length
= natoms
;
856 script
->atomMap
.vector
= (JSAtom
**)cursor
;
857 vectorSize
= natoms
* sizeof(script
->atomMap
.vector
[0]);
860 * Clear object map's vector so the GC tracing can run when not yet
861 * all atoms are copied to the array.
863 memset(cursor
, 0, vectorSize
);
864 cursor
+= vectorSize
;
868 script
->objects()->length
= nobjects
;
869 script
->objects()->vector
= (JSObject
**)cursor
;
870 vectorSize
= nobjects
* sizeof(script
->objects()->vector
[0]);
871 memset(cursor
, 0, vectorSize
);
872 cursor
+= vectorSize
;
876 script
->regexps()->length
= nregexps
;
877 script
->regexps()->vector
= (JSObject
**)cursor
;
878 vectorSize
= nregexps
* sizeof(script
->regexps()->vector
[0]);
879 memset(cursor
, 0, vectorSize
);
880 cursor
+= vectorSize
;
883 if (ntrynotes
!= 0) {
884 script
->trynotes()->length
= ntrynotes
;
885 script
->trynotes()->vector
= (JSTryNote
*)cursor
;
886 vectorSize
= ntrynotes
* sizeof(script
->trynotes()->vector
[0]);
888 memset(cursor
, 0, vectorSize
);
890 cursor
+= vectorSize
;
894 * NB: We allocate the vector of uint32 upvar cookies after all vectors of
895 * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
898 script
->upvars()->length
= nupvars
;
899 script
->upvars()->vector
= (uint32
*)cursor
;
900 vectorSize
= nupvars
* sizeof(script
->upvars()->vector
[0]);
901 memset(cursor
, 0, vectorSize
);
902 cursor
+= vectorSize
;
905 script
->code
= script
->main
= (jsbytecode
*)cursor
;
907 length
* sizeof(jsbytecode
) +
908 nsrcnotes
* sizeof(jssrcnote
) ==
909 (uint8
*)script
+ size
);
911 #ifdef CHECK_SCRIPT_OWNER
912 script
->owner
= cx
->thread
;
918 js_NewScriptFromCG(JSContext
*cx
, JSCodeGenerator
*cg
)
920 uint32 mainLength
, prologLength
, nsrcnotes
, nfixed
;
922 const char *filename
;
925 /* The counts of indexed things must be checked during code generation. */
926 JS_ASSERT(cg
->atomList
.count
<= INDEX_LIMIT
);
927 JS_ASSERT(cg
->objectList
.length
<= INDEX_LIMIT
);
928 JS_ASSERT(cg
->regexpList
.length
<= INDEX_LIMIT
);
930 mainLength
= CG_OFFSET(cg
);
931 prologLength
= CG_PROLOG_OFFSET(cg
);
933 if (prologLength
+ mainLength
<= 3) {
935 * Check very short scripts to see whether they are "empty" and return
936 * the const empty-script singleton if so. We are deliberately flexible
937 * about whether JSOP_TRACE is in the prolog.
939 jsbytecode
*pc
= prologLength
? CG_PROLOG_BASE(cg
) : CG_BASE(cg
);
941 if (JSOp(*pc
) == JSOP_TRACE
) {
943 if (pc
== CG_PROLOG_BASE(cg
) + prologLength
)
946 if ((cg
->flags
& TCF_NO_SCRIPT_RVAL
) && JSOp(*pc
) == JSOP_FALSE
)
949 if (JSOp(*pc
) == JSOP_STOP
&&
950 !cx
->debugHooks
->newScriptHook
&&
951 !(cg
->flags
& TCF_NEED_MUTABLE_SCRIPT
))
954 * We can probably use the immutable empty script singleton, just
955 * one hard case (nupvars != 0) may stand in our way.
957 JSScript
*empty
= JSScript::emptyScript();
959 if (cg
->flags
& TCF_IN_FUNCTION
) {
961 JS_ASSERT(FUN_INTERPRETED(fun
) && !FUN_SCRIPT(fun
));
962 if (fun
->u
.i
.nupvars
!= 0) {
964 * FIXME: upvar uses that were all optimized away may leave
965 * fun->u.i.nupvars non-zero, and since that count is added
966 * into fun->countLocalNames() in order to discriminate the
967 * fun->u.i.names union, we cannot force fun->u.i.nupvars
968 * to 0 to match JSScript::emptyScript()->upvars()->length.
969 * So we skip the empty script optimization.
971 * Fixing this requires the compiler to track upvar uses as
972 * it analyzes and optimizes closures, and subsequently as
973 * the emitter performs useless expression elimination.
977 js_FreezeLocalNames(cx
, fun
);
978 fun
->u
.i
.script
= empty
;
981 JS_RUNTIME_METER(cx
->runtime
, liveEmptyScripts
);
982 JS_RUNTIME_METER(cx
->runtime
, totalEmptyScripts
);
988 CG_COUNT_FINAL_SRCNOTES(cg
, nsrcnotes
);
989 script
= js_NewScript(cx
, prologLength
+ mainLength
, nsrcnotes
,
990 cg
->atomList
.count
, cg
->objectList
.length
,
991 cg
->upvarList
.count
, cg
->regexpList
.length
,
996 /* Now that we have script, error control flow must go to label bad. */
997 script
->main
+= prologLength
;
998 memcpy(script
->code
, CG_PROLOG_BASE(cg
), prologLength
* sizeof(jsbytecode
));
999 memcpy(script
->main
, CG_BASE(cg
), mainLength
* sizeof(jsbytecode
));
1000 nfixed
= (cg
->flags
& TCF_IN_FUNCTION
)
1001 ? cg
->fun
->u
.i
.nvars
1002 : cg
->ngvars
+ cg
->sharpSlots();
1003 JS_ASSERT(nfixed
< SLOTNO_LIMIT
);
1004 script
->nfixed
= (uint16
) nfixed
;
1005 js_InitAtomMap(cx
, &script
->atomMap
, &cg
->atomList
);
1007 filename
= cg
->compiler
->tokenStream
.getFilename();
1009 script
->filename
= js_SaveScriptFilename(cx
, filename
);
1010 if (!script
->filename
)
1013 script
->lineno
= cg
->firstLine
;
1014 if (script
->nfixed
+ cg
->maxStackDepth
>= JS_BIT(16)) {
1015 js_ReportCompileErrorNumber(cx
, CG_TS(cg
), NULL
, JSREPORT_ERROR
,
1016 JSMSG_NEED_DIET
, "script");
1019 script
->nslots
= script
->nfixed
+ cg
->maxStackDepth
;
1020 script
->staticLevel
= uint16(cg
->staticLevel
);
1021 script
->principals
= cg
->compiler
->principals
;
1022 if (script
->principals
)
1023 JSPRINCIPALS_HOLD(cx
, script
->principals
);
1025 if (!js_FinishTakingSrcNotes(cx
, cg
, script
->notes()))
1027 if (cg
->ntrynotes
!= 0)
1028 js_FinishTakingTryNotes(cg
, script
->trynotes());
1029 if (cg
->objectList
.length
!= 0)
1030 cg
->objectList
.finish(script
->objects());
1031 if (cg
->regexpList
.length
!= 0)
1032 cg
->regexpList
.finish(script
->regexps());
1033 if (cg
->flags
& TCF_NO_SCRIPT_RVAL
)
1034 script
->noScriptRval
= true;
1035 if (cg
->hasSharps())
1036 script
->hasSharps
= true;
1037 if (cg
->flags
& TCF_STRICT_MODE_CODE
)
1038 script
->strictModeCode
= true;
1040 if (cg
->upvarList
.count
!= 0) {
1041 JS_ASSERT(cg
->upvarList
.count
<= cg
->upvarMap
.length
);
1042 memcpy(script
->upvars()->vector
, cg
->upvarMap
.vector
,
1043 cg
->upvarList
.count
* sizeof(uint32
));
1044 cg
->upvarList
.clear();
1045 cx
->free(cg
->upvarMap
.vector
);
1046 cg
->upvarMap
.vector
= NULL
;
1050 * We initialize fun->u.script to be the script constructed above
1051 * so that the debugger has a valid FUN_SCRIPT(fun).
1054 if (cg
->flags
& TCF_IN_FUNCTION
) {
1056 JS_ASSERT(FUN_INTERPRETED(fun
) && !FUN_SCRIPT(fun
));
1057 if (script
->upvarsOffset
!= 0)
1058 JS_ASSERT(script
->upvars()->length
== fun
->u
.i
.nupvars
);
1060 fun
->u
.i
.nupvars
= 0;
1062 js_FreezeLocalNames(cx
, fun
);
1063 fun
->u
.i
.script
= script
;
1064 #ifdef CHECK_SCRIPT_OWNER
1065 script
->owner
= NULL
;
1067 if (cg
->flags
& TCF_FUN_HEAVYWEIGHT
)
1068 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
1071 /* Tell the debugger about this compiled script. */
1072 js_CallNewScriptHook(cx
, script
, fun
);
1073 JS_RUNTIME_METER(cx
->runtime
, liveScripts
);
1074 JS_RUNTIME_METER(cx
->runtime
, totalScripts
);
1078 js_DestroyScript(cx
, script
);
1083 js_CallNewScriptHook(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
)
1085 JS_ASSERT(script
!= JSScript::emptyScript());
1087 JSNewScriptHook hook
;
1089 hook
= cx
->debugHooks
->newScriptHook
;
1091 JS_KEEP_ATOMS(cx
->runtime
);
1092 hook(cx
, script
->filename
, script
->lineno
, script
, fun
,
1093 cx
->debugHooks
->newScriptHookData
);
1094 JS_UNKEEP_ATOMS(cx
->runtime
);
1099 js_CallDestroyScriptHook(JSContext
*cx
, JSScript
*script
)
1101 JS_ASSERT(script
!= JSScript::emptyScript());
1103 JSDestroyScriptHook hook
;
1105 hook
= cx
->debugHooks
->destroyScriptHook
;
1107 hook(cx
, script
, cx
->debugHooks
->destroyScriptHookData
);
1111 js_DestroyScript(JSContext
*cx
, JSScript
*script
)
1113 if (script
== JSScript::emptyScript()) {
1114 JS_RUNTIME_UNMETER(cx
->runtime
, liveEmptyScripts
);
1118 js_CallDestroyScriptHook(cx
, script
);
1119 JS_ClearScriptTraps(cx
, script
);
1121 if (script
->principals
)
1122 JSPRINCIPALS_DROP(cx
, script
->principals
);
1124 if (JS_GSN_CACHE(cx
).code
== script
->code
)
1125 JS_PURGE_GSN_CACHE(cx
);
1128 * Worry about purging the property cache and any compiled traces related
1129 * to its bytecode if this script is being destroyed from JS_DestroyScript
1130 * or equivalent according to a mandatory "New/Destroy" protocol.
1132 * The GC purges all property caches when regenerating shapes upon shape
1133 * generator overflow, so no need in that event to purge just the entries
1136 * The GC purges trace-JITted code on every GC activation, not just when
1137 * regenerating shapes, so we don't have to purge fragments if the GC is
1138 * currently running.
1140 * JS_THREADSAFE note: js_PurgePropertyCacheForScript purges only the
1141 * current thread's property cache, so a script not owned by a function
1142 * or object, which hands off lifetime management for that script to the
1143 * GC, must be used by only one thread over its lifetime.
1145 * This should be an API-compatible change, since a script is never safe
1146 * against premature GC if shared among threads without a rooted object
1147 * wrapping it to protect the script's mapped atoms against GC. We use
1148 * script->owner to enforce this requirement via assertions.
1150 #ifdef CHECK_SCRIPT_OWNER
1151 JS_ASSERT_IF(cx
->runtime
->gcRunning
, !script
->owner
);
1154 /* FIXME: bug 506341; would like to do this only if regenerating shapes. */
1155 if (!cx
->runtime
->gcRunning
) {
1156 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
1158 if (!(fp
&& (fp
->flags
& JSFRAME_EVAL
))) {
1159 js_PurgePropertyCacheForScript(cx
, script
);
1161 #ifdef CHECK_SCRIPT_OWNER
1162 JS_ASSERT(script
->owner
== cx
->thread
);
1168 PurgeScriptFragments(cx
, script
);
1173 JS_RUNTIME_UNMETER(cx
->runtime
, liveScripts
);
1177 js_TraceScript(JSTracer
*trc
, JSScript
*script
)
1183 JSObjectArray
*objarray
;
1185 map
= &script
->atomMap
;
1186 length
= map
->length
;
1187 vector
= map
->vector
;
1188 for (i
= 0; i
< length
; i
++) {
1189 v
= ATOM_KEY(vector
[i
]);
1190 if (JSVAL_IS_TRACEABLE(v
)) {
1191 JS_SET_TRACING_INDEX(trc
, "atomMap", i
);
1192 js_CallGCMarker(trc
, JSVAL_TO_TRACEABLE(v
), JSVAL_TRACE_KIND(v
));
1196 if (script
->objectsOffset
!= 0) {
1197 objarray
= script
->objects();
1198 i
= objarray
->length
;
1201 if (objarray
->vector
[i
]) {
1202 JS_SET_TRACING_INDEX(trc
, "objects", i
);
1203 js_CallGCMarker(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
1208 if (script
->regexpsOffset
!= 0) {
1209 objarray
= script
->regexps();
1210 i
= objarray
->length
;
1213 if (objarray
->vector
[i
]) {
1214 JS_SET_TRACING_INDEX(trc
, "regexps", i
);
1215 js_CallGCMarker(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
1220 if (script
->u
.object
) {
1221 JS_SET_TRACING_NAME(trc
, "object");
1222 js_CallGCMarker(trc
, script
->u
.object
, JSTRACE_OBJECT
);
1225 if (IS_GC_MARKING_TRACER(trc
) && script
->filename
)
1226 js_MarkScriptFilename(script
->filename
);
1229 typedef struct GSNCacheEntry
{
1230 JSDHashEntryHdr hdr
;
1235 #define GSN_CACHE_THRESHOLD 100
1238 js_PurgeGSNCache(JSGSNCache
*cache
)
1241 if (cache
->table
.ops
) {
1242 JS_DHashTableFinish(&cache
->table
);
1243 cache
->table
.ops
= NULL
;
1245 GSN_CACHE_METER(cache
, purges
);
1249 js_GetSrcNoteCached(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1251 ptrdiff_t target
, offset
;
1252 GSNCacheEntry
*entry
;
1253 jssrcnote
*sn
, *result
;
1257 target
= pc
- script
->code
;
1258 if ((uint32
)target
>= script
->length
)
1261 if (JS_GSN_CACHE(cx
).code
== script
->code
) {
1262 JS_METER_GSN_CACHE(cx
, hits
);
1263 entry
= (GSNCacheEntry
*)
1264 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1269 JS_METER_GSN_CACHE(cx
, misses
);
1271 for (sn
= script
->notes(); ; sn
= SN_NEXT(sn
)) {
1272 if (SN_IS_TERMINATOR(sn
)) {
1276 offset
+= SN_DELTA(sn
);
1277 if (offset
== target
&& SN_IS_GETTABLE(sn
)) {
1283 if (JS_GSN_CACHE(cx
).code
!= script
->code
&&
1284 script
->length
>= GSN_CACHE_THRESHOLD
) {
1285 JS_PURGE_GSN_CACHE(cx
);
1287 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1289 if (SN_IS_GETTABLE(sn
))
1292 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx
).table
, JS_DHashGetStubOps(),
1293 NULL
, sizeof(GSNCacheEntry
),
1294 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes
))) {
1295 JS_GSN_CACHE(cx
).table
.ops
= NULL
;
1298 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1301 if (SN_IS_GETTABLE(sn
)) {
1302 entry
= (GSNCacheEntry
*)
1303 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1309 JS_GSN_CACHE(cx
).code
= script
->code
;
1310 JS_METER_GSN_CACHE(cx
, fills
);
1318 js_FramePCToLineNumber(JSContext
*cx
, JSStackFrame
*fp
)
1320 return js_PCToLineNumber(cx
, fp
->script
, fp
->imacpc
? fp
->imacpc
: fp
->regs
->pc
);
1324 js_PCToLineNumber(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1329 ptrdiff_t offset
, target
;
1333 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1338 * Special case: function definition needs no line number note because
1339 * the function's script contains its starting line number.
1341 op
= js_GetOpcode(cx
, script
, pc
);
1342 if (js_CodeSpec
[op
].format
& JOF_INDEXBASE
)
1343 pc
+= js_CodeSpec
[op
].length
;
1344 if (*pc
== JSOP_DEFFUN
) {
1345 GET_FUNCTION_FROM_BYTECODE(script
, pc
, 0, fun
);
1346 return fun
->u
.i
.script
->lineno
;
1350 * General case: walk through source notes accumulating their deltas,
1351 * keeping track of line-number notes, until we pass the note for pc's
1352 * offset within script->code.
1354 lineno
= script
->lineno
;
1356 target
= pc
- script
->code
;
1357 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1358 offset
+= SN_DELTA(sn
);
1359 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1360 if (type
== SRC_SETLINE
) {
1361 if (offset
<= target
)
1362 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1363 } else if (type
== SRC_NEWLINE
) {
1364 if (offset
<= target
)
1367 if (offset
> target
)
1373 /* The line number limit is the same as the jssrcnote offset limit. */
1374 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1377 js_LineNumberToPC(JSScript
*script
, uintN target
)
1379 ptrdiff_t offset
, best
;
1380 uintN lineno
, bestdiff
, diff
;
1386 lineno
= script
->lineno
;
1387 bestdiff
= SN_LINE_LIMIT
;
1388 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1390 * Exact-match only if offset is not in the prolog; otherwise use
1391 * nearest greater-or-equal line number match.
1393 if (lineno
== target
&& script
->code
+ offset
>= script
->main
)
1395 if (lineno
>= target
) {
1396 diff
= lineno
- target
;
1397 if (diff
< bestdiff
) {
1402 offset
+= SN_DELTA(sn
);
1403 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1404 if (type
== SRC_SETLINE
) {
1405 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1406 } else if (type
== SRC_NEWLINE
) {
1413 return script
->code
+ offset
;
1416 JS_FRIEND_API(uintN
)
1417 js_GetScriptLineExtent(JSScript
*script
)
1423 lineno
= script
->lineno
;
1424 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1425 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1426 if (type
== SRC_SETLINE
) {
1427 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1428 } else if (type
== SRC_NEWLINE
) {
1432 return 1 + lineno
- script
->lineno
;