js/src/jsscript.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:1d0db7430d6b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 * JS script operations.
9 */
10
11 #include "jsscriptinlines.h"
12
13 #include "mozilla/DebugOnly.h"
14 #include "mozilla/MathAlgorithms.h"
15 #include "mozilla/MemoryReporting.h"
16 #include "mozilla/PodOperations.h"
17
18 #include <string.h>
19
20 #include "jsapi.h"
21 #include "jsatom.h"
22 #include "jscntxt.h"
23 #include "jsfun.h"
24 #include "jsgc.h"
25 #include "jsobj.h"
26 #include "jsopcode.h"
27 #include "jsprf.h"
28 #include "jstypes.h"
29 #include "jsutil.h"
30 #include "jswrapper.h"
31
32 #include "frontend/BytecodeCompiler.h"
33 #include "frontend/BytecodeEmitter.h"
34 #include "frontend/SharedContext.h"
35 #include "gc/Marking.h"
36 #include "jit/BaselineJIT.h"
37 #include "jit/IonCode.h"
38 #include "js/MemoryMetrics.h"
39 #include "js/OldDebugAPI.h"
40 #include "js/Utility.h"
41 #include "vm/ArgumentsObject.h"
42 #include "vm/Compression.h"
43 #include "vm/Debugger.h"
44 #include "vm/Opcodes.h"
45 #include "vm/SelfHosting.h"
46 #include "vm/Shape.h"
47 #include "vm/Xdr.h"
48
49 #include "jsfuninlines.h"
50 #include "jsinferinlines.h"
51 #include "jsobjinlines.h"
52
53 #include "vm/ScopeObject-inl.h"
54 #include "vm/Stack-inl.h"
55
56 using namespace js;
57 using namespace js::gc;
58 using namespace js::frontend;
59
60 using mozilla::PodCopy;
61 using mozilla::PodZero;
62 using mozilla::RotateLeft;
63
64 typedef Rooted<GlobalObject *> RootedGlobalObject;
65
66 /* static */ uint32_t
67 Bindings::argumentsVarIndex(ExclusiveContext *cx, InternalBindingsHandle bindings)
68 {
69 HandlePropertyName arguments = cx->names().arguments;
70 BindingIter bi(bindings);
71 while (bi->name() != arguments)
72 bi++;
73 return bi.frameIndex();
74 }
75
76 bool
77 Bindings::initWithTemporaryStorage(ExclusiveContext *cx, InternalBindingsHandle self,
78 unsigned numArgs, uint32_t numVars,
79 Binding *bindingArray, uint32_t numBlockScoped)
80 {
81 JS_ASSERT(!self->callObjShape_);
82 JS_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
83 JS_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
84 JS_ASSERT(numArgs <= ARGC_LIMIT);
85 JS_ASSERT(numVars <= LOCALNO_LIMIT);
86 JS_ASSERT(numBlockScoped <= LOCALNO_LIMIT);
87 JS_ASSERT(numVars <= LOCALNO_LIMIT - numBlockScoped);
88 JS_ASSERT(UINT32_MAX - numArgs >= numVars + numBlockScoped);
89
90 self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
91 self->numArgs_ = numArgs;
92 self->numVars_ = numVars;
93 self->numBlockScoped_ = numBlockScoped;
94
95 // Get the initial shape to use when creating CallObjects for this script.
96 // After creation, a CallObject's shape may change completely (via direct eval() or
97 // other operations that mutate the lexical scope). However, since the
98 // lexical bindings added to the initial shape are permanent and the
99 // allocKind/nfixed of a CallObject cannot change, one may assume that the
100 // slot location (whether in the fixed or dynamic slots) of a variable is
101 // the same as in the initial shape. (This is assumed by the interpreter and
102 // JITs when interpreting/compiling aliasedvar ops.)
103
104 // Since unaliased variables are, by definition, only accessed by local
105 // operations and never through the scope chain, only give shapes to
106 // aliased variables. While the debugger may observe any scope object at
107 // any time, such accesses are mediated by DebugScopeProxy (see
108 // DebugScopeProxy::handleUnaliasedAccess).
109 uint32_t nslots = CallObject::RESERVED_SLOTS;
110 for (BindingIter bi(self); bi; bi++) {
111 if (bi->aliased())
112 nslots++;
113 }
114
115 // Put as many of nslots inline into the object header as possible.
116 uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots));
117
118 // Start with the empty shape and then append one shape per aliased binding.
119 RootedShape shape(cx,
120 EmptyShape::getInitialShape(cx, &CallObject::class_, nullptr, nullptr, nullptr,
121 nfixed, BaseShape::VAROBJ | BaseShape::DELEGATE));
122 if (!shape)
123 return false;
124
125 #ifdef DEBUG
126 HashSet<PropertyName *> added(cx);
127 if (!added.init())
128 return false;
129 #endif
130
131 uint32_t slot = CallObject::RESERVED_SLOTS;
132 for (BindingIter bi(self); bi; bi++) {
133 if (!bi->aliased())
134 continue;
135
136 #ifdef DEBUG
137 // The caller ensures no duplicate aliased names.
138 JS_ASSERT(!added.has(bi->name()));
139 if (!added.put(bi->name()))
140 return false;
141 #endif
142
143 StackBaseShape stackBase(cx, &CallObject::class_, nullptr, nullptr,
144 BaseShape::VAROBJ | BaseShape::DELEGATE);
145
146 UnownedBaseShape *base = BaseShape::getUnowned(cx, stackBase);
147 if (!base)
148 return false;
149
150 unsigned attrs = JSPROP_PERMANENT |
151 JSPROP_ENUMERATE |
152 (bi->kind() == Binding::CONSTANT ? JSPROP_READONLY : 0);
153 StackShape child(base, NameToId(bi->name()), slot, attrs, 0);
154
155 shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
156 if (!shape)
157 return false;
158
159 JS_ASSERT(slot < nslots);
160 slot++;
161 }
162 JS_ASSERT(slot == nslots);
163
164 JS_ASSERT(!shape->inDictionary());
165 self->callObjShape_.init(shape);
166 return true;
167 }
168
169 uint8_t *
170 Bindings::switchToScriptStorage(Binding *newBindingArray)
171 {
172 JS_ASSERT(bindingArrayUsingTemporaryStorage());
173 JS_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT));
174
175 if (count() > 0)
176 PodCopy(newBindingArray, bindingArray(), count());
177 bindingArrayAndFlag_ = uintptr_t(newBindingArray);
178 return reinterpret_cast<uint8_t *>(newBindingArray + count());
179 }
180
181 bool
182 Bindings::clone(JSContext *cx, InternalBindingsHandle self,
183 uint8_t *dstScriptData, HandleScript srcScript)
184 {
185 /* The clone has the same bindingArray_ offset as 'src'. */
186 Bindings &src = srcScript->bindings;
187 ptrdiff_t off = (uint8_t *)src.bindingArray() - srcScript->data;
188 JS_ASSERT(off >= 0);
189 JS_ASSERT(size_t(off) <= srcScript->dataSize());
190 Binding *dstPackedBindings = (Binding *)(dstScriptData + off);
191
192 /*
193 * Since atoms are shareable throughout the runtime, we can simply copy
194 * the source's bindingArray directly.
195 */
196 if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray(),
197 src.numBlockScoped()))
198 return false;
199 self->switchToScriptStorage(dstPackedBindings);
200 return true;
201 }
202
203 /* static */ Bindings
204 GCMethods<Bindings>::initial()
205 {
206 return Bindings();
207 }
208
209 template<XDRMode mode>
210 static bool
211 XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, uint32_t numVars,
212 HandleScript script, unsigned numBlockScoped)
213 {
214 JSContext *cx = xdr->cx();
215
216 if (mode == XDR_ENCODE) {
217 for (BindingIter bi(script); bi; bi++) {
218 RootedAtom atom(cx, bi->name());
219 if (!XDRAtom(xdr, &atom))
220 return false;
221 }
222
223 for (BindingIter bi(script); bi; bi++) {
224 uint8_t u8 = (uint8_t(bi->kind()) << 1) | uint8_t(bi->aliased());
225 if (!xdr->codeUint8(&u8))
226 return false;
227 }
228 } else {
229 uint32_t nameCount = numArgs + numVars;
230
231 AutoValueVector atoms(cx);
232 if (!atoms.resize(nameCount))
233 return false;
234 for (uint32_t i = 0; i < nameCount; i++) {
235 RootedAtom atom(cx);
236 if (!XDRAtom(xdr, &atom))
237 return false;
238 atoms[i] = StringValue(atom);
239 }
240
241 Binding *bindingArray = las.alloc().newArrayUninitialized<Binding>(nameCount);
242 if (!bindingArray)
243 return false;
244 for (uint32_t i = 0; i < nameCount; i++) {
245 uint8_t u8;
246 if (!xdr->codeUint8(&u8))
247 return false;
248
249 PropertyName *name = atoms[i].toString()->asAtom().asPropertyName();
250 Binding::Kind kind = Binding::Kind(u8 >> 1);
251 bool aliased = bool(u8 & 1);
252
253 bindingArray[i] = Binding(name, kind, aliased);
254 }
255
256 InternalBindingsHandle bindings(script, &script->bindings);
257 if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars, bindingArray,
258 numBlockScoped))
259 return false;
260 }
261
262 return true;
263 }
264
265 bool
266 Bindings::bindingIsAliased(uint32_t bindingIndex)
267 {
268 JS_ASSERT(bindingIndex < count());
269 return bindingArray()[bindingIndex].aliased();
270 }
271
272 void
273 Bindings::trace(JSTracer *trc)
274 {
275 if (callObjShape_)
276 MarkShape(trc, &callObjShape_, "callObjShape");
277
278 /*
279 * As the comment in Bindings explains, bindingsArray may point into freed
280 * storage when bindingArrayUsingTemporaryStorage so we don't mark it.
281 * Note: during compilation, atoms are already kept alive by gcKeepAtoms.
282 */
283 if (bindingArrayUsingTemporaryStorage())
284 return;
285
286 for (Binding *b = bindingArray(), *end = b + count(); b != end; b++) {
287 PropertyName *name = b->name();
288 MarkStringUnbarriered(trc, &name, "bindingArray");
289 }
290 }
291
292 bool
293 js::FillBindingVector(HandleScript fromScript, BindingVector *vec)
294 {
295 for (BindingIter bi(fromScript); bi; bi++) {
296 if (!vec->append(*bi))
297 return false;
298 }
299
300 return true;
301 }
302
303 template<XDRMode mode>
304 bool
305 js::XDRScriptConst(XDRState<mode> *xdr, MutableHandleValue vp)
306 {
307 JSContext *cx = xdr->cx();
308
309 /*
310 * A script constant can be an arbitrary primitive value as they are used
311 * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see
312 * bug 407186.
313 */
314 enum ConstTag {
315 SCRIPT_INT = 0,
316 SCRIPT_DOUBLE = 1,
317 SCRIPT_ATOM = 2,
318 SCRIPT_TRUE = 3,
319 SCRIPT_FALSE = 4,
320 SCRIPT_NULL = 5,
321 SCRIPT_OBJECT = 6,
322 SCRIPT_VOID = 7,
323 SCRIPT_HOLE = 8
324 };
325
326 uint32_t tag;
327 if (mode == XDR_ENCODE) {
328 if (vp.isInt32()) {
329 tag = SCRIPT_INT;
330 } else if (vp.isDouble()) {
331 tag = SCRIPT_DOUBLE;
332 } else if (vp.isString()) {
333 tag = SCRIPT_ATOM;
334 } else if (vp.isTrue()) {
335 tag = SCRIPT_TRUE;
336 } else if (vp.isFalse()) {
337 tag = SCRIPT_FALSE;
338 } else if (vp.isNull()) {
339 tag = SCRIPT_NULL;
340 } else if (vp.isObject()) {
341 tag = SCRIPT_OBJECT;
342 } else if (vp.isMagic(JS_ELEMENTS_HOLE)) {
343 tag = SCRIPT_HOLE;
344 } else {
345 JS_ASSERT(vp.isUndefined());
346 tag = SCRIPT_VOID;
347 }
348 }
349
350 if (!xdr->codeUint32(&tag))
351 return false;
352
353 switch (tag) {
354 case SCRIPT_INT: {
355 uint32_t i;
356 if (mode == XDR_ENCODE)
357 i = uint32_t(vp.toInt32());
358 if (!xdr->codeUint32(&i))
359 return false;
360 if (mode == XDR_DECODE)
361 vp.set(Int32Value(int32_t(i)));
362 break;
363 }
364 case SCRIPT_DOUBLE: {
365 double d;
366 if (mode == XDR_ENCODE)
367 d = vp.toDouble();
368 if (!xdr->codeDouble(&d))
369 return false;
370 if (mode == XDR_DECODE)
371 vp.set(DoubleValue(d));
372 break;
373 }
374 case SCRIPT_ATOM: {
375 RootedAtom atom(cx);
376 if (mode == XDR_ENCODE)
377 atom = &vp.toString()->asAtom();
378 if (!XDRAtom(xdr, &atom))
379 return false;
380 if (mode == XDR_DECODE)
381 vp.set(StringValue(atom));
382 break;
383 }
384 case SCRIPT_TRUE:
385 if (mode == XDR_DECODE)
386 vp.set(BooleanValue(true));
387 break;
388 case SCRIPT_FALSE:
389 if (mode == XDR_DECODE)
390 vp.set(BooleanValue(false));
391 break;
392 case SCRIPT_NULL:
393 if (mode == XDR_DECODE)
394 vp.set(NullValue());
395 break;
396 case SCRIPT_OBJECT: {
397 RootedObject obj(cx);
398 if (mode == XDR_ENCODE)
399 obj = &vp.toObject();
400
401 if (!XDRObjectLiteral(xdr, &obj))
402 return false;
403
404 if (mode == XDR_DECODE)
405 vp.setObject(*obj);
406 break;
407 }
408 case SCRIPT_VOID:
409 if (mode == XDR_DECODE)
410 vp.set(UndefinedValue());
411 break;
412 case SCRIPT_HOLE:
413 if (mode == XDR_DECODE)
414 vp.setMagic(JS_ELEMENTS_HOLE);
415 break;
416 }
417 return true;
418 }
419
420 template bool
421 js::XDRScriptConst(XDRState<XDR_ENCODE> *, MutableHandleValue);
422
423 template bool
424 js::XDRScriptConst(XDRState<XDR_DECODE> *, MutableHandleValue);
425
426 // Code LazyScript's free variables.
427 template<XDRMode mode>
428 static bool
429 XDRLazyFreeVariables(XDRState<mode> *xdr, MutableHandle<LazyScript *> lazy)
430 {
431 JSContext *cx = xdr->cx();
432 RootedAtom atom(cx);
433 HeapPtrAtom *freeVariables = lazy->freeVariables();
434 size_t numFreeVariables = lazy->numFreeVariables();
435 for (size_t i = 0; i < numFreeVariables; i++) {
436 if (mode == XDR_ENCODE)
437 atom = freeVariables[i];
438
439 if (!XDRAtom(xdr, &atom))
440 return false;
441
442 if (mode == XDR_DECODE)
443 freeVariables[i] = atom;
444 }
445
446 return true;
447 }
448
449 // Code the missing part needed to re-create a LazyScript from a JSScript.
450 template<XDRMode mode>
451 static bool
452 XDRRelazificationInfo(XDRState<mode> *xdr, HandleFunction fun, HandleScript script,
453 MutableHandle<LazyScript *> lazy)
454 {
455 MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
456 MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
457
458 JSContext *cx = xdr->cx();
459
460 uint64_t packedFields;
461 {
462 uint32_t begin = script->sourceStart();
463 uint32_t end = script->sourceEnd();
464 uint32_t lineno = script->lineno();
465 uint32_t column = script->column();
466
467 if (mode == XDR_ENCODE) {
468 packedFields = lazy->packedFields();
469 MOZ_ASSERT(begin == lazy->begin());
470 MOZ_ASSERT(end == lazy->end());
471 MOZ_ASSERT(lineno == lazy->lineno());
472 MOZ_ASSERT(column == lazy->column());
473 }
474
475 if (!xdr->codeUint64(&packedFields))
476 return false;
477
478 if (mode == XDR_DECODE) {
479 lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column));
480
481 // As opposed to XDRLazyScript, we need to restore the runtime bits
482 // of the script, as we are trying to match the fact this function
483 // has already been parsed and that it would need to be re-lazified.
484 lazy->initRuntimeFields(packedFields);
485 }
486 }
487
488 // Code free variables.
489 if (!XDRLazyFreeVariables(xdr, lazy))
490 return false;
491
492 return true;
493 }
494
495 static inline uint32_t
496 FindScopeObjectIndex(JSScript *script, NestedScopeObject &scope)
497 {
498 ObjectArray *objects = script->objects();
499 HeapPtrObject *vector = objects->vector;
500 unsigned length = objects->length;
501 for (unsigned i = 0; i < length; ++i) {
502 if (vector[i] == &scope)
503 return i;
504 }
505
506 MOZ_ASSUME_UNREACHABLE("Scope not found");
507 }
508
509 static bool
510 SaveSharedScriptData(ExclusiveContext *, Handle<JSScript *>, SharedScriptData *, uint32_t);
511
512 enum XDRClassKind {
513 CK_BlockObject = 0,
514 CK_WithObject = 1,
515 CK_JSFunction = 2,
516 CK_JSObject = 3
517 };
518
519 template<XDRMode mode>
520 bool
521 js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
522 HandleFunction fun, MutableHandleScript scriptp)
523 {
524 /* NB: Keep this in sync with CloneScript. */
525
526 enum ScriptBits {
527 NoScriptRval,
528 SavedCallerFun,
529 Strict,
530 ContainsDynamicNameAccess,
531 FunHasExtensibleScope,
532 FunNeedsDeclEnvObject,
533 FunHasAnyAliasedFormal,
534 ArgumentsHasVarBinding,
535 NeedsArgsObj,
536 IsGeneratorExp,
537 IsLegacyGenerator,
538 IsStarGenerator,
539 OwnSource,
540 ExplicitUseStrict,
541 SelfHosted,
542 IsCompileAndGo,
543 HasSingleton,
544 TreatAsRunOnce,
545 HasLazyScript
546 };
547
548 uint32_t length, lineno, column, nslots, staticLevel;
549 uint32_t natoms, nsrcnotes, i;
550 uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes;
551 uint32_t prologLength, version;
552 uint32_t funLength = 0;
553 uint32_t nTypeSets = 0;
554 uint32_t scriptBits = 0;
555
556 JSContext *cx = xdr->cx();
557 RootedScript script(cx);
558 natoms = nsrcnotes = 0;
559 nconsts = nobjects = nregexps = ntrynotes = nblockscopes = 0;
560
561 /* XDR arguments and vars. */
562 uint16_t nargs = 0;
563 uint16_t nblocklocals = 0;
564 uint32_t nvars = 0;
565 if (mode == XDR_ENCODE) {
566 script = scriptp.get();
567 JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
568
569 nargs = script->bindings.numArgs();
570 nblocklocals = script->bindings.numBlockScoped();
571 nvars = script->bindings.numVars();
572 }
573 if (!xdr->codeUint16(&nargs))
574 return false;
575 if (!xdr->codeUint16(&nblocklocals))
576 return false;
577 if (!xdr->codeUint32(&nvars))
578 return false;
579
580 if (mode == XDR_ENCODE)
581 length = script->length();
582 if (!xdr->codeUint32(&length))
583 return false;
584
585 if (mode == XDR_ENCODE) {
586 prologLength = script->mainOffset();
587 JS_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
588 version = script->getVersion();
589 lineno = script->lineno();
590 column = script->column();
591 nslots = script->nslots();
592 staticLevel = script->staticLevel();
593 natoms = script->natoms();
594
595 nsrcnotes = script->numNotes();
596
597 if (script->hasConsts())
598 nconsts = script->consts()->length;
599 if (script->hasObjects())
600 nobjects = script->objects()->length;
601 if (script->hasRegexps())
602 nregexps = script->regexps()->length;
603 if (script->hasTrynotes())
604 ntrynotes = script->trynotes()->length;
605 if (script->hasBlockScopes())
606 nblockscopes = script->blockScopes()->length;
607
608 nTypeSets = script->nTypeSets();
609 funLength = script->funLength();
610
611 if (script->noScriptRval())
612 scriptBits |= (1 << NoScriptRval);
613 if (script->savedCallerFun())
614 scriptBits |= (1 << SavedCallerFun);
615 if (script->strict())
616 scriptBits |= (1 << Strict);
617 if (script->explicitUseStrict())
618 scriptBits |= (1 << ExplicitUseStrict);
619 if (script->selfHosted())
620 scriptBits |= (1 << SelfHosted);
621 if (script->bindingsAccessedDynamically())
622 scriptBits |= (1 << ContainsDynamicNameAccess);
623 if (script->funHasExtensibleScope())
624 scriptBits |= (1 << FunHasExtensibleScope);
625 if (script->funNeedsDeclEnvObject())
626 scriptBits |= (1 << FunNeedsDeclEnvObject);
627 if (script->funHasAnyAliasedFormal())
628 scriptBits |= (1 << FunHasAnyAliasedFormal);
629 if (script->argumentsHasVarBinding())
630 scriptBits |= (1 << ArgumentsHasVarBinding);
631 if (script->analyzedArgsUsage() && script->needsArgsObj())
632 scriptBits |= (1 << NeedsArgsObj);
633 if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
634 scriptBits |= (1 << OwnSource);
635 if (script->isGeneratorExp())
636 scriptBits |= (1 << IsGeneratorExp);
637 if (script->isLegacyGenerator())
638 scriptBits |= (1 << IsLegacyGenerator);
639 if (script->isStarGenerator())
640 scriptBits |= (1 << IsStarGenerator);
641 if (script->compileAndGo())
642 scriptBits |= (1 << IsCompileAndGo);
643 if (script->hasSingletons())
644 scriptBits |= (1 << HasSingleton);
645 if (script->treatAsRunOnce())
646 scriptBits |= (1 << TreatAsRunOnce);
647 if (script->isRelazifiable())
648 scriptBits |= (1 << HasLazyScript);
649 }
650
651 if (!xdr->codeUint32(&prologLength))
652 return false;
653 if (!xdr->codeUint32(&version))
654 return false;
655
656 // To fuse allocations, we need lengths of all embedded arrays early.
657 if (!xdr->codeUint32(&natoms))
658 return false;
659 if (!xdr->codeUint32(&nsrcnotes))
660 return false;
661 if (!xdr->codeUint32(&nconsts))
662 return false;
663 if (!xdr->codeUint32(&nobjects))
664 return false;
665 if (!xdr->codeUint32(&nregexps))
666 return false;
667 if (!xdr->codeUint32(&ntrynotes))
668 return false;
669 if (!xdr->codeUint32(&nblockscopes))
670 return false;
671 if (!xdr->codeUint32(&nTypeSets))
672 return false;
673 if (!xdr->codeUint32(&funLength))
674 return false;
675 if (!xdr->codeUint32(&scriptBits))
676 return false;
677
678 if (mode == XDR_DECODE) {
679 JSVersion version_ = JSVersion(version);
680 JS_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
681
682 // staticLevel is set below.
683 CompileOptions options(cx);
684 options.setVersion(version_)
685 .setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
686 .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
687 RootedScriptSource sourceObject(cx);
688 if (scriptBits & (1 << OwnSource)) {
689 ScriptSource *ss = cx->new_<ScriptSource>();
690 if (!ss)
691 return false;
692 ScriptSourceHolder ssHolder(ss);
693
694 /*
695 * We use this CompileOptions only to initialize the
696 * ScriptSourceObject. Most CompileOptions fields aren't used by
697 * ScriptSourceObject, and those that are (element; elementAttributeName)
698 * aren't preserved by XDR. So this can be simple.
699 */
700 CompileOptions options(cx);
701 options.setOriginPrincipals(xdr->originPrincipals());
702 ss->initFromOptions(cx, options);
703 sourceObject = ScriptSourceObject::create(cx, ss, options);
704 if (!sourceObject)
705 return false;
706 } else {
707 JS_ASSERT(enclosingScript);
708 // When decoding, all the scripts and the script source object
709 // are in the same compartment, so the script's source object
710 // should never be a cross-compartment wrapper.
711 JS_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
712 sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
713 }
714 script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
715 options, /* staticLevel = */ 0, sourceObject, 0, 0);
716 if (!script)
717 return false;
718 }
719
720 /* JSScript::partiallyInit assumes script->bindings is fully initialized. */
721 LifoAllocScope las(&cx->tempLifoAlloc());
722 if (!XDRScriptBindings(xdr, las, nargs, nvars, script, nblocklocals))
723 return false;
724
725 if (mode == XDR_DECODE) {
726 if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes,
727 nblockscopes, nTypeSets))
728 {
729 return false;
730 }
731
732 JS_ASSERT(!script->mainOffset());
733 script->mainOffset_ = prologLength;
734 script->setLength(length);
735 script->funLength_ = funLength;
736
737 scriptp.set(script);
738
739 if (scriptBits & (1 << Strict))
740 script->strict_ = true;
741 if (scriptBits & (1 << ExplicitUseStrict))
742 script->explicitUseStrict_ = true;
743 if (scriptBits & (1 << ContainsDynamicNameAccess))
744 script->bindingsAccessedDynamically_ = true;
745 if (scriptBits & (1 << FunHasExtensibleScope))
746 script->funHasExtensibleScope_ = true;
747 if (scriptBits & (1 << FunNeedsDeclEnvObject))
748 script->funNeedsDeclEnvObject_ = true;
749 if (scriptBits & (1 << FunHasAnyAliasedFormal))
750 script->funHasAnyAliasedFormal_ = true;
751 if (scriptBits & (1 << ArgumentsHasVarBinding))
752 script->setArgumentsHasVarBinding();
753 if (scriptBits & (1 << NeedsArgsObj))
754 script->setNeedsArgsObj(true);
755 if (scriptBits & (1 << IsGeneratorExp))
756 script->isGeneratorExp_ = true;
757 if (scriptBits & (1 << IsCompileAndGo))
758 script->compileAndGo_ = true;
759 if (scriptBits & (1 << HasSingleton))
760 script->hasSingletons_ = true;
761 if (scriptBits & (1 << TreatAsRunOnce))
762 script->treatAsRunOnce_ = true;
763
764 if (scriptBits & (1 << IsLegacyGenerator)) {
765 JS_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
766 script->setGeneratorKind(LegacyGenerator);
767 } else if (scriptBits & (1 << IsStarGenerator))
768 script->setGeneratorKind(StarGenerator);
769 }
770
771 JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
772 JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
773
774 if (scriptBits & (1 << OwnSource)) {
775 if (!script->scriptSource()->performXDR<mode>(xdr))
776 return false;
777 }
778 if (!xdr->codeUint32(&script->sourceStart_))
779 return false;
780 if (!xdr->codeUint32(&script->sourceEnd_))
781 return false;
782
783 if (!xdr->codeUint32(&lineno) ||
784 !xdr->codeUint32(&column) ||
785 !xdr->codeUint32(&nslots) ||
786 !xdr->codeUint32(&staticLevel))
787 {
788 return false;
789 }
790
791 if (mode == XDR_DECODE) {
792 script->lineno_ = lineno;
793 script->column_ = column;
794 script->nslots_ = nslots;
795 script->staticLevel_ = staticLevel;
796 }
797
798 jsbytecode *code = script->code();
799 SharedScriptData *ssd;
800 if (mode == XDR_DECODE) {
801 ssd = SharedScriptData::new_(cx, length, nsrcnotes, natoms);
802 if (!ssd)
803 return false;
804 code = ssd->data;
805 if (natoms != 0) {
806 script->natoms_ = natoms;
807 script->atoms = ssd->atoms();
808 }
809 }
810
811 if (!xdr->codeBytes(code, length) || !xdr->codeBytes(code + length, nsrcnotes)) {
812 if (mode == XDR_DECODE)
813 js_free(ssd);
814 return false;
815 }
816
817 for (i = 0; i != natoms; ++i) {
818 if (mode == XDR_DECODE) {
819 RootedAtom tmp(cx);
820 if (!XDRAtom(xdr, &tmp))
821 return false;
822 script->atoms[i].init(tmp);
823 } else {
824 RootedAtom tmp(cx, script->atoms[i]);
825 if (!XDRAtom(xdr, &tmp))
826 return false;
827 }
828 }
829
830 if (mode == XDR_DECODE) {
831 if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
832 return false;
833 }
834
835 if (nconsts) {
836 HeapValue *vector = script->consts()->vector;
837 RootedValue val(cx);
838 for (i = 0; i != nconsts; ++i) {
839 if (mode == XDR_ENCODE)
840 val = vector[i];
841 if (!XDRScriptConst(xdr, &val))
842 return false;
843 if (mode == XDR_DECODE)
844 vector[i].init(val);
845 }
846 }
847
848 /*
849 * Here looping from 0-to-length to xdr objects is essential to ensure that
850 * all references to enclosing blocks (via FindScopeObjectIndex below) happen
851 * after the enclosing block has been XDR'd.
852 */
853 for (i = 0; i != nobjects; ++i) {
854 HeapPtr<JSObject> *objp = &script->objects()->vector[i];
855 XDRClassKind classk;
856
857 if (mode == XDR_ENCODE) {
858 JSObject *obj = *objp;
859 if (obj->is<BlockObject>())
860 classk = CK_BlockObject;
861 else if (obj->is<StaticWithObject>())
862 classk = CK_WithObject;
863 else if (obj->is<JSFunction>())
864 classk = CK_JSFunction;
865 else if (obj->is<JSObject>() || obj->is<ArrayObject>())
866 classk = CK_JSObject;
867 else
868 MOZ_ASSUME_UNREACHABLE("Cannot encode this class of object.");
869 }
870
871 if (!xdr->codeEnum32(&classk))
872 return false;
873
874 switch (classk) {
875 case CK_BlockObject:
876 case CK_WithObject: {
877 /* Code the nested block's enclosing scope. */
878 uint32_t enclosingStaticScopeIndex = 0;
879 if (mode == XDR_ENCODE) {
880 NestedScopeObject &scope = (*objp)->as<NestedScopeObject>();
881 if (NestedScopeObject *enclosing = scope.enclosingNestedScope())
882 enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
883 else
884 enclosingStaticScopeIndex = UINT32_MAX;
885 }
886 if (!xdr->codeUint32(&enclosingStaticScopeIndex))
887 return false;
888 Rooted<JSObject*> enclosingStaticScope(cx);
889 if (mode == XDR_DECODE) {
890 if (enclosingStaticScopeIndex != UINT32_MAX) {
891 JS_ASSERT(enclosingStaticScopeIndex < i);
892 enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
893 } else {
894 enclosingStaticScope = fun;
895 }
896 }
897
898 if (classk == CK_BlockObject) {
899 Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject *>(objp->get()));
900 if (!XDRStaticBlockObject(xdr, enclosingStaticScope, tmp.address()))
901 return false;
902 *objp = tmp;
903 } else {
904 Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject *>(objp->get()));
905 if (!XDRStaticWithObject(xdr, enclosingStaticScope, tmp.address()))
906 return false;
907 *objp = tmp;
908 }
909 break;
910 }
911
912 case CK_JSFunction: {
913 /* Code the nested function's enclosing scope. */
914 uint32_t funEnclosingScopeIndex = 0;
915 RootedObject funEnclosingScope(cx);
916 if (mode == XDR_ENCODE) {
917 RootedFunction function(cx, &(*objp)->as<JSFunction>());
918
919 if (function->isInterpretedLazy())
920 funEnclosingScope = function->lazyScript()->enclosingScope();
921 else
922 funEnclosingScope = function->nonLazyScript()->enclosingStaticScope();
923
924 StaticScopeIter<NoGC> ssi(funEnclosingScope);
925 if (ssi.done() || ssi.type() == StaticScopeIter<NoGC>::FUNCTION) {
926 JS_ASSERT(ssi.done() == !fun);
927 funEnclosingScopeIndex = UINT32_MAX;
928 } else if (ssi.type() == StaticScopeIter<NoGC>::BLOCK) {
929 funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block());
930 JS_ASSERT(funEnclosingScopeIndex < i);
931 } else {
932 funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith());
933 JS_ASSERT(funEnclosingScopeIndex < i);
934 }
935 }
936
937 if (!xdr->codeUint32(&funEnclosingScopeIndex))
938 return false;
939
940 if (mode == XDR_DECODE) {
941 if (funEnclosingScopeIndex == UINT32_MAX) {
942 funEnclosingScope = fun;
943 } else {
944 JS_ASSERT(funEnclosingScopeIndex < i);
945 funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
946 }
947 }
948
949 // Code nested function and script.
950 RootedObject tmp(cx, *objp);
951 if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
952 return false;
953 *objp = tmp;
954 break;
955 }
956
957 case CK_JSObject: {
958 /* Code object literal. */
959 RootedObject tmp(cx, *objp);
960 if (!XDRObjectLiteral(xdr, &tmp))
961 return false;
962 *objp = tmp;
963 break;
964 }
965
966 default: {
967 MOZ_ASSERT(false, "Unknown class kind.");
968 return false;
969 }
970 }
971 }
972
973 for (i = 0; i != nregexps; ++i) {
974 if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i]))
975 return false;
976 }
977
978 if (ntrynotes != 0) {
979 JSTryNote *tnfirst = script->trynotes()->vector;
980 JS_ASSERT(script->trynotes()->length == ntrynotes);
981 JSTryNote *tn = tnfirst + ntrynotes;
982 do {
983 --tn;
984 if (!xdr->codeUint8(&tn->kind) ||
985 !xdr->codeUint32(&tn->stackDepth) ||
986 !xdr->codeUint32(&tn->start) ||
987 !xdr->codeUint32(&tn->length)) {
988 return false;
989 }
990 } while (tn != tnfirst);
991 }
992
993 for (i = 0; i < nblockscopes; ++i) {
994 BlockScopeNote *note = &script->blockScopes()->vector[i];
995 if (!xdr->codeUint32(&note->index) ||
996 !xdr->codeUint32(&note->start) ||
997 !xdr->codeUint32(&note->length) ||
998 !xdr->codeUint32(&note->parent))
999 {
1000 return false;
1001 }
1002 }
1003
1004 if (scriptBits & (1 << HasLazyScript)) {
1005 Rooted<LazyScript *> lazy(cx);
1006 if (mode == XDR_ENCODE)
1007 lazy = script->maybeLazyScript();
1008
1009 if (!XDRRelazificationInfo(xdr, fun, script, &lazy))
1010 return false;
1011
1012 if (mode == XDR_DECODE)
1013 script->setLazyScript(lazy);
1014 }
1015
1016 if (mode == XDR_DECODE) {
1017 scriptp.set(script);
1018
1019 /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
1020 CallNewScriptHook(cx, script, fun);
1021 if (!fun) {
1022 RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr);
1023 Debugger::onNewScript(cx, script, global);
1024 }
1025 }
1026
1027 return true;
1028 }
1029
1030 template bool
1031 js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction,
1032 MutableHandleScript);
1033
1034 template bool
1035 js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction,
1036 MutableHandleScript);
1037
1038 template<XDRMode mode>
1039 bool
1040 js::XDRLazyScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
1041 HandleFunction fun, MutableHandle<LazyScript *> lazy)
1042 {
1043 JSContext *cx = xdr->cx();
1044
1045 {
1046 uint32_t begin;
1047 uint32_t end;
1048 uint32_t lineno;
1049 uint32_t column;
1050 uint64_t packedFields;
1051
1052 if (mode == XDR_ENCODE) {
1053 MOZ_ASSERT(!lazy->maybeScript());
1054 MOZ_ASSERT(fun == lazy->functionNonDelazifying());
1055
1056 begin = lazy->begin();
1057 end = lazy->end();
1058 lineno = lazy->lineno();
1059 column = lazy->column();
1060 packedFields = lazy->packedFields();
1061 }
1062
1063 if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
1064 !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
1065 !xdr->codeUint64(&packedFields))
1066 {
1067 return false;
1068 }
1069
1070 if (mode == XDR_DECODE)
1071 lazy.set(LazyScript::Create(cx, fun, packedFields, begin, end, lineno, column));
1072 }
1073
1074 // Code free variables.
1075 if (!XDRLazyFreeVariables(xdr, lazy))
1076 return false;
1077
1078 // Code inner functions.
1079 {
1080 RootedObject func(cx);
1081 HeapPtrFunction *innerFunctions = lazy->innerFunctions();
1082 size_t numInnerFunctions = lazy->numInnerFunctions();
1083 for (size_t i = 0; i < numInnerFunctions; i++) {
1084 if (mode == XDR_ENCODE)
1085 func = innerFunctions[i];
1086
1087 if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func))
1088 return false;
1089
1090 if (mode == XDR_DECODE)
1091 innerFunctions[i] = &func->as<JSFunction>();
1092 }
1093 }
1094
1095 if (mode == XDR_DECODE) {
1096 JS_ASSERT(!lazy->sourceObject());
1097 ScriptSourceObject *sourceObject = &enclosingScript->scriptSourceUnwrap();
1098
1099 // Set the enclosing scope of the lazy function, this would later be
1100 // used to define the environment when the function would be used.
1101 lazy->setParent(enclosingScope, sourceObject);
1102 }
1103
1104 return true;
1105 }
1106
1107 template bool
1108 js::XDRLazyScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript,
1109 HandleFunction, MutableHandle<LazyScript *>);
1110
1111 template bool
1112 js::XDRLazyScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript,
1113 HandleFunction, MutableHandle<LazyScript *>);
1114
1115 void
1116 JSScript::setSourceObject(JSObject *object)
1117 {
1118 JS_ASSERT(compartment() == object->compartment());
1119 sourceObject_ = object;
1120 }
1121
1122 js::ScriptSourceObject &
1123 JSScript::scriptSourceUnwrap() const {
1124 return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>();
1125 }
1126
1127 js::ScriptSource *
1128 JSScript::scriptSource() const {
1129 return scriptSourceUnwrap().source();
1130 }
1131
1132 bool
1133 JSScript::initScriptCounts(JSContext *cx)
1134 {
1135 JS_ASSERT(!hasScriptCounts());
1136
1137 size_t n = 0;
1138
1139 for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc))
1140 n += PCCounts::numCounts(JSOp(*pc));
1141
1142 size_t bytes = (length() * sizeof(PCCounts)) + (n * sizeof(double));
1143 char *base = (char *) cx->calloc_(bytes);
1144 if (!base)
1145 return false;
1146
1147 /* Create compartment's scriptCountsMap if necessary. */
1148 ScriptCountsMap *map = compartment()->scriptCountsMap;
1149 if (!map) {
1150 map = cx->new_<ScriptCountsMap>();
1151 if (!map || !map->init()) {
1152 js_free(base);
1153 js_delete(map);
1154 return false;
1155 }
1156 compartment()->scriptCountsMap = map;
1157 }
1158
1159 char *cursor = base;
1160
1161 ScriptCounts scriptCounts;
1162 scriptCounts.pcCountsVector = (PCCounts *) cursor;
1163 cursor += length() * sizeof(PCCounts);
1164
1165 for (jsbytecode *pc = code(); pc < codeEnd(); pc += GetBytecodeLength(pc)) {
1166 JS_ASSERT(uintptr_t(cursor) % sizeof(double) == 0);
1167 scriptCounts.pcCountsVector[pcToOffset(pc)].counts = (double *) cursor;
1168 size_t capacity = PCCounts::numCounts(JSOp(*pc));
1169 #ifdef DEBUG
1170 scriptCounts.pcCountsVector[pcToOffset(pc)].capacity = capacity;
1171 #endif
1172 cursor += capacity * sizeof(double);
1173 }
1174
1175 if (!map->putNew(this, scriptCounts)) {
1176 js_free(base);
1177 return false;
1178 }
1179 hasScriptCounts_ = true; // safe to set this; we can't fail after this point
1180
1181 JS_ASSERT(size_t(cursor - base) == bytes);
1182
1183 /* Enable interrupts in any interpreter frames running on this script. */
1184 for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
1185 if (iter->isInterpreter())
1186 iter->asInterpreter()->enableInterruptsIfRunning(this);
1187 }
1188
1189 return true;
1190 }
1191
1192 static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript *script)
1193 {
1194 JS_ASSERT(script->hasScriptCounts());
1195 ScriptCountsMap *map = script->compartment()->scriptCountsMap;
1196 ScriptCountsMap::Ptr p = map->lookup(script);
1197 JS_ASSERT(p);
1198 return p;
1199 }
1200
1201 js::PCCounts
1202 JSScript::getPCCounts(jsbytecode *pc) {
1203 JS_ASSERT(containsPC(pc));
1204 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1205 return p->value().pcCountsVector[pcToOffset(pc)];
1206 }
1207
1208 void
1209 JSScript::addIonCounts(jit::IonScriptCounts *ionCounts)
1210 {
1211 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1212 if (p->value().ionCounts)
1213 ionCounts->setPrevious(p->value().ionCounts);
1214 p->value().ionCounts = ionCounts;
1215 }
1216
1217 jit::IonScriptCounts *
1218 JSScript::getIonCounts()
1219 {
1220 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1221 return p->value().ionCounts;
1222 }
1223
1224 ScriptCounts
1225 JSScript::releaseScriptCounts()
1226 {
1227 ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1228 ScriptCounts counts = p->value();
1229 compartment()->scriptCountsMap->remove(p);
1230 hasScriptCounts_ = false;
1231 return counts;
1232 }
1233
1234 void
1235 JSScript::destroyScriptCounts(FreeOp *fop)
1236 {
1237 if (hasScriptCounts()) {
1238 ScriptCounts scriptCounts = releaseScriptCounts();
1239 scriptCounts.destroy(fop);
1240 }
1241 }
1242
1243 void
1244 ScriptSourceObject::setSource(ScriptSource *source)
1245 {
1246 if (source)
1247 source->incref();
1248 if (this->source())
1249 this->source()->decref();
1250 setReservedSlot(SOURCE_SLOT, PrivateValue(source));
1251 }
1252
1253 JSObject *
1254 ScriptSourceObject::element() const
1255 {
1256 return getReservedSlot(ELEMENT_SLOT).toObjectOrNull();
1257 }
1258
1259 void
1260 ScriptSourceObject::initElement(HandleObject element)
1261 {
1262 JS_ASSERT(getReservedSlot(ELEMENT_SLOT).isNull());
1263 setReservedSlot(ELEMENT_SLOT, ObjectOrNullValue(element));
1264 }
1265
1266 const Value &
1267 ScriptSourceObject::elementAttributeName() const
1268 {
1269 const Value &prop = getReservedSlot(ELEMENT_PROPERTY_SLOT);
1270 JS_ASSERT(prop.isUndefined() || prop.isString());
1271 return prop;
1272 }
1273
1274 void
1275 ScriptSourceObject::initIntroductionScript(JSScript *script)
1276 {
1277 JS_ASSERT(!getReservedSlot(INTRODUCTION_SCRIPT_SLOT).toPrivate());
1278
1279 // There is no equivalent of cross-compartment wrappers for scripts. If
1280 // the introduction script would be in a different compartment from the
1281 // compiled code, we would be creating a cross-compartment script
1282 // reference, which would be bogus. In that case, just don't bother to
1283 // retain the introduction script.
1284 if (script && script->compartment() == compartment())
1285 setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
1286 }
1287
1288 void
1289 ScriptSourceObject::trace(JSTracer *trc, JSObject *obj)
1290 {
1291 ScriptSourceObject *sso = static_cast<ScriptSourceObject *>(obj);
1292
1293 if (JSScript *script = sso->introductionScript()) {
1294 MarkScriptUnbarriered(trc, &script, "ScriptSourceObject introductionScript");
1295 sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
1296 }
1297 }
1298
1299 void
1300 ScriptSourceObject::finalize(FreeOp *fop, JSObject *obj)
1301 {
1302 // ScriptSource::setSource automatically takes care of the refcount
1303 obj->as<ScriptSourceObject>().setSource(nullptr);
1304 }
1305
1306 const Class ScriptSourceObject::class_ = {
1307 "ScriptSource",
1308 JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
1309 JSCLASS_IMPLEMENTS_BARRIERS | JSCLASS_IS_ANONYMOUS,
1310 JS_PropertyStub, /* addProperty */
1311 JS_DeletePropertyStub, /* delProperty */
1312 JS_PropertyStub, /* getProperty */
1313 JS_StrictPropertyStub, /* setProperty */
1314 JS_EnumerateStub,
1315 JS_ResolveStub,
1316 JS_ConvertStub,
1317 finalize,
1318 nullptr, /* call */
1319 nullptr, /* hasInstance */
1320 nullptr, /* construct */
1321 trace
1322 };
1323
1324 ScriptSourceObject *
1325 ScriptSourceObject::create(ExclusiveContext *cx, ScriptSource *source,
1326 const ReadOnlyCompileOptions &options)
1327 {
1328 RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr, cx->global()));
1329 if (!object)
1330 return nullptr;
1331 RootedScriptSource sourceObject(cx, &object->as<ScriptSourceObject>());
1332
1333 source->incref();
1334 sourceObject->initSlot(SOURCE_SLOT, PrivateValue(source));
1335 sourceObject->initSlot(ELEMENT_SLOT, ObjectOrNullValue(options.element()));
1336 if (options.elementAttributeName())
1337 sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, StringValue(options.elementAttributeName()));
1338 else
1339 sourceObject->initSlot(ELEMENT_PROPERTY_SLOT, UndefinedValue());
1340
1341 sourceObject->initSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(nullptr));
1342 sourceObject->initIntroductionScript(options.introductionScript());
1343
1344 return sourceObject;
1345 }
1346
1347 static const unsigned char emptySource[] = "";
1348
1349 /* Adjust the amount of memory this script source uses for source data,
1350 reallocating if needed. */
1351 bool
1352 ScriptSource::adjustDataSize(size_t nbytes)
1353 {
1354 // Allocating 0 bytes has undefined behavior, so special-case it.
1355 if (nbytes == 0) {
1356 if (data.compressed != emptySource)
1357 js_free(data.compressed);
1358 data.compressed = const_cast<unsigned char *>(emptySource);
1359 return true;
1360 }
1361
1362 // |data.compressed| can be nullptr.
1363 void *buf = js_realloc(data.compressed, nbytes);
1364 if (!buf && data.compressed != emptySource)
1365 js_free(data.compressed);
1366 data.compressed = static_cast<unsigned char *>(buf);
1367 return !!data.compressed;
1368 }
1369
1370 /* static */ bool
1371 JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked)
1372 {
1373 JS_ASSERT(!ss->hasSourceData());
1374 *worked = false;
1375 if (!cx->runtime()->sourceHook || !ss->sourceRetrievable())
1376 return true;
1377 jschar *src = nullptr;
1378 size_t length;
1379 if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length))
1380 return false;
1381 if (!src)
1382 return true;
1383 ss->setSource(src, length);
1384 *worked = true;
1385 return true;
1386 }
1387
1388 JSFlatString *
1389 JSScript::sourceData(JSContext *cx)
1390 {
1391 JS_ASSERT(scriptSource()->hasSourceData());
1392 return scriptSource()->substring(cx, sourceStart(), sourceEnd());
1393 }
1394
1395 SourceDataCache::AutoHoldEntry::AutoHoldEntry()
1396 : cache_(nullptr), source_(nullptr), charsToFree_(nullptr)
1397 {
1398 }
1399
1400 void
1401 SourceDataCache::AutoHoldEntry::holdEntry(SourceDataCache *cache, ScriptSource *source)
1402 {
1403 // Initialise the holder for a specific cache and script source. This will
1404 // hold on to the cached source chars in the event that the cache is purged.
1405 JS_ASSERT(!cache_ && !source_ && !charsToFree_);
1406 cache_ = cache;
1407 source_ = source;
1408 }
1409
1410 void
1411 SourceDataCache::AutoHoldEntry::deferDelete(const jschar *chars)
1412 {
1413 // Take ownership of source chars now the cache is being purged. Remove our
1414 // reference to the ScriptSource which might soon be destroyed.
1415 JS_ASSERT(cache_ && source_ && !charsToFree_);
1416 cache_ = nullptr;
1417 source_ = nullptr;
1418 charsToFree_ = chars;
1419 }
1420
1421 SourceDataCache::AutoHoldEntry::~AutoHoldEntry()
1422 {
1423 // The holder is going out of scope. If it has taken ownership of cached
1424 // chars then delete them, otherwise unregister ourself with the cache.
1425 if (charsToFree_) {
1426 JS_ASSERT(!cache_ && !source_);
1427 js_free(const_cast<jschar *>(charsToFree_));
1428 } else if (cache_) {
1429 JS_ASSERT(source_);
1430 cache_->releaseEntry(*this);
1431 }
1432 }
1433
1434 void
1435 SourceDataCache::holdEntry(AutoHoldEntry &holder, ScriptSource *ss)
1436 {
1437 JS_ASSERT(!holder_);
1438 holder.holdEntry(this, ss);
1439 holder_ = &holder;
1440 }
1441
1442 void
1443 SourceDataCache::releaseEntry(AutoHoldEntry &holder)
1444 {
1445 JS_ASSERT(holder_ == &holder);
1446 holder_ = nullptr;
1447 }
1448
1449 const jschar *
1450 SourceDataCache::lookup(ScriptSource *ss, AutoHoldEntry &holder)
1451 {
1452 JS_ASSERT(!holder_);
1453 if (!map_)
1454 return nullptr;
1455 if (Map::Ptr p = map_->lookup(ss)) {
1456 holdEntry(holder, ss);
1457 return p->value();
1458 }
1459 return nullptr;
1460 }
1461
1462 bool
1463 SourceDataCache::put(ScriptSource *ss, const jschar *str, AutoHoldEntry &holder)
1464 {
1465 JS_ASSERT(!holder_);
1466
1467 if (!map_) {
1468 map_ = js_new<Map>();
1469 if (!map_)
1470 return false;
1471
1472 if (!map_->init()) {
1473 js_delete(map_);
1474 map_ = nullptr;
1475 return false;
1476 }
1477 }
1478
1479 if (!map_->put(ss, str))
1480 return false;
1481
1482 holdEntry(holder, ss);
1483 return true;
1484 }
1485
1486 void
1487 SourceDataCache::purge()
1488 {
1489 if (!map_)
1490 return;
1491
1492 for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
1493 const jschar *chars = r.front().value();
1494 if (holder_ && r.front().key() == holder_->source()) {
1495 holder_->deferDelete(chars);
1496 holder_ = nullptr;
1497 } else {
1498 js_free(const_cast<jschar*>(chars));
1499 }
1500 }
1501
1502 js_delete(map_);
1503 map_ = nullptr;
1504 }
1505
1506 size_t
1507 SourceDataCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
1508 {
1509 size_t n = 0;
1510 if (map_ && !map_->empty()) {
1511 n += map_->sizeOfIncludingThis(mallocSizeOf);
1512 for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
1513 const jschar *v = r.front().value();
1514 n += mallocSizeOf(v);
1515 }
1516 }
1517 return n;
1518 }
1519
1520 const jschar *
1521 ScriptSource::chars(JSContext *cx, SourceDataCache::AutoHoldEntry &holder)
1522 {
1523 if (const jschar *chars = getOffThreadCompressionChars(cx))
1524 return chars;
1525 JS_ASSERT(ready());
1526
1527 #ifdef USE_ZLIB
1528 if (compressed()) {
1529 if (const jschar *decompressed = cx->runtime()->sourceDataCache.lookup(this, holder))
1530 return decompressed;
1531
1532 const size_t nbytes = sizeof(jschar) * (length_ + 1);
1533 jschar *decompressed = static_cast<jschar *>(js_malloc(nbytes));
1534 if (!decompressed)
1535 return nullptr;
1536
1537 if (!DecompressString(data.compressed, compressedLength_,
1538 reinterpret_cast<unsigned char *>(decompressed), nbytes)) {
1539 JS_ReportOutOfMemory(cx);
1540 js_free(decompressed);
1541 return nullptr;
1542 }
1543
1544 decompressed[length_] = 0;
1545
1546 if (!cx->runtime()->sourceDataCache.put(this, decompressed, holder)) {
1547 JS_ReportOutOfMemory(cx);
1548 js_free(decompressed);
1549 return nullptr;
1550 }
1551
1552 return decompressed;
1553 }
1554 #endif
1555 return data.source;
1556 }
1557
1558 JSFlatString *
1559 ScriptSource::substring(JSContext *cx, uint32_t start, uint32_t stop)
1560 {
1561 JS_ASSERT(start <= stop);
1562 SourceDataCache::AutoHoldEntry holder;
1563 const jschar *chars = this->chars(cx, holder);
1564 if (!chars)
1565 return nullptr;
1566 return js_NewStringCopyN<CanGC>(cx, chars + start, stop - start);
1567 }
1568
1569 bool
1570 ScriptSource::setSourceCopy(ExclusiveContext *cx, SourceBufferHolder &srcBuf,
1571 bool argumentsNotIncluded, SourceCompressionTask *task)
1572 {
1573 JS_ASSERT(!hasSourceData());
1574 length_ = srcBuf.length();
1575 argumentsNotIncluded_ = argumentsNotIncluded;
1576
1577 // There are several cases where source compression is not a good idea:
1578 // - If the script is tiny, then compression will save little or no space.
1579 // - If the script is enormous, then decompression can take seconds. With
1580 // lazy parsing, decompression is not uncommon, so this can significantly
1581 // increase latency.
1582 // - If there is only one core, then compression will contend with JS
1583 // execution (which hurts benchmarketing).
1584 // - If the source contains a giant string, then parsing will finish much
1585 // faster than compression which increases latency (this case is handled
1586 // in Parser::stringLiteral).
1587 //
1588 // Lastly, since the parsing thread will eventually perform a blocking wait
1589 // on the compresion task's worker thread, require that there are at least 2
1590 // worker threads:
1591 // - If we are on a worker thread, there must be another worker thread to
1592 // execute our compression task.
1593 // - If we are on the main thread, there must be at least two worker
1594 // threads since at most one worker thread can be blocking on the main
1595 // thread (see WorkerThreadState::canStartParseTask) which would cause a
1596 // deadlock if there wasn't a second worker thread that could make
1597 // progress on our compression task.
1598 #ifdef JS_THREADSAFE
1599 bool canCompressOffThread =
1600 WorkerThreadState().cpuCount > 1 &&
1601 WorkerThreadState().threadCount >= 2;
1602 #else
1603 bool canCompressOffThread = false;
1604 #endif
1605 const size_t TINY_SCRIPT = 256;
1606 const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
1607 if (TINY_SCRIPT <= srcBuf.length() && srcBuf.length() < HUGE_SCRIPT && canCompressOffThread) {
1608 task->ss = this;
1609 task->chars = srcBuf.get();
1610 ready_ = false;
1611 if (!StartOffThreadCompression(cx, task))
1612 return false;
1613 } else if (srcBuf.ownsChars()) {
1614 data.source = srcBuf.take();
1615 } else {
1616 if (!adjustDataSize(sizeof(jschar) * srcBuf.length()))
1617 return false;
1618 PodCopy(data.source, srcBuf.get(), length_);
1619 }
1620
1621 return true;
1622 }
1623
1624 void
1625 ScriptSource::setSource(const jschar *src, size_t length)
1626 {
1627 JS_ASSERT(!hasSourceData());
1628 length_ = length;
1629 JS_ASSERT(!argumentsNotIncluded_);
1630 data.source = const_cast<jschar *>(src);
1631 }
1632
1633 bool
1634 SourceCompressionTask::work()
1635 {
1636 // A given compression token can be compressed on any thread, and the ss
1637 // not being ready indicates to other threads that its fields might change
1638 // with no lock held.
1639 JS_ASSERT(!ss->ready());
1640
1641 size_t compressedLength = 0;
1642 size_t nbytes = sizeof(jschar) * ss->length_;
1643
1644 // Memory allocation functions on JSRuntime and JSContext are not
1645 // threadsafe. We have to use the js_* variants.
1646
1647 #ifdef USE_ZLIB
1648 // Try to keep the maximum memory usage down by only allocating half the
1649 // size of the string, first.
1650 size_t firstSize = nbytes / 2;
1651 if (!ss->adjustDataSize(firstSize))
1652 return false;
1653 Compressor comp(reinterpret_cast<const unsigned char *>(chars), nbytes);
1654 if (!comp.init())
1655 return false;
1656 comp.setOutput(ss->data.compressed, firstSize);
1657 bool cont = !abort_;
1658 while (cont) {
1659 switch (comp.compressMore()) {
1660 case Compressor::CONTINUE:
1661 break;
1662 case Compressor::MOREOUTPUT: {
1663 if (comp.outWritten() == nbytes) {
1664 cont = false;
1665 break;
1666 }
1667
1668 // The compressed output is greater than half the size of the
1669 // original string. Reallocate to the full size.
1670 if (!ss->adjustDataSize(nbytes))
1671 return false;
1672 comp.setOutput(ss->data.compressed, nbytes);
1673 break;
1674 }
1675 case Compressor::DONE:
1676 cont = false;
1677 break;
1678 case Compressor::OOM:
1679 return false;
1680 }
1681 cont = cont && !abort_;
1682 }
1683 compressedLength = comp.outWritten();
1684 if (abort_ || compressedLength == nbytes)
1685 compressedLength = 0;
1686 #endif
1687
1688 if (compressedLength == 0) {
1689 if (!ss->adjustDataSize(nbytes))
1690 return false;
1691 PodCopy(ss->data.source, chars, ss->length());
1692 } else {
1693 // Shrink the buffer to the size of the compressed data. Shouldn't fail.
1694 JS_ALWAYS_TRUE(ss->adjustDataSize(compressedLength));
1695 }
1696 ss->compressedLength_ = compressedLength;
1697 return true;
1698 }
1699
1700 void
1701 ScriptSource::destroy()
1702 {
1703 JS_ASSERT(ready());
1704 adjustDataSize(0);
1705 if (introducerFilename_ != filename_)
1706 js_free(introducerFilename_);
1707 js_free(filename_);
1708 js_free(displayURL_);
1709 js_free(sourceMapURL_);
1710 if (originPrincipals_)
1711 JS_DropPrincipals(TlsPerThreadData.get()->runtimeFromMainThread(), originPrincipals_);
1712 ready_ = false;
1713 js_free(this);
1714 }
1715
1716 void
1717 ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
1718 JS::ScriptSourceInfo *info) const
1719 {
1720 if (ready() && data.compressed != emptySource) {
1721 if (compressed())
1722 info->compressed += mallocSizeOf(data.compressed);
1723 else
1724 info->uncompressed += mallocSizeOf(data.source);
1725 }
1726 info->misc += mallocSizeOf(this) + mallocSizeOf(filename_);
1727 info->numScripts++;
1728 }
1729
1730 template<XDRMode mode>
1731 bool
1732 ScriptSource::performXDR(XDRState<mode> *xdr)
1733 {
1734 uint8_t hasSource = hasSourceData();
1735 if (!xdr->codeUint8(&hasSource))
1736 return false;
1737
1738 uint8_t retrievable = sourceRetrievable_;
1739 if (!xdr->codeUint8(&retrievable))
1740 return false;
1741 sourceRetrievable_ = retrievable;
1742
1743 if (hasSource && !sourceRetrievable_) {
1744 // Only set members when we know decoding cannot fail. This prevents the
1745 // script source from being partially initialized.
1746 uint32_t length = length_;
1747 if (!xdr->codeUint32(&length))
1748 return false;
1749
1750 uint32_t compressedLength = compressedLength_;
1751 if (!xdr->codeUint32(&compressedLength))
1752 return false;
1753
1754 uint8_t argumentsNotIncluded = argumentsNotIncluded_;
1755 if (!xdr->codeUint8(&argumentsNotIncluded))
1756 return false;
1757
1758 size_t byteLen = compressedLength ? compressedLength : (length * sizeof(jschar));
1759 if (mode == XDR_DECODE) {
1760 if (!adjustDataSize(byteLen))
1761 return false;
1762 }
1763 if (!xdr->codeBytes(data.compressed, byteLen)) {
1764 if (mode == XDR_DECODE) {
1765 js_free(data.compressed);
1766 data.compressed = nullptr;
1767 }
1768 return false;
1769 }
1770 length_ = length;
1771 compressedLength_ = compressedLength;
1772 argumentsNotIncluded_ = argumentsNotIncluded;
1773 }
1774
1775 uint8_t haveSourceMap = hasSourceMapURL();
1776 if (!xdr->codeUint8(&haveSourceMap))
1777 return false;
1778
1779 if (haveSourceMap) {
1780 uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_);
1781 if (!xdr->codeUint32(&sourceMapURLLen))
1782 return false;
1783
1784 if (mode == XDR_DECODE) {
1785 size_t byteLen = (sourceMapURLLen + 1) * sizeof(jschar);
1786 sourceMapURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen));
1787 if (!sourceMapURL_)
1788 return false;
1789 }
1790 if (!xdr->codeChars(sourceMapURL_, sourceMapURLLen)) {
1791 if (mode == XDR_DECODE) {
1792 js_free(sourceMapURL_);
1793 sourceMapURL_ = nullptr;
1794 }
1795 return false;
1796 }
1797 sourceMapURL_[sourceMapURLLen] = '\0';
1798 }
1799
1800 uint8_t haveDisplayURL = hasDisplayURL();
1801 if (!xdr->codeUint8(&haveDisplayURL))
1802 return false;
1803
1804 if (haveDisplayURL) {
1805 uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_);
1806 if (!xdr->codeUint32(&displayURLLen))
1807 return false;
1808
1809 if (mode == XDR_DECODE) {
1810 size_t byteLen = (displayURLLen + 1) * sizeof(jschar);
1811 displayURL_ = static_cast<jschar *>(xdr->cx()->malloc_(byteLen));
1812 if (!displayURL_)
1813 return false;
1814 }
1815 if (!xdr->codeChars(displayURL_, displayURLLen)) {
1816 if (mode == XDR_DECODE) {
1817 js_free(displayURL_);
1818 displayURL_ = nullptr;
1819 }
1820 return false;
1821 }
1822 displayURL_[displayURLLen] = '\0';
1823 }
1824
1825 uint8_t haveFilename = !!filename_;
1826 if (!xdr->codeUint8(&haveFilename))
1827 return false;
1828
1829 if (haveFilename) {
1830 const char *fn = filename();
1831 if (!xdr->codeCString(&fn))
1832 return false;
1833 if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn))
1834 return false;
1835 }
1836
1837 if (mode == XDR_DECODE)
1838 ready_ = true;
1839
1840 return true;
1841 }
1842
1843 // Format and return a cx->malloc_'ed URL for a generated script like:
1844 // {filename} line {lineno} > {introducer}
1845 // For example:
1846 // foo.js line 7 > eval
1847 // indicating code compiled by the call to 'eval' on line 7 of foo.js.
1848 static char *
1849 FormatIntroducedFilename(ExclusiveContext *cx, const char *filename, unsigned lineno,
1850 const char *introducer)
1851 {
1852 // Compute the length of the string in advance, so we can allocate a
1853 // buffer of the right size on the first shot.
1854 //
1855 // (JS_smprintf would be perfect, as that allocates the result
1856 // dynamically as it formats the string, but it won't allocate from cx,
1857 // and wants us to use a special free function.)
1858 char linenoBuf[15];
1859 size_t filenameLen = strlen(filename);
1860 size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", lineno);
1861 size_t introducerLen = strlen(introducer);
1862 size_t len = filenameLen +
1863 6 /* == strlen(" line ") */ +
1864 linenoLen +
1865 3 /* == strlen(" > ") */ +
1866 introducerLen +
1867 1 /* \0 */;
1868 char *formatted = cx->pod_malloc<char>(len);
1869 if (!formatted)
1870 return nullptr;
1871 mozilla::DebugOnly<size_t> checkLen = JS_snprintf(formatted, len, "%s line %s > %s",
1872 filename, linenoBuf, introducer);
1873 JS_ASSERT(checkLen == len - 1);
1874
1875 return formatted;
1876 }
1877
1878 bool
1879 ScriptSource::initFromOptions(ExclusiveContext *cx, const ReadOnlyCompileOptions &options)
1880 {
1881 JS_ASSERT(!filename_);
1882 JS_ASSERT(!introducerFilename_);
1883
1884 originPrincipals_ = options.originPrincipals(cx);
1885 if (originPrincipals_)
1886 JS_HoldPrincipals(originPrincipals_);
1887
1888 introductionType_ = options.introductionType;
1889 setIntroductionOffset(options.introductionOffset);
1890
1891 if (options.hasIntroductionInfo) {
1892 JS_ASSERT(options.introductionType != nullptr);
1893 const char *filename = options.filename() ? options.filename() : "<unknown>";
1894 char *formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno,
1895 options.introductionType);
1896 if (!formatted)
1897 return false;
1898 filename_ = formatted;
1899 } else if (options.filename()) {
1900 if (!setFilename(cx, options.filename()))
1901 return false;
1902 }
1903
1904 if (options.introducerFilename()) {
1905 introducerFilename_ = js_strdup(cx, options.introducerFilename());
1906 if (!introducerFilename_)
1907 return false;
1908 } else {
1909 introducerFilename_ = filename_;
1910 }
1911
1912 return true;
1913 }
1914
1915 bool
1916 ScriptSource::setFilename(ExclusiveContext *cx, const char *filename)
1917 {
1918 JS_ASSERT(!filename_);
1919 filename_ = js_strdup(cx, filename);
1920 if (!filename_)
1921 return false;
1922 return true;
1923 }
1924
1925 bool
1926 ScriptSource::setDisplayURL(ExclusiveContext *cx, const jschar *displayURL)
1927 {
1928 JS_ASSERT(displayURL);
1929 if (hasDisplayURL()) {
1930 if (cx->isJSContext() &&
1931 !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING,
1932 js_GetErrorMessage, nullptr,
1933 JSMSG_ALREADY_HAS_PRAGMA, filename_,
1934 "//# sourceURL"))
1935 {
1936 return false;
1937 }
1938 }
1939 size_t len = js_strlen(displayURL) + 1;
1940 if (len == 1)
1941 return true;
1942 displayURL_ = js_strdup(cx, displayURL);
1943 if (!displayURL_)
1944 return false;
1945 return true;
1946 }
1947
1948 const jschar *
1949 ScriptSource::displayURL()
1950 {
1951 JS_ASSERT(hasDisplayURL());
1952 return displayURL_;
1953 }
1954
1955 bool
1956 ScriptSource::setSourceMapURL(ExclusiveContext *cx, const jschar *sourceMapURL)
1957 {
1958 JS_ASSERT(sourceMapURL);
1959 if (hasSourceMapURL()) {
1960 if (cx->isJSContext() &&
1961 !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING,
1962 js_GetErrorMessage, nullptr,
1963 JSMSG_ALREADY_HAS_PRAGMA, filename_,
1964 "//# sourceMappingURL"))
1965 {
1966 return false;
1967 }
1968 }
1969
1970 size_t len = js_strlen(sourceMapURL) + 1;
1971 if (len == 1)
1972 return true;
1973 sourceMapURL_ = js_strdup(cx, sourceMapURL);
1974 if (!sourceMapURL_)
1975 return false;
1976 return true;
1977 }
1978
1979 const jschar *
1980 ScriptSource::sourceMapURL()
1981 {
1982 JS_ASSERT(hasSourceMapURL());
1983 return sourceMapURL_;
1984 }
1985
1986 /*
1987 * Shared script data management.
1988 */
1989
1990 SharedScriptData *
1991 js::SharedScriptData::new_(ExclusiveContext *cx, uint32_t codeLength,
1992 uint32_t srcnotesLength, uint32_t natoms)
1993 {
1994 /*
1995 * Ensure the atoms are aligned, as some architectures don't allow unaligned
1996 * access.
1997 */
1998 const uint32_t pointerSize = sizeof(JSAtom *);
1999 const uint32_t pointerMask = pointerSize - 1;
2000 const uint32_t dataOffset = offsetof(SharedScriptData, data);
2001 uint32_t baseLength = codeLength + srcnotesLength;
2002 uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask;
2003 uint32_t length = baseLength + padding + pointerSize * natoms;
2004
2005 SharedScriptData *entry = (SharedScriptData *)cx->malloc_(length + dataOffset);
2006 if (!entry)
2007 return nullptr;
2008
2009 entry->length = length;
2010 entry->natoms = natoms;
2011 entry->marked = false;
2012 memset(entry->data + baseLength, 0, padding);
2013
2014 /*
2015 * Call constructors to initialize the storage that will be accessed as a
2016 * HeapPtrAtom array via atoms().
2017 */
2018 HeapPtrAtom *atoms = entry->atoms();
2019 JS_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(JSAtom *) == 0);
2020 for (unsigned i = 0; i < natoms; ++i)
2021 new (&atoms[i]) HeapPtrAtom();
2022
2023 return entry;
2024 }
2025
2026 /*
2027 * Takes ownership of its *ssd parameter and either adds it into the runtime's
2028 * ScriptDataTable or frees it if a matching entry already exists.
2029 *
2030 * Sets the |code| and |atoms| fields on the given JSScript.
2031 */
2032 static bool
2033 SaveSharedScriptData(ExclusiveContext *cx, Handle<JSScript *> script, SharedScriptData *ssd,
2034 uint32_t nsrcnotes)
2035 {
2036 ASSERT(script != nullptr);
2037 ASSERT(ssd != nullptr);
2038
2039 AutoLockForExclusiveAccess lock(cx);
2040
2041 ScriptBytecodeHasher::Lookup l(ssd);
2042
2043 ScriptDataTable::AddPtr p = cx->scriptDataTable().lookupForAdd(l);
2044 if (p) {
2045 js_free(ssd);
2046 ssd = *p;
2047 } else {
2048 if (!cx->scriptDataTable().add(p, ssd)) {
2049 script->setCode(nullptr);
2050 script->atoms = nullptr;
2051 js_free(ssd);
2052 js_ReportOutOfMemory(cx);
2053 return false;
2054 }
2055 }
2056
2057 #ifdef JSGC_INCREMENTAL
2058 /*
2059 * During the IGC we need to ensure that bytecode is marked whenever it is
2060 * accessed even if the bytecode was already in the table: at this point
2061 * old scripts or exceptions pointing to the bytecode may no longer be
2062 * reachable. This is effectively a read barrier.
2063 */
2064 if (cx->isJSContext()) {
2065 JSRuntime *rt = cx->asJSContext()->runtime();
2066 if (JS::IsIncrementalGCInProgress(rt) && rt->gcIsFull)
2067 ssd->marked = true;
2068 }
2069 #endif
2070
2071 script->setCode(ssd->data);
2072 script->atoms = ssd->atoms();
2073 return true;
2074 }
2075
2076 static inline void
2077 MarkScriptData(JSRuntime *rt, const jsbytecode *bytecode)
2078 {
2079 /*
2080 * As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of
2081 * a GC. Since SweepScriptBytecodes is only called during a full gc,
2082 * to preserve this invariant, only mark during a full gc.
2083 */
2084 if (rt->gcIsFull)
2085 SharedScriptData::fromBytecode(bytecode)->marked = true;
2086 }
2087
2088 void
2089 js::UnmarkScriptData(JSRuntime *rt)
2090 {
2091 JS_ASSERT(rt->gcIsFull);
2092 ScriptDataTable &table = rt->scriptDataTable();
2093 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
2094 SharedScriptData *entry = e.front();
2095 entry->marked = false;
2096 }
2097 }
2098
2099 void
2100 js::SweepScriptData(JSRuntime *rt)
2101 {
2102 JS_ASSERT(rt->gcIsFull);
2103 ScriptDataTable &table = rt->scriptDataTable();
2104
2105 if (rt->keepAtoms())
2106 return;
2107
2108 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
2109 SharedScriptData *entry = e.front();
2110 if (!entry->marked) {
2111 js_free(entry);
2112 e.removeFront();
2113 }
2114 }
2115 }
2116
2117 void
2118 js::FreeScriptData(JSRuntime *rt)
2119 {
2120 ScriptDataTable &table = rt->scriptDataTable();
2121 if (!table.initialized())
2122 return;
2123
2124 for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront())
2125 js_free(e.front());
2126
2127 table.clear();
2128 }
2129
2130 /*
2131 * JSScript::data and SharedScriptData::data have complex,
2132 * manually-controlled, memory layouts.
2133 *
2134 * JSScript::data begins with some optional array headers. They are optional
2135 * because they often aren't needed, i.e. the corresponding arrays often have
2136 * zero elements. Each header has a bit in JSScript::hasArrayBits that
2137 * indicates if it's present within |data|; from this the offset of each
2138 * present array header can be computed. Each header has an accessor function
2139 * in JSScript that encapsulates this offset computation.
2140 *
2141 * Array type Array elements Accessor
2142 * ---------- -------------- --------
2143 * ConstArray Consts consts()
2144 * ObjectArray Objects objects()
2145 * ObjectArray Regexps regexps()
2146 * TryNoteArray Try notes trynotes()
2147 * BlockScopeArray Scope notes blockScopes()
2148 *
2149 * Then are the elements of several arrays.
2150 * - Most of these arrays have headers listed above (if present). For each of
2151 * these, the array pointer and the array length is stored in the header.
2152 * - The remaining arrays have pointers and lengths that are stored directly in
2153 * JSScript. This is because, unlike the others, they are nearly always
2154 * non-zero length and so the optional-header space optimization isn't
2155 * worthwhile.
2156 *
2157 * Array elements Pointed to by Length
2158 * -------------- ------------- ------
2159 * Consts consts()->vector consts()->length
2160 * Objects objects()->vector objects()->length
2161 * Regexps regexps()->vector regexps()->length
2162 * Try notes trynotes()->vector trynotes()->length
2163 * Scope notes blockScopes()->vector blockScopes()->length
2164 *
2165 * IMPORTANT: This layout has two key properties.
2166 * - It ensures that everything has sufficient alignment; in particular, the
2167 * consts() elements need jsval alignment.
2168 * - It ensures there are no gaps between elements, which saves space and makes
2169 * manual layout easy. In particular, in the second part, arrays with larger
2170 * elements precede arrays with smaller elements.
2171 *
2172 * SharedScriptData::data contains data that can be shared within a
2173 * runtime. These items' layout is manually controlled to make it easier to
2174 * manage both during (temporary) allocation and during matching against
2175 * existing entries in the runtime. As the jsbytecode has to come first to
2176 * enable lookup by bytecode identity, SharedScriptData::data, the atoms part
2177 * has to manually be aligned sufficiently by adding padding after the notes
2178 * part.
2179 *
2180 * Array elements Pointed to by Length
2181 * -------------- ------------- ------
2182 * jsbytecode code length
2183 * jsscrnote notes() numNotes()
2184 * Atoms atoms natoms
2185 *
2186 * The following static assertions check JSScript::data's alignment properties.
2187 */
2188
2189 #define KEEPS_JSVAL_ALIGNMENT(T) \
2190 (JS_ALIGNMENT_OF(jsval) % JS_ALIGNMENT_OF(T) == 0 && \
2191 sizeof(T) % sizeof(jsval) == 0)
2192
2193 #define HAS_JSVAL_ALIGNMENT(T) \
2194 (JS_ALIGNMENT_OF(jsval) == JS_ALIGNMENT_OF(T) && \
2195 sizeof(T) == sizeof(jsval))
2196
2197 #define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \
2198 (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0)
2199
2200 /*
2201 * These assertions ensure that there is no padding between the array headers,
2202 * and also that the consts() elements (which follow immediately afterward) are
2203 * jsval-aligned. (There is an assumption that |data| itself is jsval-aligned;
2204 * we check this below).
2205 */
2206 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray));
2207 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray)); /* there are two of these */
2208 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray));
2209 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray));
2210
2211 /* These assertions ensure there is no padding required between array elements. */
2212 JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue));
2213 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, HeapPtrObject));
2214 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, HeapPtrObject));
2215 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote));
2216 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t));
2217 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t));
2218
2219 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, BlockScopeNote));
2220 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, BlockScopeNote));
2221 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, BlockScopeNote));
2222 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, BlockScopeNote));
2223 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, uint32_t));
2224
2225 static inline size_t
2226 ScriptDataSize(uint32_t nbindings, uint32_t nconsts, uint32_t nobjects, uint32_t nregexps,
2227 uint32_t ntrynotes, uint32_t nblockscopes)
2228 {
2229 size_t size = 0;
2230
2231 if (nconsts != 0)
2232 size += sizeof(ConstArray) + nconsts * sizeof(Value);
2233 if (nobjects != 0)
2234 size += sizeof(ObjectArray) + nobjects * sizeof(JSObject *);
2235 if (nregexps != 0)
2236 size += sizeof(ObjectArray) + nregexps * sizeof(JSObject *);
2237 if (ntrynotes != 0)
2238 size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
2239 if (nblockscopes != 0)
2240 size += sizeof(BlockScopeArray) + nblockscopes * sizeof(BlockScopeNote);
2241
2242 if (nbindings != 0) {
2243 // Make sure bindings are sufficiently aligned.
2244 size = JS_ROUNDUP(size, JS_ALIGNMENT_OF(Binding)) + nbindings * sizeof(Binding);
2245 }
2246
2247 return size;
2248 }
2249
2250 void
2251 JSScript::initCompartment(ExclusiveContext *cx)
2252 {
2253 compartment_ = cx->compartment_;
2254 }
2255
2256 JSScript *
2257 JSScript::Create(ExclusiveContext *cx, HandleObject enclosingScope, bool savedCallerFun,
2258 const ReadOnlyCompileOptions &options, unsigned staticLevel,
2259 HandleObject sourceObject, uint32_t bufStart, uint32_t bufEnd)
2260 {
2261 JS_ASSERT(bufStart <= bufEnd);
2262
2263 RootedScript script(cx, js_NewGCScript(cx));
2264 if (!script)
2265 return nullptr;
2266
2267 PodZero(script.get());
2268 new (&script->bindings) Bindings;
2269
2270 script->enclosingScopeOrOriginalFunction_ = enclosingScope;
2271 script->savedCallerFun_ = savedCallerFun;
2272 script->initCompartment(cx);
2273
2274 script->compileAndGo_ = options.compileAndGo;
2275 script->selfHosted_ = options.selfHostingMode;
2276 script->noScriptRval_ = options.noScriptRval;
2277
2278 script->version = options.version;
2279 JS_ASSERT(script->getVersion() == options.version); // assert that no overflow occurred
2280
2281 // This is an unsigned-to-uint16_t conversion, test for too-high values.
2282 // In practice, recursion in Parser and/or BytecodeEmitter will blow the
2283 // stack if we nest functions more than a few hundred deep, so this will
2284 // never trigger. Oh well.
2285 if (staticLevel > UINT16_MAX) {
2286 if (cx->isJSContext()) {
2287 JS_ReportErrorNumber(cx->asJSContext(),
2288 js_GetErrorMessage, nullptr, JSMSG_TOO_DEEP, js_function_str);
2289 }
2290 return nullptr;
2291 }
2292 script->staticLevel_ = uint16_t(staticLevel);
2293
2294 script->setSourceObject(sourceObject);
2295 script->sourceStart_ = bufStart;
2296 script->sourceEnd_ = bufEnd;
2297
2298 return script;
2299 }
2300
2301 static inline uint8_t *
2302 AllocScriptData(ExclusiveContext *cx, size_t size)
2303 {
2304 uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
2305 if (!data)
2306 return nullptr;
2307
2308 // All script data is optional, so size might be 0. In that case, we don't care about alignment.
2309 JS_ASSERT(size == 0 || size_t(data) % sizeof(Value) == 0);
2310 return data;
2311 }
2312
2313 /* static */ bool
2314 JSScript::partiallyInit(ExclusiveContext *cx, HandleScript script, uint32_t nconsts,
2315 uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes,
2316 uint32_t nblockscopes, uint32_t nTypeSets)
2317 {
2318 size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes,
2319 nblockscopes);
2320 if (size > 0) {
2321 script->data = AllocScriptData(cx, size);
2322 if (!script->data)
2323 return false;
2324 } else {
2325 script->data = nullptr;
2326 }
2327 script->dataSize_ = size;
2328
2329 JS_ASSERT(nTypeSets <= UINT16_MAX);
2330 script->nTypeSets_ = uint16_t(nTypeSets);
2331
2332 uint8_t *cursor = script->data;
2333 if (nconsts != 0) {
2334 script->setHasArray(CONSTS);
2335 cursor += sizeof(ConstArray);
2336 }
2337 if (nobjects != 0) {
2338 script->setHasArray(OBJECTS);
2339 cursor += sizeof(ObjectArray);
2340 }
2341 if (nregexps != 0) {
2342 script->setHasArray(REGEXPS);
2343 cursor += sizeof(ObjectArray);
2344 }
2345 if (ntrynotes != 0) {
2346 script->setHasArray(TRYNOTES);
2347 cursor += sizeof(TryNoteArray);
2348 }
2349 if (nblockscopes != 0) {
2350 script->setHasArray(BLOCK_SCOPES);
2351 cursor += sizeof(BlockScopeArray);
2352 }
2353
2354 if (nconsts != 0) {
2355 JS_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
2356 script->consts()->length = nconsts;
2357 script->consts()->vector = (HeapValue *)cursor;
2358 cursor += nconsts * sizeof(script->consts()->vector[0]);
2359 }
2360
2361 if (nobjects != 0) {
2362 script->objects()->length = nobjects;
2363 script->objects()->vector = (HeapPtr<JSObject> *)cursor;
2364 cursor += nobjects * sizeof(script->objects()->vector[0]);
2365 }
2366
2367 if (nregexps != 0) {
2368 script->regexps()->length = nregexps;
2369 script->regexps()->vector = (HeapPtr<JSObject> *)cursor;
2370 cursor += nregexps * sizeof(script->regexps()->vector[0]);
2371 }
2372
2373 if (ntrynotes != 0) {
2374 script->trynotes()->length = ntrynotes;
2375 script->trynotes()->vector = reinterpret_cast<JSTryNote *>(cursor);
2376 size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
2377 #ifdef DEBUG
2378 memset(cursor, 0, vectorSize);
2379 #endif
2380 cursor += vectorSize;
2381 }
2382
2383 if (nblockscopes != 0) {
2384 script->blockScopes()->length = nblockscopes;
2385 script->blockScopes()->vector = reinterpret_cast<BlockScopeNote *>(cursor);
2386 size_t vectorSize = nblockscopes * sizeof(script->blockScopes()->vector[0]);
2387 #ifdef DEBUG
2388 memset(cursor, 0, vectorSize);
2389 #endif
2390 cursor += vectorSize;
2391 }
2392
2393 if (script->bindings.count() != 0) {
2394 // Make sure bindings are sufficiently aligned.
2395 cursor = reinterpret_cast<uint8_t*>
2396 (JS_ROUNDUP(reinterpret_cast<uintptr_t>(cursor), JS_ALIGNMENT_OF(Binding)));
2397 }
2398 cursor = script->bindings.switchToScriptStorage(reinterpret_cast<Binding *>(cursor));
2399
2400 JS_ASSERT(cursor == script->data + size);
2401 return true;
2402 }
2403
2404 /* static */ bool
2405 JSScript::fullyInitTrivial(ExclusiveContext *cx, Handle<JSScript*> script)
2406 {
2407 if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0))
2408 return false;
2409
2410 SharedScriptData *ssd = SharedScriptData::new_(cx, 1, 1, 0);
2411 if (!ssd)
2412 return false;
2413
2414 ssd->data[0] = JSOP_RETRVAL;
2415 ssd->data[1] = SRC_NULL;
2416 script->setLength(1);
2417 return SaveSharedScriptData(cx, script, ssd, 1);
2418 }
2419
2420 /* static */ bool
2421 JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, BytecodeEmitter *bce)
2422 {
2423 /* The counts of indexed things must be checked during code generation. */
2424 JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
2425 JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
2426 JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
2427
2428 uint32_t mainLength = bce->offset();
2429 uint32_t prologLength = bce->prologOffset();
2430 uint32_t nsrcnotes;
2431 if (!FinishTakingSrcNotes(cx, bce, &nsrcnotes))
2432 return false;
2433 uint32_t natoms = bce->atomIndices->count();
2434 if (!partiallyInit(cx, script,
2435 bce->constList.length(), bce->objectList.length, bce->regexpList.length,
2436 bce->tryNoteList.length(), bce->blockScopeList.length(), bce->typesetCount))
2437 {
2438 return false;
2439 }
2440
2441 JS_ASSERT(script->mainOffset() == 0);
2442 script->mainOffset_ = prologLength;
2443
2444 script->lineno_ = bce->firstLine;
2445
2446 script->setLength(prologLength + mainLength);
2447 script->natoms_ = natoms;
2448 SharedScriptData *ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms);
2449 if (!ssd)
2450 return false;
2451
2452 jsbytecode *code = ssd->data;
2453 PodCopy<jsbytecode>(code, bce->prolog.code.begin(), prologLength);
2454 PodCopy<jsbytecode>(code + prologLength, bce->code().begin(), mainLength);
2455 CopySrcNotes(bce, (jssrcnote *)(code + script->length()), nsrcnotes);
2456 InitAtomMap(bce->atomIndices.getMap(), ssd->atoms());
2457
2458 if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
2459 return false;
2460
2461 FunctionBox *funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr;
2462
2463 if (bce->constList.length() != 0)
2464 bce->constList.finish(script->consts());
2465 if (bce->objectList.length != 0)
2466 bce->objectList.finish(script->objects());
2467 if (bce->regexpList.length != 0)
2468 bce->regexpList.finish(script->regexps());
2469 if (bce->tryNoteList.length() != 0)
2470 bce->tryNoteList.finish(script->trynotes());
2471 if (bce->blockScopeList.length() != 0)
2472 bce->blockScopeList.finish(script->blockScopes());
2473 script->strict_ = bce->sc->strict;
2474 script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
2475 script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
2476 script->funHasExtensibleScope_ = funbox ? funbox->hasExtensibleScope() : false;
2477 script->funNeedsDeclEnvObject_ = funbox ? funbox->needsDeclEnvObject() : false;
2478 script->hasSingletons_ = bce->hasSingletons;
2479
2480 if (funbox) {
2481 if (funbox->argumentsHasLocalBinding()) {
2482 // This must precede the script->bindings.transfer() call below
2483 script->setArgumentsHasVarBinding();
2484 if (funbox->definitelyNeedsArgsObj())
2485 script->setNeedsArgsObj(true);
2486 } else {
2487 JS_ASSERT(!funbox->definitelyNeedsArgsObj());
2488 }
2489
2490 script->funLength_ = funbox->length;
2491 }
2492
2493 RootedFunction fun(cx, nullptr);
2494 if (funbox) {
2495 JS_ASSERT(!bce->script->noScriptRval());
2496 script->isGeneratorExp_ = funbox->inGenexpLambda;
2497 script->setGeneratorKind(funbox->generatorKind());
2498 script->setFunction(funbox->function());
2499 }
2500
2501 // The call to nfixed() depends on the above setFunction() call.
2502 if (UINT32_MAX - script->nfixed() < bce->maxStackDepth) {
2503 bce->reportError(nullptr, JSMSG_NEED_DIET, "script");
2504 return false;
2505 }
2506 script->nslots_ = script->nfixed() + bce->maxStackDepth;
2507
2508 for (unsigned i = 0, n = script->bindings.numArgs(); i < n; ++i) {
2509 if (script->formalIsAliased(i)) {
2510 script->funHasAnyAliasedFormal_ = true;
2511 break;
2512 }
2513 }
2514
2515 return true;
2516 }
2517
2518 size_t
2519 JSScript::computedSizeOfData() const
2520 {
2521 return dataSize();
2522 }
2523
2524 size_t
2525 JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const
2526 {
2527 return mallocSizeOf(data);
2528 }
2529
2530 size_t
2531 JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const
2532 {
2533 return types->sizeOfIncludingThis(mallocSizeOf);
2534 }
2535
2536 /*
2537 * Nb: srcnotes are variable-length. This function computes the number of
2538 * srcnote *slots*, which may be greater than the number of srcnotes.
2539 */
2540 uint32_t
2541 JSScript::numNotes()
2542 {
2543 jssrcnote *sn;
2544 jssrcnote *notes_ = notes();
2545 for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
2546 continue;
2547 return sn - notes_ + 1; /* +1 for the terminator */
2548 }
2549
2550 js::GlobalObject&
2551 JSScript::uninlinedGlobal() const
2552 {
2553 return global();
2554 }
2555
2556 void
2557 js::CallNewScriptHook(JSContext *cx, HandleScript script, HandleFunction fun)
2558 {
2559 if (script->selfHosted())
2560 return;
2561
2562 JS_ASSERT(!script->isActiveEval());
2563 if (JSNewScriptHook hook = cx->runtime()->debugHooks.newScriptHook) {
2564 AutoKeepAtoms keepAtoms(cx->perThreadData);
2565 hook(cx, script->filename(), script->lineno(), script, fun,
2566 cx->runtime()->debugHooks.newScriptHookData);
2567 }
2568 }
2569
2570 void
2571 js::CallDestroyScriptHook(FreeOp *fop, JSScript *script)
2572 {
2573 if (script->selfHosted())
2574 return;
2575
2576 // The hook will only call into JS if a GC is not running.
2577 if (JSDestroyScriptHook hook = fop->runtime()->debugHooks.destroyScriptHook)
2578 hook(fop, script, fop->runtime()->debugHooks.destroyScriptHookData);
2579 script->clearTraps(fop);
2580 }
2581
2582 void
2583 JSScript::finalize(FreeOp *fop)
2584 {
2585 // NOTE: this JSScript may be partially initialized at this point. E.g. we
2586 // may have created it and partially initialized it with
2587 // JSScript::Create(), but not yet finished initializing it with
2588 // fullyInitFromEmitter() or fullyInitTrivial().
2589
2590 CallDestroyScriptHook(fop, this);
2591 fop->runtime()->spsProfiler.onScriptFinalized(this);
2592
2593 if (types)
2594 types->destroy();
2595
2596 #ifdef JS_ION
2597 jit::DestroyIonScripts(fop, this);
2598 #endif
2599
2600 destroyScriptCounts(fop);
2601 destroyDebugScript(fop);
2602
2603 if (data) {
2604 JS_POISON(data, 0xdb, computedSizeOfData());
2605 fop->free_(data);
2606 }
2607
2608 fop->runtime()->lazyScriptCache.remove(this);
2609 }
2610
2611 static const uint32_t GSN_CACHE_THRESHOLD = 100;
2612
2613 void
2614 GSNCache::purge()
2615 {
2616 code = nullptr;
2617 if (map.initialized())
2618 map.finish();
2619 }
2620
2621 jssrcnote *
2622 js::GetSrcNote(GSNCache &cache, JSScript *script, jsbytecode *pc)
2623 {
2624 size_t target = pc - script->code();
2625 if (target >= script->length())
2626 return nullptr;
2627
2628 if (cache.code == script->code()) {
2629 JS_ASSERT(cache.map.initialized());
2630 GSNCache::Map::Ptr p = cache.map.lookup(pc);
2631 return p ? p->value() : nullptr;
2632 }
2633
2634 size_t offset = 0;
2635 jssrcnote *result;
2636 for (jssrcnote *sn = script->notes(); ; sn = SN_NEXT(sn)) {
2637 if (SN_IS_TERMINATOR(sn)) {
2638 result = nullptr;
2639 break;
2640 }
2641 offset += SN_DELTA(sn);
2642 if (offset == target && SN_IS_GETTABLE(sn)) {
2643 result = sn;
2644 break;
2645 }
2646 }
2647
2648 if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) {
2649 unsigned nsrcnotes = 0;
2650 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
2651 sn = SN_NEXT(sn)) {
2652 if (SN_IS_GETTABLE(sn))
2653 ++nsrcnotes;
2654 }
2655 if (cache.code) {
2656 JS_ASSERT(cache.map.initialized());
2657 cache.map.finish();
2658 cache.code = nullptr;
2659 }
2660 if (cache.map.init(nsrcnotes)) {
2661 pc = script->code();
2662 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn);
2663 sn = SN_NEXT(sn)) {
2664 pc += SN_DELTA(sn);
2665 if (SN_IS_GETTABLE(sn))
2666 JS_ALWAYS_TRUE(cache.map.put(pc, sn));
2667 }
2668 cache.code = script->code();
2669 }
2670 }
2671
2672 return result;
2673 }
2674
2675 jssrcnote *
2676 js_GetSrcNote(JSContext *cx, JSScript *script, jsbytecode *pc)
2677 {
2678 return GetSrcNote(cx->runtime()->gsnCache, script, pc);
2679 }
2680
2681 unsigned
2682 js::PCToLineNumber(unsigned startLine, jssrcnote *notes, jsbytecode *code, jsbytecode *pc,
2683 unsigned *columnp)
2684 {
2685 unsigned lineno = startLine;
2686 unsigned column = 0;
2687
2688 /*
2689 * Walk through source notes accumulating their deltas, keeping track of
2690 * line-number notes, until we pass the note for pc's offset within
2691 * script->code.
2692 */
2693 ptrdiff_t offset = 0;
2694 ptrdiff_t target = pc - code;
2695 for (jssrcnote *sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
2696 offset += SN_DELTA(sn);
2697 SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
2698 if (type == SRC_SETLINE) {
2699 if (offset <= target)
2700 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
2701 column = 0;
2702 } else if (type == SRC_NEWLINE) {
2703 if (offset <= target)
2704 lineno++;
2705 column = 0;
2706 }
2707
2708 if (offset > target)
2709 break;
2710
2711 if (type == SRC_COLSPAN) {
2712 ptrdiff_t colspan = js_GetSrcNoteOffset(sn, 0);
2713
2714 if (colspan >= SN_COLSPAN_DOMAIN / 2)
2715 colspan -= SN_COLSPAN_DOMAIN;
2716 JS_ASSERT(ptrdiff_t(column) + colspan >= 0);
2717 column += colspan;
2718 }
2719 }
2720
2721 if (columnp)
2722 *columnp = column;
2723
2724 return lineno;
2725 }
2726
2727 unsigned
2728 js::PCToLineNumber(JSScript *script, jsbytecode *pc, unsigned *columnp)
2729 {
2730 /* Cope with InterpreterFrame.pc value prior to entering Interpret. */
2731 if (!pc)
2732 return 0;
2733
2734 return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp);
2735 }
2736
2737 jsbytecode *
2738 js_LineNumberToPC(JSScript *script, unsigned target)
2739 {
2740 ptrdiff_t offset = 0;
2741 ptrdiff_t best = -1;
2742 unsigned lineno = script->lineno();
2743 unsigned bestdiff = SN_MAX_OFFSET;
2744 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
2745 /*
2746 * Exact-match only if offset is not in the prolog; otherwise use
2747 * nearest greater-or-equal line number match.
2748 */
2749 if (lineno == target && offset >= ptrdiff_t(script->mainOffset()))
2750 goto out;
2751 if (lineno >= target) {
2752 unsigned diff = lineno - target;
2753 if (diff < bestdiff) {
2754 bestdiff = diff;
2755 best = offset;
2756 }
2757 }
2758 offset += SN_DELTA(sn);
2759 SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
2760 if (type == SRC_SETLINE) {
2761 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
2762 } else if (type == SRC_NEWLINE) {
2763 lineno++;
2764 }
2765 }
2766 if (best >= 0)
2767 offset = best;
2768 out:
2769 return script->offsetToPC(offset);
2770 }
2771
2772 JS_FRIEND_API(unsigned)
2773 js_GetScriptLineExtent(JSScript *script)
2774 {
2775 unsigned lineno = script->lineno();
2776 unsigned maxLineNo = lineno;
2777 for (jssrcnote *sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
2778 SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
2779 if (type == SRC_SETLINE)
2780 lineno = (unsigned) js_GetSrcNoteOffset(sn, 0);
2781 else if (type == SRC_NEWLINE)
2782 lineno++;
2783
2784 if (maxLineNo < lineno)
2785 maxLineNo = lineno;
2786 }
2787
2788 return 1 + maxLineNo - script->lineno();
2789 }
2790
2791 void
2792 js::DescribeScriptedCallerForCompilation(JSContext *cx, MutableHandleScript maybeScript,
2793 const char **file, unsigned *linenop,
2794 uint32_t *pcOffset, JSPrincipals **origin,
2795 LineOption opt)
2796 {
2797 if (opt == CALLED_FROM_JSOP_EVAL) {
2798 jsbytecode *pc = nullptr;
2799 maybeScript.set(cx->currentScript(&pc));
2800 JS_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_SPREADEVAL);
2801 JS_ASSERT(*(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
2802 : JSOP_SPREADEVAL_LENGTH)) == JSOP_LINENO);
2803 *file = maybeScript->filename();
2804 *linenop = GET_UINT16(pc + (JSOp(*pc) == JSOP_EVAL ? JSOP_EVAL_LENGTH
2805 : JSOP_SPREADEVAL_LENGTH));
2806 *pcOffset = pc - maybeScript->code();
2807 *origin = maybeScript->originPrincipals();
2808 return;
2809 }
2810
2811 NonBuiltinFrameIter iter(cx);
2812
2813 if (iter.done()) {
2814 maybeScript.set(nullptr);
2815 *file = nullptr;
2816 *linenop = 0;
2817 *pcOffset = 0;
2818 *origin = cx->compartment()->principals;
2819 return;
2820 }
2821
2822 *file = iter.scriptFilename();
2823 *linenop = iter.computeLine();
2824 *origin = iter.originPrincipals();
2825
2826 // These values are only used for introducer fields which are debugging
2827 // information and can be safely left null for asm.js frames.
2828 if (iter.hasScript()) {
2829 maybeScript.set(iter.script());
2830 *pcOffset = iter.pc() - maybeScript->code();
2831 } else {
2832 maybeScript.set(nullptr);
2833 *pcOffset = 0;
2834 }
2835 }
2836
2837 template <class T>
2838 static inline T *
2839 Rebase(JSScript *dst, JSScript *src, T *srcp)
2840 {
2841 size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
2842 return reinterpret_cast<T *>(dst->data + off);
2843 }
2844
2845 JSScript *
2846 js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src,
2847 NewObjectKind newKind /* = GenericObject */)
2848 {
2849 /* NB: Keep this in sync with XDRScript. */
2850
2851 /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */
2852 JS_ASSERT(!src->sourceObject()->isMarked(gc::GRAY));
2853
2854 uint32_t nconsts = src->hasConsts() ? src->consts()->length : 0;
2855 uint32_t nobjects = src->hasObjects() ? src->objects()->length : 0;
2856 uint32_t nregexps = src->hasRegexps() ? src->regexps()->length : 0;
2857 uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
2858 uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
2859
2860 /* Script data */
2861
2862 size_t size = src->dataSize();
2863 uint8_t *data = AllocScriptData(cx, size);
2864 if (!data)
2865 return nullptr;
2866
2867 /* Bindings */
2868
2869 Rooted<Bindings> bindings(cx);
2870 InternalHandle<Bindings*> bindingsHandle =
2871 InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
2872 if (!Bindings::clone(cx, bindingsHandle, data, src))
2873 return nullptr;
2874
2875 /* Objects */
2876
2877 AutoObjectVector objects(cx);
2878 if (nobjects != 0) {
2879 HeapPtrObject *vector = src->objects()->vector;
2880 for (unsigned i = 0; i < nobjects; i++) {
2881 RootedObject obj(cx, vector[i]);
2882 RootedObject clone(cx);
2883 if (obj->is<NestedScopeObject>()) {
2884 Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>());
2885
2886 RootedObject enclosingScope(cx);
2887 if (NestedScopeObject *enclosingBlock = innerBlock->enclosingNestedScope())
2888 enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
2889 else
2890 enclosingScope = fun;
2891
2892 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
2893 } else if (obj->is<JSFunction>()) {
2894 RootedFunction innerFun(cx, &obj->as<JSFunction>());
2895 if (innerFun->isNative()) {
2896 assertSameCompartment(cx, innerFun);
2897 clone = innerFun;
2898 } else {
2899 if (innerFun->isInterpretedLazy()) {
2900 AutoCompartment ac(cx, innerFun);
2901 if (!innerFun->getOrCreateScript(cx))
2902 return nullptr;
2903 }
2904 RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
2905 StaticScopeIter<CanGC> ssi(cx, staticScope);
2906 RootedObject enclosingScope(cx);
2907 if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::FUNCTION)
2908 enclosingScope = fun;
2909 else if (ssi.type() == StaticScopeIter<CanGC>::BLOCK)
2910 enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
2911 else
2912 enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
2913
2914 clone = CloneFunctionAndScript(cx, enclosingScope, innerFun);
2915 }
2916 } else {
2917 /*
2918 * Clone object literals emitted for the JSOP_NEWOBJECT opcode. We only emit that
2919 * instead of the less-optimized JSOP_NEWINIT for self-hosted code or code compiled
2920 * with JSOPTION_COMPILE_N_GO set. As we don't clone the latter type of code, this
2921 * case should only ever be hit when cloning objects from self-hosted code.
2922 */
2923 clone = CloneObjectLiteral(cx, cx->global(), obj);
2924 }
2925 if (!clone || !objects.append(clone))
2926 return nullptr;
2927 }
2928 }
2929
2930 /* RegExps */
2931
2932 AutoObjectVector regexps(cx);
2933 for (unsigned i = 0; i < nregexps; i++) {
2934 HeapPtrObject *vector = src->regexps()->vector;
2935 for (unsigned i = 0; i < nregexps; i++) {
2936 JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
2937 if (!clone || !regexps.append(clone))
2938 return nullptr;
2939 }
2940 }
2941
2942 /*
2943 * Wrap the script source object as needed. Self-hosted scripts may be
2944 * in another runtime, so lazily create a new script source object to
2945 * use for them.
2946 */
2947 RootedObject sourceObject(cx);
2948 if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
2949 if (!cx->compartment()->selfHostingScriptSource) {
2950 CompileOptions options(cx);
2951 FillSelfHostingCompileOptions(options);
2952
2953 ScriptSourceObject *obj = frontend::CreateScriptSourceObject(cx, options);
2954 if (!obj)
2955 return nullptr;
2956 cx->compartment()->selfHostingScriptSource = obj;
2957 }
2958 sourceObject = cx->compartment()->selfHostingScriptSource;
2959 } else {
2960 sourceObject = src->sourceObject();
2961 if (!cx->compartment()->wrap(cx, &sourceObject))
2962 return nullptr;
2963 }
2964
2965 /* Now that all fallible allocation is complete, create the GC thing. */
2966
2967 CompileOptions options(cx);
2968 options.setOriginPrincipals(src->originPrincipals())
2969 .setCompileAndGo(src->compileAndGo())
2970 .setSelfHostingMode(src->selfHosted())
2971 .setNoScriptRval(src->noScriptRval())
2972 .setVersion(src->getVersion());
2973
2974 RootedScript dst(cx, JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
2975 options, src->staticLevel(),
2976 sourceObject, src->sourceStart(), src->sourceEnd()));
2977 if (!dst) {
2978 js_free(data);
2979 return nullptr;
2980 }
2981
2982 dst->bindings = bindings;
2983
2984 /* This assignment must occur before all the Rebase calls. */
2985 dst->data = data;
2986 dst->dataSize_ = size;
2987 memcpy(data, src->data, size);
2988
2989 /* Script filenames, bytecodes and atoms are runtime-wide. */
2990 dst->setCode(src->code());
2991 dst->atoms = src->atoms;
2992
2993 dst->setLength(src->length());
2994 dst->lineno_ = src->lineno();
2995 dst->mainOffset_ = src->mainOffset();
2996 dst->natoms_ = src->natoms();
2997 dst->funLength_ = src->funLength();
2998 dst->nTypeSets_ = src->nTypeSets();
2999 dst->nslots_ = src->nslots();
3000 if (src->argumentsHasVarBinding()) {
3001 dst->setArgumentsHasVarBinding();
3002 if (src->analyzedArgsUsage())
3003 dst->setNeedsArgsObj(src->needsArgsObj());
3004 }
3005 dst->cloneHasArray(src);
3006 dst->strict_ = src->strict();
3007 dst->explicitUseStrict_ = src->explicitUseStrict();
3008 dst->bindingsAccessedDynamically_ = src->bindingsAccessedDynamically();
3009 dst->funHasExtensibleScope_ = src->funHasExtensibleScope();
3010 dst->funNeedsDeclEnvObject_ = src->funNeedsDeclEnvObject();
3011 dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
3012 dst->hasSingletons_ = src->hasSingletons();
3013 dst->treatAsRunOnce_ = src->treatAsRunOnce();
3014 dst->isGeneratorExp_ = src->isGeneratorExp();
3015 dst->setGeneratorKind(src->generatorKind());
3016
3017 /* Copy over hints. */
3018 dst->shouldInline_ = src->shouldInline();
3019 dst->shouldCloneAtCallsite_ = src->shouldCloneAtCallsite();
3020 dst->isCallsiteClone_ = src->isCallsiteClone();
3021
3022 if (nconsts != 0) {
3023 HeapValue *vector = Rebase<HeapValue>(dst, src, src->consts()->vector);
3024 dst->consts()->vector = vector;
3025 for (unsigned i = 0; i < nconsts; ++i)
3026 JS_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
3027 }
3028 if (nobjects != 0) {
3029 HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->objects()->vector);
3030 dst->objects()->vector = vector;
3031 for (unsigned i = 0; i < nobjects; ++i)
3032 vector[i].init(objects[i]);
3033 }
3034 if (nregexps != 0) {
3035 HeapPtrObject *vector = Rebase<HeapPtr<JSObject> >(dst, src, src->regexps()->vector);
3036 dst->regexps()->vector = vector;
3037 for (unsigned i = 0; i < nregexps; ++i)
3038 vector[i].init(regexps[i]);
3039 }
3040 if (ntrynotes != 0)
3041 dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
3042 if (nblockscopes != 0)
3043 dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector);
3044
3045 return dst;
3046 }
3047
3048 bool
3049 js::CloneFunctionScript(JSContext *cx, HandleFunction original, HandleFunction clone,
3050 NewObjectKind newKind /* = GenericObject */)
3051 {
3052 JS_ASSERT(clone->isInterpreted());
3053
3054 RootedScript script(cx, clone->nonLazyScript());
3055 JS_ASSERT(script);
3056 JS_ASSERT(script->compartment() == original->compartment());
3057 JS_ASSERT_IF(script->compartment() != cx->compartment(),
3058 !script->enclosingStaticScope());
3059
3060 RootedObject scope(cx, script->enclosingStaticScope());
3061
3062 clone->mutableScript().init(nullptr);
3063
3064 JSScript *cscript = CloneScript(cx, scope, clone, script, newKind);
3065 if (!cscript)
3066 return false;
3067
3068 clone->setScript(cscript);
3069 cscript->setFunction(clone);
3070
3071 script = clone->nonLazyScript();
3072 CallNewScriptHook(cx, script, clone);
3073 RootedGlobalObject global(cx, script->compileAndGo() ? &script->global() : nullptr);
3074 Debugger::onNewScript(cx, script, global);
3075
3076 return true;
3077 }
3078
3079 DebugScript *
3080 JSScript::debugScript()
3081 {
3082 JS_ASSERT(hasDebugScript_);
3083 DebugScriptMap *map = compartment()->debugScriptMap;
3084 JS_ASSERT(map);
3085 DebugScriptMap::Ptr p = map->lookup(this);
3086 JS_ASSERT(p);
3087 return p->value();
3088 }
3089
3090 DebugScript *
3091 JSScript::releaseDebugScript()
3092 {
3093 JS_ASSERT(hasDebugScript_);
3094 DebugScriptMap *map = compartment()->debugScriptMap;
3095 JS_ASSERT(map);
3096 DebugScriptMap::Ptr p = map->lookup(this);
3097 JS_ASSERT(p);
3098 DebugScript *debug = p->value();
3099 map->remove(p);
3100 hasDebugScript_ = false;
3101 return debug;
3102 }
3103
3104 void
3105 JSScript::destroyDebugScript(FreeOp *fop)
3106 {
3107 if (hasDebugScript_) {
3108 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) {
3109 if (BreakpointSite *site = getBreakpointSite(pc)) {
3110 /* Breakpoints are swept before finalization. */
3111 JS_ASSERT(site->firstBreakpoint() == nullptr);
3112 site->clearTrap(fop, nullptr, nullptr);
3113 JS_ASSERT(getBreakpointSite(pc) == nullptr);
3114 }
3115 }
3116 fop->free_(releaseDebugScript());
3117 }
3118 }
3119
3120 bool
3121 JSScript::ensureHasDebugScript(JSContext *cx)
3122 {
3123 if (hasDebugScript_)
3124 return true;
3125
3126 size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
3127 DebugScript *debug = (DebugScript *) cx->calloc_(nbytes);
3128 if (!debug)
3129 return false;
3130
3131 /* Create compartment's debugScriptMap if necessary. */
3132 DebugScriptMap *map = compartment()->debugScriptMap;
3133 if (!map) {
3134 map = cx->new_<DebugScriptMap>();
3135 if (!map || !map->init()) {
3136 js_free(debug);
3137 js_delete(map);
3138 return false;
3139 }
3140 compartment()->debugScriptMap = map;
3141 }
3142
3143 if (!map->putNew(this, debug)) {
3144 js_free(debug);
3145 return false;
3146 }
3147 hasDebugScript_ = true; // safe to set this; we can't fail after this point
3148
3149 /*
3150 * Ensure that any Interpret() instances running on this script have
3151 * interrupts enabled. The interrupts must stay enabled until the
3152 * debug state is destroyed.
3153 */
3154 for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
3155 if (iter->isInterpreter())
3156 iter->asInterpreter()->enableInterruptsIfRunning(this);
3157 }
3158
3159 return true;
3160 }
3161
3162 void
3163 JSScript::setNewStepMode(FreeOp *fop, uint32_t newValue)
3164 {
3165 DebugScript *debug = debugScript();
3166 uint32_t prior = debug->stepMode;
3167 debug->stepMode = newValue;
3168
3169 if (!prior != !newValue) {
3170 #ifdef JS_ION
3171 if (hasBaselineScript())
3172 baseline->toggleDebugTraps(this, nullptr);
3173 #endif
3174
3175 if (!stepModeEnabled() && !debug->numSites)
3176 fop->free_(releaseDebugScript());
3177 }
3178 }
3179
3180 bool
3181 JSScript::setStepModeFlag(JSContext *cx, bool step)
3182 {
3183 if (!ensureHasDebugScript(cx))
3184 return false;
3185
3186 setNewStepMode(cx->runtime()->defaultFreeOp(),
3187 (debugScript()->stepMode & stepCountMask) |
3188 (step ? stepFlagMask : 0));
3189 return true;
3190 }
3191
3192 bool
3193 JSScript::incrementStepModeCount(JSContext *cx)
3194 {
3195 assertSameCompartment(cx, this);
3196 MOZ_ASSERT(cx->compartment()->debugMode());
3197
3198 if (!ensureHasDebugScript(cx))
3199 return false;
3200
3201 DebugScript *debug = debugScript();
3202 uint32_t count = debug->stepMode & stepCountMask;
3203 MOZ_ASSERT(((count + 1) & stepCountMask) == count + 1);
3204
3205 setNewStepMode(cx->runtime()->defaultFreeOp(),
3206 (debug->stepMode & stepFlagMask) |
3207 ((count + 1) & stepCountMask));
3208 return true;
3209 }
3210
3211 void
3212 JSScript::decrementStepModeCount(FreeOp *fop)
3213 {
3214 DebugScript *debug = debugScript();
3215 uint32_t count = debug->stepMode & stepCountMask;
3216
3217 setNewStepMode(fop,
3218 (debug->stepMode & stepFlagMask) |
3219 ((count - 1) & stepCountMask));
3220 }
3221
3222 BreakpointSite *
3223 JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc)
3224 {
3225 if (!ensureHasDebugScript(cx))
3226 return nullptr;
3227
3228 DebugScript *debug = debugScript();
3229 BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)];
3230
3231 if (!site) {
3232 site = cx->runtime()->new_<BreakpointSite>(this, pc);
3233 if (!site) {
3234 js_ReportOutOfMemory(cx);
3235 return nullptr;
3236 }
3237 debug->numSites++;
3238 }
3239
3240 return site;
3241 }
3242
3243 void
3244 JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc)
3245 {
3246 DebugScript *debug = debugScript();
3247 BreakpointSite *&site = debug->breakpoints[pcToOffset(pc)];
3248 JS_ASSERT(site);
3249
3250 fop->delete_(site);
3251 site = nullptr;
3252
3253 if (--debug->numSites == 0 && !stepModeEnabled())
3254 fop->free_(releaseDebugScript());
3255 }
3256
3257 void
3258 JSScript::clearBreakpointsIn(FreeOp *fop, js::Debugger *dbg, JSObject *handler)
3259 {
3260 if (!hasAnyBreakpointsOrStepMode())
3261 return;
3262
3263 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) {
3264 BreakpointSite *site = getBreakpointSite(pc);
3265 if (site) {
3266 Breakpoint *nextbp;
3267 for (Breakpoint *bp = site->firstBreakpoint(); bp; bp = nextbp) {
3268 nextbp = bp->nextInSite();
3269 if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
3270 bp->destroy(fop);
3271 }
3272 }
3273 }
3274 }
3275
3276 bool
3277 JSScript::hasBreakpointsAt(jsbytecode *pc)
3278 {
3279 BreakpointSite *site = getBreakpointSite(pc);
3280 if (!site)
3281 return false;
3282
3283 return site->enabledCount > 0 || site->trapHandler;
3284 }
3285
3286 void
3287 JSScript::clearTraps(FreeOp *fop)
3288 {
3289 if (!hasAnyBreakpointsOrStepMode())
3290 return;
3291
3292 for (jsbytecode *pc = code(); pc < codeEnd(); pc++) {
3293 BreakpointSite *site = getBreakpointSite(pc);
3294 if (site)
3295 site->clearTrap(fop);
3296 }
3297 }
3298
3299 void
3300 JSScript::markChildren(JSTracer *trc)
3301 {
3302 // NOTE: this JSScript may be partially initialized at this point. E.g. we
3303 // may have created it and partially initialized it with
3304 // JSScript::Create(), but not yet finished initializing it with
3305 // fullyInitFromEmitter() or fullyInitTrivial().
3306
3307 JS_ASSERT_IF(trc->runtime()->gcStrictCompartmentChecking, zone()->isCollecting());
3308
3309 for (uint32_t i = 0; i < natoms(); ++i) {
3310 if (atoms[i])
3311 MarkString(trc, &atoms[i], "atom");
3312 }
3313
3314 if (hasObjects()) {
3315 ObjectArray *objarray = objects();
3316 MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
3317 }
3318
3319 if (hasRegexps()) {
3320 ObjectArray *objarray = regexps();
3321 MarkObjectRange(trc, objarray->length, objarray->vector, "objects");
3322 }
3323
3324 if (hasConsts()) {
3325 ConstArray *constarray = consts();
3326 MarkValueRange(trc, constarray->length, constarray->vector, "consts");
3327 }
3328
3329 if (sourceObject()) {
3330 JS_ASSERT(sourceObject()->compartment() == compartment());
3331 MarkObject(trc, &sourceObject_, "sourceObject");
3332 }
3333
3334 if (functionNonDelazifying())
3335 MarkObject(trc, &function_, "function");
3336
3337 if (enclosingScopeOrOriginalFunction_)
3338 MarkObject(trc, &enclosingScopeOrOriginalFunction_, "enclosing");
3339
3340 if (maybeLazyScript())
3341 MarkLazyScriptUnbarriered(trc, &lazyScript, "lazyScript");
3342
3343 if (IS_GC_MARKING_TRACER(trc)) {
3344 compartment()->mark();
3345
3346 if (code())
3347 MarkScriptData(trc->runtime(), code());
3348 }
3349
3350 bindings.trace(trc);
3351
3352 if (hasAnyBreakpointsOrStepMode()) {
3353 for (unsigned i = 0; i < length(); i++) {
3354 BreakpointSite *site = debugScript()->breakpoints[i];
3355 if (site && site->trapHandler)
3356 MarkValue(trc, &site->trapClosure, "trap closure");
3357 }
3358 }
3359
3360 #ifdef JS_ION
3361 jit::TraceIonScripts(trc, this);
3362 #endif
3363 }
3364
3365 void
3366 LazyScript::markChildren(JSTracer *trc)
3367 {
3368 if (function_)
3369 MarkObject(trc, &function_, "function");
3370
3371 if (sourceObject_)
3372 MarkObject(trc, &sourceObject_, "sourceObject");
3373
3374 if (enclosingScope_)
3375 MarkObject(trc, &enclosingScope_, "enclosingScope");
3376
3377 if (script_)
3378 MarkScript(trc, &script_, "realScript");
3379
3380 HeapPtrAtom *freeVariables = this->freeVariables();
3381 for (size_t i = 0; i < numFreeVariables(); i++)
3382 MarkString(trc, &freeVariables[i], "lazyScriptFreeVariable");
3383
3384 HeapPtrFunction *innerFunctions = this->innerFunctions();
3385 for (size_t i = 0; i < numInnerFunctions(); i++)
3386 MarkObject(trc, &innerFunctions[i], "lazyScriptInnerFunction");
3387 }
3388
3389 void
3390 LazyScript::finalize(FreeOp *fop)
3391 {
3392 if (table_)
3393 fop->free_(table_);
3394 }
3395
3396 NestedScopeObject *
3397 JSScript::getStaticScope(jsbytecode *pc)
3398 {
3399 JS_ASSERT(containsPC(pc));
3400
3401 if (!hasBlockScopes())
3402 return nullptr;
3403
3404 ptrdiff_t offset = pc - main();
3405
3406 if (offset < 0)
3407 return nullptr;
3408
3409 BlockScopeArray *scopes = blockScopes();
3410 NestedScopeObject *blockChain = nullptr;
3411
3412 // Find the innermost block chain using a binary search.
3413 size_t bottom = 0;
3414 size_t top = scopes->length;
3415
3416 while (bottom < top) {
3417 size_t mid = bottom + (top - bottom) / 2;
3418 const BlockScopeNote *note = &scopes->vector[mid];
3419 if (note->start <= offset) {
3420 // Block scopes are ordered in the list by their starting offset, and since
3421 // blocks form a tree ones earlier in the list may cover the pc even if
3422 // later blocks end before the pc. This only happens when the earlier block
3423 // is a parent of the later block, so we need to check parents of |mid| in
3424 // the searched range for coverage.
3425 size_t check = mid;
3426 while (check >= bottom) {
3427 const BlockScopeNote *checkNote = &scopes->vector[check];
3428 JS_ASSERT(checkNote->start <= offset);
3429 if (offset < checkNote->start + checkNote->length) {
3430 // We found a matching block chain but there may be inner ones
3431 // at a higher block chain index than mid. Continue the binary search.
3432 if (checkNote->index == BlockScopeNote::NoBlockScopeIndex)
3433 blockChain = nullptr;
3434 else
3435 blockChain = &getObject(checkNote->index)->as<NestedScopeObject>();
3436 break;
3437 }
3438 if (checkNote->parent == UINT32_MAX)
3439 break;
3440 check = checkNote->parent;
3441 }
3442 bottom = mid + 1;
3443 } else {
3444 top = mid;
3445 }
3446 }
3447
3448 return blockChain;
3449 }
3450
3451 void
3452 JSScript::setArgumentsHasVarBinding()
3453 {
3454 argsHasVarBinding_ = true;
3455 #ifdef JS_ION
3456 needsArgsAnalysis_ = true;
3457 #else
3458 // The arguments analysis is performed by IonBuilder.
3459 needsArgsObj_ = true;
3460 #endif
3461 }
3462
3463 void
3464 JSScript::setNeedsArgsObj(bool needsArgsObj)
3465 {
3466 JS_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
3467 needsArgsAnalysis_ = false;
3468 needsArgsObj_ = needsArgsObj;
3469 }
3470
3471 void
3472 js::SetFrameArgumentsObject(JSContext *cx, AbstractFramePtr frame,
3473 HandleScript script, JSObject *argsobj)
3474 {
3475 /*
3476 * Replace any optimized arguments in the frame with an explicit arguments
3477 * object. Note that 'arguments' may have already been overwritten.
3478 */
3479
3480 InternalBindingsHandle bindings(script, &script->bindings);
3481 const uint32_t var = Bindings::argumentsVarIndex(cx, bindings);
3482
3483 if (script->varIsAliased(var)) {
3484 /*
3485 * Scan the script to find the slot in the call object that 'arguments'
3486 * is assigned to.
3487 */
3488 jsbytecode *pc = script->code();
3489 while (*pc != JSOP_ARGUMENTS)
3490 pc += GetBytecodeLength(pc);
3491 pc += JSOP_ARGUMENTS_LENGTH;
3492 JS_ASSERT(*pc == JSOP_SETALIASEDVAR);
3493
3494 // Note that here and below, it is insufficient to only check for
3495 // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
3496 // arguments slot.
3497 if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(pc)))
3498 frame.callObj().as<ScopeObject>().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj));
3499 } else {
3500 if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(var)))
3501 frame.unaliasedLocal(var) = ObjectValue(*argsobj);
3502 }
3503 }
3504
3505 /* static */ bool
3506 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
3507 {
3508 JS_ASSERT(script->functionNonDelazifying());
3509 JS_ASSERT(script->analyzedArgsUsage());
3510 JS_ASSERT(script->argumentsHasVarBinding());
3511
3512 /*
3513 * It is possible that the arguments optimization has already failed,
3514 * everything has been fixed up, but there was an outstanding magic value
3515 * on the stack that has just now flowed into an apply. In this case, there
3516 * is nothing to do; GuardFunApplySpeculation will patch in the real
3517 * argsobj.
3518 */
3519 if (script->needsArgsObj())
3520 return true;
3521
3522 JS_ASSERT(!script->isGenerator());
3523
3524 script->needsArgsObj_ = true;
3525
3526 #ifdef JS_ION
3527 /*
3528 * Since we can't invalidate baseline scripts, set a flag that's checked from
3529 * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS
3530 * should create an arguments object next time.
3531 */
3532 if (script->hasBaselineScript())
3533 script->baselineScript()->setNeedsArgsObj();
3534 #endif
3535
3536 /*
3537 * By design, the arguments optimization is only made when there are no
3538 * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points
3539 * where the optimization could fail, other than an active invocation of
3540 * 'f.apply(x, arguments)'. Thus, there are no outstanding values of
3541 * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
3542 * three things that need fixup:
3543 * - there may be any number of activations of this script that don't have
3544 * an argsObj that now need one.
3545 * - jit code compiled (and possible active on the stack) with the static
3546 * assumption of !script->needsArgsObj();
3547 * - type inference data for the script assuming script->needsArgsObj
3548 */
3549 for (AllFramesIter i(cx); !i.done(); ++i) {
3550 /*
3551 * We cannot reliably create an arguments object for Ion activations of
3552 * this script. To maintain the invariant that "script->needsArgsObj
3553 * implies fp->hasArgsObj", the Ion bail mechanism will create an
3554 * arguments object right after restoring the BaselineFrame and before
3555 * entering Baseline code (in jit::FinishBailoutToBaseline).
3556 */
3557 if (i.isIon())
3558 continue;
3559 AbstractFramePtr frame = i.abstractFramePtr();
3560 if (frame.isFunctionFrame() && frame.script() == script) {
3561 ArgumentsObject *argsobj = ArgumentsObject::createExpected(cx, frame);
3562 if (!argsobj) {
3563 /*
3564 * We can't leave stack frames with script->needsArgsObj but no
3565 * arguments object. It is, however, safe to leave frames with
3566 * an arguments object but !script->needsArgsObj.
3567 */
3568 script->needsArgsObj_ = false;
3569 return false;
3570 }
3571
3572 SetFrameArgumentsObject(cx, frame, script, argsobj);
3573 }
3574 }
3575
3576 return true;
3577 }
3578
3579 bool
3580 JSScript::varIsAliased(uint32_t varSlot)
3581 {
3582 return bindings.bindingIsAliased(bindings.numArgs() + varSlot);
3583 }
3584
3585 bool
3586 JSScript::formalIsAliased(unsigned argSlot)
3587 {
3588 return bindings.bindingIsAliased(argSlot);
3589 }
3590
3591 bool
3592 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
3593 {
3594 return argsObjAliasesFormals() && !formalIsAliased(argSlot);
3595 }
3596
3597 LazyScript::LazyScript(JSFunction *fun, void *table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
3598 : script_(nullptr),
3599 function_(fun),
3600 enclosingScope_(nullptr),
3601 sourceObject_(nullptr),
3602 table_(table),
3603 packedFields_(packedFields),
3604 begin_(begin),
3605 end_(end),
3606 lineno_(lineno),
3607 column_(column)
3608 {
3609 JS_ASSERT(begin <= end);
3610 }
3611
3612 void
3613 LazyScript::initScript(JSScript *script)
3614 {
3615 JS_ASSERT(script && !script_);
3616 script_ = script;
3617 }
3618
3619 void
3620 LazyScript::resetScript()
3621 {
3622 JS_ASSERT(script_);
3623 script_ = nullptr;
3624 }
3625
3626 void
3627 LazyScript::setParent(JSObject *enclosingScope, ScriptSourceObject *sourceObject)
3628 {
3629 JS_ASSERT(!sourceObject_ && !enclosingScope_);
3630 JS_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
3631 JS_ASSERT(function_->compartment() == sourceObject->compartment());
3632
3633 enclosingScope_ = enclosingScope;
3634 sourceObject_ = sourceObject;
3635 }
3636
3637 ScriptSourceObject *
3638 LazyScript::sourceObject() const
3639 {
3640 return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
3641 }
3642
3643 /* static */ LazyScript *
3644 LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun,
3645 uint64_t packedFields, uint32_t begin, uint32_t end,
3646 uint32_t lineno, uint32_t column)
3647 {
3648 union {
3649 PackedView p;
3650 uint64_t packed;
3651 };
3652
3653 packed = packedFields;
3654
3655 // Reset runtime flags to obtain a fresh LazyScript.
3656 p.hasBeenCloned = false;
3657 p.treatAsRunOnce = false;
3658
3659 size_t bytes = (p.numFreeVariables * sizeof(HeapPtrAtom))
3660 + (p.numInnerFunctions * sizeof(HeapPtrFunction));
3661
3662 ScopedJSFreePtr<void> table(bytes ? cx->malloc_(bytes) : nullptr);
3663 if (bytes && !table)
3664 return nullptr;
3665
3666 LazyScript *res = js_NewGCLazyScript(cx);
3667 if (!res)
3668 return nullptr;
3669
3670 cx->compartment()->scheduleDelazificationForDebugMode();
3671
3672 return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
3673 }
3674
3675 /* static */ LazyScript *
3676 LazyScript::CreateRaw(ExclusiveContext *cx, HandleFunction fun,
3677 uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
3678 uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
3679 {
3680 union {
3681 PackedView p;
3682 uint64_t packedFields;
3683 };
3684
3685 p.version = version;
3686 p.numFreeVariables = numFreeVariables;
3687 p.numInnerFunctions = numInnerFunctions;
3688 p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
3689 p.strict = false;
3690 p.bindingsAccessedDynamically = false;
3691 p.hasDebuggerStatement = false;
3692 p.directlyInsideEval = false;
3693 p.usesArgumentsAndApply = false;
3694
3695 LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
3696 JS_ASSERT_IF(res, res->version() == version);
3697 return res;
3698 }
3699
3700 /* static */ LazyScript *
3701 LazyScript::Create(ExclusiveContext *cx, HandleFunction fun,
3702 uint64_t packedFields, uint32_t begin, uint32_t end,
3703 uint32_t lineno, uint32_t column)
3704 {
3705 // Dummy atom which is not a valid property name.
3706 RootedAtom dummyAtom(cx, cx->names().comma);
3707
3708 // Dummy function which is not a valid function as this is the one which is
3709 // holding this lazy script.
3710 HandleFunction dummyFun = fun;
3711
3712 LazyScript *res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
3713 if (!res)
3714 return nullptr;
3715
3716 // Fill with dummies, to be GC-safe after the initialization of the free
3717 // variables and inner functions.
3718 size_t i, num;
3719 HeapPtrAtom *variables = res->freeVariables();
3720 for (i = 0, num = res->numFreeVariables(); i < num; i++)
3721 variables[i].init(dummyAtom);
3722
3723 HeapPtrFunction *functions = res->innerFunctions();
3724 for (i = 0, num = res->numInnerFunctions(); i < num; i++)
3725 functions[i].init(dummyFun);
3726
3727 return res;
3728 }
3729
3730 void
3731 LazyScript::initRuntimeFields(uint64_t packedFields)
3732 {
3733 union {
3734 PackedView p;
3735 uint64_t packed;
3736 };
3737
3738 packed = packedFields;
3739 p_.hasBeenCloned = p.hasBeenCloned;
3740 p_.treatAsRunOnce = p.treatAsRunOnce;
3741 }
3742
3743 bool
3744 LazyScript::hasUncompiledEnclosingScript() const
3745 {
3746 // It can happen that we created lazy scripts while compiling an enclosing
3747 // script, but we errored out while compiling that script. When we iterate
3748 // over lazy script in a compartment, we might see lazy scripts that never
3749 // escaped to script and should be ignored.
3750 //
3751 // If the enclosing scope is a function with a null script or has a script
3752 // without code, it was not successfully compiled.
3753
3754 if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
3755 return false;
3756
3757 JSFunction &fun = enclosingScope()->as<JSFunction>();
3758 return fun.isInterpreted() && (!fun.mutableScript() || !fun.nonLazyScript()->code());
3759 }
3760
3761 uint32_t
3762 LazyScript::staticLevel(JSContext *cx) const
3763 {
3764 for (StaticScopeIter<NoGC> ssi(enclosingScope()); !ssi.done(); ssi++) {
3765 if (ssi.type() == StaticScopeIter<NoGC>::FUNCTION)
3766 return ssi.funScript()->staticLevel() + 1;
3767 }
3768 return 1;
3769 }
3770
3771 void
3772 JSScript::updateBaselineOrIonRaw()
3773 {
3774 #ifdef JS_ION
3775 if (hasIonScript()) {
3776 baselineOrIonRaw = ion->method()->raw();
3777 baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
3778 } else if (hasBaselineScript()) {
3779 baselineOrIonRaw = baseline->method()->raw();
3780 baselineOrIonSkipArgCheck = baseline->method()->raw();
3781 } else {
3782 baselineOrIonRaw = nullptr;
3783 baselineOrIonSkipArgCheck = nullptr;
3784 }
3785 #endif
3786 }
3787
3788 bool
3789 JSScript::hasLoops()
3790 {
3791 if (!hasTrynotes())
3792 return false;
3793 JSTryNote *tn = trynotes()->vector;
3794 JSTryNote *tnlimit = tn + trynotes()->length;
3795 for (; tn < tnlimit; tn++) {
3796 if (tn->kind == JSTRY_ITER || tn->kind == JSTRY_LOOP)
3797 return true;
3798 }
3799 return false;
3800 }
3801
3802 static inline void
3803 LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
3804 HashNumber hashes[3])
3805 {
3806 HashNumber hash = lineno;
3807 hash = RotateLeft(hash, 4) ^ column;
3808 hash = RotateLeft(hash, 4) ^ begin;
3809 hash = RotateLeft(hash, 4) ^ end;
3810
3811 hashes[0] = hash;
3812 hashes[1] = RotateLeft(hashes[0], 4) ^ begin;
3813 hashes[2] = RotateLeft(hashes[1], 4) ^ end;
3814 }
3815
3816 void
3817 LazyScriptHashPolicy::hash(const Lookup &lookup, HashNumber hashes[3])
3818 {
3819 LazyScript *lazy = lookup.lazy;
3820 LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes);
3821 }
3822
3823 void
3824 LazyScriptHashPolicy::hash(JSScript *script, HashNumber hashes[3])
3825 {
3826 LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes);
3827 }
3828
3829 bool
3830 LazyScriptHashPolicy::match(JSScript *script, const Lookup &lookup)
3831 {
3832 JSContext *cx = lookup.cx;
3833 LazyScript *lazy = lookup.lazy;
3834
3835 // To be a match, the script and lazy script need to have the same line
3836 // and column and to be at the same position within their respective
3837 // source blobs, and to have the same source contents and version.
3838 //
3839 // While the surrounding code in the source may differ, this is
3840 // sufficient to ensure that compiling the lazy script will yield an
3841 // identical result to compiling the original script.
3842 //
3843 // Note that the filenames and origin principals of the lazy script and
3844 // original script can differ. If there is a match, these will be fixed
3845 // up in the resulting clone by the caller.
3846
3847 if (script->lineno() != lazy->lineno() ||
3848 script->column() != lazy->column() ||
3849 script->getVersion() != lazy->version() ||
3850 script->sourceStart() != lazy->begin() ||
3851 script->sourceEnd() != lazy->end())
3852 {
3853 return false;
3854 }
3855
3856 SourceDataCache::AutoHoldEntry holder;
3857
3858 const jschar *scriptChars = script->scriptSource()->chars(cx, holder);
3859 if (!scriptChars)
3860 return false;
3861
3862 const jschar *lazyChars = lazy->source()->chars(cx, holder);
3863 if (!lazyChars)
3864 return false;
3865
3866 size_t begin = script->sourceStart();
3867 size_t length = script->sourceEnd() - begin;
3868 return !memcmp(scriptChars + begin, lazyChars + begin, length);
3869 }

mercurial