1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "src/arguments-inl.h"
6 #include "src/compiler/wasm-compiler.h"
7 #include "src/conversions.h"
8 #include "src/debug/debug.h"
9 #include "src/frame-constants.h"
10 #include "src/heap/factory.h"
11 #include "src/objects-inl.h"
12 #include "src/objects/frame-array-inl.h"
13 #include "src/runtime/runtime-utils.h"
14 #include "src/trap-handler/trap-handler.h"
15 #include "src/v8memory.h"
16 #include "src/wasm/module-compiler.h"
17 #include "src/wasm/wasm-code-manager.h"
18 #include "src/wasm/wasm-constants.h"
19 #include "src/wasm/wasm-engine.h"
20 #include "src/wasm/wasm-objects.h"
21 
22 namespace v8 {
23 namespace internal {
24 
25 namespace {
26 
GetWasmInstanceOnStackTop(Isolate * isolate)27 WasmInstanceObject* GetWasmInstanceOnStackTop(Isolate* isolate) {
28   StackFrameIterator it(isolate, isolate->thread_local_top());
29   // On top: C entry stub.
30   DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
31   it.Advance();
32   // Next: the wasm (compiled or interpreted) frame.
33   WasmInstanceObject* result = nullptr;
34   if (it.frame()->is_wasm_compiled()) {
35     result = WasmCompiledFrame::cast(it.frame())->wasm_instance();
36   } else {
37     DCHECK(it.frame()->is_wasm_interpreter_entry());
38     result = WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance();
39   }
40   return result;
41 }
42 
GetNativeContextFromWasmInstanceOnStackTop(Isolate * isolate)43 Context* GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
44   return GetWasmInstanceOnStackTop(isolate)->native_context();
45 }
46 
47 class ClearThreadInWasmScope {
48  public:
ClearThreadInWasmScope(bool coming_from_wasm)49   explicit ClearThreadInWasmScope(bool coming_from_wasm)
50       : coming_from_wasm_(coming_from_wasm) {
51     DCHECK_EQ(trap_handler::IsTrapHandlerEnabled() && coming_from_wasm,
52               trap_handler::IsThreadInWasm());
53     if (coming_from_wasm) trap_handler::ClearThreadInWasm();
54   }
~ClearThreadInWasmScope()55   ~ClearThreadInWasmScope() {
56     DCHECK(!trap_handler::IsThreadInWasm());
57     if (coming_from_wasm_) trap_handler::SetThreadInWasm();
58   }
59 
60  private:
61   const bool coming_from_wasm_;
62 };
63 
64 }  // namespace
65 
RUNTIME_FUNCTION(Runtime_WasmGrowMemory)66 RUNTIME_FUNCTION(Runtime_WasmGrowMemory) {
67   HandleScope scope(isolate);
68   DCHECK_EQ(2, args.length());
69   CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
70   // {delta_pages} is checked to be a positive smi in the WasmGrowMemory builtin
71   // which calls this runtime function.
72   CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
73 
74   // This runtime function is always being called from wasm code.
75   ClearThreadInWasmScope flag_scope(true);
76 
77   // Set the current isolate's context.
78   DCHECK_NULL(isolate->context());
79   isolate->set_context(instance->native_context());
80 
81   int ret = WasmMemoryObject::Grow(
82       isolate, handle(instance->memory_object(), isolate), delta_pages);
83   // The WasmGrowMemory builtin which calls this runtime function expects us to
84   // always return a Smi.
85   return Smi::FromInt(ret);
86 }
87 
RUNTIME_FUNCTION(Runtime_ThrowWasmError)88 RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
89   DCHECK_EQ(1, args.length());
90   CONVERT_SMI_ARG_CHECKED(message_id, 0);
91   ClearThreadInWasmScope clear_wasm_flag(isolate->context() == nullptr);
92 
93   HandleScope scope(isolate);
94   DCHECK_NULL(isolate->context());
95   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
96   Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
97       static_cast<MessageTemplate::Template>(message_id));
98   return isolate->Throw(*error_obj);
99 }
100 
RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow)101 RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
102   SealHandleScope shs(isolate);
103   DCHECK_LE(0, args.length());
104   DCHECK_NULL(isolate->context());
105   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
106   return isolate->StackOverflow();
107 }
108 
RUNTIME_FUNCTION(Runtime_WasmThrowTypeError)109 RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
110   HandleScope scope(isolate);
111   DCHECK_EQ(0, args.length());
112   THROW_NEW_ERROR_RETURN_FAILURE(
113       isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
114 }
115 
RUNTIME_FUNCTION(Runtime_WasmThrowCreate)116 RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
117   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
118   HandleScope scope(isolate);
119   DCHECK_NULL(isolate->context());
120   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
121   DCHECK_EQ(2, args.length());
122   Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
123       static_cast<MessageTemplate::Template>(
124           MessageTemplate::kWasmExceptionError));
125   isolate->set_wasm_caught_exception(*exception);
126   CONVERT_ARG_HANDLE_CHECKED(Smi, id, 0);
127   CHECK(!JSReceiver::SetProperty(isolate, exception,
128                                  isolate->factory()->InternalizeUtf8String(
129                                      wasm::WasmException::kRuntimeIdStr),
130                                  id, LanguageMode::kStrict)
131              .is_null());
132   CONVERT_SMI_ARG_CHECKED(size, 1);
133   Handle<JSTypedArray> values =
134       isolate->factory()->NewJSTypedArray(ElementsKind::UINT16_ELEMENTS, size);
135   CHECK(!JSReceiver::SetProperty(isolate, exception,
136                                  isolate->factory()->InternalizeUtf8String(
137                                      wasm::WasmException::kRuntimeValuesStr),
138                                  values, LanguageMode::kStrict)
139              .is_null());
140   return ReadOnlyRoots(isolate).undefined_value();
141 }
142 
RUNTIME_FUNCTION(Runtime_WasmThrow)143 RUNTIME_FUNCTION(Runtime_WasmThrow) {
144   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
145   HandleScope scope(isolate);
146   DCHECK_NULL(isolate->context());
147   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
148   DCHECK_EQ(0, args.length());
149   Handle<Object> exception(isolate->get_wasm_caught_exception(), isolate);
150   CHECK(!exception.is_null());
151   isolate->clear_wasm_caught_exception();
152   return isolate->Throw(*exception);
153 }
154 
RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId)155 RUNTIME_FUNCTION(Runtime_WasmGetExceptionRuntimeId) {
156   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
157   HandleScope scope(isolate);
158   DCHECK_NULL(isolate->context());
159   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
160   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
161   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
162     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
163     Handle<Object> tag;
164     if (JSReceiver::GetProperty(isolate, exception,
165                                 isolate->factory()->InternalizeUtf8String(
166                                     wasm::WasmException::kRuntimeIdStr))
167             .ToHandle(&tag)) {
168       if (tag->IsSmi()) {
169         return *tag;
170       }
171     }
172   }
173   return Smi::FromInt(wasm::kInvalidExceptionTag);
174 }
175 
RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement)176 RUNTIME_FUNCTION(Runtime_WasmExceptionGetElement) {
177   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
178   HandleScope scope(isolate);
179   DCHECK_NULL(isolate->context());
180   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
181   DCHECK_EQ(1, args.length());
182   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
183   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
184     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
185     Handle<Object> values_obj;
186     if (JSReceiver::GetProperty(isolate, exception,
187                                 isolate->factory()->InternalizeUtf8String(
188                                     wasm::WasmException::kRuntimeValuesStr))
189             .ToHandle(&values_obj)) {
190       if (values_obj->IsJSTypedArray()) {
191         Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
192         CHECK_EQ(values->type(), kExternalUint16Array);
193         CONVERT_SMI_ARG_CHECKED(index, 0);
194         CHECK_LT(index, Smi::ToInt(values->length()));
195         auto* vals =
196             reinterpret_cast<uint16_t*>(values->GetBuffer()->backing_store());
197         return Smi::FromInt(vals[index]);
198       }
199     }
200   }
201   return Smi::FromInt(0);
202 }
203 
RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement)204 RUNTIME_FUNCTION(Runtime_WasmExceptionSetElement) {
205   // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
206   HandleScope scope(isolate);
207   DCHECK_EQ(2, args.length());
208   DCHECK_NULL(isolate->context());
209   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
210   Handle<Object> except_obj(isolate->get_wasm_caught_exception(), isolate);
211   if (!except_obj.is_null() && except_obj->IsJSReceiver()) {
212     Handle<JSReceiver> exception(JSReceiver::cast(*except_obj), isolate);
213     Handle<Object> values_obj;
214     if (JSReceiver::GetProperty(isolate, exception,
215                                 isolate->factory()->InternalizeUtf8String(
216                                     wasm::WasmException::kRuntimeValuesStr))
217             .ToHandle(&values_obj)) {
218       if (values_obj->IsJSTypedArray()) {
219         Handle<JSTypedArray> values = Handle<JSTypedArray>::cast(values_obj);
220         CHECK_EQ(values->type(), kExternalUint16Array);
221         CONVERT_SMI_ARG_CHECKED(index, 0);
222         CHECK_LT(index, Smi::ToInt(values->length()));
223         CONVERT_SMI_ARG_CHECKED(value, 1);
224         auto* vals =
225             reinterpret_cast<uint16_t*>(values->GetBuffer()->backing_store());
226         vals[index] = static_cast<uint16_t>(value);
227       }
228     }
229   }
230   return ReadOnlyRoots(isolate).undefined_value();
231 }
232 
RUNTIME_FUNCTION(Runtime_WasmRunInterpreter)233 RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
234   DCHECK_EQ(2, args.length());
235   HandleScope scope(isolate);
236   CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
237   CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
238   Handle<WasmInstanceObject> instance(GetWasmInstanceOnStackTop(isolate),
239                                       isolate);
240 
241   // The arg buffer is the raw pointer to the caller's stack. It looks like a
242   // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
243   // cast it back to the raw pointer.
244   CHECK(!arg_buffer_obj->IsHeapObject());
245   CHECK(arg_buffer_obj->IsSmi());
246   Address arg_buffer = reinterpret_cast<Address>(*arg_buffer_obj);
247 
248   ClearThreadInWasmScope wasm_flag(true);
249 
250   // Set the current isolate's context.
251   DCHECK_NULL(isolate->context());
252   isolate->set_context(instance->native_context());
253 
254   // Find the frame pointer of the interpreter entry.
255   Address frame_pointer = 0;
256   {
257     StackFrameIterator it(isolate, isolate->thread_local_top());
258     // On top: C entry stub.
259     DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
260     it.Advance();
261     // Next: the wasm interpreter entry.
262     DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
263     frame_pointer = it.frame()->fp();
264   }
265 
266   // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
267   // nor the {InterpreterHandle} have to exist, because interpretation might
268   // have been triggered by another Isolate sharing the same WasmEngine.
269   Handle<WasmDebugInfo> debug_info =
270       WasmInstanceObject::GetOrCreateDebugInfo(instance);
271   bool success = WasmDebugInfo::RunInterpreter(
272       isolate, debug_info, frame_pointer, func_index, arg_buffer);
273 
274   if (!success) {
275     DCHECK(isolate->has_pending_exception());
276     return ReadOnlyRoots(isolate).exception();
277   }
278   return ReadOnlyRoots(isolate).undefined_value();
279 }
280 
RUNTIME_FUNCTION(Runtime_WasmStackGuard)281 RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
282   SealHandleScope shs(isolate);
283   DCHECK_EQ(0, args.length());
284   DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
285          trap_handler::IsThreadInWasm());
286 
287   ClearThreadInWasmScope wasm_flag(true);
288 
289   // Set the current isolate's context.
290   DCHECK_NULL(isolate->context());
291   isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
292 
293   // Check if this is a real stack overflow.
294   StackLimitCheck check(isolate);
295   if (check.JsHasOverflowed()) return isolate->StackOverflow();
296 
297   return isolate->stack_guard()->HandleInterrupts();
298 }
299 
RUNTIME_FUNCTION(Runtime_WasmCompileLazy)300 RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
301   HandleScope scope(isolate);
302   DCHECK_EQ(2, args.length());
303   CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
304   CONVERT_SMI_ARG_CHECKED(func_index, 1);
305 
306   ClearThreadInWasmScope wasm_flag(true);
307 
308 #ifdef DEBUG
309   StackFrameIterator it(isolate, isolate->thread_local_top());
310   // On top: C entry stub.
311   DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
312   it.Advance();
313   // Next: the wasm lazy compile frame.
314   DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
315   DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
316 #endif
317 
318   Address entrypoint = wasm::CompileLazy(
319       isolate, instance->module_object()->native_module(), func_index);
320   return reinterpret_cast<Object*>(entrypoint);
321 }
322 
323 }  // namespace internal
324 }  // namespace v8
325