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