WebAssembly: implement data section
[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 "JSWebAssemblyInstance.h"
36 #include "JSWebAssemblyModule.h"
37 #include "ProtoCallFrame.h"
38 #include "WasmFormat.h"
39 #include "WebAssemblyFunction.h"
40 #include <limits>
41
42 namespace JSC {
43
44 const ClassInfo WebAssemblyModuleRecord::s_info = { "WebAssemblyModuleRecord", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyModuleRecord) };
45
46 Structure* WebAssemblyModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
47 {
48     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
49 }
50
51 WebAssemblyModuleRecord* WebAssemblyModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const Wasm::ModuleInformation& moduleInformation)
52 {
53     WebAssemblyModuleRecord* instance = new (NotNull, allocateCell<WebAssemblyModuleRecord>(vm.heap)) WebAssemblyModuleRecord(vm, structure, moduleKey);
54     instance->finishCreation(exec, vm, moduleInformation);
55     return instance;
56 }
57
58 WebAssemblyModuleRecord::WebAssemblyModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey)
59     : Base(vm, structure, moduleKey)
60 {
61 }
62
63 void WebAssemblyModuleRecord::destroy(JSCell* cell)
64 {
65     WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
66     thisObject->WebAssemblyModuleRecord::~WebAssemblyModuleRecord();
67 }
68
69 void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm::ModuleInformation& moduleInformation)
70 {
71     Base::finishCreation(exec, vm);
72     ASSERT(inherits(info()));
73     for (const auto& exp : moduleInformation.exports) {
74         switch (exp.kind) {
75         case Wasm::External::Function: {
76             addExportEntry(ExportEntry::createLocal(exp.field, exp.field));
77             break;
78         }
79         case Wasm::External::Table: {
80             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
81             break;
82         }
83         case Wasm::External::Memory: {
84             // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
85             break;
86         }
87         case Wasm::External::Global: {
88             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
89             // In the MVP, only immutable global variables can be exported.
90             break;
91         }
92         }
93     }
94 }
95
96 void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
97 {
98     WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
99     Base::visitChildren(thisObject, visitor);
100     visitor.append(&thisObject->m_instance);
101     visitor.append(&thisObject->m_startFunction);
102 }
103
104 void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* instance)
105 {
106     VM& vm = state->vm();
107     auto scope = DECLARE_THROW_SCOPE(vm);
108     UNUSED_PARAM(scope);
109     auto* globalObject = state->lexicalGlobalObject();
110
111     JSWebAssemblyModule* module = instance->module();
112     const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
113
114     bool hasStart = !!moduleInformation.startFunctionIndexSpace;
115     auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.value_or(0);
116
117     SymbolTable* exportSymbolTable = module->exportSymbolTable();
118     unsigned importCount = module->importCount();
119
120     // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118
121
122     // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
123     JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this);
124     for (const auto& exp : moduleInformation.exports) {
125         JSValue exportedValue;
126         switch (exp.kind) {
127         case Wasm::External::Function: {
128             // 1. If e is a closure c:
129             //   i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
130             //   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.)
131             if (exp.functionIndex < importCount) {
132                 // FIXME Implement re-exporting an import. https://bugs.webkit.org/show_bug.cgi?id=165510
133                 RELEASE_ASSERT_NOT_REACHED();
134             }
135             //   iii. Otherwise:
136             //     a. Let func be an Exported Function Exotic Object created from c.
137             //     b. Append func to funcs.
138             //     c. Return func.
139             JSWebAssemblyCallee* wasmCallee = module->calleeFromFunctionIndexSpace(exp.functionIndex);
140             Wasm::Signature* signature = module->signatureForFunctionIndexSpace(exp.functionIndex);
141             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), exp.field.string(), instance, wasmCallee, signature);
142             exportedValue = function;
143             if (hasStart && startFunctionIndexSpace == exp.functionIndex)
144                 m_startFunction.set(vm, this, function);
145             break;
146         }
147         case Wasm::External::Table: {
148             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164135
149             break;
150         }
151         case Wasm::External::Memory: {
152             // FIXME: https://bugs.webkit.org/show_bug.cgi?id=165671
153             break;
154         }
155         case Wasm::External::Global: {
156             // FIXME https://bugs.webkit.org/show_bug.cgi?id=164133
157             // In the MVP, only immutable global variables can be exported.
158             break;
159         }
160         }
161
162         bool shouldThrowReadOnlyError = false;
163         bool ignoreReadOnlyErrors = true;
164         bool putResult = false;
165         symbolTablePutTouchWatchpointSet(moduleEnvironment, state, exp.field, exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
166         RELEASE_ASSERT(putResult);
167     }
168
169     if (hasStart) {
170         Wasm::Signature* signature = module->signatureForFunctionIndexSpace(startFunctionIndexSpace);
171         // The start function must not take any arguments or return anything. This is enforced by the parser.
172         ASSERT(!signature->arguments.size());
173         ASSERT(signature->returnType == Wasm::Void);
174         // FIXME can start call imports / tables? This assumes not. https://github.com/WebAssembly/design/issues/896
175         if (!m_startFunction.get()) {
176             // The start function wasn't added above. It must be a purely internal function.
177             JSWebAssemblyCallee* wasmCallee = module->calleeFromFunctionIndexSpace(startFunctionIndexSpace);
178             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->arguments.size(), "start", instance, wasmCallee, signature);
179             m_startFunction.set(vm, this, function);
180         }
181     }
182
183     RELEASE_ASSERT(!m_instance);
184     m_instance.set(vm, this, instance);
185     m_moduleEnvironment.set(vm, this, moduleEnvironment);
186 }
187
188 JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
189 {
190     VM& vm = state->vm();
191     auto scope = DECLARE_THROW_SCOPE(vm);
192
193     if (JSWebAssemblyMemory* jsMemory = m_instance->memory()) {
194         uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory());
195         auto sizeInBytes = jsMemory->memory()->size();
196         if (memory) {
197             const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data;
198             for (auto& segment : data) {
199                 if (segment->sizeInBytes) {
200                     if (sizeInBytes < segment->sizeInBytes
201                         || segment->offset > sizeInBytes
202                         || segment->offset > sizeInBytes - segment->sizeInBytes)
203                         return throwException(state, scope, createRangeError(state, ASCIILiteral("Data segment initializes memory out of range")));
204                     memcpy(memory + segment->offset, &segment->byte(0), segment->sizeInBytes);
205                 }
206             }
207         }
208     }
209
210     if (WebAssemblyFunction* startFunction = m_startFunction.get()) {
211         ProtoCallFrame protoCallFrame;
212         protoCallFrame.init(nullptr, startFunction, JSValue(), 1, nullptr);
213         startFunction->call(vm, &protoCallFrame);
214         RETURN_IF_EXCEPTION(scope, { });
215     }
216     return jsUndefined();
217 }
218
219 } // namespace JSC
220
221 #endif // ENABLE(WEBASSEMBLY)