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