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"
67 #if JS_HAS_SCRIPT_OBJECT
69 static const char js_script_exec_str
[] = "Script.prototype.exec";
70 static const char js_script_compile_str
[] = "Script.prototype.compile";
73 * This routine requires that obj has been locked previously.
76 GetScriptExecDepth(JSContext
*cx
, JSObject
*obj
)
80 JS_ASSERT(JS_IS_OBJ_LOCKED(cx
, obj
));
81 v
= LOCKED_OBJ_GET_SLOT(obj
, JSSLOT_START(&js_ScriptClass
));
82 return JSVAL_TO_INT(v
);
86 AdjustScriptExecDepth(JSContext
*cx
, JSObject
*obj
, jsint delta
)
91 execDepth
= GetScriptExecDepth(cx
, obj
);
92 LOCKED_OBJ_SET_SLOT(obj
, JSSLOT_START(&js_ScriptClass
),
93 INT_TO_JSVAL(execDepth
+ delta
));
94 JS_UNLOCK_OBJ(cx
, obj
);
99 script_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
109 obj
= JS_THIS_OBJECT(cx
, vp
);
110 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, vp
+ 2))
115 indent
= js_ValueToECMAUint32(cx
, &vp
[2]);
116 if (JSVAL_IS_NULL(vp
[2]))
120 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
122 /* Let n count the source string length, j the "front porch" length. */
123 j
= JS_snprintf(buf
, sizeof buf
, "(new %s(", js_ScriptClass
.name
);
126 /* Let k count the constructor argument string length. */
128 s
= NULL
; /* quell GCC overwarning */
130 str
= JS_DecompileScript(cx
, script
, "Script.prototype.toSource",
134 str
= js_QuoteString(cx
, str
, '\'');
137 JSSTRING_CHARS_AND_LENGTH(str
, s
, k
);
141 /* Allocate the source string and copy into it. */
142 t
= (jschar
*) JS_malloc(cx
, (n
+ 1) * sizeof(jschar
));
145 for (i
= 0; i
< j
; i
++)
147 for (j
= 0; j
< k
; i
++, j
++)
153 /* Create and return a JS string for t. */
154 str
= JS_NewUCString(cx
, t
, n
);
159 *vp
= STRING_TO_JSVAL(str
);
162 #endif /* JS_HAS_TOSOURCE */
165 script_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
174 indent
= js_ValueToECMAUint32(cx
, &vp
[2]);
175 if (JSVAL_IS_NULL(vp
[2]))
179 obj
= JS_THIS_OBJECT(cx
, vp
);
180 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, vp
+ 2))
182 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
184 *vp
= STRING_TO_JSVAL(cx
->runtime
->emptyString
);
188 str
= JS_DecompileScript(cx
, script
, "Script.prototype.toString",
192 *vp
= STRING_TO_JSVAL(str
);
197 script_compile_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
203 JSScript
*script
, *oldscript
;
204 JSStackFrame
*caller
;
207 JSPrincipals
*principals
;
211 /* Make sure obj is a Script object. */
212 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, argv
))
215 /* If no args, leave private undefined and return early. */
219 /* Otherwise, the first arg is the script source to compile. */
220 str
= js_ValueToString(cx
, argv
[0]);
223 argv
[0] = STRING_TO_JSVAL(str
);
227 if (!js_ValueToObject(cx
, argv
[1], &scopeobj
))
229 argv
[1] = OBJECT_TO_JSVAL(scopeobj
);
232 /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
233 caller
= js_GetScriptedCaller(cx
, NULL
);
234 JS_ASSERT(!caller
|| cx
->fp
->scopeChain
== caller
->scopeChain
);
238 scopeobj
= js_GetScopeChain(cx
, caller
);
243 principals
= JS_EvalFramePrincipals(cx
, cx
->fp
, caller
);
244 file
= js_ComputeFilename(cx
, caller
, principals
, &line
);
251 /* Ensure we compile this script with the right (inner) principals. */
252 scopeobj
= js_CheckScopeChainValidity(cx
, scopeobj
, js_script_compile_str
);
257 * Compile the new script using the caller's scope chain, a la eval().
258 * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in
259 * tcflags and use NULL for the callerFrame argument, because compilation
260 * is here separated from execution, and the run-time scope chain may not
261 * match the compile-time. TCF_COMPILE_N_GO is tested in jsemit.c and
262 * jsparse.c to optimize based on identity of run- and compile-time scope.
265 script
= js_CompileScript(cx
, scopeobj
, NULL
, principals
, tcflags
,
266 JSSTRING_CHARS(str
), JSSTRING_LENGTH(str
),
271 JS_LOCK_OBJ(cx
, obj
);
272 execDepth
= GetScriptExecDepth(cx
, obj
);
275 * execDepth must be 0 to allow compilation here, otherwise the JSScript
276 * struct can be released while running.
279 JS_UNLOCK_OBJ(cx
, obj
);
280 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
281 JSMSG_COMPILE_EXECED_SCRIPT
);
285 /* Swap script for obj's old script, if any. */
286 v
= LOCKED_OBJ_GET_SLOT(obj
, JSSLOT_PRIVATE
);
287 oldscript
= (JSScript
*) (!JSVAL_IS_VOID(v
) ? JSVAL_TO_PRIVATE(v
) : NULL
);
288 LOCKED_OBJ_SET_SLOT(obj
, JSSLOT_PRIVATE
, PRIVATE_TO_JSVAL(script
));
289 JS_UNLOCK_OBJ(cx
, obj
);
292 js_DestroyScript(cx
, oldscript
);
294 script
->u
.object
= obj
;
295 js_CallNewScriptHook(cx
, script
, NULL
);
298 /* Return the object. */
299 *rval
= OBJECT_TO_JSVAL(obj
);
304 script_compile(JSContext
*cx
, uintN argc
, jsval
*vp
)
306 return script_compile_sub(cx
, JS_THIS_OBJECT(cx
, vp
), argc
, vp
+ 2, vp
);
310 script_exec_sub(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
,
313 JSObject
*scopeobj
, *parent
;
314 JSStackFrame
*caller
;
315 JSPrincipals
*principals
;
319 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, argv
))
324 if (!js_ValueToObject(cx
, argv
[0], &scopeobj
))
326 argv
[0] = OBJECT_TO_JSVAL(scopeobj
);
330 * Emulate eval() by using caller's this, var object, sharp array, etc.,
331 * all propagated by js_Execute via a non-null fourth (down) argument to
332 * js_Execute. If there is no scripted caller, js_Execute uses its second
333 * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
335 * Unlike eval, which the compiler detects, Script.prototype.exec may be
336 * called from a lightweight function, or even from native code (in which
337 * case fp->varobj and fp->scopeChain are null). If exec is called from
338 * a lightweight function, we will need to get a Call object representing
339 * its frame, to act as the var object and scope chain head.
341 caller
= js_GetScriptedCaller(cx
, NULL
);
342 if (caller
&& !caller
->varobj
) {
343 /* Called from a lightweight function. */
344 JS_ASSERT(caller
->fun
&& !JSFUN_HEAVYWEIGHT_TEST(caller
->fun
->flags
));
346 /* Scope chain links from Call object to callee's parent. */
347 parent
= OBJ_GET_PARENT(cx
, caller
->callee
);
348 if (!js_GetCallObject(cx
, caller
, parent
))
353 /* No scope object passed in: try to use the caller's scope chain. */
356 * Load caller->scopeChain after the conditional js_GetCallObject
357 * call above, which resets scopeChain as well as varobj.
359 scopeobj
= js_GetScopeChain(cx
, caller
);
364 * Called from native code, so we don't know what scope object to
365 * use. We could use parent (see above), but Script.prototype.exec
366 * might be a shared/sealed "superglobal" method. A more general
367 * approach would use cx->globalObject, which will be the same as
368 * exec.__parent__ in the non-superglobal case. In the superglobal
369 * case it's the right object: the global, not the superglobal.
371 scopeobj
= cx
->globalObject
;
375 scopeobj
= js_CheckScopeChainValidity(cx
, scopeobj
, js_script_exec_str
);
379 /* Keep track of nesting depth for the script. */
380 AdjustScriptExecDepth(cx
, obj
, 1);
382 /* Must get to out label after this */
383 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
389 /* Belt-and-braces: check that this script object has access to scopeobj. */
390 principals
= script
->principals
;
391 ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
392 CLASS_ATOM(cx
, Script
));
396 ok
= js_Execute(cx
, scopeobj
, script
, caller
, JSFRAME_EVAL
, rval
);
399 AdjustScriptExecDepth(cx
, obj
, -1);
404 script_exec(JSContext
*cx
, uintN argc
, jsval
*vp
)
406 return script_exec_sub(cx
, JS_THIS_OBJECT(cx
, vp
), argc
, vp
+ 2, vp
);
409 #endif /* JS_HAS_SCRIPT_OBJECT */
414 js_XDRScript(JSXDRState
*xdr
, JSScript
**scriptp
, JSBool
*hasMagic
)
417 JSScript
*script
, *oldscript
;
420 uint32 length
, lineno
, nslots
, magic
;
421 uint32 natoms
, nsrcnotes
, ntrynotes
, nobjects
, nupvars
, nregexps
, i
;
422 uint32 prologLength
, version
;
423 JSTempValueRooter tvr
;
424 JSPrincipals
*principals
;
426 JSBool filenameWasSaved
;
427 jssrcnote
*notes
, *sn
;
428 JSSecurityCallbacks
*callbacks
;
432 nsrcnotes
= ntrynotes
= natoms
= nobjects
= nupvars
= nregexps
= 0;
433 filenameWasSaved
= JS_FALSE
;
436 if (xdr
->mode
== JSXDR_ENCODE
)
437 magic
= JSXDR_MAGIC_SCRIPT_CURRENT
;
438 if (!JS_XDRUint32(xdr
, &magic
))
440 if (magic
!= JSXDR_MAGIC_SCRIPT_CURRENT
) {
441 /* We do not provide binary compatibility with older scripts. */
443 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
444 JSMSG_BAD_SCRIPT_MAGIC
);
447 *hasMagic
= JS_FALSE
;
453 if (xdr
->mode
== JSXDR_ENCODE
) {
454 length
= script
->length
;
455 prologLength
= PTRDIFF(script
->main
, script
->code
, jsbytecode
);
456 JS_ASSERT((int16
)script
->version
!= JSVERSION_UNKNOWN
);
457 version
= (uint32
)script
->version
| (script
->nfixed
<< 16);
458 lineno
= (uint32
)script
->lineno
;
459 nslots
= (uint32
)script
->nslots
;
460 nslots
= (uint32
)((script
->staticDepth
<< 16) | script
->nslots
);
461 natoms
= (uint32
)script
->atomMap
.length
;
463 /* Count the srcnotes, keeping notes pointing at the first one. */
464 notes
= SCRIPT_NOTES(script
);
465 for (sn
= notes
; !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
))
467 nsrcnotes
= PTRDIFF(sn
, notes
, jssrcnote
);
468 nsrcnotes
++; /* room for the terminator */
470 if (script
->objectsOffset
!= 0)
471 nobjects
= JS_SCRIPT_OBJECTS(script
)->length
;
472 if (script
->upvarsOffset
!= 0)
473 nupvars
= JS_SCRIPT_UPVARS(script
)->length
;
474 if (script
->regexpsOffset
!= 0)
475 nregexps
= JS_SCRIPT_REGEXPS(script
)->length
;
476 if (script
->trynotesOffset
!= 0)
477 ntrynotes
= JS_SCRIPT_TRYNOTES(script
)->length
;
480 if (!JS_XDRUint32(xdr
, &length
))
482 if (!JS_XDRUint32(xdr
, &prologLength
))
484 if (!JS_XDRUint32(xdr
, &version
))
488 * To fuse allocations, we need srcnote, atom, objects, upvar, regexp,
489 * and trynote counts early.
491 if (!JS_XDRUint32(xdr
, &natoms
))
493 if (!JS_XDRUint32(xdr
, &nsrcnotes
))
495 if (!JS_XDRUint32(xdr
, &ntrynotes
))
497 if (!JS_XDRUint32(xdr
, &nobjects
))
499 if (!JS_XDRUint32(xdr
, &nupvars
))
501 if (!JS_XDRUint32(xdr
, &nregexps
))
504 if (xdr
->mode
== JSXDR_DECODE
) {
505 script
= js_NewScript(cx
, length
, nsrcnotes
, natoms
, nobjects
, nupvars
,
506 nregexps
, ntrynotes
);
510 script
->main
+= prologLength
;
511 script
->version
= (JSVersion
) (version
& 0xffff);
512 script
->nfixed
= (uint16
) (version
>> 16);
514 /* If we know nsrcnotes, we allocated space for notes in script. */
515 notes
= SCRIPT_NOTES(script
);
517 JS_PUSH_TEMP_ROOT_SCRIPT(cx
, script
, &tvr
);
521 * Control hereafter must goto error on failure, in order for the
522 * DECODE case to destroy script.
524 oldscript
= xdr
->script
;
526 if (xdr
->mode
== JSXDR_ENCODE
) {
527 code
= js_UntrapScriptCode(cx
, script
);
532 xdr
->script
= script
;
533 ok
= JS_XDRBytes(xdr
, (char *) code
, length
* sizeof(jsbytecode
));
535 if (code
!= script
->code
)
541 if (!JS_XDRBytes(xdr
, (char *)notes
, nsrcnotes
* sizeof(jssrcnote
)) ||
542 !JS_XDRCStringOrNull(xdr
, (char **)&script
->filename
) ||
543 !JS_XDRUint32(xdr
, &lineno
) ||
544 !JS_XDRUint32(xdr
, &nslots
)) {
548 callbacks
= JS_GetSecurityCallbacks(cx
);
549 if (xdr
->mode
== JSXDR_ENCODE
) {
550 principals
= script
->principals
;
551 encodeable
= callbacks
&& callbacks
->principalsTranscoder
;
552 if (!JS_XDRUint32(xdr
, &encodeable
))
555 !callbacks
->principalsTranscoder(xdr
, &principals
)) {
559 if (!JS_XDRUint32(xdr
, &encodeable
))
562 if (!(callbacks
&& callbacks
->principalsTranscoder
)) {
563 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
564 JSMSG_CANT_DECODE_PRINCIPALS
);
567 if (!callbacks
->principalsTranscoder(xdr
, &principals
))
569 script
->principals
= principals
;
573 if (xdr
->mode
== JSXDR_DECODE
) {
574 const char *filename
= script
->filename
;
576 filename
= js_SaveScriptFilename(cx
, filename
);
579 JS_free(cx
, (void *) script
->filename
);
580 script
->filename
= filename
;
581 filenameWasSaved
= JS_TRUE
;
583 script
->lineno
= (uintN
)lineno
;
584 script
->nslots
= (uint16
)nslots
;
585 script
->staticDepth
= nslots
>> 16;
588 for (i
= 0; i
!= natoms
; ++i
) {
589 if (!js_XDRAtom(xdr
, &script
->atomMap
.vector
[i
]))
594 * Here looping from 0-to-length to xdr objects is essential. It ensures
595 * that block objects from the script->objects array will be written and
596 * restored in the outer-to-inner order. block_xdrObject relies on this to
597 * restore the parent chain.
599 for (i
= 0; i
!= nobjects
; ++i
) {
600 if (!js_XDRObject(xdr
, &JS_SCRIPT_OBJECTS(script
)->vector
[i
]))
603 for (i
= 0; i
!= nupvars
; ++i
) {
604 if (!JS_XDRUint32(xdr
, &JS_SCRIPT_UPVARS(script
)->vector
[i
]))
607 for (i
= 0; i
!= nregexps
; ++i
) {
608 if (!js_XDRObject(xdr
, &JS_SCRIPT_REGEXPS(script
)->vector
[i
]))
612 if (ntrynotes
!= 0) {
614 * We combine tn->kind and tn->stackDepth when serializing as XDR is not
615 * efficient when serializing small integer types.
617 JSTryNote
*tn
, *tnfirst
;
619 JS_STATIC_ASSERT(sizeof(tn
->kind
) == sizeof(uint8
));
620 JS_STATIC_ASSERT(sizeof(tn
->stackDepth
) == sizeof(uint16
));
622 tnfirst
= JS_SCRIPT_TRYNOTES(script
)->vector
;
623 JS_ASSERT(JS_SCRIPT_TRYNOTES(script
)->length
== ntrynotes
);
624 tn
= tnfirst
+ ntrynotes
;
627 if (xdr
->mode
== JSXDR_ENCODE
) {
628 kindAndDepth
= ((uint32
)tn
->kind
<< 16)
629 | (uint32
)tn
->stackDepth
;
631 if (!JS_XDRUint32(xdr
, &kindAndDepth
) ||
632 !JS_XDRUint32(xdr
, &tn
->start
) ||
633 !JS_XDRUint32(xdr
, &tn
->length
)) {
636 if (xdr
->mode
== JSXDR_DECODE
) {
637 tn
->kind
= (uint8
)(kindAndDepth
>> 16);
638 tn
->stackDepth
= (uint16
)kindAndDepth
;
640 } while (tn
!= tnfirst
);
643 xdr
->script
= oldscript
;
644 if (xdr
->mode
== JSXDR_DECODE
)
645 JS_POP_TEMP_ROOT(cx
, &tvr
);
649 if (xdr
->mode
== JSXDR_DECODE
) {
650 JS_POP_TEMP_ROOT(cx
, &tvr
);
651 if (script
->filename
&& !filenameWasSaved
) {
652 JS_free(cx
, (void *) script
->filename
);
653 script
->filename
= NULL
;
655 js_DestroyScript(cx
, script
);
658 xdr
->script
= oldscript
;
662 #if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
664 * These cannot be exposed to web content, and chrome does not need them, so
665 * we take them out of the Mozilla client altogether. Fortunately, there is
666 * no way to serialize a native function (see fun_xdrObject in jsfun.c).
670 script_freeze(JSContext
*cx
, uintN argc
, jsval
*vp
)
680 obj
= JS_THIS_OBJECT(cx
, vp
);
681 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, vp
+ 2))
683 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
688 xdr
= JS_XDRNewMem(cx
, JSXDR_ENCODE
);
693 ok
= js_XDRScript(xdr
, &script
, &hasMagic
);
701 buf
= JS_XDRMemGetData(xdr
, &len
);
707 JS_ASSERT((jsword
)buf
% sizeof(jschar
) == 0);
708 len
/= sizeof(jschar
);
714 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
715 chars
= (jschar
*)buf
;
716 for (i
= 0; i
< len
; i
++)
717 chars
[i
] = JSXDR_SWAB16(chars
[i
]);
720 str
= JS_NewUCStringCopyN(cx
, (jschar
*)buf
, len
);
726 *vp
= STRING_TO_JSVAL(str
);
734 script_thaw(JSContext
*cx
, uintN argc
, jsval
*vp
)
742 JSScript
*script
, *oldscript
;
746 obj
= JS_THIS_OBJECT(cx
, vp
);
747 if (!JS_InstanceOf(cx
, obj
, &js_ScriptClass
, vp
+ 2))
752 str
= js_ValueToString(cx
, vp
[2]);
755 vp
[2] = STRING_TO_JSVAL(str
);
758 xdr
= JS_XDRNewMem(cx
, JSXDR_DECODE
);
762 JSSTRING_CHARS_AND_LENGTH(str
, buf
, len
);
768 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
769 from
= (jschar
*)buf
;
770 to
= (jschar
*) JS_malloc(cx
, len
* sizeof(jschar
));
775 for (i
= 0; i
< len
; i
++)
776 to
[i
] = JSXDR_SWAB16(from
[i
]);
780 len
*= sizeof(jschar
);
781 JS_XDRMemSetData(xdr
, buf
, len
);
783 /* XXXbe should magic mismatch be error, or false return value? */
784 ok
= js_XDRScript(xdr
, &script
, &hasMagic
);
792 JS_LOCK_OBJ(cx
, obj
);
793 execDepth
= GetScriptExecDepth(cx
, obj
);
796 * execDepth must be 0 to allow compilation here, otherwise the JSScript
797 * struct can be released while running.
800 JS_UNLOCK_OBJ(cx
, obj
);
801 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
802 JSMSG_COMPILE_EXECED_SCRIPT
);
806 /* Swap script for obj's old script, if any. */
807 v
= LOCKED_OBJ_GET_SLOT(obj
, JSSLOT_PRIVATE
);
808 oldscript
= !JSVAL_IS_VOID(v
) ? JSVAL_TO_PRIVATE(v
) : NULL
;
809 LOCKED_OBJ_SET_SLOT(obj
, JSSLOT_PRIVATE
, PRIVATE_TO_JSVAL(script
));
810 JS_UNLOCK_OBJ(cx
, obj
);
813 js_DestroyScript(cx
, oldscript
);
815 script
->u
.object
= obj
;
816 js_CallNewScriptHook(cx
, script
, NULL
);
820 * We reset the buffer to be NULL so that it doesn't free the chars
821 * memory owned by str (vp[2]).
823 JS_XDRMemSetData(xdr
, NULL
, 0);
832 static const char js_thaw_str
[] = "thaw";
834 #endif /* JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW */
835 #endif /* JS_HAS_XDR */
837 #if JS_HAS_SCRIPT_OBJECT
839 static JSFunctionSpec script_methods
[] = {
841 JS_FN(js_toSource_str
, script_toSource
, 0,0),
843 JS_FN(js_toString_str
, script_toString
, 0,0),
844 JS_FN("compile", script_compile
, 2,0),
845 JS_FN("exec", script_exec
, 1,0),
846 #if JS_HAS_XDR_FREEZE_THAW
847 JS_FN("freeze", script_freeze
, 0,0),
848 JS_FN(js_thaw_str
, script_thaw
, 1,0),
849 #endif /* JS_HAS_XDR_FREEZE_THAW */
853 #endif /* JS_HAS_SCRIPT_OBJECT */
856 script_finalize(JSContext
*cx
, JSObject
*obj
)
860 script
= (JSScript
*) JS_GetPrivate(cx
, obj
);
862 js_DestroyScript(cx
, script
);
866 script_call(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
868 #if JS_HAS_SCRIPT_OBJECT
869 return script_exec_sub(cx
, JSVAL_TO_OBJECT(argv
[-2]), argc
, argv
, rval
);
876 script_trace(JSTracer
*trc
, JSObject
*obj
)
880 script
= (JSScript
*) JS_GetPrivate(trc
->context
, obj
);
882 js_TraceScript(trc
, script
);
885 #if !JS_HAS_SCRIPT_OBJECT
886 #define JSProto_Script JSProto_Object
889 JS_FRIEND_DATA(JSClass
) js_ScriptClass
= {
891 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) |
892 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Script
),
893 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
894 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, script_finalize
,
895 NULL
, NULL
, script_call
, NULL
,/*XXXbe xdr*/
896 NULL
, NULL
, JS_CLASS_TRACE(script_trace
), NULL
899 #if JS_HAS_SCRIPT_OBJECT
902 Script(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
904 /* If not constructing, replace obj with a new Script object. */
905 if (!JS_IsConstructing(cx
)) {
906 obj
= js_NewObject(cx
, &js_ScriptClass
, NULL
, NULL
, 0);
911 * script_compile_sub does not use rval to root its temporaries so we
912 * can use it to root obj.
914 *rval
= OBJECT_TO_JSVAL(obj
);
917 if (!JS_SetReservedSlot(cx
, obj
, 0, INT_TO_JSVAL(0)))
920 return script_compile_sub(cx
, obj
, argc
, argv
, rval
);
923 #if JS_HAS_SCRIPT_OBJECT && JS_HAS_XDR_FREEZE_THAW
926 script_static_thaw(JSContext
*cx
, uintN argc
, jsval
*vp
)
930 obj
= js_NewObject(cx
, &js_ScriptClass
, NULL
, NULL
);
933 vp
[1] = OBJECT_TO_JSVAL(obj
);
934 if (!script_thaw(cx
, argc
, vp
))
936 *vp
= OBJECT_TO_JSVAL(obj
);
940 static JSFunctionSpec script_static_methods
[] = {
941 JS_FN(js_thaw_str
, script_static_thaw
, 1,0),
945 #else /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
947 #define script_static_methods NULL
949 #endif /* !JS_HAS_SCRIPT_OBJECT || !JS_HAS_XDR_FREEZE_THAW */
952 js_InitScriptClass(JSContext
*cx
, JSObject
*obj
)
954 return JS_InitClass(cx
, obj
, NULL
, &js_ScriptClass
, Script
, 1,
955 NULL
, script_methods
, NULL
, script_static_methods
);
958 #endif /* JS_HAS_SCRIPT_OBJECT */
961 * Shared script filename management.
964 js_compare_strings(const void *k1
, const void *k2
)
966 return strcmp((const char *) k1
, (const char *) k2
) == 0;
969 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
970 typedef struct ScriptFilenameEntry
{
971 JSHashEntry
*next
; /* hash chain linkage */
972 JSHashNumber keyHash
; /* key hash function result */
973 const void *key
; /* ptr to filename, below */
974 uint32 flags
; /* user-defined filename prefix flags */
975 JSPackedBool mark
; /* GC mark flag */
976 char filename
[3]; /* two or more bytes, NUL-terminated */
977 } ScriptFilenameEntry
;
980 js_alloc_table_space(void *priv
, size_t size
)
986 js_free_table_space(void *priv
, void *item
)
992 js_alloc_sftbl_entry(void *priv
, const void *key
)
994 size_t nbytes
= offsetof(ScriptFilenameEntry
, filename
) +
995 strlen((const char *) key
) + 1;
997 return (JSHashEntry
*) malloc(JS_MAX(nbytes
, sizeof(JSHashEntry
)));
1001 js_free_sftbl_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
1003 if (flag
!= HT_FREE_ENTRY
)
1008 static JSHashAllocOps sftbl_alloc_ops
= {
1009 js_alloc_table_space
, js_free_table_space
,
1010 js_alloc_sftbl_entry
, js_free_sftbl_entry
1014 js_InitRuntimeScriptState(JSRuntime
*rt
)
1016 #ifdef JS_THREADSAFE
1017 JS_ASSERT(!rt
->scriptFilenameTableLock
);
1018 rt
->scriptFilenameTableLock
= JS_NEW_LOCK();
1019 if (!rt
->scriptFilenameTableLock
)
1022 JS_ASSERT(!rt
->scriptFilenameTable
);
1023 rt
->scriptFilenameTable
=
1024 JS_NewHashTable(16, JS_HashString
, js_compare_strings
, NULL
,
1025 &sftbl_alloc_ops
, NULL
);
1026 if (!rt
->scriptFilenameTable
) {
1027 js_FinishRuntimeScriptState(rt
); /* free lock if threadsafe */
1030 JS_INIT_CLIST(&rt
->scriptFilenamePrefixes
);
1034 typedef struct ScriptFilenamePrefix
{
1035 JSCList links
; /* circular list linkage for easy deletion */
1036 const char *name
; /* pointer to pinned ScriptFilenameEntry string */
1037 size_t length
; /* prefix string length, precomputed */
1038 uint32 flags
; /* user-defined flags to inherit from this prefix */
1039 } ScriptFilenamePrefix
;
1042 js_FinishRuntimeScriptState(JSRuntime
*rt
)
1044 if (rt
->scriptFilenameTable
) {
1045 JS_HashTableDestroy(rt
->scriptFilenameTable
);
1046 rt
->scriptFilenameTable
= NULL
;
1048 #ifdef JS_THREADSAFE
1049 if (rt
->scriptFilenameTableLock
) {
1050 JS_DESTROY_LOCK(rt
->scriptFilenameTableLock
);
1051 rt
->scriptFilenameTableLock
= NULL
;
1057 js_FreeRuntimeScriptState(JSRuntime
*rt
)
1059 ScriptFilenamePrefix
*sfp
;
1061 if (!rt
->scriptFilenameTable
)
1064 while (!JS_CLIST_IS_EMPTY(&rt
->scriptFilenamePrefixes
)) {
1065 sfp
= (ScriptFilenamePrefix
*) rt
->scriptFilenamePrefixes
.next
;
1066 JS_REMOVE_LINK(&sfp
->links
);
1069 js_FinishRuntimeScriptState(rt
);
1072 #ifdef DEBUG_brendan
1076 size_t sftbl_savings
= 0;
1079 static ScriptFilenameEntry
*
1080 SaveScriptFilename(JSRuntime
*rt
, const char *filename
, uint32 flags
)
1085 ScriptFilenameEntry
*sfe
;
1087 JSCList
*head
, *link
;
1088 ScriptFilenamePrefix
*sfp
;
1090 table
= rt
->scriptFilenameTable
;
1091 hash
= JS_HashString(filename
);
1092 hep
= JS_HashTableRawLookup(table
, hash
, filename
);
1093 sfe
= (ScriptFilenameEntry
*) *hep
;
1096 sftbl_savings
+= strlen(sfe
->filename
);
1100 sfe
= (ScriptFilenameEntry
*)
1101 JS_HashTableRawAdd(table
, hep
, hash
, filename
, NULL
);
1104 sfe
->key
= strcpy(sfe
->filename
, filename
);
1106 sfe
->mark
= JS_FALSE
;
1109 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1111 /* Search in case filename was saved already; we must be idempotent. */
1113 length
= strlen(filename
);
1114 for (head
= link
= &rt
->scriptFilenamePrefixes
;
1116 link
= link
->next
) {
1117 /* Lag link behind sfp to insert in non-increasing length order. */
1118 sfp
= (ScriptFilenamePrefix
*) link
->next
;
1119 if (!strcmp(sfp
->name
, filename
))
1121 if (sfp
->length
<= length
) {
1129 /* No such prefix: add one now. */
1130 sfp
= (ScriptFilenamePrefix
*) malloc(sizeof(ScriptFilenamePrefix
));
1133 JS_INSERT_AFTER(&sfp
->links
, link
);
1134 sfp
->name
= sfe
->filename
;
1135 sfp
->length
= length
;
1140 * Accumulate flags in both sfe and sfp: sfe for later access from the
1141 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1142 * filename entries can inherit by prefix.
1144 sfe
->flags
|= flags
;
1145 sfp
->flags
|= flags
;
1152 js_SaveScriptFilename(JSContext
*cx
, const char *filename
)
1155 ScriptFilenameEntry
*sfe
;
1156 JSCList
*head
, *link
;
1157 ScriptFilenamePrefix
*sfp
;
1160 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
1161 sfe
= SaveScriptFilename(rt
, filename
, 0);
1163 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1164 JS_ReportOutOfMemory(cx
);
1169 * Try to inherit flags by prefix. We assume there won't be more than a
1170 * few (dozen! ;-) prefixes, so linear search is tolerable.
1171 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1173 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
1175 link
= link
->next
) {
1176 sfp
= (ScriptFilenamePrefix
*) link
;
1177 if (!strncmp(sfp
->name
, filename
, sfp
->length
)) {
1178 sfe
->flags
|= sfp
->flags
;
1182 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1183 return sfe
->filename
;
1187 js_SaveScriptFilenameRT(JSRuntime
*rt
, const char *filename
, uint32 flags
)
1189 ScriptFilenameEntry
*sfe
;
1191 /* This may be called very early, via the jsdbgapi.h entry point. */
1192 if (!rt
->scriptFilenameTable
&& !js_InitRuntimeScriptState(rt
))
1195 JS_ACQUIRE_LOCK(rt
->scriptFilenameTableLock
);
1196 sfe
= SaveScriptFilename(rt
, filename
, flags
);
1197 JS_RELEASE_LOCK(rt
->scriptFilenameTableLock
);
1201 return sfe
->filename
;
1205 * Back up from a saved filename by its offset within its hash table entry.
1207 #define FILENAME_TO_SFE(fn) \
1208 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1211 * The sfe->key member, redundant given sfe->filename but required by the old
1212 * jshash.c code, here gives us a useful sanity check. This assertion will
1213 * very likely botch if someone tries to mark a string that wasn't allocated
1214 * as an sfe->filename.
1216 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
1219 js_GetScriptFilenameFlags(const char *filename
)
1221 ScriptFilenameEntry
*sfe
;
1223 sfe
= FILENAME_TO_SFE(filename
);
1224 ASSERT_VALID_SFE(sfe
);
1229 js_MarkScriptFilename(const char *filename
)
1231 ScriptFilenameEntry
*sfe
;
1233 sfe
= FILENAME_TO_SFE(filename
);
1234 ASSERT_VALID_SFE(sfe
);
1235 sfe
->mark
= JS_TRUE
;
1239 js_script_filename_marker(JSHashEntry
*he
, intN i
, void *arg
)
1241 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
1243 sfe
->mark
= JS_TRUE
;
1244 return HT_ENUMERATE_NEXT
;
1248 js_MarkScriptFilenames(JSRuntime
*rt
, JSBool keepAtoms
)
1250 JSCList
*head
, *link
;
1251 ScriptFilenamePrefix
*sfp
;
1253 if (!rt
->scriptFilenameTable
)
1257 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
1258 js_script_filename_marker
,
1261 for (head
= &rt
->scriptFilenamePrefixes
, link
= head
->next
;
1263 link
= link
->next
) {
1264 sfp
= (ScriptFilenamePrefix
*) link
;
1265 js_MarkScriptFilename(sfp
->name
);
1270 js_script_filename_sweeper(JSHashEntry
*he
, intN i
, void *arg
)
1272 ScriptFilenameEntry
*sfe
= (ScriptFilenameEntry
*) he
;
1275 return HT_ENUMERATE_REMOVE
;
1276 sfe
->mark
= JS_FALSE
;
1277 return HT_ENUMERATE_NEXT
;
1281 js_SweepScriptFilenames(JSRuntime
*rt
)
1283 if (!rt
->scriptFilenameTable
)
1286 JS_HashTableEnumerateEntries(rt
->scriptFilenameTable
,
1287 js_script_filename_sweeper
,
1291 printf("script filename table savings so far: %u\n", sftbl_savings
);
1297 * JSScript data structures memory alignment:
1300 * JSObjectArray script objects' descriptor if JSScript.objectsOffset != 0,
1301 * use JS_SCRIPT_OBJECTS(script) macro to access it.
1302 * JSObjectArray script regexps' descriptor if JSScript.regexpsOffset != 0,
1303 * use JS_SCRIPT_REGEXPS(script) macro to access it.
1304 * JSTryNoteArray script try notes' descriptor if JSScript.tryNotesOffset
1305 * != 0, use JS_SCRIPT_TRYNOTES(script) macro to access it.
1306 * JSAtom *a[] array of JSScript.atomMap.length atoms pointed by
1307 * JSScript.atomMap.vector if any.
1308 * JSObject *o[] array of JS_SCRIPT_OBJECTS(script)->length objects if any
1309 * pointed by JS_SCRIPT_OBJECTS(script)->vector.
1310 * JSObject *r[] array of JS_SCRIPT_REGEXPS(script)->length regexps if any
1311 * pointed by JS_SCRIPT_REGEXPS(script)->vector.
1312 * JSTryNote t[] array of JS_SCRIPT_TRYNOTES(script)->length try notes if any
1313 * pointed by JS_SCRIPT_TRYNOTES(script)->vector.
1314 * jsbytecode b[] script bytecode pointed by JSScript.code.
1315 * jssrcnote s[] script source notes, use SCRIPT_NOTES(script) to access it
1317 * The alignment avoids gaps between entries as alignment requirement for each
1318 * subsequent structure or array is the same or divides the alignment
1319 * requirement for the previous one.
1321 * The followings asserts checks that assuming that the alignment requirement
1322 * for JSObjectArray and JSTryNoteArray are sizeof(void *) and for JSTryNote
1323 * it is sizeof(uint32) as the structure consists of 3 uint32 fields.
1325 JS_STATIC_ASSERT(sizeof(JSScript
) % sizeof(void *) == 0);
1326 JS_STATIC_ASSERT(sizeof(JSObjectArray
) % sizeof(void *) == 0);
1327 JS_STATIC_ASSERT(sizeof(JSTryNoteArray
) == sizeof(JSObjectArray
));
1328 JS_STATIC_ASSERT(sizeof(JSAtom
*) == sizeof(JSObject
*));
1329 JS_STATIC_ASSERT(sizeof(JSObject
*) % sizeof(uint32
) == 0);
1330 JS_STATIC_ASSERT(sizeof(JSTryNote
) == 3 * sizeof(uint32
));
1331 JS_STATIC_ASSERT(sizeof(uint32
) % sizeof(jsbytecode
) == 0);
1332 JS_STATIC_ASSERT(sizeof(jsbytecode
) % sizeof(jssrcnote
) == 0);
1335 * Check that uint8 offset for object, upvar, regexp, and try note arrays is
1338 JS_STATIC_ASSERT(sizeof(JSScript
) + 2 * sizeof(JSObjectArray
) +
1339 sizeof(JSUpvarArray
) < JS_BIT(8));
1342 js_NewScript(JSContext
*cx
, uint32 length
, uint32 nsrcnotes
, uint32 natoms
,
1343 uint32 nobjects
, uint32 nupvars
, uint32 nregexps
,
1346 size_t size
, vectorSize
;
1350 size
= sizeof(JSScript
) +
1351 sizeof(JSAtom
*) * natoms
+
1352 length
* sizeof(jsbytecode
) +
1353 nsrcnotes
* sizeof(jssrcnote
);
1355 size
+= sizeof(JSObjectArray
) + nobjects
* sizeof(JSObject
*);
1357 size
+= sizeof(JSUpvarArray
) + nupvars
* sizeof(uint32
);
1359 size
+= sizeof(JSObjectArray
) + nregexps
* sizeof(JSObject
*);
1361 size
+= sizeof(JSTryNoteArray
) + ntrynotes
* sizeof(JSTryNote
);
1363 script
= (JSScript
*) JS_malloc(cx
, size
);
1366 memset(script
, 0, sizeof(JSScript
));
1367 script
->length
= length
;
1368 script
->version
= cx
->version
;
1370 cursor
= (uint8
*)script
+ sizeof(JSScript
);
1371 if (nobjects
!= 0) {
1372 script
->objectsOffset
= (uint8
)(cursor
- (uint8
*)script
);
1373 cursor
+= sizeof(JSObjectArray
);
1376 script
->upvarsOffset
= (uint8
)(cursor
- (uint8
*)script
);
1377 cursor
+= sizeof(JSUpvarArray
);
1379 if (nregexps
!= 0) {
1380 script
->regexpsOffset
= (uint8
)(cursor
- (uint8
*)script
);
1381 cursor
+= sizeof(JSObjectArray
);
1383 if (ntrynotes
!= 0) {
1384 script
->trynotesOffset
= (uint8
)(cursor
- (uint8
*)script
);
1385 cursor
+= sizeof(JSTryNoteArray
);
1389 script
->atomMap
.length
= natoms
;
1390 script
->atomMap
.vector
= (JSAtom
**)cursor
;
1391 vectorSize
= natoms
* sizeof(script
->atomMap
.vector
[0]);
1394 * Clear object map's vector so the GC tracing can run when not yet
1395 * all atoms are copied to the array.
1397 memset(cursor
, 0, vectorSize
);
1398 cursor
+= vectorSize
;
1401 if (nobjects
!= 0) {
1402 JS_SCRIPT_OBJECTS(script
)->length
= nobjects
;
1403 JS_SCRIPT_OBJECTS(script
)->vector
= (JSObject
**)cursor
;
1404 vectorSize
= nobjects
* sizeof(JS_SCRIPT_OBJECTS(script
)->vector
[0]);
1405 memset(cursor
, 0, vectorSize
);
1406 cursor
+= vectorSize
;
1410 JS_SCRIPT_UPVARS(script
)->length
= nupvars
;
1411 JS_SCRIPT_UPVARS(script
)->vector
= (uint32
*)cursor
;
1412 vectorSize
= nupvars
* sizeof(JS_SCRIPT_UPVARS(script
)->vector
[0]);
1413 memset(cursor
, 0, vectorSize
);
1414 cursor
+= vectorSize
;
1417 if (nregexps
!= 0) {
1418 JS_SCRIPT_REGEXPS(script
)->length
= nregexps
;
1419 JS_SCRIPT_REGEXPS(script
)->vector
= (JSObject
**)cursor
;
1420 vectorSize
= nregexps
* sizeof(JS_SCRIPT_REGEXPS(script
)->vector
[0]);
1421 memset(cursor
, 0, vectorSize
);
1422 cursor
+= vectorSize
;
1425 if (ntrynotes
!= 0) {
1426 JS_SCRIPT_TRYNOTES(script
)->length
= ntrynotes
;
1427 JS_SCRIPT_TRYNOTES(script
)->vector
= (JSTryNote
*)cursor
;
1428 vectorSize
= ntrynotes
* sizeof(JS_SCRIPT_TRYNOTES(script
)->vector
[0]);
1430 memset(cursor
, 0, vectorSize
);
1432 cursor
+= vectorSize
;
1435 script
->code
= script
->main
= (jsbytecode
*)cursor
;
1437 length
* sizeof(jsbytecode
) +
1438 nsrcnotes
* sizeof(jssrcnote
) ==
1439 (uint8
*)script
+ size
);
1441 #ifdef CHECK_SCRIPT_OWNER
1442 script
->owner
= cx
->thread
;
1448 js_NewScriptFromCG(JSContext
*cx
, JSCodeGenerator
*cg
)
1450 uint32 mainLength
, prologLength
, nsrcnotes
, nfixed
;
1452 const char *filename
;
1455 /* The counts of indexed things must be checked during code generation. */
1456 JS_ASSERT(cg
->atomList
.count
<= INDEX_LIMIT
);
1457 JS_ASSERT(cg
->objectList
.length
<= INDEX_LIMIT
);
1458 JS_ASSERT(cg
->regexpList
.length
<= INDEX_LIMIT
);
1460 mainLength
= CG_OFFSET(cg
);
1461 prologLength
= CG_PROLOG_OFFSET(cg
);
1462 CG_COUNT_FINAL_SRCNOTES(cg
, nsrcnotes
);
1463 script
= js_NewScript(cx
, prologLength
+ mainLength
, nsrcnotes
,
1464 cg
->atomList
.count
, cg
->objectList
.length
,
1465 cg
->upvarList
.count
, cg
->regexpList
.length
,
1470 /* Now that we have script, error control flow must go to label bad. */
1471 script
->main
+= prologLength
;
1472 memcpy(script
->code
, CG_PROLOG_BASE(cg
), prologLength
* sizeof(jsbytecode
));
1473 memcpy(script
->main
, CG_BASE(cg
), mainLength
* sizeof(jsbytecode
));
1474 nfixed
= (cg
->treeContext
.flags
& TCF_IN_FUNCTION
)
1475 ? cg
->treeContext
.u
.fun
->u
.i
.nvars
1476 : cg
->treeContext
.ngvars
+ cg
->regexpList
.length
;
1477 JS_ASSERT(nfixed
< SLOTNO_LIMIT
);
1478 script
->nfixed
= (uint16
) nfixed
;
1479 js_InitAtomMap(cx
, &script
->atomMap
, &cg
->atomList
);
1481 filename
= cg
->treeContext
.parseContext
->tokenStream
.filename
;
1483 script
->filename
= js_SaveScriptFilename(cx
, filename
);
1484 if (!script
->filename
)
1487 script
->lineno
= cg
->firstLine
;
1488 if (script
->nfixed
+ cg
->maxStackDepth
>= JS_BIT(16)) {
1489 js_ReportCompileErrorNumber(cx
, CG_TS(cg
), NULL
, JSREPORT_ERROR
,
1490 JSMSG_NEED_DIET
, "script");
1493 script
->nslots
= script
->nfixed
+ cg
->maxStackDepth
;
1494 script
->staticDepth
= cg
->staticDepth
;
1495 script
->principals
= cg
->treeContext
.parseContext
->principals
;
1496 if (script
->principals
)
1497 JSPRINCIPALS_HOLD(cx
, script
->principals
);
1499 if (!js_FinishTakingSrcNotes(cx
, cg
, SCRIPT_NOTES(script
)))
1501 if (cg
->ntrynotes
!= 0)
1502 js_FinishTakingTryNotes(cg
, JS_SCRIPT_TRYNOTES(script
));
1503 if (cg
->objectList
.length
!= 0)
1504 FinishParsedObjects(&cg
->objectList
, JS_SCRIPT_OBJECTS(script
));
1505 if (cg
->regexpList
.length
!= 0)
1506 FinishParsedObjects(&cg
->regexpList
, JS_SCRIPT_REGEXPS(script
));
1507 if (cg
->treeContext
.flags
& TCF_NO_SCRIPT_RVAL
)
1508 script
->flags
|= JSSF_NO_SCRIPT_RVAL
;
1510 if (cg
->upvarList
.count
!= 0) {
1511 JS_ASSERT(cg
->upvarList
.count
<= cg
->upvarMap
.length
);
1512 memcpy(JS_SCRIPT_UPVARS(script
)->vector
, cg
->upvarMap
.vector
,
1513 cg
->upvarList
.count
* sizeof(uint32
));
1514 ATOM_LIST_INIT(&cg
->upvarList
);
1515 JS_free(cx
, cg
->upvarMap
.vector
);
1516 cg
->upvarMap
.vector
= NULL
;
1520 * We initialize fun->u.script to be the script constructed above
1521 * so that the debugger has a valid FUN_SCRIPT(fun).
1524 if (cg
->treeContext
.flags
& TCF_IN_FUNCTION
) {
1525 fun
= cg
->treeContext
.u
.fun
;
1526 JS_ASSERT(FUN_INTERPRETED(fun
) && !FUN_SCRIPT(fun
));
1527 JS_ASSERT_IF(script
->upvarsOffset
!= 0,
1528 JS_SCRIPT_UPVARS(script
)->length
== fun
->u
.i
.nupvars
);
1530 js_FreezeLocalNames(cx
, fun
);
1531 fun
->u
.i
.script
= script
;
1532 #ifdef CHECK_SCRIPT_OWNER
1533 script
->owner
= NULL
;
1535 if (cg
->treeContext
.flags
& TCF_FUN_HEAVYWEIGHT
)
1536 fun
->flags
|= JSFUN_HEAVYWEIGHT
;
1539 /* Tell the debugger about this compiled script. */
1540 js_CallNewScriptHook(cx
, script
, fun
);
1544 js_DestroyScript(cx
, script
);
1549 js_CallNewScriptHook(JSContext
*cx
, JSScript
*script
, JSFunction
*fun
)
1551 JSNewScriptHook hook
;
1553 hook
= cx
->debugHooks
->newScriptHook
;
1555 JS_KEEP_ATOMS(cx
->runtime
);
1556 hook(cx
, script
->filename
, script
->lineno
, script
, fun
,
1557 cx
->debugHooks
->newScriptHookData
);
1558 JS_UNKEEP_ATOMS(cx
->runtime
);
1563 js_CallDestroyScriptHook(JSContext
*cx
, JSScript
*script
)
1565 JSDestroyScriptHook hook
;
1567 hook
= cx
->debugHooks
->destroyScriptHook
;
1569 hook(cx
, script
, cx
->debugHooks
->destroyScriptHookData
);
1573 js_DestroyScript(JSContext
*cx
, JSScript
*script
)
1575 js_CallDestroyScriptHook(cx
, script
);
1576 JS_ClearScriptTraps(cx
, script
);
1578 if (script
->principals
)
1579 JSPRINCIPALS_DROP(cx
, script
->principals
);
1581 if (JS_GSN_CACHE(cx
).code
== script
->code
)
1582 JS_CLEAR_GSN_CACHE(cx
);
1585 * The GC flushes all property caches, so no need to purge just the
1586 * entries for this script.
1588 * JS_THREADSAFE note: js_FlushPropertyCacheForScript flushes only the
1589 * current thread's property cache, so a script not owned by a function
1590 * or object, which hands off lifetime management for that script to the
1591 * GC, must be used by only one thread over its lifetime.
1593 * This should be an API-compatible change, since a script is never safe
1594 * against premature GC if shared among threads without a rooted object
1595 * wrapping it to protect the script's mapped atoms against GC. We use
1596 * script->owner to enforce this requirement via assertions.
1598 #ifdef CHECK_SCRIPT_OWNER
1599 JS_ASSERT_IF(cx
->runtime
->gcRunning
, !script
->owner
);
1602 if (!cx
->runtime
->gcRunning
) {
1603 JSStackFrame
*fp
= js_GetTopStackFrame(cx
);
1605 if (!(fp
&& (fp
->flags
& JSFRAME_EVAL
))) {
1606 #ifdef CHECK_SCRIPT_OWNER
1607 JS_ASSERT(script
->owner
== cx
->thread
);
1609 js_FlushPropertyCacheForScript(cx
, script
);
1613 JS_free(cx
, script
);
1617 js_TraceScript(JSTracer
*trc
, JSScript
*script
)
1623 JSObjectArray
*objarray
;
1625 map
= &script
->atomMap
;
1626 length
= map
->length
;
1627 vector
= map
->vector
;
1628 for (i
= 0; i
< length
; i
++) {
1629 v
= ATOM_KEY(vector
[i
]);
1630 if (JSVAL_IS_TRACEABLE(v
)) {
1631 JS_SET_TRACING_INDEX(trc
, "atomMap", i
);
1632 JS_CallTracer(trc
, JSVAL_TO_TRACEABLE(v
), JSVAL_TRACE_KIND(v
));
1636 if (script
->objectsOffset
!= 0) {
1637 objarray
= JS_SCRIPT_OBJECTS(script
);
1638 i
= objarray
->length
;
1641 if (objarray
->vector
[i
]) {
1642 JS_SET_TRACING_INDEX(trc
, "objects", i
);
1643 JS_CallTracer(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
1648 if (script
->regexpsOffset
!= 0) {
1649 objarray
= JS_SCRIPT_REGEXPS(script
);
1650 i
= objarray
->length
;
1653 if (objarray
->vector
[i
]) {
1654 JS_SET_TRACING_INDEX(trc
, "regexps", i
);
1655 JS_CallTracer(trc
, objarray
->vector
[i
], JSTRACE_OBJECT
);
1660 if (script
->u
.object
) {
1661 JS_SET_TRACING_NAME(trc
, "object");
1662 JS_CallTracer(trc
, script
->u
.object
, JSTRACE_OBJECT
);
1665 if (IS_GC_MARKING_TRACER(trc
) && script
->filename
)
1666 js_MarkScriptFilename(script
->filename
);
1669 typedef struct GSNCacheEntry
{
1670 JSDHashEntryHdr hdr
;
1675 #define GSN_CACHE_THRESHOLD 100
1678 js_GetSrcNoteCached(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1680 ptrdiff_t target
, offset
;
1681 GSNCacheEntry
*entry
;
1682 jssrcnote
*sn
, *result
;
1686 target
= PTRDIFF(pc
, script
->code
, jsbytecode
);
1687 if ((uint32
)target
>= script
->length
)
1690 if (JS_GSN_CACHE(cx
).code
== script
->code
) {
1691 JS_METER_GSN_CACHE(cx
, hits
);
1692 entry
= (GSNCacheEntry
*)
1693 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1698 JS_METER_GSN_CACHE(cx
, misses
);
1700 for (sn
= SCRIPT_NOTES(script
); ; sn
= SN_NEXT(sn
)) {
1701 if (SN_IS_TERMINATOR(sn
)) {
1705 offset
+= SN_DELTA(sn
);
1706 if (offset
== target
&& SN_IS_GETTABLE(sn
)) {
1712 if (JS_GSN_CACHE(cx
).code
!= script
->code
&&
1713 script
->length
>= GSN_CACHE_THRESHOLD
) {
1714 JS_CLEAR_GSN_CACHE(cx
);
1716 for (sn
= SCRIPT_NOTES(script
); !SN_IS_TERMINATOR(sn
);
1718 if (SN_IS_GETTABLE(sn
))
1721 if (!JS_DHashTableInit(&JS_GSN_CACHE(cx
).table
, JS_DHashGetStubOps(),
1722 NULL
, sizeof(GSNCacheEntry
),
1723 JS_DHASH_DEFAULT_CAPACITY(nsrcnotes
))) {
1724 JS_GSN_CACHE(cx
).table
.ops
= NULL
;
1727 for (sn
= SCRIPT_NOTES(script
); !SN_IS_TERMINATOR(sn
);
1730 if (SN_IS_GETTABLE(sn
)) {
1731 entry
= (GSNCacheEntry
*)
1732 JS_DHashTableOperate(&JS_GSN_CACHE(cx
).table
, pc
,
1738 JS_GSN_CACHE(cx
).code
= script
->code
;
1739 JS_METER_GSN_CACHE(cx
, fills
);
1747 js_FramePCToLineNumber(JSContext
*cx
, JSStackFrame
*fp
)
1749 return js_PCToLineNumber(cx
, fp
->script
, fp
->imacpc
? fp
->imacpc
: fp
->regs
->pc
);
1753 js_PCToLineNumber(JSContext
*cx
, JSScript
*script
, jsbytecode
*pc
)
1757 ptrdiff_t offset
, target
;
1761 /* Cope with JSStackFrame.pc value prior to entering js_Interpret. */
1766 * Special case: function definition needs no line number note because
1767 * the function's script contains its starting line number.
1769 if (js_CodeSpec
[*pc
].format
& JOF_INDEXBASE
)
1770 pc
+= js_CodeSpec
[*pc
].length
;
1771 if (*pc
== JSOP_DEFFUN
) {
1772 GET_FUNCTION_FROM_BYTECODE(script
, pc
, 0, fun
);
1773 return fun
->u
.i
.script
->lineno
;
1777 * General case: walk through source notes accumulating their deltas,
1778 * keeping track of line-number notes, until we pass the note for pc's
1779 * offset within script->code.
1781 lineno
= script
->lineno
;
1783 target
= PTRDIFF(pc
, script
->code
, jsbytecode
);
1784 for (sn
= SCRIPT_NOTES(script
); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1785 offset
+= SN_DELTA(sn
);
1786 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1787 if (type
== SRC_SETLINE
) {
1788 if (offset
<= target
)
1789 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1790 } else if (type
== SRC_NEWLINE
) {
1791 if (offset
<= target
)
1794 if (offset
> target
)
1800 /* The line number limit is the same as the jssrcnote offset limit. */
1801 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1804 js_LineNumberToPC(JSScript
*script
, uintN target
)
1806 ptrdiff_t offset
, best
;
1807 uintN lineno
, bestdiff
, diff
;
1813 lineno
= script
->lineno
;
1814 bestdiff
= SN_LINE_LIMIT
;
1815 for (sn
= SCRIPT_NOTES(script
); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1817 * Exact-match only if offset is not in the prolog; otherwise use
1818 * nearest greater-or-equal line number match.
1820 if (lineno
== target
&& script
->code
+ offset
>= script
->main
)
1822 if (lineno
>= target
) {
1823 diff
= lineno
- target
;
1824 if (diff
< bestdiff
) {
1829 offset
+= SN_DELTA(sn
);
1830 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1831 if (type
== SRC_SETLINE
) {
1832 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1833 } else if (type
== SRC_NEWLINE
) {
1840 return script
->code
+ offset
;
1843 JS_FRIEND_API(uintN
)
1844 js_GetScriptLineExtent(JSScript
*script
)
1850 lineno
= script
->lineno
;
1851 for (sn
= SCRIPT_NOTES(script
); !SN_IS_TERMINATOR(sn
); sn
= SN_NEXT(sn
)) {
1852 type
= (JSSrcNoteType
) SN_TYPE(sn
);
1853 if (type
== SRC_SETLINE
) {
1854 lineno
= (uintN
) js_GetSrcNoteOffset(sn
, 0);
1855 } else if (type
== SRC_NEWLINE
) {
1859 return 1 + lineno
- script
->lineno
;
1862 #if JS_HAS_GENERATORS
1865 js_IsInsideTryWithFinally(JSScript
*script
, jsbytecode
*pc
)
1867 JSTryNoteArray
*tarray
;
1868 JSTryNote
*tn
, *tnlimit
;
1871 JS_ASSERT(script
->code
<= pc
);
1872 JS_ASSERT(pc
< script
->code
+ script
->length
);
1874 if (!script
->trynotesOffset
!= 0)
1876 tarray
= JS_SCRIPT_TRYNOTES(script
);
1877 JS_ASSERT(tarray
->length
!= 0);
1879 tn
= tarray
->vector
;
1880 tnlimit
= tn
+ tarray
->length
;
1881 off
= (uint32
)(pc
- script
->main
);
1883 if (off
- tn
->start
< tn
->length
) {
1884 if (tn
->kind
== JSTRY_FINALLY
)
1886 JS_ASSERT(tn
->kind
== JSTRY_CATCH
);
1888 } while (++tn
!= tnlimit
);