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