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