WebAssembly: name ExecState consistently
[WebKit-https.git] / Source / JavaScriptCore / wasm / js / WebAssemblyModuleRecord.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 "WebAssemblyModuleRecord.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "Error.h"
32 #include "JSCInlines.h"
33 #include "JSLexicalEnvironment.h"
34 #include "JSModuleEnvironment.h"
35 #include "JSWebAssemblyHelpers.h"
36 #include "JSWebAssemblyInstance.h"
37 #include "JSWebAssemblyLinkError.h"
38 #include "JSWebAssemblyModule.h"
39 #include "ProtoCallFrame.h"
40 #include "WasmFormat.h"
41 #include "WasmSignature.h"
42 #include "WebAssemblyFunction.h"
43 #include <limits>
44
45 namespace JSC {
46
47 const ClassInfo WebAssemblyModuleRecord::s_info = { "WebAssemblyModuleRecord", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyModuleRecord) };
48
49 Structure* WebAssemblyModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
50 {
51     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
52 }
53
54 WebAssemblyModuleRecord* WebAssemblyModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const Wasm::ModuleInformation& moduleInformation)
55 {
56     WebAssemblyModuleRecord* instance = new (NotNull, allocateCell<WebAssemblyModuleRecord>(vm.heap)) WebAssemblyModuleRecord(vm, structure, moduleKey);
57     instance->finishCreation(exec, vm, moduleInformation);
58     return instance;
59 }
60
61 WebAssemblyModuleRecord::WebAssemblyModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey)
62     : Base(vm, structure, moduleKey)
63 {
64 }
65
66 void WebAssemblyModuleRecord::destroy(JSCell* cell)
67 {
68     WebAssemblyModuleRecord* thisObject = static_cast<WebAssemblyModuleRecord*>(cell);
69     thisObject->WebAssemblyModuleRecord::~WebAssemblyModuleRecord();
70 }
71
72 void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm::ModuleInformation& moduleInformation)
73 {
74     Base::finishCreation(exec, vm);
75     ASSERT(inherits(vm, info()));
76     for (const auto& exp : moduleInformation.exports)
77         addExportEntry(ExportEntry::createLocal(exp.field, exp.field));
78 }
79
80 void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
81 {
82     WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
83     Base::visitChildren(thisObject, visitor);
84     visitor.append(thisObject->m_instance);
85     visitor.append(thisObject->m_startFunction);
86 }
87
88 void WebAssemblyModuleRecord::link(ExecState* exec, JSWebAssemblyInstance* instance)
89 {
90     VM& vm = exec->vm();
91     auto scope = DECLARE_THROW_SCOPE(vm);
92     UNUSED_PARAM(scope);
93     auto* globalObject = exec->lexicalGlobalObject();
94
95     JSWebAssemblyModule* module = instance->module();
96     JSWebAssemblyCodeBlock* codeBlock = instance->codeBlock();
97     const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
98
99     SymbolTable* exportSymbolTable = module->exportSymbolTable();
100     unsigned functionImportCount = codeBlock->functionImportCount();
101
102     // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118
103
104     // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
105     JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this);
106     for (const auto& exp : moduleInformation.exports) {
107         JSValue exportedValue;
108         switch (exp.kind) {
109         case Wasm::ExternalKind::Function: {
110             // 1. If e is a closure c:
111             //   i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
112             //   ii. (Note: At most one wrapper is created for any closure, so func is unique, even if there are multiple occurrances in the list. Moreover, if the item was an import that is already an Exported Function Exotic Object, then the original function object will be found. For imports that are regular JS functions, a new wrapper will be created.)
113             if (exp.kindIndex < functionImportCount) {
114                 unsigned functionIndex = exp.kindIndex;
115                 JSObject* functionImport = instance->importFunction(functionIndex)->get();
116                 if (isWebAssemblyHostFunction(vm, functionImport))
117                     exportedValue = functionImport;
118                 else {
119                     Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
120                     exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, functionImport, functionIndex, codeBlock, signatureIndex);
121                 }
122             } else {
123                 //   iii. Otherwise:
124                 //     a. Let func be an Exported Function Exotic Object created from c.
125                 //     b. Append func to funcs.
126                 //     c. Return func.
127                 JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
128                 JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
129                 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
130                 const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
131                 WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
132                 exportedValue = function;
133             }
134             break;
135         }
136         case Wasm::ExternalKind::Table: {
137             // This should be guaranteed by module verification.
138             RELEASE_ASSERT(instance->table()); 
139             ASSERT(exp.kindIndex == 0);
140
141             exportedValue = instance->table();
142             break;
143         }
144         case Wasm::ExternalKind::Memory: {
145             ASSERT(exp.kindIndex == 0);
146
147             exportedValue = instance->memory();
148             break;
149         }
150         case Wasm::ExternalKind::Global: {
151             // Assert: the global is immutable by MVP validation constraint.
152             const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
153             ASSERT(global.mutability == Wasm::Global::Immutable);
154             // Return ToJSValue(v).
155             switch (global.type) {
156             case Wasm::I32:
157                 exportedValue = JSValue(instance->loadI32Global(exp.kindIndex));
158                 break;
159
160             case Wasm::I64:
161                 throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("exported global cannot be an i64")));
162                 return;
163
164             case Wasm::F32:
165                 exportedValue = JSValue(instance->loadF32Global(exp.kindIndex));
166                 break;
167
168             case Wasm::F64:
169                 exportedValue = JSValue(instance->loadF64Global(exp.kindIndex));
170                 break;
171
172             default:
173                 RELEASE_ASSERT_NOT_REACHED();
174             }
175             break;
176         }
177         }
178
179         bool shouldThrowReadOnlyError = false;
180         bool ignoreReadOnlyErrors = true;
181         bool putResult = false;
182         symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, exp.field, exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
183         RELEASE_ASSERT(putResult);
184     }
185
186     bool hasStart = !!moduleInformation.startFunctionIndexSpace;
187     if (hasStart) {
188         auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.value_or(0);
189         Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(startFunctionIndexSpace);
190         const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
191         // The start function must not take any arguments or return anything. This is enforced by the parser.
192         ASSERT(!signature->argumentCount());
193         ASSERT(signature->returnType() == Wasm::Void);
194         if (startFunctionIndexSpace < codeBlock->functionImportCount()) {
195             JSObject* startFunction = instance->importFunction(startFunctionIndexSpace)->get();
196             m_startFunction.set(vm, this, startFunction);
197         } else {
198             JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
199             JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
200             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), "start", instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
201             m_startFunction.set(vm, this, function);
202         }
203     }
204
205     RELEASE_ASSERT(!m_instance);
206     m_instance.set(vm, this, instance);
207     m_moduleEnvironment.set(vm, this, moduleEnvironment);
208 }
209
210 template <typename Scope, typename M, typename N, typename ...Args>
211 NEVER_INLINE static JSValue dataSegmentFail(ExecState* exec, VM& vm, Scope& scope, M memorySize, N segmentSize, N offset, Args... args)
212 {
213     return throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, makeString(ASCIILiteral("Invalid data segment initialization: segment of "), String::number(segmentSize), ASCIILiteral(" bytes memory of "), String::number(memorySize), ASCIILiteral(" bytes, at offset "), String::number(offset), args...)));
214 }
215
216 JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
217 {
218     VM& vm = exec->vm();
219     auto scope = DECLARE_THROW_SCOPE(vm);
220
221     {
222         JSWebAssemblyModule* module = m_instance->module();
223         JSWebAssemblyCodeBlock* codeBlock = m_instance->codeBlock();
224         const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
225         JSWebAssemblyTable* table = m_instance->table();
226         for (const Wasm::Element& element : moduleInformation.elements) {
227             // It should be a validation error to have any elements without a table.
228             // Also, it could be that a table wasn't imported, or that the table
229             // imported wasn't compatible. However, those should error out before
230             // getting here.
231             ASSERT(!!table);
232             if (!element.functionIndices.size())
233                 continue;
234
235             uint32_t tableIndex;
236
237             if (element.offset.isGlobalImport())
238                 tableIndex = static_cast<uint32_t>(m_instance->loadI32Global(element.offset.globalImportIndex()));
239             else
240                 tableIndex = element.offset.constValue();
241
242             uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1;
243             if (lastWrittenIndex >= table->size())
244                 return throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Element is trying to set an out of bounds table index")));
245
246             for (uint32_t i = 0; i < element.functionIndices.size(); ++i) {
247                 // FIXME: This essentially means we're exporting an import.
248                 // We need a story here. We need to create a WebAssemblyFunction
249                 // for the import.
250                 // https://bugs.webkit.org/show_bug.cgi?id=165510
251                 uint32_t functionIndex = element.functionIndices[i];
252                 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
253                 if (functionIndex < codeBlock->functionImportCount()) {
254                     JSObject* functionImport = jsCast<JSObject*>(m_instance->importFunction(functionIndex)->get());
255                     if (isWebAssemblyHostFunction(vm, functionImport)) {
256                         WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, functionImport);
257                         // If we ever import a WebAssemblyWrapperFunction, we set the import as the unwrapped value.
258                         // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction,
259                         // the only type this could be is WebAssemblyFunction.
260                         RELEASE_ASSERT(wasmFunction);
261                         table->setFunction(vm, tableIndex, wasmFunction);
262                         ++tableIndex;
263                         continue;
264                     }
265
266                     table->setFunction(vm, tableIndex,
267                         WebAssemblyWrapperFunction::create(vm, m_instance->globalObject(), functionImport, functionIndex, codeBlock, signatureIndex));
268                     ++tableIndex;
269                     continue;
270                 }
271
272                 JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(functionIndex);
273                 JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(functionIndex);
274                 const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
275                 // FIXME: Say we export local function "foo" at function index 0.
276                 // What if we also set it to the table an Element w/ index 0.
277                 // Does (new Instance(...)).exports.foo === table.get(0)?
278                 // https://bugs.webkit.org/show_bug.cgi?id=165825
279                 WebAssemblyFunction* function = WebAssemblyFunction::create(
280                     vm, m_instance->globalObject(), signature->argumentCount(), String(), m_instance.get(), jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
281
282                 table->setFunction(vm, tableIndex, function);
283                 ++tableIndex;
284             }
285         }
286     }
287
288     {
289         const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data;
290         JSWebAssemblyMemory* jsMemory = m_instance->memory();
291         if (!data.isEmpty()) {
292             uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory().memory());
293             uint64_t sizeInBytes = jsMemory->memory().size();
294             for (auto& segment : data) {
295                 if (segment->sizeInBytes) {
296                     uint32_t offset;
297                     if (segment->offset.isGlobalImport())
298                         offset = static_cast<uint32_t>(m_instance->loadI32Global(segment->offset.globalImportIndex()));
299                     else
300                         offset = segment->offset.constValue();
301
302                     if (UNLIKELY(sizeInBytes < segment->sizeInBytes))
303                         return dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment is too big"));
304                     if (UNLIKELY(offset > sizeInBytes - segment->sizeInBytes))
305                         return dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment writes outside of memory"));
306                     RELEASE_ASSERT(memory);
307                     memcpy(memory + offset, &segment->byte(0), segment->sizeInBytes);
308                 }
309             }
310         }
311     }
312
313     if (JSObject* startFunction = m_startFunction.get()) {
314         CallData callData;
315         CallType callType = JSC::getCallData(startFunction, callData);
316         call(exec, startFunction, callType, callData, jsUndefined(), exec->emptyList());
317         RETURN_IF_EXCEPTION(scope, { });
318     }
319
320     return jsUndefined();
321 }
322
323 } // namespace JSC
324
325 #endif // ENABLE(WEBASSEMBLY)