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 (moduleInformation.globals[import.kindIndex].type != Wasm::Anyref && !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::Anyref:
243 m_instance->instance().setGlobal(import.kindIndex, value);
244 break;
245 case Wasm::I32:
246 m_instance->instance().setGlobal(import.kindIndex, value.toInt32(exec));
247 break;
248 case Wasm::F32:
249 m_instance->instance().setGlobal(import.kindIndex, bitwise_cast<uint32_t>(value.toFloat(exec)));
250 break;
251 case Wasm::F64:
252 m_instance->instance().setGlobal(import.kindIndex, bitwise_cast<uint64_t>(value.asNumber()));
253 break;
254 default:
255 RELEASE_ASSERT_NOT_REACHED();
256 }
257 scope.assertNoException();
258 break;
259 }
260
261 case Wasm::ExternalKind::Table: {
262 RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure.
263 // 7. Otherwise (i is a table import):
264 hasTableImport = true;
265 JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value);
266 // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError.
267 if (!table)
268 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "is not an instance of WebAssembly.Table")));
269
270 uint32_t expectedInitial = moduleInformation.tableInformation.initial();
271 uint32_t actualInitial = table->length();
272 if (actualInitial < expectedInitial)
273 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided an 'initial' that is too small")));
274
275 if (Optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) {
276 Optional<uint32_t> actualMaximum = table->maximum();
277 if (!actualMaximum)
278 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "does not have a 'maximum' but the module requires that it does")));
279 if (*actualMaximum > *expectedMaximum)
280 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Imported Table", "'maximum' is larger than the module's expected 'maximum'")));
281 }
282
283 auto expectedType = moduleInformation.tableInformation.type();
284 auto actualType = table->table()->type();
285 if (expectedType != actualType)
286 return exception(createJSWebAssemblyLinkError(exec, vm, importFailMessage(import, "Table import", "provided a 'type' that is wrong")));
287
288 // ii. Append v to tables.
289 // iii. Append v.[[Table]] to imports.
290 m_instance->setTable(vm, table);
291 RETURN_IF_EXCEPTION(scope, void());
292 break;
293 }
294
295 case Wasm::ExternalKind::Memory:
296 break;
297 }
298 }
299
300 {
301 if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) {
302 // We should either have a Table import or we should have thrown an exception.
303 RELEASE_ASSERT(hasTableImport);
304 }
305
306 if (!!moduleInformation.tableInformation && !hasTableImport) {
307 RELEASE_ASSERT(!moduleInformation.tableInformation.isImport());
308 // We create a Table when it's a Table definition.
309 RefPtr<Wasm::Table> wasmTable = Wasm::Table::tryCreate(moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum(), moduleInformation.tableInformation.type());
310 if (!wasmTable)
311 return exception(createJSWebAssemblyLinkError(exec, vm, "couldn't create Table"));
312 JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, globalObject->webAssemblyTableStructure(), wasmTable.releaseNonNull());
313 // We should always be able to allocate a JSWebAssemblyTable we've defined.
314 // If it's defined to be too large, we should have thrown a validation error.
315 scope.assertNoException();
316 ASSERT(table);
317 m_instance->setTable(vm, table);
318 RETURN_IF_EXCEPTION(scope, void());
319 }
320 }
321
322 // Globals
323 {
324 for (size_t globalIndex = moduleInformation.firstInternalGlobal; globalIndex < moduleInformation.globals.size(); ++globalIndex) {
325 const auto& global = moduleInformation.globals[globalIndex];
326 ASSERT(global.initializationType != Wasm::Global::IsImport);
327 if (global.initializationType == Wasm::Global::FromGlobalImport) {
328 ASSERT(global.initialBitsOrImportNumber < moduleInformation.firstInternalGlobal);
329 m_instance->instance().setGlobal(globalIndex, m_instance->instance().loadI64Global(global.initialBitsOrImportNumber));
330 } else
331 m_instance->instance().setGlobal(globalIndex, global.initialBitsOrImportNumber);
332 }
333 }
334
335 SymbolTable* exportSymbolTable = module->exportSymbolTable();
336 unsigned functionImportCount = codeBlock->functionImportCount();
337
338 // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows:
339 JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this);
340 for (const auto& exp : moduleInformation.exports) {
341 JSValue exportedValue;
342 switch (exp.kind) {
343 case Wasm::ExternalKind::Function: {
344 // 1. If e is a closure c:
345 // i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func.
346 // 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.)
347 if (exp.kindIndex < functionImportCount) {
348 unsigned functionIndex = exp.kindIndex;
349 JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(functionIndex)->get();
350 if (isWebAssemblyHostFunction(vm, functionImport))
351 exportedValue = functionImport;
352 else {
353 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
354 exportedValue = WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex);
355 }
356 } else {
357 // iii. Otherwise:
358 // a. Let func be an Exported Function Exotic Object created from c.
359 // b. Append func to funcs.
360 // c. Return func.
361 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
362 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(exp.kindIndex);
363 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
364 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
365 WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String::fromUTF8(exp.field), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
366 exportedValue = function;
367 }
368 break;
369 }
370 case Wasm::ExternalKind::Table: {
371 // This should be guaranteed by module verification.
372 RELEASE_ASSERT(m_instance->table());
373 ASSERT(exp.kindIndex == 0);
374
375 exportedValue = m_instance->table();
376 break;
377 }
378 case Wasm::ExternalKind::Memory: {
379 ASSERT(exp.kindIndex == 0);
380
381 exportedValue = m_instance->memory();
382 break;
383 }
384 case Wasm::ExternalKind::Global: {
385 // Assert: the global is immutable by MVP validation constraint.
386 const Wasm::Global& global = moduleInformation.globals[exp.kindIndex];
387 ASSERT(global.mutability == Wasm::Global::Immutable);
388 // Return ToJSValue(v).
389 switch (global.type) {
390 case Wasm::Anyref:
391 // FIXME: We need to box wasm Funcrefs once they are supported here.
392 // <https://bugs.webkit.org/show_bug.cgi?id=198157>
393 exportedValue = JSValue::decode(m_instance->instance().loadI64Global(exp.kindIndex));
394 break;
395
396 case Wasm::I32:
397 exportedValue = JSValue(m_instance->instance().loadI32Global(exp.kindIndex));
398 break;
399
400 case Wasm::I64:
401 throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "exported global cannot be an i64"_s));
402 return;
403
404 case Wasm::F32:
405 exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF32Global(exp.kindIndex)));
406 break;
407
408 case Wasm::F64:
409 exportedValue = jsNumber(purifyNaN(m_instance->instance().loadF64Global(exp.kindIndex)));
410 break;
411
412 default:
413 RELEASE_ASSERT_NOT_REACHED();
414 }
415 break;
416 }
417 }
418
419 bool shouldThrowReadOnlyError = false;
420 bool ignoreReadOnlyErrors = true;
421 bool putResult = false;
422 symbolTablePutTouchWatchpointSet(moduleEnvironment, exec, Identifier::fromString(&vm, String::fromUTF8(exp.field)), exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult);
423 scope.assertNoException();
424 RELEASE_ASSERT(putResult);
425 }
426
427 bool hasStart = !!moduleInformation.startFunctionIndexSpace;
428 if (hasStart) {
429 auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.valueOr(0);
430 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(startFunctionIndexSpace);
431 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
432 // The start function must not take any arguments or return anything. This is enforced by the parser.
433 ASSERT(!signature.argumentCount());
434 ASSERT(signature.returnType() == Wasm::Void);
435 if (startFunctionIndexSpace < codeBlock->functionImportCount()) {
436 JSObject* startFunction = m_instance->instance().importFunction<WriteBarrier<JSObject>>(startFunctionIndexSpace)->get();
437 m_startFunction.set(vm, this, startFunction);
438 } else {
439 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
440 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(startFunctionIndexSpace);
441 WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), "start", m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
442 m_startFunction.set(vm, this, function);
443 }
444 }
445 m_moduleEnvironment.set(vm, this, moduleEnvironment);
446}
447
448template <typename Scope, typename M, typename N, typename ...Args>
449NEVER_INLINE static JSValue dataSegmentFail(ExecState* exec, VM& vm, Scope& scope, M memorySize, N segmentSize, N offset, Args... args)
450{
451 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...)));
452}
453
454JSValue WebAssemblyModuleRecord::evaluate(ExecState* exec)
455{
456 VM& vm = exec->vm();
457 auto scope = DECLARE_THROW_SCOPE(vm);
458
459 Wasm::Module& module = m_instance->instance().module();
460 Wasm::CodeBlock* codeBlock = m_instance->instance().codeBlock();
461 const Wasm::ModuleInformation& moduleInformation = module.moduleInformation();
462 JSWebAssemblyTable* table = m_instance->table();
463
464 const Vector<Wasm::Segment::Ptr>& data = moduleInformation.data;
465
466 Optional<JSValue> exception;
467
468 auto forEachElement = [&] (auto fn) {
469 for (const Wasm::Element& element : moduleInformation.elements) {
470 // It should be a validation error to have any elements without a table.
471 // Also, it could be that a table wasn't imported, or that the table
472 // imported wasn't compatible. However, those should error out before
473 // getting here.
474 ASSERT(!!table);
475
476 if (!element.functionIndices.size())
477 continue;
478
479 uint32_t tableIndex = element.offset.isGlobalImport()
480 ? static_cast<uint32_t>(m_instance->instance().loadI32Global(element.offset.globalImportIndex()))
481 : element.offset.constValue();
482
483 fn(element, tableIndex);
484
485 if (exception)
486 break;
487 }
488 };
489
490 auto forEachSegment = [&] (auto fn) {
491 uint8_t* memory = reinterpret_cast<uint8_t*>(m_instance->instance().cachedMemory());
492 uint64_t sizeInBytes = m_instance->instance().cachedMemorySize();
493
494 for (const Wasm::Segment::Ptr& segment : data) {
495 uint32_t offset = segment->offset.isGlobalImport()
496 ? static_cast<uint32_t>(m_instance->instance().loadI32Global(segment->offset.globalImportIndex()))
497 : segment->offset.constValue();
498
499 fn(memory, sizeInBytes, segment, offset);
500
501 if (exception)
502 break;
503 }
504 };
505
506 // Validation of all element ranges comes before all Table and Memory initialization.
507 forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
508 uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1;
509 if (UNLIKELY(lastWrittenIndex >= table->length()))
510 exception = JSValue(throwException(exec, scope, createJSWebAssemblyLinkError(exec, vm, "Element is trying to set an out of bounds table index"_s)));
511 });
512
513 if (UNLIKELY(exception))
514 return exception.value();
515
516 // Validation of all segment ranges comes before all Table and Memory initialization.
517 forEachSegment([&] (uint8_t*, uint64_t sizeInBytes, const Wasm::Segment::Ptr& segment, uint32_t offset) {
518 if (UNLIKELY(sizeInBytes < segment->sizeInBytes))
519 exception = dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ", segment is too big"_s);
520 else if (UNLIKELY(offset > sizeInBytes - segment->sizeInBytes))
521 exception = dataSegmentFail(exec, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ", segment writes outside of memory"_s);
522 });
523
524 if (UNLIKELY(exception))
525 return exception.value();
526
527 JSGlobalObject* globalObject = m_instance->globalObject(vm);
528 forEachElement([&] (const Wasm::Element& element, uint32_t tableIndex) {
529 for (uint32_t i = 0; i < element.functionIndices.size(); ++i) {
530 // FIXME: This essentially means we're exporting an import.
531 // We need a story here. We need to create a WebAssemblyFunction
532 // for the import.
533 // https://bugs.webkit.org/show_bug.cgi?id=165510
534 uint32_t functionIndex = element.functionIndices[i];
535 Wasm::SignatureIndex signatureIndex = module.signatureIndexFromFunctionIndexSpace(functionIndex);
536 if (functionIndex < codeBlock->functionImportCount()) {
537 JSObject* functionImport = m_instance->instance().importFunction<WriteBarrier<JSObject>>(functionIndex)->get();
538 if (isWebAssemblyHostFunction(vm, functionImport)) {
539 WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, functionImport);
540 // If we ever import a WebAssemblyWrapperFunction, we set the import as the unwrapped value.
541 // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction,
542 // the only type this could be is WebAssemblyFunction.
543 RELEASE_ASSERT(wasmFunction);
544 table->set(tableIndex, wasmFunction);
545 ++tableIndex;
546 continue;
547 }
548
549 table->set(tableIndex,
550 WebAssemblyWrapperFunction::create(vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, m_instance.get(), signatureIndex));
551 ++tableIndex;
552 continue;
553 }
554
555 Wasm::Callee& embedderEntrypointCallee = codeBlock->embedderEntrypointCalleeFromFunctionIndexSpace(functionIndex);
556 Wasm::WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock->entrypointLoadLocationFromFunctionIndexSpace(functionIndex);
557 const Wasm::Signature& signature = Wasm::SignatureInformation::get(signatureIndex);
558 // FIXME: Say we export local function "foo" at function index 0.
559 // What if we also set it to the table an Element w/ index 0.
560 // Does (new Instance(...)).exports.foo === table.get(0)?
561 // https://bugs.webkit.org/show_bug.cgi?id=165825
562 WebAssemblyFunction* function = WebAssemblyFunction::create(
563 vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), m_instance.get(), embedderEntrypointCallee, entrypointLoadLocation, signatureIndex);
564
565 table->set(tableIndex, function);
566 ++tableIndex;
567 }
568 });
569
570 ASSERT(!exception);
571
572 forEachSegment([&] (uint8_t* memory, uint64_t, const Wasm::Segment::Ptr& segment, uint32_t offset) {
573 // Empty segments are valid, but only if memory isn't present, which would be undefined behavior in memcpy.
574 if (segment->sizeInBytes) {
575 RELEASE_ASSERT(memory);
576 memcpy(memory + offset, &segment->byte(0), segment->sizeInBytes);
577 }
578 });
579
580 ASSERT(!exception);
581
582 if (JSObject* startFunction = m_startFunction.get()) {
583 CallData callData;
584 CallType callType = JSC::getCallData(vm, startFunction, callData);
585 call(exec, startFunction, callType, callData, jsUndefined(), *vm.emptyList);
586 RETURN_IF_EXCEPTION(scope, { });
587 }
588
589 return jsUndefined();
590}
591
592} // namespace JSC
593
594#endif // ENABLE(WEBASSEMBLY)
595