Demarcate code added due to lack of NSDMI for aggregates
[WebKit.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 "WasmContext.h"
43 #include "WasmFormat.h"
44 #include "WasmMemory.h"
45 #include <wtf/FastTLS.h>
46 #include <wtf/SystemTracing.h>
47
48 namespace JSC {
49
50 const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) };
51
52 static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
53 {
54     VM& vm = exec->vm();
55     auto scope = DECLARE_THROW_SCOPE(vm);
56     WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, exec->jsCallee());
57     if (!wasmFunction)
58         return JSValue::encode(throwException(exec, scope, createTypeError(exec, "expected a WebAssembly function", defaultSourceAppender, runtimeTypeForValue(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()->codeBlock()->isSafeToRun(wasmFunction->instance()->memory()));
64     {
65         // Check if we have a disallowed I64 use.
66
67         for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
68             if (signature.argument(argIndex) == Wasm::I64) {
69                 JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(),
70                     "WebAssembly function with an i64 argument can't be called from JavaScript");
71                 return JSValue::encode(throwException(exec, scope, error));
72             }
73         }
74
75         if (signature.returnType() == Wasm::I64) {
76             JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(),
77                 "WebAssembly function that returns i64 can't be called from JavaScript");
78             return JSValue::encode(throwException(exec, scope, error));
79         }
80     }
81
82     TraceScope traceScope(WebAssemblyExecuteStart, WebAssemblyExecuteEnd);
83
84     Vector<JSValue> boxedArgs;
85     Wasm::Context* wasmContext = wasmFunction->instance();
86     // When we don't use fast TLS to store the context, the js
87     // entry wrapper expects the WasmContext* as the first argument.
88     if (!Wasm::useFastTLSForContext())
89         boxedArgs.append(wasmContext);
90
91     for (unsigned argIndex = 0; argIndex < signature.argumentCount(); ++argIndex) {
92         JSValue arg = exec->argument(argIndex);
93         switch (signature.argument(argIndex)) {
94         case Wasm::I32:
95             arg = JSValue::decode(arg.toInt32(exec));
96             break;
97         case Wasm::F32:
98             arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(exec)));
99             break;
100         case Wasm::F64:
101             arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(exec)));
102             break;
103         case Wasm::Void:
104         case Wasm::I64:
105         case Wasm::Func:
106         case Wasm::Anyfunc:
107             RELEASE_ASSERT_NOT_REACHED();
108         }
109         RETURN_IF_EXCEPTION(scope, encodedJSValue());
110         boxedArgs.append(arg);
111     }
112
113     JSValue firstArgument = JSValue();
114     int argCount = 1;
115     JSValue* remainingArgs = nullptr;
116     if (boxedArgs.size()) {
117         remainingArgs = boxedArgs.data();
118         firstArgument = *remainingArgs;
119         remainingArgs++;
120         argCount = boxedArgs.size();
121     }
122
123     // Note: we specifically use the WebAssemblyFunction as the callee to begin with in the ProtoCallFrame.
124     // The reason for this is that calling into the llint may stack overflow, and the stack overflow
125     // handler might read the global object from the callee.
126     ProtoCallFrame protoCallFrame;
127     protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs);
128
129     // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486
130     Wasm::Context* prevWasmContext = Wasm::loadContext(vm);
131     {
132         // We do the stack check here for the wrapper function because we don't
133         // want to emit a stack check inside every wrapper function.
134         const intptr_t sp = bitwise_cast<intptr_t>(&sp); // A proxy for the current stack pointer.
135         const intptr_t frameSize = (boxedArgs.size() + CallFrame::headerSizeInRegisters) * sizeof(Register);
136         const intptr_t stackSpaceUsed = 2 * frameSize; // We're making two calls. One to the wrapper, and one to the actual wasm code.
137         if (UNLIKELY((sp < stackSpaceUsed) || ((sp - stackSpaceUsed) < bitwise_cast<intptr_t>(vm.softStackLimit()))))
138             return JSValue::encode(throwException(exec, scope, createStackOverflowError(exec)));
139     }
140     Wasm::storeContext(vm, wasmContext);
141     ASSERT(wasmFunction->instance());
142     ASSERT(wasmFunction->instance() == Wasm::loadContext(vm));
143     EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->jsEntrypoint(), &vm, &protoCallFrame);
144     // We need to make sure this is in a register or on the stack since it's stored in Vector<JSValue>.
145     // This probably isn't strictly necessary, since the WebAssemblyFunction* should keep the instance
146     // alive. But it's good hygiene.
147     wasmContext->use();
148     if (prevWasmContext != wasmContext) {
149         // This is just for some extra safety instead of leaving a cached
150         // value in there. If we ever forget to set the value to be a real
151         // bounds, this will force every stack overflow check to immediately
152         // fire.
153         wasmContext->setCachedStackLimit(bitwise_cast<void*>(std::numeric_limits<uintptr_t>::max()));
154     }
155     Wasm::storeContext(vm, prevWasmContext);
156     RETURN_IF_EXCEPTION(scope, { });
157
158     switch (signature.returnType()) {
159     case Wasm::Void:
160         return JSValue::encode(jsUndefined());
161     case Wasm::I32:
162         return JSValue::encode(jsNumber(static_cast<int32_t>(rawResult)));
163     case Wasm::F32:
164         return JSValue::encode(jsNumber(purifyNaN(static_cast<double>(bitwise_cast<float>(static_cast<int32_t>(rawResult))))));
165     case Wasm::F64:
166         return JSValue::encode(jsNumber(purifyNaN(bitwise_cast<double>(rawResult))));
167     case Wasm::I64:
168     case Wasm::Func:
169     case Wasm::Anyfunc:
170         RELEASE_ASSERT_NOT_REACHED();
171     }
172
173     return EncodedJSValue();
174 }
175
176 WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, JSWebAssemblyInstance* instance, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation wasmEntrypoint, Wasm::SignatureIndex signatureIndex)
177 {
178     NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name);
179     Structure* structure = globalObject->webAssemblyFunctionStructure();
180     WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, jsEntrypoint, wasmEntrypoint, signatureIndex);
181     function->finishCreation(vm, executable, length, name, instance);
182     ASSERT_WITH_MESSAGE(!function->isLargeAllocation(), "WebAssemblyFunction should be allocated not in large allocation since it is JSCallee.");
183     return function;
184 }
185
186 Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
187 {
188     ASSERT(globalObject);
189     return Structure::create(vm, globalObject, prototype, TypeInfo(WebAssemblyFunctionType, StructureFlags), info());
190 }
191
192 WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, Wasm::Callee& jsEntrypoint, Wasm::WasmEntrypointLoadLocation wasmEntrypoint, Wasm::SignatureIndex signatureIndex)
193     : Base { vm, globalObject, structure }
194     , m_jsEntrypoint { jsEntrypoint.entrypoint() }
195     , m_wasmFunction { signatureIndex, wasmEntrypoint }
196 { }
197
198 } // namespace JSC
199
200 #endif // ENABLE(WEBASSEMBLY)