1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=8 sts=2 et sw=2 tw=80:
3 *
4 * Copyright 2021 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/WasmFrame.h"
20
21 #include "jsmath.h"
22 #include "jit/JitFrames.h"
23 #include "jit/MacroAssembler.h"
24 #include "js/friend/ErrorMessages.h" // JSMSG_*
25 #include "vm/EnvironmentObject.h"
26 #include "vm/JSObject.h"
27 #include "wasm/WasmBaselineCompile.h"
28 #include "wasm/WasmInstance.h"
29 #include "wasm/WasmStubs.h"
30 #include "wasm/WasmTlsData.h"
31 #include "wasm/WasmTypes.h"
32
33 #include "vm/JSObject-inl.h"
34 #include "vm/NativeObject-inl.h"
35
36 using namespace js;
37 using namespace js::jit;
38 using namespace js::wasm;
39
40 /* static */
from(Frame * fp)41 DebugFrame* DebugFrame::from(Frame* fp) {
42 MOZ_ASSERT(
43 GetNearestEffectiveTls(fp)->instance->code().metadata().debugEnabled);
44 auto* df =
45 reinterpret_cast<DebugFrame*>((uint8_t*)fp - DebugFrame::offsetOfFrame());
46 MOZ_ASSERT(GetNearestEffectiveTls(fp)->instance == df->instance());
47 return df;
48 }
49
alignmentStaticAsserts()50 void DebugFrame::alignmentStaticAsserts() {
51 // VS2017 doesn't consider offsetOfFrame() to be a constexpr, so we have
52 // to use offsetof directly. These asserts can't be at class-level
53 // because the type is incomplete.
54
55 static_assert(WasmStackAlignment >= Alignment,
56 "Aligned by ABI before pushing DebugFrame");
57 #ifndef JS_CODEGEN_NONE
58 static_assert((offsetof(DebugFrame, frame_) + sizeof(Frame)) % Alignment == 0,
59 "Aligned after pushing DebugFrame");
60 #endif
61 #ifdef JS_CODEGEN_ARM64
62 // This constraint may or may not be necessary. If you hit this because
63 // you've changed the frame size then feel free to remove it, but be extra
64 // aware of possible problems.
65 static_assert(sizeof(DebugFrame) % 16 == 0, "ARM64 SP alignment");
66 #endif
67 }
68
instance() const69 Instance* DebugFrame::instance() const {
70 return GetNearestEffectiveTls(&frame_)->instance;
71 }
72
global() const73 GlobalObject* DebugFrame::global() const {
74 return &instance()->object()->global();
75 }
76
hasGlobal(const GlobalObject * global) const77 bool DebugFrame::hasGlobal(const GlobalObject* global) const {
78 return global == &instance()->objectUnbarriered()->global();
79 }
80
environmentChain() const81 JSObject* DebugFrame::environmentChain() const {
82 return &global()->lexicalEnvironment();
83 }
84
getLocal(uint32_t localIndex,MutableHandleValue vp)85 bool DebugFrame::getLocal(uint32_t localIndex, MutableHandleValue vp) {
86 ValTypeVector locals;
87 size_t argsLength;
88 StackResults stackResults;
89 if (!instance()->debug().debugGetLocalTypes(funcIndex(), &locals, &argsLength,
90 &stackResults)) {
91 return false;
92 }
93
94 ValTypeVector args;
95 MOZ_ASSERT(argsLength <= locals.length());
96 if (!args.append(locals.begin(), argsLength)) {
97 return false;
98 }
99 ArgTypeVector abiArgs(args, stackResults);
100
101 BaseLocalIter iter(locals, abiArgs, /* debugEnabled = */ true);
102 while (!iter.done() && iter.index() < localIndex) {
103 iter++;
104 }
105 MOZ_ALWAYS_TRUE(!iter.done());
106
107 uint8_t* frame = static_cast<uint8_t*>((void*)this) + offsetOfFrame();
108 void* dataPtr = frame - iter.frameOffset();
109 switch (iter.mirType()) {
110 case jit::MIRType::Int32:
111 vp.set(Int32Value(*static_cast<int32_t*>(dataPtr)));
112 break;
113 case jit::MIRType::Int64:
114 // Just display as a Number; it's ok if we lose some precision
115 vp.set(NumberValue((double)*static_cast<int64_t*>(dataPtr)));
116 break;
117 case jit::MIRType::Float32:
118 vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<float*>(dataPtr))));
119 break;
120 case jit::MIRType::Double:
121 vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<double*>(dataPtr))));
122 break;
123 case jit::MIRType::RefOrNull:
124 vp.set(ObjectOrNullValue(*(JSObject**)dataPtr));
125 break;
126 #ifdef ENABLE_WASM_SIMD
127 case jit::MIRType::Simd128:
128 vp.set(NumberValue(0));
129 break;
130 #endif
131 default:
132 MOZ_CRASH("local type");
133 }
134 return true;
135 }
136
updateReturnJSValue(JSContext * cx)137 bool DebugFrame::updateReturnJSValue(JSContext* cx) {
138 MutableHandleValue rval =
139 MutableHandleValue::fromMarkedLocation(&cachedReturnJSValue_);
140 rval.setUndefined();
141 flags_.hasCachedReturnJSValue = true;
142 ResultType resultType = instance()->debug().debugGetResultType(funcIndex());
143 Maybe<char*> stackResultsLoc;
144 if (ABIResultIter::HasStackResults(resultType)) {
145 stackResultsLoc = Some(static_cast<char*>(stackResultsPointer_));
146 }
147 DebugCodegen(DebugChannel::Function,
148 "wasm-function[%d] updateReturnJSValue [", funcIndex());
149 bool ok =
150 ResultsToJSValue(cx, resultType, registerResults_, stackResultsLoc, rval);
151 DebugCodegen(DebugChannel::Function, "]\n");
152 return ok;
153 }
154
returnValue() const155 HandleValue DebugFrame::returnValue() const {
156 MOZ_ASSERT(flags_.hasCachedReturnJSValue);
157 return HandleValue::fromMarkedLocation(&cachedReturnJSValue_);
158 }
159
clearReturnJSValue()160 void DebugFrame::clearReturnJSValue() {
161 flags_.hasCachedReturnJSValue = true;
162 cachedReturnJSValue_.setUndefined();
163 }
164
observe(JSContext * cx)165 void DebugFrame::observe(JSContext* cx) {
166 if (!flags_.observing) {
167 instance()->debug().adjustEnterAndLeaveFrameTrapsState(
168 cx, /* enabled = */ true);
169 flags_.observing = true;
170 }
171 }
172
leave(JSContext * cx)173 void DebugFrame::leave(JSContext* cx) {
174 if (flags_.observing) {
175 instance()->debug().adjustEnterAndLeaveFrameTrapsState(
176 cx, /* enabled = */ false);
177 flags_.observing = false;
178 }
179 }
180