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