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, ®exp))
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(¬e->index) ||
1186 !xdr->codeUint32(¬e->start) ||
1187 !xdr->codeUint32(¬e->length) ||
1188 !xdr->codeUint32(¬e->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(®exps[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