1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=80:
3 *
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 *
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/
11 *
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
16 *
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
19 *
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.
24 *
25 * Contributor(s):
26 *
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.
38 *
39 * ***** END LICENSE BLOCK ***** */
40
41 /*
42 * JS script operations.
43 */
44 #include "jsstddef.h"
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jsprf.h"
49 #include "jsapi.h"
50 #include "jsatom.h"
51 #include "jscntxt.h"
52 #include "jsconfig.h"
53 #include "jsdbgapi.h"
54 #include "jsemit.h"
55 #include "jsfun.h"
56 #include "jsinterp.h"
57 #include "jslock.h"
58 #include "jsnum.h"
59 #include "jsopcode.h"
60 #include "jsscript.h"
61 #if JS_HAS_XDR
62 #include "jsxdrapi.h"
63 #endif
64
65 #if JS_HAS_SCRIPT_OBJECT
66
67 static const char js_script_exec[] = "Script.prototype.exec";
68 static const char js_script_compile[] = "Script.prototype.compile";
69
70 #if JS_HAS_TOSOURCE
71 static JSBool
72 script_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
73 jsval *rval)
74 0 {
75 JSScript *script;
76 size_t i, j, k, n;
77 char buf[16];
78 jschar *s, *t;
79 uint32 indent;
80 JSString *str;
81
82 0 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
83 0 return JS_FALSE;
84 0 script = (JSScript *) JS_GetPrivate(cx, obj);
85
86 /* Let n count the source string length, j the "front porch" length. */
87 0 j = JS_snprintf(buf, sizeof buf, "(new %s(", js_ScriptClass.name);
88 0 n = j + 2;
89 0 if (!script) {
90 /* Let k count the constructor argument string length. */
91 0 k = 0;
92 0 s = NULL; /* quell GCC overwarning */
93 } else {
94 0 indent = 0;
95 0 if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
96 0 return JS_FALSE;
97 0 str = JS_DecompileScript(cx, script, "Script.prototype.toSource",
98 (uintN)indent);
99 0 if (!str)
100 0 return JS_FALSE;
101 0 str = js_QuoteString(cx, str, '\'');
102 0 if (!str)
103 0 return JS_FALSE;
104 0 s = JSSTRING_CHARS(str);
105 0 k = JSSTRING_LENGTH(str);
106 0 n += k;
107 }
108
109 /* Allocate the source string and copy into it. */
110 0 t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
111 0 if (!t)
112 0 return JS_FALSE;
113 0 for (i = 0; i < j; i++)
114 0 t[i] = buf[i];
115 0 for (j = 0; j < k; i++, j++)
116 0 t[i] = s[j];
117 0 t[i++] = ')';
118 0 t[i++] = ')';
119 0 t[i] = 0;
120
121 /* Create and return a JS string for t. */
122 0 str = JS_NewUCString(cx, t, n);
123 0 if (!str) {
124 0 JS_free(cx, t);
125 0 return JS_FALSE;
126 }
127 0 *rval = STRING_TO_JSVAL(str);
128 0 return JS_TRUE;
129 }
130 #endif /* JS_HAS_TOSOURCE */
131
132 static JSBool
133 script_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
134 jsval *rval)
135 0 {
136 JSScript *script;
137 uint32 indent;
138 JSString *str;
139
140 0 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
141 0 return JS_FALSE;
142 0 script = (JSScript *) JS_GetPrivate(cx, obj);
143 0 if (!script) {
144 0 *rval = STRING_TO_JSVAL(cx->runtime->emptyString);
145 0 return JS_TRUE;
146 }
147
148 0 indent = 0;
149 0 if (argc && !js_ValueToECMAUint32(cx, argv[0], &indent))
150 0 return JS_FALSE;
151 0 str = JS_DecompileScript(cx, script, "Script.prototype.toString",
152 (uintN)indent);
153 0 if (!str)
154 0 return JS_FALSE;
155 0 *rval = STRING_TO_JSVAL(str);
156 0 return JS_TRUE;
157 }
158
159 static JSBool
160 script_compile(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
161 jsval *rval)
162 0 {
163 JSScript *oldscript, *script;
164 JSStackFrame *fp, *caller;
165 JSString *str;
166 JSObject *scopeobj;
167 const char *file;
168 uintN line;
169 JSPrincipals *principals;
170
171 /* Make sure obj is a Script object. */
172 0 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
173 0 return JS_FALSE;
174
175 /* If no args, leave private undefined and return early. */
176 0 if (argc == 0)
177 0 goto out;
178
179 /* XXX thread safety was completely neglected in this function... */
180 0 oldscript = (JSScript *) JS_GetPrivate(cx, obj);
181 0 if (oldscript) {
182 0 for (fp = cx->fp; fp; fp = fp->down) {
183 0 if (fp->script == oldscript) {
184 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
185 JSMSG_SELF_MODIFYING_SCRIPT);
186 0 return JS_FALSE;
187 }
188 }
189 }
190
191 /* Otherwise, the first arg is the script source to compile. */
192 0 str = js_ValueToString(cx, argv[0]);
193 0 if (!str)
194 0 return JS_FALSE;
195 0 argv[0] = STRING_TO_JSVAL(str);
196
197 /* Compile using the caller's scope chain, which js_Invoke passes to fp. */
198 0 fp = cx->fp;
199 0 caller = JS_GetScriptedCaller(cx, fp);
200 JS_ASSERT(!caller || fp->scopeChain == caller->scopeChain);
201
202 0 scopeobj = NULL;
203 0 if (argc >= 2) {
204 0 if (!js_ValueToObject(cx, argv[1], &scopeobj))
205 0 return JS_FALSE;
206 0 argv[1] = OBJECT_TO_JSVAL(scopeobj);
207 }
208 0 if (caller) {
209 0 if (!scopeobj)
210 0 scopeobj = caller->scopeChain;
211
212 0 file = caller->script->filename;
213 0 line = js_PCToLineNumber(cx, caller->script, caller->pc);
214 0 principals = JS_EvalFramePrincipals(cx, fp, caller);
215 } else {
216 0 file = NULL;
217 0 line = 0;
218 0 principals = NULL;
219 }
220
221 /* Ensure we compile this script with the right (inner) principals. */
222 0 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_compile);
223 0 if (!scopeobj)
224 0 return JS_FALSE;
225
226 /*
227 * Compile the new script using the caller's scope chain, a la eval().
228 * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's
229 * flags, because compilation is here separated from execution, and the
230 * run-time scope chain may not match the compile-time. JSFRAME_EVAL is
231 * tested in jsemit.c and jsscan.c to optimize based on identity of run-
232 * and compile-time scope.
233 */
234 0 fp->flags |= JSFRAME_SCRIPT_OBJECT;
235 0 script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
236 JSSTRING_CHARS(str),
237 JSSTRING_LENGTH(str),
238 file, line);
239 0 if (!script)
240 0 return JS_FALSE;
241
242 /* Swap script for obj's old script, if any. */
243 0 if (!JS_SetPrivate(cx, obj, script)) {
244 0 js_DestroyScript(cx, script);
245 0 return JS_FALSE;
246 }
247 0 if (oldscript)
248 0 js_DestroyScript(cx, oldscript);
249
250 0 script->object = obj;
251 0 out:
252 /* Return the object. */
253 0 *rval = OBJECT_TO_JSVAL(obj);
254 0 return JS_TRUE;
255 }
256
257 static JSBool
258 script_exec(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
259 0 {
260 JSScript *script;
261 JSObject *scopeobj, *parent;
262 JSStackFrame *fp, *caller;
263 JSPrincipals *principals;
264
265 0 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
266 0 return JS_FALSE;
267 0 script = (JSScript *) JS_GetPrivate(cx, obj);
268 0 if (!script)
269 0 return JS_TRUE;
270
271 0 scopeobj = NULL;
272 0 if (argc) {
273 0 if (!js_ValueToObject(cx, argv[0], &scopeobj))
274 0 return JS_FALSE;
275 0 argv[0] = OBJECT_TO_JSVAL(scopeobj);
276 }
277
278 /*
279 * Emulate eval() by using caller's this, var object, sharp array, etc.,
280 * all propagated by js_Execute via a non-null fourth (down) argument to
281 * js_Execute. If there is no scripted caller, js_Execute uses its second
282 * (chain) argument to set the exec frame's varobj, thisp, and scopeChain.
283 *
284 * Unlike eval, which the compiler detects, Script.prototype.exec may be
285 * called from a lightweight function, or even from native code (in which
286 * case fp->varobj and fp->scopeChain are null). If exec is called from
287 * a lightweight function, we will need to get a Call object representing
288 * its frame, to act as the var object and scope chain head.
289 */
290 0 fp = cx->fp;
291 0 caller = JS_GetScriptedCaller(cx, fp);
292 0 if (caller && !caller->varobj) {
293 /* Called from a lightweight function. */
294 JS_ASSERT(caller->fun && !(caller->fun->flags & JSFUN_HEAVYWEIGHT));
295
296 /* Scope chain links from Call object to callee's parent. */
297 0 parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(caller->argv[-2]));
298 0 if (!js_GetCallObject(cx, caller, parent))
299 0 return JS_FALSE;
300 }
301
302 0 if (!scopeobj) {
303 /* No scope object passed in: try to use the caller's scope chain. */
304 0 if (caller) {
305 /*
306 * Load caller->scopeChain after the conditional js_GetCallObject
307 * call above, which resets scopeChain as well as varobj.
308 */
309 0 scopeobj = caller->scopeChain;
310 } else {
311 /*
312 * Called from native code, so we don't know what scope object to
313 * use. We could use parent (see above), but Script.prototype.exec
314 * might be a shared/sealed "superglobal" method. A more general
315 * approach would use cx->globalObject, which will be the same as
316 * exec.__parent__ in the non-superglobal case. In the superglobal
317 * case it's the right object: the global, not the superglobal.
318 */
319 0 scopeobj = cx->globalObject;
320 }
321 }
322
323 0 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_script_exec);
324 0 if (!scopeobj)
325 0 return JS_FALSE;
326
327 /* Belt-and-braces: check that this script object has access to scopeobj. */
328 0 principals = script->principals;
329 0 if (!js_CheckPrincipalsAccess(cx, scopeobj, principals, js_script_exec))
330 0 return JS_FALSE;
331
332 0 return js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
333 }
334
335 #if JS_HAS_XDR
336
337 static JSBool
338 XDRAtomListElement(JSXDRState *xdr, JSAtomListElement *ale)
339 0 {
340 jsval value;
341 jsatomid index;
342
343 0 if (xdr->mode == JSXDR_ENCODE)
344 0 value = ATOM_KEY(ALE_ATOM(ale));
345
346 0 index = ALE_INDEX(ale);
347 0 if (!JS_XDRUint32(xdr, &index))
348 0 return JS_FALSE;
349 0 ALE_SET_INDEX(ale, index);
350
351 0 if (!JS_XDRValue(xdr, &value))
352 0 return JS_FALSE;
353
354 0 if (xdr->mode == JSXDR_DECODE) {
355 0 if (!ALE_SET_ATOM(ale, js_AtomizeValue(xdr->cx, value, 0)))
356 0 return JS_FALSE;
357 }
358 0 return JS_TRUE;
359 }
360
361 static JSBool
362 XDRAtomMap(JSXDRState *xdr, JSAtomMap *map)
363 0 {
364 uint32 length;
365 uintN i;
366 JSBool ok;
367
368 0 if (xdr->mode == JSXDR_ENCODE)
369 0 length = map->length;
370
371 0 if (!JS_XDRUint32(xdr, &length))
372 0 return JS_FALSE;
373
374 0 if (xdr->mode == JSXDR_DECODE) {
375 JSContext *cx;
376 void *mark;
377 JSAtomList al;
378 JSAtomListElement *ale;
379
380 0 cx = xdr->cx;
381 0 mark = JS_ARENA_MARK(&cx->tempPool);
382 0 ATOM_LIST_INIT(&al);
383 0 for (i = 0; i < length; i++) {
384 0 JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
385 0 if (!ale ||
386 !XDRAtomListElement(xdr, ale)) {
387 0 if (!ale)
388 0 JS_ReportOutOfMemory(cx);
389 0 JS_ARENA_RELEASE(&cx->tempPool, mark);
390 0 return JS_FALSE;
391 }
392 0 ALE_SET_NEXT(ale, al.list);
393 0 al.count++;
394 0 al.list = ale;
395 }
396 0 ok = js_InitAtomMap(cx, map, &al);
397 0 JS_ARENA_RELEASE(&cx->tempPool, mark);
398 0 return ok;
399 }
400
401 0 if (xdr->mode == JSXDR_ENCODE) {
402 JSAtomListElement ale;
403
404 0 for (i = 0; i < map->length; i++) {
405 0 ALE_SET_ATOM(&ale, map->vector[i]);
406 0 ALE_SET_INDEX(&ale, i);
407 0 if (!XDRAtomListElement(xdr, &ale))
408 0 return JS_FALSE;
409 }
410 }
411 0 return JS_TRUE;
412 }
413
414 JSBool
415 js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
416 0 {
417 JSContext *cx;
418 JSScript *script, *newscript;
419 uint32 length, lineno, depth, magic, nsrcnotes, ntrynotes;
420 uint32 prologLength, version;
421 JSBool filenameWasSaved;
422 jssrcnote *notes, *sn;
423
424 0 cx = xdr->cx;
425 0 script = *scriptp;
426 0 nsrcnotes = ntrynotes = 0;
427 0 filenameWasSaved = JS_FALSE;
428 0 notes = NULL;
429
430 /*
431 * Encode prologLength and version after script->length (_2 or greater),
432 * but decode both new (>= _2) and old, prolog&version-free (_1) scripts.
433 * Version _3 supports principals serialization. Version _4 reorders the
434 * nsrcnotes and ntrynotes fields to come before everything except magic,
435 * length, prologLength, and version, so that srcnote and trynote storage
436 * can be allocated as part of the JSScript (along with bytecode storage).
437 */
438 0 if (xdr->mode == JSXDR_ENCODE)
439 0 magic = JSXDR_MAGIC_SCRIPT_CURRENT;
440 0 if (!JS_XDRUint32(xdr, &magic))
441 0 return JS_FALSE;
442 0 if (magic != JSXDR_MAGIC_SCRIPT_4 &&
443 magic != JSXDR_MAGIC_SCRIPT_3 &&
444 magic != JSXDR_MAGIC_SCRIPT_2 &&
445 magic != JSXDR_MAGIC_SCRIPT_1) {
446 0 if (!hasMagic) {
447 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
448 JSMSG_BAD_SCRIPT_MAGIC);
449 0 return JS_FALSE;
450 }
451 0 *hasMagic = JS_FALSE;
452 0 return JS_TRUE;
453 }
454 0 if (hasMagic)
455 0 *hasMagic = JS_TRUE;
456
457 0 if (xdr->mode == JSXDR_ENCODE) {
458 0 length = script->length;
459 0 prologLength = PTRDIFF(script->main, script->code, jsbytecode);
460 JS_ASSERT((int16)script->version != JSVERSION_UNKNOWN);
461 0 version = (uint32)script->version | (script->numGlobalVars << 16);
462 0 lineno = (uint32)script->lineno;
463 0 depth = (uint32)script->depth;
464
465 /* Count the srcnotes, keeping notes pointing at the first one. */
466 0 notes = SCRIPT_NOTES(script);
467 0 for (sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
468 continue;
469 0 nsrcnotes = PTRDIFF(sn, notes, jssrcnote);
470 0 nsrcnotes++; /* room for the terminator */
471
472 /* Count the trynotes. */
473 0 if (script->trynotes) {
474 0 while (script->trynotes[ntrynotes].catchStart)
475 0 ntrynotes++;
476 0 ntrynotes++; /* room for the end marker */
477 }
478 }
479
480 0 if (!JS_XDRUint32(xdr, &length))
481 0 return JS_FALSE;
482 0 if (magic >= JSXDR_MAGIC_SCRIPT_2) {
483 0 if (!JS_XDRUint32(xdr, &prologLength))
484 0 return JS_FALSE;
485 0 if (!JS_XDRUint32(xdr, &version))
486 0 return JS_FALSE;
487
488 /* To fuse allocations, we need srcnote and trynote counts early. */
489 0 if (magic >= JSXDR_MAGIC_SCRIPT_4) {
490 0 if (!JS_XDRUint32(xdr, &nsrcnotes))
491 0 return JS_FALSE;
492 0 if (!JS_XDRUint32(xdr, &ntrynotes))
493 0 return JS_FALSE;
494 }
495 }
496
497 0 if (xdr->mode == JSXDR_DECODE) {
498 0 script = js_NewScript(cx, length, nsrcnotes, ntrynotes);
499 0 if (!script)
500 0 return JS_FALSE;
501 0 if (magic >= JSXDR_MAGIC_SCRIPT_2) {
502 0 script->main += prologLength;
503 0 script->version = (JSVersion) (version & 0xffff);
504 0 script->numGlobalVars = (uint16) (version >> 16);
505
506 /* If we know nsrcnotes, we allocated space for notes in script. */
507 0 if (magic >= JSXDR_MAGIC_SCRIPT_4)
508 0 notes = SCRIPT_NOTES(script);
509 }
510 0 *scriptp = script;
511 }
512
513 /*
514 * Control hereafter must goto error on failure, in order for the DECODE
515 * case to destroy script and conditionally free notes, which if non-null
516 * in the (DECODE and magic < _4) case must point at a temporary vector
517 * allocated just below.
518 */
519 0 if (!JS_XDRBytes(xdr, (char *)script->code, length * sizeof(jsbytecode)) ||
520 !XDRAtomMap(xdr, &script->atomMap)) {
521 goto error;
522 }
523
524 0 if (magic < JSXDR_MAGIC_SCRIPT_4) {
525 0 if (!JS_XDRUint32(xdr, &nsrcnotes))
526 0 goto error;
527 0 if (xdr->mode == JSXDR_DECODE) {
528 0 notes = (jssrcnote *) JS_malloc(cx, nsrcnotes * sizeof(jssrcnote));
529 0 if (!notes)
530 0 goto error;
531 }
532 }
533
534 0 if (!JS_XDRBytes(xdr, (char *)notes, nsrcnotes * sizeof(jssrcnote)) ||
535 !JS_XDRCStringOrNull(xdr, (char **)&script->filename) ||
536 !JS_XDRUint32(xdr, &lineno) ||
537 !JS_XDRUint32(xdr, &depth) ||
538 (magic < JSXDR_MAGIC_SCRIPT_4 && !JS_XDRUint32(xdr, &ntrynotes))) {
539 goto error;
540 }
541
542 /* Script principals transcoding support comes with versions >= _3. */
543 0 if (magic >= JSXDR_MAGIC_SCRIPT_3) {
544 JSPrincipals *principals;
545 uint32 encodeable;
546
547 0 if (xdr->mode == JSXDR_ENCODE) {
548 0 principals = script->principals;
549 0 encodeable = (cx->runtime->principalsTranscoder != NULL);
550 0 if (!JS_XDRUint32(xdr, &encodeable))
551 0 goto error;
552 0 if (encodeable &&
553 !cx->runtime->principalsTranscoder(xdr, &principals)) {
554 0 goto error;
555 }
556 } else {
557 0 if (!JS_XDRUint32(xdr, &encodeable))
558 0 goto error;
559 0 if (encodeable) {
560 0 if (!cx->runtime->principalsTranscoder) {
561 0 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
562 JSMSG_CANT_DECODE_PRINCIPALS);
563 0 goto error;
564 }
565 0 if (!cx->runtime->principalsTranscoder(xdr, &principals))
566 0 goto error;
567 0 script->principals = principals;
568 }
569 }
570 }
571
572 0 if (xdr->mode == JSXDR_DECODE) {
573 0 const char *filename = script->filename;
574 0 if (filename) {
575 0 filename = js_SaveScriptFilename(cx, filename);
576 0 if (!filename)
577 0 goto error;
578 0 JS_free(cx, (void *) script->filename);
579 0 script->filename = filename;
580 0 filenameWasSaved = JS_TRUE;
581 }
582 0 script->lineno = (uintN)lineno;
583 0 script->depth = (uintN)depth;
584
585 0 if (magic < JSXDR_MAGIC_SCRIPT_4) {
586 /*
587 * Argh, we have to reallocate script, copy notes into the extra
588 * space after the bytecodes, and free the temporary notes vector.
589 * First, add enough slop to nsrcnotes so we can align the address
590 * after the srcnotes of the first trynote.
591 */
592 0 uint32 osrcnotes = nsrcnotes;
593
594 0 if (ntrynotes)
595 0 nsrcnotes += JSTRYNOTE_ALIGNMASK;
596 0 newscript = (JSScript *) JS_realloc(cx, script,
597 sizeof(JSScript) +
598 length * sizeof(jsbytecode) +
599 nsrcnotes * sizeof(jssrcnote) +
600 ntrynotes * sizeof(JSTryNote));
601 0 if (!newscript)
602 0 goto error;
603
604 0 *scriptp = script = newscript;
605 0 script->code = (jsbytecode *)(script + 1);
606 0 script->main = script->code + prologLength;
607 0 memcpy(script->code + length, notes, osrcnotes * sizeof(jssrcnote));
608 0 JS_free(cx, (void *) notes);
609 0 notes = NULL;
610 0 if (ntrynotes) {
611 0 script->trynotes = (JSTryNote *)
612 ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
613 ~(jsword)JSTRYNOTE_ALIGNMASK);
614 0 memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
615 }
616 }
617 }
618
619 0 while (ntrynotes) {
620 0 JSTryNote *tn = &script->trynotes[--ntrynotes];
621 0 uint32 start = (uint32) tn->start,
622 0 catchLength = (uint32) tn->length,
623 0 catchStart = (uint32) tn->catchStart;
624
625 0 if (!JS_XDRUint32(xdr, &start) ||
626 !JS_XDRUint32(xdr, &catchLength) ||
627 !JS_XDRUint32(xdr, &catchStart)) {
628 goto error;
629 }
630 0 tn->start = (ptrdiff_t) start;
631 0 tn->length = (ptrdiff_t) catchLength;
632 0 tn->catchStart = (ptrdiff_t) catchStart;
633 }
634 0 return JS_TRUE;
635
636 0 error:
637 0 if (xdr->mode == JSXDR_DECODE) {
638 0 if (script->filename && !filenameWasSaved) {
639 0 JS_free(cx, (void *) script->filename);
640 0 script->filename = NULL;
641 }
642 0 if (notes && magic < JSXDR_MAGIC_SCRIPT_4)
643 0 JS_free(cx, (void *) notes);
644 0 js_DestroyScript(cx, script);
645 0 *scriptp = NULL;
646 }
647 0 return JS_FALSE;
648 }
649
650 #if JS_HAS_XDR_FREEZE_THAW
651 /*
652 * These cannot be exposed to web content, and chrome does not need them, so
653 * we take them out of the Mozilla client altogether. Fortunately, there is
654 * no way to serialize a native function (see fun_xdrObject in jsfun.c).
655 */
656
657 static JSBool
658 script_freeze(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
659 jsval *rval)
660 {
661 JSXDRState *xdr;
662 JSScript *script;
663 JSBool ok, hasMagic;
664 uint32 len;
665 void *buf;
666 JSString *str;
667
668 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
669 return JS_FALSE;
670 script = (JSScript *) JS_GetPrivate(cx, obj);
671 if (!script)
672 return JS_TRUE;
673
674 /* create new XDR */
675 xdr = JS_XDRNewMem(cx, JSXDR_ENCODE);
676 if (!xdr)
677 return JS_FALSE;
678
679 /* write */
680 ok = js_XDRScript(xdr, &script, &hasMagic);
681 if (!ok)
682 goto out;
683 if (!hasMagic) {
684 *rval = JSVAL_VOID;
685 goto out;
686 }
687
688 buf = JS_XDRMemGetData(xdr, &len);
689 if (!buf) {
690 ok = JS_FALSE;
691 goto out;
692 }
693
694 JS_ASSERT((jsword)buf % sizeof(jschar) == 0);
695 len /= sizeof(jschar);
696 str = JS_NewUCStringCopyN(cx, (jschar *)buf, len);
697 if (!str) {
698 ok = JS_FALSE;
699 goto out;
700 }
701
702 #if IS_BIG_ENDIAN
703 {
704 jschar *chars;
705 uint32 i;
706
707 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
708 chars = JS_GetStringChars(str);
709 for (i = 0; i < len; i++)
710 chars[i] = JSXDR_SWAB16(chars[i]);
711 }
712 #endif
713 *rval = STRING_TO_JSVAL(str);
714
715 out:
716 JS_XDRDestroy(xdr);
717 return ok;
718 }
719
720 static JSBool
721 script_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
722 jsval *rval)
723 {
724 JSXDRState *xdr;
725 JSString *str;
726 void *buf;
727 uint32 len;
728 JSScript *script, *oldscript;
729 JSBool ok, hasMagic;
730
731 if (!JS_InstanceOf(cx, obj, &js_ScriptClass, argv))
732 return JS_FALSE;
733
734 if (argc == 0)
735 return JS_TRUE;
736 str = js_ValueToString(cx, argv[0]);
737 if (!str)
738 return JS_FALSE;
739 argv[0] = STRING_TO_JSVAL(str);
740
741 /* create new XDR */
742 xdr = JS_XDRNewMem(cx, JSXDR_DECODE);
743 if (!xdr)
744 return JS_FALSE;
745
746 buf = JS_GetStringChars(str);
747 len = JS_GetStringLength(str);
748 #if IS_BIG_ENDIAN
749 {
750 jschar *from, *to;
751 uint32 i;
752
753 /* Swap bytes in Unichars to keep frozen strings machine-independent. */
754 from = (jschar *)buf;
755 to = (jschar *) JS_malloc(cx, len * sizeof(jschar));
756 if (!to) {
757 JS_XDRDestroy(xdr);
758 return JS_FALSE;
759 }
760 for (i = 0; i < len; i++)
761 to[i] = JSXDR_SWAB16(from[i]);
762 buf = (char *)to;
763 }
764 #endif
765 len *= sizeof(jschar);
766 JS_XDRMemSetData(xdr, buf, len);
767
768 /* XXXbe should magic mismatch be error, or false return value? */
769 ok = js_XDRScript(xdr, &script, &hasMagic);
770 if (!ok)
771 goto out;
772 if (!hasMagic) {
773 *rval = JSVAL_FALSE;
774 goto out;
775 }
776
777 /* Swap script for obj's old script, if any. */
778 oldscript = (JSScript *) JS_GetPrivate(cx, obj);
779 ok = JS_SetPrivate(cx, obj, script);
780 if (!ok) {
781 JS_free(cx, script);
782 goto out;
783 }
784 if (oldscript)
785 js_DestroyScript(cx, oldscript);
786
787 script->object = obj;
788 js_CallNewScriptHook(cx, script, NULL);
789
790 out:
791 /*
792 * We reset the buffer to be NULL so that it doesn't free the chars
793 * memory owned by str (argv[0]).
794 */
795 JS_XDRMemSetData(xdr, NULL, 0);
796 JS_XDRDestroy(xdr);
797 #if IS_BIG_ENDIAN
798 JS_free(cx, buf);
799 #endif
800 *rval = JSVAL_TRUE;
801 return ok;
802 }
803
804 static const char js_thaw_str[] = "thaw";
805
806 #endif /* JS_HAS_XDR_FREEZE_THAW */
807 #endif /* JS_HAS_XDR */
808
809 static JSFunctionSpec script_methods[] = {
810 #if JS_HAS_TOSOURCE
811 {js_toSource_str, script_toSource, 0,0,0},
812 #endif
813 {js_toString_str, script_toString, 0,0,0},
814 {"compile", script_compile, 2,0,0},
815 {"exec", script_exec, 1,0,0},
816 #if JS_HAS_XDR_FREEZE_THAW
817 {"freeze", script_freeze, 0,0,0},
818 {js_thaw_str, script_thaw, 1,0,0},
819 #endif /* JS_HAS_XDR_FREEZE_THAW */
820 {0,0,0,0,0}
821 };
822
823 #endif /* JS_HAS_SCRIPT_OBJECT */
824
825 static void
826 script_finalize(JSContext *cx, JSObject *obj)
827 34 {
828 JSScript *script;
829
830 34 script = (JSScript *) JS_GetPrivate(cx, obj);
831 34 if (script)
832 0 js_DestroyScript(cx, script);
833 34 }
834
835 static JSBool
836 script_call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
837 0 {
838 #if JS_HAS_SCRIPT_OBJECT
839 0 return script_exec(cx, JSVAL_TO_OBJECT(argv[-2]), argc, argv, rval);
840 #else
841 return JS_FALSE;
842 #endif
843 }
844
845 static uint32
846 script_mark(JSContext *cx, JSObject *obj, void *arg)
847 34 {
848 JSScript *script;
849
850 34 script = (JSScript *) JS_GetPrivate(cx, obj);
851 34 if (script)
852 0 js_MarkScript(cx, script, arg);
853 34 return 0;
854 }
855
856 JS_FRIEND_DATA(JSClass) js_ScriptClass = {
857 js_Script_str,
858 JSCLASS_HAS_PRIVATE,
859 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
860 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize,
861 NULL, NULL, script_call, NULL,/*XXXbe xdr*/
862 NULL, NULL, script_mark, 0
863 };
864
865 #if JS_HAS_SCRIPT_OBJECT
866
867 static JSBool
868 Script(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
869 0 {
870 /* If not constructing, replace obj with a new Script object. */
871 0 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
872 0 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
873 0 if (!obj)
874 0 return JS_FALSE;
875
876 /*
877 * script_compile does not use rval to root its temporaries
878 * so we can use it to root obj.
879 */
880 0 *rval = OBJECT_TO_JSVAL(obj);
881 }
882 0 return script_compile(cx, obj, argc, argv, rval);
883 }
884
885 #if JS_HAS_XDR_FREEZE_THAW
886
887 static JSBool
888 script_static_thaw(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
889 jsval *rval)
890 {
891 obj = js_NewObject(cx, &js_ScriptClass, NULL, NULL);
892 if (!obj)
893 return JS_FALSE;
894 if (!script_thaw(cx, obj, argc, argv, rval))
895 return JS_FALSE;
896 *rval = OBJECT_TO_JSVAL(obj);
897 return JS_TRUE;
898 }
899
900 static JSFunctionSpec script_static_methods[] = {
901 {js_thaw_str, script_static_thaw, 1,0,0},
902 {0,0,0,0,0}
903 };
904
905 #else /* !JS_HAS_XDR_FREEZE_THAW */
906
907 #define script_static_methods NULL
908
909 #endif /* !JS_HAS_XDR_FREEZE_THAW */
910
911 JSObject *
912 js_InitScriptClass(JSContext *cx, JSObject *obj)
913 34 {
914 34 return JS_InitClass(cx, obj, NULL, &js_ScriptClass, Script, 1,
915 NULL, script_methods, NULL, script_static_methods);
916 }
917
918 #endif /* JS_HAS_SCRIPT_OBJECT */
919
920 /*
921 * Shared script filename management.
922 */
923 JS_STATIC_DLL_CALLBACK(int)
924 js_compare_strings(const void *k1, const void *k2)
925 9396 {
926 9396 return strcmp(k1, k2) == 0;
927 }
928
929 /* Shared with jsatom.c to save code space. */
930 extern void * JS_DLL_CALLBACK
931 js_alloc_table_space(void *priv, size_t size);
932
933 extern void JS_DLL_CALLBACK
934 js_free_table_space(void *priv, void *item);
935
936 /* NB: This struct overlays JSHashEntry -- see jshash.h, do not reorganize. */
937 typedef struct ScriptFilenameEntry {
938 JSHashEntry *next; /* hash chain linkage */
939 JSHashNumber keyHash; /* key hash function result */
940 const void *key; /* ptr to filename, below */
941 uint32 flags; /* user-defined filename prefix flags */
942 JSPackedBool mark; /* GC mark flag */
943 char filename[3]; /* two or more bytes, NUL-terminated */
944 } ScriptFilenameEntry;
945
946 JS_STATIC_DLL_CALLBACK(JSHashEntry *)
947 js_alloc_sftbl_entry(void *priv, const void *key)
948 518 {
949 518 size_t nbytes = offsetof(ScriptFilenameEntry, filename) + strlen(key) + 1;
950
951 518 return (JSHashEntry *) malloc(JS_MAX(nbytes, sizeof(JSHashEntry)));
952 }
953
954 JS_STATIC_DLL_CALLBACK(void)
955 js_free_sftbl_entry(void *priv, JSHashEntry *he, uintN flag)
956 518 {
957 518 if (flag != HT_FREE_ENTRY)
958 0 return;
959 518 free(he);
960 }
961
962 static JSHashAllocOps sftbl_alloc_ops = {
963 js_alloc_table_space, js_free_table_space,
964 js_alloc_sftbl_entry, js_free_sftbl_entry
965 };
966
967 JSBool
968 js_InitRuntimeScriptState(JSRuntime *rt)
969 34 {
970 #ifdef JS_THREADSAFE
971 JS_ASSERT(!rt->scriptFilenameTableLock);
972 rt->scriptFilenameTableLock = JS_NEW_LOCK();
973 if (!rt->scriptFilenameTableLock)
974 return JS_FALSE;
975 #endif
976 JS_ASSERT(!rt->scriptFilenameTable);
977 34 rt->scriptFilenameTable =
978 JS_NewHashTable(16, JS_HashString, js_compare_strings, NULL,
979 &sftbl_alloc_ops, NULL);
980 34 if (!rt->scriptFilenameTable) {
981 0 js_FinishRuntimeScriptState(rt); /* free lock if threadsafe */
982 0 return JS_FALSE;
983 }
984 34 JS_INIT_CLIST(&rt->scriptFilenamePrefixes);
985 34 return JS_TRUE;
986 }
987
988 typedef struct ScriptFilenamePrefix {
989 JSCList links; /* circular list linkage for easy deletion */
990 const char *name; /* pointer to pinned ScriptFilenameEntry string */
991 size_t length; /* prefix string length, precomputed */
992 uint32 flags; /* user-defined flags to inherit from this prefix */
993 } ScriptFilenamePrefix;
994
995 void
996 js_FinishRuntimeScriptState(JSRuntime *rt)
997 68 {
998 68 if (rt->scriptFilenameTable) {
999 34 JS_HashTableDestroy(rt->scriptFilenameTable);
1000 34 rt->scriptFilenameTable = NULL;
1001 }
1002 #ifdef JS_THREADSAFE
1003 if (rt->scriptFilenameTableLock) {
1004 JS_DESTROY_LOCK(rt->scriptFilenameTableLock);
1005 rt->scriptFilenameTableLock = NULL;
1006 }
1007 #endif
1008 68 }
1009
1010 void
1011 js_FreeRuntimeScriptState(JSRuntime *rt)
1012 34 {
1013 ScriptFilenamePrefix *sfp;
1014
1015 68 while (!JS_CLIST_IS_EMPTY(&rt->scriptFilenamePrefixes)) {
1016 0 sfp = (ScriptFilenamePrefix *) rt->scriptFilenamePrefixes.next;
1017 0 JS_REMOVE_LINK(&sfp->links);
1018 0 free(sfp);
1019 }
1020 34 js_FinishRuntimeScriptState(rt);
1021 34 }
1022
1023 #ifdef DEBUG_brendan
1024 size_t sftbl_savings = 0;
1025 #endif
1026
1027 static ScriptFilenameEntry *
1028 SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
1029 9914 {
1030 JSHashTable *table;
1031 JSHashNumber hash;
1032 JSHashEntry **hep;
1033 ScriptFilenameEntry *sfe;
1034 size_t length;
1035 JSCList *head, *link;
1036 ScriptFilenamePrefix *sfp;
1037
1038 9914 table = rt->scriptFilenameTable;
1039 9914 hash = JS_HashString(filename);
1040 9914 hep = JS_HashTableRawLookup(table, hash, filename);
1041 9914 sfe = (ScriptFilenameEntry *) *hep;
1042 #ifdef DEBUG_brendan
1043 if (sfe)
1044 sftbl_savings += strlen(sfe->filename);
1045 #endif
1046
1047 9914 if (!sfe) {
1048 518 sfe = (ScriptFilenameEntry *)
1049 JS_HashTableRawAdd(table, hep, hash, filename, NULL);
1050 518 if (!sfe)
1051 0 return NULL;
1052 518 sfe->key = strcpy(sfe->filename, filename);
1053 518 sfe->flags = 0;
1054 518 sfe->mark = JS_FALSE;
1055 }
1056
1057 /* If saving a prefix, add it to the set in rt->scriptFilenamePrefixes. */
1058 9914 if (flags != 0) {
1059 /* Search in case filename was saved already; we must be idempotent. */
1060 0 sfp = NULL;
1061 0 length = strlen(filename);
1062 0 for (head = link = &rt->scriptFilenamePrefixes;
1063 0 link->next != head;
1064 0 link = link->next) {
1065 /* Lag link behind sfp to insert in non-increasing length order. */
1066 0 sfp = (ScriptFilenamePrefix *) link->next;
1067 0 if (!strcmp(sfp->name, filename))
1068 0 break;
1069 0 if (sfp->length <= length) {
1070 0 sfp = NULL;
1071 0 break;
1072 }
1073 0 sfp = NULL;
1074 }
1075
1076 0 if (!sfp) {
1077 /* No such prefix: add one now. */
1078 0 sfp = (ScriptFilenamePrefix *) malloc(sizeof(ScriptFilenamePrefix));
1079 0 if (!sfp)
1080 0 return NULL;
1081 0 JS_INSERT_AFTER(&sfp->links, link);
1082 0 sfp->name = sfe->filename;
1083 0 sfp->length = length;
1084 0 sfp->flags = 0;
1085 }
1086
1087 /*
1088 * Accumulate flags in both sfe and sfp: sfe for later access from the
1089 * JS_GetScriptedCallerFilenameFlags debug-API, and sfp so that longer
1090 * filename entries can inherit by prefix.
1091 */
1092 0 sfe->flags |= flags;
1093 0 sfp->flags |= flags;
1094 }
1095
1096 9914 return sfe;
1097 }
1098
1099 const char *
1100 js_SaveScriptFilename(JSContext *cx, const char *filename)
1101 9914 {
1102 JSRuntime *rt;
1103 ScriptFilenameEntry *sfe;
1104 JSCList *head, *link;
1105 ScriptFilenamePrefix *sfp;
1106
1107 9914 rt = cx->runtime;
1108 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1109 9914 sfe = SaveScriptFilename(rt, filename, 0);
1110 9914 if (!sfe) {
1111 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1112 0 JS_ReportOutOfMemory(cx);
1113 0 return NULL;
1114 }
1115
1116 /*
1117 * Try to inherit flags by prefix. We assume there won't be more than a
1118 * few (dozen! ;-) prefixes, so linear search is tolerable.
1119 * XXXbe every time I've assumed that in the JS engine, I've been wrong!
1120 */
1121 9914 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1122 19828 link != head;
1123 0 link = link->next) {
1124 0 sfp = (ScriptFilenamePrefix *) link;
1125 0 if (!strncmp(sfp->name, filename, sfp->length)) {
1126 0 sfe->flags |= sfp->flags;
1127 0 break;
1128 }
1129 }
1130 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1131 9914 return sfe->filename;
1132 }
1133
1134 const char *
1135 js_SaveScriptFilenameRT(JSRuntime *rt, const char *filename, uint32 flags)
1136 0 {
1137 ScriptFilenameEntry *sfe;
1138
1139 /* This may be called very early, via the jsdbgapi.h entry point. */
1140 0 if (!rt->scriptFilenameTable && !js_InitRuntimeScriptState(rt))
1141 0 return NULL;
1142
1143 JS_ACQUIRE_LOCK(rt->scriptFilenameTableLock);
1144 0 sfe = SaveScriptFilename(rt, filename, flags);
1145 JS_RELEASE_LOCK(rt->scriptFilenameTableLock);
1146 0 if (!sfe)
1147 0 return NULL;
1148
1149 0 return sfe->filename;
1150 }
1151
1152 /*
1153 * Back up from a saved filename by its offset within its hash table entry.
1154 */
1155 #define FILENAME_TO_SFE(fn) \
1156 ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
1157
1158 /*
1159 * The sfe->key member, redundant given sfe->filename but required by the old
1160 * jshash.c code, here gives us a useful sanity check. This assertion will
1161 * very likely botch if someone tries to mark a string that wasn't allocated
1162 * as an sfe->filename.
1163 */
1164 #define ASSERT_VALID_SFE(sfe) JS_ASSERT((sfe)->key == (sfe)->filename)
1165
1166 uint32
1167 js_GetScriptFilenameFlags(const char *filename)
1168 0 {
1169 ScriptFilenameEntry *sfe;
1170
1171 0 sfe = FILENAME_TO_SFE(filename);
1172 ASSERT_VALID_SFE(sfe);
1173 0 return sfe->flags;
1174 }
1175
1176 void
1177 js_MarkScriptFilename(const char *filename)
1178 3819 {
1179 ScriptFilenameEntry *sfe;
1180
1181 3819 sfe = FILENAME_TO_SFE(filename);
1182 ASSERT_VALID_SFE(sfe);
1183 3819 sfe->mark = JS_TRUE;
1184 3819 }
1185
1186 JS_STATIC_DLL_CALLBACK(intN)
1187 js_script_filename_marker(JSHashEntry *he, intN i, void *arg)
1188 0 {
1189 0 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1190
1191 0 sfe->mark = JS_TRUE;
1192 0 return HT_ENUMERATE_NEXT;
1193 }
1194
1195 void
1196 js_MarkScriptFilenames(JSRuntime *rt, uintN gcflags)
1197 68 {
1198 JSCList *head, *link;
1199 ScriptFilenamePrefix *sfp;
1200
1201 68 if (gcflags & GC_KEEP_ATOMS) {
1202 0 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1203 js_script_filename_marker,
1204 rt);
1205 }
1206 68 for (head = &rt->scriptFilenamePrefixes, link = head->next;
1207 136 link != head;
1208 0 link = link->next) {
1209 0 sfp = (ScriptFilenamePrefix *) link;
1210 0 js_MarkScriptFilename(sfp->name);
1211 }
1212 68 }
1213
1214 JS_STATIC_DLL_CALLBACK(intN)
1215 js_script_filename_sweeper(JSHashEntry *he, intN i, void *arg)
1216 618 {
1217 618 ScriptFilenameEntry *sfe = (ScriptFilenameEntry *) he;
1218
1219 618 if (!sfe->mark)
1220 518 return HT_ENUMERATE_REMOVE;
1221 100 sfe->mark = JS_FALSE;
1222 100 return HT_ENUMERATE_NEXT;
1223 }
1224
1225 void
1226 js_SweepScriptFilenames(JSRuntime *rt)
1227 68 {
1228 68 JS_HashTableEnumerateEntries(rt->scriptFilenameTable,
1229 js_script_filename_sweeper,
1230 rt);
1231 #ifdef DEBUG_notme
1232 printf("script filename table savings so far: %u\n", sftbl_savings);
1233 #endif
1234 68 }
1235
1236 JSScript *
1237 js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 ntrynotes)
1238 9948 {
1239 JSScript *script;
1240
1241 /* Round up source note count to align script->trynotes for its type. */
1242 9948 if (ntrynotes)
1243 66 nsrcnotes += JSTRYNOTE_ALIGNMASK;
1244 9948 script = (JSScript *) JS_malloc(cx,
1245 sizeof(JSScript) +
1246 length * sizeof(jsbytecode) +
1247 nsrcnotes * sizeof(jssrcnote) +
1248 ntrynotes * sizeof(JSTryNote));
1249 9948 if (!script)
1250 0 return NULL;
1251 9948 memset(script, 0, sizeof(JSScript));
1252 9948 script->code = script->main = (jsbytecode *)(script + 1);
1253 9948 script->length = length;
1254 9948 script->version = cx->version;
1255 9948 if (ntrynotes) {
1256 66 script->trynotes = (JSTryNote *)
1257 ((jsword)(SCRIPT_NOTES(script) + nsrcnotes) &
1258 ~(jsword)JSTRYNOTE_ALIGNMASK);
1259 66 memset(script->trynotes, 0, ntrynotes * sizeof(JSTryNote));
1260 }
1261 9948 return script;
1262 }
1263
1264 JS_FRIEND_API(JSScript *)
1265 js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun)
1266 9914 {
1267 uint32 mainLength, prologLength, nsrcnotes, ntrynotes;
1268 JSScript *script;
1269 const char *filename;
1270
1271 9914 mainLength = CG_OFFSET(cg);
1272 9914 prologLength = CG_PROLOG_OFFSET(cg);
1273 9914 CG_COUNT_FINAL_SRCNOTES(cg, nsrcnotes);
1274 9914 CG_COUNT_FINAL_TRYNOTES(cg, ntrynotes);
1275 9914 script = js_NewScript(cx, prologLength + mainLength, nsrcnotes, ntrynotes);
1276 9914 if (!script)
1277 0 return NULL;
1278
1279 /* Now that we have script, error control flow must go to label bad. */
1280 9914 script->main += prologLength;
1281 9914 memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
1282 9914 memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
1283 9914 script->numGlobalVars = cg->treeContext.numGlobalVars;
1284 9914 if (!js_InitAtomMap(cx, &script->atomMap, &cg->atomList))
1285 0 goto bad;
1286
1287 9914 filename = cg->filename;
1288 9914 if (filename) {
1289 9914 script->filename = js_SaveScriptFilename(cx, filename);
1290 9914 if (!script->filename)
1291 0 goto bad;
1292 }
1293 9914 script->lineno = cg->firstLine;
1294 9914 script->depth = cg->maxStackDepth;
1295 9914 if (cg->principals) {
1296 0 script->principals = cg->principals;
1297 0 JSPRINCIPALS_HOLD(cx, script->principals);
1298 }
1299
1300 9914 if (!js_FinishTakingSrcNotes(cx, cg, SCRIPT_NOTES(script)))
1301 0 goto bad;
1302 9914 if (script->trynotes)
1303 66 js_FinishTakingTryNotes(cx, cg, script->trynotes);
1304
1305 /* Tell the debugger about this compiled script. */
1306 9914 js_CallNewScriptHook(cx, script, fun);
1307 9914 return script;
1308
1309 0 bad:
1310 0 js_DestroyScript(cx, script);
1311 0 return NULL;
1312 }
1313
1314 JS_FRIEND_API(void)
1315 js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1316 9914 {
1317 JSRuntime *rt;
1318 JSNewScriptHook hook;
1319
1320 9914 rt = cx->runtime;
1321 9914 hook = rt->newScriptHook;
1322 9914 if (hook) {
1323 0 JS_KEEP_ATOMS(rt);
1324 0 hook(cx, script->filename, script->lineno, script, fun,
1325 rt->newScriptHookData);
1326 0 JS_UNKEEP_ATOMS(rt);
1327 }
1328 9914 }
1329
1330 JS_FRIEND_API(void)
1331 js_CallDestroyScriptHook(JSContext *cx, JSScript *script)
1332 9948 {
1333 JSRuntime *rt;
1334 JSDestroyScriptHook hook;
1335
1336 9948 rt = cx->runtime;
1337 9948 hook = rt->destroyScriptHook;
1338 9948 if (hook)
1339 0 hook(cx, script, rt->destroyScriptHookData);
1340 9948 }
1341
1342 void
1343 js_DestroyScript(JSContext *cx, JSScript *script)
1344 9948 {
1345 9948 js_CallDestroyScriptHook(cx, script);
1346
1347 9948 JS_ClearScriptTraps(cx, script);
1348 9948 js_FreeAtomMap(cx, &script->atomMap);
1349 9948 if (script->principals)
1350 0 JSPRINCIPALS_DROP(cx, script->principals);
1351 9948 JS_free(cx, script);
1352 9948 }
1353
1354 void
1355 js_MarkScript(JSContext *cx, JSScript *script, void *arg)
1356 3853 {
1357 JSAtomMap *map;
1358 uintN i, length;
1359 JSAtom **vector;
1360
1361 3853 map = &script->atomMap;
1362 3853 length = map->length;
1363 3853 vector = map->vector;
1364 49329 for (i = 0; i < length; i++)
1365 45476 GC_MARK_ATOM(cx, vector[i], arg);
1366
1367 3853 if (script->filename)
1368 3819 js_MarkScriptFilename(script->filename);
1369 3853 }
1370
1371 jssrcnote *
1372 js_GetSrcNote(JSScript *script, jsbytecode *pc)
1373 0 {
1374 jssrcnote *sn;
1375 ptrdiff_t offset, target;
1376
1377 0 target = PTRDIFF(pc, script->code, jsbytecode);
1378 0 if ((uint32)target >= script->length)
1379 0 return NULL;
1380 0 offset = 0;
1381 0 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1382 0 offset += SN_DELTA(sn);
1383 0 if (offset == target && SN_IS_GETTABLE(sn))
1384 0 return sn;
1385 }
1386 0 return NULL;
1387 }
1388
1389 uintN
1390 js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc)
1391 0 {
1392 JSAtom *atom;
1393 JSFunction *fun;
1394 uintN lineno;
1395 ptrdiff_t offset, target;
1396 jssrcnote *sn;
1397 JSSrcNoteType type;
1398
1399 /*
1400 * Special case: function definition needs no line number note because
1401 * the function's script contains its starting line number.
1402 */
1403 0 if (*pc == JSOP_DEFFUN) {
1404 0 atom = GET_ATOM(cx, script, pc);
1405 0 fun = (JSFunction *) JS_GetPrivate(cx, ATOM_TO_OBJECT(atom));
1406 JS_ASSERT(fun->interpreted);
1407 0 return fun->u.script->lineno;
1408 }
1409
1410 /*
1411 * General case: walk through source notes accumulating their deltas,
1412 * keeping track of line-number notes, until we pass the note for pc's
1413 * offset within script->code.
1414 */
1415 0 lineno = script->lineno;
1416 0 offset = 0;
1417 0 target = PTRDIFF(pc, script->code, jsbytecode);
1418 0 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1419 0 offset += SN_DELTA(sn);
1420 0 type = (JSSrcNoteType) SN_TYPE(sn);
1421 0 if (type == SRC_SETLINE) {
1422 0 if (offset <= target)
1423 0 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1424 0 } else if (type == SRC_NEWLINE) {
1425 0 if (offset <= target)
1426 0 lineno++;
1427 }
1428 0 if (offset > target)
1429 0 break;
1430 }
1431 0 return lineno;
1432 }
1433
1434 /* The line number limit is the same as the jssrcnote offset limit. */
1435 #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1436
1437 jsbytecode *
1438 js_LineNumberToPC(JSScript *script, uintN target)
1439 0 {
1440 ptrdiff_t offset, best;
1441 uintN lineno, bestdiff, diff;
1442 jssrcnote *sn;
1443 JSSrcNoteType type;
1444
1445 0 offset = 0;
1446 0 best = -1;
1447 0 lineno = script->lineno;
1448 0 bestdiff = SN_LINE_LIMIT;
1449 0 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1450 0 if (lineno == target)
1451 0 goto out;
1452 0 if (lineno > target) {
1453 0 diff = lineno - target;
1454 0 if (diff < bestdiff) {
1455 0 bestdiff = diff;
1456 0 best = offset;
1457 }
1458 }
1459 0 offset += SN_DELTA(sn);
1460 0 type = (JSSrcNoteType) SN_TYPE(sn);
1461 0 if (type == SRC_SETLINE) {
1462 0 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1463 0 } else if (type == SRC_NEWLINE) {
1464 0 lineno++;
1465 }
1466 }
1467 0 if (best >= 0)
1468 0 offset = best;
1469 0 out:
1470 0 return script->code + offset;
1471 }
1472
1473 JS_FRIEND_API(uintN)
1474 js_GetScriptLineExtent(JSScript *script)
1475 0 {
1476 uintN lineno;
1477 jssrcnote *sn;
1478 JSSrcNoteType type;
1479
1480 0 lineno = script->lineno;
1481 0 for (sn = SCRIPT_NOTES(script); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1482 0 type = (JSSrcNoteType) SN_TYPE(sn);
1483 0 if (type == SRC_SETLINE) {
1484 0 lineno = (uintN) js_GetSrcNoteOffset(sn, 0);
1485 0 } else if (type == SRC_NEWLINE) {
1486 0 lineno++;
1487 }
1488 }
1489 0 return 1 + lineno - script->lineno;