42468ef021e4cb8027e70b6979417c88b81984e1
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WebAssemblyFunction.cpp
1 /*
2  * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebAssemblyFunction.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "B3Compilation.h"
32 #include "JSCInlines.h"
33 #include "JSFunctionInlines.h"
34 #include "JSObject.h"
35 #include "JSWebAssemblyInstance.h"
36 #include "JSWebAssemblyMemory.h"
37 #include "JSWebAssemblyRuntimeError.h"
38 #include "LLIntThunks.h"
39 #include "ProtoCallFrame.h"
40 #include "VM.h"
41 #include "WasmCallee.h"
42 #include "WasmContextInlines.h"
43 #include "WasmFormat.h"
44 #include "WasmMemory.h"
45 #include "WasmSignatureInlines.h"
46 #include <wtf/FastTLS.h>
47 #include <wtf/StackPointer.h>
48 #include <wtf/SystemTracing.h>
49
50 namespace JSC {
51
52 const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) };
53
54 static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
55 {
56     VM& vm = exec->vm();
57     auto scope = DECLARE_THROW_SCOPE(vm);
58     WebAssemblyFunction* wasmFunction = jsCast<WebAssemblyFunction*>(exec->jsCallee());
59     Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex();
60     const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
61
62     // Make sure that the memory we think we are going to run with matches the one we expect.
63     ASSERT(wasmFunction->instance()->instance().codeBlock()->isSafeToRun(wasmFunction->instance()->memory()->memory().mode()));
64
65     Optional<TraceScope> traceScope;
66     if (Options::useTracePoints())
67         traceScope.emplace(WebAssemblyExecuteStart, WebAssemblyExecuteEnd);
68
69     Vector<JSValue, MarkedArgumentBuffer::inlineCapacity> boxedArgs;
70     JSWebAssemblyInstance* instance = wasmFunction->instance();
71     Wasm::Instance* wasmInstance = &instance->instance();
72     // When we don't use fast TLS to store the context, the JS
73     // entry wrapper expects a JSWebAssemblyInstance as the first argument.
74     if (!Wasm::Context::useFastTLS())
75         boxedArgs.append(instance);
76
77     for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
78         JSValue arg = exec->argument(argIndex);
79         switch (signature.argument(argIndex)) {
80         case Wasm::I32:
81             arg = JSValue::decode(arg.toInt32(exec));
82             break;
83         case Wasm::I64:
84             arg = JSValue();
85             break;
86         case Wasm::F32:
87             arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(exec)));
88             break;
89         case Wasm::F64:
90             arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(exec)));
91             break;
92         case Wasm::Void:
93         case Wasm::Func:
94         case Wasm::Anyfunc:
95             RELEASE_ASSERT_NOT_REACHED();
96         }
97         RETURN_IF_EXCEPTION(scope, encodedJSValue());
98         boxedArgs.append(arg);
99     }
100
101     JSValue firstArgument = JSValue();
102     int argCount = 1;
103     JSValue* remainingArgs = nullptr;
104     if (boxedArgs.size()) {
105         remainingArgs = boxedArgs.data();
106         firstArgument = *remainingArgs;
107         remainingArgs++;
108         argCount = boxedArgs.size();
109     }
110
111     // Note: we specifically use the WebAssemblyFunction as the callee to begin with in the ProtoCallFrame.
112     // The reason for this is that calling into the llint may stack overflow, and the stack overflow
113     // handler might read the global object from the callee.
114     ProtoCallFrame protoCallFrame;
115     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
116
117     // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
118     Wasm::Instance* prevWasmInstance = vm.wasmContext.load();
119     {
120         // We do the stack check here for the wrapper function because we don't
121         // want to emit a stack check inside every wrapper function.
122         const intptr_t sp = bitwise_cast<intptr_t>(currentStackPointer());
123         const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
124         const intptr_t stackSpaceUsed = 2 * frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
125         if (UNLIKELY((sp < stackSpaceUsed) || ((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit()))))
126             return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
127     }
128     vm.wasmContext.store(wasmInstance, vm.softStackLimit());
129     ASSERT(wasmFunction->instance());
130     ASSERT(&wasmFunction->instance()->instance() == vm.wasmContext.load());
131     EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->jsEntrypoint(MustCheckArity).executableAddress(), &vm, &protoCallFrame);
132     // We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
133     // This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
134     // alive. But it's good hygiene.
135     instance->use();
136     if (prevWasmInstance != wasmInstance) {
137         // This is just for some extra safety instead of leaving a cached
138         // value in there. If we ever forget to set the value to be a real
139         // bounds, this will force every stack overflow check to immediately
140         // fire. The stack limit never changes while executing except when
141         // WebAssembly is used through the JSC API: API users can ask the code
142         // to migrate threads.
143         wasmInstance->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
144     }
145     vm.wasmContext.store(prevWasmInstance, vm.softStackLimit());
146     RETURN_IF_EXCEPTION(scope, { });
147
148     return rawResult;
149 }
150
151 WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, Structure* structure, unsigned length, const String& name, JSWebAssemblyInstance* instance, Wasm::Callee& jsEntrypoint, Wasm::WasmToWasmImportableFunction::LoadLocation wasmToWasmEntrypointLoadLocation, Wasm::SignatureIndex signatureIndex)
152 {
153     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
154     WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, jsEntrypoint, wasmToWasmEntrypointLoadLocation, signatureIndex);
155     function->finishCreation(vm, executable, length, name, instance);
156     ASSERT_WITH_MESSAGE(!function->isLargeAllocation(), "WebAssemblyFunction should be allocated not in large allocation since it is JSCallee.");
157     return function;
158 }
159
160 Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
161 {
162     ASSERT(globalObject);
163     return Structure::create(vm, globalObject, prototype, TypeInfo(JSFunctionType, StructureFlags), info());
164 }
165
166 WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, Wasm::Callee& jsEntrypoint, Wasm::WasmToWasmImportableFunction::LoadLocation wasmToWasmEntrypointLoadLocation, Wasm::SignatureIndex signatureIndex)
167     : Base { vm, globalObject, structure }
168     , m_jsEntrypoint { jsEntrypoint.entrypoint() }
169     , m_importableFunction { signatureIndex, wasmToWasmEntrypointLoadLocation }
170 { }
171
172 } // namespace JSC
173
174 #endif // ENABLE(WEBASSEMBLY)