WebAssembly: ModuleInformation should be a ref counted thing that can be shared acros...
[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 "WasmPlan.h"
42 #include "WasmWorklist.h"
43 #include "WebAssemblyInstanceConstructor.h"
44 #include "WebAssemblyModuleConstructor.h"
45
46 using JSC::Wasm::Plan;
47
48 namespace JSC {
49 static EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState*);
50 static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState*);
51 static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState*);
52 }
53
54 #include "WebAssemblyPrototype.lut.h"
55
56 namespace JSC {
57
58 const ClassInfo WebAssemblyPrototype::s_info = { "WebAssembly.prototype", &Base::s_info, &prototypeTableWebAssembly, CREATE_METHOD_TABLE(WebAssemblyPrototype) };
59
60 /* Source for WebAssemblyPrototype.lut.h
61  @begin prototypeTableWebAssembly
62  compile     webAssemblyCompileFunc     DontEnum|Function 1
63  instantiate webAssemblyInstantiateFunc DontEnum|Function 1
64  validate    webAssemblyValidateFunc    DontEnum|Function 1
65  @end
66  */
67
68 static EncodedJSValue reject(ExecState* exec, CatchScope& catchScope, JSPromiseDeferred* promise)
69 {
70     Exception* exception = catchScope.exception();
71     ASSERT(exception);
72     catchScope.clearException();
73     promise->reject(exec, exception->value());
74     return JSValue::encode(promise->promise());
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<uint8_t> source = createSourceBufferFromValue(vm, exec, exec->argument(0));
87     RETURN_IF_EXCEPTION(scope, { });
88
89     Vector<Strong<JSCell>> dependencies;
90     dependencies.append(Strong<JSCell>(vm, globalObject));
91     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
92
93     Ref<Plan> plan = adoptRef(*new Plan(vm, WTFMove(source), Plan::Validation, [promise, globalObject] (Plan& p) mutable {
94         RefPtr<Plan> plan = makeRef(p);
95         plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [promise, globalObject, plan = WTFMove(plan)] () mutable {
96             VM& vm = plan->vm();
97             auto scope = DECLARE_CATCH_SCOPE(vm);
98             ExecState* exec = globalObject->globalExec();
99             JSValue module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), WTFMove(plan));
100             if (scope.exception()) {
101                 reject(exec, scope, promise);
102                 return;
103             }
104
105             promise->resolve(exec, module);
106         });
107     }));
108
109     Wasm::ensureWorklist().enqueue(WTFMove(plan));
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, Resolve entries)
115 {
116     auto scope = DECLARE_CATCH_SCOPE(vm);
117     instance->finalizeCreation(vm, exec);
118     if (scope.exception()) {
119         reject(exec, scope, promise);
120         return;
121     }
122
123     if (entries == Resolve::WithInstance)
124         promise->resolve(exec, instance);
125     else {
126         JSObject* result = constructEmptyObject(exec);
127         result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("module")), module);
128         result->putDirect(vm, Identifier::fromString(&vm, ASCIILiteral("instance")), instance);
129         promise->resolve(exec, result);
130     }
131 }
132
133 static void instantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSWebAssemblyModule* module, JSObject* importObject, Resolve entries)
134 {
135     auto scope = DECLARE_CATCH_SCOPE(vm);
136     // In order to avoid potentially recompiling a module. We first gather all the import/memory information prior to compiling code.
137     JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, exec, module, importObject, exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
138     if (scope.exception()) {
139         reject(exec, scope, promise);
140         return;
141     }
142
143     // There are three possible cases:
144     // 1) The instance already has an initialized CodeBlock, so we have no more work to do.
145     // 2) The instance has no CodeBlock, so we need to make one and compile the code for it.
146     // 3) The instance already has an uninitialized CodeBlock, so someone else is compiling code and we just need to wait for them.
147
148     if (instance->initialized()) {
149         resolve(vm, exec, promise, instance, module, entries);
150         return;
151     }
152
153     Vector<Strong<JSCell>> dependencies;
154     // The instance keeps the module alive.
155     dependencies.append(Strong<JSCell>(vm, instance));
156     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
157
158     if (instance->codeBlock()) {
159         vm.promiseDeferredTimer->scheduleBlockedTask(instance->codeBlock()->plan().pendingPromise(), [&vm, promise, instance, module, entries] () {
160             auto* globalObject = instance->globalObject();
161             ExecState* exec = globalObject->globalExec();
162             resolve(vm, exec, promise, instance, module, entries);
163         });
164         return;
165     }
166     ASSERT(!instance->codeBlock());
167
168     // FIXME: This re-parses the module header, which shouldn't be necessary.
169     // https://bugs.webkit.org/show_bug.cgi?id=170205
170     Ref<Plan> plan = adoptRef(*new Plan(vm, makeRef(const_cast<Wasm::ModuleInformation&>(module->moduleInformation())), Plan::FullCompile, [promise, instance, module, entries] (Plan& p) {
171         RefPtr<Plan> plan = makeRef(p);
172         plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [promise, instance, module, entries, plan = WTFMove(plan)] () {
173             VM& vm = plan->vm();
174             ExecState* exec = instance->globalObject()->globalExec();
175             resolve(vm, exec, promise, instance, module, entries);
176         });
177     }));
178
179     instance->addUnitializedCodeBlock(vm, plan.copyRef());
180     plan->setModeAndPromise(instance->memoryMode(), promise);
181     Wasm::ensureWorklist().enqueue(WTFMove(plan));
182 }
183
184 static void compileAndInstantiate(VM& vm, ExecState* exec, JSPromiseDeferred* promise, JSValue buffer, JSObject* importObject)
185 {
186     auto scope = DECLARE_THROW_SCOPE(vm);
187     Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, buffer);
188     RETURN_IF_EXCEPTION(scope, void());
189
190     auto* globalObject = exec->lexicalGlobalObject();
191
192     Vector<Strong<JSCell>> dependencies;
193     dependencies.append(Strong<JSCell>(vm, importObject));
194     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
195
196     Ref<Plan> plan = adoptRef(*new Plan(vm, WTFMove(source), Plan::Validation, [promise, importObject, globalObject] (Plan& p) mutable {
197         RefPtr<Plan> plan = makeRef(p);
198         plan->vm().promiseDeferredTimer->scheduleWorkSoon(promise, [promise, importObject, globalObject, plan = WTFMove(plan)] () mutable {
199             VM& vm = plan->vm();
200             auto scope = DECLARE_CATCH_SCOPE(vm);
201             ExecState* exec = globalObject->globalExec();
202             JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, exec, globalObject->WebAssemblyModuleStructure(), plan.copyRef());
203             if (scope.exception()) {
204                 reject(exec, scope, promise);
205                 return;
206             }
207
208             instantiate(vm, exec, promise, module, importObject, Resolve::WithModuleAndInstance);
209         });
210     }));
211
212     Wasm::ensureWorklist().enqueue(WTFMove(plan));
213 }
214
215 static EncodedJSValue JSC_HOST_CALL webAssemblyInstantiateFunc(ExecState* exec)
216 {
217     VM& vm = exec->vm();
218     auto catchScope = DECLARE_CATCH_SCOPE(vm);
219
220     JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, exec->lexicalGlobalObject());
221     RETURN_IF_EXCEPTION(catchScope, encodedJSValue());
222
223     JSValue importArgument = exec->argument(1);
224     JSObject* importObject = importArgument.getObject();
225     if (!importArgument.isUndefined() && !importObject) {
226         promise->reject(exec, createTypeError(exec,
227             ASCIILiteral("second argument to WebAssembly.instantiate must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument)));
228         return JSValue::encode(promise->promise());
229     }
230
231     JSValue firstArgument = exec->argument(0);
232     if (auto* module = jsDynamicCast<JSWebAssemblyModule*>(vm, firstArgument))
233         instantiate(vm, exec, promise, module, importObject, Resolve::WithInstance);
234     else
235         compileAndInstantiate(vm, exec, promise, firstArgument, importObject);
236
237     return JSValue::encode(promise->promise());
238 }
239
240 static EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec)
241 {
242     VM& vm = exec->vm();
243     auto scope = DECLARE_THROW_SCOPE(vm);
244
245     size_t byteOffset;
246     size_t byteSize;
247     uint8_t* base = getWasmBufferFromValue(exec, exec->argument(0), byteOffset, byteSize);
248     RETURN_IF_EXCEPTION(scope, encodedJSValue());
249     Wasm::Plan plan(vm, base + byteOffset, byteSize, Plan::Validation, Plan::dontFinalize);
250     // FIXME: We might want to throw an OOM exception here if we detect that something will OOM.
251     // https://bugs.webkit.org/show_bug.cgi?id=166015
252     return JSValue::encode(jsBoolean(plan.parseAndValidateModule()));
253 }
254
255 WebAssemblyPrototype* WebAssemblyPrototype::create(VM& vm, JSGlobalObject*, Structure* structure)
256 {
257     auto* object = new (NotNull, allocateCell<WebAssemblyPrototype>(vm.heap)) WebAssemblyPrototype(vm, structure);
258     object->finishCreation(vm);
259     return object;
260 }
261
262 Structure* WebAssemblyPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
263 {
264     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
265 }
266
267 void WebAssemblyPrototype::finishCreation(VM& vm)
268 {
269     Base::finishCreation(vm);
270 }
271
272 WebAssemblyPrototype::WebAssemblyPrototype(VM& vm, Structure* structure)
273     : Base(vm, structure)
274 {
275 }
276
277 } // namespace JSC
278
279 #endif // ENABLE(WEBASSEMBLY)