Unreviewed, rolling out r222791 and r222873.
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / JSWebAssemblyInstance.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 "JSWebAssemblyInstance.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "AbstractModuleRecord.h"
32 #include "JSCInlines.h"
33 #include "JSModuleEnvironment.h"
34 #include "JSModuleNamespaceObject.h"
35 #include "JSWebAssemblyHelpers.h"
36 #include "JSWebAssemblyLinkError.h"
37 #include "JSWebAssemblyMemory.h"
38 #include "JSWebAssemblyModule.h"
39 #include "WebAssemblyModuleRecord.h"
40 #include "WebAssemblyToJSCallee.h"
41 #include <wtf/StdLibExtras.h>
42
43 namespace JSC {
44
45 const ClassInfo JSWebAssemblyInstance::s_info = { "WebAssembly.Instance", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyInstance) };
46
47 Structure* JSWebAssemblyInstance::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
48 {
49     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
50 }
51
52 JSWebAssemblyInstance::JSWebAssemblyInstance(VM& vm, Structure* structure, unsigned numImportFunctions)
53     : Base(vm, structure)
54     , m_vm(&vm)
55     , m_numImportFunctions(numImportFunctions)
56 {
57     memset(importFunctions(), 0, m_numImportFunctions * sizeof(WriteBarrier<JSObject>));
58 }
59
60 void JSWebAssemblyInstance::finishCreation(VM& vm, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
61 {
62     Base::finishCreation(vm);
63     ASSERT(inherits(vm, info()));
64
65     m_module.set(vm, this, module);
66     const size_t extraMemorySize = globalMemoryByteSize();
67     m_globals = MallocPtr<uint64_t>::malloc(extraMemorySize);
68     heap()->reportExtraMemoryAllocated(extraMemorySize);
69
70     m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
71     m_callee.set(vm, this, module->callee());
72 }
73
74 void JSWebAssemblyInstance::destroy(JSCell* cell)
75 {
76     static_cast<JSWebAssemblyInstance*>(cell)->JSWebAssemblyInstance::~JSWebAssemblyInstance();
77 }
78
79 void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
80 {
81     auto* thisObject = jsCast<JSWebAssemblyInstance*>(cell);
82     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
83
84     Base::visitChildren(thisObject, visitor);
85     visitor.append(thisObject->m_module);
86     visitor.append(thisObject->m_codeBlock);
87     visitor.append(thisObject->m_moduleNamespaceObject);
88     visitor.append(thisObject->m_memory);
89     visitor.append(thisObject->m_table);
90     visitor.append(thisObject->m_callee);
91     visitor.reportExtraMemoryVisited(thisObject->globalMemoryByteSize());
92     for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i)
93         visitor.append(thisObject->importFunctions()[i]);
94 }
95
96 void JSWebAssemblyInstance::finalizeCreation(VM& vm, ExecState* exec, Ref<Wasm::CodeBlock>&& wasmCodeBlock)
97 {
98     auto scope = DECLARE_THROW_SCOPE(vm);
99     if (!wasmCodeBlock->runnable()) {
100         throwException(exec, scope, JSWebAssemblyLinkError::create(exec, vm, globalObject()->WebAssemblyLinkErrorStructure(), wasmCodeBlock->errorMessage()));
101         return;
102     }
103
104     RELEASE_ASSERT(wasmCodeBlock->isSafeToRun(memoryMode()));
105     JSWebAssemblyCodeBlock* codeBlock = module()->codeBlock(memoryMode());
106     if (codeBlock) {
107         // A CodeBlock might have already been compiled. If so, it means
108         // that the CodeBlock we are trying to compile must be the same
109         // because we will never compile a CodeBlock again once it's
110         // runnable.
111         ASSERT(&codeBlock->codeBlock() == wasmCodeBlock.ptr());
112         m_codeBlock.set(vm, this, codeBlock);
113     } else {
114         codeBlock = JSWebAssemblyCodeBlock::create(vm, wasmCodeBlock.copyRef(), m_module.get());
115         if (UNLIKELY(!codeBlock->runnable())) {
116             throwException(exec, scope, JSWebAssemblyLinkError::create(exec, vm, globalObject()->WebAssemblyLinkErrorStructure(), codeBlock->errorMessage()));
117             return;
118         }
119         m_codeBlock.set(vm, this, codeBlock);
120         module()->setCodeBlock(vm, memoryMode(), codeBlock);
121     }
122
123     auto* moduleRecord = jsCast<WebAssemblyModuleRecord*>(m_moduleNamespaceObject->moduleRecord());
124     moduleRecord->link(exec, module(), this);
125     RETURN_IF_EXCEPTION(scope, void());
126
127     JSValue startResult = moduleRecord->evaluate(exec);
128     UNUSED_PARAM(startResult);
129     RETURN_IF_EXCEPTION(scope, void());
130 }
131
132 JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, ExecState* exec, JSWebAssemblyModule* jsModule, JSObject* importObject, Structure* instanceStructure)
133 {
134     auto throwScope = DECLARE_THROW_SCOPE(vm);
135     auto* globalObject = exec->lexicalGlobalObject();
136
137     const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation();
138
139     auto exception = [&] (JSObject* error) {
140         throwException(exec, throwScope, error);
141         return nullptr;
142     };
143
144     if (!globalObject->webAssemblyEnabled())
145         return exception(createEvalError(exec, globalObject->webAssemblyDisabledErrorMessage()));
146
147     auto importFailMessage = [&] (const Wasm::Import& import, const char* before, const char* after) {
148         return makeString(before, " ", String::fromUTF8(import.module), ":", String::fromUTF8(import.field), " ", after);
149     };
150
151     // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown.
152     if (moduleInformation.imports.size() && !importObject)
153         return exception(createTypeError(exec, ASCIILiteral("can't make WebAssembly.Instance because there is no imports Object and the WebAssembly.Module requires imports")));
154
155     Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance"));
156     WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation);
157     RETURN_IF_EXCEPTION(throwScope, nullptr);
158
159     JSModuleNamespaceObject* moduleNamespace = moduleRecord->getModuleNamespace(exec);
160     // FIXME: These objects could be pretty big we should try to throw OOM here.
161     auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(moduleInformation.importFunctionCount()))) JSWebAssemblyInstance(vm, instanceStructure, moduleInformation.importFunctionCount());
162     instance->finishCreation(vm, jsModule, moduleNamespace);
163     RETURN_IF_EXCEPTION(throwScope, nullptr);
164
165     // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
166     // Let imports be an initially-empty list of external values.
167     unsigned numImportFunctions = 0;
168     unsigned numImportGlobals = 0;
169
170     bool hasMemoryImport = false;
171     bool hasTableImport = false;
172     // For each import i in module.imports:
173     for (auto& import : moduleInformation.imports) {
174         // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
175         JSValue importModuleValue = importObject->get(exec, Identifier::fromString(&vm, String::fromUTF8(import.module)));
176         RETURN_IF_EXCEPTION(throwScope, nullptr);
177         // 2. If Type(o) is not Object, throw a TypeError.
178         if (!importModuleValue.isObject())
179             return exception(createTypeError(exec, importFailMessage(import, "import", "must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue)));
180
181         // 3. Let v be the value of performing Get(o, i.item_name)
182         JSObject* object = jsCast<JSObject*>(importModuleValue);
183         JSValue value = object->get(exec, Identifier::fromString(&vm, String::fromUTF8(import.field)));
184         RETURN_IF_EXCEPTION(throwScope, nullptr);
185
186         switch (import.kind) {
187         case Wasm::ExternalKind::Function: {
188             // 4. If i is a function import:
189             // i. If IsCallable(v) is false, throw a WebAssembly.LinkError.
190             if (!value.isFunction())
191                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "import function", "must be callable")));
192
193             JSObject* function = jsCast<JSObject*>(value);
194             // ii. If v is an Exported Function Exotic Object:
195             WebAssemblyFunction* wasmFunction;
196             WebAssemblyWrapperFunction* wasmWrapperFunction;
197             if (isWebAssemblyHostFunction(vm, function, wasmFunction, wasmWrapperFunction)) {
198                 // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
199                 Wasm::SignatureIndex importedSignatureIndex;
200                 if (wasmFunction)
201                     importedSignatureIndex = wasmFunction->signatureIndex();
202                 else {
203                     importedSignatureIndex = wasmWrapperFunction->signatureIndex();
204                     // b. Let closure be v.[[Closure]].
205                     function = wasmWrapperFunction->function();
206                 }
207                 Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
208                 if (importedSignatureIndex != expectedSignatureIndex)
209                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported function", "signature doesn't match the provided WebAssembly function's signature")));
210             }
211             // iii. Otherwise:
212             // 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.
213             // Note: done as part of Plan compilation.
214             // iv. Append v to funcs.
215             // 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).
216
217             ASSERT(numImportFunctions == import.kindIndex);
218             instance->importFunctions()[numImportFunctions++].set(vm, instance, function);
219             // v. Append closure to imports.
220             break;
221         }
222         case Wasm::ExternalKind::Table: {
223             RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
224             // 7. Otherwise (i is a table import):
225             hasTableImport = true;
226             JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
227             // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
228             if (!table)
229                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "is not an instance of WebAssembly.Table")));
230
231             uint32_t expectedInitial = moduleInformation.tableInformation.initial();
232             uint32_t actualInitial = table->size();
233             if (actualInitial < expectedInitial)
234                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided an 'initial' that is too small")));
235
236             if (std::optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
237                 std::optional<uint32_t> actualMaximum = table->maximum();
238                 if (!actualMaximum)
239                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "does not have a 'maximum' but the module requires that it does")));
240                 if (*actualMaximum > *expectedMaximum)
241                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Imported Table", "'maximum' is larger than the module's expected 'maximum'")));
242             }
243
244             // ii. Append v to tables.
245             // iii. Append v.[[Table]] to imports.
246             instance->m_table.set(vm, instance, table);
247             break;
248         }
249
250         case Wasm::ExternalKind::Memory: {
251             // 6. If i is a memory import:
252             RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure.
253             RELEASE_ASSERT(moduleInformation.memory);
254             hasMemoryImport = true;
255             JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value);
256             // i. If v is not a WebAssembly.Memory object, throw a WebAssembly.LinkError.
257             if (!memory)
258                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Memory import", "is not an instance of WebAssembly.Memory")));
259
260             Wasm::PageCount declaredInitial = moduleInformation.memory.initial();
261             Wasm::PageCount importedInitial = memory->memory().initial();
262             if (importedInitial < declaredInitial)
263                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Memory import", "provided an 'initial' that is smaller than the module's declared 'initial' import memory size")));
264
265             if (Wasm::PageCount declaredMaximum = moduleInformation.memory.maximum()) {
266                 Wasm::PageCount importedMaximum = memory->memory().maximum();
267                 if (!importedMaximum)
268                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Memory import", "did not have a 'maximum' but the module requires that it does")));
269
270                 if (importedMaximum > declaredMaximum)
271                     return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Memory import", "provided a 'maximum' that is larger than the module's declared 'maximum' import memory size")));
272             }
273
274             // ii. Append v to memories.
275             // iii. Append v.[[Memory]] to imports.
276             ASSERT(!instance->m_memory);
277             instance->m_memory.set(vm, instance, memory);
278             RETURN_IF_EXCEPTION(throwScope, nullptr);
279             break;
280         }
281         case Wasm::ExternalKind::Global: {
282             // 5. If i is a global import:
283             // i. If i is not an immutable global, throw a TypeError.
284             ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
285             // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError.
286             if (moduleInformation.globals[import.kindIndex].type == Wasm::I64)
287                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "cannot be an i64")));
288             if (!value.isNumber())
289                 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "must be a number")));
290             // iii. Append ToWebAssemblyValue(v) to imports.
291             ASSERT(numImportGlobals == import.kindIndex);
292             switch (moduleInformation.globals[import.kindIndex].type) {
293             case Wasm::I32:
294                 instance->setGlobal(numImportGlobals++, value.toInt32(exec));
295                 break;
296             case Wasm::F32:
297                 instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec)));
298                 break;
299             case Wasm::F64:
300                 instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber()));
301                 break;
302             default:
303                 RELEASE_ASSERT_NOT_REACHED();
304             }
305             throwScope.assertNoException();
306             break;
307         }
308         }
309     }
310
311     {
312         if (!!moduleInformation.memory && moduleInformation.memory.isImport()) {
313             // We should either have a Memory import or we should have thrown an exception.
314             RELEASE_ASSERT(hasMemoryImport);
315         }
316
317         if (moduleInformation.memory && !hasMemoryImport) {
318             RELEASE_ASSERT(!moduleInformation.memory.isImport());
319             // We create a memory when it's a memory definition.
320             RefPtr<Wasm::Memory> memory = Wasm::Memory::create(vm, moduleInformation.memory.initial(), moduleInformation.memory.maximum());
321             if (!memory)
322                 return exception(createOutOfMemoryError(exec));
323
324             instance->m_memory.set(vm, instance,
325                 JSWebAssemblyMemory::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), memory.releaseNonNull()));
326             RETURN_IF_EXCEPTION(throwScope, nullptr);
327         }
328     }
329
330     {
331         if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
332             // We should either have a Table import or we should have thrown an exception.
333             RELEASE_ASSERT(hasTableImport);
334         }
335
336         if (!!moduleInformation.tableInformation && !hasTableImport) {
337             RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
338             // We create a Table when it's a Table definition.
339             JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(),
340                 moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
341             // We should always be able to allocate a JSWebAssemblyTable we've defined.
342             // If it's defined to be too large, we should have thrown a validation error.
343             throwScope.assertNoException();
344             ASSERT(table);
345             instance->m_table.set(vm, instance, table);
346         }
347     }
348     
349     if (!instance->memory()) {
350         // Make sure we have a dummy memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
351         instance->m_memory.set(vm, instance, JSWebAssemblyMemory::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), Wasm::Memory::create(vm, 0, 0).releaseNonNull()));
352         RETURN_IF_EXCEPTION(throwScope, nullptr);
353     }
354     
355     // Globals
356     {
357         ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
358         for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
359             const auto& global = moduleInformation.globals[globalIndex];
360             ASSERT(global.initializationType != Wasm::Global::IsImport);
361             if (global.initializationType == Wasm::Global::FromGlobalImport) {
362                 ASSERT(global.initialBitsOrImportNumber < numImportGlobals);
363                 instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber));
364             } else
365                 instance->setGlobal(globalIndex, global.initialBitsOrImportNumber);
366         }
367     }
368
369     ASSERT(!instance->codeBlock());
370
371     return instance;
372 }
373
374 size_t JSWebAssemblyInstance::globalMemoryByteSize() const
375 {
376     return m_module->moduleInformation().globals.size() * sizeof(Register);
377 }
378
379 } // namespace JSC
380
381 #endif // ENABLE(WEBASSEMBLY)