WebAssembly: name ExecState consistently
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WebAssemblyInstanceConstructor.cpp
1 /*
2  * Copyright (C) 2016 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 "WebAssemblyInstanceConstructor.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "FunctionPrototype.h"
32 #include "JSCInlines.h"
33 #include "JSModuleEnvironment.h"
34 #include "JSModuleNamespaceObject.h"
35 #include "JSWebAssemblyHelpers.h"
36 #include "JSWebAssemblyInstance.h"
37 #include "JSWebAssemblyLinkError.h"
38 #include "JSWebAssemblyMemory.h"
39 #include "JSWebAssemblyModule.h"
40 #include "WebAssemblyFunction.h"
41 #include "WebAssemblyInstancePrototype.h"
42 #include "WebAssemblyModuleRecord.h"
43
44 #include "WebAssemblyInstanceConstructor.lut.h"
45
46 namespace JSC {
47
48 static const bool verbose = false;
49
50 const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyInstance, CREATE_METHOD_TABLE(WebAssemblyInstanceConstructor) };
51
52 /* Source for WebAssemblyInstanceConstructor.lut.h
53  @begin constructorTableWebAssemblyInstance
54  @end
55  */
56
57 static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* exec)
58 {
59     auto& vm = exec->vm();
60     auto throwScope = DECLARE_THROW_SCOPE(vm);
61
62     // If moduleObject is not a WebAssembly.Module instance, a TypeError is thrown.
63     JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->argument(0));
64     if (!jsModule)
65         return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(exec->argument(0)))));
66
67     // If the importObject parameter is not undefined and Type(importObject) is not Object, a TypeError is thrown.
68     JSValue importArgument = exec->argument(1);
69     JSObject* importObject = importArgument.getObject();
70     if (!importArgument.isUndefined() && !importObject)
71         return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument))));
72     
73     Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), exec->lexicalGlobalObject()->WebAssemblyInstanceStructure());
74     RETURN_IF_EXCEPTION(throwScope, { });
75
76     throwScope.release();
77     return JSValue::encode(WebAssemblyInstanceConstructor::createInstance(exec, jsModule, importObject, instanceStructure));
78 }
79
80 JSWebAssemblyInstance* WebAssemblyInstanceConstructor::createInstance(ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure)
81 {
82     auto& vm = exec->vm();
83     auto throwScope = DECLARE_THROW_SCOPE(vm);
84     auto* globalObject = exec->lexicalGlobalObject();
85
86     const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
87
88     auto exception = [&] (JSObject* error) {
89         throwException(exec, throwScope, error);
90         return nullptr;
91     };
92
93     // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
94     if (moduleInformation.imports.size() && !importObject)
95         return exception(createTypeError(exec, ASCIILiteral("can't make WebAssembly.Instance because there is no imports Object and the WebAssembly.Module requires imports")));
96
97     Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
98     WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
99     RETURN_IF_EXCEPTION(throwScope, nullptr);
100
101
102     JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec));
103     RETURN_IF_EXCEPTION(throwScope, nullptr);
104
105     // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
106     // Let imports be an initially-empty list of external values.
107     unsigned numImportFunctions = 0;
108     unsigned numImportGlobals = 0;
109
110     bool hasMemoryImport = false;
111     bool hasTableImport = false;
112     // For each import i in module.imports:
113     for (auto& import : moduleInformation.imports) {
114         // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
115         JSValue importModuleValue = importObject->get(exec, import.module);
116         RETURN_IF_EXCEPTION(throwScope, nullptr);
117         // 2. If Type(o) is not Object, throw a TypeError.
118         if (!importModuleValue.isObject())
119             return exception(createTypeError(exec, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue)));
120
121         // 3. Let v be the value of performing Get(o, i.item_name)
122         JSObject* object = jsCast<JSObject*>(importModuleValue);
123         JSValue value = object->get(exec, import.field);
124         RETURN_IF_EXCEPTION(throwScope, nullptr);
125
126         switch (import.kind) {
127         case Wasm::ExternalKind::Function: {
128             // 4. If i is a function import:
129             // i. If IsCallable(v) is false, throw a WebAssembly.LinkError.
130             if (!value.isFunction())
131                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("import function must be callable")));
132
133             JSObject* function = jsCast<JSObject*>(value);
134             // ii. If v is an Exported Function Exotic Object:
135             WebAssemblyFunction* wasmFunction;
136             WebAssemblyWrapperFunction* wasmWrapperFunction;
137             if (isWebAssemblyHostFunction(vm, function, wasmFunction, wasmWrapperFunction)) {
138                 // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
139                 Wasm::SignatureIndex importedSignatureIndex;
140                 if (wasmFunction)
141                     importedSignatureIndex = wasmFunction->signatureIndex();
142                 else {
143                     importedSignatureIndex = wasmWrapperFunction->signatureIndex();
144                     // b. Let closure be v.[[Closure]].
145                     function = wasmWrapperFunction->function();
146                 }
147                 Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
148                 if (importedSignatureIndex != expectedSignatureIndex)
149                     return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported function's signature doesn't match the provided WebAssembly function's signature")));
150             }
151             // iii. Otherwise:
152             // a. Let closure be a new host function of the given signature which calls v by coercing WebAssembly arguments to JavaScript arguments via ToJSValue and returns the result, if any, by coercing via ToWebAssemblyValue.
153             // Note: done as part of Plan compilation.
154             // iv. Append v to funcs.
155             // Note: adding the JSCell to the instance list fulfills closure requirements b. above (the WebAssembly.Instance wil be kept alive) and v. below (the JSFunction).
156
157             ASSERT(numImportFunctions == import.kindIndex);
158             instance->setImportFunction(vm, function, numImportFunctions++);
159             // v. Append closure to imports.
160             break;
161         }
162         case Wasm::ExternalKind::Table: {
163             RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
164             // 7. Otherwise (i is a table import):
165             hasTableImport = true;
166             JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
167             // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
168             if (!table)
169                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import is not an instance of WebAssembly.Table")));
170
171             uint32_t expectedInitial = moduleInformation.tableInformation.initial();
172             uint32_t actualInitial = table->size();
173             if (actualInitial < expectedInitial)
174                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import provided an 'initial' that is too small")));
175
176             if (std::optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
177                 std::optional<uint32_t> actualMaximum = table->maximum();
178                 if (!actualMaximum)
179                     return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import does not have a 'maximum' but the module requires that it does")));
180                 if (*actualMaximum > *expectedMaximum)
181                     return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Imported Table's 'maximum' is larger than the module's expected 'maximum'")));
182             }
183
184             // ii. Append v to tables.
185             // iii. Append v.[[Table]] to imports.
186             instance->setTable(vm, table);
187             break;
188         }
189         case Wasm::ExternalKind::Memory: {
190             // 6. If i is a memory import:
191             RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
192             RELEASE_ASSERT(moduleInformation.memory);
193             hasMemoryImport = true;
194             JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value);
195             // i. If v is not a WebAssembly.Memory object, throw a WebAssembly.LinkError.
196             if (!memory)
197                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory")));
198
199             Wasm::PageCount expectedInitial = moduleInformation.memory.initial();
200             Wasm::PageCount actualInitial = memory->memory().initial();
201             if (actualInitial < expectedInitial)
202                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided an 'initial' that is too small")));
203
204             if (Wasm::PageCount expectedMaximum = moduleInformation.memory.maximum()) {
205                 Wasm::PageCount actualMaximum = memory->memory().maximum();
206                 if (!actualMaximum)
207                     return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does")));
208
209                 if (actualMaximum > expectedMaximum)
210                     return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory imports 'maximum' is larger than the module's expected 'maximum'")));
211             }
212
213             // ii. Append v to memories.
214             // iii. Append v.[[Memory]] to imports.
215             instance->setMemory(vm, exec, memory);
216             RETURN_IF_EXCEPTION(throwScope, nullptr);
217             break;
218         }
219         case Wasm::ExternalKind::Global: {
220             // 5. If i is a global import:
221             // i. If i is not an immutable global, throw a TypeError.
222             ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
223             // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError.
224             if (moduleInformation.globals[import.kindIndex].type == Wasm::I64)
225                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global cannot be an i64")));
226             if (!value.isNumber())
227                 return exception(createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global must be a number")));
228             // iii. Append ToWebAssemblyValue(v) to imports.
229             ASSERT(numImportGlobals == import.kindIndex);
230             switch (moduleInformation.globals[import.kindIndex].type) {
231             case Wasm::I32:
232                 instance->setGlobal(numImportGlobals++, value.toInt32(exec));
233                 break;
234             case Wasm::F32:
235                 instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec)));
236                 break;
237             case Wasm::F64:
238                 instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber()));
239                 break;
240             default:
241                 RELEASE_ASSERT_NOT_REACHED();
242             }
243             ASSERT(!throwScope.exception());
244             break;
245         }
246         }
247     }
248
249     {
250         if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
251             // We should either have a Memory import or we should have thrown an exception.
252             RELEASE_ASSERT(hasMemoryImport);
253         }
254
255         if (moduleInformation.memory && !hasMemoryImport) {
256             RELEASE_ASSERT(!moduleInformation.memory.isImport());
257             // We create a memory when it's a memory definition.
258             RefPtr<Wasm::Memory> memory;
259             if (moduleInformation.memory.hasReservedMemory())
260                 memory = jsModule->takeReservedMemory();
261             else {
262                 memory = Wasm::Memory::create(vm, moduleInformation.memory.initial(), moduleInformation.memory.maximum());
263                 if (!memory)
264                     return exception(createOutOfMemoryError(exec));
265             }
266             instance->setMemory(vm, exec,
267                 JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), memory.releaseNonNull()));
268             RETURN_IF_EXCEPTION(throwScope, nullptr);
269         }
270     }
271
272     {
273         if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
274             // We should either have a Table import or we should have thrown an exception.
275             RELEASE_ASSERT(hasTableImport);
276         }
277
278         if (!!moduleInformation.tableInformation && !hasTableImport) {
279             RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
280             // We create a Table when it's a Table definition.
281             JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(),
282                 moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
283             // We should always be able to allocate a JSWebAssemblyTable we've defined.
284             // If it's defined to be too large, we should have thrown a validation error.
285             ASSERT(!throwScope.exception());
286             ASSERT(table); 
287             instance->setTable(vm, table);
288         }
289     }
290
291     if (!instance->memory()) {
292         // Make sure we have a dummy memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
293         instance->setMemory(vm, exec, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), adoptRef(*(new Wasm::Memory()))));
294     }
295
296     // Globals
297     {
298         ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
299         for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
300             const auto& global = moduleInformation.globals[globalIndex];
301             ASSERT(global.initializationType != Wasm::Global::IsImport);
302             if (global.initializationType == Wasm::Global::FromGlobalImport) {
303                 ASSERT(global.initialBitsOrImportNumber < numImportGlobals);
304                 instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber));
305             } else
306                 instance->setGlobal(globalIndex, global.initialBitsOrImportNumber);
307         }
308     }
309
310     moduleRecord->link(exec, instance);
311     RETURN_IF_EXCEPTION(throwScope, nullptr);
312
313     if (verbose)
314         moduleRecord->dump();
315     JSValue startResult = moduleRecord->evaluate(exec);
316     UNUSED_PARAM(startResult);
317     RETURN_IF_EXCEPTION(throwScope, nullptr);
318
319     return instance;
320 }
321
322 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyInstance(ExecState* exec)
323 {
324     VM& vm = exec->vm();
325     auto scope = DECLARE_THROW_SCOPE(vm);
326     return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(exec, scope, "WebAssembly.Instance"));
327 }
328
329 WebAssemblyInstanceConstructor* WebAssemblyInstanceConstructor::create(VM& vm, Structure* structure, WebAssemblyInstancePrototype* thisPrototype)
330 {
331     auto* constructor = new (NotNull, allocateCell<WebAssemblyInstanceConstructor>(vm.heap)) WebAssemblyInstanceConstructor(vm, structure);
332     constructor->finishCreation(vm, thisPrototype);
333     return constructor;
334 }
335
336 Structure* WebAssemblyInstanceConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
337 {
338     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
339 }
340
341 void WebAssemblyInstanceConstructor::finishCreation(VM& vm, WebAssemblyInstancePrototype* prototype)
342 {
343     Base::finishCreation(vm, ASCIILiteral("Instance"));
344     putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly);
345     putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete);
346 }
347
348 WebAssemblyInstanceConstructor::WebAssemblyInstanceConstructor(VM& vm, Structure* structure)
349     : Base(vm, structure)
350 {
351 }
352
353 ConstructType WebAssemblyInstanceConstructor::getConstructData(JSCell*, ConstructData& constructData)
354 {
355     constructData.native.function = constructJSWebAssemblyInstance;
356     return ConstructType::Host;
357 }
358
359 CallType WebAssemblyInstanceConstructor::getCallData(JSCell*, CallData& callData)
360 {
361     callData.native.function = callJSWebAssemblyInstance;
362     return CallType::Host;
363 }
364
365 void WebAssemblyInstanceConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor)
366 {
367     auto* thisObject = jsCast<WebAssemblyInstanceConstructor*>(cell);
368     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
369     Base::visitChildren(thisObject, visitor);
370 }
371
372 } // namespace JSC
373
374 #endif // ENABLE(WEBASSEMBLY)
375