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 : *
4 : * Copyright 2016 Mozilla Foundation
5 : *
6 : * Licensed under the Apache License, Version 2.0 (the "License");
7 : * you may not use this file except in compliance with the License.
8 : * You may obtain a copy of the License at
9 : *
10 : * http://www.apache.org/licenses/LICENSE-2.0
11 : *
12 : * Unless required by applicable law or agreed to in writing, software
13 : * distributed under the License is distributed on an "AS IS" BASIS,
14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 : * See the License for the specific language governing permissions and
16 : * limitations under the License.
17 : */
18 :
19 : #include "wasm/WasmDebug.h"
20 :
21 : #include "mozilla/BinarySearch.h"
22 :
23 : #include "ds/Sort.h"
24 : #include "jit/ExecutableAllocator.h"
25 : #include "jit/MacroAssembler.h"
26 : #include "vm/Debugger.h"
27 : #include "vm/StringBuffer.h"
28 : #include "wasm/WasmBinaryToText.h"
29 : #include "wasm/WasmValidate.h"
30 :
31 : using namespace js;
32 : using namespace js::jit;
33 : using namespace js::wasm;
34 :
35 : using mozilla::BinarySearchIf;
36 :
37 : bool
38 0 : GeneratedSourceMap::searchLineByOffset(JSContext* cx, uint32_t offset, size_t* exprlocIndex)
39 : {
40 0 : MOZ_ASSERT(!exprlocs_.empty());
41 0 : size_t exprlocsLength = exprlocs_.length();
42 :
43 : // Lazily build sorted array for fast log(n) lookup.
44 0 : if (!sortedByOffsetExprLocIndices_) {
45 0 : ExprLocIndexVector scratch;
46 0 : auto indices = MakeUnique<ExprLocIndexVector>();
47 0 : if (!indices || !indices->resize(exprlocsLength) || !scratch.resize(exprlocsLength)) {
48 0 : ReportOutOfMemory(cx);
49 0 : return false;
50 : }
51 0 : sortedByOffsetExprLocIndices_ = Move(indices);
52 :
53 0 : for (size_t i = 0; i < exprlocsLength; i++)
54 0 : (*sortedByOffsetExprLocIndices_)[i] = i;
55 :
56 0 : auto compareExprLocViaIndex = [&](uint32_t i, uint32_t j, bool* lessOrEqualp) -> bool {
57 0 : *lessOrEqualp = exprlocs_[i].offset <= exprlocs_[j].offset;
58 0 : return true;
59 0 : };
60 0 : MOZ_ALWAYS_TRUE(MergeSort(sortedByOffsetExprLocIndices_->begin(), exprlocsLength,
61 : scratch.begin(), compareExprLocViaIndex));
62 : }
63 :
64 : // Allowing non-exact search and if BinarySearchIf returns out-of-bound
65 : // index, moving the index to the last index.
66 0 : auto lookupFn = [&](uint32_t i) -> int {
67 0 : const ExprLoc& loc = exprlocs_[i];
68 0 : return offset == loc.offset ? 0 : offset < loc.offset ? -1 : 1;
69 0 : };
70 : size_t match;
71 0 : Unused << BinarySearchIf(sortedByOffsetExprLocIndices_->begin(), 0, exprlocsLength, lookupFn, &match);
72 0 : if (match >= exprlocsLength)
73 0 : match = exprlocsLength - 1;
74 0 : *exprlocIndex = (*sortedByOffsetExprLocIndices_)[match];
75 0 : return true;
76 : }
77 :
78 : size_t
79 0 : GeneratedSourceMap::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
80 : {
81 0 : size_t size = exprlocs_.sizeOfExcludingThis(mallocSizeOf);
82 0 : if (sortedByOffsetExprLocIndices_)
83 0 : size += sortedByOffsetExprLocIndices_->sizeOfIncludingThis(mallocSizeOf);
84 0 : return size;
85 : }
86 :
87 0 : DebugState::DebugState(SharedCode code,
88 : const ShareableBytes* maybeBytecode,
89 0 : bool binarySource)
90 0 : : code_(Move(code)),
91 : maybeBytecode_(maybeBytecode),
92 : binarySource_(binarySource),
93 0 : enterAndLeaveFrameTrapsCounter_(0)
94 : {
95 0 : MOZ_ASSERT_IF(debugEnabled(), maybeBytecode);
96 0 : }
97 :
98 : const char enabledMessage[] =
99 : "Restart with developer tools open to view WebAssembly source";
100 :
101 : const char tooBigMessage[] =
102 : "Unfortunately, this WebAssembly module is too big to view as text.\n"
103 : "We are working hard to remove this limitation.";
104 :
105 : const char notGeneratedMessage[] =
106 : "WebAssembly text generation was disabled.";
107 :
108 : static const unsigned TooBig = 1000000;
109 :
110 : static const uint32_t DefaultBinarySourceColumnNumber = 1;
111 :
112 : static const CallSite*
113 0 : SlowCallSiteSearchByOffset(const MetadataTier& metadata, uint32_t offset)
114 : {
115 0 : for (const CallSite& callSite : metadata.callSites) {
116 0 : if (callSite.lineOrBytecode() == offset && callSite.kind() == CallSiteDesc::Breakpoint)
117 0 : return &callSite;
118 : }
119 0 : return nullptr;
120 : }
121 :
122 : JSString*
123 0 : DebugState::createText(JSContext* cx)
124 : {
125 0 : StringBuffer buffer(cx);
126 0 : if (!maybeBytecode_) {
127 0 : if (!buffer.append(enabledMessage))
128 0 : return nullptr;
129 :
130 0 : MOZ_ASSERT(!maybeSourceMap_);
131 0 : } else if (binarySource_) {
132 0 : if (!buffer.append(notGeneratedMessage))
133 0 : return nullptr;
134 0 : return buffer.finishString();
135 0 : } else if (maybeBytecode_->bytes.length() > TooBig) {
136 0 : if (!buffer.append(tooBigMessage))
137 0 : return nullptr;
138 :
139 0 : MOZ_ASSERT(!maybeSourceMap_);
140 : } else {
141 0 : const Bytes& bytes = maybeBytecode_->bytes;
142 0 : auto sourceMap = MakeUnique<GeneratedSourceMap>();
143 0 : if (!sourceMap) {
144 0 : ReportOutOfMemory(cx);
145 0 : return nullptr;
146 : }
147 0 : maybeSourceMap_ = Move(sourceMap);
148 :
149 0 : if (!BinaryToText(cx, bytes.begin(), bytes.length(), buffer, maybeSourceMap_.get()))
150 0 : return nullptr;
151 :
152 : #if DEBUG
153 : // Check that expression locations are sorted by line number.
154 0 : uint32_t lastLineno = 0;
155 0 : for (const ExprLoc& loc : maybeSourceMap_->exprlocs()) {
156 0 : MOZ_ASSERT(lastLineno <= loc.lineno);
157 0 : lastLineno = loc.lineno;
158 : }
159 : #endif
160 : }
161 :
162 0 : return buffer.finishString();
163 : }
164 :
165 : bool
166 0 : DebugState::ensureSourceMap(JSContext* cx)
167 : {
168 0 : if (maybeSourceMap_ || !maybeBytecode_)
169 0 : return true;
170 :
171 : // We just need to cache maybeSourceMap_, ignoring the text result.
172 0 : return createText(cx);
173 : }
174 :
175 : struct LineComparator
176 : {
177 : const uint32_t lineno;
178 0 : explicit LineComparator(uint32_t lineno) : lineno(lineno) {}
179 :
180 0 : int operator()(const ExprLoc& loc) const {
181 0 : return lineno == loc.lineno ? 0 : lineno < loc.lineno ? -1 : 1;
182 : }
183 : };
184 :
185 : bool
186 0 : DebugState::getLineOffsets(JSContext* cx, size_t lineno, Vector<uint32_t>* offsets)
187 : {
188 0 : if (!debugEnabled())
189 0 : return true;
190 :
191 0 : if (binarySource_) {
192 0 : const CallSite* callsite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), lineno);
193 0 : if (callsite && !offsets->append(lineno))
194 0 : return false;
195 0 : return true;
196 : }
197 :
198 0 : if (!ensureSourceMap(cx))
199 0 : return false;
200 :
201 0 : if (!maybeSourceMap_)
202 0 : return true; // no source text available, keep offsets empty.
203 :
204 0 : ExprLocVector& exprlocs = maybeSourceMap_->exprlocs();
205 :
206 : // Binary search for the expression with the specified line number and
207 : // rewind to the first expression, if more than one expression on the same line.
208 : size_t match;
209 0 : if (!BinarySearchIf(exprlocs, 0, exprlocs.length(), LineComparator(lineno), &match))
210 0 : return true;
211 :
212 0 : while (match > 0 && exprlocs[match - 1].lineno == lineno)
213 0 : match--;
214 :
215 : // Return all expression offsets that were printed on the specified line.
216 0 : for (size_t i = match; i < exprlocs.length() && exprlocs[i].lineno == lineno; i++) {
217 0 : if (!offsets->append(exprlocs[i].offset))
218 0 : return false;
219 : }
220 :
221 0 : return true;
222 : }
223 :
224 : bool
225 0 : DebugState::getAllColumnOffsets(JSContext* cx, Vector<ExprLoc>* offsets)
226 : {
227 0 : if (!metadata().debugEnabled)
228 0 : return true;
229 :
230 0 : if (binarySource_) {
231 0 : for (const CallSite& callSite : metadata(Tier::Debug).callSites) {
232 0 : if (callSite.kind() != CallSite::Breakpoint)
233 0 : continue;
234 0 : uint32_t offset = callSite.lineOrBytecode();
235 0 : if (!offsets->emplaceBack(offset, DefaultBinarySourceColumnNumber, offset))
236 0 : return false;
237 : }
238 0 : return true;
239 : }
240 :
241 0 : if (!ensureSourceMap(cx))
242 0 : return false;
243 :
244 0 : if (!maybeSourceMap_)
245 0 : return true; // no source text available, keep offsets empty.
246 :
247 0 : return offsets->appendAll(maybeSourceMap_->exprlocs());
248 : }
249 :
250 : bool
251 0 : DebugState::getOffsetLocation(JSContext* cx, uint32_t offset, bool* found, size_t* lineno, size_t* column)
252 : {
253 0 : *found = false;
254 0 : if (!debugEnabled())
255 0 : return true;
256 :
257 0 : if (binarySource_) {
258 0 : if (!SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset))
259 0 : return true; // offset was not found
260 0 : *found = true;
261 0 : *lineno = offset;
262 0 : *column = DefaultBinarySourceColumnNumber;
263 0 : return true;
264 : }
265 :
266 0 : if (!ensureSourceMap(cx))
267 0 : return false;
268 :
269 0 : if (!maybeSourceMap_ || maybeSourceMap_->exprlocs().empty())
270 0 : return true; // no source text available
271 :
272 : size_t foundAt;
273 0 : if (!maybeSourceMap_->searchLineByOffset(cx, offset, &foundAt))
274 0 : return false;
275 :
276 0 : const ExprLoc& loc = maybeSourceMap_->exprlocs()[foundAt];
277 0 : *found = true;
278 0 : *lineno = loc.lineno;
279 0 : *column = loc.column;
280 0 : return true;
281 : }
282 :
283 : bool
284 0 : DebugState::totalSourceLines(JSContext* cx, uint32_t* count)
285 : {
286 0 : *count = 0;
287 0 : if (!debugEnabled())
288 0 : return true;
289 :
290 0 : if (binarySource_) {
291 0 : if (maybeBytecode_)
292 0 : *count = maybeBytecode_->length();
293 0 : return true;
294 : }
295 :
296 0 : if (!ensureSourceMap(cx))
297 0 : return false;
298 :
299 0 : if (maybeSourceMap_)
300 0 : *count = maybeSourceMap_->totalLines();
301 0 : return true;
302 : }
303 :
304 : bool
305 0 : DebugState::stepModeEnabled(uint32_t funcIndex) const
306 : {
307 0 : return stepModeCounters_.initialized() && stepModeCounters_.lookup(funcIndex);
308 : }
309 :
310 : bool
311 0 : DebugState::incrementStepModeCount(JSContext* cx, uint32_t funcIndex)
312 : {
313 0 : MOZ_ASSERT(debugEnabled());
314 0 : const CodeRange& codeRange = codeRanges(Tier::Debug)[debugFuncToCodeRangeIndex(funcIndex)];
315 0 : MOZ_ASSERT(codeRange.isFunction());
316 :
317 0 : if (!stepModeCounters_.initialized() && !stepModeCounters_.init()) {
318 0 : ReportOutOfMemory(cx);
319 0 : return false;
320 : }
321 :
322 0 : StepModeCounters::AddPtr p = stepModeCounters_.lookupForAdd(funcIndex);
323 0 : if (p) {
324 0 : MOZ_ASSERT(p->value() > 0);
325 0 : p->value()++;
326 0 : return true;
327 : }
328 0 : if (!stepModeCounters_.add(p, funcIndex, 1)) {
329 0 : ReportOutOfMemory(cx);
330 0 : return false;
331 : }
332 :
333 0 : AutoWritableJitCode awjc(cx->runtime(), code_->segment(Tier::Debug).base() + codeRange.begin(),
334 0 : codeRange.end() - codeRange.begin());
335 0 : AutoFlushICache afc("Code::incrementStepModeCount");
336 :
337 0 : for (const CallSite& callSite : callSites(Tier::Debug)) {
338 0 : if (callSite.kind() != CallSite::Breakpoint)
339 0 : continue;
340 0 : uint32_t offset = callSite.returnAddressOffset();
341 0 : if (codeRange.begin() <= offset && offset <= codeRange.end())
342 0 : toggleDebugTrap(offset, true);
343 : }
344 0 : return true;
345 : }
346 :
347 : bool
348 0 : DebugState::decrementStepModeCount(FreeOp* fop, uint32_t funcIndex)
349 : {
350 0 : MOZ_ASSERT(debugEnabled());
351 0 : const CodeRange& codeRange = codeRanges(Tier::Debug)[debugFuncToCodeRangeIndex(funcIndex)];
352 0 : MOZ_ASSERT(codeRange.isFunction());
353 :
354 0 : MOZ_ASSERT(stepModeCounters_.initialized() && !stepModeCounters_.empty());
355 0 : StepModeCounters::Ptr p = stepModeCounters_.lookup(funcIndex);
356 0 : MOZ_ASSERT(p);
357 0 : if (--p->value())
358 0 : return true;
359 :
360 0 : stepModeCounters_.remove(p);
361 :
362 0 : AutoWritableJitCode awjc(fop->runtime(), code_->segment(Tier::Debug).base() + codeRange.begin(),
363 0 : codeRange.end() - codeRange.begin());
364 0 : AutoFlushICache afc("Code::decrementStepModeCount");
365 :
366 0 : for (const CallSite& callSite : callSites(Tier::Debug)) {
367 0 : if (callSite.kind() != CallSite::Breakpoint)
368 0 : continue;
369 0 : uint32_t offset = callSite.returnAddressOffset();
370 0 : if (codeRange.begin() <= offset && offset <= codeRange.end()) {
371 0 : bool enabled = breakpointSites_.initialized() && breakpointSites_.has(offset);
372 0 : toggleDebugTrap(offset, enabled);
373 : }
374 : }
375 0 : return true;
376 : }
377 :
378 : bool
379 0 : DebugState::hasBreakpointTrapAtOffset(uint32_t offset)
380 : {
381 0 : if (!debugEnabled())
382 0 : return false;
383 0 : return SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
384 : }
385 :
386 : void
387 0 : DebugState::toggleBreakpointTrap(JSRuntime* rt, uint32_t offset, bool enabled)
388 : {
389 0 : MOZ_ASSERT(debugEnabled());
390 0 : const CallSite* callSite = SlowCallSiteSearchByOffset(metadata(Tier::Debug), offset);
391 0 : if (!callSite)
392 0 : return;
393 0 : size_t debugTrapOffset = callSite->returnAddressOffset();
394 :
395 0 : const CodeSegment& codeSegment = code_->segment(Tier::Debug);
396 0 : const CodeRange* codeRange = code_->lookupRange(codeSegment.base() + debugTrapOffset);
397 0 : MOZ_ASSERT(codeRange && codeRange->isFunction());
398 :
399 0 : if (stepModeCounters_.initialized() && stepModeCounters_.lookup(codeRange->funcIndex()))
400 0 : return; // no need to toggle when step mode is enabled
401 :
402 0 : AutoWritableJitCode awjc(rt, codeSegment.base(), codeSegment.length());
403 0 : AutoFlushICache afc("Code::toggleBreakpointTrap");
404 0 : AutoFlushICache::setRange(uintptr_t(codeSegment.base()), codeSegment.length());
405 0 : toggleDebugTrap(debugTrapOffset, enabled);
406 : }
407 :
408 : WasmBreakpointSite*
409 0 : DebugState::getOrCreateBreakpointSite(JSContext* cx, uint32_t offset)
410 : {
411 : WasmBreakpointSite* site;
412 0 : if (!breakpointSites_.initialized() && !breakpointSites_.init()) {
413 0 : ReportOutOfMemory(cx);
414 0 : return nullptr;
415 : }
416 :
417 0 : WasmBreakpointSiteMap::AddPtr p = breakpointSites_.lookupForAdd(offset);
418 0 : if (!p) {
419 0 : site = cx->runtime()->new_<WasmBreakpointSite>(this, offset);
420 0 : if (!site || !breakpointSites_.add(p, offset, site)) {
421 0 : js_delete(site);
422 0 : ReportOutOfMemory(cx);
423 0 : return nullptr;
424 : }
425 : } else {
426 0 : site = p->value();
427 : }
428 0 : return site;
429 : }
430 :
431 : bool
432 0 : DebugState::hasBreakpointSite(uint32_t offset)
433 : {
434 0 : return breakpointSites_.initialized() && breakpointSites_.has(offset);
435 : }
436 :
437 : void
438 0 : DebugState::destroyBreakpointSite(FreeOp* fop, uint32_t offset)
439 : {
440 0 : MOZ_ASSERT(breakpointSites_.initialized());
441 0 : WasmBreakpointSiteMap::Ptr p = breakpointSites_.lookup(offset);
442 0 : MOZ_ASSERT(p);
443 0 : fop->delete_(p->value());
444 0 : breakpointSites_.remove(p);
445 0 : }
446 :
447 : bool
448 0 : DebugState::clearBreakpointsIn(JSContext* cx, WasmInstanceObject* instance, js::Debugger* dbg, JSObject* handler)
449 : {
450 0 : MOZ_ASSERT(instance);
451 0 : if (!breakpointSites_.initialized())
452 0 : return true;
453 :
454 : // Make copy of all sites list, so breakpointSites_ can be modified by
455 : // destroyBreakpointSite calls.
456 0 : Vector<WasmBreakpointSite*> sites(cx);
457 0 : if (!sites.resize(breakpointSites_.count()))
458 0 : return false;
459 0 : size_t i = 0;
460 0 : for (WasmBreakpointSiteMap::Range r = breakpointSites_.all(); !r.empty(); r.popFront())
461 0 : sites[i++] = r.front().value();
462 :
463 0 : for (WasmBreakpointSite* site : sites) {
464 : Breakpoint* nextbp;
465 0 : for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
466 0 : nextbp = bp->nextInSite();
467 0 : if (bp->asWasm()->wasmInstance == instance &&
468 0 : (!dbg || bp->debugger == dbg) &&
469 0 : (!handler || bp->getHandler() == handler))
470 : {
471 0 : bp->destroy(cx->runtime()->defaultFreeOp());
472 : }
473 : }
474 : }
475 0 : return true;
476 : }
477 :
478 : void
479 0 : DebugState::toggleDebugTrap(uint32_t offset, bool enabled)
480 : {
481 0 : MOZ_ASSERT(offset);
482 0 : uint8_t* trap = code_->segment(Tier::Debug).base() + offset;
483 0 : const Uint32Vector& farJumpOffsets = metadata(Tier::Debug).debugTrapFarJumpOffsets;
484 0 : if (enabled) {
485 0 : MOZ_ASSERT(farJumpOffsets.length() > 0);
486 0 : size_t i = 0;
487 0 : while (i < farJumpOffsets.length() && offset < farJumpOffsets[i])
488 0 : i++;
489 0 : if (i >= farJumpOffsets.length() ||
490 0 : (i > 0 && offset - farJumpOffsets[i - 1] < farJumpOffsets[i] - offset))
491 0 : i--;
492 0 : uint8_t* farJump = code_->segment(Tier::Debug).base() + farJumpOffsets[i];
493 0 : MacroAssembler::patchNopToCall(trap, farJump);
494 : } else {
495 0 : MacroAssembler::patchCallToNop(trap);
496 : }
497 0 : }
498 :
499 : void
500 0 : DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx, bool enabled)
501 : {
502 0 : MOZ_ASSERT(debugEnabled());
503 0 : MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);
504 :
505 0 : bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
506 0 : if (enabled)
507 0 : ++enterAndLeaveFrameTrapsCounter_;
508 : else
509 0 : --enterAndLeaveFrameTrapsCounter_;
510 0 : bool stillEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
511 0 : if (wasEnabled == stillEnabled)
512 0 : return;
513 :
514 0 : const CodeSegment& codeSegment = code_->segment(Tier::Debug);
515 0 : AutoWritableJitCode awjc(cx->runtime(), codeSegment.base(), codeSegment.length());
516 0 : AutoFlushICache afc("Code::adjustEnterAndLeaveFrameTrapsState");
517 0 : AutoFlushICache::setRange(uintptr_t(codeSegment.base()), codeSegment.length());
518 0 : for (const CallSite& callSite : callSites(Tier::Debug)) {
519 0 : if (callSite.kind() != CallSite::EnterFrame && callSite.kind() != CallSite::LeaveFrame)
520 0 : continue;
521 0 : toggleDebugTrap(callSite.returnAddressOffset(), stillEnabled);
522 : }
523 : }
524 :
525 : bool
526 0 : DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals, size_t* argsLength)
527 : {
528 0 : MOZ_ASSERT(debugEnabled());
529 :
530 0 : const ValTypeVector& args = metadata().debugFuncArgTypes[funcIndex];
531 0 : *argsLength = args.length();
532 0 : if (!locals->appendAll(args))
533 0 : return false;
534 :
535 : // Decode local var types from wasm binary function body.
536 0 : const CodeRange& range = codeRanges(Tier::Debug)[debugFuncToCodeRangeIndex(funcIndex)];
537 : // In wasm, the Code points to the function start via funcLineOrBytecode.
538 0 : MOZ_ASSERT(!metadata().isAsmJS() && maybeBytecode_);
539 0 : size_t offsetInModule = range.funcLineOrBytecode();
540 0 : Decoder d(maybeBytecode_->begin() + offsetInModule, maybeBytecode_->end(),
541 0 : offsetInModule, /* error = */ nullptr);
542 0 : return DecodeLocalEntries(d, metadata().kind, locals);
543 : }
544 :
545 : ExprType
546 0 : DebugState::debugGetResultType(uint32_t funcIndex)
547 : {
548 0 : MOZ_ASSERT(debugEnabled());
549 0 : return metadata().debugFuncReturnTypes[funcIndex];
550 : }
551 :
552 : JSString*
553 0 : DebugState::debugDisplayURL(JSContext* cx) const
554 : {
555 : // Build wasm module URL from following parts:
556 : // - "wasm:" as protocol;
557 : // - URI encoded filename from metadata (if can be encoded), plus ":";
558 : // - 64-bit hash of the module bytes (as hex dump).
559 0 : js::StringBuffer result(cx);
560 0 : if (!result.append("wasm:"))
561 0 : return nullptr;
562 0 : if (const char* filename = metadata().filename.get()) {
563 0 : js::StringBuffer filenamePrefix(cx);
564 : // EncodeURI returns false due to invalid chars or OOM -- fail only
565 : // during OOM.
566 0 : if (!EncodeURI(cx, filenamePrefix, filename, strlen(filename))) {
567 0 : if (!cx->isExceptionPending())
568 0 : return nullptr;
569 0 : cx->clearPendingException(); // ignore invalid URI
570 0 : } else if (!result.append(filenamePrefix.finishString()) || !result.append(":")) {
571 0 : return nullptr;
572 : }
573 : }
574 :
575 0 : const ModuleHash& hash = metadata().hash;
576 0 : for (size_t i = 0; i < sizeof(ModuleHash); i++) {
577 0 : char digit1 = hash[i] / 16, digit2 = hash[i] % 16;
578 0 : if (!result.append((char)(digit1 < 10 ? digit1 + '0' : digit1 + 'a' - 10)))
579 0 : return nullptr;
580 0 : if (!result.append((char)(digit2 < 10 ? digit2 + '0' : digit2 + 'a' - 10)))
581 0 : return nullptr;
582 : }
583 0 : return result.finishString();
584 : }
585 :
586 : bool
587 0 : DebugState::getSourceMappingURL(JSContext* cx, MutableHandleString result) const
588 : {
589 0 : result.set(nullptr);
590 0 : if (!maybeBytecode_)
591 0 : return true;
592 :
593 0 : for (const CustomSection& customSection : metadata().customSections) {
594 0 : const NameInBytecode& sectionName = customSection.name;
595 0 : if (strlen(SourceMappingURLSectionName) != sectionName.length ||
596 0 : memcmp(SourceMappingURLSectionName, maybeBytecode_->begin() + sectionName.offset,
597 0 : sectionName.length) != 0)
598 : {
599 0 : continue;
600 : }
601 :
602 : // Parse found "SourceMappingURL" custom section.
603 0 : Decoder d(maybeBytecode_->begin() + customSection.offset,
604 0 : maybeBytecode_->begin() + customSection.offset + customSection.length,
605 0 : customSection.offset,
606 0 : /* error = */ nullptr);
607 : uint32_t nchars;
608 0 : if (!d.readVarU32(&nchars))
609 0 : return true; // ignoring invalid section data
610 : const uint8_t* chars;
611 0 : if (!d.readBytes(nchars, &chars) || d.currentPosition() != d.end())
612 0 : return true; // ignoring invalid section data
613 :
614 0 : UTF8Chars utf8Chars(reinterpret_cast<const char*>(chars), nchars);
615 0 : JSString* str = JS_NewStringCopyUTF8N(cx, utf8Chars);
616 0 : if (!str)
617 0 : return false;
618 0 : result.set(str);
619 0 : break;
620 : }
621 0 : return true;
622 : }
623 :
624 : void
625 0 : DebugState::addSizeOfMisc(MallocSizeOf mallocSizeOf,
626 : Metadata::SeenSet* seenMetadata,
627 : ShareableBytes::SeenSet* seenBytes,
628 : Code::SeenSet* seenCode,
629 : size_t* code,
630 : size_t* data) const
631 : {
632 0 : code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenMetadata, seenCode, code, data);
633 0 : if (maybeSourceMap_)
634 0 : *data += maybeSourceMap_->sizeOfExcludingThis(mallocSizeOf);
635 0 : if (maybeBytecode_)
636 0 : *data += maybeBytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
637 0 : }
|