/* * Copyright (C) 2017-2019 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "WasmInstance.h" #if ENABLE(WEBASSEMBLY) #include "JSCJSValueInlines.h" #include "JSWebAssemblyInstance.h" #include "Register.h" #include "WasmModuleInformation.h" #include "WasmSignatureInlines.h" #include #ifdef DARLING #include "JSWebAssemblyHelpers.h" #endif namespace JSC { namespace Wasm { namespace { size_t globalMemoryByteSize(Module& module) { return (Checked(module.moduleInformation().globals.size()) * sizeof(Register)).unsafeGet(); } } Instance::Instance(Context* context, Ref&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame) : m_context(context) , m_module(WTFMove(module)) , m_globals(MallocPtr::malloc(globalMemoryByteSize(m_module.get()))) , m_globalsToMark(m_module.get().moduleInformation().globals.size()) , m_globalsToBinding(m_module.get().moduleInformation().globals.size()) , m_pointerToTopEntryFrame(pointerToTopEntryFrame) , m_pointerToActualStackLimit(pointerToActualStackLimit) , m_storeTopCallFrame(WTFMove(storeTopCallFrame)) , m_numImportFunctions(m_module->moduleInformation().importFunctionCount()) , m_passiveElements(m_module->moduleInformation().elementCount()) , m_passiveDataSegments(m_module->moduleInformation().dataSegmentsCount()) { for (unsigned i = 0; i < m_numImportFunctions; ++i) new (importFunctionInfo(i)) ImportFunctionInfo(); memset(static_cast(m_globals.get()), 0, globalMemoryByteSize(m_module.get())); for (unsigned i = 0; i < m_module->moduleInformation().globals.size(); ++i) { const Wasm::GlobalInformation& global = m_module.get().moduleInformation().globals[i]; if (global.bindingMode == Wasm::GlobalInformation::BindingMode::Portable) { // This is kept alive by JSWebAssemblyInstance -> JSWebAssemblyGlobal -> binding. m_globalsToBinding.set(i); } else if (isRefType(global.type)) { // This is kept alive by JSWebAssemblyInstance -> binding. m_globalsToMark.set(i); } } memset(bitwise_cast(this) + offsetOfTablePtr(m_numImportFunctions, 0), 0, m_module->moduleInformation().tableCount() * sizeof(Table*)); for (unsigned elementIndex = 0; elementIndex < m_module->moduleInformation().elementCount(); ++elementIndex) { const auto& element = m_module->moduleInformation().elements[elementIndex]; if (element.isPassive()) m_passiveElements.quickSet(elementIndex); } for (unsigned dataSegmentIndex = 0; dataSegmentIndex < m_module->moduleInformation().dataSegmentsCount(); ++dataSegmentIndex) { const auto& dataSegment = m_module->moduleInformation().data[dataSegmentIndex]; if (dataSegment->isPassive()) m_passiveDataSegments.quickSet(dataSegmentIndex); } } Ref Instance::create(Context* context, Ref&& module, EntryFrame** pointerToTopEntryFrame, void** pointerToActualStackLimit, StoreTopCallFrameCallback&& storeTopCallFrame) { return adoptRef(*new (NotNull, fastMalloc(allocationSize(module->moduleInformation().importFunctionCount(), module->moduleInformation().tableCount()))) Instance(context, WTFMove(module), pointerToTopEntryFrame, pointerToActualStackLimit, WTFMove(storeTopCallFrame))); } Instance::~Instance() { } size_t Instance::extraMemoryAllocated() const { return globalMemoryByteSize(m_module.get()) + allocationSize(m_numImportFunctions, m_module->moduleInformation().tableCount()); } void Instance::setGlobal(unsigned i, JSValue value) { Global::Value* slot = m_globals.get() + i; if (m_globalsToBinding.get(i)) { Wasm::Global* global = getGlobalBinding(i); if (!global) return; global->valuePointer()->m_externref.set(owner()->vm(), global->owner(), value); return; } ASSERT(m_owner); slot->m_externref.set(owner()->vm(), owner(), value); } JSValue Instance::getFunctionWrapper(unsigned i) const { JSValue value = m_functionWrappers.get(i).get(); if (value.isEmpty()) return jsNull(); return value; } void Instance::setFunctionWrapper(unsigned i, JSValue value) { ASSERT(m_owner); ASSERT(value.isCallable(owner()->vm())); ASSERT(!m_functionWrappers.contains(i)); auto locker = holdLock(owner()->cellLock()); m_functionWrappers.set(i, WriteBarrier(owner()->vm(), owner(), value)); ASSERT(getFunctionWrapper(i) == value); } Table* Instance::table(unsigned i) { RELEASE_ASSERT(i < m_module->moduleInformation().tableCount()); return *bitwise_cast(bitwise_cast(this) + offsetOfTablePtr(m_numImportFunctions, i)); } void Instance::tableCopy(uint32_t dstOffset, uint32_t srcOffset, uint32_t length, uint32_t dstTableIndex, uint32_t srcTableIndex) { RELEASE_ASSERT(srcTableIndex < m_module->moduleInformation().tableCount()); RELEASE_ASSERT(dstTableIndex < m_module->moduleInformation().tableCount()); Table* dstTable = table(dstTableIndex); Table* srcTable = table(srcTableIndex); RELEASE_ASSERT(dstTable->type() == srcTable->type()); auto forEachTableElement = [&](auto fn) { if (dstTableIndex == srcTableIndex && dstOffset > srcOffset) { for (uint32_t index = length; index--;) fn(dstTable, srcTable, dstOffset + index, srcOffset + index); } else if (dstTableIndex == srcTableIndex && dstOffset == srcOffset) return; else { for (uint32_t index = 0; index < length; ++index) fn(dstTable, srcTable, dstOffset + index, srcOffset + index); } }; if (dstTable->isExternrefTable()) { forEachTableElement([](Table* dstTable, Table* srcTable, uint32_t dstIndex, uint32_t srcIndex) { dstTable->copy(srcTable, dstIndex, srcIndex); }); return; } forEachTableElement([](Table* dstTable, Table* srcTable, uint32_t dstIndex, uint32_t srcIndex) { dstTable->asFuncrefTable()->copyFunction(srcTable->asFuncrefTable(), dstIndex, srcIndex); }); } void Instance::elemDrop(uint32_t elementIndex) { m_passiveElements.quickClear(elementIndex); } bool Instance::memoryInit(uint32_t dstAddress, uint32_t srcAddress, uint32_t length, uint32_t dataSegmentIndex) { RELEASE_ASSERT(dataSegmentIndex < module().moduleInformation().dataSegmentsCount()); if (sumOverflows(srcAddress, length)) return false; const Segment::Ptr& segment = module().moduleInformation().data[dataSegmentIndex]; const uint32_t segmentSizeInBytes = m_passiveDataSegments.quickGet(dataSegmentIndex) ? segment->sizeInBytes : 0U; if (srcAddress + length > segmentSizeInBytes) return false; const uint8_t* segmentData = !length ? nullptr : &segment->byte(srcAddress); ASSERT(memory()); return memory()->init(dstAddress, segmentData, length); } void Instance::dataDrop(uint32_t dataSegmentIndex) { m_passiveDataSegments.quickClear(dataSegmentIndex); } const Element* Instance::elementAt(unsigned index) const { RELEASE_ASSERT(index < m_module->moduleInformation().elementCount()); if (m_passiveElements.quickGet(index)) return &m_module->moduleInformation().elements[index]; return nullptr; } void Instance::initElementSegment(uint32_t tableIndex, const Element& segment, uint32_t dstOffset, uint32_t srcOffset, uint32_t length) { RELEASE_ASSERT(length <= segment.length()); JSWebAssemblyInstance* jsInstance = owner(); JSWebAssemblyTable* jsTable = jsInstance->table(tableIndex); JSGlobalObject* globalObject = jsInstance->globalObject(); VM& vm = globalObject->vm(); for (uint32_t index = 0; index < length; ++index) { const auto srcIndex = srcOffset + index; const auto dstIndex = dstOffset + index; if (Element::isNullFuncIndex(segment.functionIndices[srcIndex])) { jsTable->clear(dstIndex); continue; } // FIXME: This essentially means we're exporting an import. // We need a story here. We need to create a WebAssemblyFunction // for the import. // https://bugs.webkit.org/show_bug.cgi?id=165510 uint32_t functionIndex = segment.functionIndices[srcIndex]; SignatureIndex signatureIndex = m_module->signatureIndexFromFunctionIndexSpace(functionIndex); if (isImportFunction(functionIndex)) { JSObject* functionImport = importFunction>(functionIndex)->get(); if (isWebAssemblyHostFunction(vm, functionImport)) { WebAssemblyFunction* wasmFunction = jsDynamicCast(vm, functionImport); // If we ever import a WebAssemblyWrapperFunction, we set the import as the unwrapped value. // Because a WebAssemblyWrapperFunction can never wrap another WebAssemblyWrapperFunction, // the only type this could be is WebAssemblyFunction. RELEASE_ASSERT(wasmFunction); jsTable->set(dstIndex, wasmFunction); continue; } auto* wrapperFunction = WebAssemblyWrapperFunction::create( vm, globalObject, globalObject->webAssemblyWrapperFunctionStructure(), functionImport, functionIndex, jsInstance, signatureIndex); jsTable->set(dstIndex, wrapperFunction); continue; } Callee& embedderEntrypointCallee = codeBlock()->embedderEntrypointCalleeFromFunctionIndexSpace(functionIndex); WasmToWasmImportableFunction::LoadLocation entrypointLoadLocation = codeBlock()->entrypointLoadLocationFromFunctionIndexSpace(functionIndex); const Signature& signature = SignatureInformation::get(signatureIndex); // FIXME: Say we export local function "foo" at function index 0. // What if we also set it to the table an Element w/ index 0. // Does (new Instance(...)).exports.foo === table.get(0)? // https://bugs.webkit.org/show_bug.cgi?id=165825 WebAssemblyFunction* function = WebAssemblyFunction::create( vm, globalObject, globalObject->webAssemblyFunctionStructure(), signature.argumentCount(), String(), jsInstance, embedderEntrypointCallee, entrypointLoadLocation, signatureIndex); jsTable->set(dstIndex, function); } } void Instance::tableInit(uint32_t dstOffset, uint32_t srcOffset, uint32_t length, uint32_t elementIndex, uint32_t tableIndex) { RELEASE_ASSERT(elementIndex < m_module->moduleInformation().elementCount()); RELEASE_ASSERT(tableIndex < m_module->moduleInformation().tableCount()); const Element* elementSegment = elementAt(elementIndex); RELEASE_ASSERT(elementSegment); RELEASE_ASSERT(elementSegment->isPassive()); initElementSegment(tableIndex, *elementSegment, dstOffset, srcOffset, length); } void Instance::setTable(unsigned i, Ref&& table) { RELEASE_ASSERT(i < m_module->moduleInformation().tableCount()); ASSERT(!this->table(i)); *bitwise_cast(bitwise_cast(this) + offsetOfTablePtr(m_numImportFunctions, i)) = &table.leakRef(); } void Instance::linkGlobal(unsigned i, Ref&& global) { m_globals.get()[i].m_pointer = global->valuePointer(); m_linkedGlobals.set(i, WTFMove(global)); } } } // namespace JSC::Wasm #endif // ENABLE(WEBASSEMBLY)