46441d3d22f6a6b2a0ddf599aec54f63f2d4b26d
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WebAssemblyPrototype.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 "WebAssemblyPrototype.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "CatchScope.h"
32 #include "Exception.h"
33 #include "FunctionPrototype.h"
34 #include "JSCInlines.h"
35 #include "JSPromiseDeferred.h"
36 #include "JSToWasm.h"
37 #include "JSWebAssemblyHelpers.h"
38 #include "JSWebAssemblyInstance.h"
39 #include "JSWebAssemblyModule.h"
40 #include "ObjectConstructor.h"
41 #include "PromiseDeferredTimer.h"
42 #include "StrongInlines.h"
43 #include "ThrowScope.h"
44 #include "WasmBBQPlan.h"
45 #include "WasmToJS.h"
46 #include "WasmWorklist.h"
47 #include "WebAssemblyInstanceConstructor.h"
48 #include "WebAssemblyModuleConstructor.h"
49
50 using JSC::Wasm::Plan;
51 using JSC::Wasm::BBQPlan;
52
53 namespace JSC {
54 static EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState*);
55 static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState*);
56 static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState*);
57 }
58
59 #include "WebAssemblyPrototype.lut.h"
60
61 namespace JSC {
62
63 const ClassInfo WebAssemblyPrototype::s_info = { "WebAssembly", &Base::s_info, &prototypeTableWebAssembly, nullptr, CREATE_METHOD_TABLE(WebAssemblyPrototype) };
64
65 /* Source for WebAssemblyPrototype.lut.h
66  @begin prototypeTableWebAssembly
67  compile     webAssemblyCompileFunc     DontEnum|Function 1
68  instantiate webAssemblyInstantiateFunc DontEnum|Function 1
69  validate    webAssemblyValidateFunc    DontEnum|Function 1
70  @end
71  */
72
73 static void reject(ExecState* exec, CatchScope& catchScope, JSPromiseDeferred* promise)
74 {
75     Exception* exception = catchScope.exception();
76     ASSERT(exception);
77     catchScope.clearException();
78     promise->reject(exec, exception->value());
79     CLEAR_AND_RETURN_IF_EXCEPTION(catchScope, void());
80 }
81
82 static EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState* exec)
83 {
84     VM& vm = exec->vm();
85     auto throwScope = DECLARE_THROW_SCOPE(vm);
86     auto* globalObject = exec->lexicalGlobalObject();
87
88     JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, globalObject);
89     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
90
91     {
92         auto catchScope = DECLARE_CATCH_SCOPE(vm);
93
94         Vector<Strong<JSCell>> dependencies;
95         dependencies.append(Strong<JSCell>(vm, globalObject));
96         vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
97
98         Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, exec->argument(0));
99
100         if (UNLIKELY(catchScope.exception()))
101             reject(exec, catchScope, promise);
102         else {
103             Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
104                 vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, globalObject, result = WTFMove(result), &vm] () mutable {
105                     auto scope = DECLARE_CATCH_SCOPE(vm);
106                     ExecState* exec = globalObject->globalExec();
107                     JSValue module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(result));
108                     if (UNLIKELY(scope.exception())) {
109                         reject(exec, scope, promise);
110                         return;
111                     }
112
113                     promise->resolve(exec, module);
114                     CLEAR_AND_RETURN_IF_EXCEPTION(scope, void());
115                 });
116             }));
117         }
118
119         return JSValue::encode(promise->promise());
120     }
121 }
122
123 enum class Resolve { WithInstance, WithModuleAndInstance };
124 static void resolve(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyInstance* instance, JSWebAssemblyModule* module, Ref<Wasm::CodeBlock>&& codeBlock, Resolve resolveKind)
125 {
126     auto scope = DECLARE_CATCH_SCOPE(vm);
127     instance->finalizeCreation(vm, exec, WTFMove(codeBlock));
128     RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
129
130     if (resolveKind == Resolve::WithInstance)
131         promise->resolve(exec, instance);
132     else {
133         JSObject* result = constructEmptyObject(exec);
134         result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("module")), module);
135         result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("instance")), instance);
136         promise->resolve(exec, result);
137     }
138     CLEAR_AND_RETURN_IF_EXCEPTION(scope, void());
139 }
140
141 static void instantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyModule* module, JSObject* importObject, Resolve resolveKind)
142 {
143     auto scope = DECLARE_CATCH_SCOPE(vm);
144     // In order to avoid potentially recompiling a module. We first gather all the import/memory information prior to compiling code.
145     JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure(), Ref<Wasm::Module>(module->module()));
146     RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
147
148     Vector<Strong<JSCell>> dependencies;
149     // The instance keeps the module alive.
150     dependencies.append(Strong<JSCell>(vm, instance));
151     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
152     // Note: This completion task may or may not get called immediately.
153     module->module().compileAsync(&vm.wasmContext, instance->memoryMode(), createSharedTask<Wasm::CodeBlock::CallbackType>([promise, instance, module, resolveKind, &vm] (Ref<Wasm::CodeBlock>&& refCodeBlock) mutable {
154         RefPtr<Wasm::CodeBlock> codeBlock = WTFMove(refCodeBlock);
155         vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, instance, module, resolveKind, &vm, codeBlock = WTFMove(codeBlock)] () mutable {
156             ExecState* exec = instance->globalObject()->globalExec();
157             resolve(vm, exec, promise, instance, module, codeBlock.releaseNonNull(), resolveKind);
158         });
159     }), &Wasm::createJSToWasmWrapper, &Wasm::wasmToJSException);
160 }
161
162 static void compileAndInstantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSValue buffer, JSObject* importObject)
163 {
164     auto scope = DECLARE_CATCH_SCOPE(vm);
165
166     auto* globalObject = exec->lexicalGlobalObject();
167
168     Vector<Strong<JSCell>> dependencies;
169     dependencies.append(Strong<JSCell>(vm, importObject));
170     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
171
172     Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, buffer);
173     RETURN_IF_EXCEPTION(scope, reject(exec, scope, promise));
174
175     Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, importObject, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
176         vm.promiseDeferredTimer->scheduleWorkSoon(promise, [promise, importObject, globalObject, result = WTFMove(result), &vm] () mutable {
177             auto scope = DECLARE_CATCH_SCOPE(vm);
178             ExecState* exec = globalObject->globalExec();
179             JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(result));
180             if (UNLIKELY(scope.exception()))
181                 return reject(exec, scope, promise);
182
183             instantiate(vm, exec, promise, module, importObject, Resolve::WithModuleAndInstance);
184         });
185     }));
186 }
187
188 static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState* exec)
189 {
190     VM& vm = exec->vm();
191     auto throwScope = DECLARE_THROW_SCOPE(vm);
192     auto* globalObject = exec->lexicalGlobalObject();
193
194     JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, globalObject);
195     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
196
197     {
198         auto catchScope = DECLARE_CATCH_SCOPE(vm);
199
200         JSValue importArgument = exec->argument(1);
201         JSObject* importObject = importArgument.getObject();
202         if (UNLIKELY(!importArgument.isUndefined() && !importObject)) {
203             promise->reject(exec, createTypeError(exec,
204                 ASCIILiteral("second argument to WebAssembly.instantiate must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument)));
205             CLEAR_AND_RETURN_IF_EXCEPTION(catchScope, JSValue::encode(promise->promise()));
206         } else {
207             JSValue firstArgument = exec->argument(0);
208             if (auto* module = jsDynamicCast<JSWebAssemblyModule*>(vm, firstArgument))
209                 instantiate(vm, exec, promise, module, importObject, Resolve::WithInstance);
210             else
211                 compileAndInstantiate(vm, exec, promise, firstArgument, importObject);
212         }
213
214         return JSValue::encode(promise->promise());
215     }
216 }
217
218 static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec)
219 {
220     VM& vm = exec->vm();
221     auto scope = DECLARE_THROW_SCOPE(vm);
222
223     uint8_t* base;
224     size_t byteSize;
225     std::tie(base, byteSize) = getWasmBufferFromValue(exec, exec->argument(0));
226     RETURN_IF_EXCEPTION(scope, encodedJSValue());
227     BBQPlan plan(&vm.wasmContext, base, byteSize, BBQPlan::Validation, Plan::dontFinalize());
228     // FIXME: We might want to throw an OOM exception here if we detect that something will OOM.
229     // https://bugs.webkit.org/show_bug.cgi?id=166015
230     return JSValue::encode(jsBoolean(plan.parseAndValidateModule()));
231 }
232
233 WebAssemblyPrototype* WebAssemblyPrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
234 {
235     auto* object = new (NotNull, allocateCell<WebAssemblyPrototype>(vm.heap)) WebAssemblyPrototype(vm, structure);
236     object->finishCreation(vm);
237     return object;
238 }
239
240 Structure* WebAssemblyPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
241 {
242     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
243 }
244
245 void WebAssemblyPrototype::finishCreation(VM& vm)
246 {
247     Base::finishCreation(vm);
248 }
249
250 WebAssemblyPrototype::WebAssemblyPrototype(VM& vm, Structure* structure)
251     : Base(vm, structure)
252 {
253 }
254
255 } // namespace JSC
256
257 #endif // ENABLE(WEBASSEMBLY)