1/*
2 * Copyright (C) 2016-2019 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 "WasmSignatureInlines.h"
41#include "WebAssemblyFunction.h"
42#include <limits>
43
44namespace JSC {
45
46const ClassInfo WebAssemblyModuleRecord::s_info = { "WebAssemblyModuleRecord", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(WebAssemblyModuleRecord) };
47
48Structure* WebAssemblyModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
49{
50 return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
51}
52
53WebAssemblyModuleRecord* WebAssemblyModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const Wasm::ModuleInformation& moduleInformation)
54{
55 WebAssemblyModuleRecord* instance = new (NotNull, allocateCell<WebAssemblyModuleRecord>(vm.heap)) WebAssemblyModuleRecord(vm, structure, moduleKey);
56 instance->finishCreation(exec, vm, moduleInformation);
57 return instance;
58}
59
60WebAssemblyModuleRecord::WebAssemblyModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey)
61 : Base(vm, structure, moduleKey)
62{
63}
64
65void WebAssemblyModuleRecord::destroy(JSCell* cell)
66{
67 WebAssemblyModuleRecord* thisObject = static_cast<WebAssemblyModuleRecord*>(cell);
68 thisObject->WebAssemblyModuleRecord::~WebAssemblyModuleRecord();
69}
70
71void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm::ModuleInformation& moduleInformation)
72{
73 Base::finishCreation(exec, vm);
74 ASSERT(inherits(vm, info()));
75 for (const auto& exp : moduleInformation.exports) {
76 Identifier field = Identifier::fromString(&vm, String::fromUTF8(exp.field));
77 addExportEntry(ExportEntry::createLocal(field, field));
78 }
79}
80
81void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor)
82{
83 WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell);
84 Base::visitChildren(thisObject, visitor);
85 visitor.append(thisObject->m_instance);
86 visitor.append(thisObject->m_startFunction);
87}
88
89void WebAssemblyModuleRecord::prepareLink(VM& vm, JSWebAssemblyInstance* instance)
90{
91 RELEASE_ASSERT(!m_instance);
92 m_instance.set(vm, this, instance);
93}
94
95void WebAssemblyModuleRecord::link(ExecState* exec, JSValue, JSObject* importObject, Wasm::CreationMode creationMode)
96{
97 VM& vm = exec->vm();
98 auto scope = DECLARE_THROW_SCOPE(vm);
99 UNUSED_PARAM(scope);
100 auto* globalObject = exec->lexicalGlobalObject();
101
102 RELEASE_ASSERT(m_instance);
103
104 Wasm::CodeBlock* codeBlock = m_instance->instance().codeBlock();
105 JSWebAssemblyModule* module = m_instance->module();
106 const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
107
108 auto exception = [&] (JSObject* error) {
109 throwException(exec, scope, error);
110 };
111
112 auto importFailMessage = [&] (const Wasm::Import& import, const char* before, const char* after) {
113 return makeString(before, " ", String::fromUTF8(import.module), ":", String::fromUTF8(import.field), " ", after);
114 };
115
116 bool hasTableImport = false;
117
118 for (const auto& import : moduleInformation.imports) {
119 // Validation and linking other than Wasm::ExternalKind::Function is already done in JSWebAssemblyInstance.
120 // Eventually we will move all the linking code in JSWebAssemblyInstance here and remove this switch statement.
121 switch (import.kind) {
122 case Wasm::ExternalKind::Function:
123 case Wasm::ExternalKind::Global:
124 case Wasm::ExternalKind::Table:
125 break;
126 case Wasm::ExternalKind::Memory:
127 continue;
128 }
129
130 Identifier moduleName = Identifier::fromString(&vm, String::fromUTF8(import.module));
131 Identifier fieldName = Identifier::fromString(&vm, String::fromUTF8(import.field));
132 JSValue value;
133 if (creationMode == Wasm::CreationMode::FromJS) {
134 // 1. Let o be the resultant value of performing Get(importObject, i.module_name).
135 JSValue importModuleValue = importObject->get(exec, moduleName);
136 RETURN_IF_EXCEPTION(scope, void());
137 // 2. If Type(o) is not Object, throw a TypeError.
138 if (!importModuleValue.isObject())
139 return exception(createTypeError(exec, importFailMessage(import, "import", "must be an object"), defaultSourceAppender, runtimeTypeForValue(vm, importModuleValue)));
140
141 // 3. Let v be the value of performing Get(o, i.item_name)
142 JSObject* object = jsCast<JSObject*>(importModuleValue);
143 value = object->get(exec, fieldName);
144 RETURN_IF_EXCEPTION(scope, void());
145 } else {
146 AbstractModuleRecord* importedModule = hostResolveImportedModule(exec, moduleName);
147 RETURN_IF_EXCEPTION(scope, void());
148 Resolution resolution = importedModule->resolveExport(exec, fieldName);
149 RETURN_IF_EXCEPTION(scope, void());
150 switch (resolution.type) {
151 case Resolution::Type::NotFound:
152 throwSyntaxError(exec, scope, makeString("Importing binding name '", String(fieldName.impl()), "' is not found."));
153 return;
154
155 case Resolution::Type::Ambiguous:
156 throwSyntaxError(exec, scope, makeString("Importing binding name '", String(fieldName.impl()), "' cannot be resolved due to ambiguous multiple bindings."));
157 return;
158
159 case Resolution::Type::Error:
160 throwSyntaxError(exec, scope, makeString("Importing binding name 'default' cannot be resolved by star export entries."));
161 return;
162
163 case Resolution::Type::Resolved:
164 break;
165 }
166
167 AbstractModuleRecord* importedRecord = resolution.moduleRecord;
168 JSModuleEnvironment* importedEnvironment = importedRecord->moduleEnvironmentMayBeNull();
169 // It means that target module is not linked yet. In wasm loading, we allow this since we do not solve cyclic resolution as if JS's bindings.
170 // At that time, error occurs since |value| is an empty, and later |value| becomes an undefined.
171 // https://github.com/WebAssembly/esm-integration/tree/master/proposals/esm-integration#js---wasm-cycle-where-js-is-higher-in-the-module-graph
172 if (importedEnvironment) {
173 SymbolTable* symbolTable = importedEnvironment->symbolTable();
174 ConcurrentJSLocker locker(symbolTable->m_lock);
175 auto iter = symbolTable->find(locker, resolution.localName.impl());
176 ASSERT(iter != symbolTable->end(locker));
177 SymbolTableEntry& entry = iter->value;
178 ASSERT(!entry.isNull());
179 ASSERT(importedEnvironment->isValidScopeOffset(entry.scopeOffset()));
180
181 // Snapshotting a value.
182 value = importedEnvironment->variableAt(entry.scopeOffset()).get();
183 }
184 }
185 if (!value)
186 value = jsUndefined();
187
188 switch (import.kind) {
189 case Wasm::ExternalKind::Function: {
190 // 4. If i is a function import:
191 // i. If IsCallable(v) is false, throw a WebAssembly.LinkError.
192 if (!value.isFunction(vm))
193 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "import function", "must be callable")));
194
195 Wasm::Instance* calleeInstance = nullptr;
196 WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = nullptr;
197 JSObject* function = jsCast<JSObject*>(value);
198
199 // ii. If v is an Exported Function Exotic Object:
200 WebAssemblyFunction* wasmFunction;
201 WebAssemblyWrapperFunction* wasmWrapperFunction;
202 if (isWebAssemblyHostFunction(vm, function, wasmFunction, wasmWrapperFunction)) {
203 // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError.
204 Wasm::SignatureIndex importedSignatureIndex;
205 if (wasmFunction) {
206 importedSignatureIndex = wasmFunction->signatureIndex();
207 calleeInstance = &wasmFunction->instance()->instance();
208 entrypointLoadLocation = wasmFunction->entrypointLoadLocation();
209 } else {
210 importedSignatureIndex = wasmWrapperFunction->signatureIndex();
211 // b. Let closure be v.[[Closure]].
212 function = wasmWrapperFunction->function();
213 }
214 Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex];
215 if (importedSignatureIndex != expectedSignatureIndex)
216 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported function", "signature doesn't match the provided WebAssembly function's signature")));
217 }
218 // iii. Otherwise:
219 // 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.
220 // Note: done as part of Plan compilation.
221 // iv. Append v to funcs.
222 // 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).
223
224 auto* info = m_instance->instance().importFunctionInfo(import.kindIndex);
225 info->targetInstance = calleeInstance;
226 info->wasmEntrypointLoadLocation = entrypointLoadLocation;
227 m_instance->instance().importFunction<WriteBarrier<JSObject>>(import.kindIndex)->set(vm, m_instance.get(), function);
228 break;
229 }
230
231 case Wasm::ExternalKind::Global: {
232 // 5. If i is a global import:
233 // i. If i is not an immutable global, throw a TypeError.
234 ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable);
235 // ii. If the global_type of i is i64 or Type(v) is not Number, throw a WebAssembly.LinkError.
236 if (moduleInformation.globals[import.kindIndex].type == Wasm::I64)
237 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "cannot be an i64")));
238 if (!value.isNumber())
239 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "imported global", "must be a number")));
240 // iii. Append ToWebAssemblyValue(v) to imports.
241 switch (moduleInformation.globals[import.kindIndex].type) {
242 case Wasm::I32:
243 m_instance->instance().setGlobal(import.kindIndex, value.toInt32(exec));
244 break;
245 case Wasm::F32:
246 m_instance->instance().setGlobal(import.kindIndex, bitwise_cast<uint32_t>(value.toFloat(exec)));
247 break;
248 case Wasm::F64:
249 m_instance->instance().setGlobal(import.kindIndex, bitwise_cast<uint64_t>(value.asNumber()));
250 break;
251 default:
252 RELEASE_ASSERT_NOT_REACHED();
253 }
254 scope.assertNoException();
255 break;
256 }
257
258 case Wasm::ExternalKind::Table: {
259 RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
260 // 7. Otherwise (i is a table import):
261 hasTableImport = true;
262 JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
263 // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
264 if (!table)
265 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "is not an instance of WebAssembly.Table")));
266
267 uint32_t expectedInitial = moduleInformation.tableInformation.initial();
268 uint32_t actualInitial = table->length();
269 if (actualInitial < expectedInitial)
270 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided an 'initial' that is too small")));
271
272 if (Optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
273 Optional<uint32_t> actualMaximum = table->maximum();
274 if (!actualMaximum)
275 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "does not have a 'maximum' but the module requires that it does")));
276 if (*actualMaximum > *expectedMaximum)
277 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Imported Table", "'maximum' is larger than the module's expected 'maximum'")));
278 }
279
280 // ii. Append v to tables.
281 // iii. Append v.[[Table]] to imports.
282 m_instance->setTable(vm, table);
283 RETURN_IF_EXCEPTION(scope, void());
284 break;
285 }
286
287 case Wasm::ExternalKind::Memory:
288 break;
289 }
290 }
291
292 {
293 if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
294 // We should either have a Table import or we should have thrown an exception.
295 RELEASE_ASSERT(hasTableImport);
296 }
297
298 if (!!moduleInformation.tableInformation && !hasTableImport) {
299 RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
300 // We create a Table when it's a Table definition.
301 RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum());
302 if (!wasmTable)
303 return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table"));
304 JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull());
305 // We should always be able to allocate a JSWebAssemblyTable we've defined.
306 // If it's defined to be too large, we should have thrown a validation error.
307 scope.assertNoException();
308 ASSERT(table);
309 m_instance->setTable(vm, table);
310 RETURN_IF_EXCEPTION(scope, void());
311 }
312 }
313
314 // Globals
315 {
316 for (size_t globalIndex = moduleInformation.firstInternalGlobal; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
317 const auto& global = moduleInformation.globals[globalIndex];
318 ASSERT(global.initializationType != Wasm::Global::IsImport);
319 if (global.initializationType == Wasm::Global::FromGlobalImport) {
320 ASSERT(global.initialBitsOrImportNumber < moduleInformation.firstInternalGlobal);
321 m_instance->instance().setGlobal(globalIndex, m_instance->instance().loadI64Global(global.initialBitsOrImportNumber));
322 } else
323 m_instance->instance().setGlobal(globalIndex, global.initialBitsOrImportNumber);
324 }
325 }
326
327 SymbolTable* exportSymbolTable = module->exportSymbolTable();
328 unsigned functionImportCount = codeBlock->functionImportCount();
329
330 // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
331 JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this);
332 for (const auto& exp : moduleInformation.exports) {
333 JSValue exportedValue;
334 switch (exp.kind) {
335 case Wasm::ExternalKind::Function: {
336 // 1. If e is a closure c:
337 // i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
338 // 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.)
339 if (exp.kindIndex < functionImportCount) {
340 unsigned functionIndex = exp.kindIndex;
341 JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(functionIndex)->get();
342 if (isWebAssemblyHostFunction(vm, functionImport))
343 exportedValue = functionImport;
344 else {
345 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
346 exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex);
347 }
348 } else {
349 // iii. Otherwise:
350 // a. Let func be an Exported Function Exotic Object created from c.
351 // b. Append func to funcs.
352 // c. Return func.
353 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
354 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(exp.kindIndex);
355 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
356 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
357 WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String::fromUTF8(exp.field), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
358 exportedValue = function;
359 }
360 break;
361 }
362 case Wasm::ExternalKind::Table: {
363 // This should be guaranteed by module verification.
364 RELEASE_ASSERT(m_instance->table());
365 ASSERT(exp.kindIndex == 0);
366
367 exportedValue = m_instance->table();
368 break;
369 }
370 case Wasm::ExternalKind::Memory: {
371 ASSERT(exp.kindIndex == 0);
372
373 exportedValue = m_instance->memory();
374 break;
375 }
376 case Wasm::ExternalKind::Global: {
377 // Assert: the global is immutable by MVP validation constraint.
378 const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
379 ASSERT(global.mutability == Wasm::Global::Immutable);
380 // Return ToJSValue(v).
381 switch (global.type) {
382 case Wasm::I32:
383 exportedValue = JSValue(m_instance->instance().loadI32Global(exp.kindIndex));
384 break;
385
386 case Wasm::I64:
387 throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "exported global cannot be an i64"_s));
388 return;
389
390 case Wasm::F32:
391 exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF32Global(exp.kindIndex)));
392 break;
393
394 case Wasm::F64:
395 exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF64Global(exp.kindIndex)));
396 break;
397
398 default:
399 RELEASE_ASSERT_NOT_REACHED();
400 }
401 break;
402 }
403 }
404
405 bool shouldThrowReadOnlyError = false;
406 bool ignoreReadOnlyErrors = true;
407 bool putResult = false;
408 symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, Identifier::fromString(&vm, String::fromUTF8(exp.field)), exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
409 scope.assertNoException();
410 RELEASE_ASSERT(putResult);
411 }
412
413 bool hasStart = !!moduleInformation.startFunctionIndexSpace;
414 if (hasStart) {
415 auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.valueOr(0);
416 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(startFunctionIndexSpace);
417 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
418 // The start function must not take any arguments or return anything. This is enforced by the parser.
419 ASSERT(!signature.argumentCount());
420 ASSERT(signature.returnType() == Wasm::Void);
421 if (startFunctionIndexSpace < codeBlock->functionImportCount()) {
422 JSObject* startFunction = m_instance->instance().importFunction<WriteBarrier<JSObject>>(startFunctionIndexSpace)->get();
423 m_startFunction.set(vm, this, startFunction);
424 } else {
425 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
426 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(startFunctionIndexSpace);
427 WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), "start", m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
428 m_startFunction.set(vm, this, function);
429 }
430 }
431 m_moduleEnvironment.set(vm, this, moduleEnvironment);
432}
433
434template <typename Scope, typename M, typename N, typename ...Args>
435NEVER_INLINE static JSValue dataSegmentFail(ExecState* exec, VM& vm, Scope& scope, M memorySize, N segmentSize, N offset, Args... args)
436{
437 return throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, makeString("Invalid data segment initialization: segment of "_s, String::number(segmentSize), " bytes memory of "_s, String::number(memorySize), " bytes, at offset "_s, String::number(offset), args...)));
438}
439
440JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
441{
442 VM& vm = exec->vm();
443 auto scope = DECLARE_THROW_SCOPE(vm);
444
445 Wasm::Module& module = m_instance->instance().module();
446 Wasm::CodeBlock* codeBlock = m_instance->instance().codeBlock();
447 const Wasm::ModuleInformation& moduleInformation = module.moduleInformation();
448 JSWebAssemblyTable* table = m_instance->table();
449
450 const Vector<Wasm::Segment::Ptr>& data = moduleInformation.data;
451
452 Optional<JSValue> exception;
453
454 auto forEachElement = [&] (auto fn) {
455 for (const Wasm::Element& element : moduleInformation.elements) {
456 // It should be a validation error to have any elements without a table.
457 // Also, it could be that a table wasn't imported, or that the table
458 // imported wasn't compatible. However, those should error out before
459 // getting here.
460 ASSERT(!!table);
461
462 if (!element.functionIndices.size())
463 continue;
464
465 uint32_t tableIndex = element.offset.isGlobalImport()
466 ? static_cast<uint32_t>(m_instance->instance().loadI32Global(element.offset.globalImportIndex()))
467 : element.offset.constValue();
468
469 fn(element, tableIndex);
470
471 if (exception)
472 break;
473 }
474 };
475
476 auto forEachSegment = [&] (auto fn) {
477 uint8_t* memory = reinterpret_cast<uint8_t*>(m_instance->instance().cachedMemory());
478 uint64_t sizeInBytes = m_instance->instance().cachedMemorySize();
479
480 for (const Wasm::Segment::Ptr& segment : data) {
481 uint32_t offset = segment->offset.isGlobalImport()
482 ? static_cast<uint32_t>(m_instance->instance().loadI32Global(segment->offset.globalImportIndex()))
483 : segment->offset.constValue();
484
485 fn(memory, sizeInBytes, segment, offset);
486
487 if (exception)
488 break;
489 }
490 };
491
492 // Validation of all element ranges comes before all Table and Memory initialization.
493 forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
494 uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1;
495 if (UNLIKELY(lastWrittenIndex >= table->length()))
496 exception = JSValue(throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "Element is trying to set an out of bounds table index"_s)));
497 });
498
499 if (UNLIKELY(exception))
500 return exception.value();
501
502 // Validation of all segment ranges comes before all Table and Memory initialization.
503 forEachSegment([&] (uint8_t*, uint64_t sizeInBytes, const Wasm::Segment::Ptr& segment, uint32_t offset) {
504 if (UNLIKELY(sizeInBytes < segment->sizeInBytes))
505 exception = dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ", segment is too big"_s);
506 else if (UNLIKELY(offset > sizeInBytes - segment->sizeInBytes))
507 exception = dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ", segment writes outside of memory"_s);
508 });
509
510 if (UNLIKELY(exception))
511 return exception.value();
512
513 JSGlobalObject* globalObject = m_instance->globalObject(vm);
514 forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
515 for (uint32_t i = 0; i < element.functionIndices.size(); ++i) {
516 // FIXME: This essentially means we're exporting an import.
517 // We need a story here. We need to create a WebAssemblyFunction
518 // for the import.
519 // https://bugs.webkit.org/show_bug.cgi?id=165510
520 uint32_t functionIndex = element.functionIndices[i];
521 Wasm::SignatureIndex signatureIndex = module.signatureIndexFromFunctionIndexSpace(functionIndex);
522 if (functionIndex < codeBlock->functionImportCount()) {
523 JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(functionIndex)->get();
524 if (isWebAssemblyHostFunction(vm, functionImport)) {
525 WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, functionImport);
526 // If we ever import a WebAssemblyWrapperFunction, we set the import as the unwrapped value.
527 // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction,
528 // the only type this could be is WebAssemblyFunction.
529 RELEASE_ASSERT(wasmFunction);
530 table->setFunction(vm, tableIndex, wasmFunction);
531 ++tableIndex;
532 continue;
533 }
534
535 table->setFunction(vm, tableIndex,
536 WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex));
537 ++tableIndex;
538 continue;
539 }
540
541 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(functionIndex);
542 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(functionIndex);
543 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
544 // FIXME: Say we export local function "foo" at function index 0.
545 // What if we also set it to the table an Element w/ index 0.
546 // Does (new Instance(...)).exports.foo === table.get(0)?
547 // https://bugs.webkit.org/show_bug.cgi?id=165825
548 WebAssemblyFunction* function = WebAssemblyFunction::create(
549 vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
550
551 table->setFunction(vm, tableIndex, function);
552 ++tableIndex;
553 }
554 });
555
556 ASSERT(!exception);
557
558 forEachSegment([&] (uint8_t* memory, uint64_t, const Wasm::Segment::Ptr& segment, uint32_t offset) {
559 // Empty segments are valid, but only if memory isn't present, which would be undefined behavior in memcpy.
560 if (segment->sizeInBytes) {
561 RELEASE_ASSERT(memory);
562 memcpy(memory + offset, &segment->byte(0), segment->sizeInBytes);
563 }
564 });
565
566 ASSERT(!exception);
567
568 if (JSObject* startFunction = m_startFunction.get()) {
569 CallData callData;
570 CallType callType = JSC::getCallData(vm, startFunction, callData);
571 call(exec, startFunction, callType, callData, jsUndefined(), *vm.emptyList);
572 RETURN_IF_EXCEPTION(scope, { });
573 }
574
575 return jsUndefined();
576}
577
578} // namespace JSC
579
580#endif // ENABLE(WEBASSEMBLY)
581