1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
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 : * Nick Fitzgerald <nfitzgerald@mozilla.com>
27 : *
28 : * Alternatively, the contents of this file may be used under the terms of
29 : * either of the GNU General Public License Version 2 or later (the "GPL"),
30 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 : * in which case the provisions of the GPL or the LGPL are applicable instead
32 : * of those above. If you wish to allow use of your version of this file only
33 : * under the terms of either the GPL or the LGPL, and not to allow others to
34 : * use your version of this file under the terms of the MPL, indicate your
35 : * decision by deleting the provisions above and replace them with the notice
36 : * and other provisions required by the GPL or the LGPL. If you do not delete
37 : * the provisions above, a recipient may use your version of this file under
38 : * the terms of any one of the MPL, the GPL or the LGPL.
39 : *
40 : * ***** END LICENSE BLOCK ***** */
41 :
42 : /*
43 : * JS script operations.
44 : */
45 :
46 : #include <string.h>
47 : #include "jstypes.h"
48 : #include "jsutil.h"
49 : #include "jscrashreport.h"
50 : #include "jsprf.h"
51 : #include "jsapi.h"
52 : #include "jsatom.h"
53 : #include "jscntxt.h"
54 : #include "jsversion.h"
55 : #include "jsdbgapi.h"
56 : #include "jsfun.h"
57 : #include "jsgc.h"
58 : #include "jsgcmark.h"
59 : #include "jsinterp.h"
60 : #include "jslock.h"
61 : #include "jsnum.h"
62 : #include "jsopcode.h"
63 : #include "jsscope.h"
64 : #include "jsscript.h"
65 :
66 : #include "frontend/BytecodeEmitter.h"
67 : #include "frontend/Parser.h"
68 : #include "js/MemoryMetrics.h"
69 : #include "methodjit/MethodJIT.h"
70 : #include "methodjit/Retcon.h"
71 : #include "vm/Debugger.h"
72 : #include "vm/Xdr.h"
73 :
74 : #include "jsinferinlines.h"
75 : #include "jsinterpinlines.h"
76 : #include "jsobjinlines.h"
77 : #include "jsscriptinlines.h"
78 :
79 : using namespace js;
80 : using namespace js::gc;
81 : using namespace js::frontend;
82 :
83 : namespace js {
84 :
85 : BindingKind
86 881766 : Bindings::lookup(JSContext *cx, JSAtom *name, unsigned *indexp) const
87 : {
88 881766 : if (!lastBinding)
89 13328 : return NONE;
90 :
91 : Shape **spp;
92 868438 : Shape *shape = Shape::search(cx, lastBinding, ATOM_TO_JSID(name), &spp);
93 868438 : if (!shape)
94 827428 : return NONE;
95 :
96 41010 : if (indexp)
97 41001 : *indexp = shape->shortid();
98 :
99 41010 : if (shape->getter() == CallObject::getArgOp)
100 2488 : return ARGUMENT;
101 :
102 38522 : return shape->writable() ? VARIABLE : CONSTANT;
103 : }
104 :
105 : bool
106 355333 : Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind)
107 : {
108 355333 : if (!ensureShape(cx))
109 0 : return false;
110 :
111 : /*
112 : * We still follow 10.2.3 of ES3 and make argument and variable properties
113 : * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
114 : * avoid objects as activations, something we should do too.
115 : */
116 355333 : unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
117 :
118 : uint16_t *indexp;
119 : PropertyOp getter;
120 : StrictPropertyOp setter;
121 355333 : uint32_t slot = CallObject::RESERVED_SLOTS;
122 :
123 355333 : if (kind == ARGUMENT) {
124 229339 : JS_ASSERT(nvars == 0);
125 229339 : indexp = &nargs;
126 229339 : getter = CallObject::getArgOp;
127 229339 : setter = CallObject::setArgOp;
128 229339 : slot += nargs;
129 : } else {
130 125994 : JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
131 :
132 125994 : indexp = &nvars;
133 125994 : getter = CallObject::getVarOp;
134 125994 : setter = CallObject::setVarOp;
135 125994 : if (kind == CONSTANT)
136 63 : attrs |= JSPROP_READONLY;
137 125994 : slot += nargs + nvars;
138 : }
139 :
140 355333 : if (*indexp == BINDING_COUNT_LIMIT) {
141 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
142 : (kind == ARGUMENT)
143 : ? JSMSG_TOO_MANY_FUN_ARGS
144 0 : : JSMSG_TOO_MANY_LOCALS);
145 0 : return false;
146 : }
147 :
148 : jsid id;
149 355333 : if (!name) {
150 81 : JS_ASSERT(kind == ARGUMENT); /* destructuring */
151 81 : id = INT_TO_JSID(nargs);
152 : } else {
153 355252 : id = ATOM_TO_JSID(name);
154 : }
155 :
156 355333 : StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
157 355333 : base.updateGetterSetter(attrs, getter, setter);
158 :
159 355333 : UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
160 355333 : if (!nbase)
161 0 : return NULL;
162 :
163 355333 : StackShape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
164 :
165 : /* Shapes in bindings cannot be dictionaries. */
166 355333 : Shape *shape = lastBinding->getChildBinding(cx, child);
167 355333 : if (!shape)
168 0 : return false;
169 :
170 355333 : lastBinding = shape;
171 355333 : ++*indexp;
172 355333 : return true;
173 : }
174 :
175 : Shape *
176 745077 : Bindings::callObjectShape(JSContext *cx) const
177 : {
178 745077 : if (!hasDup())
179 745068 : return lastShape();
180 :
181 : /*
182 : * Build a vector of non-duplicate properties in order from last added
183 : * to first (i.e., the order we normally have iterate over Shapes). Choose
184 : * the last added property in each set of dups.
185 : */
186 18 : Vector<const Shape *> shapes(cx);
187 18 : HashSet<jsid> seen(cx);
188 9 : if (!seen.init())
189 0 : return NULL;
190 :
191 27 : for (Shape::Range r = lastShape()->all(); !r.empty(); r.popFront()) {
192 18 : const Shape &s = r.front();
193 36 : HashSet<jsid>::AddPtr p = seen.lookupForAdd(s.propid());
194 18 : if (!p) {
195 9 : if (!seen.add(p, s.propid()))
196 0 : return NULL;
197 9 : if (!shapes.append(&s))
198 0 : return NULL;
199 : }
200 : }
201 :
202 : /*
203 : * Now build the Shape without duplicate properties.
204 : */
205 18 : RootedVarShape shape(cx);
206 9 : shape = initialShape(cx);
207 18 : for (int i = shapes.length() - 1; i >= 0; --i) {
208 9 : shape = shape->getChildBinding(cx, shapes[i]);
209 9 : if (!shape)
210 0 : return NULL;
211 : }
212 :
213 9 : return shape;
214 : }
215 :
216 : bool
217 15424 : Bindings::getLocalNameArray(JSContext *cx, Vector<JSAtom *> *namesp)
218 : {
219 15424 : JS_ASSERT(lastBinding);
220 15424 : JS_ASSERT(hasLocalNames());
221 :
222 15424 : Vector<JSAtom *> &names = *namesp;
223 15424 : JS_ASSERT(names.empty());
224 :
225 15424 : unsigned n = countLocalNames();
226 15424 : if (!names.growByUninitialized(n))
227 0 : return false;
228 :
229 : #ifdef DEBUG
230 15424 : JSAtom * const POISON = reinterpret_cast<JSAtom *>(0xdeadbeef);
231 34956 : for (unsigned i = 0; i < n; i++)
232 19532 : names[i] = POISON;
233 : #endif
234 :
235 34956 : for (Shape::Range r = lastBinding->all(); !r.empty(); r.popFront()) {
236 19532 : const Shape &shape = r.front();
237 19532 : unsigned index = uint16_t(shape.shortid());
238 :
239 19532 : if (shape.getter() == CallObject::getArgOp) {
240 15569 : JS_ASSERT(index < nargs);
241 : } else {
242 3963 : JS_ASSERT(index < nvars);
243 3963 : index += nargs;
244 : }
245 :
246 19532 : if (JSID_IS_ATOM(shape.propid())) {
247 19487 : names[index] = JSID_TO_ATOM(shape.propid());
248 : } else {
249 45 : JS_ASSERT(JSID_IS_INT(shape.propid()));
250 45 : JS_ASSERT(shape.getter() == CallObject::getArgOp);
251 45 : names[index] = NULL;
252 : }
253 : }
254 :
255 : #ifdef DEBUG
256 34956 : for (unsigned i = 0; i < n; i++)
257 19532 : JS_ASSERT(names[i] != POISON);
258 : #endif
259 :
260 15424 : return true;
261 : }
262 :
263 : const Shape *
264 0 : Bindings::lastArgument() const
265 : {
266 0 : JS_ASSERT(lastBinding);
267 :
268 0 : const js::Shape *shape = lastVariable();
269 0 : if (nvars > 0) {
270 0 : while (shape->previous() && shape->getter() != CallObject::getArgOp)
271 0 : shape = shape->previous();
272 : }
273 0 : return shape;
274 : }
275 :
276 : const Shape *
277 414 : Bindings::lastVariable() const
278 : {
279 414 : JS_ASSERT(lastBinding);
280 414 : return lastBinding;
281 : }
282 :
283 : void
284 261506 : Bindings::makeImmutable()
285 : {
286 261506 : JS_ASSERT(lastBinding);
287 261506 : JS_ASSERT(!lastBinding->inDictionary());
288 261506 : }
289 :
290 : void
291 152545 : Bindings::trace(JSTracer *trc)
292 : {
293 152545 : if (lastBinding)
294 105151 : MarkShape(trc, &lastBinding, "shape");
295 152545 : }
296 :
297 : } /* namespace js */
298 :
299 : template<XDRMode mode>
300 : static bool
301 0 : XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
302 : {
303 : /*
304 : * A script constant can be an arbitrary primitive value as they are used
305 : * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see
306 : * bug 407186.
307 : */
308 : enum ConstTag {
309 : SCRIPT_INT = 0,
310 : SCRIPT_DOUBLE = 1,
311 : SCRIPT_STRING = 2,
312 : SCRIPT_TRUE = 3,
313 : SCRIPT_FALSE = 4,
314 : SCRIPT_NULL = 5,
315 : SCRIPT_VOID = 6
316 : };
317 :
318 : uint32_t tag;
319 : if (mode == XDR_ENCODE) {
320 0 : if (vp->isInt32()) {
321 0 : tag = SCRIPT_INT;
322 0 : } else if (vp->isDouble()) {
323 0 : tag = SCRIPT_DOUBLE;
324 0 : } else if (vp->isString()) {
325 0 : tag = SCRIPT_STRING;
326 0 : } else if (vp->isTrue()) {
327 0 : tag = SCRIPT_TRUE;
328 0 : } else if (vp->isFalse()) {
329 0 : tag = SCRIPT_FALSE;
330 0 : } else if (vp->isNull()) {
331 0 : tag = SCRIPT_NULL;
332 : } else {
333 0 : JS_ASSERT(vp->isUndefined());
334 0 : tag = SCRIPT_VOID;
335 : }
336 : }
337 :
338 0 : if (!xdr->codeUint32(&tag))
339 0 : return false;
340 :
341 0 : switch (tag) {
342 : case SCRIPT_INT: {
343 : uint32_t i;
344 : if (mode == XDR_ENCODE)
345 0 : i = uint32_t(vp->toInt32());
346 0 : if (!xdr->codeUint32(&i))
347 0 : return JS_FALSE;
348 : if (mode == XDR_DECODE)
349 0 : vp->init(Int32Value(int32_t(i)));
350 0 : break;
351 : }
352 : case SCRIPT_DOUBLE: {
353 : double d;
354 : if (mode == XDR_ENCODE)
355 0 : d = vp->toDouble();
356 0 : if (!xdr->codeDouble(&d))
357 0 : return false;
358 : if (mode == XDR_DECODE)
359 0 : vp->init(DoubleValue(d));
360 0 : break;
361 : }
362 : case SCRIPT_STRING: {
363 : JSString *str;
364 : if (mode == XDR_ENCODE)
365 0 : str = vp->toString();
366 0 : if (!xdr->codeString(&str))
367 0 : return false;
368 : if (mode == XDR_DECODE)
369 0 : vp->init(StringValue(str));
370 0 : break;
371 : }
372 : case SCRIPT_TRUE:
373 : if (mode == XDR_DECODE)
374 0 : vp->init(BooleanValue(true));
375 0 : break;
376 : case SCRIPT_FALSE:
377 : if (mode == XDR_DECODE)
378 0 : vp->init(BooleanValue(false));
379 0 : break;
380 : case SCRIPT_NULL:
381 : if (mode == XDR_DECODE)
382 0 : vp->init(NullValue());
383 0 : break;
384 : case SCRIPT_VOID:
385 : if (mode == XDR_DECODE)
386 0 : vp->init(UndefinedValue());
387 0 : break;
388 : }
389 0 : return true;
390 : }
391 :
392 : template<XDRMode mode>
393 : bool
394 4992 : js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
395 : {
396 : enum ScriptBits {
397 : NoScriptRval,
398 : SavedCallerFun,
399 : StrictModeCode,
400 : UsesEval,
401 : MayNeedArgsObj,
402 : NeedsArgsObj,
403 : OwnFilename,
404 : ParentFilename
405 : };
406 :
407 : uint32_t length, lineno, nslots;
408 : uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, nClosedArgs, nClosedVars, i;
409 : uint32_t prologLength, version;
410 4992 : uint32_t nTypeSets = 0;
411 4992 : uint32_t scriptBits = 0;
412 :
413 4992 : JSContext *cx = xdr->cx();
414 : JSScript *script;
415 4992 : nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = nClosedArgs = nClosedVars = 0;
416 4992 : jssrcnote *notes = NULL;
417 :
418 : /* XDR arguments, local vars, and upvars. */
419 : uint16_t nargs, nvars;
420 : #if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
421 4992 : script = NULL;
422 4992 : nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
423 : #endif
424 : uint32_t argsVars;
425 : if (mode == XDR_ENCODE) {
426 2496 : script = *scriptp;
427 2496 : JS_ASSERT_IF(parentScript, parentScript->compartment() == script->compartment());
428 :
429 : /* Should not XDR scripts optimized for a single global object. */
430 2496 : JS_ASSERT(!JSScript::isValidOffset(script->globalsOffset));
431 :
432 2496 : nargs = script->bindings.countArgs();
433 2496 : nvars = script->bindings.countVars();
434 2496 : argsVars = (nargs << 16) | nvars;
435 : }
436 4992 : if (!xdr->codeUint32(&argsVars))
437 0 : return false;
438 : if (mode == XDR_DECODE) {
439 2496 : nargs = argsVars >> 16;
440 2496 : nvars = argsVars & 0xFFFF;
441 : }
442 4992 : JS_ASSERT(nargs != Bindings::BINDING_COUNT_LIMIT);
443 4992 : JS_ASSERT(nvars != Bindings::BINDING_COUNT_LIMIT);
444 :
445 9984 : Bindings bindings(cx);
446 4992 : uint32_t nameCount = nargs + nvars;
447 4992 : if (nameCount > 0) {
448 9480 : LifoAllocScope las(&cx->tempLifoAlloc());
449 :
450 : /*
451 : * To xdr the names we prefix the names with a bitmap descriptor and
452 : * then xdr the names as strings. For argument names (indexes below
453 : * nargs) the corresponding bit in the bitmap is unset when the name
454 : * is null. Such null names are not encoded or decoded. For variable
455 : * names (indexes starting from nargs) bitmap's bit is set when the
456 : * name is declared as const, not as ordinary var.
457 : * */
458 4740 : unsigned bitmapLength = JS_HOWMANY(nameCount, JS_BITS_PER_UINT32);
459 4740 : uint32_t *bitmap = cx->tempLifoAlloc().newArray<uint32_t>(bitmapLength);
460 4740 : if (!bitmap) {
461 0 : js_ReportOutOfMemory(cx);
462 0 : return false;
463 : }
464 :
465 9480 : Vector<JSAtom *> names(cx);
466 : if (mode == XDR_ENCODE) {
467 2370 : if (!script->bindings.getLocalNameArray(cx, &names))
468 0 : return false;
469 2370 : PodZero(bitmap, bitmapLength);
470 5337 : for (unsigned i = 0; i < nameCount; i++) {
471 2967 : if (i < nargs && names[i])
472 2371 : bitmap[i >> JS_BITS_PER_UINT32_LOG2] |= JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
473 : }
474 : }
475 9480 : for (unsigned i = 0; i < bitmapLength; ++i) {
476 4740 : if (!xdr->codeUint32(&bitmap[i]))
477 0 : return false;
478 : }
479 :
480 10674 : for (unsigned i = 0; i < nameCount; i++) {
481 5934 : if (i < nargs &&
482 : !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))))
483 : {
484 : if (mode == XDR_DECODE) {
485 : uint16_t dummy;
486 0 : if (!bindings.addDestructuring(cx, &dummy))
487 0 : return false;
488 : } else {
489 0 : JS_ASSERT(!names[i]);
490 : }
491 0 : continue;
492 : }
493 :
494 : JSAtom *name;
495 : if (mode == XDR_ENCODE)
496 2967 : name = names[i];
497 5934 : if (!XDRAtom(xdr, &name))
498 0 : return false;
499 : if (mode == XDR_DECODE) {
500 : BindingKind kind = (i < nargs)
501 : ? ARGUMENT
502 : : (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
503 : JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
504 : ? CONSTANT
505 2967 : : VARIABLE);
506 2967 : if (!bindings.add(cx, name, kind))
507 0 : return false;
508 : }
509 : }
510 : }
511 :
512 : if (mode == XDR_DECODE) {
513 2496 : if (!bindings.ensureShape(cx))
514 0 : return false;
515 2496 : bindings.makeImmutable();
516 : }
517 :
518 : if (mode == XDR_ENCODE)
519 2496 : length = script->length;
520 4992 : if (!xdr->codeUint32(&length))
521 0 : return JS_FALSE;
522 :
523 : if (mode == XDR_ENCODE) {
524 2496 : prologLength = script->mainOffset;
525 2496 : JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
526 2496 : version = (uint32_t)script->getVersion() | (script->nfixed << 16);
527 2496 : lineno = script->lineno;
528 2496 : nslots = (uint32_t)script->nslots;
529 2496 : nslots = (uint32_t)((script->staticLevel << 16) | script->nslots);
530 2496 : natoms = script->natoms;
531 :
532 2496 : notes = script->notes();
533 2496 : nsrcnotes = script->numNotes();
534 :
535 2496 : if (JSScript::isValidOffset(script->constsOffset))
536 0 : nconsts = script->consts()->length;
537 2496 : if (JSScript::isValidOffset(script->objectsOffset))
538 1942 : nobjects = script->objects()->length;
539 2496 : if (JSScript::isValidOffset(script->regexpsOffset))
540 0 : nregexps = script->regexps()->length;
541 2496 : if (JSScript::isValidOffset(script->trynotesOffset))
542 396 : ntrynotes = script->trynotes()->length;
543 : /* no globals when encoding; see assertion above */
544 2496 : nClosedArgs = script->nClosedArgs();
545 2496 : nClosedVars = script->nClosedVars();
546 :
547 2496 : nTypeSets = script->nTypeSets;
548 :
549 2496 : if (script->noScriptRval)
550 0 : scriptBits |= (1 << NoScriptRval);
551 2496 : if (script->savedCallerFun)
552 0 : scriptBits |= (1 << SavedCallerFun);
553 2496 : if (script->strictModeCode)
554 45 : scriptBits |= (1 << StrictModeCode);
555 2496 : if (script->usesEval)
556 506 : scriptBits |= (1 << UsesEval);
557 2496 : if (script->mayNeedArgsObj()) {
558 505 : scriptBits |= (1 << MayNeedArgsObj);
559 : /*
560 : * In some cases, the front-end calls setNeedsArgsObj when the
561 : * script definitely needsArgsObj; preserve this information which
562 : * would otherwise be lost.
563 : */
564 505 : if (script->analyzedArgsUsage() && script->needsArgsObj())
565 505 : scriptBits |= (1 << NeedsArgsObj);
566 : }
567 2496 : if (script->filename) {
568 2496 : scriptBits |= (parentScript && parentScript->filename == script->filename)
569 : ? (1 << ParentFilename)
570 : : (1 << OwnFilename);
571 : }
572 :
573 2496 : JS_ASSERT(!script->compileAndGo);
574 2496 : JS_ASSERT(!script->hasSingletons);
575 : }
576 :
577 4992 : if (!xdr->codeUint32(&prologLength))
578 0 : return JS_FALSE;
579 4992 : if (!xdr->codeUint32(&version))
580 0 : return JS_FALSE;
581 :
582 : /*
583 : * To fuse allocations, we need srcnote, atom, objects, regexp, and trynote
584 : * counts early.
585 : */
586 4992 : if (!xdr->codeUint32(&natoms))
587 0 : return JS_FALSE;
588 4992 : if (!xdr->codeUint32(&nsrcnotes))
589 0 : return JS_FALSE;
590 4992 : if (!xdr->codeUint32(&ntrynotes))
591 0 : return JS_FALSE;
592 4992 : if (!xdr->codeUint32(&nobjects))
593 0 : return JS_FALSE;
594 4992 : if (!xdr->codeUint32(&nregexps))
595 0 : return JS_FALSE;
596 4992 : if (!xdr->codeUint32(&nconsts))
597 0 : return JS_FALSE;
598 4992 : if (!xdr->codeUint32(&nClosedArgs))
599 0 : return JS_FALSE;
600 4992 : if (!xdr->codeUint32(&nClosedVars))
601 0 : return JS_FALSE;
602 4992 : if (!xdr->codeUint32(&nTypeSets))
603 0 : return JS_FALSE;
604 4992 : if (!xdr->codeUint32(&scriptBits))
605 0 : return JS_FALSE;
606 :
607 : if (mode == XDR_DECODE) {
608 : /* Note: version is packed into the 32b space with another 16b value. */
609 2496 : JSVersion version_ = JSVersion(version & JS_BITMASK(16));
610 2496 : JS_ASSERT((version_ & VersionFlags::FULL_MASK) == unsigned(version_));
611 2496 : script = JSScript::NewScript(cx, length, nsrcnotes, natoms, nobjects,
612 : nregexps, ntrynotes, nconsts, 0, nClosedArgs,
613 : nClosedVars, nTypeSets, version_);
614 2496 : if (!script)
615 0 : return JS_FALSE;
616 :
617 2496 : script->bindings.transfer(cx, &bindings);
618 2496 : JS_ASSERT(!script->mainOffset);
619 2496 : script->mainOffset = prologLength;
620 2496 : script->nfixed = uint16_t(version >> 16);
621 :
622 : /* If we know nsrcnotes, we allocated space for notes in script. */
623 2496 : notes = script->notes();
624 2496 : *scriptp = script;
625 :
626 2496 : if (scriptBits & (1 << NoScriptRval))
627 0 : script->noScriptRval = true;
628 2496 : if (scriptBits & (1 << SavedCallerFun))
629 0 : script->savedCallerFun = true;
630 2496 : if (scriptBits & (1 << StrictModeCode))
631 45 : script->strictModeCode = true;
632 2496 : if (scriptBits & (1 << UsesEval))
633 506 : script->usesEval = true;
634 2496 : if (scriptBits & (1 << MayNeedArgsObj)) {
635 505 : script->setMayNeedArgsObj();
636 505 : if (scriptBits & (1 << NeedsArgsObj))
637 505 : script->setNeedsArgsObj(true);
638 : } else {
639 1991 : JS_ASSERT(!(scriptBits & (1 << NeedsArgsObj)));
640 : }
641 : }
642 :
643 : JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
644 : JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
645 4992 : if (!xdr->codeBytes(script->code, length) ||
646 : !xdr->codeBytes(notes, nsrcnotes) ||
647 : !xdr->codeUint32(&lineno) ||
648 : !xdr->codeUint32(&nslots)) {
649 0 : return false;
650 : }
651 :
652 4992 : if (scriptBits & (1 << OwnFilename)) {
653 : const char *filename;
654 : if (mode == XDR_ENCODE)
655 2496 : filename = script->filename;
656 4992 : if (!xdr->codeCString(&filename))
657 0 : return false;
658 : if (mode == XDR_DECODE) {
659 2496 : script->filename = SaveScriptFilename(cx, filename);
660 2496 : if (!script->filename)
661 0 : return false;
662 : }
663 0 : } else if (scriptBits & (1 << ParentFilename)) {
664 0 : JS_ASSERT(parentScript);
665 : if (mode == XDR_DECODE)
666 0 : script->filename = parentScript->filename;
667 : }
668 :
669 : if (mode == XDR_DECODE) {
670 2496 : script->lineno = lineno;
671 2496 : script->nslots = uint16_t(nslots);
672 2496 : script->staticLevel = uint16_t(nslots >> 16);
673 2496 : xdr->initScriptPrincipals(script);
674 : }
675 :
676 8654 : for (i = 0; i != natoms; ++i) {
677 3662 : if (!XDRAtom(xdr, &script->atoms[i]))
678 0 : return false;
679 : }
680 :
681 : /*
682 : * Here looping from 0-to-length to xdr objects is essential. It ensures
683 : * that block objects from the script->objects array will be written and
684 : * restored in the outer-to-inner order. js_XDRBlockObject relies on this
685 : * to restore the parent chain.
686 : */
687 9724 : for (i = 0; i != nobjects; ++i) {
688 4732 : HeapPtr<JSObject> *objp = &script->objects()->vector[i];
689 : uint32_t isBlock;
690 : if (mode == XDR_ENCODE) {
691 2366 : JSObject *obj = *objp;
692 2366 : JS_ASSERT(obj->isFunction() || obj->isStaticBlock());
693 2366 : isBlock = obj->isBlock() ? 1 : 0;
694 : }
695 4732 : if (!xdr->codeUint32(&isBlock))
696 0 : return false;
697 4732 : if (isBlock == 0) {
698 212 : JSObject *tmp = *objp;
699 212 : if (!XDRInterpretedFunction(xdr, &tmp, parentScript))
700 0 : return false;
701 212 : *objp = tmp;
702 : } else {
703 4520 : JS_ASSERT(isBlock == 1);
704 4520 : StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
705 4520 : if (!XDRStaticBlockObject(xdr, script, &tmp))
706 0 : return false;
707 4520 : *objp = tmp;
708 : }
709 : }
710 4992 : for (i = 0; i != nregexps; ++i) {
711 0 : if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i]))
712 0 : return false;
713 : }
714 5012 : for (i = 0; i != nClosedArgs; ++i) {
715 20 : if (!xdr->codeUint32(&script->closedArgs()->vector[i]))
716 0 : return false;
717 : }
718 4992 : for (i = 0; i != nClosedVars; ++i) {
719 0 : if (!xdr->codeUint32(&script->closedVars()->vector[i]))
720 0 : return false;
721 : }
722 :
723 4992 : if (ntrynotes != 0) {
724 : /*
725 : * We combine tn->kind and tn->stackDepth when serializing as XDR is not
726 : * efficient when serializing small integer types.
727 : */
728 : JSTryNote *tn, *tnfirst;
729 : uint32_t kindAndDepth;
730 : JS_STATIC_ASSERT(sizeof(tn->kind) == sizeof(uint8_t));
731 : JS_STATIC_ASSERT(sizeof(tn->stackDepth) == sizeof(uint16_t));
732 :
733 792 : tnfirst = script->trynotes()->vector;
734 792 : JS_ASSERT(script->trynotes()->length == ntrynotes);
735 792 : tn = tnfirst + ntrynotes;
736 900 : do {
737 900 : --tn;
738 : if (mode == XDR_ENCODE) {
739 450 : kindAndDepth = (uint32_t(tn->kind) << 16)
740 : | uint32_t(tn->stackDepth);
741 : }
742 900 : if (!xdr->codeUint32(&kindAndDepth) ||
743 : !xdr->codeUint32(&tn->start) ||
744 : !xdr->codeUint32(&tn->length)) {
745 0 : return false;
746 : }
747 : if (mode == XDR_DECODE) {
748 450 : tn->kind = uint8_t(kindAndDepth >> 16);
749 450 : tn->stackDepth = uint16_t(kindAndDepth);
750 : }
751 : } while (tn != tnfirst);
752 : }
753 :
754 4992 : if (nconsts) {
755 0 : HeapValue *vector = script->consts()->vector;
756 0 : for (i = 0; i != nconsts; ++i) {
757 0 : if (!XDRScriptConst(xdr, &vector[i]))
758 0 : return false;
759 : }
760 : }
761 :
762 : if (mode == XDR_DECODE) {
763 2496 : if (cx->hasRunOption(JSOPTION_PCCOUNT))
764 0 : (void) script->initScriptCounts(cx);
765 2496 : *scriptp = script;
766 : }
767 :
768 4992 : return true;
769 : }
770 :
771 : template bool
772 : js::XDRScript(XDRState<XDR_ENCODE> *xdr, JSScript **scriptp, JSScript *parentScript);
773 :
774 : template bool
775 : js::XDRScript(XDRState<XDR_DECODE> *xdr, JSScript **scriptp, JSScript *parentScript);
776 :
777 : bool
778 0 : JSScript::initScriptCounts(JSContext *cx)
779 : {
780 0 : JS_ASSERT(!scriptCounts);
781 :
782 0 : size_t n = 0;
783 :
784 : jsbytecode *pc, *next;
785 0 : for (pc = code; pc < code + length; pc = next) {
786 0 : n += PCCounts::numCounts(JSOp(*pc));
787 0 : next = pc + GetBytecodeLength(pc);
788 : }
789 :
790 0 : size_t bytes = (length * sizeof(PCCounts)) + (n * sizeof(double));
791 0 : char *cursor = (char *) cx->calloc_(bytes);
792 0 : if (!cursor)
793 0 : return false;
794 :
795 0 : DebugOnly<char *> base = cursor;
796 :
797 0 : scriptCounts.pcCountsVector = (PCCounts *) cursor;
798 0 : cursor += length * sizeof(PCCounts);
799 :
800 0 : for (pc = code; pc < code + length; pc = next) {
801 0 : scriptCounts.pcCountsVector[pc - code].counts = (double *) cursor;
802 0 : size_t capacity = PCCounts::numCounts(JSOp(*pc));
803 : #ifdef DEBUG
804 0 : scriptCounts.pcCountsVector[pc - code].capacity = capacity;
805 : #endif
806 0 : cursor += capacity * sizeof(double);
807 0 : next = pc + GetBytecodeLength(pc);
808 : }
809 :
810 0 : JS_ASSERT(size_t(cursor - base) == bytes);
811 :
812 : /* Enable interrupts in any interpreter frames running on this script. */
813 : InterpreterFrames *frames;
814 0 : for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
815 0 : frames->enableInterruptsIfRunning(this);
816 :
817 0 : return true;
818 : }
819 :
820 : void
821 285135 : JSScript::destroyScriptCounts(FreeOp *fop)
822 : {
823 285135 : if (scriptCounts) {
824 0 : fop->free_(scriptCounts.pcCountsVector);
825 0 : scriptCounts.pcCountsVector = NULL;
826 : }
827 285135 : }
828 :
829 : /*
830 : * Shared script filename management.
831 : */
832 :
833 : const char *
834 6038588 : js::SaveScriptFilename(JSContext *cx, const char *filename)
835 : {
836 6038588 : JSCompartment *comp = cx->compartment;
837 :
838 12077176 : ScriptFilenameTable::AddPtr p = comp->scriptFilenameTable.lookupForAdd(filename);
839 6038588 : if (!p) {
840 61168 : size_t size = offsetof(ScriptFilenameEntry, filename) + strlen(filename) + 1;
841 61168 : ScriptFilenameEntry *entry = (ScriptFilenameEntry *) cx->malloc_(size);
842 61168 : if (!entry)
843 0 : return NULL;
844 61168 : entry->marked = false;
845 61168 : strcpy(entry->filename, filename);
846 :
847 61168 : if (!comp->scriptFilenameTable.add(p, entry)) {
848 0 : Foreground::free_(entry);
849 0 : JS_ReportOutOfMemory(cx);
850 0 : return NULL;
851 : }
852 : }
853 :
854 6038588 : ScriptFilenameEntry *sfe = *p;
855 : #ifdef JSGC_INCREMENTAL
856 : /*
857 : * During the IGC we need to ensure that filename is marked whenever it is
858 : * accessed even if the name was already in the table. At this point old
859 : * scripts or exceptions pointing to the filename may no longer be
860 : * reachable.
861 : */
862 6038588 : if (comp->needsBarrier() && !sfe->marked)
863 56 : sfe->marked = true;
864 : #endif
865 :
866 6038588 : return sfe->filename;
867 : }
868 :
869 : /*
870 : * Back up from a saved filename by its offset within its hash table entry.
871 : */
872 : #define FILENAME_TO_SFE(fn) \
873 : ((ScriptFilenameEntry *) ((fn) - offsetof(ScriptFilenameEntry, filename)))
874 :
875 : void
876 91458 : js::MarkScriptFilename(const char *filename)
877 : {
878 91458 : ScriptFilenameEntry *sfe = FILENAME_TO_SFE(filename);
879 91458 : sfe->marked = true;
880 91458 : }
881 :
882 : void
883 83697 : js::SweepScriptFilenames(JSCompartment *comp)
884 : {
885 83697 : ScriptFilenameTable &table = comp->scriptFilenameTable;
886 205926 : for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront()) {
887 122229 : ScriptFilenameEntry *entry = e.front();
888 122229 : if (entry->marked) {
889 61103 : entry->marked = false;
890 61126 : } else if (!comp->rt->gcKeepAtoms) {
891 61112 : Foreground::free_(entry);
892 61112 : e.removeFront();
893 : }
894 : }
895 83697 : }
896 :
897 : void
898 41285 : js::FreeScriptFilenames(JSCompartment *comp)
899 : {
900 41285 : ScriptFilenameTable &table = comp->scriptFilenameTable;
901 41341 : for (ScriptFilenameTable::Enum e(table); !e.empty(); e.popFront())
902 56 : Foreground::free_(e.front());
903 :
904 41285 : table.clear();
905 41285 : }
906 :
907 : /*
908 : * JSScript::data has a complex, manually-controlled, memory layout.
909 : *
910 : * First are some optional array headers. They are optional because they
911 : * often aren't needed, i.e. the corresponding arrays often have zero elements.
912 : * Each header has an offset in JSScript that indicates its location within
913 : * |data|; that offset is INVALID_OFFSET if the array header is not present.
914 : * Each header also has an accessor function in JSScript.
915 : *
916 : * Array type Array elements Offset Accessor
917 : * ---------- -------------- ------ --------
918 : * JSConstArray Consts constsOffset consts()
919 : * JSObjectArray Objects objectsOffset objects()
920 : * JSObjectArray Regexps regexpsOffset regexps()
921 : * JSTryNoteArray Try notes tryNotesOffset trynotes()
922 : * GlobalSlotArray Globals globalsOffset globals()
923 : * ClosedSlotArray ClosedArgs closedArgsOffset closedArgs()
924 : * ClosedSlotArray ClosedVars closedVarsOffset closedVars()
925 : *
926 : * Then are the elements of several arrays.
927 : * - Most of these arrays have headers listed above (if present). For each of
928 : * these, the array pointer and the array length is stored in the header.
929 : * - The remaining arrays have pointers and lengths that are stored directly in
930 : * JSScript. This is because, unlike the others, they are nearly always
931 : * non-zero length and so the optional-header space optimization isn't
932 : * worthwhile.
933 : *
934 : * Array elements Pointed to by Length
935 : * -------------- ------------- ------
936 : * Consts consts()->vector consts()->length
937 : * Atoms atoms natoms
938 : * Objects objects()->vector objects()->length
939 : * Regexps regexps()->vector regexps()->length
940 : * Try notes trynotes()->vector trynotes()->length
941 : * Globals globals()->vector globals()->length
942 : * Closed args closedArgs()->vector closedArgs()->length
943 : * Closed vars closedVars()->vector closedVars()->length
944 : * Bytecodes code length
945 : * Source notes notes() numNotes() * sizeof(jssrcnote)
946 : *
947 : * IMPORTANT: This layout has two key properties.
948 : * - It ensures that everything has sufficient alignment; in particular, the
949 : * consts() elements need jsval alignment.
950 : * - It ensures there are no gaps between elements, which saves space and makes
951 : * manual layout easy. In particular, in the second part, arrays with larger
952 : * elements precede arrays with smaller elements.
953 : *
954 : * The following static assertions check these properties.
955 : */
956 :
957 : #define KEEPS_JSVAL_ALIGNMENT(T) \
958 : (JS_ALIGNMENT_OF(jsval) % JS_ALIGNMENT_OF(T) == 0 && \
959 : sizeof(T) % sizeof(jsval) == 0)
960 :
961 : #define HAS_JSVAL_ALIGNMENT(T) \
962 : (JS_ALIGNMENT_OF(jsval) == JS_ALIGNMENT_OF(T) && \
963 : sizeof(T) == sizeof(jsval))
964 :
965 : #define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \
966 : (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0)
967 :
968 : /*
969 : * These assertions ensure that there is no padding between the array headers,
970 : * and also that the consts() elements (which follow immediately afterward) are
971 : * jsval-aligned. (There is an assumption that |data| itself is jsval-aligned;
972 : * we check this below).
973 : */
974 : JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(JSConstArray));
975 : JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(JSObjectArray)); /* there are two of these */
976 : JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(JSTryNoteArray));
977 : JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(GlobalSlotArray));
978 : JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ClosedSlotArray)); /* there are two of these */
979 :
980 : /* These assertions ensure there is no padding required between array elements. */
981 : JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue));
982 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, JSAtom *));
983 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSAtom *, HeapPtrObject));
984 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, HeapPtrObject));
985 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote));
986 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, GlobalSlotArray::Entry));
987 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(GlobalSlotArray::Entry, uint32_t));
988 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t));
989 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, jsbytecode));
990 : JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(jsbytecode, jssrcnote));
991 :
992 : /*
993 : * Check that uint8_t offsets is enough to reach any optional array allocated
994 : * within |data|. For that we check that the maximum possible offset for the
995 : * closedVars array -- the last optional array -- still fits in 1 byte and does
996 : * not coincide with INVALID_OFFSET.
997 : */
998 : JS_STATIC_ASSERT(sizeof(JSConstArray) +
999 : sizeof(JSObjectArray) +
1000 : sizeof(JSObjectArray) +
1001 : sizeof(JSTryNoteArray) +
1002 : sizeof(js::GlobalSlotArray) +
1003 : sizeof(js::ClosedSlotArray)
1004 : < JSScript::INVALID_OFFSET);
1005 : JS_STATIC_ASSERT(JSScript::INVALID_OFFSET <= 255);
1006 :
1007 : JSScript *
1008 285135 : JSScript::NewScript(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
1009 : uint32_t nobjects, uint32_t nregexps,
1010 : uint32_t ntrynotes, uint32_t nconsts, uint32_t nglobals,
1011 : uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets, JSVersion version)
1012 : {
1013 285135 : size_t size = 0;
1014 :
1015 285135 : if (nconsts != 0)
1016 2774 : size += sizeof(JSConstArray) + nconsts * sizeof(Value);
1017 285135 : size += sizeof(JSAtom *) * natoms;
1018 285135 : if (nobjects != 0)
1019 85246 : size += sizeof(JSObjectArray) + nobjects * sizeof(JSObject *);
1020 285135 : if (nregexps != 0)
1021 1391 : size += sizeof(JSObjectArray) + nregexps * sizeof(JSObject *);
1022 285135 : if (ntrynotes != 0)
1023 7768 : size += sizeof(JSTryNoteArray) + ntrynotes * sizeof(JSTryNote);
1024 285135 : if (nglobals != 0)
1025 12668 : size += sizeof(GlobalSlotArray) + nglobals * sizeof(GlobalSlotArray::Entry);
1026 285135 : if (nClosedArgs != 0)
1027 1307 : size += sizeof(ClosedSlotArray) + nClosedArgs * sizeof(uint32_t);
1028 285135 : if (nClosedVars != 0)
1029 2106 : size += sizeof(ClosedSlotArray) + nClosedVars * sizeof(uint32_t);
1030 :
1031 285135 : size += length * sizeof(jsbytecode);
1032 285135 : size += nsrcnotes * sizeof(jssrcnote);
1033 :
1034 : /*
1035 : * We assume that calloc aligns on sizeof(Value) if the size we ask to
1036 : * allocate divides sizeof(Value).
1037 : */
1038 : JS_STATIC_ASSERT(sizeof(Value) == sizeof(double));
1039 285135 : uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
1040 285135 : if (!data)
1041 0 : return NULL;
1042 :
1043 285135 : JSScript *script = js_NewGCScript(cx);
1044 285135 : if (!script) {
1045 0 : Foreground::free_(data);
1046 0 : return NULL;
1047 : }
1048 :
1049 285135 : PodZero(script);
1050 285135 : script->data = data;
1051 285135 : script->length = length;
1052 285135 : script->version = version;
1053 285135 : new (&script->bindings) Bindings(cx);
1054 :
1055 285135 : uint8_t *cursor = data;
1056 285135 : if (nconsts != 0) {
1057 2774 : script->constsOffset = uint8_t(cursor - data);
1058 2774 : cursor += sizeof(JSConstArray);
1059 : } else {
1060 282361 : script->constsOffset = JSScript::INVALID_OFFSET;
1061 : }
1062 285135 : if (nobjects != 0) {
1063 85246 : script->objectsOffset = uint8_t(cursor - data);
1064 85246 : cursor += sizeof(JSObjectArray);
1065 : } else {
1066 199889 : script->objectsOffset = JSScript::INVALID_OFFSET;
1067 : }
1068 285135 : if (nregexps != 0) {
1069 1391 : script->regexpsOffset = uint8_t(cursor - data);
1070 1391 : cursor += sizeof(JSObjectArray);
1071 : } else {
1072 283744 : script->regexpsOffset = JSScript::INVALID_OFFSET;
1073 : }
1074 285135 : if (ntrynotes != 0) {
1075 7768 : script->trynotesOffset = uint8_t(cursor - data);
1076 7768 : cursor += sizeof(JSTryNoteArray);
1077 : } else {
1078 277367 : script->trynotesOffset = JSScript::INVALID_OFFSET;
1079 : }
1080 285135 : if (nglobals != 0) {
1081 12668 : script->globalsOffset = uint8_t(cursor - data);
1082 12668 : cursor += sizeof(GlobalSlotArray);
1083 : } else {
1084 272467 : script->globalsOffset = JSScript::INVALID_OFFSET;
1085 : }
1086 285135 : if (nClosedArgs != 0) {
1087 1307 : script->closedArgsOffset = uint8_t(cursor - data);
1088 1307 : cursor += sizeof(ClosedSlotArray);
1089 : } else {
1090 283828 : script->closedArgsOffset = JSScript::INVALID_OFFSET;
1091 : }
1092 285135 : JS_ASSERT(cursor - data < 0xFF);
1093 285135 : if (nClosedVars != 0) {
1094 2106 : script->closedVarsOffset = uint8_t(cursor - data);
1095 2106 : cursor += sizeof(ClosedSlotArray);
1096 : } else {
1097 283029 : script->closedVarsOffset = JSScript::INVALID_OFFSET;
1098 : }
1099 :
1100 285135 : if (nconsts != 0) {
1101 2774 : JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
1102 2774 : script->consts()->length = nconsts;
1103 2774 : script->consts()->vector = (HeapValue *)cursor;
1104 2774 : cursor += nconsts * sizeof(script->consts()->vector[0]);
1105 : }
1106 :
1107 285135 : if (natoms != 0) {
1108 167038 : script->natoms = natoms;
1109 167038 : script->atoms = reinterpret_cast<JSAtom **>(cursor);
1110 167038 : cursor += natoms * sizeof(script->atoms[0]);
1111 : }
1112 :
1113 285135 : if (nobjects != 0) {
1114 85246 : script->objects()->length = nobjects;
1115 85246 : script->objects()->vector = (HeapPtr<JSObject> *)cursor;
1116 85246 : cursor += nobjects * sizeof(script->objects()->vector[0]);
1117 : }
1118 :
1119 285135 : if (nregexps != 0) {
1120 1391 : script->regexps()->length = nregexps;
1121 1391 : script->regexps()->vector = (HeapPtr<JSObject> *)cursor;
1122 1391 : cursor += nregexps * sizeof(script->regexps()->vector[0]);
1123 : }
1124 :
1125 285135 : if (ntrynotes != 0) {
1126 7768 : script->trynotes()->length = ntrynotes;
1127 7768 : script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
1128 7768 : size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
1129 : #ifdef DEBUG
1130 7768 : memset(cursor, 0, vectorSize);
1131 : #endif
1132 7768 : cursor += vectorSize;
1133 : }
1134 :
1135 285135 : if (nglobals != 0) {
1136 12668 : script->globals()->length = nglobals;
1137 12668 : script->globals()->vector = reinterpret_cast<GlobalSlotArray::Entry *>(cursor);
1138 12668 : cursor += nglobals * sizeof(script->globals()->vector[0]);
1139 : }
1140 :
1141 285135 : if (nClosedArgs != 0) {
1142 1307 : script->closedArgs()->length = nClosedArgs;
1143 1307 : script->closedArgs()->vector = reinterpret_cast<uint32_t *>(cursor);
1144 1307 : cursor += nClosedArgs * sizeof(script->closedArgs()->vector[0]);
1145 : }
1146 :
1147 285135 : if (nClosedVars != 0) {
1148 2106 : script->closedVars()->length = nClosedVars;
1149 2106 : script->closedVars()->vector = reinterpret_cast<uint32_t *>(cursor);
1150 2106 : cursor += nClosedVars * sizeof(script->closedVars()->vector[0]);
1151 : }
1152 :
1153 285135 : JS_ASSERT(nTypeSets <= UINT16_MAX);
1154 285135 : script->nTypeSets = uint16_t(nTypeSets);
1155 :
1156 285135 : script->code = (jsbytecode *)cursor;
1157 285135 : JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
1158 :
1159 : #ifdef DEBUG
1160 285135 : script->id_ = 0;
1161 : #endif
1162 :
1163 285135 : JS_ASSERT(script->getVersion() == version);
1164 285135 : return script;
1165 : }
1166 :
1167 : JSScript *
1168 259010 : JSScript::NewScriptFromEmitter(JSContext *cx, BytecodeEmitter *bce)
1169 : {
1170 : uint32_t mainLength, prologLength, nfixed;
1171 : JSScript *script;
1172 : const char *filename;
1173 : JSFunction *fun;
1174 :
1175 : /* The counts of indexed things must be checked during code generation. */
1176 259010 : JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
1177 259010 : JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
1178 259010 : JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
1179 :
1180 259010 : mainLength = bce->offset();
1181 259010 : prologLength = bce->prologOffset();
1182 :
1183 259010 : if (!bce->bindings.ensureShape(cx))
1184 0 : return NULL;
1185 :
1186 259010 : uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
1187 259010 : uint16_t nClosedArgs = uint16_t(bce->closedArgs.length());
1188 259010 : JS_ASSERT(nClosedArgs == bce->closedArgs.length());
1189 259010 : uint16_t nClosedVars = uint16_t(bce->closedVars.length());
1190 259010 : JS_ASSERT(nClosedVars == bce->closedVars.length());
1191 : script = NewScript(cx, prologLength + mainLength, nsrcnotes,
1192 : bce->atomIndices->count(), bce->objectList.length,
1193 : bce->regexpList.length, bce->ntrynotes, bce->constList.length(),
1194 : bce->globalUses.length(), nClosedArgs, nClosedVars,
1195 259010 : bce->typesetCount, bce->version());
1196 259010 : if (!script)
1197 0 : return NULL;
1198 :
1199 259010 : bce->bindings.makeImmutable();
1200 :
1201 259010 : JS_ASSERT(script->mainOffset == 0);
1202 259010 : script->mainOffset = prologLength;
1203 259010 : PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
1204 259010 : PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
1205 259010 : nfixed = bce->inFunction() ? bce->bindings.countVars() : 0;
1206 259010 : JS_ASSERT(nfixed < SLOTNO_LIMIT);
1207 259010 : script->nfixed = uint16_t(nfixed);
1208 259010 : js_InitAtomMap(cx, bce->atomIndices.getMap(), script->atoms);
1209 :
1210 259010 : filename = bce->parser->tokenStream.getFilename();
1211 259010 : if (filename) {
1212 259006 : script->filename = SaveScriptFilename(cx, filename);
1213 259006 : if (!script->filename)
1214 0 : return NULL;
1215 : }
1216 259010 : script->lineno = bce->firstLine;
1217 259010 : if (script->nfixed + bce->maxStackDepth >= JS_BIT(16)) {
1218 : ReportCompileErrorNumber(cx, bce->tokenStream(), NULL, JSREPORT_ERROR, JSMSG_NEED_DIET,
1219 0 : "script");
1220 0 : return NULL;
1221 : }
1222 259010 : script->nslots = script->nfixed + bce->maxStackDepth;
1223 259010 : script->staticLevel = uint16_t(bce->staticLevel);
1224 259010 : script->principals = bce->parser->principals;
1225 :
1226 259010 : if (script->principals)
1227 100 : JS_HoldPrincipals(script->principals);
1228 :
1229 : /* Establish invariant: principals implies originPrincipals. */
1230 259010 : script->originPrincipals = bce->parser->originPrincipals;
1231 259010 : if (!script->originPrincipals)
1232 258915 : script->originPrincipals = script->principals;
1233 259010 : if (script->originPrincipals)
1234 106 : JS_HoldPrincipals(script->originPrincipals);
1235 :
1236 259010 : script->sourceMap = (jschar *) bce->parser->tokenStream.releaseSourceMap();
1237 :
1238 259010 : if (!FinishTakingSrcNotes(cx, bce, script->notes()))
1239 0 : return NULL;
1240 259010 : if (bce->ntrynotes != 0)
1241 7372 : FinishTakingTryNotes(bce, script->trynotes());
1242 259010 : if (bce->objectList.length != 0)
1243 83304 : bce->objectList.finish(script->objects());
1244 259010 : if (bce->regexpList.length != 0)
1245 1391 : bce->regexpList.finish(script->regexps());
1246 259010 : if (bce->constList.length() != 0)
1247 2774 : bce->constList.finish(script->consts());
1248 259010 : if (bce->flags & TCF_NO_SCRIPT_RVAL)
1249 38232 : script->noScriptRval = true;
1250 259010 : if (bce->flags & TCF_STRICT_MODE_CODE)
1251 1589 : script->strictModeCode = true;
1252 259010 : if (bce->flags & TCF_COMPILE_N_GO) {
1253 248300 : script->compileAndGo = true;
1254 248300 : const StackFrame *fp = bce->parser->callerFrame;
1255 248300 : if (fp && fp->isFunctionFrame())
1256 30448 : script->savedCallerFun = true;
1257 : }
1258 259010 : if (bce->callsEval())
1259 6241 : script->usesEval = true;
1260 259010 : if (bce->flags & TCF_HAS_SINGLETONS)
1261 28116 : script->hasSingletons = true;
1262 :
1263 : /*
1264 : * The arguments-usage analysis in analyzeSSA only looks at
1265 : * JSOP_ARGUMENTS use. Therefore, anything else that definitely requires an
1266 : * arguments object needs to be accounted for here.
1267 : */
1268 259010 : if (bce->inFunction()) {
1269 164453 : bool needsArgsObj = bce->mayOverwriteArguments() || bce->needsEagerArguments();
1270 164453 : if (needsArgsObj || bce->usesArguments()) {
1271 6624 : script->setMayNeedArgsObj();
1272 6624 : if (needsArgsObj)
1273 4158 : script->setNeedsArgsObj(true);
1274 : }
1275 : }
1276 :
1277 259010 : if (bce->globalUses.length()) {
1278 12668 : PodCopy<GlobalSlotArray::Entry>(script->globals()->vector, &bce->globalUses[0],
1279 25336 : bce->globalUses.length());
1280 : }
1281 :
1282 259010 : if (nClosedArgs)
1283 1297 : PodCopy<uint32_t>(script->closedArgs()->vector, &bce->closedArgs[0], nClosedArgs);
1284 259010 : if (nClosedVars)
1285 2106 : PodCopy<uint32_t>(script->closedVars()->vector, &bce->closedVars[0], nClosedVars);
1286 :
1287 259010 : script->bindings.transfer(cx, &bce->bindings);
1288 :
1289 259010 : fun = NULL;
1290 259010 : if (bce->inFunction()) {
1291 : /*
1292 : * We initialize fun->script() to be the script constructed above
1293 : * so that the debugger has a valid fun->script().
1294 : */
1295 164453 : fun = bce->fun();
1296 164453 : JS_ASSERT(fun->isInterpreted());
1297 164453 : JS_ASSERT(!fun->script());
1298 164453 : if (bce->flags & TCF_FUN_HEAVYWEIGHT)
1299 10243 : fun->flags |= JSFUN_HEAVYWEIGHT;
1300 :
1301 : /*
1302 : * Mark functions which will only be executed once as singletons.
1303 : */
1304 : bool singleton =
1305 164453 : cx->typeInferenceEnabled() &&
1306 : bce->parent &&
1307 85535 : bce->parent->compiling() &&
1308 249988 : bce->parent->asBytecodeEmitter()->checkSingletonContext();
1309 :
1310 164453 : if (!script->typeSetFunction(cx, fun, singleton))
1311 0 : return NULL;
1312 :
1313 164453 : fun->setScript(script);
1314 164453 : script->globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
1315 : } else {
1316 : /*
1317 : * Initialize script->object, if necessary, so that the debugger has a
1318 : * valid holder object.
1319 : */
1320 94557 : if (bce->flags & TCF_NEED_SCRIPT_GLOBAL)
1321 60566 : script->globalObject = GetCurrentGlobal(cx);
1322 : }
1323 :
1324 : /* Tell the debugger about this compiled script. */
1325 259010 : js_CallNewScriptHook(cx, script, fun);
1326 259010 : if (!bce->parent) {
1327 104860 : GlobalObject *compileAndGoGlobal = NULL;
1328 104860 : if (script->compileAndGo) {
1329 94521 : compileAndGoGlobal = script->globalObject;
1330 94521 : if (!compileAndGoGlobal)
1331 33991 : compileAndGoGlobal = &bce->scopeChain()->global();
1332 : }
1333 104860 : Debugger::onNewScript(cx, script, compileAndGoGlobal);
1334 : }
1335 :
1336 259010 : if (cx->hasRunOption(JSOPTION_PCCOUNT))
1337 0 : (void) script->initScriptCounts(cx);
1338 :
1339 259010 : return script;
1340 : }
1341 :
1342 : size_t
1343 285135 : JSScript::computedSizeOfData()
1344 : {
1345 285135 : uint8_t *dataEnd = code + length * sizeof(jsbytecode) + numNotes() * sizeof(jssrcnote);
1346 285135 : JS_ASSERT(dataEnd >= data);
1347 285135 : return dataEnd - data;
1348 : }
1349 :
1350 : size_t
1351 0 : JSScript::sizeOfData(JSMallocSizeOfFun mallocSizeOf)
1352 : {
1353 0 : return mallocSizeOf(data);
1354 : }
1355 :
1356 : /*
1357 : * Nb: srcnotes are variable-length. This function computes the number of
1358 : * srcnote *slots*, which may be greater than the number of srcnotes.
1359 : */
1360 : uint32_t
1361 287631 : JSScript::numNotes()
1362 : {
1363 : jssrcnote *sn;
1364 287631 : jssrcnote *notes_ = notes();
1365 3246688 : for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
1366 2959057 : continue;
1367 287631 : return sn - notes_ + 1; /* +1 for the terminator */
1368 : }
1369 :
1370 : JS_FRIEND_API(void)
1371 343732 : js_CallNewScriptHook(JSContext *cx, JSScript *script, JSFunction *fun)
1372 : {
1373 343732 : JS_ASSERT(!script->callDestroyHook);
1374 343732 : if (JSNewScriptHook hook = cx->runtime->debugHooks.newScriptHook) {
1375 0 : AutoKeepAtoms keep(cx->runtime);
1376 : hook(cx, script->filename, script->lineno, script, fun,
1377 0 : cx->runtime->debugHooks.newScriptHookData);
1378 : }
1379 343732 : script->callDestroyHook = true;
1380 343732 : }
1381 :
1382 : void
1383 377723 : js::CallDestroyScriptHook(FreeOp *fop, JSScript *script)
1384 : {
1385 377723 : if (!script->callDestroyHook)
1386 33991 : return;
1387 :
1388 343732 : if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook)
1389 0 : hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData);
1390 343732 : script->callDestroyHook = false;
1391 343732 : script->clearTraps(fop);
1392 : }
1393 :
1394 : void
1395 285135 : JSScript::finalize(FreeOp *fop)
1396 : {
1397 285135 : CallDestroyScriptHook(fop, this);
1398 :
1399 285135 : JS_ASSERT_IF(principals, originPrincipals);
1400 285135 : if (principals)
1401 117 : JS_DropPrincipals(fop->runtime(), principals);
1402 285135 : if (originPrincipals)
1403 128 : JS_DropPrincipals(fop->runtime(), originPrincipals);
1404 :
1405 285135 : if (types)
1406 153935 : types->destroy();
1407 :
1408 : #ifdef JS_METHODJIT
1409 285135 : mjit::ReleaseScriptCode(fop, this);
1410 : #endif
1411 :
1412 285135 : destroyScriptCounts(fop);
1413 :
1414 285135 : if (sourceMap)
1415 1 : fop->free_(sourceMap);
1416 :
1417 285135 : if (debug) {
1418 1 : jsbytecode *end = code + length;
1419 68 : for (jsbytecode *pc = code; pc < end; pc++) {
1420 67 : if (BreakpointSite *site = getBreakpointSite(pc)) {
1421 : /* Breakpoints are swept before finalization. */
1422 0 : JS_ASSERT(site->firstBreakpoint() == NULL);
1423 0 : site->clearTrap(fop, NULL, NULL);
1424 0 : JS_ASSERT(getBreakpointSite(pc) == NULL);
1425 : }
1426 : }
1427 1 : fop->free_(debug);
1428 : }
1429 :
1430 285135 : JS_POISON(data, 0xdb, computedSizeOfData());
1431 285135 : fop->free_(data);
1432 285135 : }
1433 :
1434 : namespace js {
1435 :
1436 : static const uint32_t GSN_CACHE_THRESHOLD = 100;
1437 : static const uint32_t GSN_CACHE_MAP_INIT_SIZE = 20;
1438 :
1439 : void
1440 39964 : GSNCache::purge()
1441 : {
1442 39964 : code = NULL;
1443 39964 : if (map.initialized())
1444 4038 : map.finish();
1445 39964 : }
1446 :
1447 : } /* namespace js */
1448 :
1449 : jssrcnote *
1450 38338044 : js_GetSrcNoteCached(JSContext *cx, JSScript *script, jsbytecode *pc)
1451 : {
1452 38338044 : size_t target = pc - script->code;
1453 38338044 : if (target >= size_t(script->length))
1454 0 : return NULL;
1455 :
1456 38338044 : GSNCache *cache = GetGSNCache(cx);
1457 38338044 : if (cache->code == script->code) {
1458 5756530 : JS_ASSERT(cache->map.initialized());
1459 5756530 : GSNCache::Map::Ptr p = cache->map.lookup(pc);
1460 5756530 : return p ? p->value : NULL;
1461 : }
1462 :
1463 32581514 : size_t offset = 0;
1464 : jssrcnote *result;
1465 220660687 : for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) {
1466 220660687 : if (SN_IS_TERMINATOR(sn)) {
1467 30694675 : result = NULL;
1468 30694675 : break;
1469 : }
1470 189966012 : offset += SN_DELTA(sn);
1471 189966012 : if (offset == target && SN_IS_GETTABLE(sn)) {
1472 1886839 : result = sn;
1473 1886839 : break;
1474 : }
1475 : }
1476 :
1477 32581514 : if (cache->code != script->code && script->length >= GSN_CACHE_THRESHOLD) {
1478 21570 : unsigned nsrcnotes = 0;
1479 2876115 : for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
1480 2854545 : sn = SN_NEXT(sn)) {
1481 2854545 : if (SN_IS_GETTABLE(sn))
1482 1095459 : ++nsrcnotes;
1483 : }
1484 21570 : if (cache->code) {
1485 17532 : JS_ASSERT(cache->map.initialized());
1486 17532 : cache->map.finish();
1487 17532 : cache->code = NULL;
1488 : }
1489 21570 : if (cache->map.init(nsrcnotes)) {
1490 21570 : pc = script->code;
1491 2876115 : for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
1492 2854545 : sn = SN_NEXT(sn)) {
1493 2854545 : pc += SN_DELTA(sn);
1494 2854545 : if (SN_IS_GETTABLE(sn))
1495 1095459 : JS_ALWAYS_TRUE(cache->map.put(pc, sn));
1496 : }
1497 21570 : cache->code = script->code;
1498 : }
1499 : }
1500 :
1501 32581514 : return result;
1502 : }
1503 :
1504 : unsigned
1505 6429462 : js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc)
1506 : {
1507 6429462 : unsigned lineno = startLine;
1508 :
1509 : /*
1510 : * Walk through source notes accumulating their deltas, keeping track of
1511 : * line-number notes, until we pass the note for pc's offset within
1512 : * script->code.
1513 : */
1514 6429462 : ptrdiff_t offset = 0;
1515 6429462 : ptrdiff_t target = pc - code;
1516 51459189 : for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1517 48630120 : offset += SN_DELTA(sn);
1518 48630120 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
1519 48630120 : if (type == SRC_SETLINE) {
1520 895302 : if (offset <= target)
1521 886440 : lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
1522 47734818 : } else if (type == SRC_NEWLINE) {
1523 18134115 : if (offset <= target)
1524 18103869 : lineno++;
1525 : }
1526 48630120 : if (offset > target)
1527 3600393 : break;
1528 : }
1529 :
1530 6429462 : return lineno;
1531 : }
1532 :
1533 : unsigned
1534 6429462 : js::PCToLineNumber(JSScript *script, jsbytecode *pc)
1535 : {
1536 : /* Cope with StackFrame.pc value prior to entering js_Interpret. */
1537 6429462 : if (!pc)
1538 0 : return 0;
1539 :
1540 6429462 : return PCToLineNumber(script->lineno, script->notes(), script->code, pc);
1541 : }
1542 :
1543 : /* The line number limit is the same as the jssrcnote offset limit. */
1544 : #define SN_LINE_LIMIT (SN_3BYTE_OFFSET_FLAG << 16)
1545 :
1546 : jsbytecode *
1547 12 : js_LineNumberToPC(JSScript *script, unsigned target)
1548 : {
1549 12 : ptrdiff_t offset = 0;
1550 12 : ptrdiff_t best = -1;
1551 12 : unsigned lineno = script->lineno;
1552 12 : unsigned bestdiff = SN_LINE_LIMIT;
1553 81 : for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1554 : /*
1555 : * Exact-match only if offset is not in the prolog; otherwise use
1556 : * nearest greater-or-equal line number match.
1557 : */
1558 72 : if (lineno == target && offset >= ptrdiff_t(script->mainOffset))
1559 3 : goto out;
1560 69 : if (lineno >= target) {
1561 2 : unsigned diff = lineno - target;
1562 2 : if (diff < bestdiff) {
1563 1 : bestdiff = diff;
1564 1 : best = offset;
1565 : }
1566 : }
1567 69 : offset += SN_DELTA(sn);
1568 69 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
1569 69 : if (type == SRC_SETLINE) {
1570 3 : lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
1571 66 : } else if (type == SRC_NEWLINE) {
1572 34 : lineno++;
1573 : }
1574 : }
1575 9 : if (best >= 0)
1576 0 : offset = best;
1577 : out:
1578 12 : return script->code + offset;
1579 : }
1580 :
1581 : JS_FRIEND_API(unsigned)
1582 2683 : js_GetScriptLineExtent(JSScript *script)
1583 : {
1584 2683 : unsigned lineno = script->lineno;
1585 2683 : unsigned maxLineNo = 0;
1586 2683 : bool counting = true;
1587 13333 : for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
1588 10650 : SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
1589 10650 : if (type == SRC_SETLINE) {
1590 3682 : if (maxLineNo < lineno)
1591 1261 : maxLineNo = lineno;
1592 3682 : lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
1593 3682 : counting = true;
1594 3682 : if (maxLineNo < lineno)
1595 3681 : maxLineNo = lineno;
1596 : else
1597 1 : counting = false;
1598 6968 : } else if (type == SRC_NEWLINE) {
1599 2295 : if (counting)
1600 2295 : lineno++;
1601 : }
1602 : }
1603 :
1604 2683 : if (maxLineNo > lineno)
1605 1 : lineno = maxLineNo;
1606 :
1607 2683 : return 1 + lineno - script->lineno;
1608 : }
1609 :
1610 : namespace js {
1611 :
1612 : unsigned
1613 105137 : CurrentLine(JSContext *cx)
1614 : {
1615 105137 : return PCToLineNumber(cx->fp()->script(), cx->regs().pc);
1616 : }
1617 :
1618 : void
1619 24642 : CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, unsigned *linenop,
1620 : JSPrincipals **origin)
1621 : {
1622 24642 : FrameRegsIter iter(cx);
1623 49284 : while (!iter.done() && !iter.fp()->isScriptFrame())
1624 0 : ++iter;
1625 :
1626 24642 : if (iter.done()) {
1627 4 : *file = NULL;
1628 4 : *linenop = 0;
1629 4 : *origin = NULL;
1630 4 : return;
1631 : }
1632 :
1633 24638 : JSScript *script = iter.fp()->script();
1634 24638 : *file = script->filename;
1635 24638 : *linenop = PCToLineNumber(iter.fp()->script(), iter.pc());
1636 24638 : *origin = script->originPrincipals;
1637 : }
1638 :
1639 : } /* namespace js */
1640 :
1641 : JSScript *
1642 2369 : js::CloneScript(JSContext *cx, JSScript *script)
1643 : {
1644 2369 : JS_ASSERT(cx->compartment != script->compartment());
1645 :
1646 : /* Serialize script. */
1647 4738 : XDREncoder encoder(cx);
1648 :
1649 2369 : if (!XDRScript(&encoder, &script, NULL))
1650 0 : return NULL;
1651 :
1652 : uint32_t nbytes;
1653 2369 : const void *p = encoder.getData(&nbytes);
1654 :
1655 : /* De-serialize script. */
1656 2369 : XDRDecoder decoder(cx, p, nbytes, cx->compartment->principals, script->originPrincipals);
1657 :
1658 : JSScript *newScript;
1659 2369 : if (!XDRScript(&decoder, &newScript, NULL))
1660 0 : return NULL;
1661 :
1662 2369 : return newScript;
1663 : }
1664 :
1665 : bool
1666 2577 : JSScript::ensureHasDebug(JSContext *cx)
1667 : {
1668 2577 : if (debug)
1669 1243 : return true;
1670 :
1671 1334 : size_t nbytes = offsetof(DebugScript, breakpoints) + length * sizeof(BreakpointSite*);
1672 1334 : debug = (DebugScript *) cx->calloc_(nbytes);
1673 1334 : if (!debug)
1674 0 : return false;
1675 :
1676 : /*
1677 : * Ensure that any Interpret() instances running on this script have
1678 : * interrupts enabled. The interrupts must stay enabled until the
1679 : * debug state is destroyed.
1680 : */
1681 : InterpreterFrames *frames;
1682 3875 : for (frames = cx->runtime->interpreterFrames; frames; frames = frames->older)
1683 2541 : frames->enableInterruptsIfRunning(this);
1684 :
1685 1334 : return true;
1686 : }
1687 :
1688 : void
1689 883 : JSScript::recompileForStepMode(FreeOp *fop)
1690 : {
1691 : #ifdef JS_METHODJIT
1692 883 : if (hasJITCode()) {
1693 198 : mjit::Recompiler::clearStackReferences(fop, this);
1694 198 : mjit::ReleaseScriptCode(fop, this);
1695 : }
1696 : #endif
1697 883 : }
1698 :
1699 : bool
1700 937 : JSScript::tryNewStepMode(JSContext *cx, uint32_t newValue)
1701 : {
1702 937 : JS_ASSERT(debug);
1703 :
1704 937 : uint32_t prior = debug->stepMode;
1705 937 : debug->stepMode = newValue;
1706 :
1707 937 : if (!prior != !newValue) {
1708 : /* Step mode has been enabled or disabled. Alert the methodjit. */
1709 883 : recompileForStepMode(cx->runtime->defaultFreeOp());
1710 :
1711 883 : if (!stepModeEnabled() && !debug->numSites) {
1712 432 : cx->free_(debug);
1713 432 : debug = NULL;
1714 : }
1715 : }
1716 :
1717 937 : return true;
1718 : }
1719 :
1720 : bool
1721 1 : JSScript::setStepModeFlag(JSContext *cx, bool step)
1722 : {
1723 1 : if (!ensureHasDebug(cx))
1724 0 : return false;
1725 :
1726 1 : return tryNewStepMode(cx, (debug->stepMode & stepCountMask) | (step ? stepFlagMask : 0));
1727 : }
1728 :
1729 : bool
1730 936 : JSScript::changeStepModeCount(JSContext *cx, int delta)
1731 : {
1732 936 : if (!ensureHasDebug(cx))
1733 0 : return false;
1734 :
1735 936 : assertSameCompartment(cx, this);
1736 936 : JS_ASSERT_IF(delta > 0, cx->compartment->debugMode());
1737 :
1738 936 : uint32_t count = debug->stepMode & stepCountMask;
1739 936 : JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
1740 : return tryNewStepMode(cx,
1741 : (debug->stepMode & stepFlagMask) |
1742 936 : ((count + delta) & stepCountMask));
1743 : }
1744 :
1745 : BreakpointSite *
1746 1640 : JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
1747 : GlobalObject *scriptGlobal)
1748 : {
1749 1640 : JS_ASSERT(size_t(pc - code) < length);
1750 :
1751 1640 : if (!ensureHasDebug(cx))
1752 0 : return NULL;
1753 :
1754 1640 : BreakpointSite *&site = debug->breakpoints[pc - code];
1755 :
1756 1640 : if (!site) {
1757 1172 : site = cx->runtime->new_<BreakpointSite>(this, pc);
1758 1172 : if (!site) {
1759 0 : js_ReportOutOfMemory(cx);
1760 0 : return NULL;
1761 : }
1762 1172 : debug->numSites++;
1763 : }
1764 :
1765 1640 : if (site->scriptGlobal)
1766 279 : JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal);
1767 : else
1768 1361 : site->scriptGlobal = scriptGlobal;
1769 :
1770 1640 : return site;
1771 : }
1772 :
1773 : void
1774 1172 : JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc)
1775 : {
1776 1172 : JS_ASSERT(unsigned(pc - code) < length);
1777 :
1778 1172 : BreakpointSite *&site = debug->breakpoints[pc - code];
1779 1172 : JS_ASSERT(site);
1780 :
1781 1172 : fop->delete_(site);
1782 1172 : site = NULL;
1783 :
1784 1172 : if (--debug->numSites == 0 && !stepModeEnabled()) {
1785 901 : fop->free_(debug);
1786 901 : debug = NULL;
1787 : }
1788 1172 : }
1789 :
1790 : void
1791 8819 : JSScript::clearBreakpointsIn(JSContext *cx, js::Debugger *dbg, JSObject *handler)
1792 : {
1793 8819 : if (!hasAnyBreakpointsOrStepMode())
1794 8351 : return;
1795 :
1796 468 : jsbytecode *end = code + length;
1797 8885736 : for (jsbytecode *pc = code; pc < end; pc++) {
1798 8885268 : BreakpointSite *site = getBreakpointSite(pc);
1799 8885268 : if (site) {
1800 : Breakpoint *nextbp;
1801 1683 : for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
1802 1035 : nextbp = bp->nextInSite();
1803 1035 : if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
1804 756 : bp->destroy(cx->runtime->defaultFreeOp());
1805 : }
1806 : }
1807 : }
1808 : }
1809 :
1810 : void
1811 344247 : JSScript::clearTraps(FreeOp *fop)
1812 : {
1813 344247 : if (!hasAnyBreakpointsOrStepMode())
1814 343731 : return;
1815 :
1816 516 : jsbytecode *end = code + length;
1817 17807 : for (jsbytecode *pc = code; pc < end; pc++) {
1818 17291 : BreakpointSite *site = getBreakpointSite(pc);
1819 17291 : if (site)
1820 632 : site->clearTrap(fop);
1821 : }
1822 : }
1823 :
1824 : void
1825 152539 : JSScript::markChildren(JSTracer *trc)
1826 : {
1827 152539 : JS_ASSERT_IF(trc->runtime->gcStrictCompartmentChecking, compartment()->isCollecting());
1828 :
1829 540979 : for (uint32_t i = 0; i < natoms; ++i) {
1830 388440 : if (atoms[i])
1831 388440 : MarkStringUnbarriered(trc, &atoms[i], "atom");
1832 : }
1833 :
1834 152539 : if (JSScript::isValidOffset(objectsOffset)) {
1835 25946 : JSObjectArray *objarray = objects();
1836 25946 : MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
1837 : }
1838 :
1839 152539 : if (JSScript::isValidOffset(regexpsOffset)) {
1840 653 : JSObjectArray *objarray = regexps();
1841 653 : MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
1842 : }
1843 :
1844 152539 : if (JSScript::isValidOffset(constsOffset)) {
1845 253 : JSConstArray *constarray = consts();
1846 253 : MarkValueRange(trc, constarray->length, constarray->vector, "consts");
1847 : }
1848 :
1849 152539 : if (function())
1850 106798 : MarkObject(trc, &function_, "function");
1851 :
1852 152539 : if (!isCachedEval && globalObject)
1853 101606 : MarkObject(trc, &globalObject, "object");
1854 :
1855 152539 : if (IS_GC_MARKING_TRACER(trc) && filename)
1856 87678 : MarkScriptFilename(filename);
1857 :
1858 152539 : bindings.trace(trc);
1859 :
1860 152539 : if (types)
1861 70791 : types->trace(trc);
1862 :
1863 152539 : if (hasAnyBreakpointsOrStepMode()) {
1864 23641 : for (unsigned i = 0; i < length; i++) {
1865 23007 : BreakpointSite *site = debug->breakpoints[i];
1866 23007 : if (site && site->trapHandler)
1867 629 : MarkValue(trc, &site->trapClosure, "trap closure");
1868 : }
1869 : }
1870 152539 : }
1871 :
1872 : void
1873 155053 : JSScript::setNeedsArgsObj(bool needsArgsObj)
1874 : {
1875 155053 : JS_ASSERT(!analyzedArgsUsage_);
1876 155053 : analyzedArgsUsage_ = true;
1877 155053 : needsArgsObj_ = needsArgsObj;
1878 155053 : }
1879 :
1880 : bool
1881 52 : JSScript::applySpeculationFailed(JSContext *cx)
1882 : {
1883 52 : JS_ASSERT(analyzedArgsUsage());
1884 52 : JS_ASSERT(!needsArgsObj());
1885 52 : needsArgsObj_ = true;
1886 :
1887 : /*
1888 : * By design, the apply-arguments optimization is only made when there
1889 : * are no outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) other
1890 : * than this particular invocation of 'f.apply(x, arguments)'. Thus, there
1891 : * are no outstanding values of MagicValue(JS_OPTIMIZED_ARGUMENTS) on the
1892 : * stack. However, there are three things that need fixup:
1893 : * - there may be any number of activations of this script that don't have
1894 : * an argsObj that now need one.
1895 : * - jit code compiled (and possible active on the stack) with the static
1896 : * assumption of !script->needsArgsObj();
1897 : * - type inference data for the script assuming script->needsArgsObj; and
1898 : */
1899 160 : for (AllFramesIter i(cx->stack.space()); !i.done(); ++i) {
1900 108 : StackFrame *fp = i.fp();
1901 108 : if (fp->isFunctionFrame() && fp->script() == this) {
1902 25 : if (!fp->hasArgsObj() && !ArgumentsObject::create(cx, fp)) {
1903 : /*
1904 : * We can't leave stack frames where fp->script->needsArgsObj
1905 : * and !fp->hasArgsObj. It is, however, safe to leave frames
1906 : * where fp->hasArgsObj and !fp->script->needsArgsObj.
1907 : */
1908 0 : needsArgsObj_ = false;
1909 0 : return false;
1910 : }
1911 : }
1912 : }
1913 :
1914 : #ifdef JS_METHODJIT
1915 52 : if (hasJITCode()) {
1916 47 : mjit::Recompiler::clearStackReferences(cx->runtime->defaultFreeOp(), this);
1917 47 : mjit::ReleaseScriptCode(cx->runtime->defaultFreeOp(), this);
1918 : }
1919 : #endif
1920 :
1921 52 : if (hasAnalysis() && analysis()->ranInference()) {
1922 52 : types::AutoEnterTypeInference enter(cx);
1923 328 : for (unsigned off = 0; off < length; off += GetBytecodeLength(code + off)) {
1924 302 : if (code[off] == JSOP_ARGUMENTS) {
1925 26 : types::TypeSet *set = analysis()->pushedTypes(off, 0);
1926 26 : JS_ASSERT(set->isLazyArguments(cx));
1927 26 : set->addType(cx, types::Type::UnknownType());
1928 : }
1929 : }
1930 : }
1931 :
1932 52 : return true;
1933 : }
|