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.
52 #include "jsversion.h"
67 #include "methodjit/MethodJIT.h"
69 #include "jsinterpinlines.h"
70 #include "jsobjinlines.h"
71 #include "jsscriptinlines.h"
74 using namespace js::gc
;
79 Bindings::lookup(JSContext
*cx
, JSAtom
*name
, uintN
*indexp
) const
81 JS_ASSERT(lastBinding
);
84 SHAPE_FETCH(Shape::search(cx
->runtime
, const_cast<Shape
**>(&lastBinding
),
90 *indexp
= shape
->shortid
;
92 if (shape
->getter() == GetCallArg
)
94 if (shape
->getter() == GetCallUpvar
)
97 return shape
->writable() ? VARIABLE
: CONSTANT
;
101 Bindings::add(JSContext
*cx
, JSAtom
*name
, BindingKind kind
)
103 JS_ASSERT(lastBinding
);
106 * We still follow 10.2.3 of ES3 and make argument and variable properties
107 * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
108 * avoid objects as activations, something we should do too.
110 uintN attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
;
114 StrictPropertyOp setter
;
115 uint32 slot
= JSObject::CALL_RESERVED_SLOTS
;
117 if (kind
== ARGUMENT
) {
118 JS_ASSERT(nvars
== 0);
119 JS_ASSERT(nupvars
== 0);
124 } else if (kind
== UPVAR
) {
126 getter
= GetCallUpvar
;
127 setter
= SetCallUpvar
;
128 slot
= SHAPE_INVALID_SLOT
;
130 JS_ASSERT(kind
== VARIABLE
|| kind
== CONSTANT
);
131 JS_ASSERT(nupvars
== 0);
136 if (kind
== CONSTANT
)
137 attrs
|= JSPROP_READONLY
;
138 slot
+= nargs
+ nvars
;
141 if (*indexp
== BINDING_COUNT_LIMIT
) {
142 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
144 ? JSMSG_TOO_MANY_FUN_ARGS
145 : JSMSG_TOO_MANY_LOCALS
);
151 JS_ASSERT(kind
== ARGUMENT
); /* destructuring */
152 id
= INT_TO_JSID(nargs
);
154 id
= ATOM_TO_JSID(name
);
157 Shape
child(id
, getter
, setter
, slot
, attrs
, Shape::HAS_SHORTID
, *indexp
);
159 Shape
*shape
= lastBinding
->getChild(cx
, child
, &lastBinding
);
163 JS_ASSERT(lastBinding
== shape
);
169 Bindings::getLocalNameArray(JSContext
*cx
, JSArenaPool
*pool
)
171 JS_ASSERT(lastBinding
);
173 JS_ASSERT(hasLocalNames());
175 uintN n
= countLocalNames();
178 JS_ASSERT(SIZE_MAX
/ size_t(n
) > sizeof *names
);
179 JS_ARENA_ALLOCATE_CAST(names
, jsuword
*, pool
, size_t(n
) * sizeof *names
);
181 js_ReportOutOfScriptQuota(cx
);
186 for (uintN i
= 0; i
!= n
; i
++)
187 names
[i
] = 0xdeadbeef;
190 for (Shape::Range r
= lastBinding
; !r
.empty(); r
.popFront()) {
191 const Shape
&shape
= r
.front();
192 uintN index
= uint16(shape
.shortid
);
193 jsuword constFlag
= 0;
195 if (shape
.getter() == GetCallArg
) {
196 JS_ASSERT(index
< nargs
);
197 } else if (shape
.getter() == GetCallUpvar
) {
198 JS_ASSERT(index
< nupvars
);
199 index
+= nargs
+ nvars
;
201 JS_ASSERT(index
< nvars
);
203 if (!shape
.writable())
208 if (JSID_IS_ATOM(shape
.id
)) {
209 atom
= JSID_TO_ATOM(shape
.id
);
211 JS_ASSERT(JSID_IS_INT(shape
.id
));
212 JS_ASSERT(shape
.getter() == GetCallArg
);
216 names
[index
] = jsuword(atom
);
220 for (uintN i
= 0; i
!= n
; i
++)
221 JS_ASSERT(names
[i
] != 0xdeadbeef);
227 Bindings::lastArgument() const
229 JS_ASSERT(lastBinding
);
231 const js::Shape
*shape
= lastVariable();
233 while (shape
->previous() && shape
->getter() != GetCallArg
)
234 shape
= shape
->previous();
240 Bindings::lastVariable() const
242 JS_ASSERT(lastBinding
);
244 const js::Shape
*shape
= lastUpvar();
246 while (shape
->getter() == GetCallUpvar
)
247 shape
= shape
->previous();
253 Bindings::lastUpvar() const
255 JS_ASSERT(lastBinding
);
260 Bindings::sharpSlotBase(JSContext
*cx
)
262 JS_ASSERT(lastBinding
);
263 #if JS_HAS_SHARP_VARS
264 if (JSAtom
*name
= js_Atomize(cx
, "#array", 6, 0)) {
265 uintN index
= uintN(-1);
269 lookup(cx
, name
, &index
);
270 JS_ASSERT(kind
== VARIABLE
);
278 Bindings::makeImmutable()
280 JS_ASSERT(lastBinding
);
281 Shape
*shape
= lastBinding
;
282 if (shape
->inDictionary()) {
284 JS_ASSERT(!shape
->frozen());
286 } while ((shape
= shape
->parent
) != NULL
);
291 Bindings::trace(JSTracer
*trc
)
293 for (const Shape
*shape
= lastBinding
; shape
; shape
= shape
->previous())
311 js_XDRScript(JSXDRState
*xdr
, JSScript
**scriptp
, JSBool
*hasMagic
)
316 uint32 length
, lineno
, nslots
;
317 uint32 natoms
, nsrcnotes
, ntrynotes
, nobjects
, nregexps
, nconsts
, i
;
318 uint32 prologLength
, version
, encodedClosedCount
;
319 uint16 nClosedArgs
= 0, nClosedVars
= 0;
320 JSPrincipals
*principals
;
322 JSBool filenameWasSaved
;
324 JSSecurityCallbacks
*callbacks
;
325 uint32 scriptBits
= 0;
327 JSContext
*cx
= xdr
->cx
;
328 JSScript
*script
= *scriptp
;
329 nsrcnotes
= ntrynotes
= natoms
= nobjects
= nregexps
= nconsts
= 0;
330 filenameWasSaved
= JS_FALSE
;
331 jssrcnote
*notes
= NULL
;
333 /* Should not XDR scripts optimized for a single global object. */
334 JS_ASSERT_IF(script
, !JSScript::isValidOffset(script
->globalsOffset
));
337 if (xdr
->mode
== JSXDR_ENCODE
)
338 magic
= JSXDR_MAGIC_SCRIPT_CURRENT
;
339 if (!JS_XDRUint32(xdr
, &magic
))
341 if (magic
!= JSXDR_MAGIC_SCRIPT_CURRENT
) {
342 /* We do not provide binary compatibility with older scripts. */
344 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
345 JSMSG_BAD_SCRIPT_MAGIC
);
348 *hasMagic
= JS_FALSE
;
354 /* XDR arguments, local vars, and upvars. */
355 uint16 nargs
, nvars
, nupvars
;
356 #if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
357 nargs
= nvars
= nupvars
= Bindings::BINDING_COUNT_LIMIT
;
359 uint32 argsVars
, paddingUpvars
;
360 if (xdr
->mode
== JSXDR_ENCODE
) {
361 nargs
= script
->bindings
.countArgs();
362 nvars
= script
->bindings
.countVars();
363 nupvars
= script
->bindings
.countUpvars();
364 argsVars
= (nargs
<< 16) | nvars
;
365 paddingUpvars
= nupvars
;
367 if (!JS_XDRUint32(xdr
, &argsVars
) || !JS_XDRUint32(xdr
, &paddingUpvars
))
369 if (xdr
->mode
== JSXDR_DECODE
) {
370 nargs
= argsVars
>> 16;
371 nvars
= argsVars
& 0xFFFF;
372 JS_ASSERT((paddingUpvars
>> 16) == 0);
373 nupvars
= paddingUpvars
& 0xFFFF;
375 JS_ASSERT(nargs
!= Bindings::BINDING_COUNT_LIMIT
);
376 JS_ASSERT(nvars
!= Bindings::BINDING_COUNT_LIMIT
);
377 JS_ASSERT(nupvars
!= Bindings::BINDING_COUNT_LIMIT
);
379 Bindings
bindings(cx
);
380 AutoBindingsRooter
rooter(cx
, bindings
);
381 uint32 nameCount
= nargs
+ nvars
+ nupvars
;
384 JSArenaPool
* const pool
;
386 AutoMark(JSArenaPool
*pool
) : pool(pool
), mark(JS_ARENA_MARK(pool
)) { }
388 JS_ARENA_RELEASE(pool
, mark
);
390 } automark(&cx
->tempPool
);
393 * To xdr the names we prefix the names with a bitmap descriptor and
394 * then xdr the names as strings. For argument names (indexes below
395 * nargs) the corresponding bit in the bitmap is unset when the name
396 * is null. Such null names are not encoded or decoded. For variable
397 * names (indexes starting from nargs) bitmap's bit is set when the
398 * name is declared as const, not as ordinary var.
400 uintN bitmapLength
= JS_HOWMANY(nameCount
, JS_BITS_PER_UINT32
);
402 JS_ARENA_ALLOCATE_CAST(bitmap
, uint32
*, &cx
->tempPool
,
403 bitmapLength
* sizeof *bitmap
);
405 js_ReportOutOfScriptQuota(cx
);
410 if (xdr
->mode
== JSXDR_ENCODE
) {
411 names
= script
->bindings
.getLocalNameArray(cx
, &cx
->tempPool
);
414 PodZero(bitmap
, bitmapLength
);
415 for (uintN i
= 0; i
< nameCount
; i
++) {
417 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
418 : JS_LOCAL_NAME_IS_CONST(names
[i
]))
420 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |= JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
426 names
= NULL
; /* quell GCC uninitialized warning */
429 for (uintN i
= 0; i
< bitmapLength
; ++i
) {
430 if (!JS_XDRUint32(xdr
, &bitmap
[i
]))
434 for (uintN i
= 0; i
< nameCount
; i
++) {
436 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] & JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))))
438 if (xdr
->mode
== JSXDR_DECODE
) {
440 if (!bindings
.addDestructuring(cx
, &dummy
))
443 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
449 if (xdr
->mode
== JSXDR_ENCODE
)
450 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
451 if (!js_XDRAtom(xdr
, &name
))
453 if (xdr
->mode
== JSXDR_DECODE
) {
454 BindingKind kind
= (i
< nargs
)
456 : (i
< uintN(nargs
+ nvars
))
457 ? (bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
458 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
462 if (!bindings
.add(cx
, name
, kind
))
467 if (xdr
->mode
== JSXDR_DECODE
)
468 bindings
.makeImmutable();
471 if (xdr
->mode
== JSXDR_ENCODE
)
472 length
= script
->length
;
473 if (!JS_XDRUint32(xdr
, &length
))
476 if (xdr
->mode
== JSXDR_ENCODE
) {
477 prologLength
= script
->main
- script
->code
;
478 JS_ASSERT(script
->getVersion() != JSVERSION_UNKNOWN
);
479 version
= (uint32
)script
->getVersion() | (script
->nfixed
<< 16);
480 lineno
= (uint32
)script
->lineno
;
481 nslots
= (uint32
)script
->nslots
;
482 nslots
= (uint32
)((script
->staticLevel
<< 16) | script
->nslots
);
483 natoms
= (uint32
)script
->atomMap
.length
;
485 /* Count the srcnotes, keeping notes pointing at the first one. */
486 notes
= script
->notes();
487 for (sn
= notes
; !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
))
489 nsrcnotes
= sn
- notes
;
490 nsrcnotes
++; /* room for the terminator */
492 if (JSScript::isValidOffset(script
->objectsOffset
))
493 nobjects
= script
->objects()->length
;
494 if (JSScript::isValidOffset(script
->upvarsOffset
))
495 JS_ASSERT(script
->bindings
.countUpvars() == script
->upvars()->length
);
496 if (JSScript::isValidOffset(script
->regexpsOffset
))
497 nregexps
= script
->regexps()->length
;
498 if (JSScript::isValidOffset(script
->trynotesOffset
))
499 ntrynotes
= script
->trynotes()->length
;
500 if (JSScript::isValidOffset(script
->constOffset
))
501 nconsts
= script
->consts()->length
;
503 nClosedArgs
= script
->nClosedArgs
;
504 nClosedVars
= script
->nClosedVars
;
505 encodedClosedCount
= (nClosedArgs
<< 16) | nClosedVars
;
507 if (script
->noScriptRval
)
508 scriptBits
|= (1 << NoScriptRval
);
509 if (script
->savedCallerFun
)
510 scriptBits
|= (1 << SavedCallerFun
);
511 if (script
->hasSharps
)
512 scriptBits
|= (1 << HasSharps
);
513 if (script
->strictModeCode
)
514 scriptBits
|= (1 << StrictModeCode
);
515 if (script
->usesEval
)
516 scriptBits
|= (1 << UsesEval
);
517 if (script
->usesArguments
)
518 scriptBits
|= (1 << UsesArguments
);
519 JS_ASSERT(!script
->compileAndGo
);
520 JS_ASSERT(!script
->hasSingletons
);
523 if (!JS_XDRUint32(xdr
, &prologLength
))
525 if (!JS_XDRUint32(xdr
, &version
))
529 * To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
532 if (!JS_XDRUint32(xdr
, &natoms
))
534 if (!JS_XDRUint32(xdr
, &nsrcnotes
))
536 if (!JS_XDRUint32(xdr
, &ntrynotes
))
538 if (!JS_XDRUint32(xdr
, &nobjects
))
540 if (!JS_XDRUint32(xdr
, &nregexps
))
542 if (!JS_XDRUint32(xdr
, &nconsts
))
544 if (!JS_XDRUint32(xdr
, &encodedClosedCount
))
546 if (!JS_XDRUint32(xdr
, &scriptBits
))
549 AutoScriptRooter
tvr(cx
, NULL
);
551 if (xdr
->mode
== JSXDR_DECODE
) {
552 nClosedArgs
= encodedClosedCount
>> 16;
553 nClosedVars
= encodedClosedCount
& 0xFFFF;
555 /* Note: version is packed into the 32b space with another 16b value. */
556 JSVersion version_
= JSVersion(version
& JS_BITMASK(16));
557 JS_ASSERT((version_
& VersionFlags::FULL_MASK
) == uintN(version_
));
558 script
= JSScript::NewScript(cx
, length
, nsrcnotes
, natoms
, nobjects
, nupvars
,
559 nregexps
, ntrynotes
, nconsts
, 0, nClosedArgs
,
560 nClosedVars
, version_
);
564 script
->bindings
.transfer(cx
, &bindings
);
566 script
->main
+= prologLength
;
567 script
->nfixed
= uint16(version
>> 16);
569 /* If we know nsrcnotes, we allocated space for notes in script. */
570 notes
= script
->notes();
572 tvr
.setScript(script
);
574 if (scriptBits
& (1 << NoScriptRval
))
575 script
->noScriptRval
= true;
576 if (scriptBits
& (1 << SavedCallerFun
))
577 script
->savedCallerFun
= true;
578 if (scriptBits
& (1 << HasSharps
))
579 script
->hasSharps
= true;
580 if (scriptBits
& (1 << StrictModeCode
))
581 script
->strictModeCode
= true;
582 if (scriptBits
& (1 << UsesEval
))
583 script
->usesEval
= true;
584 if (scriptBits
& (1 << UsesArguments
))
585 script
->usesArguments
= true;
589 * Control hereafter must goto error on failure, in order for the
590 * DECODE case to destroy script.
592 oldscript
= xdr
->script
;
594 if (xdr
->mode
== JSXDR_ENCODE
) {
595 code
= js_UntrapScriptCode(cx
, script
);
600 xdr
->script
= script
;
601 ok
= JS_XDRBytes(xdr
, (char *) code
, length
* sizeof(jsbytecode
));
603 if (code
!= script
->code
)
609 if (!JS_XDRBytes(xdr
, (char *)notes
, nsrcnotes
* sizeof(jssrcnote
)) ||
610 !JS_XDRCStringOrNull(xdr
, (char **)&script
->filename
) ||
611 !JS_XDRUint32(xdr
, &lineno
) ||
612 !JS_XDRUint32(xdr
, &nslots
)) {
616 callbacks
= JS_GetSecurityCallbacks(cx
);
617 if (xdr
->mode
== JSXDR_ENCODE
) {
618 principals
= script
->principals
;
619 encodeable
= callbacks
&& callbacks
->principalsTranscoder
;
620 if (!JS_XDRUint32(xdr
, &encodeable
))
623 !callbacks
->principalsTranscoder(xdr
, &principals
)) {
627 if (!JS_XDRUint32(xdr
, &encodeable
))
630 if (!(callbacks
&& callbacks
->principalsTranscoder
)) {
631 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
632 JSMSG_CANT_DECODE_PRINCIPALS
);
635 if (!callbacks
->principalsTranscoder(xdr
, &principals
))
637 script
->principals
= principals
;
641 if (xdr
->mode
== JSXDR_DECODE
) {
642 const char *filename
= script
->filename
;
644 filename
= js_SaveScriptFilename(cx
, filename
);
647 cx
->free((void *) script
->filename
);
648 script
->filename
= filename
;
649 filenameWasSaved
= JS_TRUE
;
651 script
->lineno
= (uintN
)lineno
;
652 script
->nslots
= (uint16
)nslots
;
653 script
->staticLevel
= (uint16
)(nslots
>> 16);
657 for (i
= 0; i
!= natoms
; ++i
) {
658 if (!js_XDRAtom(xdr
, &script
->atomMap
.vector
[i
]))
663 * Here looping from 0-to-length to xdr objects is essential. It ensures
664 * that block objects from the script->objects array will be written and
665 * restored in the outer-to-inner order. js_XDRBlockObject relies on this
666 * to restore the parent chain.
668 for (i
= 0; i
!= nobjects
; ++i
) {
669 JSObject
**objp
= &script
->objects()->vector
[i
];
671 if (xdr
->mode
== JSXDR_ENCODE
) {
672 Class
*clasp
= (*objp
)->getClass();
673 JS_ASSERT(clasp
== &js_FunctionClass
||
674 clasp
== &js_BlockClass
);
675 isBlock
= (clasp
== &js_BlockClass
) ? 1 : 0;
677 if (!JS_XDRUint32(xdr
, &isBlock
))
680 if (!js_XDRFunctionObject(xdr
, objp
))
683 JS_ASSERT(isBlock
== 1);
684 if (!js_XDRBlockObject(xdr
, objp
))
688 for (i
= 0; i
!= nupvars
; ++i
) {
689 if (!JS_XDRUint32(xdr
, reinterpret_cast<uint32
*>(&script
->upvars()->vector
[i
])))
692 for (i
= 0; i
!= nregexps
; ++i
) {
693 if (!js_XDRRegExpObject(xdr
, &script
->regexps()->vector
[i
]))
696 for (i
= 0; i
!= nClosedArgs
; ++i
) {
697 if (!JS_XDRUint32(xdr
, &script
->closedSlots
[i
]))
700 for (i
= 0; i
!= nClosedVars
; ++i
) {
701 if (!JS_XDRUint32(xdr
, &script
->closedSlots
[nClosedArgs
+ i
]))
705 if (ntrynotes
!= 0) {
707 * We combine tn->kind and tn->stackDepth when serializing as XDR is not
708 * efficient when serializing small integer types.
710 JSTryNote
*tn
, *tnfirst
;
712 JS_STATIC_ASSERT(sizeof(tn
->kind
) == sizeof(uint8
));
713 JS_STATIC_ASSERT(sizeof(tn
->stackDepth
) == sizeof(uint16
));
715 tnfirst
= script
->trynotes()->vector
;
716 JS_ASSERT(script
->trynotes()->length
== ntrynotes
);
717 tn
= tnfirst
+ ntrynotes
;
720 if (xdr
->mode
== JSXDR_ENCODE
) {
721 kindAndDepth
= ((uint32
)tn
->kind
<< 16)
722 | (uint32
)tn
->stackDepth
;
724 if (!JS_XDRUint32(xdr
, &kindAndDepth
) ||
725 !JS_XDRUint32(xdr
, &tn
->start
) ||
726 !JS_XDRUint32(xdr
, &tn
->length
)) {
729 if (xdr
->mode
== JSXDR_DECODE
) {
730 tn
->kind
= (uint8
)(kindAndDepth
>> 16);
731 tn
->stackDepth
= (uint16
)kindAndDepth
;
733 } while (tn
!= tnfirst
);
736 for (i
= 0; i
!= nconsts
; ++i
) {
737 if (!JS_XDRValue(xdr
, Jsvalify(&script
->consts()->vector
[i
])))
741 xdr
->script
= oldscript
;
745 if (xdr
->mode
== JSXDR_DECODE
) {
746 if (script
->filename
&& !filenameWasSaved
) {
747 cx
->free((void *) script
->filename
);
748 script
->filename
= NULL
;
750 js_DestroyScript(cx
, script
);
753 xdr
->script
= oldscript
;
757 #endif /* JS_HAS_XDR */
760 script_finalize(JSContext
*cx
, JSObject
*obj
)
762 JSScript
*script
= (JSScript
*) obj
->getPrivate();
764 js_DestroyScriptFromGC(cx
, script
);
768 script_trace(JSTracer
*trc
, JSObject
*obj
)
770 JSScript
*script
= (JSScript
*) obj
->getPrivate();
772 js_TraceScript(trc
, script
);
775 Class js_ScriptClass
= {
777 JSCLASS_HAS_PRIVATE
|
778 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
779 PropertyStub
, /* addProperty */
780 PropertyStub
, /* delProperty */
781 PropertyStub
, /* getProperty */
782 StrictPropertyStub
, /* setProperty */
787 NULL
, /* reserved0 */
788 NULL
, /* checkAccess */
790 NULL
, /* construct */
791 NULL
, /* xdrObject */
792 NULL
, /* hasInstance */
793 JS_CLASS_TRACE(script_trace
)
797 * Shared script filename management.
800 js_compare_strings(const void *k1
, const void *k2
)
802 return strcmp((const char *) k1
, (const char *) k2
) == 0;
805 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
806 typedef struct ScriptFilenameEntry
{
807 JSHashEntry
*next
; /* hash chain linkage */
808 JSHashNumber keyHash
; /* key hash function result */
809 const void *key
; /* ptr to filename, below */
810 uint32 flags
; /* user-defined filename prefix flags */
811 JSPackedBool mark
; /* GC mark flag */
812 char filename
[3]; /* two or more bytes, NUL-terminated */
813 } ScriptFilenameEntry
;
816 js_alloc_table_space(void *priv
, size_t size
)
818 return js_malloc(size
);
822 js_free_table_space(void *priv
, void *item
, size_t size
)
828 js_alloc_sftbl_entry(void *priv
, const void *key
)
830 size_t nbytes
= offsetof(ScriptFilenameEntry
, filename
) +
831 strlen((const char *) key
) + 1;
833 return (JSHashEntry
*) js_malloc(JS_MAX(nbytes
, sizeof(JSHashEntry
)));
837 js_free_sftbl_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
839 if (flag
!= HT_FREE_ENTRY
)
844 static JSHashAllocOps sftbl_alloc_ops
= {
845 js_alloc_table_space
, js_free_table_space
,
846 js_alloc_sftbl_entry
, js_free_sftbl_entry
850 FinishRuntimeScriptState(JSRuntime
*rt
)
852 if (rt
->scriptFilenameTable
) {
853 JS_HashTableDestroy(rt
->scriptFilenameTable
);
854 rt
->scriptFilenameTable
= NULL
;
857 if (rt
->scriptFilenameTableLock
) {
858 JS_DESTROY_LOCK(rt
->scriptFilenameTableLock
);
859 rt
->scriptFilenameTableLock
= NULL
;
865 js_InitRuntimeScriptState(JSRuntime
*rt
)
868 JS_ASSERT(!rt
->scriptFilenameTableLock
);
869 rt
->scriptFilenameTableLock
= JS_NEW_LOCK();
870 if (!rt
->scriptFilenameTableLock
)
873 JS_ASSERT(!rt
->scriptFilenameTable
);
874 rt
->scriptFilenameTable
=
875 JS_NewHashTable(16, JS_HashString
, js_compare_strings
, NULL
,
876 &sftbl_alloc_ops
, NULL
);
877 if (!rt
->scriptFilenameTable
) {
878 FinishRuntimeScriptState(rt
); /* free lock if threadsafe */
881 JS_INIT_CLIST(&rt
->scriptFilenamePrefixes
);
885 typedef struct ScriptFilenamePrefix
{
886 JSCList links
; /* circular list linkage for easy deletion */
887 const char *name
; /* pointer to pinned ScriptFilenameEntry string */
888 size_t length
; /* prefix string length, precomputed */
889 uint32 flags
; /* user-defined flags to inherit from this prefix */
890 } ScriptFilenamePrefix
;
893 js_FreeRuntimeScriptState(JSRuntime
*rt
)
895 if (!rt
->scriptFilenameTable
)
898 while (!JS_CLIST_IS_EMPTY(&rt
->scriptFilenamePrefixes
)) {
899 ScriptFilenamePrefix
*sfp
= (ScriptFilenamePrefix
*)
900 rt
->scriptFilenamePrefixes
.next
;
901 JS_REMOVE_LINK(&sfp
->links
);
904 FinishRuntimeScriptState(rt
);
911 size_t sftbl_savings
= 0;
914 static ScriptFilenameEntry
*
915 SaveScriptFilename(JSRuntime
*rt
, const char *filename
, uint32 flags
)
920 ScriptFilenameEntry
*sfe
;
922 JSCList
*head
, *link
;
923 ScriptFilenamePrefix
*sfp
;
925 table
= rt
->scriptFilenameTable
;
926 hash
= JS_HashString(filename
);
927 hep
= JS_HashTableRawLookup(table
, hash
, filename
);
928 sfe
= (ScriptFilenameEntry
*) *hep
;
931 sftbl_savings
+= strlen(sfe
->filename
);
935 sfe
= (ScriptFilenameEntry
*)
936 JS_HashTableRawAdd(table
, hep
, hash
, filename
, NULL
);
939 sfe
->key
= strcpy(sfe
->filename
, filename
);
941 sfe
->mark
= JS_FALSE
;
944 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
946 /* Search in case filename was saved already; we must be idempotent. */
948 length
= strlen(filename
);
949 for (head
= link
= &rt
->scriptFilenamePrefixes
;
952 /* Lag link behind sfp to insert in non-increasing length order. */
953 sfp
= (ScriptFilenamePrefix
*) link
->next
;
954 if (!strcmp(sfp
->name
, filename
))
956 if (sfp
->length
<= length
) {
964 /* No such prefix: add one now. */
965 sfp
= (ScriptFilenamePrefix
*) js_malloc(sizeof(ScriptFilenamePrefix
));
968 JS_INSERT_AFTER(&sfp
->links
, link
);
969 sfp
->name
= sfe
->filename
;
970 sfp
->length
= length
;
975 * Accumulate flags in both sfe and sfp: sfe for later access from the
976 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
977 * filename entries can inherit by prefix.
984 if (rt
->functionMeterFilename
) {
985 size_t len
= strlen(sfe
->filename
);
986 if (len
>= sizeof rt
->lastScriptFilename
)
987 len
= sizeof rt
->lastScriptFilename
- 1;
988 memcpy(rt
->lastScriptFilename
, sfe
->filename
, len
);
989 rt
->lastScriptFilename
[len
] = '\0';
997 js_SaveScriptFilename(JSContext
*cx
, const char *filename
)
1000 ScriptFilenameEntry
*sfe
;
1001 JSCList
*head
, *link
;
1002 ScriptFilenamePrefix
*sfp
;
1005 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
1006 sfe
= SaveScriptFilename(rt
, filename
, 0);
1008 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1009 JS_ReportOutOfMemory(cx
);
1014 * Try to inherit flags by prefix. We assume there won't be more than a
1015 * few (dozen! ;-) prefixes, so linear search is tolerable.
1016 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1018 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
1020 link
= link
->next
) {
1021 sfp
= (ScriptFilenamePrefix
*) link
;
1022 if (!strncmp(sfp
->name
, filename
, sfp
->length
)) {
1023 sfe
->flags
|= sfp
->flags
;
1027 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1028 return sfe
->filename
;
1032 js_SaveScriptFilenameRT(JSRuntime
*rt
, const char *filename
, uint32 flags
)
1034 ScriptFilenameEntry
*sfe
;
1036 /* This may be called very early, via the jsdbgapi.h entry point. */
1037 if (!rt
->scriptFilenameTable
&& !js_InitRuntimeScriptState(rt
))
1040 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
1041 sfe
= SaveScriptFilename(rt
, filename
, flags
);
1042 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1046 return sfe
->filename
;
1050 * Back up from a saved filename by its offset within its hash table entry.
1052 #define FILENAME_TO_SFE(fn) \
1053 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1056 * The sfe->key member, redundant given sfe->filename but required by the old
1057 * jshash.c code, here gives us a useful sanity check. This assertion will
1058 * very likely botch if someone tries to mark a string that wasn't allocated
1059 * as an sfe->filename.
1061 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
1064 js_GetScriptFilenameFlags(const char *filename
)
1066 ScriptFilenameEntry
*sfe
;
1068 sfe
= FILENAME_TO_SFE(filename
);
1069 ASSERT_VALID_SFE(sfe
);
1074 js_MarkScriptFilename(const char *filename
)
1076 ScriptFilenameEntry
*sfe
;
1078 sfe
= FILENAME_TO_SFE(filename
);
1079 ASSERT_VALID_SFE(sfe
);
1080 sfe
->mark
= JS_TRUE
;
1084 js_script_filename_marker(JSHashEntry
*he
, intN i
, void *arg
)
1086 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
1088 sfe
->mark
= JS_TRUE
;
1089 return HT_ENUMERATE_NEXT
;
1093 js_MarkScriptFilenames(JSRuntime
*rt
)
1095 JSCList
*head
, *link
;
1096 ScriptFilenamePrefix
*sfp
;
1098 if (!rt
->scriptFilenameTable
)
1101 if (rt
->gcKeepAtoms
) {
1102 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
1103 js_script_filename_marker
,
1106 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
1108 link
= link
->next
) {
1109 sfp
= (ScriptFilenamePrefix
*) link
;
1110 js_MarkScriptFilename(sfp
->name
);
1115 js_script_filename_sweeper(JSHashEntry
*he
, intN i
, void *arg
)
1117 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
1120 return HT_ENUMERATE_REMOVE
;
1121 sfe
->mark
= JS_FALSE
;
1122 return HT_ENUMERATE_NEXT
;
1126 js_SweepScriptFilenames(JSRuntime
*rt
)
1128 if (!rt
->scriptFilenameTable
)
1132 * JS_HashTableEnumerateEntries shrinks the table if many entries are
1133 * removed preventing wasting memory on a too sparse table.
1135 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
1136 js_script_filename_sweeper
,
1140 printf("script filename table savings so far: %u\n", sftbl_savings
);
1146 * JSScript data structures memory alignment:
1149 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
1150 * use script->objects() to access it.
1151 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
1152 * use script->regexps() to access it.
1153 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
1154 * != 0, use script->trynotes() to access it.
1155 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
1156 * JSScript.atomMap.vector if any.
1157 * JSObject *o[] array of script->objects()->length objects if any
1158 * pointed by script->objects()->vector.
1159 * JSObject *r[] array of script->regexps()->length regexps if any
1160 * pointed by script->regexps()->vector.
1161 * JSTryNote t[] array of script->trynotes()->length try notes if any
1162 * pointed by script->trynotes()->vector.
1163 * jsbytecode b[] script bytecode pointed by JSScript.code.
1164 * jssrcnote s[] script source notes, use script->notes() to access it
1166 * The alignment avoids gaps between entries as alignment requirement for each
1167 * subsequent structure or array is the same or divides the alignment
1168 * requirement for the previous one.
1170 * The followings asserts checks that assuming that the alignment requirement
1171 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
1172 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
1174 JS_STATIC_ASSERT(sizeof(JSScript
) % sizeof(void *) == 0);
1175 JS_STATIC_ASSERT(sizeof(JSObjectArray
) % sizeof(void *) == 0);
1176 JS_STATIC_ASSERT(sizeof(JSTryNoteArray
) == sizeof(JSObjectArray
));
1177 JS_STATIC_ASSERT(sizeof(JSAtom
*) == sizeof(JSObject
*));
1178 JS_STATIC_ASSERT(sizeof(JSObject
*) % sizeof(uint32
) == 0);
1179 JS_STATIC_ASSERT(sizeof(JSTryNote
) == 3 * sizeof(uint32
));
1180 JS_STATIC_ASSERT(sizeof(uint32
) % sizeof(jsbytecode
) == 0);
1181 JS_STATIC_ASSERT(sizeof(jsbytecode
) % sizeof(jssrcnote
) == 0);
1184 * Check that uint8 offsets is enough to reach any optional array allocated
1185 * after JSScript. For that we check that the maximum possible offset for
1186 * JSConstArray, that last optional array, still fits 1 byte and do not
1187 * coincide with INVALID_OFFSET.
1189 JS_STATIC_ASSERT(sizeof(JSObjectArray
) +
1190 sizeof(JSUpvarArray
) +
1191 sizeof(JSObjectArray
) +
1192 sizeof(JSTryNoteArray
) +
1193 sizeof(js::GlobalSlotArray
)
1194 < JSScript::INVALID_OFFSET
);
1195 JS_STATIC_ASSERT(JSScript::INVALID_OFFSET
<= 255);
1198 JSScript::NewScript(JSContext
*cx
, uint32 length
, uint32 nsrcnotes
, uint32 natoms
,
1199 uint32 nobjects
, uint32 nupvars
, uint32 nregexps
,
1200 uint32 ntrynotes
, uint32 nconsts
, uint32 nglobals
,
1201 uint16 nClosedArgs
, uint16 nClosedVars
, JSVersion version
)
1203 size_t size
, vectorSize
;
1206 unsigned constPadding
= 0;
1208 uint32 totalClosed
= nClosedArgs
+ nClosedVars
;
1210 size
= sizeof(JSScript
) +
1211 sizeof(JSAtom
*) * natoms
;
1214 size
+= sizeof(JSObjectArray
) + nobjects
* sizeof(JSObject
*);
1216 size
+= sizeof(JSUpvarArray
) + nupvars
* sizeof(uint32
);
1218 size
+= sizeof(JSObjectArray
) + nregexps
* sizeof(JSObject
*);
1220 size
+= sizeof(JSTryNoteArray
) + ntrynotes
* sizeof(JSTryNote
);
1222 size
+= sizeof(GlobalSlotArray
) + nglobals
* sizeof(GlobalSlotArray::Entry
);
1223 if (totalClosed
!= 0)
1224 size
+= totalClosed
* sizeof(uint32
);
1227 size
+= sizeof(JSConstArray
);
1229 * Calculate padding assuming that consts go after the other arrays,
1230 * but before the bytecode and source notes.
1232 constPadding
= (8 - (size
% 8)) % 8;
1233 size
+= constPadding
+ nconsts
* sizeof(Value
);
1236 size
+= length
* sizeof(jsbytecode
) +
1237 nsrcnotes
* sizeof(jssrcnote
);
1239 script
= (JSScript
*) cx
->malloc(size
);
1244 script
->length
= length
;
1245 script
->version
= version
;
1246 new (&script
->bindings
) Bindings(cx
);
1248 uint8
*scriptEnd
= reinterpret_cast<uint8
*>(script
+ 1);
1251 if (nobjects
!= 0) {
1252 script
->objectsOffset
= (uint8
)(cursor
- scriptEnd
);
1253 cursor
+= sizeof(JSObjectArray
);
1255 script
->objectsOffset
= JSScript::INVALID_OFFSET
;
1258 script
->upvarsOffset
= (uint8
)(cursor
- scriptEnd
);
1259 cursor
+= sizeof(JSUpvarArray
);
1261 script
->upvarsOffset
= JSScript::INVALID_OFFSET
;
1263 if (nregexps
!= 0) {
1264 script
->regexpsOffset
= (uint8
)(cursor
- scriptEnd
);
1265 cursor
+= sizeof(JSObjectArray
);
1267 script
->regexpsOffset
= JSScript::INVALID_OFFSET
;
1269 if (ntrynotes
!= 0) {
1270 script
->trynotesOffset
= (uint8
)(cursor
- scriptEnd
);
1271 cursor
+= sizeof(JSTryNoteArray
);
1273 script
->trynotesOffset
= JSScript::INVALID_OFFSET
;
1275 if (nglobals
!= 0) {
1276 script
->globalsOffset
= (uint8
)(cursor
- scriptEnd
);
1277 cursor
+= sizeof(GlobalSlotArray
);
1279 script
->globalsOffset
= JSScript::INVALID_OFFSET
;
1281 JS_ASSERT(cursor
- scriptEnd
< 0xFF);
1283 script
->constOffset
= (uint8
)(cursor
- scriptEnd
);
1284 cursor
+= sizeof(JSConstArray
);
1286 script
->constOffset
= JSScript::INVALID_OFFSET
;
1289 JS_STATIC_ASSERT(sizeof(JSObjectArray
) +
1290 sizeof(JSUpvarArray
) +
1291 sizeof(JSObjectArray
) +
1292 sizeof(JSTryNoteArray
) +
1293 sizeof(GlobalSlotArray
) < 0xFF);
1296 script
->atomMap
.length
= natoms
;
1297 script
->atomMap
.vector
= (JSAtom
**)cursor
;
1298 vectorSize
= natoms
* sizeof(script
->atomMap
.vector
[0]);
1301 * Clear object map's vector so the GC tracing can run when not yet
1302 * all atoms are copied to the array.
1304 memset(cursor
, 0, vectorSize
);
1305 cursor
+= vectorSize
;
1308 if (nobjects
!= 0) {
1309 script
->objects()->length
= nobjects
;
1310 script
->objects()->vector
= (JSObject
**)cursor
;
1311 vectorSize
= nobjects
* sizeof(script
->objects()->vector
[0]);
1312 memset(cursor
, 0, vectorSize
);
1313 cursor
+= vectorSize
;
1316 if (nregexps
!= 0) {
1317 script
->regexps()->length
= nregexps
;
1318 script
->regexps()->vector
= (JSObject
**)cursor
;
1319 vectorSize
= nregexps
* sizeof(script
->regexps()->vector
[0]);
1320 memset(cursor
, 0, vectorSize
);
1321 cursor
+= vectorSize
;
1324 if (ntrynotes
!= 0) {
1325 script
->trynotes()->length
= ntrynotes
;
1326 script
->trynotes()->vector
= (JSTryNote
*)cursor
;
1327 vectorSize
= ntrynotes
* sizeof(script
->trynotes()->vector
[0]);
1329 memset(cursor
, 0, vectorSize
);
1331 cursor
+= vectorSize
;
1334 if (nglobals
!= 0) {
1335 script
->globals()->length
= nglobals
;
1336 script
->globals()->vector
= (GlobalSlotArray::Entry
*)cursor
;
1337 vectorSize
= nglobals
* sizeof(script
->globals()->vector
[0]);
1338 cursor
+= vectorSize
;
1341 if (totalClosed
!= 0) {
1342 script
->nClosedArgs
= nClosedArgs
;
1343 script
->nClosedVars
= nClosedVars
;
1344 script
->closedSlots
= (uint32
*)cursor
;
1345 cursor
+= totalClosed
* sizeof(uint32
);
1349 * NB: We allocate the vector of uint32 upvar cookies after all vectors of
1350 * pointers, to avoid misalignment on 64-bit platforms. See bug 514645.
1353 script
->upvars()->length
= nupvars
;
1354 script
->upvars()->vector
= reinterpret_cast<UpvarCookie
*>(cursor
);
1355 vectorSize
= nupvars
* sizeof(script
->upvars()->vector
[0]);
1356 memset(cursor
, 0, vectorSize
);
1357 cursor
+= vectorSize
;
1360 /* Must go after other arrays; see constPadding definition. */
1362 cursor
+= constPadding
;
1363 script
->consts()->length
= nconsts
;
1364 script
->consts()->vector
= (Value
*)cursor
;
1365 JS_ASSERT((size_t)cursor
% sizeof(double) == 0);
1366 vectorSize
= nconsts
* sizeof(script
->consts()->vector
[0]);
1367 memset(cursor
, 0, vectorSize
);
1368 cursor
+= vectorSize
;
1371 script
->code
= script
->main
= (jsbytecode
*)cursor
;
1373 length
* sizeof(jsbytecode
) +
1374 nsrcnotes
* sizeof(jssrcnote
) ==
1375 (uint8
*)script
+ size
);
1377 script
->compartment
= cx
->compartment
;
1378 #ifdef CHECK_SCRIPT_OWNER
1379 script
->owner
= cx
->thread
;
1382 JS_APPEND_LINK(&script
->links
, &cx
->compartment
->scripts
);
1383 JS_ASSERT(script
->getVersion() == version
);
1388 JSScript::NewScriptFromCG(JSContext
*cx
, JSCodeGenerator
*cg
)
1390 uint32 mainLength
, prologLength
, nsrcnotes
, nfixed
;
1392 const char *filename
;
1395 /* The counts of indexed things must be checked during code generation. */
1396 JS_ASSERT(cg
->atomList
.count
<= INDEX_LIMIT
);
1397 JS_ASSERT(cg
->objectList
.length
<= INDEX_LIMIT
);
1398 JS_ASSERT(cg
->regexpList
.length
<= INDEX_LIMIT
);
1400 mainLength
= CG_OFFSET(cg
);
1401 prologLength
= CG_PROLOG_OFFSET(cg
);
1403 CG_COUNT_FINAL_SRCNOTES(cg
, nsrcnotes
);
1404 uint16 nClosedArgs
= uint16(cg
->closedArgs
.length());
1405 JS_ASSERT(nClosedArgs
== cg
->closedArgs
.length());
1406 uint16 nClosedVars
= uint16(cg
->closedVars
.length());
1407 JS_ASSERT(nClosedVars
== cg
->closedVars
.length());
1408 script
= NewScript(cx
, prologLength
+ mainLength
, nsrcnotes
,
1409 cg
->atomList
.count
, cg
->objectList
.length
,
1410 cg
->upvarList
.count
, cg
->regexpList
.length
,
1411 cg
->ntrynotes
, cg
->constList
.length(),
1412 cg
->globalUses
.length(), nClosedArgs
, nClosedVars
, cg
->version());
1416 /* Now that we have script, error control flow must go to label bad. */
1417 script
->main
+= prologLength
;
1418 memcpy(script
->code
, CG_PROLOG_BASE(cg
), prologLength
* sizeof(jsbytecode
));
1419 memcpy(script
->main
, CG_BASE(cg
), mainLength
* sizeof(jsbytecode
));
1420 nfixed
= cg
->inFunction()
1421 ? cg
->bindings
.countVars()
1423 JS_ASSERT(nfixed
< SLOTNO_LIMIT
);
1424 script
->nfixed
= (uint16
) nfixed
;
1425 js_InitAtomMap(cx
, &script
->atomMap
, &cg
->atomList
);
1427 filename
= cg
->parser
->tokenStream
.getFilename();
1429 script
->filename
= js_SaveScriptFilename(cx
, filename
);
1430 if (!script
->filename
)
1433 script
->lineno
= cg
->firstLine
;
1434 if (script
->nfixed
+ cg
->maxStackDepth
>= JS_BIT(16)) {
1435 ReportCompileErrorNumber(cx
, CG_TS(cg
), NULL
, JSREPORT_ERROR
, JSMSG_NEED_DIET
, "script");
1438 script
->nslots
= script
->nfixed
+ cg
->maxStackDepth
;
1439 script
->staticLevel
= uint16(cg
->staticLevel
);
1440 script
->principals
= cg
->parser
->principals
;
1441 if (script
->principals
)
1442 JSPRINCIPALS_HOLD(cx
, script
->principals
);
1444 if (!js_FinishTakingSrcNotes(cx
, cg
, script
->notes()))
1446 if (cg
->ntrynotes
!= 0)
1447 js_FinishTakingTryNotes(cg
, script
->trynotes());
1448 if (cg
->objectList
.length
!= 0)
1449 cg
->objectList
.finish(script
->objects());
1450 if (cg
->regexpList
.length
!= 0)
1451 cg
->regexpList
.finish(script
->regexps());
1452 if (cg
->constList
.length() != 0)
1453 cg
->constList
.finish(script
->consts());
1454 if (cg
->flags
& TCF_NO_SCRIPT_RVAL
)
1455 script
->noScriptRval
= true;
1456 if (cg
->hasSharps())
1457 script
->hasSharps
= true;
1458 if (cg
->flags
& TCF_STRICT_MODE_CODE
)
1459 script
->strictModeCode
= true;
1460 if (cg
->flags
& TCF_COMPILE_N_GO
)
1461 script
->compileAndGo
= true;
1462 if (cg
->callsEval())
1463 script
->usesEval
= true;
1464 if (cg
->flags
& TCF_FUN_USES_ARGUMENTS
)
1465 script
->usesArguments
= true;
1466 if (cg
->flags
& TCF_HAS_SINGLETONS
)
1467 script
->hasSingletons
= true;
1469 if (cg
->upvarList
.count
!= 0) {
1470 JS_ASSERT(cg
->upvarList
.count
<= cg
->upvarMap
.length
);
1471 memcpy(script
->upvars()->vector
, cg
->upvarMap
.vector
,
1472 cg
->upvarList
.count
* sizeof(uint32
));
1473 cg
->upvarList
.clear();
1474 cx
->free(cg
->upvarMap
.vector
);
1475 cg
->upvarMap
.vector
= NULL
;
1478 if (cg
->globalUses
.length()) {
1479 memcpy(script
->globals()->vector
, &cg
->globalUses
[0],
1480 cg
->globalUses
.length() * sizeof(GlobalSlotArray::Entry
));
1483 if (script
->nClosedArgs
)
1484 memcpy(script
->closedSlots
, &cg
->closedArgs
[0], script
->nClosedArgs
* sizeof(uint32
));
1485 if (script
->nClosedVars
) {
1486 memcpy(&script
->closedSlots
[script
->nClosedArgs
], &cg
->closedVars
[0],
1487 script
->nClosedVars
* sizeof(uint32
));
1490 cg
->bindings
.makeImmutable();
1491 script
->bindings
.transfer(cx
, &cg
->bindings
);
1494 * We initialize fun->u.script to be the script constructed above
1495 * so that the debugger has a valid FUN_SCRIPT(fun).
1498 if (cg
->inFunction()) {
1500 JS_ASSERT(fun
->isInterpreted());
1501 JS_ASSERT(!fun
->script());
1503 if (JSScript::isValidOffset(script
->upvarsOffset
))
1504 JS_ASSERT(script
->upvars()->length
== script
->bindings
.countUpvars());
1506 JS_ASSERT(script
->bindings
.countUpvars() == 0);
1508 fun
->u
.i
.script
= script
;
1509 #ifdef CHECK_SCRIPT_OWNER
1510 script
->owner
= NULL
;
1512 if (cg
->flags
& TCF_FUN_HEAVYWEIGHT
)
1513 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
1516 /* Tell the debugger about this compiled script. */
1517 js_CallNewScriptHook(cx
, script
, fun
);
1520 jsrefcount newEmptyLive
, newLive
, newTotal
;
1521 if (script
->isEmpty()) {
1522 newEmptyLive
= JS_RUNTIME_METER(cx
->runtime
, liveEmptyScripts
);
1523 newLive
= cx
->runtime
->liveScripts
;
1525 JS_RUNTIME_METER(cx
->runtime
, totalEmptyScripts
) + cx
->runtime
->totalScripts
;
1527 newEmptyLive
= cx
->runtime
->liveEmptyScripts
;
1528 newLive
= JS_RUNTIME_METER(cx
->runtime
, liveScripts
);
1530 cx
->runtime
->totalEmptyScripts
+ JS_RUNTIME_METER(cx
->runtime
, totalScripts
);
1533 jsrefcount oldHigh
= cx
->runtime
->highWaterLiveScripts
;
1534 if (newEmptyLive
+ newLive
> oldHigh
) {
1535 JS_ATOMIC_SET(&cx
->runtime
->highWaterLiveScripts
, newEmptyLive
+ newLive
);
1536 if (getenv("JS_DUMP_LIVE_SCRIPTS")) {
1537 fprintf(stderr
, "high water script count: %d empty, %d not (total %d)\n",
1538 newEmptyLive
, newLive
, newTotal
);
1547 js_DestroyScript(cx
, script
);
1552 js_CallNewScriptHook(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
)
1554 JSNewScriptHook hook
;
1556 hook
= cx
->debugHooks
->newScriptHook
;
1558 AutoKeepAtoms
keep(cx
->runtime
);
1559 hook(cx
, script
->filename
, script
->lineno
, script
, fun
,
1560 cx
->debugHooks
->newScriptHookData
);
1565 js_CallDestroyScriptHook(JSContext
*cx
, JSScript
*script
)
1567 JSDestroyScriptHook hook
;
1569 hook
= cx
->debugHooks
->destroyScriptHook
;
1571 hook(cx
, script
, cx
->debugHooks
->destroyScriptHookData
);
1572 JS_ClearScriptTraps(cx
, script
);
1576 DestroyScript(JSContext
*cx
, JSScript
*script
)
1579 if (script
->isEmpty())
1580 JS_RUNTIME_UNMETER(cx
->runtime
, liveEmptyScripts
);
1582 JS_RUNTIME_UNMETER(cx
->runtime
, liveScripts
);
1585 if (script
->principals
)
1586 JSPRINCIPALS_DROP(cx
, script
->principals
);
1588 if (JS_GSN_CACHE(cx
).code
== script
->code
)
1589 JS_PURGE_GSN_CACHE(cx
);
1592 * Worry about purging the property cache and any compiled traces related
1593 * to its bytecode if this script is being destroyed from JS_DestroyScript
1594 * or equivalent according to a mandatory "New/Destroy" protocol.
1596 * The GC purges all property caches when regenerating shapes upon shape
1597 * generator overflow, so no need in that event to purge just the entries
1600 * The GC purges trace-JITted code on every GC activation, not just when
1601 * regenerating shapes, so we don't have to purge fragments if the GC is
1602 * currently running.
1604 * JS_THREADSAFE note: The code below purges only the current thread's
1605 * property cache, so a script not owned by a function or object, which
1606 * hands off lifetime management for that script to the GC, must be used by
1607 * only one thread over its lifetime.
1609 * This should be an API-compatible change, since a script is never safe
1610 * against premature GC if shared among threads without a rooted object
1611 * wrapping it to protect the script's mapped atoms against GC. We use
1612 * script->owner to enforce this requirement via assertions.
1614 #ifdef CHECK_SCRIPT_OWNER
1615 JS_ASSERT_IF(cx
->runtime
->gcRunning
, !script
->owner
);
1618 /* FIXME: bug 506341; would like to do this only if regenerating shapes. */
1619 if (!cx
->runtime
->gcRunning
) {
1620 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
1622 if (!(fp
&& fp
->isEvalFrame())) {
1623 JS_PROPERTY_CACHE(cx
).purgeForScript(script
);
1625 #ifdef CHECK_SCRIPT_OWNER
1626 JS_ASSERT(script
->owner
== cx
->thread
);
1632 PurgeScriptFragments(&script
->compartment
->traceMonitor
, script
);
1635 #if defined(JS_METHODJIT)
1636 mjit::ReleaseScriptCode(cx
, script
);
1638 JS_REMOVE_LINK(&script
->links
);
1644 js_DestroyScript(JSContext
*cx
, JSScript
*script
)
1646 JS_ASSERT(!cx
->runtime
->gcRunning
);
1647 js_CallDestroyScriptHook(cx
, script
);
1648 DestroyScript(cx
, script
);
1652 js_DestroyScriptFromGC(JSContext
*cx
, JSScript
*script
)
1654 JS_ASSERT(cx
->runtime
->gcRunning
);
1655 js_CallDestroyScriptHook(cx
, script
);
1656 DestroyScript(cx
, script
);
1660 js_DestroyCachedScript(JSContext
*cx
, JSScript
*script
)
1662 JS_ASSERT(cx
->runtime
->gcRunning
);
1663 DestroyScript(cx
, script
);
1667 js_TraceScript(JSTracer
*trc
, JSScript
*script
)
1669 JSAtomMap
*map
= &script
->atomMap
;
1670 MarkAtomRange(trc
, map
->length
, map
->vector
, "atomMap");
1672 if (JSScript::isValidOffset(script
->objectsOffset
)) {
1673 JSObjectArray
*objarray
= script
->objects();
1674 uintN i
= objarray
->length
;
1677 if (objarray
->vector
[i
]) {
1678 JS_SET_TRACING_INDEX(trc
, "objects", i
);
1679 Mark(trc
, objarray
->vector
[i
]);
1684 if (JSScript::isValidOffset(script
->regexpsOffset
)) {
1685 JSObjectArray
*objarray
= script
->regexps();
1686 uintN i
= objarray
->length
;
1689 if (objarray
->vector
[i
]) {
1690 JS_SET_TRACING_INDEX(trc
, "regexps", i
);
1691 Mark(trc
, objarray
->vector
[i
]);
1696 if (JSScript::isValidOffset(script
->constOffset
)) {
1697 JSConstArray
*constarray
= script
->consts();
1698 MarkValueRange(trc
, constarray
->length
, constarray
->vector
, "consts");
1701 if (script
->u
.object
) {
1702 JS_SET_TRACING_NAME(trc
, "object");
1703 Mark(trc
, script
->u
.object
);
1706 if (IS_GC_MARKING_TRACER(trc
) && script
->filename
)
1707 js_MarkScriptFilename(script
->filename
);
1709 script
->bindings
.trace(trc
);
1713 js_NewScriptObject(JSContext
*cx
, JSScript
*script
)
1715 AutoScriptRooter
root(cx
, script
);
1717 JS_ASSERT(!script
->u
.object
);
1719 JSObject
*obj
= NewNonFunction
<WithProto::Class
>(cx
, &js_ScriptClass
, NULL
, NULL
);
1722 obj
->setPrivate(script
);
1723 script
->u
.object
= obj
;
1726 * Clear the object's proto, to avoid entraining stuff. Once we no longer use the parent
1727 * for security checks, then we can clear the parent, too.
1731 #ifdef CHECK_SCRIPT_OWNER
1732 script
->owner
= NULL
;
1738 typedef struct GSNCacheEntry
{
1739 JSDHashEntryHdr hdr
;
1744 #define GSN_CACHE_THRESHOLD 100
1747 js_PurgeGSNCache(JSGSNCache
*cache
)
1750 if (cache
->table
.ops
) {
1751 JS_DHashTableFinish(&cache
->table
);
1752 cache
->table
.ops
= NULL
;
1754 GSN_CACHE_METER(cache
, purges
);
1758 js_GetSrcNoteCached(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1760 ptrdiff_t target
, offset
;
1761 GSNCacheEntry
*entry
;
1762 jssrcnote
*sn
, *result
;
1766 target
= pc
- script
->code
;
1767 if ((uint32
)target
>= script
->length
)
1770 if (JS_GSN_CACHE(cx
).code
== script
->code
) {
1771 JS_METER_GSN_CACHE(cx
, hits
);
1772 entry
= (GSNCacheEntry
*)
1773 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1778 JS_METER_GSN_CACHE(cx
, misses
);
1780 for (sn
= script
->notes(); ; sn
= SN_NEXT(sn
)) {
1781 if (SN_IS_TERMINATOR(sn
)) {
1785 offset
+= SN_DELTA(sn
);
1786 if (offset
== target
&& SN_IS_GETTABLE(sn
)) {
1792 if (JS_GSN_CACHE(cx
).code
!= script
->code
&&
1793 script
->length
>= GSN_CACHE_THRESHOLD
) {
1794 JS_PURGE_GSN_CACHE(cx
);
1796 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1798 if (SN_IS_GETTABLE(sn
))
1801 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx
).table
, JS_DHashGetStubOps(),
1802 NULL
, sizeof(GSNCacheEntry
),
1803 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes
))) {
1804 JS_GSN_CACHE(cx
).table
.ops
= NULL
;
1807 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
);
1810 if (SN_IS_GETTABLE(sn
)) {
1811 entry
= (GSNCacheEntry
*)
1812 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1818 JS_GSN_CACHE(cx
).code
= script
->code
;
1819 JS_METER_GSN_CACHE(cx
, fills
);
1827 js_FramePCToLineNumber(JSContext
*cx
, JSStackFrame
*fp
)
1829 return js_PCToLineNumber(cx
, fp
->script(),
1830 fp
->hasImacropc() ? fp
->imacropc() : fp
->pc(cx
));
1834 js_PCToLineNumber(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1839 ptrdiff_t offset
, target
;
1843 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1848 * Special case: function definition needs no line number note because
1849 * the function's script contains its starting line number.
1851 op
= js_GetOpcode(cx
, script
, pc
);
1852 if (js_CodeSpec
[op
].format
& JOF_INDEXBASE
)
1853 pc
+= js_CodeSpec
[op
].length
;
1854 if (*pc
== JSOP_DEFFUN
) {
1855 GET_FUNCTION_FROM_BYTECODE(script
, pc
, 0, fun
);
1856 return fun
->u
.i
.script
->lineno
;
1860 * General case: walk through source notes accumulating their deltas,
1861 * keeping track of line-number notes, until we pass the note for pc's
1862 * offset within script->code.
1864 lineno
= script
->lineno
;
1866 target
= pc
- script
->code
;
1867 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1868 offset
+= SN_DELTA(sn
);
1869 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1870 if (type
== SRC_SETLINE
) {
1871 if (offset
<= target
)
1872 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1873 } else if (type
== SRC_NEWLINE
) {
1874 if (offset
<= target
)
1877 if (offset
> target
)
1883 /* The line number limit is the same as the jssrcnote offset limit. */
1884 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1887 js_LineNumberToPC(JSScript
*script
, uintN target
)
1889 ptrdiff_t offset
, best
;
1890 uintN lineno
, bestdiff
, diff
;
1896 lineno
= script
->lineno
;
1897 bestdiff
= SN_LINE_LIMIT
;
1898 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1900 * Exact-match only if offset is not in the prolog; otherwise use
1901 * nearest greater-or-equal line number match.
1903 if (lineno
== target
&& script
->code
+ offset
>= script
->main
)
1905 if (lineno
>= target
) {
1906 diff
= lineno
- target
;
1907 if (diff
< bestdiff
) {
1912 offset
+= SN_DELTA(sn
);
1913 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1914 if (type
== SRC_SETLINE
) {
1915 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1916 } else if (type
== SRC_NEWLINE
) {
1923 return script
->code
+ offset
;
1926 JS_FRIEND_API(uintN
)
1927 js_GetScriptLineExtent(JSScript
*script
)
1933 lineno
= script
->lineno
;
1934 for (sn
= script
->notes(); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1935 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1936 if (type
== SRC_SETLINE
) {
1937 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1938 } else if (type
== SRC_NEWLINE
) {
1942 return 1 + lineno
- script
->lineno
;
1945 class DisablePrincipalsTranscoding
{
1946 JSSecurityCallbacks
*callbacks
;
1947 JSPrincipalsTranscoder temp
;
1950 DisablePrincipalsTranscoding(JSContext
*cx
)
1951 : callbacks(JS_GetRuntimeSecurityCallbacks(cx
->runtime
)),
1955 temp
= callbacks
->principalsTranscoder
;
1956 callbacks
->principalsTranscoder
= NULL
;
1960 ~DisablePrincipalsTranscoding() {
1962 callbacks
->principalsTranscoder
= temp
;
1967 js_CloneScript(JSContext
*cx
, JSScript
*script
)
1969 JS_ASSERT(cx
->compartment
!= script
->compartment
);
1970 JS_ASSERT(script
->compartment
);
1973 JSXDRState
*w
= JS_XDRNewMem(cx
, JSXDR_ENCODE
);
1977 // we don't want gecko to transcribe our principals for us
1978 DisablePrincipalsTranscoding
disable(cx
);
1980 if (!JS_XDRScript(w
, &script
)) {
1986 void *p
= JS_XDRMemGetData(w
, &nbytes
);
1992 // de-serialize script
1993 JSXDRState
*r
= JS_XDRNewMem(cx
, JSXDR_DECODE
);
1999 // Hand p off from w to r. Don't want them to share the data
2000 // mem, lest they both try to free it in JS_XDRDestroy
2001 JS_XDRMemSetData(r
, p
, nbytes
);
2002 JS_XDRMemSetData(w
, NULL
, 0);
2004 // We can't use the public API because it makes a script object.
2005 if (!js_XDRScript(r
, &script
, NULL
))
2011 // set the proper principals for the script
2012 script
->principals
= script
->compartment
->principals
;
2013 if (script
->principals
)
2014 JSPRINCIPALS_HOLD(cx
, script
->principals
);
2020 JSScript::copyClosedSlotsTo(JSScript
*other
)
2022 memcpy(other
->closedSlots
, closedSlots
, nClosedArgs
+ nClosedVars
);