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 #include "mozilla/Vector.h"
18 
19 #include <algorithm>
20 #include <string.h>
21 
22 #include "jsapi.h"
23 #include "jsatom.h"
24 #include "jscntxt.h"
25 #include "jsfun.h"
26 #include "jsgc.h"
27 #include "jsobj.h"
28 #include "jsopcode.h"
29 #include "jsprf.h"
30 #include "jstypes.h"
31 #include "jsutil.h"
32 #include "jswrapper.h"
33 
34 #include "frontend/BytecodeCompiler.h"
35 #include "frontend/BytecodeEmitter.h"
36 #include "frontend/SharedContext.h"
37 #include "gc/Marking.h"
38 #include "jit/BaselineJIT.h"
39 #include "jit/Ion.h"
40 #include "jit/IonCode.h"
41 #include "js/MemoryMetrics.h"
42 #include "js/Utility.h"
43 #include "vm/ArgumentsObject.h"
44 #include "vm/Compression.h"
45 #include "vm/Debugger.h"
46 #include "vm/Opcodes.h"
47 #include "vm/SelfHosting.h"
48 #include "vm/Shape.h"
49 #include "vm/Xdr.h"
50 
51 #include "jsfuninlines.h"
52 #include "jsobjinlines.h"
53 
54 #include "vm/ScopeObject-inl.h"
55 #include "vm/Stack-inl.h"
56 
57 using namespace js;
58 using namespace js::gc;
59 using namespace js::frontend;
60 
61 using mozilla::PodCopy;
62 using mozilla::PodZero;
63 using mozilla::RotateLeft;
64 
65 static BindingIter
GetBinding(HandleScript script,HandlePropertyName name)66 GetBinding(HandleScript script, HandlePropertyName name)
67 {
68     BindingIter bi(script);
69     while (bi->name() != name)
70         bi++;
71     return bi;
72 }
73 
74 /* static */ BindingIter
argumentsBinding(ExclusiveContext * cx,HandleScript script)75 Bindings::argumentsBinding(ExclusiveContext* cx, HandleScript script)
76 {
77     return GetBinding(script, cx->names().arguments);
78 }
79 
80 /* static */ BindingIter
thisBinding(ExclusiveContext * cx,HandleScript script)81 Bindings::thisBinding(ExclusiveContext* cx, HandleScript script)
82 {
83     return GetBinding(script, cx->names().dotThis);
84 }
85 
86 bool
initWithTemporaryStorage(ExclusiveContext * cx,MutableHandle<Bindings> self,uint32_t numArgs,uint32_t numVars,uint32_t numBodyLevelLexicals,uint32_t numBlockScoped,uint32_t numUnaliasedVars,uint32_t numUnaliasedBodyLevelLexicals,const Binding * bindingArray,bool isModule)87 Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings> self,
88                                    uint32_t numArgs, uint32_t numVars,
89                                    uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
90                                    uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
91                                    const Binding* bindingArray, bool isModule /* = false */)
92 {
93     MOZ_ASSERT(!self.callObjShape());
94     MOZ_ASSERT(self.bindingArrayUsingTemporaryStorage());
95     MOZ_ASSERT(!self.bindingArray());
96     MOZ_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
97     MOZ_ASSERT(numArgs <= ARGC_LIMIT);
98     MOZ_ASSERT(numVars <= LOCALNO_LIMIT);
99     MOZ_ASSERT(numBlockScoped <= LOCALNO_LIMIT);
100     MOZ_ASSERT(numBodyLevelLexicals <= LOCALNO_LIMIT);
101     mozilla::DebugOnly<uint64_t> totalSlots = uint64_t(numVars) +
102                                               uint64_t(numBodyLevelLexicals) +
103                                               uint64_t(numBlockScoped);
104     MOZ_ASSERT(totalSlots <= LOCALNO_LIMIT);
105     MOZ_ASSERT(UINT32_MAX - numArgs >= totalSlots);
106 
107     MOZ_ASSERT(numUnaliasedVars <= numVars);
108     MOZ_ASSERT(numUnaliasedBodyLevelLexicals <= numBodyLevelLexicals);
109 
110     self.setBindingArray(bindingArray, TEMPORARY_STORAGE_BIT);
111     self.setNumArgs(numArgs);
112     self.setNumVars(numVars);
113     self.setNumBodyLevelLexicals(numBodyLevelLexicals);
114     self.setNumBlockScoped(numBlockScoped);
115     self.setNumUnaliasedVars(numUnaliasedVars);
116     self.setNumUnaliasedBodyLevelLexicals(numUnaliasedBodyLevelLexicals);
117 
118     // Get the initial shape to use when creating CallObjects for this script.
119     // After creation, a CallObject's shape may change completely (via direct eval() or
120     // other operations that mutate the lexical scope). However, since the
121     // lexical bindings added to the initial shape are permanent and the
122     // allocKind/nfixed of a CallObject cannot change, one may assume that the
123     // slot location (whether in the fixed or dynamic slots) of a variable is
124     // the same as in the initial shape. (This is assumed by the interpreter and
125     // JITs when interpreting/compiling aliasedvar ops.)
126 
127     // Since unaliased variables are, by definition, only accessed by local
128     // operations and never through the scope chain, only give shapes to
129     // aliased variables. While the debugger may observe any scope object at
130     // any time, such accesses are mediated by DebugScopeProxy (see
131     // DebugScopeProxy::handleUnaliasedAccess).
132     uint32_t nslots = CallObject::RESERVED_SLOTS;
133 
134     // Unless there are aliased body-level lexical bindings at all, set the
135     // begin index to an impossible slot number.
136     uint32_t aliasedBodyLevelLexicalBegin = LOCALNO_LIMIT;
137     for (BindingIter bi(self); bi; bi++) {
138         if (bi->aliased()) {
139             // Per ES6, lexical bindings cannot be accessed until
140             // initialized. Remember the first aliased slot that is a
141             // body-level lexical, so that they may be initialized to sentinel
142             // magic values.
143             if (numBodyLevelLexicals > 0 &&
144                 nslots < aliasedBodyLevelLexicalBegin &&
145                 bi.isBodyLevelLexical() &&
146                 bi.localIndex() >= numVars)
147             {
148                 aliasedBodyLevelLexicalBegin = nslots;
149             }
150 
151             nslots++;
152         }
153     }
154     self.setAliasedBodyLevelLexicalBegin(aliasedBodyLevelLexicalBegin);
155 
156     // Put as many of nslots inline into the object header as possible.
157     uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots));
158 
159     // Start with the empty shape and then append one shape per aliased binding.
160     const Class* cls = isModule ? &ModuleEnvironmentObject::class_ : &CallObject::class_;
161     uint32_t baseShapeFlags = BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE;
162     if (isModule)
163         baseShapeFlags |= BaseShape::NOT_EXTENSIBLE; // Module code is always strict.
164     RootedShape shape(cx,
165         EmptyShape::getInitialShape(cx, cls, TaggedProto(nullptr), nfixed, baseShapeFlags));
166     if (!shape)
167         return false;
168 
169 #ifdef DEBUG
170     HashSet<PropertyName*> added(cx);
171     if (!added.init()) {
172         ReportOutOfMemory(cx);
173         return false;
174     }
175 #endif
176 
177     uint32_t slot = CallObject::RESERVED_SLOTS;
178     for (BindingIter bi(self); bi; bi++) {
179         MOZ_ASSERT_IF(isModule, bi->aliased());
180         if (!bi->aliased())
181             continue;
182 
183 #ifdef DEBUG
184         // The caller ensures no duplicate aliased names.
185         MOZ_ASSERT(!added.has(bi->name()));
186         if (!added.put(bi->name())) {
187             ReportOutOfMemory(cx);
188             return false;
189         }
190 #endif
191 
192         StackBaseShape stackBase(cx, cls, baseShapeFlags);
193         UnownedBaseShape* base = BaseShape::getUnowned(cx, stackBase);
194         if (!base)
195             return false;
196 
197         unsigned attrs = JSPROP_PERMANENT |
198                          JSPROP_ENUMERATE |
199                          (bi->kind() == Binding::CONSTANT ? JSPROP_READONLY : 0);
200         Rooted<StackShape> child(cx, StackShape(base, NameToId(bi->name()), slot, attrs, 0));
201 
202         shape = cx->compartment()->propertyTree.getChild(cx, shape, child);
203         if (!shape)
204             return false;
205 
206         MOZ_ASSERT(slot < nslots);
207         slot++;
208     }
209     MOZ_ASSERT(slot == nslots);
210 
211     MOZ_ASSERT(!shape->inDictionary());
212     self.setCallObjShape(shape);
213     return true;
214 }
215 
216 bool
initTrivial(ExclusiveContext * cx)217 Bindings::initTrivial(ExclusiveContext* cx)
218 {
219     Shape* shape = EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
220                                                CallObject::RESERVED_SLOTS,
221                                                BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE);
222     if (!shape)
223         return false;
224     callObjShape_.init(shape);
225     return true;
226 }
227 
228 uint8_t*
switchToScriptStorage(Binding * newBindingArray)229 Bindings::switchToScriptStorage(Binding* newBindingArray)
230 {
231     MOZ_ASSERT(bindingArrayUsingTemporaryStorage());
232     MOZ_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT));
233 
234     if (count() > 0)
235         PodCopy(newBindingArray, bindingArray(), count());
236     bindingArrayAndFlag_ = uintptr_t(newBindingArray);
237     return reinterpret_cast<uint8_t*>(newBindingArray + count());
238 }
239 
240 /* static */ bool
clone(JSContext * cx,MutableHandle<Bindings> self,uint8_t * dstScriptData,HandleScript srcScript)241 Bindings::clone(JSContext* cx, MutableHandle<Bindings> self,
242                 uint8_t* dstScriptData, HandleScript srcScript)
243 {
244     /* The clone has the same bindingArray_ offset as 'src'. */
245     Handle<Bindings> src = Handle<Bindings>::fromMarkedLocation(&srcScript->bindings);
246     ptrdiff_t off = (uint8_t*)src.bindingArray() - srcScript->data;
247     MOZ_ASSERT(off >= 0);
248     MOZ_ASSERT(size_t(off) <= srcScript->dataSize());
249     Binding* dstPackedBindings = (Binding*)(dstScriptData + off);
250 
251     /*
252      * Since atoms are shareable throughout the runtime, we can simply copy
253      * the source's bindingArray directly.
254      */
255     if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(),
256                                   src.numBodyLevelLexicals(),
257                                   src.numBlockScoped(),
258                                   src.numUnaliasedVars(),
259                                   src.numUnaliasedBodyLevelLexicals(),
260                                   src.bindingArray()))
261     {
262         return false;
263     }
264 
265     self.switchToScriptStorage(dstPackedBindings);
266     return true;
267 }
268 
269 template<XDRMode mode>
270 static bool
XDRScriptBindings(XDRState<mode> * xdr,LifoAllocScope & las,uint16_t numArgs,uint32_t numVars,uint16_t numBodyLevelLexicals,uint16_t numBlockScoped,uint32_t numUnaliasedVars,uint16_t numUnaliasedBodyLevelLexicals,HandleScript script)271 XDRScriptBindings(XDRState<mode>* xdr, LifoAllocScope& las, uint16_t numArgs, uint32_t numVars,
272                   uint16_t numBodyLevelLexicals, uint16_t numBlockScoped,
273                   uint32_t numUnaliasedVars, uint16_t numUnaliasedBodyLevelLexicals,
274                   HandleScript script)
275 {
276     JSContext* cx = xdr->cx();
277 
278     if (mode == XDR_ENCODE) {
279         for (BindingIter bi(script); bi; bi++) {
280             RootedAtom atom(cx, bi->name());
281             if (!XDRAtom(xdr, &atom))
282                 return false;
283         }
284 
285         for (BindingIter bi(script); bi; bi++) {
286             uint8_t u8 = (uint8_t(bi->kind()) << 1) | uint8_t(bi->aliased());
287             if (!xdr->codeUint8(&u8))
288                 return false;
289         }
290     } else {
291         uint32_t nameCount = numArgs + numVars + numBodyLevelLexicals;
292 
293         AutoValueVector atoms(cx);
294         if (!atoms.resize(nameCount))
295             return false;
296         for (uint32_t i = 0; i < nameCount; i++) {
297             RootedAtom atom(cx);
298             if (!XDRAtom(xdr, &atom))
299                 return false;
300             atoms[i].setString(atom);
301         }
302 
303         Binding* bindingArray = las.alloc().newArrayUninitialized<Binding>(nameCount);
304         if (!bindingArray)
305             return false;
306         for (uint32_t i = 0; i < nameCount; i++) {
307             uint8_t u8;
308             if (!xdr->codeUint8(&u8))
309                 return false;
310 
311             PropertyName* name = atoms[i].toString()->asAtom().asPropertyName();
312             Binding::Kind kind = Binding::Kind(u8 >> 1);
313             bool aliased = bool(u8 & 1);
314 
315             bindingArray[i] = Binding(name, kind, aliased);
316         }
317 
318         Rooted<Bindings> bindings(cx, script->bindings);
319         if (!Bindings::initWithTemporaryStorage(cx, &bindings, numArgs, numVars,
320                                                 numBodyLevelLexicals, numBlockScoped,
321                                                 numUnaliasedVars, numUnaliasedBodyLevelLexicals,
322                                                 bindingArray))
323         {
324             return false;
325         }
326         script->bindings = bindings;
327     }
328 
329     return true;
330 }
331 
332 bool
bindingIsAliased(uint32_t bindingIndex)333 Bindings::bindingIsAliased(uint32_t bindingIndex)
334 {
335     MOZ_ASSERT(bindingIndex < count());
336     return bindingArray()[bindingIndex].aliased();
337 }
338 
339 void
trace(JSTracer * trc)340 Binding::trace(JSTracer* trc)
341 {
342     PropertyName* name = this->name();
343     TraceManuallyBarrieredEdge(trc, &name, "binding");
344 }
345 
346 void
trace(JSTracer * trc)347 Bindings::trace(JSTracer* trc)
348 {
349     if (callObjShape_)
350         TraceEdge(trc, &callObjShape_, "callObjShape");
351 
352     /*
353      * As the comment in Bindings explains, bindingsArray may point into freed
354      * storage when bindingArrayUsingTemporaryStorage so we don't mark it.
355      * Note: during compilation, atoms are already kept alive by gcKeepAtoms.
356      */
357     if (bindingArrayUsingTemporaryStorage())
358         return;
359 
360     for (Binding& b : *this)
361         b.trace(trc);
362 }
363 
364 template<XDRMode mode>
365 bool
XDRScriptConst(XDRState<mode> * xdr,MutableHandleValue vp)366 js::XDRScriptConst(XDRState<mode>* xdr, MutableHandleValue vp)
367 {
368     JSContext* cx = xdr->cx();
369 
370     /*
371      * A script constant can be an arbitrary primitive value as they are used
372      * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see
373      * bug 407186.
374      */
375     enum ConstTag {
376         SCRIPT_INT     = 0,
377         SCRIPT_DOUBLE  = 1,
378         SCRIPT_ATOM    = 2,
379         SCRIPT_TRUE    = 3,
380         SCRIPT_FALSE   = 4,
381         SCRIPT_NULL    = 5,
382         SCRIPT_OBJECT  = 6,
383         SCRIPT_VOID    = 7,
384         SCRIPT_HOLE    = 8
385     };
386 
387     uint32_t tag;
388     if (mode == XDR_ENCODE) {
389         if (vp.isInt32()) {
390             tag = SCRIPT_INT;
391         } else if (vp.isDouble()) {
392             tag = SCRIPT_DOUBLE;
393         } else if (vp.isString()) {
394             tag = SCRIPT_ATOM;
395         } else if (vp.isTrue()) {
396             tag = SCRIPT_TRUE;
397         } else if (vp.isFalse()) {
398             tag = SCRIPT_FALSE;
399         } else if (vp.isNull()) {
400             tag = SCRIPT_NULL;
401         } else if (vp.isObject()) {
402             tag = SCRIPT_OBJECT;
403         } else if (vp.isMagic(JS_ELEMENTS_HOLE)) {
404             tag = SCRIPT_HOLE;
405         } else {
406             MOZ_ASSERT(vp.isUndefined());
407             tag = SCRIPT_VOID;
408         }
409     }
410 
411     if (!xdr->codeUint32(&tag))
412         return false;
413 
414     switch (tag) {
415       case SCRIPT_INT: {
416         uint32_t i;
417         if (mode == XDR_ENCODE)
418             i = uint32_t(vp.toInt32());
419         if (!xdr->codeUint32(&i))
420             return false;
421         if (mode == XDR_DECODE)
422             vp.set(Int32Value(int32_t(i)));
423         break;
424       }
425       case SCRIPT_DOUBLE: {
426         double d;
427         if (mode == XDR_ENCODE)
428             d = vp.toDouble();
429         if (!xdr->codeDouble(&d))
430             return false;
431         if (mode == XDR_DECODE)
432             vp.set(DoubleValue(d));
433         break;
434       }
435       case SCRIPT_ATOM: {
436         RootedAtom atom(cx);
437         if (mode == XDR_ENCODE)
438             atom = &vp.toString()->asAtom();
439         if (!XDRAtom(xdr, &atom))
440             return false;
441         if (mode == XDR_DECODE)
442             vp.set(StringValue(atom));
443         break;
444       }
445       case SCRIPT_TRUE:
446         if (mode == XDR_DECODE)
447             vp.set(BooleanValue(true));
448         break;
449       case SCRIPT_FALSE:
450         if (mode == XDR_DECODE)
451             vp.set(BooleanValue(false));
452         break;
453       case SCRIPT_NULL:
454         if (mode == XDR_DECODE)
455             vp.set(NullValue());
456         break;
457       case SCRIPT_OBJECT: {
458         RootedObject obj(cx);
459         if (mode == XDR_ENCODE)
460             obj = &vp.toObject();
461 
462         if (!XDRObjectLiteral(xdr, &obj))
463             return false;
464 
465         if (mode == XDR_DECODE)
466             vp.setObject(*obj);
467         break;
468       }
469       case SCRIPT_VOID:
470         if (mode == XDR_DECODE)
471             vp.set(UndefinedValue());
472         break;
473       case SCRIPT_HOLE:
474         if (mode == XDR_DECODE)
475             vp.setMagic(JS_ELEMENTS_HOLE);
476         break;
477     }
478     return true;
479 }
480 
481 template bool
482 js::XDRScriptConst(XDRState<XDR_ENCODE>*, MutableHandleValue);
483 
484 template bool
485 js::XDRScriptConst(XDRState<XDR_DECODE>*, MutableHandleValue);
486 
487 // Code LazyScript's free variables.
488 template<XDRMode mode>
489 static bool
XDRLazyFreeVariables(XDRState<mode> * xdr,MutableHandle<LazyScript * > lazy)490 XDRLazyFreeVariables(XDRState<mode>* xdr, MutableHandle<LazyScript*> lazy)
491 {
492     JSContext* cx = xdr->cx();
493     RootedAtom atom(cx);
494     uint8_t isHoistedUse;
495     LazyScript::FreeVariable* freeVariables = lazy->freeVariables();
496     size_t numFreeVariables = lazy->numFreeVariables();
497     for (size_t i = 0; i < numFreeVariables; i++) {
498         if (mode == XDR_ENCODE) {
499             atom = freeVariables[i].atom();
500             isHoistedUse = freeVariables[i].isHoistedUse();
501         }
502 
503         if (!XDRAtom(xdr, &atom))
504             return false;
505         if (!xdr->codeUint8(&isHoistedUse))
506             return false;
507 
508         if (mode == XDR_DECODE) {
509             freeVariables[i] = LazyScript::FreeVariable(atom);
510             if (isHoistedUse)
511                 freeVariables[i].setIsHoistedUse();
512         }
513     }
514 
515     return true;
516 }
517 
518 // Code the missing part needed to re-create a LazyScript from a JSScript.
519 template<XDRMode mode>
520 static bool
XDRRelazificationInfo(XDRState<mode> * xdr,HandleFunction fun,HandleScript script,HandleObject enclosingScope,MutableHandle<LazyScript * > lazy)521 XDRRelazificationInfo(XDRState<mode>* xdr, HandleFunction fun, HandleScript script,
522                       HandleObject enclosingScope, MutableHandle<LazyScript*> lazy)
523 {
524     MOZ_ASSERT_IF(mode == XDR_ENCODE, script->isRelazifiable() && script->maybeLazyScript());
525     MOZ_ASSERT_IF(mode == XDR_ENCODE, !lazy->numInnerFunctions());
526 
527     JSContext* cx = xdr->cx();
528 
529     uint64_t packedFields;
530     {
531         uint32_t begin = script->sourceStart();
532         uint32_t end = script->sourceEnd();
533         uint32_t lineno = script->lineno();
534         uint32_t column = script->column();
535 
536         if (mode == XDR_ENCODE) {
537             packedFields = lazy->packedFields();
538             MOZ_ASSERT(begin == lazy->begin());
539             MOZ_ASSERT(end == lazy->end());
540             MOZ_ASSERT(lineno == lazy->lineno());
541             MOZ_ASSERT(column == lazy->column());
542             // We can assert we have no inner functions because we don't
543             // relazify scripts with inner functions.  See
544             // JSFunction::createScriptForLazilyInterpretedFunction.
545             MOZ_ASSERT(lazy->numInnerFunctions() == 0);
546         }
547 
548         if (!xdr->codeUint64(&packedFields))
549             return false;
550 
551         if (mode == XDR_DECODE) {
552             lazy.set(LazyScript::Create(cx, fun, script, enclosingScope, script,
553                                         packedFields, begin, end, lineno, column));
554 
555             // As opposed to XDRLazyScript, we need to restore the runtime bits
556             // of the script, as we are trying to match the fact this function
557             // has already been parsed and that it would need to be re-lazified.
558             lazy->initRuntimeFields(packedFields);
559         }
560     }
561 
562     // Code free variables.
563     if (!XDRLazyFreeVariables(xdr, lazy))
564         return false;
565 
566     // No need to do anything with inner functions, since we asserted we don't
567     // have any.
568 
569     return true;
570 }
571 
572 static inline uint32_t
FindScopeObjectIndex(JSScript * script,NestedScopeObject & scope)573 FindScopeObjectIndex(JSScript* script, NestedScopeObject& scope)
574 {
575     ObjectArray* objects = script->objects();
576     HeapPtrObject* vector = objects->vector;
577     unsigned length = objects->length;
578     for (unsigned i = 0; i < length; ++i) {
579         if (vector[i] == &scope)
580             return i;
581     }
582 
583     MOZ_CRASH("Scope not found");
584 }
585 
586 static bool
587 SaveSharedScriptData(ExclusiveContext*, Handle<JSScript*>, SharedScriptData*, uint32_t);
588 
589 enum XDRClassKind {
590     CK_BlockObject = 0,
591     CK_WithObject  = 1,
592     CK_JSFunction  = 2,
593     CK_JSObject    = 3
594 };
595 
596 template<XDRMode mode>
597 bool
XDRScript(XDRState<mode> * xdr,HandleObject enclosingScopeArg,HandleScript enclosingScript,HandleFunction fun,MutableHandleScript scriptp)598 js::XDRScript(XDRState<mode>* xdr, HandleObject enclosingScopeArg, HandleScript enclosingScript,
599               HandleFunction fun, MutableHandleScript scriptp)
600 {
601     /* NB: Keep this in sync with CopyScript. */
602 
603     MOZ_ASSERT(enclosingScopeArg);
604 
605     enum ScriptBits {
606         NoScriptRval,
607         SavedCallerFun,
608         Strict,
609         ContainsDynamicNameAccess,
610         FunHasExtensibleScope,
611         FunNeedsDeclEnvObject,
612         FunHasAnyAliasedFormal,
613         ArgumentsHasVarBinding,
614         NeedsArgsObj,
615         HasMappedArgsObj,
616         FunctionHasThisBinding,
617         IsGeneratorExp,
618         IsLegacyGenerator,
619         IsStarGenerator,
620         OwnSource,
621         ExplicitUseStrict,
622         SelfHosted,
623         HasSingleton,
624         TreatAsRunOnce,
625         HasLazyScript,
626         HasNonSyntacticScope,
627         HasInnerFunctions,
628         NeedsHomeObject,
629         IsDerivedClassConstructor,
630     };
631 
632     uint32_t length, lineno, column, nslots;
633     uint32_t natoms, nsrcnotes, i;
634     uint32_t nconsts, nobjects, nregexps, ntrynotes, nblockscopes, nyieldoffsets;
635     uint32_t prologueLength, version;
636     uint32_t funLength = 0;
637     uint32_t nTypeSets = 0;
638     uint32_t scriptBits = 0;
639 
640     JSContext* cx = xdr->cx();
641     RootedScript script(cx);
642     RootedObject enclosingScope(cx, enclosingScopeArg);
643     natoms = nsrcnotes = 0;
644     nconsts = nobjects = nregexps = ntrynotes = nblockscopes = nyieldoffsets = 0;
645 
646     /* XDR arguments and vars. */
647     uint16_t nargs = 0;
648     uint16_t nblocklocals = 0;
649     uint16_t nbodylevellexicals = 0;
650     uint32_t nvars = 0;
651     uint32_t nunaliasedvars = 0;
652     uint16_t nunaliasedbodylevellexicals = 0;
653     if (mode == XDR_ENCODE) {
654         script = scriptp.get();
655         MOZ_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
656         MOZ_ASSERT(script->functionNonDelazifying() == fun);
657 
658         if (!fun && script->treatAsRunOnce()) {
659             // This is a toplevel or eval script that's runOnce.  We want to
660             // make sure that we're not XDR-saving an object we emitted for
661             // JSOP_OBJECT that then got modified.  So throw if we're not
662             // cloning in JSOP_OBJECT or if we ever didn't clone in it in the
663             // past.
664             const JS::CompartmentOptions& opts = JS::CompartmentOptionsRef(cx);
665             if (!opts.cloneSingletons() || !opts.getSingletonsAsTemplates()) {
666                 JS_ReportError(cx,
667                                "Can't serialize a run-once non-function script "
668                                "when we're not doing singleton cloning");
669                 return false;
670             }
671         }
672 
673         nargs = script->bindings.numArgs();
674         nblocklocals = script->bindings.numBlockScoped();
675         nbodylevellexicals = script->bindings.numBodyLevelLexicals();
676         nvars = script->bindings.numVars();
677         nunaliasedvars = script->bindings.numUnaliasedVars();
678         nunaliasedbodylevellexicals = script->bindings.numUnaliasedBodyLevelLexicals();
679     }
680     if (!xdr->codeUint16(&nargs))
681         return false;
682     if (!xdr->codeUint16(&nblocklocals))
683         return false;
684     if (!xdr->codeUint16(&nbodylevellexicals))
685         return false;
686     if (!xdr->codeUint32(&nvars))
687         return false;
688     if (!xdr->codeUint32(&nunaliasedvars))
689         return false;
690     if (!xdr->codeUint16(&nunaliasedbodylevellexicals))
691         return false;
692 
693     if (mode == XDR_ENCODE)
694         length = script->length();
695     if (!xdr->codeUint32(&length))
696         return false;
697 
698     if (mode == XDR_ENCODE) {
699         prologueLength = script->mainOffset();
700         MOZ_ASSERT(script->getVersion() != JSVERSION_UNKNOWN);
701         version = script->getVersion();
702         lineno = script->lineno();
703         column = script->column();
704         nslots = script->nslots();
705         natoms = script->natoms();
706 
707         nsrcnotes = script->numNotes();
708 
709         if (script->hasConsts())
710             nconsts = script->consts()->length;
711         if (script->hasObjects())
712             nobjects = script->objects()->length;
713         if (script->hasRegexps())
714             nregexps = script->regexps()->length;
715         if (script->hasTrynotes())
716             ntrynotes = script->trynotes()->length;
717         if (script->hasBlockScopes())
718             nblockscopes = script->blockScopes()->length;
719         if (script->hasYieldOffsets())
720             nyieldoffsets = script->yieldOffsets().length();
721 
722         nTypeSets = script->nTypeSets();
723         funLength = script->funLength();
724 
725         if (script->noScriptRval())
726             scriptBits |= (1 << NoScriptRval);
727         if (script->savedCallerFun())
728             scriptBits |= (1 << SavedCallerFun);
729         if (script->strict())
730             scriptBits |= (1 << Strict);
731         if (script->explicitUseStrict())
732             scriptBits |= (1 << ExplicitUseStrict);
733         if (script->selfHosted())
734             scriptBits |= (1 << SelfHosted);
735         if (script->bindingsAccessedDynamically())
736             scriptBits |= (1 << ContainsDynamicNameAccess);
737         if (script->funHasExtensibleScope())
738             scriptBits |= (1 << FunHasExtensibleScope);
739         if (script->funNeedsDeclEnvObject())
740             scriptBits |= (1 << FunNeedsDeclEnvObject);
741         if (script->funHasAnyAliasedFormal())
742             scriptBits |= (1 << FunHasAnyAliasedFormal);
743         if (script->argumentsHasVarBinding())
744             scriptBits |= (1 << ArgumentsHasVarBinding);
745         if (script->analyzedArgsUsage() && script->needsArgsObj())
746             scriptBits |= (1 << NeedsArgsObj);
747         if (script->hasMappedArgsObj())
748             scriptBits |= (1 << HasMappedArgsObj);
749         if (script->functionHasThisBinding())
750             scriptBits |= (1 << FunctionHasThisBinding);
751         if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
752             scriptBits |= (1 << OwnSource);
753         if (script->isGeneratorExp())
754             scriptBits |= (1 << IsGeneratorExp);
755         if (script->isLegacyGenerator())
756             scriptBits |= (1 << IsLegacyGenerator);
757         if (script->isStarGenerator())
758             scriptBits |= (1 << IsStarGenerator);
759         if (script->hasSingletons())
760             scriptBits |= (1 << HasSingleton);
761         if (script->treatAsRunOnce())
762             scriptBits |= (1 << TreatAsRunOnce);
763         if (script->isRelazifiable())
764             scriptBits |= (1 << HasLazyScript);
765         if (script->hasNonSyntacticScope())
766             scriptBits |= (1 << HasNonSyntacticScope);
767         if (script->hasInnerFunctions())
768             scriptBits |= (1 << HasInnerFunctions);
769         if (script->needsHomeObject())
770             scriptBits |= (1 << NeedsHomeObject);
771         if (script->isDerivedClassConstructor())
772             scriptBits |= (1 << IsDerivedClassConstructor);
773     }
774 
775     if (!xdr->codeUint32(&prologueLength))
776         return false;
777     if (!xdr->codeUint32(&version))
778         return false;
779 
780     // To fuse allocations, we need lengths of all embedded arrays early.
781     if (!xdr->codeUint32(&natoms))
782         return false;
783     if (!xdr->codeUint32(&nsrcnotes))
784         return false;
785     if (!xdr->codeUint32(&nconsts))
786         return false;
787     if (!xdr->codeUint32(&nobjects))
788         return false;
789     if (!xdr->codeUint32(&nregexps))
790         return false;
791     if (!xdr->codeUint32(&ntrynotes))
792         return false;
793     if (!xdr->codeUint32(&nblockscopes))
794         return false;
795     if (!xdr->codeUint32(&nyieldoffsets))
796         return false;
797     if (!xdr->codeUint32(&nTypeSets))
798         return false;
799     if (!xdr->codeUint32(&funLength))
800         return false;
801     if (!xdr->codeUint32(&scriptBits))
802         return false;
803 
804     if (mode == XDR_DECODE) {
805         JSVersion version_ = JSVersion(version);
806         MOZ_ASSERT((version_ & VersionFlags::MASK) == unsigned(version_));
807 
808         CompileOptions options(cx);
809         options.setVersion(version_)
810                .setNoScriptRval(!!(scriptBits & (1 << NoScriptRval)))
811                .setSelfHostingMode(!!(scriptBits & (1 << SelfHosted)));
812         RootedScriptSource sourceObject(cx);
813         if (scriptBits & (1 << OwnSource)) {
814             ScriptSource* ss = cx->new_<ScriptSource>();
815             if (!ss)
816                 return false;
817             ScriptSourceHolder ssHolder(ss);
818 
819             /*
820              * We use this CompileOptions only to initialize the
821              * ScriptSourceObject. Most CompileOptions fields aren't used by
822              * ScriptSourceObject, and those that are (element; elementAttributeName)
823              * aren't preserved by XDR. So this can be simple.
824              */
825             CompileOptions options(cx);
826             ss->initFromOptions(cx, options);
827             sourceObject = ScriptSourceObject::create(cx, ss);
828             if (!sourceObject ||
829                 !ScriptSourceObject::initFromOptions(cx, sourceObject, options))
830                 return false;
831         } else {
832             MOZ_ASSERT(enclosingScript);
833             // When decoding, all the scripts and the script source object
834             // are in the same compartment, so the script's source object
835             // should never be a cross-compartment wrapper.
836             MOZ_ASSERT(enclosingScript->sourceObject()->is<ScriptSourceObject>());
837             sourceObject = &enclosingScript->sourceObject()->as<ScriptSourceObject>();
838         }
839 
840         // If the outermost script has a non-syntactic scope, reflect that on
841         // the static scope chain.
842         if (scriptBits & (1 << HasNonSyntacticScope) &&
843             IsStaticGlobalLexicalScope(enclosingScope))
844         {
845             enclosingScope = StaticNonSyntacticScopeObjects::create(cx, enclosingScope);
846             if (!enclosingScope)
847                 return false;
848         }
849 
850         script = JSScript::Create(cx, enclosingScope, !!(scriptBits & (1 << SavedCallerFun)),
851                                   options, sourceObject, 0, 0);
852         if (!script)
853             return false;
854 
855         // Set the script in its function now so that inner scripts to be
856         // decoded may iterate the static scope chain.
857         if (fun) {
858             fun->initScript(script);
859             script->setFunction(fun);
860         }
861     }
862 
863     /* JSScript::partiallyInit assumes script->bindings is fully initialized. */
864     LifoAllocScope las(&cx->tempLifoAlloc());
865     if (!XDRScriptBindings(xdr, las, nargs, nvars, nbodylevellexicals, nblocklocals,
866                            nunaliasedvars, nunaliasedbodylevellexicals, script))
867         return false;
868 
869     if (mode == XDR_DECODE) {
870         if (!JSScript::partiallyInit(cx, script, nconsts, nobjects, nregexps, ntrynotes,
871                                      nblockscopes, nyieldoffsets, nTypeSets))
872         {
873             return false;
874         }
875 
876         MOZ_ASSERT(!script->mainOffset());
877         script->mainOffset_ = prologueLength;
878         script->setLength(length);
879         script->funLength_ = funLength;
880 
881         scriptp.set(script);
882 
883         if (scriptBits & (1 << Strict))
884             script->strict_ = true;
885         if (scriptBits & (1 << ExplicitUseStrict))
886             script->explicitUseStrict_ = true;
887         if (scriptBits & (1 << ContainsDynamicNameAccess))
888             script->bindingsAccessedDynamically_ = true;
889         if (scriptBits & (1 << FunHasExtensibleScope))
890             script->funHasExtensibleScope_ = true;
891         if (scriptBits & (1 << FunNeedsDeclEnvObject))
892             script->funNeedsDeclEnvObject_ = true;
893         if (scriptBits & (1 << FunHasAnyAliasedFormal))
894             script->funHasAnyAliasedFormal_ = true;
895         if (scriptBits & (1 << ArgumentsHasVarBinding))
896             script->setArgumentsHasVarBinding();
897         if (scriptBits & (1 << NeedsArgsObj))
898             script->setNeedsArgsObj(true);
899         if (scriptBits & (1 << HasMappedArgsObj))
900             script->hasMappedArgsObj_ = true;
901         if (scriptBits & (1 << FunctionHasThisBinding))
902             script->functionHasThisBinding_ = true;
903         if (scriptBits & (1 << IsGeneratorExp))
904             script->isGeneratorExp_ = true;
905         if (scriptBits & (1 << HasSingleton))
906             script->hasSingletons_ = true;
907         if (scriptBits & (1 << TreatAsRunOnce))
908             script->treatAsRunOnce_ = true;
909         if (scriptBits & (1 << HasNonSyntacticScope))
910             script->hasNonSyntacticScope_ = true;
911         if (scriptBits & (1 << HasInnerFunctions))
912             script->hasInnerFunctions_ = true;
913         if (scriptBits & (1 << NeedsHomeObject))
914             script->needsHomeObject_ = true;
915         if (scriptBits & (1 << IsDerivedClassConstructor))
916             script->isDerivedClassConstructor_ = true;
917 
918         if (scriptBits & (1 << IsLegacyGenerator)) {
919             MOZ_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
920             script->setGeneratorKind(LegacyGenerator);
921         } else if (scriptBits & (1 << IsStarGenerator))
922             script->setGeneratorKind(StarGenerator);
923     }
924 
925     JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
926     JS_STATIC_ASSERT(sizeof(jssrcnote) == 1);
927 
928     if (scriptBits & (1 << OwnSource)) {
929         if (!script->scriptSource()->performXDR<mode>(xdr))
930             return false;
931     }
932     if (!xdr->codeUint32(&script->sourceStart_))
933         return false;
934     if (!xdr->codeUint32(&script->sourceEnd_))
935         return false;
936 
937     if (!xdr->codeUint32(&lineno) ||
938         !xdr->codeUint32(&column) ||
939         !xdr->codeUint32(&nslots))
940     {
941         return false;
942     }
943 
944     if (mode == XDR_DECODE) {
945         script->lineno_ = lineno;
946         script->column_ = column;
947         script->nslots_ = nslots;
948     }
949 
950     jsbytecode* code = script->code();
951     SharedScriptData* ssd;
952     if (mode == XDR_DECODE) {
953         ssd = SharedScriptData::new_(cx, length, nsrcnotes, natoms);
954         if (!ssd)
955             return false;
956         code = ssd->data;
957         if (natoms != 0) {
958             script->natoms_ = natoms;
959             script->atoms = ssd->atoms();
960         }
961     }
962 
963     if (!xdr->codeBytes(code, length) || !xdr->codeBytes(code + length, nsrcnotes)) {
964         if (mode == XDR_DECODE)
965             js_free(ssd);
966         return false;
967     }
968 
969     for (i = 0; i != natoms; ++i) {
970         if (mode == XDR_DECODE) {
971             RootedAtom tmp(cx);
972             if (!XDRAtom(xdr, &tmp))
973                 return false;
974             script->atoms[i].init(tmp);
975         } else {
976             RootedAtom tmp(cx, script->atoms[i]);
977             if (!XDRAtom(xdr, &tmp))
978                 return false;
979         }
980     }
981 
982     if (mode == XDR_DECODE) {
983         if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
984             return false;
985     }
986 
987     if (nconsts) {
988         HeapValue* vector = script->consts()->vector;
989         RootedValue val(cx);
990         for (i = 0; i != nconsts; ++i) {
991             if (mode == XDR_ENCODE)
992                 val = vector[i];
993             if (!XDRScriptConst(xdr, &val))
994                 return false;
995             if (mode == XDR_DECODE)
996                 vector[i].init(val);
997         }
998     }
999 
1000     /*
1001      * Here looping from 0-to-length to xdr objects is essential to ensure that
1002      * all references to enclosing blocks (via FindScopeObjectIndex below) happen
1003      * after the enclosing block has been XDR'd.
1004      */
1005     for (i = 0; i != nobjects; ++i) {
1006         HeapPtrObject* objp = &script->objects()->vector[i];
1007         XDRClassKind classk;
1008 
1009         if (mode == XDR_ENCODE) {
1010             JSObject* obj = *objp;
1011             if (obj->is<BlockObject>())
1012                 classk = CK_BlockObject;
1013             else if (obj->is<StaticWithObject>())
1014                 classk = CK_WithObject;
1015             else if (obj->is<JSFunction>())
1016                 classk = CK_JSFunction;
1017             else if (obj->is<PlainObject>() || obj->is<ArrayObject>())
1018                 classk = CK_JSObject;
1019             else
1020                 MOZ_CRASH("Cannot encode this class of object.");
1021         }
1022 
1023         if (!xdr->codeEnum32(&classk))
1024             return false;
1025 
1026         switch (classk) {
1027           case CK_BlockObject:
1028           case CK_WithObject: {
1029             /* Code the nested block's enclosing scope. */
1030             uint32_t enclosingStaticScopeIndex = 0;
1031             if (mode == XDR_ENCODE) {
1032                 NestedScopeObject& scope = (*objp)->as<NestedScopeObject>();
1033                 if (NestedScopeObject* enclosing = scope.enclosingNestedScope()) {
1034                     if (IsStaticGlobalLexicalScope(enclosing))
1035                         enclosingStaticScopeIndex = UINT32_MAX;
1036                     else
1037                         enclosingStaticScopeIndex = FindScopeObjectIndex(script, *enclosing);
1038                 } else {
1039                     enclosingStaticScopeIndex = UINT32_MAX;
1040                 }
1041             }
1042             if (!xdr->codeUint32(&enclosingStaticScopeIndex))
1043                 return false;
1044             Rooted<JSObject*> enclosingStaticScope(cx);
1045             if (mode == XDR_DECODE) {
1046                 if (enclosingStaticScopeIndex != UINT32_MAX) {
1047                     MOZ_ASSERT(enclosingStaticScopeIndex < i);
1048                     enclosingStaticScope = script->objects()->vector[enclosingStaticScopeIndex];
1049                 } else {
1050                     // This is not ternary because MSVC can't typecheck the
1051                     // ternary.
1052                     if (fun)
1053                         enclosingStaticScope = fun;
1054                     else
1055                         enclosingStaticScope = enclosingScope;
1056                 }
1057             }
1058 
1059             if (classk == CK_BlockObject) {
1060                 Rooted<StaticBlockObject*> tmp(cx, static_cast<StaticBlockObject*>(objp->get()));
1061                 if (!XDRStaticBlockObject(xdr, enclosingStaticScope, &tmp))
1062                     return false;
1063                 *objp = tmp;
1064             } else {
1065                 Rooted<StaticWithObject*> tmp(cx, static_cast<StaticWithObject*>(objp->get()));
1066                 if (!XDRStaticWithObject(xdr, enclosingStaticScope, &tmp))
1067                     return false;
1068                 *objp = tmp;
1069             }
1070             break;
1071           }
1072 
1073           case CK_JSFunction: {
1074             /* Code the nested function's enclosing scope. */
1075             uint32_t funEnclosingScopeIndex = 0;
1076             RootedObject funEnclosingScope(cx);
1077             if (mode == XDR_ENCODE) {
1078                 RootedFunction function(cx, &(*objp)->as<JSFunction>());
1079 
1080                 if (function->isInterpretedLazy())
1081                     funEnclosingScope = function->lazyScript()->enclosingScope();
1082                 else if (function->isInterpreted())
1083                     funEnclosingScope = function->nonLazyScript()->enclosingStaticScope();
1084                 else {
1085                     MOZ_ASSERT(function->isAsmJSNative());
1086                     JS_ReportError(cx, "AsmJS modules are not yet supported in XDR serialization.");
1087                     return false;
1088                 }
1089 
1090                 StaticScopeIter<NoGC> ssi(funEnclosingScope);
1091 
1092                 // Starting from a nested function, hitting a non-syntactic
1093                 // scope on the static scope chain means that its enclosing
1094                 // function has a non-syntactic scope. Nested functions
1095                 // themselves never have non-syntactic scope chains.
1096                 if (ssi.done() ||
1097                     ssi.type() == StaticScopeIter<NoGC>::NonSyntactic ||
1098                     ssi.type() == StaticScopeIter<NoGC>::Function)
1099                 {
1100                     MOZ_ASSERT_IF(ssi.done() || ssi.type() != StaticScopeIter<NoGC>::Function, !fun);
1101                     funEnclosingScopeIndex = UINT32_MAX;
1102                 } else if (ssi.type() == StaticScopeIter<NoGC>::Block) {
1103                     if (ssi.block().isGlobal()) {
1104                         funEnclosingScopeIndex = UINT32_MAX;
1105                     } else {
1106                         funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.block());
1107                         MOZ_ASSERT(funEnclosingScopeIndex < i);
1108                     }
1109                 } else {
1110                     funEnclosingScopeIndex = FindScopeObjectIndex(script, ssi.staticWith());
1111                     MOZ_ASSERT(funEnclosingScopeIndex < i);
1112                 }
1113             }
1114 
1115             if (!xdr->codeUint32(&funEnclosingScopeIndex))
1116                 return false;
1117 
1118             if (mode == XDR_DECODE) {
1119                 if (funEnclosingScopeIndex == UINT32_MAX) {
1120                     // This is not ternary because MSVC can't typecheck the
1121                     // ternary.
1122                     if (fun)
1123                         funEnclosingScope = fun;
1124                     else
1125                         funEnclosingScope = enclosingScope;
1126                 } else {
1127                     MOZ_ASSERT(funEnclosingScopeIndex < i);
1128                     funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
1129                 }
1130             }
1131 
1132             // Code nested function and script.
1133             RootedFunction tmp(cx);
1134             if (mode == XDR_ENCODE)
1135                 tmp = &(*objp)->as<JSFunction>();
1136             if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
1137                 return false;
1138             *objp = tmp;
1139             break;
1140           }
1141 
1142           case CK_JSObject: {
1143             /* Code object literal. */
1144             RootedObject tmp(cx, *objp);
1145             if (!XDRObjectLiteral(xdr, &tmp))
1146                 return false;
1147             *objp = tmp;
1148             break;
1149           }
1150 
1151           default: {
1152             MOZ_ASSERT(false, "Unknown class kind.");
1153             return false;
1154           }
1155         }
1156     }
1157 
1158     for (i = 0; i != nregexps; ++i) {
1159         Rooted<RegExpObject*> regexp(cx);
1160         if (mode == XDR_ENCODE)
1161             regexp = &script->regexps()->vector[i]->as<RegExpObject>();
1162         if (!XDRScriptRegExpObject(xdr, &regexp))
1163             return false;
1164         if (mode == XDR_DECODE)
1165             script->regexps()->vector[i] = regexp;
1166     }
1167 
1168     if (ntrynotes != 0) {
1169         JSTryNote* tnfirst = script->trynotes()->vector;
1170         MOZ_ASSERT(script->trynotes()->length == ntrynotes);
1171         JSTryNote* tn = tnfirst + ntrynotes;
1172         do {
1173             --tn;
1174             if (!xdr->codeUint8(&tn->kind) ||
1175                 !xdr->codeUint32(&tn->stackDepth) ||
1176                 !xdr->codeUint32(&tn->start) ||
1177                 !xdr->codeUint32(&tn->length)) {
1178                 return false;
1179             }
1180         } while (tn != tnfirst);
1181     }
1182 
1183     for (i = 0; i < nblockscopes; ++i) {
1184         BlockScopeNote* note = &script->blockScopes()->vector[i];
1185         if (!xdr->codeUint32(&note->index) ||
1186             !xdr->codeUint32(&note->start) ||
1187             !xdr->codeUint32(&note->length) ||
1188             !xdr->codeUint32(&note->parent))
1189         {
1190             return false;
1191         }
1192     }
1193 
1194     for (i = 0; i < nyieldoffsets; ++i) {
1195         uint32_t* offset = &script->yieldOffsets()[i];
1196         if (!xdr->codeUint32(offset))
1197             return false;
1198     }
1199 
1200     if (scriptBits & (1 << HasLazyScript)) {
1201         Rooted<LazyScript*> lazy(cx);
1202         if (mode == XDR_ENCODE)
1203             lazy = script->maybeLazyScript();
1204 
1205         if (!XDRRelazificationInfo(xdr, fun, script, enclosingScope, &lazy))
1206             return false;
1207 
1208         if (mode == XDR_DECODE)
1209             script->setLazyScript(lazy);
1210     }
1211 
1212     if (mode == XDR_DECODE) {
1213         scriptp.set(script);
1214 
1215         /* see BytecodeEmitter::tellDebuggerAboutCompiledScript */
1216         if (!fun)
1217             Debugger::onNewScript(cx, script);
1218     }
1219 
1220     return true;
1221 }
1222 
1223 template bool
1224 js::XDRScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript, HandleFunction,
1225               MutableHandleScript);
1226 
1227 template bool
1228 js::XDRScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript, HandleFunction,
1229               MutableHandleScript);
1230 
1231 template<XDRMode mode>
1232 bool
XDRLazyScript(XDRState<mode> * xdr,HandleObject enclosingScope,HandleScript enclosingScript,HandleFunction fun,MutableHandle<LazyScript * > lazy)1233 js::XDRLazyScript(XDRState<mode>* xdr, HandleObject enclosingScope, HandleScript enclosingScript,
1234                   HandleFunction fun, MutableHandle<LazyScript*> lazy)
1235 {
1236     JSContext* cx = xdr->cx();
1237 
1238     {
1239         uint32_t begin;
1240         uint32_t end;
1241         uint32_t lineno;
1242         uint32_t column;
1243         uint64_t packedFields;
1244 
1245         if (mode == XDR_ENCODE) {
1246             // Note: it's possible the LazyScript has a non-null script_ pointer
1247             // to a JSScript. We don't encode it: we can just delazify the
1248             // lazy script.
1249 
1250             MOZ_ASSERT(fun == lazy->functionNonDelazifying());
1251 
1252             begin = lazy->begin();
1253             end = lazy->end();
1254             lineno = lazy->lineno();
1255             column = lazy->column();
1256             packedFields = lazy->packedFields();
1257         }
1258 
1259         if (!xdr->codeUint32(&begin) || !xdr->codeUint32(&end) ||
1260             !xdr->codeUint32(&lineno) || !xdr->codeUint32(&column) ||
1261             !xdr->codeUint64(&packedFields))
1262         {
1263             return false;
1264         }
1265 
1266         if (mode == XDR_DECODE) {
1267             lazy.set(LazyScript::Create(cx, fun, nullptr, enclosingScope, enclosingScript,
1268                                         packedFields, begin, end, lineno, column));
1269             if (!lazy)
1270                 return false;
1271             fun->initLazyScript(lazy);
1272         }
1273     }
1274 
1275     // Code free variables.
1276     if (!XDRLazyFreeVariables(xdr, lazy))
1277         return false;
1278 
1279     // Code inner functions.
1280     {
1281         RootedFunction func(cx);
1282         HeapPtrFunction* innerFunctions = lazy->innerFunctions();
1283         size_t numInnerFunctions = lazy->numInnerFunctions();
1284         for (size_t i = 0; i < numInnerFunctions; i++) {
1285             if (mode == XDR_ENCODE)
1286                 func = innerFunctions[i];
1287 
1288             if (!XDRInterpretedFunction(xdr, fun, enclosingScript, &func))
1289                 return false;
1290 
1291             if (mode == XDR_DECODE)
1292                 innerFunctions[i] = func;
1293         }
1294     }
1295 
1296     return true;
1297 }
1298 
1299 template bool
1300 js::XDRLazyScript(XDRState<XDR_ENCODE>*, HandleObject, HandleScript,
1301                   HandleFunction, MutableHandle<LazyScript*>);
1302 
1303 template bool
1304 js::XDRLazyScript(XDRState<XDR_DECODE>*, HandleObject, HandleScript,
1305                   HandleFunction, MutableHandle<LazyScript*>);
1306 
1307 void
setSourceObject(JSObject * object)1308 JSScript::setSourceObject(JSObject* object)
1309 {
1310     MOZ_ASSERT(compartment() == object->compartment());
1311     sourceObject_ = object;
1312 }
1313 
1314 js::ScriptSourceObject&
scriptSourceUnwrap() const1315 JSScript::scriptSourceUnwrap() const {
1316     return UncheckedUnwrap(sourceObject())->as<ScriptSourceObject>();
1317 }
1318 
1319 js::ScriptSource*
scriptSource() const1320 JSScript::scriptSource() const {
1321     return scriptSourceUnwrap().source();
1322 }
1323 
1324 js::ScriptSource*
maybeForwardedScriptSource() const1325 JSScript::maybeForwardedScriptSource() const {
1326     return UncheckedUnwrap(MaybeForwarded(sourceObject()))->as<ScriptSourceObject>().source();
1327 }
1328 
1329 bool
initScriptCounts(JSContext * cx)1330 JSScript::initScriptCounts(JSContext* cx)
1331 {
1332     MOZ_ASSERT(!hasScriptCounts());
1333 
1334     // Record all pc which are the first instruction of a basic block.
1335     mozilla::Vector<jsbytecode*, 16, SystemAllocPolicy> jumpTargets;
1336     jsbytecode* end = codeEnd();
1337     jsbytecode* mainEntry = main();
1338     for (jsbytecode* pc = code(); pc != end; pc = GetNextPc(pc)) {
1339         if (pc == mainEntry) {
1340             if (!jumpTargets.append(pc))
1341                 return false;
1342         }
1343 
1344         bool jump = IsJumpOpcode(JSOp(*pc));
1345         if (jump) {
1346             jsbytecode* target = pc + GET_JUMP_OFFSET(pc);
1347             if (!jumpTargets.append(target))
1348                 return false;
1349 
1350             if (BytecodeFallsThrough(JSOp(*pc))) {
1351                 jsbytecode* fallthrough = GetNextPc(pc);
1352                 if (!jumpTargets.append(fallthrough))
1353                     return false;
1354             }
1355         }
1356 
1357         if (JSOp(*pc) == JSOP_TABLESWITCH) {
1358             jsbytecode* pc2 = pc;
1359             int32_t len = GET_JUMP_OFFSET(pc2);
1360 
1361             // Default target.
1362             if (!jumpTargets.append(pc + len))
1363                 return false;
1364 
1365             pc2 += JUMP_OFFSET_LEN;
1366             int32_t low = GET_JUMP_OFFSET(pc2);
1367             pc2 += JUMP_OFFSET_LEN;
1368             int32_t high = GET_JUMP_OFFSET(pc2);
1369 
1370             for (int i = 0; i < high-low+1; i++) {
1371                 pc2 += JUMP_OFFSET_LEN;
1372                 int32_t off = (int32_t) GET_JUMP_OFFSET(pc2);
1373                 if (off) {
1374                     // Case (i + low)
1375                     if (!jumpTargets.append(pc + off))
1376                         return false;
1377                 }
1378             }
1379         }
1380     }
1381 
1382     // Mark catch/finally blocks as being jump targets.
1383     if (hasTrynotes()) {
1384         JSTryNote* tn = trynotes()->vector;
1385         JSTryNote* tnlimit = tn + trynotes()->length;
1386         for (; tn < tnlimit; tn++) {
1387             jsbytecode* tryStart = mainEntry + tn->start;
1388             jsbytecode* tryPc = tryStart - 1;
1389             if (JSOp(*tryPc) != JSOP_TRY)
1390                 continue;
1391 
1392             jsbytecode* tryTarget = tryStart + tn->length;
1393             if (!jumpTargets.append(tryTarget))
1394                 return false;
1395         }
1396     }
1397 
1398     // Sort all pc, and remove duplicates.
1399     std::sort(jumpTargets.begin(), jumpTargets.end());
1400     auto last = std::unique(jumpTargets.begin(), jumpTargets.end());
1401     jumpTargets.erase(last, jumpTargets.end());
1402 
1403     // Initialize all PCCounts counters to 0.
1404     ScriptCounts::PCCountsVector base;
1405     if (!base.reserve(jumpTargets.length()))
1406         return false;
1407 
1408     for (size_t i = 0; i < jumpTargets.length(); i++)
1409         base.infallibleEmplaceBack(pcToOffset(jumpTargets[i]));
1410 
1411     // Create compartment's scriptCountsMap if necessary.
1412     ScriptCountsMap* map = compartment()->scriptCountsMap;
1413     if (!map) {
1414         map = cx->new_<ScriptCountsMap>();
1415         if (!map)
1416             return false;
1417 
1418         if (!map->init()) {
1419             js_delete(map);
1420             ReportOutOfMemory(cx);
1421             return false;
1422         }
1423 
1424         compartment()->scriptCountsMap = map;
1425     }
1426 
1427     // Register the current ScriptCount in the compartment's map.
1428     if (!map->putNew(this, Move(base)))
1429         return false;
1430 
1431     // safe to set this;  we can't fail after this point.
1432     hasScriptCounts_ = true;
1433 
1434     // Enable interrupts in any interpreter frames running on this script. This
1435     // is used to let the interpreter increment the PCCounts, if present.
1436     for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
1437         if (iter->isInterpreter())
1438             iter->asInterpreter()->enableInterruptsIfRunning(this);
1439     }
1440 
1441     return true;
1442 }
1443 
GetScriptCountsMapEntry(JSScript * script)1444 static inline ScriptCountsMap::Ptr GetScriptCountsMapEntry(JSScript* script)
1445 {
1446     MOZ_ASSERT(script->hasScriptCounts());
1447     ScriptCountsMap* map = script->compartment()->scriptCountsMap;
1448     ScriptCountsMap::Ptr p = map->lookup(script);
1449     MOZ_ASSERT(p);
1450     return p;
1451 }
1452 
1453 ScriptCounts&
getScriptCounts()1454 JSScript::getScriptCounts()
1455 {
1456     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1457     return p->value();
1458 }
1459 
1460 js::PCCounts*
maybeGetPCCounts(size_t offset)1461 ScriptCounts::maybeGetPCCounts(size_t offset) {
1462     PCCounts searched = PCCounts(offset);
1463     PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
1464     if (elem == pcCounts_.end() || elem->pcOffset() != offset)
1465         return nullptr;
1466     return elem;
1467 }
1468 
1469 const js::PCCounts*
maybeGetPCCounts(size_t offset) const1470 ScriptCounts::maybeGetPCCounts(size_t offset) const {
1471     PCCounts searched = PCCounts(offset);
1472     const PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
1473     if (elem == pcCounts_.end() || elem->pcOffset() != offset)
1474         return nullptr;
1475     return elem;
1476 }
1477 
1478 js::PCCounts*
getImmediatePrecedingPCCounts(size_t offset)1479 ScriptCounts::getImmediatePrecedingPCCounts(size_t offset)
1480 {
1481     PCCounts searched = PCCounts(offset);
1482     PCCounts* elem = std::lower_bound(pcCounts_.begin(), pcCounts_.end(), searched);
1483     if (elem == pcCounts_.end())
1484         return &pcCounts_.back();
1485     if (elem->pcOffset() == offset)
1486         return elem;
1487     if (elem != pcCounts_.begin())
1488         return elem - 1;
1489     return nullptr;
1490 }
1491 
1492 const js::PCCounts*
maybeGetThrowCounts(size_t offset) const1493 ScriptCounts::maybeGetThrowCounts(size_t offset) const {
1494     PCCounts searched = PCCounts(offset);
1495     const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
1496     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
1497         return nullptr;
1498     return elem;
1499 }
1500 
1501 const js::PCCounts*
getImmediatePrecedingThrowCounts(size_t offset) const1502 ScriptCounts::getImmediatePrecedingThrowCounts(size_t offset) const
1503 {
1504     PCCounts searched = PCCounts(offset);
1505     const PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
1506     if (elem == throwCounts_.end()) {
1507         if (throwCounts_.begin() == throwCounts_.end())
1508             return nullptr;
1509         return &throwCounts_.back();
1510     }
1511     if (elem->pcOffset() == offset)
1512         return elem;
1513     if (elem != throwCounts_.begin())
1514         return elem - 1;
1515     return nullptr;
1516 }
1517 
1518 js::PCCounts*
getThrowCounts(size_t offset)1519 ScriptCounts::getThrowCounts(size_t offset) {
1520     PCCounts searched = PCCounts(offset);
1521     PCCounts* elem = std::lower_bound(throwCounts_.begin(), throwCounts_.end(), searched);
1522     if (elem == throwCounts_.end() || elem->pcOffset() != offset)
1523         elem = throwCounts_.insert(elem, searched);
1524     return elem;
1525 }
1526 
1527 void
setIonScript(JSContext * maybecx,js::jit::IonScript * ionScript)1528 JSScript::setIonScript(JSContext* maybecx, js::jit::IonScript* ionScript)
1529 {
1530     MOZ_ASSERT_IF(ionScript != ION_DISABLED_SCRIPT, !baselineScript()->hasPendingIonBuilder());
1531     if (hasIonScript())
1532         js::jit::IonScript::writeBarrierPre(zone(), ion);
1533     ion = ionScript;
1534     MOZ_ASSERT_IF(hasIonScript(), hasBaselineScript());
1535     updateBaselineOrIonRaw(maybecx);
1536 }
1537 
1538 js::PCCounts*
maybeGetPCCounts(jsbytecode * pc)1539 JSScript::maybeGetPCCounts(jsbytecode* pc) {
1540     MOZ_ASSERT(containsPC(pc));
1541     return getScriptCounts().maybeGetPCCounts(pcToOffset(pc));
1542 }
1543 
1544 const js::PCCounts*
maybeGetThrowCounts(jsbytecode * pc)1545 JSScript::maybeGetThrowCounts(jsbytecode* pc) {
1546     MOZ_ASSERT(containsPC(pc));
1547     return getScriptCounts().maybeGetThrowCounts(pcToOffset(pc));
1548 }
1549 
1550 js::PCCounts*
getThrowCounts(jsbytecode * pc)1551 JSScript::getThrowCounts(jsbytecode* pc) {
1552     MOZ_ASSERT(containsPC(pc));
1553     return getScriptCounts().getThrowCounts(pcToOffset(pc));
1554 }
1555 
1556 uint64_t
getHitCount(jsbytecode * pc)1557 JSScript::getHitCount(jsbytecode* pc)
1558 {
1559     MOZ_ASSERT(containsPC(pc));
1560     if (pc < main())
1561         pc = main();
1562 
1563     ScriptCounts& sc = getScriptCounts();
1564     size_t targetOffset = pcToOffset(pc);
1565     const js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(targetOffset);
1566     if (!baseCount)
1567         return 0;
1568     if (baseCount->pcOffset() == targetOffset)
1569         return baseCount->numExec();
1570     MOZ_ASSERT(baseCount->pcOffset() < targetOffset);
1571     uint64_t count = baseCount->numExec();
1572     do {
1573         const js::PCCounts* throwCount = sc.getImmediatePrecedingThrowCounts(targetOffset);
1574         if (!throwCount)
1575             return count;
1576         if (throwCount->pcOffset() <= baseCount->pcOffset())
1577             return count;
1578         count -= throwCount->numExec();
1579         targetOffset = throwCount->pcOffset() - 1;
1580     } while (true);
1581 }
1582 
1583 void
incHitCount(jsbytecode * pc)1584 JSScript::incHitCount(jsbytecode* pc)
1585 {
1586     MOZ_ASSERT(containsPC(pc));
1587     if (pc < main())
1588         pc = main();
1589 
1590     ScriptCounts& sc = getScriptCounts();
1591     js::PCCounts* baseCount = sc.getImmediatePrecedingPCCounts(pcToOffset(pc));
1592     if (!baseCount)
1593         return;
1594     baseCount->numExec()++;
1595 }
1596 
1597 void
addIonCounts(jit::IonScriptCounts * ionCounts)1598 JSScript::addIonCounts(jit::IonScriptCounts* ionCounts)
1599 {
1600     ScriptCounts& sc = getScriptCounts();
1601     if (sc.ionCounts_)
1602         ionCounts->setPrevious(sc.ionCounts_);
1603     sc.ionCounts_ = ionCounts;
1604 }
1605 
1606 jit::IonScriptCounts*
getIonCounts()1607 JSScript::getIonCounts()
1608 {
1609     return getScriptCounts().ionCounts_;
1610 }
1611 
1612 void
takeOverScriptCountsMapEntry(ScriptCounts * entryValue)1613 JSScript::takeOverScriptCountsMapEntry(ScriptCounts* entryValue)
1614 {
1615 #ifdef DEBUG
1616     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1617     MOZ_ASSERT(entryValue == &p->value());
1618 #endif
1619     hasScriptCounts_ = false;
1620 }
1621 
1622 void
releaseScriptCounts(ScriptCounts * counts)1623 JSScript::releaseScriptCounts(ScriptCounts* counts)
1624 {
1625     ScriptCountsMap::Ptr p = GetScriptCountsMapEntry(this);
1626     *counts = Move(p->value());
1627     compartment()->scriptCountsMap->remove(p);
1628     hasScriptCounts_ = false;
1629 }
1630 
1631 void
destroyScriptCounts(FreeOp * fop)1632 JSScript::destroyScriptCounts(FreeOp* fop)
1633 {
1634     if (hasScriptCounts()) {
1635         ScriptCounts scriptCounts;
1636         releaseScriptCounts(&scriptCounts);
1637     }
1638 }
1639 
1640 void
trace(JSTracer * trc,JSObject * obj)1641 ScriptSourceObject::trace(JSTracer* trc, JSObject* obj)
1642 {
1643     ScriptSourceObject* sso = static_cast<ScriptSourceObject*>(obj);
1644 
1645     // Don't trip over the poison 'not yet initialized' values.
1646     if (!sso->getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC)) {
1647         JSScript* script = sso->introductionScript();
1648         if (script) {
1649             TraceManuallyBarrieredEdge(trc, &script, "ScriptSourceObject introductionScript");
1650             sso->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(script));
1651         }
1652     }
1653 }
1654 
1655 void
finalize(FreeOp * fop,JSObject * obj)1656 ScriptSourceObject::finalize(FreeOp* fop, JSObject* obj)
1657 {
1658     ScriptSourceObject* sso = &obj->as<ScriptSourceObject>();
1659 
1660     // If code coverage is enabled, record the filename associated with this
1661     // source object.
1662     if (fop->runtime()->lcovOutput.isEnabled())
1663         sso->compartment()->lcovOutput.collectSourceFile(sso->compartment(), sso);
1664 
1665     sso->source()->decref();
1666     sso->setReservedSlot(SOURCE_SLOT, PrivateValue(nullptr));
1667 }
1668 
1669 const Class ScriptSourceObject::class_ = {
1670     "ScriptSource",
1671     JSCLASS_HAS_RESERVED_SLOTS(RESERVED_SLOTS) |
1672     JSCLASS_IS_ANONYMOUS,
1673     nullptr, /* addProperty */
1674     nullptr, /* delProperty */
1675     nullptr, /* getProperty */
1676     nullptr, /* setProperty */
1677     nullptr, /* enumerate */
1678     nullptr, /* resolve */
1679     nullptr, /* mayResolve */
1680     finalize,
1681     nullptr, /* call */
1682     nullptr, /* hasInstance */
1683     nullptr, /* construct */
1684     trace
1685 };
1686 
1687 ScriptSourceObject*
create(ExclusiveContext * cx,ScriptSource * source)1688 ScriptSourceObject::create(ExclusiveContext* cx, ScriptSource* source)
1689 {
1690     RootedObject object(cx, NewObjectWithGivenProto(cx, &class_, nullptr));
1691     if (!object)
1692         return nullptr;
1693     RootedScriptSource sourceObject(cx, &object->as<ScriptSourceObject>());
1694 
1695     source->incref();    // The matching decref is in ScriptSourceObject::finalize.
1696     sourceObject->initReservedSlot(SOURCE_SLOT, PrivateValue(source));
1697 
1698     // The remaining slots should eventually be populated by a call to
1699     // initFromOptions. Poison them until that point.
1700     sourceObject->initReservedSlot(ELEMENT_SLOT, MagicValue(JS_GENERIC_MAGIC));
1701     sourceObject->initReservedSlot(ELEMENT_PROPERTY_SLOT, MagicValue(JS_GENERIC_MAGIC));
1702     sourceObject->initReservedSlot(INTRODUCTION_SCRIPT_SLOT, MagicValue(JS_GENERIC_MAGIC));
1703 
1704     return sourceObject;
1705 }
1706 
1707 /* static */ bool
initFromOptions(JSContext * cx,HandleScriptSource source,const ReadOnlyCompileOptions & options)1708 ScriptSourceObject::initFromOptions(JSContext* cx, HandleScriptSource source,
1709                                     const ReadOnlyCompileOptions& options)
1710 {
1711     releaseAssertSameCompartment(cx, source);
1712     MOZ_ASSERT(source->getReservedSlot(ELEMENT_SLOT).isMagic(JS_GENERIC_MAGIC));
1713     MOZ_ASSERT(source->getReservedSlot(ELEMENT_PROPERTY_SLOT).isMagic(JS_GENERIC_MAGIC));
1714     MOZ_ASSERT(source->getReservedSlot(INTRODUCTION_SCRIPT_SLOT).isMagic(JS_GENERIC_MAGIC));
1715 
1716     RootedValue element(cx, ObjectOrNullValue(options.element()));
1717     if (!cx->compartment()->wrap(cx, &element))
1718         return false;
1719     source->setReservedSlot(ELEMENT_SLOT, element);
1720 
1721     RootedValue elementAttributeName(cx);
1722     if (options.elementAttributeName())
1723         elementAttributeName = StringValue(options.elementAttributeName());
1724     else
1725         elementAttributeName = UndefinedValue();
1726     if (!cx->compartment()->wrap(cx, &elementAttributeName))
1727         return false;
1728     source->setReservedSlot(ELEMENT_PROPERTY_SLOT, elementAttributeName);
1729 
1730     // There is no equivalent of cross-compartment wrappers for scripts. If the
1731     // introduction script and ScriptSourceObject are in different compartments,
1732     // we would be creating a cross-compartment script reference, which is
1733     // forbidden. In that case, simply don't bother to retain the introduction
1734     // script.
1735     if (options.introductionScript() &&
1736         options.introductionScript()->compartment() == cx->compartment())
1737     {
1738         source->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, PrivateValue(options.introductionScript()));
1739     } else {
1740         source->setReservedSlot(INTRODUCTION_SCRIPT_SLOT, UndefinedValue());
1741     }
1742 
1743     return true;
1744 }
1745 
1746 /* static */ bool
loadSource(JSContext * cx,ScriptSource * ss,bool * worked)1747 JSScript::loadSource(JSContext* cx, ScriptSource* ss, bool* worked)
1748 {
1749     MOZ_ASSERT(!ss->hasSourceData());
1750     *worked = false;
1751     if (!cx->runtime()->sourceHook || !ss->sourceRetrievable())
1752         return true;
1753     char16_t* src = nullptr;
1754     size_t length;
1755     if (!cx->runtime()->sourceHook->load(cx, ss->filename(), &src, &length))
1756         return false;
1757     if (!src)
1758         return true;
1759     ss->setSource(src, length);
1760     *worked = true;
1761     return true;
1762 }
1763 
1764 JSFlatString*
sourceData(JSContext * cx)1765 JSScript::sourceData(JSContext* cx)
1766 {
1767     MOZ_ASSERT(scriptSource()->hasSourceData());
1768     return scriptSource()->substring(cx, sourceStart(), sourceEnd());
1769 }
1770 
AutoHoldEntry()1771 UncompressedSourceCache::AutoHoldEntry::AutoHoldEntry()
1772   : cache_(nullptr), source_(nullptr), charsToFree_(nullptr)
1773 {
1774 }
1775 
1776 void
holdEntry(UncompressedSourceCache * cache,ScriptSource * source)1777 UncompressedSourceCache::AutoHoldEntry::holdEntry(UncompressedSourceCache* cache, ScriptSource* source)
1778 {
1779     // Initialise the holder for a specific cache and script source. This will
1780     // hold on to the cached source chars in the event that the cache is purged.
1781     MOZ_ASSERT(!cache_ && !source_ && !charsToFree_);
1782     cache_ = cache;
1783     source_ = source;
1784 }
1785 
1786 void
deferDelete(const char16_t * chars)1787 UncompressedSourceCache::AutoHoldEntry::deferDelete(const char16_t* chars)
1788 {
1789     // Take ownership of source chars now the cache is being purged. Remove our
1790     // reference to the ScriptSource which might soon be destroyed.
1791     MOZ_ASSERT(cache_ && source_ && !charsToFree_);
1792     cache_ = nullptr;
1793     source_ = nullptr;
1794     charsToFree_ = chars;
1795 }
1796 
~AutoHoldEntry()1797 UncompressedSourceCache::AutoHoldEntry::~AutoHoldEntry()
1798 {
1799     // The holder is going out of scope. If it has taken ownership of cached
1800     // chars then delete them, otherwise unregister ourself with the cache.
1801     if (charsToFree_) {
1802         MOZ_ASSERT(!cache_ && !source_);
1803         js_free(const_cast<char16_t*>(charsToFree_));
1804     } else if (cache_) {
1805         MOZ_ASSERT(source_);
1806         cache_->releaseEntry(*this);
1807     }
1808 }
1809 
1810 void
holdEntry(AutoHoldEntry & holder,ScriptSource * ss)1811 UncompressedSourceCache::holdEntry(AutoHoldEntry& holder, ScriptSource* ss)
1812 {
1813     MOZ_ASSERT(!holder_);
1814     holder.holdEntry(this, ss);
1815     holder_ = &holder;
1816 }
1817 
1818 void
releaseEntry(AutoHoldEntry & holder)1819 UncompressedSourceCache::releaseEntry(AutoHoldEntry& holder)
1820 {
1821     MOZ_ASSERT(holder_ == &holder);
1822     holder_ = nullptr;
1823 }
1824 
1825 const char16_t*
lookup(ScriptSource * ss,AutoHoldEntry & holder)1826 UncompressedSourceCache::lookup(ScriptSource* ss, AutoHoldEntry& holder)
1827 {
1828     MOZ_ASSERT(!holder_);
1829     if (!map_)
1830         return nullptr;
1831     if (Map::Ptr p = map_->lookup(ss)) {
1832         holdEntry(holder, ss);
1833         return p->value();
1834     }
1835     return nullptr;
1836 }
1837 
1838 bool
put(ScriptSource * ss,const char16_t * str,AutoHoldEntry & holder)1839 UncompressedSourceCache::put(ScriptSource* ss, const char16_t* str, AutoHoldEntry& holder)
1840 {
1841     MOZ_ASSERT(!holder_);
1842 
1843     if (!map_) {
1844         map_ = js_new<Map>();
1845         if (!map_)
1846             return false;
1847 
1848         if (!map_->init()) {
1849             js_delete(map_);
1850             map_ = nullptr;
1851             return false;
1852         }
1853     }
1854 
1855     if (!map_->put(ss, str))
1856         return false;
1857 
1858     holdEntry(holder, ss);
1859     return true;
1860 }
1861 
1862 void
purge()1863 UncompressedSourceCache::purge()
1864 {
1865     if (!map_)
1866         return;
1867 
1868     for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
1869         const char16_t* chars = r.front().value();
1870         if (holder_ && r.front().key() == holder_->source()) {
1871             holder_->deferDelete(chars);
1872             holder_ = nullptr;
1873         } else {
1874             js_free(const_cast<char16_t*>(chars));
1875         }
1876     }
1877 
1878     js_delete(map_);
1879     map_ = nullptr;
1880 }
1881 
1882 size_t
sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)1883 UncompressedSourceCache::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf)
1884 {
1885     size_t n = 0;
1886     if (map_ && !map_->empty()) {
1887         n += map_->sizeOfIncludingThis(mallocSizeOf);
1888         for (Map::Range r = map_->all(); !r.empty(); r.popFront()) {
1889             const char16_t* v = r.front().value();
1890             n += mallocSizeOf(v);
1891         }
1892     }
1893     return n;
1894 }
1895 
1896 const char16_t*
chars(JSContext * cx,UncompressedSourceCache::AutoHoldEntry & holder)1897 ScriptSource::chars(JSContext* cx, UncompressedSourceCache::AutoHoldEntry& holder)
1898 {
1899     switch (dataType) {
1900       case DataUncompressed:
1901         return uncompressedChars();
1902 
1903       case DataCompressed: {
1904         if (const char16_t* decompressed = cx->runtime()->uncompressedSourceCache.lookup(this, holder))
1905             return decompressed;
1906 
1907         const size_t nbytes = sizeof(char16_t) * (length_ + 1);
1908         char16_t* decompressed = static_cast<char16_t*>(js_malloc(nbytes));
1909         if (!decompressed) {
1910             JS_ReportOutOfMemory(cx);
1911             return nullptr;
1912         }
1913 
1914         if (!DecompressString((const unsigned char*) compressedData(), compressedBytes(),
1915                               reinterpret_cast<unsigned char*>(decompressed), nbytes)) {
1916             JS_ReportOutOfMemory(cx);
1917             js_free(decompressed);
1918             return nullptr;
1919         }
1920 
1921         decompressed[length_] = 0;
1922 
1923         if (!cx->runtime()->uncompressedSourceCache.put(this, decompressed, holder)) {
1924             JS_ReportOutOfMemory(cx);
1925             js_free(decompressed);
1926             return nullptr;
1927         }
1928 
1929         return decompressed;
1930       }
1931 
1932       case DataParent:
1933         return parent()->chars(cx, holder);
1934 
1935       default:
1936         MOZ_CRASH();
1937     }
1938 }
1939 
1940 JSFlatString*
substring(JSContext * cx,uint32_t start,uint32_t stop)1941 ScriptSource::substring(JSContext* cx, uint32_t start, uint32_t stop)
1942 {
1943     MOZ_ASSERT(start <= stop);
1944     UncompressedSourceCache::AutoHoldEntry holder;
1945     const char16_t* chars = this->chars(cx, holder);
1946     if (!chars)
1947         return nullptr;
1948     return NewStringCopyN<CanGC>(cx, chars + start, stop - start);
1949 }
1950 
1951 JSFlatString*
substringDontDeflate(JSContext * cx,uint32_t start,uint32_t stop)1952 ScriptSource::substringDontDeflate(JSContext* cx, uint32_t start, uint32_t stop)
1953 {
1954     MOZ_ASSERT(start <= stop);
1955     UncompressedSourceCache::AutoHoldEntry holder;
1956     const char16_t* chars = this->chars(cx, holder);
1957     if (!chars)
1958         return nullptr;
1959     return NewStringCopyNDontDeflate<CanGC>(cx, chars + start, stop - start);
1960 }
1961 
1962 void
setSource(const char16_t * chars,size_t length,bool ownsChars)1963 ScriptSource::setSource(const char16_t* chars, size_t length, bool ownsChars /* = true */)
1964 {
1965     MOZ_ASSERT(dataType == DataMissing);
1966 
1967     dataType = DataUncompressed;
1968     data.uncompressed.chars = chars;
1969     data.uncompressed.ownsChars = ownsChars;
1970 
1971     length_ = length;
1972 }
1973 
1974 void
setCompressedSource(JSRuntime * maybert,void * raw,size_t nbytes,HashNumber hash)1975 ScriptSource::setCompressedSource(JSRuntime* maybert, void* raw, size_t nbytes, HashNumber hash)
1976 {
1977     MOZ_ASSERT(dataType == DataMissing || dataType == DataUncompressed);
1978     if (dataType == DataUncompressed && ownsUncompressedChars())
1979         js_free(const_cast<char16_t*>(uncompressedChars()));
1980 
1981     dataType = DataCompressed;
1982     data.compressed.raw = raw;
1983     data.compressed.nbytes = nbytes;
1984     data.compressed.hash = hash;
1985 
1986     if (maybert)
1987         updateCompressedSourceSet(maybert);
1988 }
1989 
1990 void
updateCompressedSourceSet(JSRuntime * rt)1991 ScriptSource::updateCompressedSourceSet(JSRuntime* rt)
1992 {
1993     MOZ_ASSERT(dataType == DataCompressed);
1994     MOZ_ASSERT(!inCompressedSourceSet);
1995 
1996     CompressedSourceSet::AddPtr p = rt->compressedSourceSet.lookupForAdd(this);
1997     if (p) {
1998         // There is another ScriptSource with the same compressed data.
1999         // Mark that ScriptSource as the parent and use it for all attempts to
2000         // get the source for this ScriptSource.
2001         ScriptSource* parent = *p;
2002         parent->incref();
2003 
2004         js_free(compressedData());
2005         dataType = DataParent;
2006         data.parent = parent;
2007     } else {
2008         if (rt->compressedSourceSet.add(p, this))
2009             inCompressedSourceSet = true;
2010     }
2011 }
2012 
2013 bool
ensureOwnsSource(ExclusiveContext * cx)2014 ScriptSource::ensureOwnsSource(ExclusiveContext* cx)
2015 {
2016     MOZ_ASSERT(dataType == DataUncompressed);
2017     if (ownsUncompressedChars())
2018         return true;
2019 
2020     char16_t* uncompressed = cx->zone()->pod_malloc<char16_t>(Max<size_t>(length_, 1));
2021     if (!uncompressed) {
2022         ReportOutOfMemory(cx);
2023         return false;
2024     }
2025     PodCopy(uncompressed, uncompressedChars(), length_);
2026 
2027     data.uncompressed.chars = uncompressed;
2028     data.uncompressed.ownsChars = true;
2029     return true;
2030 }
2031 
2032 bool
setSourceCopy(ExclusiveContext * cx,SourceBufferHolder & srcBuf,bool argumentsNotIncluded,SourceCompressionTask * task)2033 ScriptSource::setSourceCopy(ExclusiveContext* cx, SourceBufferHolder& srcBuf,
2034                             bool argumentsNotIncluded, SourceCompressionTask* task)
2035 {
2036     MOZ_ASSERT(!hasSourceData());
2037     argumentsNotIncluded_ = argumentsNotIncluded;
2038 
2039     bool owns = srcBuf.ownsChars();
2040     setSource(owns ? srcBuf.take() : srcBuf.get(), srcBuf.length(), owns);
2041 
2042     // There are several cases where source compression is not a good idea:
2043     //  - If the script is tiny, then compression will save little or no space.
2044     //  - If the script is enormous, then decompression can take seconds. With
2045     //    lazy parsing, decompression is not uncommon, so this can significantly
2046     //    increase latency.
2047     //  - If there is only one core, then compression will contend with JS
2048     //    execution (which hurts benchmarketing).
2049     //  - If the source contains a giant string, then parsing will finish much
2050     //    faster than compression which increases latency (this case is handled
2051     //    in Parser::stringLiteral).
2052     //
2053     // Lastly, since the parsing thread will eventually perform a blocking wait
2054     // on the compression task's thread, require that there are at least 2
2055     // helper threads:
2056     //  - If we are on a helper thread, there must be another helper thread to
2057     //    execute our compression task.
2058     //  - If we are on the main thread, there must be at least two helper
2059     //    threads since at most one helper thread can be blocking on the main
2060     //    thread (see HelperThreadState::canStartParseTask) which would cause a
2061     //    deadlock if there wasn't a second helper thread that could make
2062     //    progress on our compression task.
2063     bool canCompressOffThread =
2064         HelperThreadState().cpuCount > 1 &&
2065         HelperThreadState().threadCount >= 2 &&
2066         CanUseExtraThreads();
2067     const size_t TINY_SCRIPT = 256;
2068     const size_t HUGE_SCRIPT = 5 * 1024 * 1024;
2069     if (TINY_SCRIPT <= srcBuf.length() && srcBuf.length() < HUGE_SCRIPT && canCompressOffThread) {
2070         task->ss = this;
2071         if (!StartOffThreadCompression(cx, task))
2072             return false;
2073     } else if (!ensureOwnsSource(cx)) {
2074         return false;
2075     }
2076 
2077     return true;
2078 }
2079 
2080 SourceCompressionTask::ResultType
work()2081 SourceCompressionTask::work()
2082 {
2083     // Try to keep the maximum memory usage down by only allocating half the
2084     // size of the string, first.
2085     size_t inputBytes = ss->length() * sizeof(char16_t);
2086     size_t firstSize = inputBytes / 2;
2087     compressed = js_malloc(firstSize);
2088     if (!compressed)
2089         return OOM;
2090 
2091     Compressor comp(reinterpret_cast<const unsigned char*>(ss->uncompressedChars()), inputBytes);
2092     if (!comp.init())
2093         return OOM;
2094 
2095     comp.setOutput((unsigned char*) compressed, firstSize);
2096     bool cont = true;
2097     while (cont) {
2098         if (abort_)
2099             return Aborted;
2100 
2101         switch (comp.compressMore()) {
2102           case Compressor::CONTINUE:
2103             break;
2104           case Compressor::MOREOUTPUT: {
2105             if (comp.outWritten() == inputBytes) {
2106                 // The compressed string is longer than the original string.
2107                 return Aborted;
2108             }
2109 
2110             // The compressed output is greater than half the size of the
2111             // original string. Reallocate to the full size.
2112             compressed = js_realloc(compressed, inputBytes);
2113             if (!compressed)
2114                 return OOM;
2115 
2116             comp.setOutput((unsigned char*) compressed, inputBytes);
2117             break;
2118           }
2119           case Compressor::DONE:
2120             cont = false;
2121             break;
2122           case Compressor::OOM:
2123             return OOM;
2124         }
2125     }
2126     compressedBytes = comp.outWritten();
2127     compressedHash = CompressedSourceHasher::computeHash(compressed, compressedBytes);
2128 
2129     // Shrink the buffer to the size of the compressed data.
2130     if (void* newCompressed = js_realloc(compressed, compressedBytes))
2131         compressed = newCompressed;
2132 
2133     return Success;
2134 }
2135 
~ScriptSource()2136 ScriptSource::~ScriptSource()
2137 {
2138     MOZ_ASSERT_IF(inCompressedSourceSet, dataType == DataCompressed);
2139 
2140     switch (dataType) {
2141       case DataUncompressed:
2142         if (ownsUncompressedChars())
2143             js_free(const_cast<char16_t*>(uncompressedChars()));
2144         break;
2145 
2146       case DataCompressed:
2147         // Script source references are only manipulated on the main thread,
2148         // except during off thread parsing when the source may be created
2149         // and used exclusively by the thread doing the parse. In this case the
2150         // ScriptSource might be destroyed while off the main thread, but it
2151         // will not have been added to the runtime's compressed source set
2152         // until the parse is finished on the main thread.
2153         if (inCompressedSourceSet)
2154             TlsPerThreadData.get()->runtimeFromMainThread()->compressedSourceSet.remove(this);
2155         js_free(compressedData());
2156         break;
2157 
2158       case DataParent:
2159         parent()->decref();
2160         break;
2161 
2162       default:
2163         break;
2164     }
2165 }
2166 
2167 void
addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,JS::ScriptSourceInfo * info) const2168 ScriptSource::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf,
2169                                      JS::ScriptSourceInfo* info) const
2170 {
2171     if (dataType == DataUncompressed && ownsUncompressedChars())
2172         info->uncompressed += mallocSizeOf(uncompressedChars());
2173     else if (dataType == DataCompressed)
2174         info->compressed += mallocSizeOf(compressedData());
2175     info->misc += mallocSizeOf(this) +
2176                   mallocSizeOf(filename_.get()) +
2177                   mallocSizeOf(introducerFilename_.get());
2178     info->numScripts++;
2179 }
2180 
2181 template<XDRMode mode>
2182 bool
performXDR(XDRState<mode> * xdr)2183 ScriptSource::performXDR(XDRState<mode>* xdr)
2184 {
2185     uint8_t hasSource = hasSourceData();
2186     if (!xdr->codeUint8(&hasSource))
2187         return false;
2188 
2189     uint8_t retrievable = sourceRetrievable_;
2190     if (!xdr->codeUint8(&retrievable))
2191         return false;
2192     sourceRetrievable_ = retrievable;
2193 
2194     if (hasSource && !sourceRetrievable_) {
2195         if (!xdr->codeUint32(&length_))
2196             return false;
2197 
2198         uint32_t compressedLength;
2199         if (mode == XDR_ENCODE) {
2200             switch (dataType) {
2201               case DataUncompressed:
2202                 compressedLength = 0;
2203                 break;
2204               case DataCompressed:
2205                 compressedLength = compressedBytes();
2206                 break;
2207               case DataParent:
2208                 compressedLength = parent()->compressedBytes();
2209                 break;
2210               default:
2211                 MOZ_CRASH();
2212             }
2213         }
2214         if (!xdr->codeUint32(&compressedLength))
2215             return false;
2216 
2217         {
2218             uint8_t argumentsNotIncluded;
2219             if (mode == XDR_ENCODE)
2220                 argumentsNotIncluded = argumentsNotIncluded_;
2221             if (!xdr->codeUint8(&argumentsNotIncluded))
2222                 return false;
2223             if (mode == XDR_DECODE)
2224                 argumentsNotIncluded_ = argumentsNotIncluded;
2225         }
2226 
2227         size_t byteLen = compressedLength ? compressedLength : (length_ * sizeof(char16_t));
2228         if (mode == XDR_DECODE) {
2229             uint8_t* p = xdr->cx()->template pod_malloc<uint8_t>(Max<size_t>(byteLen, 1));
2230             if (!p || !xdr->codeBytes(p, byteLen)) {
2231                 js_free(p);
2232                 return false;
2233             }
2234 
2235             if (compressedLength)
2236                 setCompressedSource(xdr->cx()->runtime(), p, compressedLength,
2237                                     CompressedSourceHasher::computeHash(p, compressedLength));
2238             else
2239                 setSource((const char16_t*) p, length_);
2240         } else {
2241             void* p;
2242             switch (dataType) {
2243               case DataUncompressed:
2244                 p = (void*) uncompressedChars();
2245                 break;
2246               case DataCompressed:
2247                 p = compressedData();
2248                 break;
2249               case DataParent:
2250                 p = parent()->compressedData();
2251                 break;
2252               default:
2253                 MOZ_CRASH();
2254             }
2255             if (!xdr->codeBytes(p, byteLen))
2256                 return false;
2257         }
2258     }
2259 
2260     uint8_t haveSourceMap = hasSourceMapURL();
2261     if (!xdr->codeUint8(&haveSourceMap))
2262         return false;
2263 
2264     if (haveSourceMap) {
2265         uint32_t sourceMapURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(sourceMapURL_.get());
2266         if (!xdr->codeUint32(&sourceMapURLLen))
2267             return false;
2268 
2269         if (mode == XDR_DECODE) {
2270             sourceMapURL_ = xdr->cx()->template make_pod_array<char16_t>(sourceMapURLLen + 1);
2271             if (!sourceMapURL_)
2272                 return false;
2273         }
2274         if (!xdr->codeChars(sourceMapURL_.get(), sourceMapURLLen)) {
2275             if (mode == XDR_DECODE)
2276                 sourceMapURL_ = nullptr;
2277             return false;
2278         }
2279         sourceMapURL_[sourceMapURLLen] = '\0';
2280     }
2281 
2282     uint8_t haveDisplayURL = hasDisplayURL();
2283     if (!xdr->codeUint8(&haveDisplayURL))
2284         return false;
2285 
2286     if (haveDisplayURL) {
2287         uint32_t displayURLLen = (mode == XDR_DECODE) ? 0 : js_strlen(displayURL_.get());
2288         if (!xdr->codeUint32(&displayURLLen))
2289             return false;
2290 
2291         if (mode == XDR_DECODE) {
2292             displayURL_ = xdr->cx()->template make_pod_array<char16_t>(displayURLLen + 1);
2293             if (!displayURL_)
2294                 return false;
2295         }
2296         if (!xdr->codeChars(displayURL_.get(), displayURLLen)) {
2297             if (mode == XDR_DECODE)
2298                 displayURL_ = nullptr;
2299             return false;
2300         }
2301         displayURL_[displayURLLen] = '\0';
2302     }
2303 
2304     uint8_t haveFilename = !!filename_;
2305     if (!xdr->codeUint8(&haveFilename))
2306         return false;
2307 
2308     if (haveFilename) {
2309         const char* fn = filename();
2310         if (!xdr->codeCString(&fn))
2311             return false;
2312         if (mode == XDR_DECODE && !setFilename(xdr->cx(), fn))
2313             return false;
2314     }
2315 
2316     return true;
2317 }
2318 
2319 // Format and return a cx->zone()->pod_malloc'ed URL for a generated script like:
2320 //   {filename} line {lineno} > {introducer}
2321 // For example:
2322 //   foo.js line 7 > eval
2323 // indicating code compiled by the call to 'eval' on line 7 of foo.js.
2324 static char*
FormatIntroducedFilename(ExclusiveContext * cx,const char * filename,unsigned lineno,const char * introducer)2325 FormatIntroducedFilename(ExclusiveContext* cx, const char* filename, unsigned lineno,
2326                          const char* introducer)
2327 {
2328     // Compute the length of the string in advance, so we can allocate a
2329     // buffer of the right size on the first shot.
2330     //
2331     // (JS_smprintf would be perfect, as that allocates the result
2332     // dynamically as it formats the string, but it won't allocate from cx,
2333     // and wants us to use a special free function.)
2334     char linenoBuf[15];
2335     size_t filenameLen = strlen(filename);
2336     size_t linenoLen = JS_snprintf(linenoBuf, 15, "%u", lineno);
2337     size_t introducerLen = strlen(introducer);
2338     size_t len = filenameLen                    +
2339                  6 /* == strlen(" line ") */    +
2340                  linenoLen                      +
2341                  3 /* == strlen(" > ") */       +
2342                  introducerLen                  +
2343                  1 /* \0 */;
2344     char* formatted = cx->zone()->pod_malloc<char>(len);
2345     if (!formatted) {
2346         ReportOutOfMemory(cx);
2347         return nullptr;
2348     }
2349     mozilla::DebugOnly<size_t> checkLen = JS_snprintf(formatted, len, "%s line %s > %s",
2350                                                       filename, linenoBuf, introducer);
2351     MOZ_ASSERT(checkLen == len - 1);
2352 
2353     return formatted;
2354 }
2355 
2356 bool
initFromOptions(ExclusiveContext * cx,const ReadOnlyCompileOptions & options)2357 ScriptSource::initFromOptions(ExclusiveContext* cx, const ReadOnlyCompileOptions& options)
2358 {
2359     MOZ_ASSERT(!filename_);
2360     MOZ_ASSERT(!introducerFilename_);
2361 
2362     mutedErrors_ = options.mutedErrors();
2363 
2364     introductionType_ = options.introductionType;
2365     setIntroductionOffset(options.introductionOffset);
2366 
2367     if (options.hasIntroductionInfo) {
2368         MOZ_ASSERT(options.introductionType != nullptr);
2369         const char* filename = options.filename() ? options.filename() : "<unknown>";
2370         char* formatted = FormatIntroducedFilename(cx, filename, options.introductionLineno,
2371                                                    options.introductionType);
2372         if (!formatted)
2373             return false;
2374         filename_.reset(formatted);
2375     } else if (options.filename()) {
2376         if (!setFilename(cx, options.filename()))
2377             return false;
2378     }
2379 
2380     if (options.introducerFilename()) {
2381         introducerFilename_ = DuplicateString(cx, options.introducerFilename());
2382         if (!introducerFilename_)
2383             return false;
2384     }
2385 
2386     return true;
2387 }
2388 
2389 bool
setFilename(ExclusiveContext * cx,const char * filename)2390 ScriptSource::setFilename(ExclusiveContext* cx, const char* filename)
2391 {
2392     MOZ_ASSERT(!filename_);
2393     filename_ = DuplicateString(cx, filename);
2394     return filename_ != nullptr;
2395 }
2396 
2397 bool
setDisplayURL(ExclusiveContext * cx,const char16_t * displayURL)2398 ScriptSource::setDisplayURL(ExclusiveContext* cx, const char16_t* displayURL)
2399 {
2400     MOZ_ASSERT(displayURL);
2401     if (hasDisplayURL()) {
2402         if (cx->isJSContext() &&
2403             !JS_ReportErrorFlagsAndNumber(cx->asJSContext(), JSREPORT_WARNING,
2404                                           GetErrorMessage, nullptr,
2405                                           JSMSG_ALREADY_HAS_PRAGMA, filename_.get(),
2406                                           "//# sourceURL"))
2407         {
2408             return false;
2409         }
2410     }
2411     size_t len = js_strlen(displayURL) + 1;
2412     if (len == 1)
2413         return true;
2414 
2415     displayURL_ = DuplicateString(cx, displayURL);
2416     return displayURL_ != nullptr;
2417 }
2418 
2419 bool
setSourceMapURL(ExclusiveContext * cx,const char16_t * sourceMapURL)2420 ScriptSource::setSourceMapURL(ExclusiveContext* cx, const char16_t* sourceMapURL)
2421 {
2422     MOZ_ASSERT(sourceMapURL);
2423 
2424     size_t len = js_strlen(sourceMapURL) + 1;
2425     if (len == 1)
2426         return true;
2427 
2428     sourceMapURL_ = DuplicateString(cx, sourceMapURL);
2429     return sourceMapURL_ != nullptr;
2430 }
2431 
2432 size_t
computedSizeOfData() const2433 ScriptSource::computedSizeOfData() const
2434 {
2435     if (dataType == DataUncompressed && ownsUncompressedChars())
2436         return sizeof(char16_t) * length_;
2437     if (dataType == DataCompressed)
2438         return compressedBytes();
2439     return 0;
2440 }
2441 
2442 /*
2443  * Shared script data management.
2444  */
2445 
2446 SharedScriptData*
new_(ExclusiveContext * cx,uint32_t codeLength,uint32_t srcnotesLength,uint32_t natoms)2447 js::SharedScriptData::new_(ExclusiveContext* cx, uint32_t codeLength,
2448                            uint32_t srcnotesLength, uint32_t natoms)
2449 {
2450     /*
2451      * Ensure the atoms are aligned, as some architectures don't allow unaligned
2452      * access.
2453      */
2454     const uint32_t pointerSize = sizeof(JSAtom*);
2455     const uint32_t pointerMask = pointerSize - 1;
2456     const uint32_t dataOffset = offsetof(SharedScriptData, data);
2457     uint32_t baseLength = codeLength + srcnotesLength;
2458     uint32_t padding = (pointerSize - ((baseLength + dataOffset) & pointerMask)) & pointerMask;
2459     uint32_t length = baseLength + padding + pointerSize * natoms;
2460 
2461     SharedScriptData* entry = reinterpret_cast<SharedScriptData*>(
2462             cx->zone()->pod_malloc<uint8_t>(length + dataOffset));
2463     if (!entry) {
2464         ReportOutOfMemory(cx);
2465         return nullptr;
2466     }
2467 
2468     entry->length = length;
2469     entry->natoms = natoms;
2470     entry->marked = false;
2471     memset(entry->data + baseLength, 0, padding);
2472 
2473     /*
2474      * Call constructors to initialize the storage that will be accessed as a
2475      * HeapPtrAtom array via atoms().
2476      */
2477     HeapPtrAtom* atoms = entry->atoms();
2478     MOZ_ASSERT(reinterpret_cast<uintptr_t>(atoms) % sizeof(JSAtom*) == 0);
2479     for (unsigned i = 0; i < natoms; ++i)
2480         new (&atoms[i]) HeapPtrAtom();
2481 
2482     return entry;
2483 }
2484 
2485 /*
2486  * Takes ownership of its *ssd parameter and either adds it into the runtime's
2487  * ScriptDataTable or frees it if a matching entry already exists.
2488  *
2489  * Sets the |code| and |atoms| fields on the given JSScript.
2490  */
2491 static bool
SaveSharedScriptData(ExclusiveContext * cx,Handle<JSScript * > script,SharedScriptData * ssd,uint32_t nsrcnotes)2492 SaveSharedScriptData(ExclusiveContext* cx, Handle<JSScript*> script, SharedScriptData* ssd,
2493                      uint32_t nsrcnotes)
2494 {
2495     MOZ_ASSERT(script != nullptr);
2496     MOZ_ASSERT(ssd != nullptr);
2497 
2498     AutoLockForExclusiveAccess lock(cx);
2499 
2500     ScriptBytecodeHasher::Lookup l(ssd);
2501 
2502     ScriptDataTable::AddPtr p = cx->scriptDataTable().lookupForAdd(l);
2503     if (p) {
2504         js_free(ssd);
2505         ssd = *p;
2506     } else {
2507         if (!cx->scriptDataTable().add(p, ssd)) {
2508             script->setCode(nullptr);
2509             script->atoms = nullptr;
2510             js_free(ssd);
2511             ReportOutOfMemory(cx);
2512             return false;
2513         }
2514     }
2515 
2516     /*
2517      * During the IGC we need to ensure that bytecode is marked whenever it is
2518      * accessed even if the bytecode was already in the table: at this point
2519      * old scripts or exceptions pointing to the bytecode may no longer be
2520      * reachable. This is effectively a read barrier.
2521      */
2522     if (cx->isJSContext()) {
2523         JSRuntime* rt = cx->asJSContext()->runtime();
2524         if (JS::IsIncrementalGCInProgress(rt) && rt->gc.isFullGc())
2525             ssd->marked = true;
2526     }
2527 
2528     script->setCode(ssd->data);
2529     script->atoms = ssd->atoms();
2530     return true;
2531 }
2532 
2533 static inline void
MarkScriptData(JSRuntime * rt,const jsbytecode * bytecode)2534 MarkScriptData(JSRuntime* rt, const jsbytecode* bytecode)
2535 {
2536     /*
2537      * As an invariant, a ScriptBytecodeEntry should not be 'marked' outside of
2538      * a GC. Since SweepScriptBytecodes is only called during a full gc,
2539      * to preserve this invariant, only mark during a full gc.
2540      */
2541     if (rt->gc.isFullGc())
2542         SharedScriptData::fromBytecode(bytecode)->marked = true;
2543 }
2544 
2545 void
UnmarkScriptData(JSRuntime * rt)2546 js::UnmarkScriptData(JSRuntime* rt)
2547 {
2548     MOZ_ASSERT(rt->gc.isFullGc());
2549     ScriptDataTable& table = rt->scriptDataTable();
2550     for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
2551         SharedScriptData* entry = e.front();
2552         entry->marked = false;
2553     }
2554 }
2555 
2556 void
SweepScriptData(JSRuntime * rt)2557 js::SweepScriptData(JSRuntime* rt)
2558 {
2559     MOZ_ASSERT(rt->gc.isFullGc());
2560     ScriptDataTable& table = rt->scriptDataTable();
2561 
2562     if (rt->keepAtoms())
2563         return;
2564 
2565     for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront()) {
2566         SharedScriptData* entry = e.front();
2567         if (!entry->marked) {
2568             js_free(entry);
2569             e.removeFront();
2570         }
2571     }
2572 }
2573 
2574 void
FreeScriptData(JSRuntime * rt)2575 js::FreeScriptData(JSRuntime* rt)
2576 {
2577     ScriptDataTable& table = rt->scriptDataTable();
2578     if (!table.initialized())
2579         return;
2580 
2581     for (ScriptDataTable::Enum e(table); !e.empty(); e.popFront())
2582         js_free(e.front());
2583 
2584     table.clear();
2585 }
2586 
2587 /*
2588  * JSScript::data and SharedScriptData::data have complex,
2589  * manually-controlled, memory layouts.
2590  *
2591  * JSScript::data begins with some optional array headers. They are optional
2592  * because they often aren't needed, i.e. the corresponding arrays often have
2593  * zero elements. Each header has a bit in JSScript::hasArrayBits that
2594  * indicates if it's present within |data|; from this the offset of each
2595  * present array header can be computed. Each header has an accessor function
2596  * in JSScript that encapsulates this offset computation.
2597  *
2598  * Array type       Array elements  Accessor
2599  * ----------       --------------  --------
2600  * ConstArray       Consts          consts()
2601  * ObjectArray      Objects         objects()
2602  * ObjectArray      Regexps         regexps()
2603  * TryNoteArray     Try notes       trynotes()
2604  * BlockScopeArray  Scope notes     blockScopes()
2605  *
2606  * Then are the elements of several arrays.
2607  * - Most of these arrays have headers listed above (if present). For each of
2608  *   these, the array pointer and the array length is stored in the header.
2609  * - The remaining arrays have pointers and lengths that are stored directly in
2610  *   JSScript. This is because, unlike the others, they are nearly always
2611  *   non-zero length and so the optional-header space optimization isn't
2612  *   worthwhile.
2613  *
2614  * Array elements   Pointed to by         Length
2615  * --------------   -------------         ------
2616  * Consts           consts()->vector      consts()->length
2617  * Objects          objects()->vector     objects()->length
2618  * Regexps          regexps()->vector     regexps()->length
2619  * Try notes        trynotes()->vector    trynotes()->length
2620  * Scope notes      blockScopes()->vector blockScopes()->length
2621  *
2622  * IMPORTANT: This layout has two key properties.
2623  * - It ensures that everything has sufficient alignment; in particular, the
2624  *   consts() elements need Value alignment.
2625  * - It ensures there are no gaps between elements, which saves space and makes
2626  *   manual layout easy. In particular, in the second part, arrays with larger
2627  *   elements precede arrays with smaller elements.
2628  *
2629  * SharedScriptData::data contains data that can be shared within a
2630  * runtime. These items' layout is manually controlled to make it easier to
2631  * manage both during (temporary) allocation and during matching against
2632  * existing entries in the runtime. As the jsbytecode has to come first to
2633  * enable lookup by bytecode identity, SharedScriptData::data, the atoms part
2634  * has to manually be aligned sufficiently by adding padding after the notes
2635  * part.
2636  *
2637  * Array elements   Pointed to by         Length
2638  * --------------   -------------         ------
2639  * jsbytecode       code                  length
2640  * jsscrnote        notes()               numNotes()
2641  * Atoms            atoms                 natoms
2642  *
2643  * The following static assertions check JSScript::data's alignment properties.
2644  */
2645 
2646 #define KEEPS_JSVAL_ALIGNMENT(T) \
2647     (JS_ALIGNMENT_OF(JS::Value) % JS_ALIGNMENT_OF(T) == 0 && \
2648      sizeof(T) % sizeof(JS::Value) == 0)
2649 
2650 #define HAS_JSVAL_ALIGNMENT(T) \
2651     (JS_ALIGNMENT_OF(JS::Value) == JS_ALIGNMENT_OF(T) && \
2652      sizeof(T) == sizeof(JS::Value))
2653 
2654 #define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \
2655     (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0)
2656 
2657 /*
2658  * These assertions ensure that there is no padding between the array headers,
2659  * and also that the consts() elements (which follow immediately afterward) are
2660  * Value-aligned.  (There is an assumption that |data| itself is Value-aligned;
2661  * we check this below).
2662  */
2663 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray));
2664 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray));       /* there are two of these */
2665 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray));
2666 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray));
2667 
2668 /* These assertions ensure there is no padding required between array elements. */
2669 JS_STATIC_ASSERT(HAS_JSVAL_ALIGNMENT(HeapValue));
2670 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, HeapPtrObject));
2671 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, HeapPtrObject));
2672 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, JSTryNote));
2673 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, uint32_t));
2674 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(uint32_t, uint32_t));
2675 
2676 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapValue, BlockScopeNote));
2677 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, BlockScopeNote));
2678 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(JSTryNote, BlockScopeNote));
2679 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(HeapPtrObject, BlockScopeNote));
2680 JS_STATIC_ASSERT(NO_PADDING_BETWEEN_ENTRIES(BlockScopeNote, uint32_t));
2681 
2682 static inline size_t
ScriptDataSize(uint32_t nbindings,uint32_t nconsts,uint32_t nobjects,uint32_t nregexps,uint32_t ntrynotes,uint32_t nblockscopes,uint32_t nyieldoffsets)2683 ScriptDataSize(uint32_t nbindings, uint32_t nconsts, uint32_t nobjects, uint32_t nregexps,
2684                uint32_t ntrynotes, uint32_t nblockscopes, uint32_t nyieldoffsets)
2685 {
2686     size_t size = 0;
2687 
2688     if (nconsts != 0)
2689         size += sizeof(ConstArray) + nconsts * sizeof(Value);
2690     if (nobjects != 0)
2691         size += sizeof(ObjectArray) + nobjects * sizeof(NativeObject*);
2692     if (nregexps != 0)
2693         size += sizeof(ObjectArray) + nregexps * sizeof(NativeObject*);
2694     if (ntrynotes != 0)
2695         size += sizeof(TryNoteArray) + ntrynotes * sizeof(JSTryNote);
2696     if (nblockscopes != 0)
2697         size += sizeof(BlockScopeArray) + nblockscopes * sizeof(BlockScopeNote);
2698     if (nyieldoffsets != 0)
2699         size += sizeof(YieldOffsetArray) + nyieldoffsets * sizeof(uint32_t);
2700 
2701     if (nbindings != 0) {
2702 	// Make sure bindings are sufficiently aligned.
2703         size = JS_ROUNDUP(size, JS_ALIGNMENT_OF(Binding)) + nbindings * sizeof(Binding);
2704     }
2705 
2706     return size;
2707 }
2708 
2709 void
initCompartment(ExclusiveContext * cx)2710 JSScript::initCompartment(ExclusiveContext* cx)
2711 {
2712     compartment_ = cx->compartment_;
2713 }
2714 
2715 /* static */ JSScript*
Create(ExclusiveContext * cx,HandleObject enclosingScope,bool savedCallerFun,const ReadOnlyCompileOptions & options,HandleObject sourceObject,uint32_t bufStart,uint32_t bufEnd)2716 JSScript::Create(ExclusiveContext* cx, HandleObject enclosingScope, bool savedCallerFun,
2717                  const ReadOnlyCompileOptions& options, HandleObject sourceObject,
2718                  uint32_t bufStart, uint32_t bufEnd)
2719 {
2720     MOZ_ASSERT(bufStart <= bufEnd);
2721 
2722     RootedScript script(cx, Allocate<JSScript>(cx));
2723     if (!script)
2724         return nullptr;
2725 
2726     PodZero(script.get());
2727     new (&script->bindings) Bindings;
2728 
2729     script->enclosingStaticScope_ = enclosingScope;
2730     script->savedCallerFun_ = savedCallerFun;
2731     script->initCompartment(cx);
2732 
2733     script->selfHosted_ = options.selfHostingMode;
2734     script->noScriptRval_ = options.noScriptRval;
2735     script->treatAsRunOnce_ = options.isRunOnce;
2736 
2737     // Compute whether this script is under a non-syntactic scope. We don't
2738     // need to walk the entire static scope chain if the script is nested in a
2739     // function. In that case, we can propagate the cached value from the
2740     // outer script.
2741     script->hasNonSyntacticScope_ = HasNonSyntacticStaticScopeChain(enclosingScope);
2742 
2743     script->version = options.version;
2744     MOZ_ASSERT(script->getVersion() == options.version);     // assert that no overflow occurred
2745 
2746     script->setSourceObject(sourceObject);
2747     script->sourceStart_ = bufStart;
2748     script->sourceEnd_ = bufEnd;
2749 
2750     return script;
2751 }
2752 
2753 static inline uint8_t*
AllocScriptData(JS::Zone * zone,size_t size)2754 AllocScriptData(JS::Zone* zone, size_t size)
2755 {
2756     if (!size)
2757         return nullptr;
2758 
2759     uint8_t* data = zone->pod_calloc<uint8_t>(JS_ROUNDUP(size, sizeof(Value)));
2760     if (!data)
2761         return nullptr;
2762     MOZ_ASSERT(size_t(data) % sizeof(Value) == 0);
2763     return data;
2764 }
2765 
2766 /* static */ bool
partiallyInit(ExclusiveContext * cx,HandleScript script,uint32_t nconsts,uint32_t nobjects,uint32_t nregexps,uint32_t ntrynotes,uint32_t nblockscopes,uint32_t nyieldoffsets,uint32_t nTypeSets)2767 JSScript::partiallyInit(ExclusiveContext* cx, HandleScript script, uint32_t nconsts,
2768                         uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes,
2769                         uint32_t nblockscopes, uint32_t nyieldoffsets, uint32_t nTypeSets)
2770 {
2771     size_t size = ScriptDataSize(script->bindings.count(), nconsts, nobjects, nregexps, ntrynotes,
2772                                  nblockscopes, nyieldoffsets);
2773     script->data = AllocScriptData(script->zone(), size);
2774     if (size && !script->data) {
2775         ReportOutOfMemory(cx);
2776         return false;
2777     }
2778     script->dataSize_ = size;
2779 
2780     MOZ_ASSERT(nTypeSets <= UINT16_MAX);
2781     script->nTypeSets_ = uint16_t(nTypeSets);
2782 
2783     uint8_t* cursor = script->data;
2784     if (nconsts != 0) {
2785         script->setHasArray(CONSTS);
2786         cursor += sizeof(ConstArray);
2787     }
2788     if (nobjects != 0) {
2789         script->setHasArray(OBJECTS);
2790         cursor += sizeof(ObjectArray);
2791     }
2792     if (nregexps != 0) {
2793         script->setHasArray(REGEXPS);
2794         cursor += sizeof(ObjectArray);
2795     }
2796     if (ntrynotes != 0) {
2797         script->setHasArray(TRYNOTES);
2798         cursor += sizeof(TryNoteArray);
2799     }
2800     if (nblockscopes != 0) {
2801         script->setHasArray(BLOCK_SCOPES);
2802         cursor += sizeof(BlockScopeArray);
2803     }
2804 
2805     YieldOffsetArray* yieldOffsets = nullptr;
2806     if (nyieldoffsets != 0) {
2807         yieldOffsets = reinterpret_cast<YieldOffsetArray*>(cursor);
2808         cursor += sizeof(YieldOffsetArray);
2809     }
2810 
2811     if (nconsts != 0) {
2812         MOZ_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(JS::Value) == 0);
2813         script->consts()->length = nconsts;
2814         script->consts()->vector = (HeapValue*)cursor;
2815         cursor += nconsts * sizeof(script->consts()->vector[0]);
2816     }
2817 
2818     if (nobjects != 0) {
2819         script->objects()->length = nobjects;
2820         script->objects()->vector = (HeapPtrObject*)cursor;
2821         cursor += nobjects * sizeof(script->objects()->vector[0]);
2822     }
2823 
2824     if (nregexps != 0) {
2825         script->regexps()->length = nregexps;
2826         script->regexps()->vector = (HeapPtrObject*)cursor;
2827         cursor += nregexps * sizeof(script->regexps()->vector[0]);
2828     }
2829 
2830     if (ntrynotes != 0) {
2831         script->trynotes()->length = ntrynotes;
2832         script->trynotes()->vector = reinterpret_cast<JSTryNote*>(cursor);
2833         size_t vectorSize = ntrynotes * sizeof(script->trynotes()->vector[0]);
2834 #ifdef DEBUG
2835         memset(cursor, 0, vectorSize);
2836 #endif
2837         cursor += vectorSize;
2838     }
2839 
2840     if (nblockscopes != 0) {
2841         script->blockScopes()->length = nblockscopes;
2842         script->blockScopes()->vector = reinterpret_cast<BlockScopeNote*>(cursor);
2843         size_t vectorSize = nblockscopes * sizeof(script->blockScopes()->vector[0]);
2844 #ifdef DEBUG
2845         memset(cursor, 0, vectorSize);
2846 #endif
2847         cursor += vectorSize;
2848     }
2849 
2850     if (nyieldoffsets != 0) {
2851         yieldOffsets->init(reinterpret_cast<uint32_t*>(cursor), nyieldoffsets);
2852         size_t vectorSize = nyieldoffsets * sizeof(script->yieldOffsets()[0]);
2853 #ifdef DEBUG
2854         memset(cursor, 0, vectorSize);
2855 #endif
2856         cursor += vectorSize;
2857     }
2858 
2859     if (script->bindings.count() != 0) {
2860         // Make sure bindings are sufficiently aligned.
2861         cursor = reinterpret_cast<uint8_t*>
2862             (JS_ROUNDUP(reinterpret_cast<uintptr_t>(cursor), JS_ALIGNMENT_OF(Binding)));
2863     }
2864     cursor = script->bindings.switchToScriptStorage(reinterpret_cast<Binding*>(cursor));
2865 
2866     MOZ_ASSERT(cursor == script->data + size);
2867     return true;
2868 }
2869 
2870 /* static */ bool
fullyInitTrivial(ExclusiveContext * cx,Handle<JSScript * > script)2871 JSScript::fullyInitTrivial(ExclusiveContext* cx, Handle<JSScript*> script)
2872 {
2873     if (!script->bindings.initTrivial(cx))
2874         return false;
2875 
2876     if (!partiallyInit(cx, script, 0, 0, 0, 0, 0, 0, 0))
2877         return false;
2878 
2879     SharedScriptData* ssd = SharedScriptData::new_(cx, 1, 1, 0);
2880     if (!ssd)
2881         return false;
2882 
2883     ssd->data[0] = JSOP_RETRVAL;
2884     ssd->data[1] = SRC_NULL;
2885     script->setLength(1);
2886     return SaveSharedScriptData(cx, script, ssd, 1);
2887 }
2888 
2889 /* static */ void
linkToFunctionFromEmitter(js::ExclusiveContext * cx,JS::Handle<JSScript * > script,js::frontend::FunctionBox * funbox)2890 JSScript::linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
2891                                     js::frontend::FunctionBox* funbox)
2892 {
2893     script->funHasExtensibleScope_ = funbox->hasExtensibleScope();
2894     script->funNeedsDeclEnvObject_ = funbox->needsDeclEnvObject();
2895     script->needsHomeObject_       = funbox->needsHomeObject();
2896     script->isDerivedClassConstructor_ = funbox->isDerivedClassConstructor();
2897 
2898     if (funbox->argumentsHasLocalBinding()) {
2899         script->setArgumentsHasVarBinding();
2900         if (funbox->definitelyNeedsArgsObj())
2901             script->setNeedsArgsObj(true);
2902     } else {
2903         MOZ_ASSERT(!funbox->definitelyNeedsArgsObj());
2904     }
2905     script->hasMappedArgsObj_ = funbox->hasMappedArgsObj();
2906 
2907     script->functionHasThisBinding_ = funbox->hasThisBinding();
2908 
2909     script->funLength_ = funbox->length;
2910 
2911     script->isGeneratorExp_ = funbox->inGenexpLambda;
2912     script->setGeneratorKind(funbox->generatorKind());
2913 
2914     // Link the function and the script to each other, so that StaticScopeIter
2915     // may walk the scope chain of currently compiling scripts.
2916     RootedFunction fun(cx, funbox->function());
2917     MOZ_ASSERT(fun->isInterpreted());
2918 
2919     script->setFunction(fun);
2920 
2921     if (fun->isInterpretedLazy())
2922         fun->setUnlazifiedScript(script);
2923     else
2924         fun->setScript(script);
2925 }
2926 
2927 /* static */ void
linkToModuleFromEmitter(js::ExclusiveContext * cx,JS::Handle<JSScript * > script,js::frontend::ModuleBox * modulebox)2928 JSScript::linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
2929                                     js::frontend::ModuleBox* modulebox)
2930 {
2931     script->funHasExtensibleScope_ = false;
2932     script->funNeedsDeclEnvObject_ = false;
2933     script->needsHomeObject_ = false;
2934     script->isDerivedClassConstructor_ = false;
2935     script->funLength_ = 0;
2936 
2937     script->isGeneratorExp_ = false;
2938     script->setGeneratorKind(NotGenerator);
2939 
2940     // Link the module and the script to each other, so that StaticScopeIter
2941     // may walk the scope chain of currently compiling scripts.
2942     RootedModuleObject module(cx, modulebox->module());
2943     script->setModule(module);
2944 }
2945 
2946 /* static */ bool
fullyInitFromEmitter(ExclusiveContext * cx,HandleScript script,BytecodeEmitter * bce)2947 JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, BytecodeEmitter* bce)
2948 {
2949     /* The counts of indexed things must be checked during code generation. */
2950     MOZ_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
2951     MOZ_ASSERT(bce->objectList.length <= INDEX_LIMIT);
2952     MOZ_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
2953 
2954     uint32_t mainLength = bce->offset();
2955     uint32_t prologueLength = bce->prologueOffset();
2956     uint32_t nsrcnotes;
2957     if (!bce->finishTakingSrcNotes(&nsrcnotes))
2958         return false;
2959     uint32_t natoms = bce->atomIndices->count();
2960     if (!partiallyInit(cx, script,
2961                        bce->constList.length(), bce->objectList.length, bce->regexpList.length,
2962                        bce->tryNoteList.length(), bce->blockScopeList.length(),
2963                        bce->yieldOffsetList.length(), bce->typesetCount))
2964     {
2965         return false;
2966     }
2967 
2968     MOZ_ASSERT(script->mainOffset() == 0);
2969     script->mainOffset_ = prologueLength;
2970 
2971     script->lineno_ = bce->firstLine;
2972 
2973     script->setLength(prologueLength + mainLength);
2974     script->natoms_ = natoms;
2975     SharedScriptData* ssd = SharedScriptData::new_(cx, script->length(), nsrcnotes, natoms);
2976     if (!ssd)
2977         return false;
2978 
2979     jsbytecode* code = ssd->data;
2980     PodCopy<jsbytecode>(code, bce->prologue.code.begin(), prologueLength);
2981     PodCopy<jsbytecode>(code + prologueLength, bce->code().begin(), mainLength);
2982     bce->copySrcNotes((jssrcnote*)(code + script->length()), nsrcnotes);
2983     InitAtomMap(bce->atomIndices.getMap(), ssd->atoms());
2984 
2985     if (!SaveSharedScriptData(cx, script, ssd, nsrcnotes))
2986         return false;
2987 
2988 #ifdef DEBUG
2989     FunctionBox* funbox = bce->sc->isFunctionBox() ? bce->sc->asFunctionBox() : nullptr;
2990 
2991     // Assert that the properties set by linkToFunctionFromEmitter are
2992     // correct.
2993     if (funbox) {
2994         MOZ_ASSERT(script->funHasExtensibleScope_ == funbox->hasExtensibleScope());
2995         MOZ_ASSERT(script->funNeedsDeclEnvObject_ == funbox->needsDeclEnvObject());
2996         MOZ_ASSERT(script->needsHomeObject_ == funbox->needsHomeObject());
2997         MOZ_ASSERT(script->isDerivedClassConstructor_ == funbox->isDerivedClassConstructor());
2998         MOZ_ASSERT(script->argumentsHasVarBinding() == funbox->argumentsHasLocalBinding());
2999         MOZ_ASSERT(script->hasMappedArgsObj() == funbox->hasMappedArgsObj());
3000         MOZ_ASSERT(script->functionHasThisBinding() == funbox->hasThisBinding());
3001         MOZ_ASSERT(script->functionNonDelazifying() == funbox->function());
3002         MOZ_ASSERT(script->isGeneratorExp_ == funbox->inGenexpLambda);
3003         MOZ_ASSERT(script->generatorKind() == funbox->generatorKind());
3004     } else {
3005         MOZ_ASSERT(!script->funHasExtensibleScope_);
3006         MOZ_ASSERT(!script->funNeedsDeclEnvObject_);
3007         MOZ_ASSERT(!script->needsHomeObject_);
3008         MOZ_ASSERT(!script->isDerivedClassConstructor_);
3009         MOZ_ASSERT(!script->argumentsHasVarBinding());
3010         MOZ_ASSERT(!script->hasMappedArgsObj());
3011         MOZ_ASSERT(!script->isGeneratorExp_);
3012         MOZ_ASSERT(script->generatorKind() == NotGenerator);
3013     }
3014 #endif
3015 
3016     if (bce->constList.length() != 0)
3017         bce->constList.finish(script->consts());
3018     if (bce->objectList.length != 0)
3019         bce->objectList.finish(script->objects());
3020     if (bce->regexpList.length != 0)
3021         bce->regexpList.finish(script->regexps());
3022     if (bce->tryNoteList.length() != 0)
3023         bce->tryNoteList.finish(script->trynotes());
3024     if (bce->blockScopeList.length() != 0)
3025         bce->blockScopeList.finish(script->blockScopes(), prologueLength);
3026     script->strict_ = bce->sc->strict();
3027     script->explicitUseStrict_ = bce->sc->hasExplicitUseStrict();
3028     script->bindingsAccessedDynamically_ = bce->sc->bindingsAccessedDynamically();
3029     script->hasSingletons_ = bce->hasSingletons;
3030 
3031     if (bce->yieldOffsetList.length() != 0)
3032         bce->yieldOffsetList.finish(script->yieldOffsets(), prologueLength);
3033 
3034     // The call to nfixed() depends on the above setFunction() call.
3035     if (UINT32_MAX - script->nfixed() < bce->maxStackDepth) {
3036         bce->reportError(nullptr, JSMSG_NEED_DIET, "script");
3037         return false;
3038     }
3039     script->nslots_ = script->nfixed() + bce->maxStackDepth;
3040 
3041     for (unsigned i = 0, n = script->bindings.numArgs(); i < n; ++i) {
3042         if (script->formalIsAliased(i)) {
3043             script->funHasAnyAliasedFormal_ = true;
3044             break;
3045         }
3046     }
3047 
3048     return true;
3049 }
3050 
3051 size_t
computedSizeOfData() const3052 JSScript::computedSizeOfData() const
3053 {
3054     return dataSize();
3055 }
3056 
3057 size_t
sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const3058 JSScript::sizeOfData(mozilla::MallocSizeOf mallocSizeOf) const
3059 {
3060     return mallocSizeOf(data);
3061 }
3062 
3063 size_t
sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const3064 JSScript::sizeOfTypeScript(mozilla::MallocSizeOf mallocSizeOf) const
3065 {
3066     return types_->sizeOfIncludingThis(mallocSizeOf);
3067 }
3068 
3069 /*
3070  * Nb: srcnotes are variable-length.  This function computes the number of
3071  * srcnote *slots*, which may be greater than the number of srcnotes.
3072  */
3073 uint32_t
numNotes()3074 JSScript::numNotes()
3075 {
3076     jssrcnote* sn;
3077     jssrcnote* notes_ = notes();
3078     for (sn = notes_; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn))
3079         continue;
3080     return sn - notes_ + 1;    /* +1 for the terminator */
3081 }
3082 
3083 js::GlobalObject&
uninlinedGlobal() const3084 JSScript::uninlinedGlobal() const
3085 {
3086     return global();
3087 }
3088 
3089 void
fixEnclosingStaticGlobalLexicalScope()3090 JSScript::fixEnclosingStaticGlobalLexicalScope()
3091 {
3092     MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingStaticScope_));
3093     enclosingStaticScope_ = &global().lexicalScope().staticBlock();
3094 }
3095 
3096 void
fixEnclosingStaticGlobalLexicalScope()3097 LazyScript::fixEnclosingStaticGlobalLexicalScope()
3098 {
3099     MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingScope_));
3100     enclosingScope_ = &function_->global().lexicalScope().staticBlock();
3101 }
3102 
3103 void
finalize(FreeOp * fop)3104 JSScript::finalize(FreeOp* fop)
3105 {
3106     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
3107     // may have created it and partially initialized it with
3108     // JSScript::Create(), but not yet finished initializing it with
3109     // fullyInitFromEmitter() or fullyInitTrivial().
3110 
3111     // Collect code coverage information for this script and all its inner
3112     // scripts, and store the aggregated information on the compartment.
3113     if (fop->runtime()->lcovOutput.isEnabled())
3114         compartment()->lcovOutput.collectCodeCoverageInfo(compartment(), sourceObject(), this);
3115 
3116     fop->runtime()->spsProfiler.onScriptFinalized(this);
3117 
3118     if (types_)
3119         types_->destroy();
3120 
3121     jit::DestroyJitScripts(fop, this);
3122 
3123     destroyScriptCounts(fop);
3124     destroyDebugScript(fop);
3125 
3126     if (data) {
3127         JS_POISON(data, 0xdb, computedSizeOfData());
3128         fop->free_(data);
3129     }
3130 
3131     fop->runtime()->lazyScriptCache.remove(this);
3132 
3133     // In most cases, our LazyScript's script pointer will reference this
3134     // script, and thus be nulled out by normal weakref processing. However, if
3135     // we unlazified the LazyScript during incremental sweeping, it will have a
3136     // completely different JSScript.
3137     MOZ_ASSERT_IF(lazyScript && !IsAboutToBeFinalizedUnbarriered(&lazyScript),
3138                   !lazyScript->hasScript() || lazyScript->maybeScriptUnbarriered() != this);
3139 }
3140 
3141 static const uint32_t GSN_CACHE_THRESHOLD = 100;
3142 
3143 void
purge()3144 GSNCache::purge()
3145 {
3146     code = nullptr;
3147     if (map.initialized())
3148         map.finish();
3149 }
3150 
3151 jssrcnote*
GetSrcNote(GSNCache & cache,JSScript * script,jsbytecode * pc)3152 js::GetSrcNote(GSNCache& cache, JSScript* script, jsbytecode* pc)
3153 {
3154     size_t target = pc - script->code();
3155     if (target >= script->length())
3156         return nullptr;
3157 
3158     if (cache.code == script->code()) {
3159         MOZ_ASSERT(cache.map.initialized());
3160         GSNCache::Map::Ptr p = cache.map.lookup(pc);
3161         return p ? p->value() : nullptr;
3162     }
3163 
3164     size_t offset = 0;
3165     jssrcnote* result;
3166     for (jssrcnote* sn = script->notes(); ; sn = SN_NEXT(sn)) {
3167         if (SN_IS_TERMINATOR(sn)) {
3168             result = nullptr;
3169             break;
3170         }
3171         offset += SN_DELTA(sn);
3172         if (offset == target && SN_IS_GETTABLE(sn)) {
3173             result = sn;
3174             break;
3175         }
3176     }
3177 
3178     if (cache.code != script->code() && script->length() >= GSN_CACHE_THRESHOLD) {
3179         unsigned nsrcnotes = 0;
3180         for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
3181              sn = SN_NEXT(sn))
3182         {
3183             if (SN_IS_GETTABLE(sn))
3184                 ++nsrcnotes;
3185         }
3186         if (cache.code) {
3187             MOZ_ASSERT(cache.map.initialized());
3188             cache.map.finish();
3189             cache.code = nullptr;
3190         }
3191         if (cache.map.init(nsrcnotes)) {
3192             pc = script->code();
3193             for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn);
3194                  sn = SN_NEXT(sn))
3195             {
3196                 pc += SN_DELTA(sn);
3197                 if (SN_IS_GETTABLE(sn))
3198                     cache.map.putNewInfallible(pc, sn);
3199             }
3200             cache.code = script->code();
3201         }
3202     }
3203 
3204     return result;
3205 }
3206 
3207 jssrcnote*
GetSrcNote(JSContext * cx,JSScript * script,jsbytecode * pc)3208 js::GetSrcNote(JSContext* cx, JSScript* script, jsbytecode* pc)
3209 {
3210     return GetSrcNote(cx->runtime()->gsnCache, script, pc);
3211 }
3212 
3213 unsigned
PCToLineNumber(unsigned startLine,jssrcnote * notes,jsbytecode * code,jsbytecode * pc,unsigned * columnp)3214 js::PCToLineNumber(unsigned startLine, jssrcnote* notes, jsbytecode* code, jsbytecode* pc,
3215                    unsigned* columnp)
3216 {
3217     unsigned lineno = startLine;
3218     unsigned column = 0;
3219 
3220     /*
3221      * Walk through source notes accumulating their deltas, keeping track of
3222      * line-number notes, until we pass the note for pc's offset within
3223      * script->code.
3224      */
3225     ptrdiff_t offset = 0;
3226     ptrdiff_t target = pc - code;
3227     for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
3228         offset += SN_DELTA(sn);
3229         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
3230         if (type == SRC_SETLINE) {
3231             if (offset <= target)
3232                 lineno = unsigned(GetSrcNoteOffset(sn, 0));
3233             column = 0;
3234         } else if (type == SRC_NEWLINE) {
3235             if (offset <= target)
3236                 lineno++;
3237             column = 0;
3238         }
3239 
3240         if (offset > target)
3241             break;
3242 
3243         if (type == SRC_COLSPAN) {
3244             ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
3245             MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
3246             column += colspan;
3247         }
3248     }
3249 
3250     if (columnp)
3251         *columnp = column;
3252 
3253     return lineno;
3254 }
3255 
3256 unsigned
PCToLineNumber(JSScript * script,jsbytecode * pc,unsigned * columnp)3257 js::PCToLineNumber(JSScript* script, jsbytecode* pc, unsigned* columnp)
3258 {
3259     /* Cope with InterpreterFrame.pc value prior to entering Interpret. */
3260     if (!pc)
3261         return 0;
3262 
3263     return PCToLineNumber(script->lineno(), script->notes(), script->code(), pc, columnp);
3264 }
3265 
3266 jsbytecode*
LineNumberToPC(JSScript * script,unsigned target)3267 js::LineNumberToPC(JSScript* script, unsigned target)
3268 {
3269     ptrdiff_t offset = 0;
3270     ptrdiff_t best = -1;
3271     unsigned lineno = script->lineno();
3272     unsigned bestdiff = SN_MAX_OFFSET;
3273     for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
3274         /*
3275          * Exact-match only if offset is not in the prologue; otherwise use
3276          * nearest greater-or-equal line number match.
3277          */
3278         if (lineno == target && offset >= ptrdiff_t(script->mainOffset()))
3279             goto out;
3280         if (lineno >= target) {
3281             unsigned diff = lineno - target;
3282             if (diff < bestdiff) {
3283                 bestdiff = diff;
3284                 best = offset;
3285             }
3286         }
3287         offset += SN_DELTA(sn);
3288         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
3289         if (type == SRC_SETLINE) {
3290             lineno = unsigned(GetSrcNoteOffset(sn, 0));
3291         } else if (type == SRC_NEWLINE) {
3292             lineno++;
3293         }
3294     }
3295     if (best >= 0)
3296         offset = best;
3297 out:
3298     return script->offsetToPC(offset);
3299 }
3300 
JS_FRIEND_API(unsigned)3301 JS_FRIEND_API(unsigned)
3302 js::GetScriptLineExtent(JSScript* script)
3303 {
3304     unsigned lineno = script->lineno();
3305     unsigned maxLineNo = lineno;
3306     for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
3307         SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
3308         if (type == SRC_SETLINE)
3309             lineno = unsigned(GetSrcNoteOffset(sn, 0));
3310         else if (type == SRC_NEWLINE)
3311             lineno++;
3312 
3313         if (maxLineNo < lineno)
3314             maxLineNo = lineno;
3315     }
3316 
3317     return 1 + maxLineNo - script->lineno();
3318 }
3319 
3320 void
DescribeScriptedCallerForCompilation(JSContext * cx,MutableHandleScript maybeScript,const char ** file,unsigned * linenop,uint32_t * pcOffset,bool * mutedErrors,LineOption opt)3321 js::DescribeScriptedCallerForCompilation(JSContext* cx, MutableHandleScript maybeScript,
3322                                          const char** file, unsigned* linenop,
3323                                          uint32_t* pcOffset, bool* mutedErrors,
3324                                          LineOption opt)
3325 {
3326     if (opt == CALLED_FROM_JSOP_EVAL) {
3327         jsbytecode* pc = nullptr;
3328         maybeScript.set(cx->currentScript(&pc));
3329         static_assert(JSOP_SPREADEVAL_LENGTH == JSOP_STRICTSPREADEVAL_LENGTH,
3330                     "next op after a spread must be at consistent offset");
3331         static_assert(JSOP_EVAL_LENGTH == JSOP_STRICTEVAL_LENGTH,
3332                     "next op after a direct eval must be at consistent offset");
3333         MOZ_ASSERT(JSOp(*pc) == JSOP_EVAL || JSOp(*pc) == JSOP_STRICTEVAL ||
3334                    JSOp(*pc) == JSOP_SPREADEVAL || JSOp(*pc) == JSOP_STRICTSPREADEVAL);
3335 
3336         bool isSpread = JSOp(*pc) == JSOP_SPREADEVAL || JSOp(*pc) == JSOP_STRICTSPREADEVAL;
3337         jsbytecode* nextpc = pc + (isSpread ? JSOP_SPREADEVAL_LENGTH : JSOP_EVAL_LENGTH);
3338         MOZ_ASSERT(*nextpc == JSOP_LINENO);
3339 
3340         *file = maybeScript->filename();
3341         *linenop = GET_UINT32(nextpc);
3342         *pcOffset = pc - maybeScript->code();
3343         *mutedErrors = maybeScript->mutedErrors();
3344         return;
3345     }
3346 
3347     NonBuiltinFrameIter iter(cx);
3348 
3349     if (iter.done()) {
3350         maybeScript.set(nullptr);
3351         *file = nullptr;
3352         *linenop = 0;
3353         *pcOffset = 0;
3354         *mutedErrors = false;
3355         return;
3356     }
3357 
3358     *file = iter.scriptFilename();
3359     *linenop = iter.computeLine();
3360     *mutedErrors = iter.mutedErrors();
3361 
3362     // These values are only used for introducer fields which are debugging
3363     // information and can be safely left null for asm.js frames.
3364     if (iter.hasScript()) {
3365         maybeScript.set(iter.script());
3366         *pcOffset = iter.pc() - maybeScript->code();
3367     } else {
3368         maybeScript.set(nullptr);
3369         *pcOffset = 0;
3370     }
3371 }
3372 
3373 template <class T>
3374 static inline T*
Rebase(JSScript * dst,JSScript * src,T * srcp)3375 Rebase(JSScript* dst, JSScript* src, T* srcp)
3376 {
3377     size_t off = reinterpret_cast<uint8_t*>(srcp) - src->data;
3378     return reinterpret_cast<T*>(dst->data + off);
3379 }
3380 
3381 static JSObject*
CloneInnerInterpretedFunction(JSContext * cx,HandleObject enclosingScope,HandleFunction srcFun)3382 CloneInnerInterpretedFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction srcFun)
3383 {
3384     /* NB: Keep this in sync with XDRInterpretedFunction. */
3385     RootedObject cloneProto(cx);
3386     if (srcFun->isStarGenerator()) {
3387         cloneProto = GlobalObject::getOrCreateStarGeneratorFunctionPrototype(cx, cx->global());
3388         if (!cloneProto)
3389             return nullptr;
3390     }
3391 
3392     gc::AllocKind allocKind = srcFun->getAllocKind();
3393     RootedFunction clone(cx, NewFunctionWithProto(cx, nullptr, 0,
3394                                                   JSFunction::INTERPRETED, nullptr, nullptr,
3395                                                   cloneProto, allocKind, TenuredObject));
3396     if (!clone)
3397         return nullptr;
3398 
3399     JSScript::AutoDelazify srcScript(cx, srcFun);
3400     if (!srcScript)
3401         return nullptr;
3402     JSScript* cloneScript = CloneScriptIntoFunction(cx, enclosingScope, clone, srcScript);
3403     if (!cloneScript)
3404         return nullptr;
3405 
3406     clone->setArgCount(srcFun->nargs());
3407     clone->setFlags(srcFun->flags());
3408     clone->initAtom(srcFun->displayAtom());
3409     if (!JSFunction::setTypeForScriptedFunction(cx, clone))
3410         return nullptr;
3411 
3412     return clone;
3413 }
3414 
3415 bool
CopyScript(JSContext * cx,HandleObject scriptStaticScope,HandleScript src,HandleScript dst)3416 js::detail::CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src,
3417                        HandleScript dst)
3418 {
3419     if (src->treatAsRunOnce() && !src->functionNonDelazifying()) {
3420         // Toplevel run-once scripts may not be cloned.
3421         JS_ReportError(cx, "No cloning toplevel run-once scripts");
3422         return false;
3423     }
3424 
3425     /* NB: Keep this in sync with XDRScript. */
3426 
3427     /* Some embeddings are not careful to use ExposeObjectToActiveJS as needed. */
3428     MOZ_ASSERT(!src->sourceObject()->asTenured().isMarked(gc::GRAY));
3429 
3430     uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
3431     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
3432     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
3433     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
3434     uint32_t nblockscopes = src->hasBlockScopes() ? src->blockScopes()->length : 0;
3435     uint32_t nyieldoffsets = src->hasYieldOffsets() ? src->yieldOffsets().length() : 0;
3436 
3437     /* Script data */
3438 
3439     size_t size = src->dataSize();
3440     ScopedJSFreePtr<uint8_t> data(AllocScriptData(cx->zone(), size));
3441     if (size && !data) {
3442         ReportOutOfMemory(cx);
3443         return false;
3444     }
3445 
3446     /* Bindings */
3447 
3448     Rooted<Bindings> bindings(cx);
3449     if (!Bindings::clone(cx, &bindings, data, src))
3450         return false;
3451 
3452     /* Objects */
3453 
3454     AutoObjectVector objects(cx);
3455     if (nobjects != 0) {
3456         HeapPtrObject* vector = src->objects()->vector;
3457         for (unsigned i = 0; i < nobjects; i++) {
3458             RootedObject obj(cx, vector[i]);
3459             RootedObject clone(cx);
3460             if (obj->is<NestedScopeObject>()) {
3461                 Rooted<NestedScopeObject*> innerBlock(cx, &obj->as<NestedScopeObject>());
3462 
3463                 RootedObject enclosingScope(cx);
3464                 if (NestedScopeObject* enclosingBlock = innerBlock->enclosingNestedScope()) {
3465                     if (IsStaticGlobalLexicalScope(enclosingBlock)) {
3466                         MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
3467                                    scriptStaticScope->is<StaticNonSyntacticScopeObjects>());
3468                         enclosingScope = scriptStaticScope;
3469                     } else {
3470                         enclosingScope = objects[FindScopeObjectIndex(src, *enclosingBlock)];
3471                     }
3472                 } else {
3473                     enclosingScope = scriptStaticScope;
3474                 }
3475 
3476                 clone = CloneNestedScopeObject(cx, enclosingScope, innerBlock);
3477             } else if (obj->is<JSFunction>()) {
3478                 RootedFunction innerFun(cx, &obj->as<JSFunction>());
3479                 if (innerFun->isNative()) {
3480                     if (cx->compartment() != innerFun->compartment()) {
3481                         MOZ_ASSERT(innerFun->isAsmJSNative());
3482                         JS_ReportError(cx, "AsmJS modules do not yet support cloning.");
3483                         return false;
3484                     }
3485                     clone = innerFun;
3486                 } else {
3487                     if (innerFun->isInterpretedLazy()) {
3488                         AutoCompartment ac(cx, innerFun);
3489                         if (!innerFun->getOrCreateScript(cx))
3490                             return false;
3491                     }
3492                     RootedObject staticScope(cx, innerFun->nonLazyScript()->enclosingStaticScope());
3493                     StaticScopeIter<CanGC> ssi(cx, staticScope);
3494                     RootedObject enclosingScope(cx);
3495                     if (ssi.done() || ssi.type() == StaticScopeIter<CanGC>::NonSyntactic) {
3496                         enclosingScope = scriptStaticScope;
3497                     } else if (ssi.type() == StaticScopeIter<CanGC>::Function) {
3498                         MOZ_ASSERT(scriptStaticScope->is<JSFunction>());
3499                         enclosingScope = scriptStaticScope;
3500                     } else if (ssi.type() == StaticScopeIter<CanGC>::Block) {
3501                         if (ssi.block().isGlobal()) {
3502                             MOZ_ASSERT(IsStaticGlobalLexicalScope(scriptStaticScope) ||
3503                                        scriptStaticScope->is<StaticNonSyntacticScopeObjects>());
3504                             enclosingScope = scriptStaticScope;
3505                         } else {
3506                             enclosingScope = objects[FindScopeObjectIndex(src, ssi.block())];
3507                         }
3508                     } else {
3509                         enclosingScope = objects[FindScopeObjectIndex(src, ssi.staticWith())];
3510                     }
3511 
3512                     clone = CloneInnerInterpretedFunction(cx, enclosingScope, innerFun);
3513                 }
3514             } else {
3515                 clone = DeepCloneObjectLiteral(cx, obj, TenuredObject);
3516             }
3517             if (!clone || !objects.append(clone))
3518                 return false;
3519         }
3520     }
3521 
3522     /* RegExps */
3523 
3524     AutoObjectVector regexps(cx);
3525     for (unsigned i = 0; i < nregexps; i++) {
3526         HeapPtrObject* vector = src->regexps()->vector;
3527         for (unsigned i = 0; i < nregexps; i++) {
3528             JSObject* clone = CloneScriptRegExpObject(cx, vector[i]->as<RegExpObject>());
3529             if (!clone || !regexps.append(clone))
3530                 return false;
3531         }
3532     }
3533 
3534     /* Now that all fallible allocation is complete, do the copying. */
3535 
3536     dst->bindings = bindings;
3537 
3538     /* This assignment must occur before all the Rebase calls. */
3539     dst->data = data.forget();
3540     dst->dataSize_ = size;
3541     memcpy(dst->data, src->data, size);
3542 
3543     /* Script filenames, bytecodes and atoms are runtime-wide. */
3544     dst->setCode(src->code());
3545     dst->atoms = src->atoms;
3546 
3547     dst->setLength(src->length());
3548     dst->lineno_ = src->lineno();
3549     dst->mainOffset_ = src->mainOffset();
3550     dst->natoms_ = src->natoms();
3551     dst->funLength_ = src->funLength();
3552     dst->nTypeSets_ = src->nTypeSets();
3553     dst->nslots_ = src->nslots();
3554     if (src->argumentsHasVarBinding()) {
3555         dst->setArgumentsHasVarBinding();
3556         if (src->analyzedArgsUsage())
3557             dst->setNeedsArgsObj(src->needsArgsObj());
3558     }
3559     dst->hasMappedArgsObj_ = src->hasMappedArgsObj();
3560     dst->functionHasThisBinding_ = src->functionHasThisBinding();
3561     dst->cloneHasArray(src);
3562     dst->strict_ = src->strict();
3563     dst->explicitUseStrict_ = src->explicitUseStrict();
3564     dst->bindingsAccessedDynamically_ = src->bindingsAccessedDynamically();
3565     dst->funHasExtensibleScope_ = src->funHasExtensibleScope();
3566     dst->funNeedsDeclEnvObject_ = src->funNeedsDeclEnvObject();
3567     dst->funHasAnyAliasedFormal_ = src->funHasAnyAliasedFormal();
3568     dst->hasSingletons_ = src->hasSingletons();
3569     dst->treatAsRunOnce_ = src->treatAsRunOnce();
3570     dst->hasInnerFunctions_ = src->hasInnerFunctions();
3571     dst->isGeneratorExp_ = src->isGeneratorExp();
3572     dst->setGeneratorKind(src->generatorKind());
3573 
3574     if (nconsts != 0) {
3575         HeapValue* vector = Rebase<HeapValue>(dst, src, src->consts()->vector);
3576         dst->consts()->vector = vector;
3577         for (unsigned i = 0; i < nconsts; ++i)
3578             MOZ_ASSERT_IF(vector[i].isMarkable(), vector[i].toString()->isAtom());
3579     }
3580     if (nobjects != 0) {
3581         HeapPtrObject* vector = Rebase<HeapPtrObject>(dst, src, src->objects()->vector);
3582         dst->objects()->vector = vector;
3583         for (unsigned i = 0; i < nobjects; ++i)
3584             vector[i].init(&objects[i]->as<NativeObject>());
3585     }
3586     if (nregexps != 0) {
3587         HeapPtrObject* vector = Rebase<HeapPtrObject>(dst, src, src->regexps()->vector);
3588         dst->regexps()->vector = vector;
3589         for (unsigned i = 0; i < nregexps; ++i)
3590             vector[i].init(&regexps[i]->as<NativeObject>());
3591     }
3592     if (ntrynotes != 0)
3593         dst->trynotes()->vector = Rebase<JSTryNote>(dst, src, src->trynotes()->vector);
3594     if (nblockscopes != 0)
3595         dst->blockScopes()->vector = Rebase<BlockScopeNote>(dst, src, src->blockScopes()->vector);
3596     if (nyieldoffsets != 0)
3597         dst->yieldOffsets().vector_ = Rebase<uint32_t>(dst, src, src->yieldOffsets().vector_);
3598 
3599     /*
3600      * Function delazification assumes that their script does not have a
3601      * non-syntactic global scope.  We ensure that as follows:
3602      *
3603      * 1) Initial parsing only creates lazy functions if
3604      *    !hasNonSyntacticScope.
3605      * 2) Cloning a lazy function into a non-global scope will always require
3606      *    that its script be cloned.  See comments in
3607      *    CloneFunctionObjectUseSameScript.
3608      * 3) Cloning a script never sets a lazyScript on the clone, so the function
3609      *    cannot be relazified.
3610      *
3611      * If you decide that lazy functions should be supported with a
3612      * non-syntactic global scope, make sure delazification can deal.
3613      */
3614     MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->maybeLazyScript());
3615     MOZ_ASSERT_IF(dst->hasNonSyntacticScope(), !dst->isRelazifiable());
3616     return true;
3617 }
3618 
3619 static JSScript*
CreateEmptyScriptForClone(JSContext * cx,HandleObject enclosingScope,HandleScript src)3620 CreateEmptyScriptForClone(JSContext* cx, HandleObject enclosingScope, HandleScript src)
3621 {
3622     /*
3623      * Wrap the script source object as needed. Self-hosted scripts may be
3624      * in another runtime, so lazily create a new script source object to
3625      * use for them.
3626      */
3627     RootedObject sourceObject(cx);
3628     if (cx->runtime()->isSelfHostingCompartment(src->compartment())) {
3629         if (!cx->compartment()->selfHostingScriptSource) {
3630             CompileOptions options(cx);
3631             FillSelfHostingCompileOptions(options);
3632 
3633             ScriptSourceObject* obj = frontend::CreateScriptSourceObject(cx, options);
3634             if (!obj)
3635                 return nullptr;
3636             cx->compartment()->selfHostingScriptSource.set(obj);
3637         }
3638         sourceObject = cx->compartment()->selfHostingScriptSource;
3639     } else {
3640         sourceObject = src->sourceObject();
3641         if (!cx->compartment()->wrap(cx, &sourceObject))
3642             return nullptr;
3643     }
3644 
3645     CompileOptions options(cx);
3646     options.setMutedErrors(src->mutedErrors())
3647            .setSelfHostingMode(src->selfHosted())
3648            .setNoScriptRval(src->noScriptRval())
3649            .setVersion(src->getVersion());
3650 
3651     return JSScript::Create(cx, enclosingScope, src->savedCallerFun(),
3652                             options, sourceObject, src->sourceStart(), src->sourceEnd());
3653 }
3654 
3655 JSScript*
CloneGlobalScript(JSContext * cx,Handle<ScopeObject * > enclosingScope,HandleScript src)3656 js::CloneGlobalScript(JSContext* cx, Handle<ScopeObject*> enclosingScope, HandleScript src)
3657 {
3658     MOZ_ASSERT(IsStaticGlobalLexicalScope(enclosingScope) ||
3659                enclosingScope->is<StaticNonSyntacticScopeObjects>());
3660 
3661     RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
3662     if (!dst)
3663         return nullptr;
3664 
3665     if (!detail::CopyScript(cx, enclosingScope, src, dst))
3666         return nullptr;
3667 
3668     return dst;
3669 }
3670 
3671 JSScript*
CloneScriptIntoFunction(JSContext * cx,HandleObject enclosingScope,HandleFunction fun,HandleScript src)3672 js::CloneScriptIntoFunction(JSContext* cx, HandleObject enclosingScope, HandleFunction fun,
3673                             HandleScript src)
3674 {
3675     MOZ_ASSERT(fun->isInterpreted());
3676 
3677     // Allocate the destination script up front and set it as the script of
3678     // |fun|, which is to be its container.
3679     //
3680     // This is so that when cloning nested functions, they can walk the static
3681     // scope chain via fun and correctly compute the presence of a
3682     // non-syntactic global.
3683     RootedScript dst(cx, CreateEmptyScriptForClone(cx, enclosingScope, src));
3684     if (!dst)
3685         return nullptr;
3686 
3687     // Save flags in case we need to undo the early mutations.
3688     const int preservedFlags = fun->flags();
3689 
3690     dst->setFunction(fun);
3691     Rooted<LazyScript*> lazy(cx);
3692     if (fun->isInterpretedLazy()) {
3693         lazy = fun->lazyScriptOrNull();
3694         fun->setUnlazifiedScript(dst);
3695     } else {
3696         fun->initScript(dst);
3697     }
3698 
3699     if (!detail::CopyScript(cx, fun, src, dst)) {
3700         if (lazy)
3701             fun->initLazyScript(lazy);
3702         else
3703             fun->setScript(nullptr);
3704         fun->setFlags(preservedFlags);
3705         return nullptr;
3706     }
3707 
3708     return dst;
3709 }
3710 
3711 DebugScript*
debugScript()3712 JSScript::debugScript()
3713 {
3714     MOZ_ASSERT(hasDebugScript_);
3715     DebugScriptMap* map = compartment()->debugScriptMap;
3716     MOZ_ASSERT(map);
3717     DebugScriptMap::Ptr p = map->lookup(this);
3718     MOZ_ASSERT(p);
3719     return p->value();
3720 }
3721 
3722 DebugScript*
releaseDebugScript()3723 JSScript::releaseDebugScript()
3724 {
3725     MOZ_ASSERT(hasDebugScript_);
3726     DebugScriptMap* map = compartment()->debugScriptMap;
3727     MOZ_ASSERT(map);
3728     DebugScriptMap::Ptr p = map->lookup(this);
3729     MOZ_ASSERT(p);
3730     DebugScript* debug = p->value();
3731     map->remove(p);
3732     hasDebugScript_ = false;
3733     return debug;
3734 }
3735 
3736 void
destroyDebugScript(FreeOp * fop)3737 JSScript::destroyDebugScript(FreeOp* fop)
3738 {
3739     if (hasDebugScript_) {
3740 #ifdef DEBUG
3741         for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
3742             if (BreakpointSite* site = getBreakpointSite(pc)) {
3743                 /* Breakpoints are swept before finalization. */
3744                 MOZ_ASSERT(site->firstBreakpoint() == nullptr);
3745                 MOZ_ASSERT(getBreakpointSite(pc) == nullptr);
3746             }
3747         }
3748 #endif
3749         fop->free_(releaseDebugScript());
3750     }
3751 }
3752 
3753 bool
ensureHasDebugScript(JSContext * cx)3754 JSScript::ensureHasDebugScript(JSContext* cx)
3755 {
3756     if (hasDebugScript_)
3757         return true;
3758 
3759     size_t nbytes = offsetof(DebugScript, breakpoints) + length() * sizeof(BreakpointSite*);
3760     DebugScript* debug = (DebugScript*) zone()->pod_calloc<uint8_t>(nbytes);
3761     if (!debug)
3762         return false;
3763 
3764     /* Create compartment's debugScriptMap if necessary. */
3765     DebugScriptMap* map = compartment()->debugScriptMap;
3766     if (!map) {
3767         map = cx->new_<DebugScriptMap>();
3768         if (!map || !map->init()) {
3769             js_free(debug);
3770             js_delete(map);
3771             return false;
3772         }
3773         compartment()->debugScriptMap = map;
3774     }
3775 
3776     if (!map->putNew(this, debug)) {
3777         js_free(debug);
3778         return false;
3779     }
3780     hasDebugScript_ = true; // safe to set this;  we can't fail after this point
3781 
3782     /*
3783      * Ensure that any Interpret() instances running on this script have
3784      * interrupts enabled. The interrupts must stay enabled until the
3785      * debug state is destroyed.
3786      */
3787     for (ActivationIterator iter(cx->runtime()); !iter.done(); ++iter) {
3788         if (iter->isInterpreter())
3789             iter->asInterpreter()->enableInterruptsIfRunning(this);
3790     }
3791 
3792     return true;
3793 }
3794 
3795 void
setNewStepMode(FreeOp * fop,uint32_t newValue)3796 JSScript::setNewStepMode(FreeOp* fop, uint32_t newValue)
3797 {
3798     DebugScript* debug = debugScript();
3799     uint32_t prior = debug->stepMode;
3800     debug->stepMode = newValue;
3801 
3802     if (!prior != !newValue) {
3803         if (hasBaselineScript())
3804             baseline->toggleDebugTraps(this, nullptr);
3805 
3806         if (!stepModeEnabled() && !debug->numSites)
3807             fop->free_(releaseDebugScript());
3808     }
3809 }
3810 
3811 bool
incrementStepModeCount(JSContext * cx)3812 JSScript::incrementStepModeCount(JSContext* cx)
3813 {
3814     assertSameCompartment(cx, this);
3815     MOZ_ASSERT(cx->compartment()->isDebuggee());
3816 
3817     if (!ensureHasDebugScript(cx))
3818         return false;
3819 
3820     DebugScript* debug = debugScript();
3821     uint32_t count = debug->stepMode;
3822     setNewStepMode(cx->runtime()->defaultFreeOp(), count + 1);
3823     return true;
3824 }
3825 
3826 void
decrementStepModeCount(FreeOp * fop)3827 JSScript::decrementStepModeCount(FreeOp* fop)
3828 {
3829     DebugScript* debug = debugScript();
3830     uint32_t count = debug->stepMode;
3831     MOZ_ASSERT(count > 0);
3832     setNewStepMode(fop, count - 1);
3833 }
3834 
3835 BreakpointSite*
getOrCreateBreakpointSite(JSContext * cx,jsbytecode * pc)3836 JSScript::getOrCreateBreakpointSite(JSContext* cx, jsbytecode* pc)
3837 {
3838     if (!ensureHasDebugScript(cx))
3839         return nullptr;
3840 
3841     DebugScript* debug = debugScript();
3842     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
3843 
3844     if (!site) {
3845         site = cx->runtime()->new_<BreakpointSite>(this, pc);
3846         if (!site) {
3847             ReportOutOfMemory(cx);
3848             return nullptr;
3849         }
3850         debug->numSites++;
3851     }
3852 
3853     return site;
3854 }
3855 
3856 void
destroyBreakpointSite(FreeOp * fop,jsbytecode * pc)3857 JSScript::destroyBreakpointSite(FreeOp* fop, jsbytecode* pc)
3858 {
3859     DebugScript* debug = debugScript();
3860     BreakpointSite*& site = debug->breakpoints[pcToOffset(pc)];
3861     MOZ_ASSERT(site);
3862 
3863     fop->delete_(site);
3864     site = nullptr;
3865 
3866     if (--debug->numSites == 0 && !stepModeEnabled())
3867         fop->free_(releaseDebugScript());
3868 }
3869 
3870 void
clearBreakpointsIn(FreeOp * fop,js::Debugger * dbg,JSObject * handler)3871 JSScript::clearBreakpointsIn(FreeOp* fop, js::Debugger* dbg, JSObject* handler)
3872 {
3873     if (!hasAnyBreakpointsOrStepMode())
3874         return;
3875 
3876     for (jsbytecode* pc = code(); pc < codeEnd(); pc++) {
3877         BreakpointSite* site = getBreakpointSite(pc);
3878         if (site) {
3879             Breakpoint* nextbp;
3880             for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
3881                 nextbp = bp->nextInSite();
3882                 if ((!dbg || bp->debugger == dbg) && (!handler || bp->getHandler() == handler))
3883                     bp->destroy(fop);
3884             }
3885         }
3886     }
3887 }
3888 
3889 bool
hasBreakpointsAt(jsbytecode * pc)3890 JSScript::hasBreakpointsAt(jsbytecode* pc)
3891 {
3892     BreakpointSite* site = getBreakpointSite(pc);
3893     if (!site)
3894         return false;
3895 
3896     return site->enabledCount > 0;
3897 }
3898 
3899 void
traceChildren(JSTracer * trc)3900 JSScript::traceChildren(JSTracer* trc)
3901 {
3902     // NOTE: this JSScript may be partially initialized at this point.  E.g. we
3903     // may have created it and partially initialized it with
3904     // JSScript::Create(), but not yet finished initializing it with
3905     // fullyInitFromEmitter() or fullyInitTrivial().
3906 
3907     MOZ_ASSERT_IF(trc->isMarkingTracer() &&
3908                   static_cast<GCMarker*>(trc)->shouldCheckCompartments(),
3909                   zone()->isCollecting());
3910 
3911     if (atoms) {
3912         for (uint32_t i = 0; i < natoms(); ++i) {
3913             if (atoms[i])
3914                 TraceEdge(trc, &atoms[i], "atom");
3915         }
3916     }
3917 
3918     if (hasObjects()) {
3919         ObjectArray* objarray = objects();
3920         TraceRange(trc, objarray->length, objarray->vector, "objects");
3921     }
3922 
3923     if (hasRegexps()) {
3924         ObjectArray* objarray = regexps();
3925         TraceRange(trc, objarray->length, objarray->vector, "regexps");
3926     }
3927 
3928     if (hasConsts()) {
3929         ConstArray* constarray = consts();
3930         TraceRange(trc, constarray->length, constarray->vector, "consts");
3931     }
3932 
3933     if (sourceObject()) {
3934         MOZ_ASSERT(MaybeForwarded(sourceObject())->compartment() == compartment());
3935         TraceEdge(trc, &sourceObject_, "sourceObject");
3936     }
3937 
3938     if (functionNonDelazifying())
3939         TraceEdge(trc, &function_, "function");
3940 
3941     if (module_)
3942         TraceEdge(trc, &module_, "module");
3943 
3944     if (enclosingStaticScope_)
3945         TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");
3946 
3947     if (maybeLazyScript())
3948         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
3949 
3950     if (trc->isMarkingTracer()) {
3951         compartment()->mark();
3952 
3953         if (code())
3954             MarkScriptData(trc->runtime(), code());
3955     }
3956 
3957     bindings.trace(trc);
3958 
3959     jit::TraceJitScripts(trc, this);
3960 }
3961 
3962 void
finalize(FreeOp * fop)3963 LazyScript::finalize(FreeOp* fop)
3964 {
3965     if (table_)
3966         fop->free_(table_);
3967 }
3968 
3969 size_t
calculateLiveFixed(jsbytecode * pc)3970 JSScript::calculateLiveFixed(jsbytecode* pc)
3971 {
3972     size_t nlivefixed = nbodyfixed();
3973 
3974     if (nfixed() != nlivefixed) {
3975         NestedScopeObject* staticScope = getStaticBlockScope(pc);
3976         if (staticScope)
3977             staticScope = MaybeForwarded(staticScope);
3978         while (staticScope && !staticScope->is<StaticBlockObject>()) {
3979             staticScope = staticScope->enclosingNestedScope();
3980             if (staticScope)
3981                 staticScope = MaybeForwarded(staticScope);
3982         }
3983 
3984         if (staticScope && !IsStaticGlobalLexicalScope(staticScope)) {
3985             StaticBlockObject& blockObj = staticScope->as<StaticBlockObject>();
3986             nlivefixed = blockObj.localOffset() + blockObj.numVariables();
3987         }
3988     }
3989 
3990     MOZ_ASSERT(nlivefixed <= nfixed());
3991     MOZ_ASSERT(nlivefixed >= nbodyfixed());
3992 
3993     return nlivefixed;
3994 }
3995 
3996 NestedScopeObject*
getStaticBlockScope(jsbytecode * pc)3997 JSScript::getStaticBlockScope(jsbytecode* pc)
3998 {
3999     MOZ_ASSERT(containsPC(pc));
4000 
4001     if (!hasBlockScopes())
4002         return nullptr;
4003 
4004     size_t offset = pc - code();
4005 
4006     BlockScopeArray* scopes = blockScopes();
4007     NestedScopeObject* blockChain = nullptr;
4008 
4009     // Find the innermost block chain using a binary search.
4010     size_t bottom = 0;
4011     size_t top = scopes->length;
4012 
4013     while (bottom < top) {
4014         size_t mid = bottom + (top - bottom) / 2;
4015         const BlockScopeNote* note = &scopes->vector[mid];
4016         if (note->start <= offset) {
4017             // Block scopes are ordered in the list by their starting offset, and since
4018             // blocks form a tree ones earlier in the list may cover the pc even if
4019             // later blocks end before the pc. This only happens when the earlier block
4020             // is a parent of the later block, so we need to check parents of |mid| in
4021             // the searched range for coverage.
4022             size_t check = mid;
4023             while (check >= bottom) {
4024                 const BlockScopeNote* checkNote = &scopes->vector[check];
4025                 MOZ_ASSERT(checkNote->start <= offset);
4026                 if (offset < checkNote->start + checkNote->length) {
4027                     // We found a matching block chain but there may be inner ones
4028                     // at a higher block chain index than mid. Continue the binary search.
4029                     if (checkNote->index == BlockScopeNote::NoBlockScopeIndex)
4030                         blockChain = nullptr;
4031                     else
4032                         blockChain = &getObject(checkNote->index)->as<NestedScopeObject>();
4033                     break;
4034                 }
4035                 if (checkNote->parent == UINT32_MAX)
4036                     break;
4037                 check = checkNote->parent;
4038             }
4039             bottom = mid + 1;
4040         } else {
4041             top = mid;
4042         }
4043     }
4044 
4045     return blockChain;
4046 }
4047 
4048 JSObject*
innermostStaticScopeInScript(jsbytecode * pc)4049 JSScript::innermostStaticScopeInScript(jsbytecode* pc)
4050 {
4051     if (JSObject* scope = getStaticBlockScope(pc))
4052         return scope;
4053     if (module())
4054         return module();
4055     return functionNonDelazifying();
4056 }
4057 
4058 JSObject*
innermostStaticScope(jsbytecode * pc)4059 JSScript::innermostStaticScope(jsbytecode* pc)
4060 {
4061     if (JSObject* scope = innermostStaticScopeInScript(pc))
4062         return scope;
4063     return enclosingStaticScope();
4064 }
4065 
4066 void
setArgumentsHasVarBinding()4067 JSScript::setArgumentsHasVarBinding()
4068 {
4069     argsHasVarBinding_ = true;
4070     needsArgsAnalysis_ = true;
4071 }
4072 
4073 void
setNeedsArgsObj(bool needsArgsObj)4074 JSScript::setNeedsArgsObj(bool needsArgsObj)
4075 {
4076     MOZ_ASSERT_IF(needsArgsObj, argumentsHasVarBinding());
4077     needsArgsAnalysis_ = false;
4078     needsArgsObj_ = needsArgsObj;
4079 }
4080 
4081 void
SetFrameArgumentsObject(JSContext * cx,AbstractFramePtr frame,HandleScript script,JSObject * argsobj)4082 js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
4083                             HandleScript script, JSObject* argsobj)
4084 {
4085     /*
4086      * Replace any optimized arguments in the frame with an explicit arguments
4087      * object. Note that 'arguments' may have already been overwritten.
4088      */
4089 
4090     BindingIter bi = Bindings::argumentsBinding(cx, script);
4091 
4092     if (script->bindingIsAliased(bi)) {
4093         /*
4094          * Scan the script to find the slot in the call object that 'arguments'
4095          * is assigned to.
4096          */
4097         jsbytecode* pc = script->code();
4098         while (*pc != JSOP_ARGUMENTS)
4099             pc += GetBytecodeLength(pc);
4100         pc += JSOP_ARGUMENTS_LENGTH;
4101         MOZ_ASSERT(*pc == JSOP_SETALIASEDVAR);
4102 
4103         // Note that here and below, it is insufficient to only check for
4104         // JS_OPTIMIZED_ARGUMENTS, as Ion could have optimized out the
4105         // arguments slot.
4106         if (IsOptimizedPlaceholderMagicValue(frame.callObj().as<ScopeObject>().aliasedVar(ScopeCoordinate(pc))))
4107             frame.callObj().as<ScopeObject>().setAliasedVar(cx, ScopeCoordinate(pc), cx->names().arguments, ObjectValue(*argsobj));
4108     } else {
4109         if (IsOptimizedPlaceholderMagicValue(frame.unaliasedLocal(bi.frameIndex())))
4110             frame.unaliasedLocal(bi.frameIndex()) = ObjectValue(*argsobj);
4111     }
4112 }
4113 
4114 /* static */ bool
argumentsOptimizationFailed(JSContext * cx,HandleScript script)4115 JSScript::argumentsOptimizationFailed(JSContext* cx, HandleScript script)
4116 {
4117     MOZ_ASSERT(script->functionNonDelazifying());
4118     MOZ_ASSERT(script->analyzedArgsUsage());
4119     MOZ_ASSERT(script->argumentsHasVarBinding());
4120 
4121     /*
4122      * It is possible that the arguments optimization has already failed,
4123      * everything has been fixed up, but there was an outstanding magic value
4124      * on the stack that has just now flowed into an apply. In this case, there
4125      * is nothing to do; GuardFunApplySpeculation will patch in the real
4126      * argsobj.
4127      */
4128     if (script->needsArgsObj())
4129         return true;
4130 
4131     MOZ_ASSERT(!script->isGenerator());
4132 
4133     script->needsArgsObj_ = true;
4134 
4135     /*
4136      * Since we can't invalidate baseline scripts, set a flag that's checked from
4137      * JIT code to indicate the arguments optimization failed and JSOP_ARGUMENTS
4138      * should create an arguments object next time.
4139      */
4140     if (script->hasBaselineScript())
4141         script->baselineScript()->setNeedsArgsObj();
4142 
4143     /*
4144      * By design, the arguments optimization is only made when there are no
4145      * outstanding cases of MagicValue(JS_OPTIMIZED_ARGUMENTS) at any points
4146      * where the optimization could fail, other than an active invocation of
4147      * 'f.apply(x, arguments)'. Thus, there are no outstanding values of
4148      * MagicValue(JS_OPTIMIZED_ARGUMENTS) on the stack. However, there are
4149      * three things that need fixup:
4150      *  - there may be any number of activations of this script that don't have
4151      *    an argsObj that now need one.
4152      *  - jit code compiled (and possible active on the stack) with the static
4153      *    assumption of !script->needsArgsObj();
4154      *  - type inference data for the script assuming script->needsArgsObj
4155      */
4156     for (AllFramesIter i(cx); !i.done(); ++i) {
4157         /*
4158          * We cannot reliably create an arguments object for Ion activations of
4159          * this script.  To maintain the invariant that "script->needsArgsObj
4160          * implies fp->hasArgsObj", the Ion bail mechanism will create an
4161          * arguments object right after restoring the BaselineFrame and before
4162          * entering Baseline code (in jit::FinishBailoutToBaseline).
4163          */
4164         if (i.isIon())
4165             continue;
4166         AbstractFramePtr frame = i.abstractFramePtr();
4167         if (frame.isFunctionFrame() && frame.script() == script) {
4168             /* We crash on OOM since cleaning up here would be complicated. */
4169             AutoEnterOOMUnsafeRegion oomUnsafe;
4170             ArgumentsObject* argsobj = ArgumentsObject::createExpected(cx, frame);
4171             if (!argsobj)
4172                 oomUnsafe.crash("JSScript::argumentsOptimizationFailed");
4173             SetFrameArgumentsObject(cx, frame, script, argsobj);
4174         }
4175     }
4176 
4177     return true;
4178 }
4179 
4180 bool
bindingIsAliased(const BindingIter & bi)4181 JSScript::bindingIsAliased(const BindingIter& bi)
4182 {
4183     return bindings.bindingIsAliased(bi.i_);
4184 }
4185 
4186 bool
formalIsAliased(unsigned argSlot)4187 JSScript::formalIsAliased(unsigned argSlot)
4188 {
4189     MOZ_ASSERT(argSlot < bindings.numArgs());
4190     return bindings.bindingIsAliased(argSlot);
4191 }
4192 
4193 bool
localIsAliased(unsigned localSlot)4194 JSScript::localIsAliased(unsigned localSlot)
4195 {
4196     return bindings.bindingIsAliased(bindings.numArgs() + localSlot);
4197 }
4198 
4199 bool
formalLivesInArgumentsObject(unsigned argSlot)4200 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
4201 {
4202     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
4203 }
4204 
LazyScript(JSFunction * fun,void * table,uint64_t packedFields,uint32_t begin,uint32_t end,uint32_t lineno,uint32_t column)4205 LazyScript::LazyScript(JSFunction* fun, void* table, uint64_t packedFields, uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
4206   : script_(nullptr),
4207     function_(fun),
4208     enclosingScope_(nullptr),
4209     sourceObject_(nullptr),
4210     table_(table),
4211     packedFields_(packedFields),
4212     begin_(begin),
4213     end_(end),
4214     lineno_(lineno),
4215     column_(column)
4216 {
4217     MOZ_ASSERT(begin <= end);
4218 }
4219 
4220 void
initScript(JSScript * script)4221 LazyScript::initScript(JSScript* script)
4222 {
4223     MOZ_ASSERT(script);
4224     MOZ_ASSERT(!script_.unbarrieredGet());
4225     script_.set(script);
4226 }
4227 
4228 void
resetScript()4229 LazyScript::resetScript()
4230 {
4231     MOZ_ASSERT(script_.unbarrieredGet());
4232     script_.set(nullptr);
4233 }
4234 
4235 void
setParent(JSObject * enclosingScope,ScriptSourceObject * sourceObject)4236 LazyScript::setParent(JSObject* enclosingScope, ScriptSourceObject* sourceObject)
4237 {
4238     MOZ_ASSERT(!sourceObject_ && !enclosingScope_);
4239     MOZ_ASSERT_IF(enclosingScope, function_->compartment() == enclosingScope->compartment());
4240     MOZ_ASSERT(function_->compartment() == sourceObject->compartment());
4241 
4242     enclosingScope_ = enclosingScope;
4243     sourceObject_ = sourceObject;
4244 }
4245 
4246 ScriptSourceObject*
sourceObject() const4247 LazyScript::sourceObject() const
4248 {
4249     return sourceObject_ ? &sourceObject_->as<ScriptSourceObject>() : nullptr;
4250 }
4251 
4252 ScriptSource*
maybeForwardedScriptSource() const4253 LazyScript::maybeForwardedScriptSource() const
4254 {
4255     return UncheckedUnwrap(MaybeForwarded(sourceObject()))->as<ScriptSourceObject>().source();
4256 }
4257 
4258 /* static */ LazyScript*
CreateRaw(ExclusiveContext * cx,HandleFunction fun,uint64_t packedFields,uint32_t begin,uint32_t end,uint32_t lineno,uint32_t column)4259 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
4260                       uint64_t packedFields, uint32_t begin, uint32_t end,
4261                       uint32_t lineno, uint32_t column)
4262 {
4263     union {
4264         PackedView p;
4265         uint64_t packed;
4266     };
4267 
4268     packed = packedFields;
4269 
4270     // Reset runtime flags to obtain a fresh LazyScript.
4271     p.hasBeenCloned = false;
4272     p.treatAsRunOnce = false;
4273 
4274     size_t bytes = (p.numFreeVariables * sizeof(FreeVariable))
4275                  + (p.numInnerFunctions * sizeof(HeapPtrFunction));
4276 
4277     ScopedJSFreePtr<uint8_t> table(bytes ? fun->zone()->pod_malloc<uint8_t>(bytes) : nullptr);
4278     if (bytes && !table) {
4279         ReportOutOfMemory(cx);
4280         return nullptr;
4281     }
4282 
4283     LazyScript* res = Allocate<LazyScript>(cx);
4284     if (!res)
4285         return nullptr;
4286 
4287     cx->compartment()->scheduleDelazificationForDebugger();
4288 
4289     return new (res) LazyScript(fun, table.forget(), packed, begin, end, lineno, column);
4290 }
4291 
4292 /* static */ LazyScript*
CreateRaw(ExclusiveContext * cx,HandleFunction fun,uint32_t numFreeVariables,uint32_t numInnerFunctions,JSVersion version,uint32_t begin,uint32_t end,uint32_t lineno,uint32_t column)4293 LazyScript::CreateRaw(ExclusiveContext* cx, HandleFunction fun,
4294                       uint32_t numFreeVariables, uint32_t numInnerFunctions, JSVersion version,
4295                       uint32_t begin, uint32_t end, uint32_t lineno, uint32_t column)
4296 {
4297     union {
4298         PackedView p;
4299         uint64_t packedFields;
4300     };
4301 
4302     p.version = version;
4303     p.numFreeVariables = numFreeVariables;
4304     p.numInnerFunctions = numInnerFunctions;
4305     p.generatorKindBits = GeneratorKindAsBits(NotGenerator);
4306     p.strict = false;
4307     p.bindingsAccessedDynamically = false;
4308     p.hasDebuggerStatement = false;
4309     p.hasDirectEval = false;
4310     p.isLikelyConstructorWrapper = false;
4311     p.isDerivedClassConstructor = false;
4312     p.needsHomeObject = false;
4313 
4314     LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
4315     MOZ_ASSERT_IF(res, res->version() == version);
4316     return res;
4317 }
4318 
4319 /* static */ LazyScript*
Create(ExclusiveContext * cx,HandleFunction fun,HandleScript script,HandleObject enclosingScope,HandleScript sourceObjectScript,uint64_t packedFields,uint32_t begin,uint32_t end,uint32_t lineno,uint32_t column)4320 LazyScript::Create(ExclusiveContext* cx, HandleFunction fun,
4321                    HandleScript script, HandleObject enclosingScope,
4322                    HandleScript sourceObjectScript,
4323                    uint64_t packedFields, uint32_t begin, uint32_t end,
4324                    uint32_t lineno, uint32_t column)
4325 {
4326     // Dummy atom which is not a valid property name.
4327     RootedAtom dummyAtom(cx, cx->names().comma);
4328 
4329     // Dummy function which is not a valid function as this is the one which is
4330     // holding this lazy script.
4331     HandleFunction dummyFun = fun;
4332 
4333     LazyScript* res = LazyScript::CreateRaw(cx, fun, packedFields, begin, end, lineno, column);
4334     if (!res)
4335         return nullptr;
4336 
4337     // Fill with dummies, to be GC-safe after the initialization of the free
4338     // variables and inner functions.
4339     size_t i, num;
4340     FreeVariable* variables = res->freeVariables();
4341     for (i = 0, num = res->numFreeVariables(); i < num; i++)
4342         variables[i] = FreeVariable(dummyAtom);
4343 
4344     HeapPtrFunction* functions = res->innerFunctions();
4345     for (i = 0, num = res->numInnerFunctions(); i < num; i++)
4346         functions[i].init(dummyFun);
4347 
4348     // Set the enclosing scope of the lazy function, this would later be
4349     // used to define the environment when the function would be used.
4350     MOZ_ASSERT(!res->sourceObject());
4351     res->setParent(enclosingScope, &sourceObjectScript->scriptSourceUnwrap());
4352 
4353     MOZ_ASSERT(!res->hasScript());
4354     if (script)
4355         res->initScript(script);
4356 
4357     return res;
4358 }
4359 
4360 void
initRuntimeFields(uint64_t packedFields)4361 LazyScript::initRuntimeFields(uint64_t packedFields)
4362 {
4363     union {
4364         PackedView p;
4365         uint64_t packed;
4366     };
4367 
4368     packed = packedFields;
4369     p_.hasBeenCloned = p.hasBeenCloned;
4370     p_.treatAsRunOnce = p.treatAsRunOnce;
4371 }
4372 
4373 bool
hasUncompiledEnclosingScript() const4374 LazyScript::hasUncompiledEnclosingScript() const
4375 {
4376     // It can happen that we created lazy scripts while compiling an enclosing
4377     // script, but we errored out while compiling that script. When we iterate
4378     // over lazy script in a compartment, we might see lazy scripts that never
4379     // escaped to script and should be ignored.
4380     //
4381     // If the enclosing scope is a function with a null script or has a script
4382     // without code, it was not successfully compiled.
4383 
4384     if (!enclosingScope() || !enclosingScope()->is<JSFunction>())
4385         return false;
4386 
4387     JSFunction& fun = enclosingScope()->as<JSFunction>();
4388     return !fun.hasScript() || fun.hasUncompiledScript() || !fun.nonLazyScript()->code();
4389 }
4390 
4391 void
updateBaselineOrIonRaw(JSContext * maybecx)4392 JSScript::updateBaselineOrIonRaw(JSContext* maybecx)
4393 {
4394     if (hasBaselineScript() && baseline->hasPendingIonBuilder()) {
4395         MOZ_ASSERT(maybecx);
4396         MOZ_ASSERT(!isIonCompilingOffThread());
4397         baselineOrIonRaw = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
4398         baselineOrIonSkipArgCheck = maybecx->runtime()->jitRuntime()->lazyLinkStub()->raw();
4399     } else if (hasIonScript()) {
4400         baselineOrIonRaw = ion->method()->raw();
4401         baselineOrIonSkipArgCheck = ion->method()->raw() + ion->getSkipArgCheckEntryOffset();
4402     } else if (hasBaselineScript()) {
4403         baselineOrIonRaw = baseline->method()->raw();
4404         baselineOrIonSkipArgCheck = baseline->method()->raw();
4405     } else {
4406         baselineOrIonRaw = nullptr;
4407         baselineOrIonSkipArgCheck = nullptr;
4408     }
4409 }
4410 
4411 bool
hasLoops()4412 JSScript::hasLoops()
4413 {
4414     if (!hasTrynotes())
4415         return false;
4416     JSTryNote* tn = trynotes()->vector;
4417     JSTryNote* tnlimit = tn + trynotes()->length;
4418     for (; tn < tnlimit; tn++) {
4419         if (tn->kind == JSTRY_FOR_IN || tn->kind == JSTRY_LOOP)
4420             return true;
4421     }
4422     return false;
4423 }
4424 
4425 bool
mayReadFrameArgsDirectly()4426 JSScript::mayReadFrameArgsDirectly()
4427 {
4428     return argumentsHasVarBinding() || (function_ && function_->hasRest());
4429 }
4430 
4431 static inline void
LazyScriptHash(uint32_t lineno,uint32_t column,uint32_t begin,uint32_t end,HashNumber hashes[3])4432 LazyScriptHash(uint32_t lineno, uint32_t column, uint32_t begin, uint32_t end,
4433                HashNumber hashes[3])
4434 {
4435     HashNumber hash = lineno;
4436     hash = RotateLeft(hash, 4) ^ column;
4437     hash = RotateLeft(hash, 4) ^ begin;
4438     hash = RotateLeft(hash, 4) ^ end;
4439 
4440     hashes[0] = hash;
4441     hashes[1] = RotateLeft(hashes[0], 4) ^ begin;
4442     hashes[2] = RotateLeft(hashes[1], 4) ^ end;
4443 }
4444 
4445 void
hash(const Lookup & lookup,HashNumber hashes[3])4446 LazyScriptHashPolicy::hash(const Lookup& lookup, HashNumber hashes[3])
4447 {
4448     LazyScript* lazy = lookup.lazy;
4449     LazyScriptHash(lazy->lineno(), lazy->column(), lazy->begin(), lazy->end(), hashes);
4450 }
4451 
4452 void
hash(JSScript * script,HashNumber hashes[3])4453 LazyScriptHashPolicy::hash(JSScript* script, HashNumber hashes[3])
4454 {
4455     LazyScriptHash(script->lineno(), script->column(), script->sourceStart(), script->sourceEnd(), hashes);
4456 }
4457 
4458 bool
match(JSScript * script,const Lookup & lookup)4459 LazyScriptHashPolicy::match(JSScript* script, const Lookup& lookup)
4460 {
4461     JSContext* cx = lookup.cx;
4462     LazyScript* lazy = lookup.lazy;
4463 
4464     // To be a match, the script and lazy script need to have the same line
4465     // and column and to be at the same position within their respective
4466     // source blobs, and to have the same source contents and version.
4467     //
4468     // While the surrounding code in the source may differ, this is
4469     // sufficient to ensure that compiling the lazy script will yield an
4470     // identical result to compiling the original script.
4471     //
4472     // Note that the filenames and origin principals of the lazy script and
4473     // original script can differ. If there is a match, these will be fixed
4474     // up in the resulting clone by the caller.
4475 
4476     if (script->lineno() != lazy->lineno() ||
4477         script->column() != lazy->column() ||
4478         script->getVersion() != lazy->version() ||
4479         script->sourceStart() != lazy->begin() ||
4480         script->sourceEnd() != lazy->end())
4481     {
4482         return false;
4483     }
4484 
4485     UncompressedSourceCache::AutoHoldEntry holder;
4486 
4487     const char16_t* scriptChars = script->scriptSource()->chars(cx, holder);
4488     if (!scriptChars)
4489         return false;
4490 
4491     const char16_t* lazyChars = lazy->scriptSource()->chars(cx, holder);
4492     if (!lazyChars)
4493         return false;
4494 
4495     size_t begin = script->sourceStart();
4496     size_t length = script->sourceEnd() - begin;
4497     return !memcmp(scriptChars + begin, lazyChars + begin, length);
4498 }
4499 
4500 void
holdScript(JS::HandleFunction fun)4501 JSScript::AutoDelazify::holdScript(JS::HandleFunction fun)
4502 {
4503     if (fun) {
4504         if (fun->compartment()->isSelfHosting) {
4505             // The self-hosting compartment is shared across runtimes, so we
4506             // can't use JSAutoCompartment: it could cause races. Functions in
4507             // the self-hosting compartment will never be lazy, so we can safely
4508             // assume we don't have to delazify.
4509             script_ = fun->nonLazyScript();
4510         } else {
4511             JSAutoCompartment ac(cx_, fun);
4512             script_ = fun->getOrCreateScript(cx_);
4513             if (script_) {
4514                 oldDoNotRelazify_ = script_->doNotRelazify_;
4515                 script_->setDoNotRelazify(true);
4516             }
4517         }
4518     }
4519 }
4520 
4521 void
dropScript()4522 JSScript::AutoDelazify::dropScript()
4523 {
4524     // Don't touch script_ if it's in the self-hosting compartment, see the
4525     // comment in holdScript.
4526     if (script_ && !script_->compartment()->isSelfHosting)
4527         script_->setDoNotRelazify(oldDoNotRelazify_);
4528     script_ = nullptr;
4529 }
4530 
4531 JS::ubi::Node::Size
size(mozilla::MallocSizeOf mallocSizeOf) const4532 JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
4533 {
4534     Size size = Arena::thingSize(get().asTenured().getAllocKind());
4535 
4536     size += get().sizeOfData(mallocSizeOf);
4537     size += get().sizeOfTypeScript(mallocSizeOf);
4538 
4539     size_t baselineSize = 0;
4540     size_t baselineStubsSize = 0;
4541     jit::AddSizeOfBaselineData(&get(), mallocSizeOf, &baselineSize, &baselineStubsSize);
4542     size += baselineSize;
4543     size += baselineStubsSize;
4544 
4545     size += jit::SizeOfIonData(&get(), mallocSizeOf);
4546 
4547     MOZ_ASSERT(size > 0);
4548     return size;
4549 }
4550 
4551 const char*
scriptFilename() const4552 JS::ubi::Concrete<JSScript>::scriptFilename() const
4553 {
4554     return get().filename();
4555 }
4556 
4557 JS::ubi::Node::Size
size(mozilla::MallocSizeOf mallocSizeOf) const4558 JS::ubi::Concrete<js::LazyScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
4559 {
4560     Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
4561     size += get().sizeOfExcludingThis(mallocSizeOf);
4562     return size;
4563 }
4564 
4565 const char*
scriptFilename() const4566 JS::ubi::Concrete<js::LazyScript>::scriptFilename() const
4567 {
4568     auto sourceObject = get().sourceObject();
4569     if (!sourceObject)
4570         return nullptr;
4571 
4572     auto source = sourceObject->source();
4573     if (!source)
4574         return nullptr;
4575 
4576     return source->filename();
4577 }
4578