Line data Source code
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/counters.h"
9 : #include "src/debug/debug.h"
10 : #include "src/frame-constants.h"
11 : #include "src/heap/factory.h"
12 : #include "src/message-template.h"
13 : #include "src/objects-inl.h"
14 : #include "src/objects/frame-array-inl.h"
15 : #include "src/runtime/runtime-utils.h"
16 : #include "src/trap-handler/trap-handler.h"
17 : #include "src/v8memory.h"
18 : #include "src/wasm/module-compiler.h"
19 : #include "src/wasm/wasm-code-manager.h"
20 : #include "src/wasm/wasm-constants.h"
21 : #include "src/wasm/wasm-engine.h"
22 : #include "src/wasm/wasm-objects.h"
23 :
24 : namespace v8 {
25 : namespace internal {
26 :
27 : namespace {
28 :
29 3872 : WasmInstanceObject GetWasmInstanceOnStackTop(Isolate* isolate) {
30 3872 : StackFrameIterator it(isolate, isolate->thread_local_top());
31 : // On top: C entry stub.
32 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
33 3872 : it.Advance();
34 : // Next: the wasm compiled frame.
35 : DCHECK(it.frame()->is_wasm_compiled());
36 3872 : WasmCompiledFrame* frame = WasmCompiledFrame::cast(it.frame());
37 7744 : return frame->wasm_instance();
38 : }
39 :
40 2184 : Context GetNativeContextFromWasmInstanceOnStackTop(Isolate* isolate) {
41 2184 : return GetWasmInstanceOnStackTop(isolate)->native_context();
42 : }
43 :
44 : class ClearThreadInWasmScope {
45 : public:
46 192213 : ClearThreadInWasmScope() {
47 : DCHECK_EQ(trap_handler::IsTrapHandlerEnabled(),
48 : trap_handler::IsThreadInWasm());
49 : trap_handler::ClearThreadInWasm();
50 192213 : }
51 192213 : ~ClearThreadInWasmScope() {
52 : DCHECK(!trap_handler::IsThreadInWasm());
53 : trap_handler::SetThreadInWasm();
54 192213 : }
55 : };
56 :
57 : } // namespace
58 :
59 64 : RUNTIME_FUNCTION(Runtime_WasmIsValidAnyFuncValue) {
60 64 : HandleScope scope(isolate);
61 : DCHECK_EQ(1, args.length());
62 64 : CONVERT_ARG_HANDLE_CHECKED(Object, function, 0);
63 :
64 128 : if (function->IsNull(isolate)) {
65 0 : return Smi::FromInt(true);
66 : }
67 64 : if (WasmExportedFunction::IsWasmExportedFunction(*function)) {
68 48 : return Smi::FromInt(true);
69 : }
70 16 : return Smi::FromInt(false);
71 : }
72 :
73 1944 : RUNTIME_FUNCTION(Runtime_WasmMemoryGrow) {
74 1944 : HandleScope scope(isolate);
75 : DCHECK_EQ(2, args.length());
76 3888 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
77 : // {delta_pages} is checked to be a positive smi in the WasmMemoryGrow builtin
78 : // which calls this runtime function.
79 3888 : CONVERT_UINT32_ARG_CHECKED(delta_pages, 1);
80 :
81 : // This runtime function is always being called from wasm code.
82 3888 : ClearThreadInWasmScope flag_scope;
83 :
84 : int ret = WasmMemoryObject::Grow(
85 5832 : isolate, handle(instance->memory_object(), isolate), delta_pages);
86 : // The WasmMemoryGrow builtin which calls this runtime function expects us to
87 : // always return a Smi.
88 3888 : return Smi::FromInt(ret);
89 : }
90 :
91 148555 : RUNTIME_FUNCTION(Runtime_ThrowWasmError) {
92 : DCHECK_EQ(1, args.length());
93 297110 : CONVERT_SMI_ARG_CHECKED(message_id, 0);
94 148555 : ClearThreadInWasmScope clear_wasm_flag;
95 :
96 297110 : HandleScope scope(isolate);
97 : Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
98 148555 : MessageTemplateFromInt(message_id));
99 297110 : return isolate->Throw(*error_obj);
100 : }
101 :
102 0 : RUNTIME_FUNCTION(Runtime_ThrowWasmStackOverflow) {
103 : SealHandleScope shs(isolate);
104 : DCHECK_LE(0, args.length());
105 : DCHECK(isolate->context().is_null());
106 0 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
107 0 : return isolate->StackOverflow();
108 : }
109 :
110 120 : RUNTIME_FUNCTION(Runtime_WasmThrowTypeError) {
111 120 : HandleScope scope(isolate);
112 : DCHECK_EQ(0, args.length());
113 240 : THROW_NEW_ERROR_RETURN_FAILURE(
114 120 : isolate, NewTypeError(MessageTemplate::kWasmTrapTypeError));
115 : }
116 :
117 680 : RUNTIME_FUNCTION(Runtime_WasmThrowCreate) {
118 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
119 680 : HandleScope scope(isolate);
120 : DCHECK_EQ(2, args.length());
121 : DCHECK(isolate->context().is_null());
122 680 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
123 1360 : CONVERT_ARG_CHECKED(WasmExceptionTag, tag_raw, 0);
124 1360 : CONVERT_SMI_ARG_CHECKED(size, 1);
125 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
126 : Handle<Object> tag(tag_raw, isolate);
127 : Handle<Object> exception = isolate->factory()->NewWasmRuntimeError(
128 680 : MessageTemplate::kWasmExceptionError);
129 1360 : CHECK(!Object::SetProperty(isolate, exception,
130 : isolate->factory()->wasm_exception_tag_symbol(),
131 : tag, StoreOrigin::kMaybeKeyed,
132 : Just(ShouldThrow::kThrowOnError))
133 : .is_null());
134 680 : Handle<FixedArray> values = isolate->factory()->NewFixedArray(size);
135 2040 : CHECK(!Object::SetProperty(isolate, exception,
136 : isolate->factory()->wasm_exception_values_symbol(),
137 : values, StoreOrigin::kMaybeKeyed,
138 : Just(ShouldThrow::kThrowOnError))
139 : .is_null());
140 680 : return *exception;
141 : }
142 :
143 480 : RUNTIME_FUNCTION(Runtime_WasmExceptionGetTag) {
144 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
145 480 : HandleScope scope(isolate);
146 : DCHECK_EQ(1, args.length());
147 : DCHECK(isolate->context().is_null());
148 480 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
149 480 : CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
150 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
151 : Handle<Object> except_obj(except_obj_raw, isolate);
152 960 : return *WasmExceptionPackage::GetExceptionTag(isolate, except_obj);
153 : }
154 :
155 1024 : RUNTIME_FUNCTION(Runtime_WasmExceptionGetValues) {
156 : // TODO(kschimpf): Can this be replaced with equivalent TurboFan code/calls.
157 1024 : HandleScope scope(isolate);
158 : DCHECK_EQ(1, args.length());
159 : DCHECK(isolate->context().is_null());
160 1024 : isolate->set_context(GetNativeContextFromWasmInstanceOnStackTop(isolate));
161 1024 : CONVERT_ARG_CHECKED(Object, except_obj_raw, 0);
162 : // TODO(mstarzinger): Manually box because parameters are not visited yet.
163 : Handle<Object> except_obj(except_obj_raw, isolate);
164 2048 : return *WasmExceptionPackage::GetExceptionValues(isolate, except_obj);
165 : }
166 :
167 33452 : RUNTIME_FUNCTION(Runtime_WasmRunInterpreter) {
168 : DCHECK_EQ(2, args.length());
169 33452 : HandleScope scope(isolate);
170 66904 : CONVERT_NUMBER_CHECKED(int32_t, func_index, Int32, args[0]);
171 33452 : CONVERT_ARG_HANDLE_CHECKED(Object, arg_buffer_obj, 1);
172 :
173 : // The arg buffer is the raw pointer to the caller's stack. It looks like a
174 : // Smi (lowest bit not set, as checked by IsSmi), but is no valid Smi. We just
175 : // cast it back to the raw pointer.
176 66904 : CHECK(!arg_buffer_obj->IsHeapObject());
177 66904 : CHECK(arg_buffer_obj->IsSmi());
178 33452 : Address arg_buffer = arg_buffer_obj->ptr();
179 :
180 66904 : ClearThreadInWasmScope wasm_flag;
181 :
182 : // Find the frame pointer and instance of the interpreter frame on the stack.
183 : Handle<WasmInstanceObject> instance;
184 : Address frame_pointer = 0;
185 : {
186 33452 : StackFrameIterator it(isolate, isolate->thread_local_top());
187 : // On top: C entry stub.
188 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
189 33452 : it.Advance();
190 : // Next: the wasm interpreter entry.
191 : DCHECK_EQ(StackFrame::WASM_INTERPRETER_ENTRY, it.frame()->type());
192 : instance = handle(
193 33452 : WasmInterpreterEntryFrame::cast(it.frame())->wasm_instance(), isolate);
194 33452 : frame_pointer = it.frame()->fp();
195 : }
196 :
197 : // Set the current isolate's context.
198 : DCHECK(isolate->context().is_null());
199 33452 : isolate->set_context(instance->native_context());
200 :
201 : // Run the function in the interpreter. Note that neither the {WasmDebugInfo}
202 : // nor the {InterpreterHandle} have to exist, because interpretation might
203 : // have been triggered by another Isolate sharing the same WasmEngine.
204 : Handle<WasmDebugInfo> debug_info =
205 33452 : WasmInstanceObject::GetOrCreateDebugInfo(instance);
206 : bool success = WasmDebugInfo::RunInterpreter(
207 33452 : isolate, debug_info, frame_pointer, func_index, arg_buffer);
208 :
209 33452 : if (!success) {
210 : DCHECK(isolate->has_pending_exception());
211 : return ReadOnlyRoots(isolate).exception();
212 : }
213 33452 : return ReadOnlyRoots(isolate).undefined_value();
214 : }
215 :
216 222 : RUNTIME_FUNCTION(Runtime_WasmStackGuard) {
217 : SealHandleScope shs(isolate);
218 : DCHECK_EQ(0, args.length());
219 : DCHECK(!trap_handler::IsTrapHandlerEnabled() ||
220 : trap_handler::IsThreadInWasm());
221 :
222 222 : ClearThreadInWasmScope wasm_flag;
223 :
224 : // Check if this is a real stack overflow.
225 222 : StackLimitCheck check(isolate);
226 222 : if (check.JsHasOverflowed()) return isolate->StackOverflow();
227 :
228 54 : return isolate->stack_guard()->HandleInterrupts();
229 : }
230 :
231 8040 : RUNTIME_FUNCTION(Runtime_WasmCompileLazy) {
232 8040 : HandleScope scope(isolate);
233 : DCHECK_EQ(2, args.length());
234 16080 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
235 16080 : CONVERT_SMI_ARG_CHECKED(func_index, 1);
236 :
237 16080 : ClearThreadInWasmScope wasm_flag;
238 :
239 : #ifdef DEBUG
240 : StackFrameIterator it(isolate, isolate->thread_local_top());
241 : // On top: C entry stub.
242 : DCHECK_EQ(StackFrame::EXIT, it.frame()->type());
243 : it.Advance();
244 : // Next: the wasm lazy compile frame.
245 : DCHECK_EQ(StackFrame::WASM_COMPILE_LAZY, it.frame()->type());
246 : DCHECK_EQ(*instance, WasmCompileLazyFrame::cast(it.frame())->wasm_instance());
247 : #endif
248 :
249 : Address entrypoint = wasm::CompileLazy(
250 16080 : isolate, instance->module_object()->native_module(), func_index);
251 16080 : return Object(entrypoint);
252 : }
253 :
254 : // Should be called from within a handle scope
255 596 : Handle<JSArrayBuffer> getSharedArrayBuffer(Handle<WasmInstanceObject> instance,
256 : Isolate* isolate, uint32_t address) {
257 : DCHECK(instance->has_memory_object());
258 1196 : Handle<JSArrayBuffer> array_buffer(instance->memory_object()->array_buffer(),
259 1196 : isolate);
260 :
261 : // Validation should have failed if the memory was not shared.
262 : DCHECK(array_buffer->is_shared());
263 :
264 : // Should have trapped if address was OOB
265 : DCHECK_LT(address, array_buffer->byte_length());
266 600 : return array_buffer;
267 : }
268 :
269 136 : RUNTIME_FUNCTION(Runtime_WasmAtomicWake) {
270 136 : HandleScope scope(isolate);
271 : DCHECK_EQ(3, args.length());
272 272 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
273 272 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
274 272 : CONVERT_NUMBER_CHECKED(uint32_t, count, Uint32, args[2]);
275 : Handle<JSArrayBuffer> array_buffer =
276 136 : getSharedArrayBuffer(instance, isolate, address);
277 136 : return FutexEmulation::Wake(array_buffer, address, count);
278 : }
279 :
280 461 : double WaitTimeoutInMs(double timeout_ns) {
281 : return timeout_ns < 0
282 : ? V8_INFINITY
283 : : timeout_ns / (base::Time::kNanosecondsPerMicrosecond *
284 461 : base::Time::kMicrosecondsPerMillisecond);
285 : }
286 :
287 231 : RUNTIME_FUNCTION(Runtime_WasmI32AtomicWait) {
288 231 : HandleScope scope(isolate);
289 : DCHECK_EQ(4, args.length());
290 460 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
291 462 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
292 464 : CONVERT_NUMBER_CHECKED(int32_t, expected_value, Int32, args[2]);
293 464 : CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 3);
294 231 : double timeout_ms = WaitTimeoutInMs(timeout_ns);
295 : Handle<JSArrayBuffer> array_buffer =
296 231 : getSharedArrayBuffer(instance, isolate, address);
297 : return FutexEmulation::Wait32(isolate, array_buffer, address, expected_value,
298 232 : timeout_ms);
299 : }
300 :
301 231 : RUNTIME_FUNCTION(Runtime_WasmI64AtomicWait) {
302 231 : HandleScope scope(isolate);
303 : DCHECK_EQ(5, args.length());
304 463 : CONVERT_ARG_HANDLE_CHECKED(WasmInstanceObject, instance, 0);
305 463 : CONVERT_NUMBER_CHECKED(uint32_t, address, Uint32, args[1]);
306 463 : CONVERT_NUMBER_CHECKED(uint32_t, expected_value_high, Uint32, args[2]);
307 464 : CONVERT_NUMBER_CHECKED(uint32_t, expected_value_low, Uint32, args[3]);
308 463 : CONVERT_DOUBLE_ARG_CHECKED(timeout_ns, 4);
309 231 : int64_t expected_value = (static_cast<uint64_t>(expected_value_high) << 32) |
310 231 : static_cast<uint64_t>(expected_value_low);
311 231 : double timeout_ms = WaitTimeoutInMs(timeout_ns);
312 : Handle<JSArrayBuffer> array_buffer =
313 231 : getSharedArrayBuffer(instance, isolate, address);
314 : return FutexEmulation::Wait64(isolate, array_buffer, address, expected_value,
315 232 : timeout_ms);
316 : }
317 :
318 : namespace {
319 1416 : Object ThrowTableOutOfBounds(Isolate* isolate,
320 : Handle<WasmInstanceObject> instance) {
321 : // Handle out-of-bounds access here in the runtime call, rather
322 : // than having the lower-level layers deal with JS exceptions.
323 1416 : if (isolate->context().is_null()) {
324 2576 : isolate->set_context(instance->native_context());
325 : }
326 : Handle<Object> error_obj = isolate->factory()->NewWasmRuntimeError(
327 1416 : MessageTemplate::kWasmTrapTableOutOfBounds);
328 1416 : return isolate->Throw(*error_obj);
329 : }
330 : } // namespace
331 :
332 184 : RUNTIME_FUNCTION(Runtime_WasmTableInit) {
333 184 : HandleScope scope(isolate);
334 : DCHECK_EQ(5, args.length());
335 : auto instance =
336 184 : Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
337 368 : CONVERT_UINT32_ARG_CHECKED(table_index, 0);
338 368 : CONVERT_UINT32_ARG_CHECKED(elem_segment_index, 1);
339 368 : CONVERT_UINT32_ARG_CHECKED(dst, 2);
340 368 : CONVERT_UINT32_ARG_CHECKED(src, 3);
341 368 : CONVERT_UINT32_ARG_CHECKED(count, 4);
342 :
343 : DCHECK(isolate->context().is_null());
344 184 : isolate->set_context(instance->native_context());
345 :
346 : bool oob = !WasmInstanceObject::InitTableEntries(
347 184 : isolate, instance, table_index, elem_segment_index, dst, src, count);
348 184 : if (oob) return ThrowTableOutOfBounds(isolate, instance);
349 184 : return ReadOnlyRoots(isolate).undefined_value();
350 : }
351 :
352 1504 : RUNTIME_FUNCTION(Runtime_WasmTableCopy) {
353 1504 : HandleScope scope(isolate);
354 : DCHECK_EQ(5, args.length());
355 : auto instance =
356 1504 : Handle<WasmInstanceObject>(GetWasmInstanceOnStackTop(isolate), isolate);
357 3008 : CONVERT_UINT32_ARG_CHECKED(table_src_index, 0);
358 3008 : CONVERT_UINT32_ARG_CHECKED(table_dst_index, 1);
359 3008 : CONVERT_UINT32_ARG_CHECKED(dst, 2);
360 3008 : CONVERT_UINT32_ARG_CHECKED(src, 3);
361 3008 : CONVERT_UINT32_ARG_CHECKED(count, 4);
362 :
363 : bool oob = !WasmInstanceObject::CopyTableEntries(
364 1504 : isolate, instance, table_src_index, table_dst_index, dst, src, count);
365 1504 : if (oob) return ThrowTableOutOfBounds(isolate, instance);
366 1504 : return ReadOnlyRoots(isolate).undefined_value();
367 : }
368 : } // namespace internal
369 178779 : } // namespace v8
|