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 "jsobjinlines.h"
69 #include "jsscriptinlines.h"
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
85 js_XDRScript(JSXDRState
*xdr
, JSScript
**scriptp
, bool needMutableScript
,
89 JSScript
*script
, *oldscript
;
92 uint32 length
, lineno
, nslots
, magic
;
93 uint32 natoms
, nsrcnotes
, ntrynotes
, nobjects
, nupvars
, nregexps
, nconsts
, i
;
94 uint32 prologLength
, version
;
95 JSPrincipals
*principals
;
97 JSBool filenameWasSaved
;
98 jssrcnote
*notes
, *sn
;
99 JSSecurityCallbacks
*callbacks
;
103 nsrcnotes
= ntrynotes
= natoms
= nobjects
= nupvars
= nregexps
= nconsts
= 0;
104 filenameWasSaved
= JS_FALSE
;
107 if (xdr
->mode
== JSXDR_ENCODE
)
108 magic
= JSXDR_MAGIC_SCRIPT_CURRENT
;
109 if (!JS_XDRUint32(xdr
, &magic
))
111 if (magic
!= JSXDR_MAGIC_SCRIPT_CURRENT
) {
112 /* We do not provide binary compatibility with older scripts. */
114 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
115 JSMSG_BAD_SCRIPT_MAGIC
);
118 *hasMagic
= JS_FALSE
;
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
))
134 if (xdr
->mode
== JSXDR_ENCODE
) {
135 JS_ASSERT(*scriptp
== JSScript::emptyScript());
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);
152 script
->version
= JSVERSION_DEFAULT
;
153 script
->noScriptRval
= true;
154 script
->code
[0] = JSOP_STOP
;
155 script
->code
[1] = SRC_NULL
;
160 *scriptp
= JSScript::emptyScript();
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
))
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
))
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
))
213 if (!JS_XDRUint32(xdr
, &nconsts
))
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
);
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();
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
;
240 if (xdr
->mode
== JSXDR_ENCODE
) {
241 code
= js_UntrapScriptCode(cx
, script
);
246 xdr
->script
= script
;
247 ok
= JS_XDRBytes(xdr
, (char *) code
, length
* sizeof(jsbytecode
));
249 if (code
!= script
->code
)
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
)) {
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
))
269 !callbacks
->principalsTranscoder(xdr
, &principals
)) {
273 if (!JS_XDRUint32(xdr
, &encodeable
))
276 if (!(callbacks
&& callbacks
->principalsTranscoder
)) {
277 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
278 JSMSG_CANT_DECODE_PRINCIPALS
);
281 if (!callbacks
->principalsTranscoder(xdr
, &principals
))
283 script
->principals
= principals
;
287 if (xdr
->mode
== JSXDR_DECODE
) {
288 const char *filename
= script
->filename
;
290 filename
= js_SaveScriptFilename(cx
, filename
);
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
]))
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
];
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
))
325 if (!js_XDRFunctionObject(xdr
, objp
))
328 JS_ASSERT(isBlock
== 1);
329 if (!js_XDRBlockObject(xdr
, objp
))
333 for (i
= 0; i
!= nupvars
; ++i
) {
334 if (!JS_XDRUint32(xdr
, reinterpret_cast<uint32
*>(&script
->upvars()->vector
[i
])))
337 for (i
= 0; i
!= nregexps
; ++i
) {
338 if (!js_XDRRegExpObject(xdr
, &script
->regexps()->vector
[i
]))
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
;
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
;
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
)) {
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
])))
378 xdr
->script
= oldscript
;
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
);
390 xdr
->script
= oldscript
;
394 #endif /* JS_HAS_XDR */
397 script_finalize(JSContext
*cx
, JSObject
*obj
)
399 JSScript
*script
= (JSScript
*) obj
->getPrivate();
401 js_DestroyScript(cx
, script
);
405 script_trace(JSTracer
*trc
, JSObject
*obj
)
407 JSScript
*script
= (JSScript
*) obj
->getPrivate();
409 js_TraceScript(trc
, script
);
412 Class js_ScriptClass
= {
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 */
424 NULL
, /* reserved0 */
425 NULL
, /* checkAccess */
427 NULL
, /* construct */
428 NULL
, /* xdrObject */
429 NULL
, /* hasInstance */
430 JS_CLASS_TRACE(script_trace
)
434 * Shared script filename management.
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
;
453 js_alloc_table_space(void *priv
, size_t size
)
455 return js_malloc(size
);
459 js_free_table_space(void *priv
, void *item
, size_t size
)
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
)));
474 js_free_sftbl_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
476 if (flag
!= HT_FREE_ENTRY
)
481 static JSHashAllocOps sftbl_alloc_ops
= {
482 js_alloc_table_space
, js_free_table_space
,
483 js_alloc_sftbl_entry
, js_free_sftbl_entry
487 FinishRuntimeScriptState(JSRuntime
*rt
)
489 if (rt
->scriptFilenameTable
) {
490 JS_HashTableDestroy(rt
->scriptFilenameTable
);
491 rt
->scriptFilenameTable
= NULL
;
494 if (rt
->scriptFilenameTableLock
) {
495 JS_DESTROY_LOCK(rt
->scriptFilenameTableLock
);
496 rt
->scriptFilenameTableLock
= NULL
;
502 js_InitRuntimeScriptState(JSRuntime
*rt
)
505 JS_ASSERT(!rt
->scriptFilenameTableLock
);
506 rt
->scriptFilenameTableLock
= JS_NEW_LOCK();
507 if (!rt
->scriptFilenameTableLock
)
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 */
518 JS_INIT_CLIST(&rt
->scriptFilenamePrefixes
);
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
;
530 js_FreeRuntimeScriptState(JSRuntime
*rt
)
532 if (!rt
->scriptFilenameTable
)
535 while (!JS_CLIST_IS_EMPTY(&rt
->scriptFilenamePrefixes
)) {
536 ScriptFilenamePrefix
*sfp
= (ScriptFilenamePrefix
*)
537 rt
->scriptFilenamePrefixes
.next
;
538 JS_REMOVE_LINK(&sfp
->links
);
541 FinishRuntimeScriptState(rt
);
548 size_t sftbl_savings
= 0;
551 static ScriptFilenameEntry
*
552 SaveScriptFilename(JSRuntime
*rt
, const char *filename
, uint32 flags
)
557 ScriptFilenameEntry
*sfe
;
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
;
568 sftbl_savings
+= strlen(sfe
->filename
);
572 sfe
= (ScriptFilenameEntry
*)
573 JS_HashTableRawAdd(table
, hep
, hash
, filename
, NULL
);
576 sfe
->key
= strcpy(sfe
->filename
, filename
);
578 sfe
->mark
= JS_FALSE
;
581 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
583 /* Search in case filename was saved already; we must be idempotent. */
585 length
= strlen(filename
);
586 for (head
= link
= &rt
->scriptFilenamePrefixes
;
589 /* Lag link behind sfp to insert in non-increasing length order. */
590 sfp
= (ScriptFilenamePrefix
*) link
->next
;
591 if (!strcmp(sfp
->name
, filename
))
593 if (sfp
->length
<= length
) {
601 /* No such prefix: add one now. */
602 sfp
= (ScriptFilenamePrefix
*) js_malloc(sizeof(ScriptFilenamePrefix
));
605 JS_INSERT_AFTER(&sfp
->links
, link
);
606 sfp
->name
= sfe
->filename
;
607 sfp
->length
= length
;
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.
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';
634 js_SaveScriptFilename(JSContext
*cx
, const char *filename
)
637 ScriptFilenameEntry
*sfe
;
638 JSCList
*head
, *link
;
639 ScriptFilenamePrefix
*sfp
;
642 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
643 sfe
= SaveScriptFilename(rt
, filename
, 0);
645 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
646 JS_ReportOutOfMemory(cx
);
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
;
658 sfp
= (ScriptFilenamePrefix
*) link
;
659 if (!strncmp(sfp
->name
, filename
, sfp
->length
)) {
660 sfe
->flags
|= sfp
->flags
;
664 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
665 return sfe
->filename
;
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
))
677 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
678 sfe
= SaveScriptFilename(rt
, filename
, flags
);
679 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
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)
701 js_GetScriptFilenameFlags(const char *filename
)
703 ScriptFilenameEntry
*sfe
;
705 sfe
= FILENAME_TO_SFE(filename
);
706 ASSERT_VALID_SFE(sfe
);
711 js_MarkScriptFilename(const char *filename
)
713 ScriptFilenameEntry
*sfe
;
715 sfe
= FILENAME_TO_SFE(filename
);
716 ASSERT_VALID_SFE(sfe
);
721 js_script_filename_marker(JSHashEntry
*he
, intN i
, void *arg
)
723 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
726 return HT_ENUMERATE_NEXT
;
730 js_MarkScriptFilenames(JSRuntime
*rt
)
732 JSCList
*head
, *link
;
733 ScriptFilenamePrefix
*sfp
;
735 if (!rt
->scriptFilenameTable
)
738 if (rt
->gcKeepAtoms
) {
739 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
740 js_script_filename_marker
,
743 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
746 sfp
= (ScriptFilenamePrefix
*) link
;
747 js_MarkScriptFilename(sfp
->name
);
752 js_script_filename_sweeper(JSHashEntry
*he
, intN i
, void *arg
)
754 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
757 return HT_ENUMERATE_REMOVE
;
758 sfe
->mark
= JS_FALSE
;
759 return HT_ENUMERATE_NEXT
;
763 js_SweepScriptFilenames(JSRuntime
*rt
)
765 if (!rt
->scriptFilenameTable
)
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
,
777 printf("script filename table savings so far: %u\n", sftbl_savings
);
783 * JSScript data structures memory alignment:
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
824 JS_STATIC_ASSERT(sizeof(JSScript
) + 2 * sizeof(JSObjectArray
) +
825 sizeof(JSUpvarArray
) < JS_BIT(8));
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
;
835 unsigned constPadding
= 0;
837 size
= sizeof(JSScript
) +
838 sizeof(JSAtom
*) * natoms
;
841 size
+= sizeof(JSObjectArray
) + nobjects
* sizeof(JSObject
*);
843 size
+= sizeof(JSUpvarArray
) + nupvars
* sizeof(uint32
);
845 size
+= sizeof(JSObjectArray
) + nregexps
* sizeof(JSObject
*);
847 size
+= sizeof(JSTryNoteArray
) + ntrynotes
* sizeof(JSTryNote
);
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
);
866 script
->length
= length
;
867 script
->version
= cx
->version
;
869 cursor
= (uint8
*)script
+ sizeof(JSScript
);
871 script
->objectsOffset
= (uint8
)(cursor
- (uint8
*)script
);
872 cursor
+= sizeof(JSObjectArray
);
875 script
->upvarsOffset
= (uint8
)(cursor
- (uint8
*)script
);
876 cursor
+= sizeof(JSUpvarArray
);
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
);
887 script
->constOffset
= (uint8
)(cursor
- (uint8
*)script
);
888 cursor
+= sizeof(JSConstArray
);
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
;
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
;
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]);
925 memset(cursor
, 0, vectorSize
);
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.
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. */
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
;
955 length
* sizeof(jsbytecode
) +
956 nsrcnotes
* sizeof(jssrcnote
) ==
957 (uint8
*)script
+ size
);
959 #ifdef CHECK_SCRIPT_OWNER
960 script
->owner
= cx
->thread
;
966 js_NewScriptFromCG(JSContext
*cx
, JSCodeGenerator
*cg
)
968 uint32 mainLength
, prologLength
, nsrcnotes
, nfixed
;
970 const char *filename
;
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
) {
991 if (pc
== CG_PROLOG_BASE(cg
) + prologLength
)
994 if ((cg
->flags
& TCF_NO_SCRIPT_RVAL
) && JSOp(*pc
) == JSOP_FALSE
)
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
1006 JSScript
*empty
= JSScript::emptyScript();
1008 if (cg
->flags
& TCF_IN_FUNCTION
) {
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
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.
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
);
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());
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();
1069 script
->filename
= js_SaveScriptFilename(cx
, filename
);
1070 if (!script
->filename
)
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");
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()))
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).
1115 if (cg
->flags
& TCF_IN_FUNCTION
) {
1117 JS_ASSERT(FUN_INTERPRETED(fun
) && !FUN_SCRIPT(fun
));
1118 if (script
->upvarsOffset
!= 0)
1119 JS_ASSERT(script
->upvars()->length
== fun
->u
.i
.nupvars
);
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
;
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
);
1139 js_DestroyScript(cx
, script
);
1144 js_CallNewScriptHook(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
)
1146 JS_ASSERT(script
!= JSScript::emptyScript());
1148 JSNewScriptHook hook
;
1150 hook
= cx
->debugHooks
->newScriptHook
;
1152 AutoKeepAtoms
keep(cx
->runtime
);
1153 hook(cx
, script
->filename
, script
->lineno
, script
, fun
,
1154 cx
->debugHooks
->newScriptHookData
);
1159 js_CallDestroyScriptHook(JSContext
*cx
, JSScript
*script
)
1161 JS_ASSERT(script
!= JSScript::emptyScript());
1163 JSDestroyScriptHook hook
;
1165 hook
= cx
->debugHooks
->destroyScriptHook
;
1167 hook(cx
, script
, cx
->debugHooks
->destroyScriptHookData
);
1171 js_DestroyScript(JSContext
*cx
, JSScript
*script
)
1173 if (script
== JSScript::emptyScript()) {
1174 JS_RUNTIME_UNMETER(cx
->runtime
, liveEmptyScripts
);
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
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
);
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
);
1228 PurgeScriptFragments(cx
, script
);
1233 JS_RUNTIME_UNMETER(cx
->runtime
, liveScripts
);
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
;
1247 if (objarray
->vector
[i
]) {
1248 JS_SET_TRACING_INDEX(trc
, "objects", i
);
1249 Mark(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
1254 if (script
->regexpsOffset
!= 0) {
1255 JSObjectArray
*objarray
= script
->regexps();
1256 uintN i
= objarray
->length
;
1259 if (objarray
->vector
[i
]) {
1260 JS_SET_TRACING_INDEX(trc
, "regexps", i
);
1261 Mark(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
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
);
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
);
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.
1300 #ifdef CHECK_SCRIPT_OWNER
1301 script
->owner
= NULL
;
1307 typedef struct GSNCacheEntry
{
1308 JSDHashEntryHdr hdr
;
1313 #define GSN_CACHE_THRESHOLD 100
1316 js_PurgeGSNCache(JSGSNCache
*cache
)
1319 if (cache
->table
.ops
) {
1320 JS_DHashTableFinish(&cache
->table
);
1321 cache
->table
.ops
= NULL
;
1323 GSN_CACHE_METER(cache
, purges
);
1327 js_GetSrcNoteCached(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1329 ptrdiff_t target
, offset
;
1330 GSNCacheEntry
*entry
;
1331 jssrcnote
*sn
, *result
;
1335 target
= pc
- script
->code
;
1336 if ((uint32
)target
>= script
->length
)
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
,
1347 JS_METER_GSN_CACHE(cx
, misses
);
1349 for (sn
= script
->notes(); ; sn
= SN_NEXT(sn
)) {
1350 if (SN_IS_TERMINATOR(sn
)) {
1354 offset
+= SN_DELTA(sn
);
1355 if (offset
== target
&& SN_IS_GETTABLE(sn
)) {
1361 if (JS_GSN_CACHE(cx
).code
!= script
->code
&&
1362 script
->length
>= GSN_CACHE_THRESHOLD
) {
1363 JS_PURGE_GSN_CACHE(cx
);
1365 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1367 if (SN_IS_GETTABLE(sn
))
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
;
1376 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1379 if (SN_IS_GETTABLE(sn
)) {
1380 entry
= (GSNCacheEntry
*)
1381 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1387 JS_GSN_CACHE(cx
).code
= script
->code
;
1388 JS_METER_GSN_CACHE(cx
, fills
);
1396 js_FramePCToLineNumber(JSContext
*cx
, JSStackFrame
*fp
)
1398 return js_PCToLineNumber(cx
, fp
->getScript(),
1399 fp
->hasIMacroPC() ? fp
->getIMacroPC() : fp
->pc(cx
));
1403 js_PCToLineNumber(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1408 ptrdiff_t offset
, target
;
1412 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
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
;
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
)
1446 if (offset
> target
)
1452 /* The line number limit is the same as the jssrcnote offset limit. */
1453 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1456 js_LineNumberToPC(JSScript
*script
, uintN target
)
1458 ptrdiff_t offset
, best
;
1459 uintN lineno
, bestdiff
, diff
;
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
)
1474 if (lineno
>= target
) {
1475 diff
= lineno
- target
;
1476 if (diff
< bestdiff
) {
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
) {
1492 return script
->code
+ offset
;
1495 JS_FRIEND_API(uintN
)
1496 js_GetScriptLineExtent(JSScript
*script
)
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
) {
1511 return 1 + lineno
- script
->lineno
;