diff options
Diffstat (limited to 'Source/JavaScriptCore/wasm')
88 files changed, 12383 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wasm/JSWebAssembly.cpp b/Source/JavaScriptCore/wasm/JSWebAssembly.cpp new file mode 100644 index 000000000..5701560ab --- /dev/null +++ b/Source/JavaScriptCore/wasm/JSWebAssembly.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2016 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 "JSWebAssembly.h" + +#if ENABLE(WEBASSEMBLY) + +#include "Exception.h" +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSPromiseDeferred.h" +#include "JSWebAssemblyHelpers.h" +#include "WasmPlan.h" +#include "WebAssemblyModuleConstructor.h" + +namespace JSC { + +STATIC_ASSERT_IS_TRIVIALLY_DESTRUCTIBLE(JSWebAssembly); + +EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState*); +EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState*); + +EncodedJSValue JSC_HOST_CALL webAssemblyCompileFunc(ExecState* exec) +{ + VM& vm = exec->vm(); + auto catchScope = DECLARE_CATCH_SCOPE(vm); + + JSPromiseDeferred* promise = JSPromiseDeferred::create(exec, exec->lexicalGlobalObject()); + RETURN_IF_EXCEPTION(catchScope, encodedJSValue()); + + // FIXME: Make this truly asynchronous: + // https://bugs.webkit.org/show_bug.cgi?id=166016 + JSValue module = WebAssemblyModuleConstructor::createModule(exec, exec->lexicalGlobalObject()->WebAssemblyModuleStructure()); + if (Exception* exception = catchScope.exception()) { + catchScope.clearException(); + promise->reject(exec, exception->value()); + return JSValue::encode(promise->promise()); + } + + promise->resolve(exec, module); + return JSValue::encode(promise->promise()); +} + +EncodedJSValue JSC_HOST_CALL webAssemblyValidateFunc(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + size_t byteOffset; + size_t byteSize; + uint8_t* base = getWasmBufferFromValue(exec, exec->argument(0), byteOffset, byteSize); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + Wasm::Plan plan(&vm, base + byteOffset, byteSize); + // FIXME: We might want to throw an OOM exception here if we detect that something will OOM. + // https://bugs.webkit.org/show_bug.cgi?id=166015 + return JSValue::encode(jsBoolean(plan.parseAndValidateModule())); +} + +const ClassInfo JSWebAssembly::s_info = { "WebAssembly", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssembly) }; + +JSWebAssembly* JSWebAssembly::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<JSWebAssembly>(vm.heap)) JSWebAssembly(vm, structure); + object->finishCreation(vm, globalObject); + return object; +} + +Structure* JSWebAssembly::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void JSWebAssembly::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("validate", webAssemblyValidateFunc, DontEnum, 1); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("compile", webAssemblyCompileFunc, DontEnum, 1); +} + +JSWebAssembly::JSWebAssembly(VM& vm, Structure* structure) + : JSNonFinalObject(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/JSWebAssembly.h b/Source/JavaScriptCore/wasm/JSWebAssembly.h new file mode 100644 index 000000000..133ef818f --- /dev/null +++ b/Source/JavaScriptCore/wasm/JSWebAssembly.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSObject.h" +#include "js/JSWebAssemblyCallee.h" +#include "js/JSWebAssemblyCompileError.h" +#include "js/JSWebAssemblyInstance.h" +#include "js/JSWebAssemblyLinkError.h" +#include "js/JSWebAssemblyMemory.h" +#include "js/JSWebAssemblyModule.h" +#include "js/JSWebAssemblyRuntimeError.h" +#include "js/JSWebAssemblyTable.h" +#include "js/WebAssemblyCompileErrorConstructor.h" +#include "js/WebAssemblyCompileErrorPrototype.h" +#include "js/WebAssemblyFunction.h" +#include "js/WebAssemblyInstanceConstructor.h" +#include "js/WebAssemblyInstancePrototype.h" +#include "js/WebAssemblyLinkErrorConstructor.h" +#include "js/WebAssemblyLinkErrorPrototype.h" +#include "js/WebAssemblyMemoryConstructor.h" +#include "js/WebAssemblyMemoryPrototype.h" +#include "js/WebAssemblyModuleConstructor.h" +#include "js/WebAssemblyModulePrototype.h" +#include "js/WebAssemblyModuleRecord.h" +#include "js/WebAssemblyPrototype.h" +#include "js/WebAssemblyRuntimeErrorConstructor.h" +#include "js/WebAssemblyRuntimeErrorPrototype.h" +#include "js/WebAssemblyTableConstructor.h" +#include "js/WebAssemblyTablePrototype.h" +#include "js/WebAssemblyToJSCallee.h" + +namespace JSC { + +class JSWebAssembly : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + + static JSWebAssembly* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + JSWebAssembly(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp new file mode 100644 index 000000000..2ff30501f --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp @@ -0,0 +1,1614 @@ +/* + * Copyright (C) 2016 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 "WasmB3IRGenerator.h" + +#if ENABLE(WEBASSEMBLY) + +#include "B3BasicBlockInlines.h" +#include "B3CCallValue.h" +#include "B3Compile.h" +#include "B3ConstPtrValue.h" +#include "B3FixSSA.h" +#include "B3Generate.h" +#include "B3StackmapGenerationParams.h" +#include "B3SwitchValue.h" +#include "B3Validate.h" +#include "B3ValueInlines.h" +#include "B3Variable.h" +#include "B3VariableValue.h" +#include "B3WasmAddressValue.h" +#include "B3WasmBoundsCheckValue.h" +#include "JSCInlines.h" +#include "JSWebAssemblyInstance.h" +#include "JSWebAssemblyModule.h" +#include "JSWebAssemblyRuntimeError.h" +#include "VirtualRegister.h" +#include "WasmCallingConvention.h" +#include "WasmExceptionType.h" +#include "WasmFunctionParser.h" +#include "WasmMemory.h" +#include <wtf/Optional.h> + +void dumpProcedure(void* ptr) +{ + JSC::B3::Procedure* proc = static_cast<JSC::B3::Procedure*>(ptr); + proc->dump(WTF::dataFile()); +} + +namespace JSC { namespace Wasm { + +using namespace B3; + +namespace { +const bool verbose = false; +} + +class B3IRGenerator { +public: + struct ControlData { + ControlData(Procedure& proc, Type signature, BlockType type, BasicBlock* continuation, BasicBlock* special = nullptr) + : blockType(type) + , continuation(continuation) + , special(special) + { + if (signature != Void) + result.append(proc.addVariable(toB3Type(signature))); + } + + ControlData() + { + } + + void dump(PrintStream& out) const + { + switch (type()) { + case BlockType::If: + out.print("If: "); + break; + case BlockType::Block: + out.print("Block: "); + break; + case BlockType::Loop: + out.print("Loop: "); + break; + case BlockType::TopLevel: + out.print("TopLevel: "); + break; + } + out.print("Continuation: ", *continuation, ", Special: "); + if (special) + out.print(*special); + else + out.print("None"); + } + + BlockType type() const { return blockType; } + + bool hasNonVoidSignature() const { return result.size(); } + + BasicBlock* targetBlockForBranch() + { + if (type() == BlockType::Loop) + return special; + return continuation; + } + + void convertIfToBlock() + { + ASSERT(type() == BlockType::If); + blockType = BlockType::Block; + special = nullptr; + } + + private: + friend class B3IRGenerator; + BlockType blockType; + BasicBlock* continuation; + BasicBlock* special; + Vector<Variable*, 1> result; + }; + + typedef Value* ExpressionType; + typedef ControlData ControlType; + typedef Vector<ExpressionType, 1> ExpressionList; + typedef Vector<Variable*, 1> ResultList; + typedef FunctionParser<B3IRGenerator>::ControlEntry ControlEntry; + + static constexpr ExpressionType emptyExpression = nullptr; + + typedef String ErrorType; + typedef UnexpectedType<ErrorType> UnexpectedResult; + typedef Expected<std::unique_ptr<WasmInternalFunction>, ErrorType> Result; + typedef Expected<void, ErrorType> PartialResult; + template <typename ...Args> + NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const + { + using namespace FailureHelper; // See ADL comment in WasmParser.h. + return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module failed compiling: "), makeString(args)...)); + } +#define WASM_COMPILE_FAIL_IF(condition, ...) do { \ + if (UNLIKELY(condition)) \ + return fail(__VA_ARGS__); \ + } while (0) + + B3IRGenerator(VM&, const ModuleInformation&, Procedure&, WasmInternalFunction*, Vector<UnlinkedWasmToWasmCall>&); + + PartialResult WARN_UNUSED_RETURN addArguments(const Signature*); + PartialResult WARN_UNUSED_RETURN addLocal(Type, uint32_t); + ExpressionType addConstant(Type, uint64_t); + + // Locals + PartialResult WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); + + // Globals + PartialResult WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value); + + // Memory + PartialResult WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset); + PartialResult WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset); + PartialResult WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result); + + // Basic operators + template<OpType> + PartialResult WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result); + template<OpType> + PartialResult WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result); + + // Control flow + ControlData WARN_UNUSED_RETURN addTopLevel(Type signature); + ControlData WARN_UNUSED_RETURN addBlock(Type signature); + ControlData WARN_UNUSED_RETURN addLoop(Type signature); + PartialResult WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result); + PartialResult WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&); + PartialResult WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); + + PartialResult WARN_UNUSED_RETURN addReturn(const ControlData&, const ExpressionList& returnValues); + PartialResult WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& returnValues); + PartialResult WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTargets, const ExpressionList& expressionStack); + PartialResult WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); + PartialResult WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); + + // Calls + PartialResult WARN_UNUSED_RETURN addCall(uint32_t calleeIndex, const Signature*, Vector<ExpressionType>& args, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN addCallIndirect(const Signature*, SignatureIndex, Vector<ExpressionType>& args, ExpressionType& result); + PartialResult WARN_UNUSED_RETURN addUnreachable(); + + void dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack); + + void emitExceptionCheck(CCallHelpers&, ExceptionType); + +private: + ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp); + ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset); + void emitStoreOp(StoreOpType, Origin, ExpressionType pointer, ExpressionType value, uint32_t offset); + + void unify(Variable* target, const ExpressionType source); + void unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& stack); + Value* zeroForType(Type); + + void emitChecksForModOrDiv(B3::Opcode, ExpressionType left, ExpressionType right); + + VM& m_vm; + const ModuleInformation& m_info; + Procedure& m_proc; + BasicBlock* m_currentBlock; + Vector<Variable*> m_locals; + Vector<UnlinkedWasmToWasmCall>& m_unlinkedWasmToWasmCalls; // List each call site and the function index whose address it should be patched with. + GPRReg m_memoryBaseGPR; + GPRReg m_memorySizeGPR; + Value* m_zeroValues[numTypes]; + Value* m_instanceValue; +}; + +B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& procedure, WasmInternalFunction* compilation, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls) + : m_vm(vm) + , m_info(info) + , m_proc(procedure) + , m_unlinkedWasmToWasmCalls(unlinkedWasmToWasmCalls) +{ + m_currentBlock = m_proc.addBlock(); + + for (unsigned i = 0; i < numTypes; ++i) { + switch (B3::Type b3Type = toB3Type(linearizedToType(i))) { + case B3::Int32: + case B3::Int64: + case B3::Float: + case B3::Double: + m_zeroValues[i] = m_currentBlock->appendIntConstant(m_proc, Origin(), b3Type, 0); + break; + case B3::Void: + m_zeroValues[i] = nullptr; + break; + } + } + + // FIXME we don't really need to pin registers here if there's no memory. It makes wasm -> wasm thunks simpler for now. https://bugs.webkit.org/show_bug.cgi?id=166623 + const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get(); + m_memoryBaseGPR = pinnedRegs.baseMemoryPointer; + m_proc.pinRegister(m_memoryBaseGPR); + ASSERT(!pinnedRegs.sizeRegisters[0].sizeOffset); + m_memorySizeGPR = pinnedRegs.sizeRegisters[0].sizeRegister; + for (const PinnedSizeRegisterInfo& regInfo : pinnedRegs.sizeRegisters) + m_proc.pinRegister(regInfo.sizeRegister); + + if (info.hasMemory()) { + m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) { + AllowMacroScratchRegisterUsage allowScratch(jit); + ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR); + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess); + }); + } + + wasmCallingConvention().setupFrameInPrologue(&compilation->wasmCalleeMoveLocation, m_proc, Origin(), m_currentBlock); + + m_instanceValue = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), + m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), &m_vm.topJSWebAssemblyInstance)); +} + +struct MemoryBaseAndSize { + Value* base; + Value* size; +}; + +static MemoryBaseAndSize getMemoryBaseAndSize(VM& vm, Value* instance, Procedure& proc, BasicBlock* block) +{ + Value* memoryObject = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), instance, JSWebAssemblyInstance::offsetOfMemory()); + + static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->memory())) == sizeof(void*), "codegen relies on this size"); + static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->size())) == sizeof(uint64_t), "codegen relies on this size"); + MemoryBaseAndSize result; + result.base = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), memoryObject, JSWebAssemblyMemory::offsetOfMemory()); + result.size = block->appendNew<MemoryValue>(proc, Load, Int64, Origin(), memoryObject, JSWebAssemblyMemory::offsetOfSize()); + + return result; +} + +static void restoreWebAssemblyGlobalState(VM& vm, const MemoryInformation& memory, Value* instance, Procedure& proc, BasicBlock* block) +{ + block->appendNew<MemoryValue>(proc, Store, Origin(), instance, block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance)); + + if (!!memory) { + const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get(); + RegisterSet clobbers; + clobbers.set(pinnedRegs->baseMemoryPointer); + for (auto info : pinnedRegs->sizeRegisters) + clobbers.set(info.sizeRegister); + + B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Void, Origin()); + patchpoint->effects = Effects::none(); + patchpoint->effects.writesPinned = true; + patchpoint->clobber(clobbers); + + patchpoint->append(instance, ValueRep::SomeRegister); + + patchpoint->setGenerator([pinnedRegs] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) { + GPRReg baseMemory = pinnedRegs->baseMemoryPointer; + jit.loadPtr(CCallHelpers::Address(params[0].gpr(), JSWebAssemblyInstance::offsetOfMemory()), baseMemory); + const auto& sizeRegs = pinnedRegs->sizeRegisters; + ASSERT(sizeRegs.size() >= 1); + ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0. + jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); + jit.loadPtr(CCallHelpers::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); + for (unsigned i = 1; i < sizeRegs.size(); ++i) + jit.add64(CCallHelpers::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister); + }); + } +} + +void B3IRGenerator::emitExceptionCheck(CCallHelpers& jit, ExceptionType type) +{ + jit.move(CCallHelpers::TrustedImm32(static_cast<uint32_t>(type)), GPRInfo::argumentGPR1); + auto jumpToExceptionStub = jit.jump(); + + VM* vm = &m_vm; + jit.addLinkTask([vm, jumpToExceptionStub] (LinkBuffer& linkBuffer) { + linkBuffer.link(jumpToExceptionStub, CodeLocationLabel(vm->getCTIStub(throwExceptionFromWasmThunkGenerator).code())); + }); +} + +Value* B3IRGenerator::zeroForType(Type type) +{ + ASSERT(type != Void); + Value* zeroValue = m_zeroValues[linearizeType(type)]; + ASSERT(zeroValue); + return zeroValue; +} + +auto B3IRGenerator::addLocal(Type type, uint32_t count) -> PartialResult +{ + WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(m_locals.size() + count), "can't allocate memory for ", m_locals.size() + count, " locals"); + + for (uint32_t i = 0; i < count; ++i) { + Variable* local = m_proc.addVariable(toB3Type(type)); + m_locals.uncheckedAppend(local); + m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), local, zeroForType(type)); + } + return { }; +} + +auto B3IRGenerator::addArguments(const Signature* signature) -> PartialResult +{ + ASSERT(!m_locals.size()); + WASM_COMPILE_FAIL_IF(!m_locals.tryReserveCapacity(signature->argumentCount()), "can't allocate memory for ", signature->argumentCount(), " arguments"); + + m_locals.grow(signature->argumentCount()); + wasmCallingConvention().loadArguments(signature, m_proc, m_currentBlock, Origin(), + [&] (ExpressionType argument, unsigned i) { + Variable* argumentVariable = m_proc.addVariable(argument->type()); + m_locals[i] = argumentVariable; + m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), argumentVariable, argument); + }); + return { }; +} + +auto B3IRGenerator::getLocal(uint32_t index, ExpressionType& result) -> PartialResult +{ + ASSERT(m_locals[index]); + result = m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), m_locals[index]); + return { }; +} + +auto B3IRGenerator::addUnreachable() -> PartialResult +{ + B3::PatchpointValue* unreachable = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin()); + unreachable->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::Unreachable); + }); + unreachable->effects.terminal = true; + return { }; +} + +auto B3IRGenerator::addGrowMemory(ExpressionType delta, ExpressionType& result) -> PartialResult +{ + int32_t (*growMemory) (ExecState*, int32_t) = [] (ExecState* exec, int32_t delta) -> int32_t { + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyInstance* instance = vm.topJSWebAssemblyInstance; + JSWebAssemblyMemory* wasmMemory = instance->memory(); + + if (delta < 0) + return -1; + + bool shouldThrowExceptionsOnFailure = false; + PageCount result = wasmMemory->grow(exec, static_cast<uint32_t>(delta), shouldThrowExceptionsOnFailure); + RELEASE_ASSERT(!scope.exception()); + if (!result) + return -1; + + return result.pageCount(); + }; + + result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, Origin(), + m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(growMemory)), + m_currentBlock->appendNew<B3::Value>(m_proc, B3::FramePointer, Origin()), delta); + + restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, m_currentBlock); + + return { }; +} + +auto B3IRGenerator::addCurrentMemory(ExpressionType& result) -> PartialResult +{ + auto memoryValue = getMemoryBaseAndSize(m_vm, m_instanceValue, m_proc, m_currentBlock); + + constexpr uint32_t shiftValue = 16; + static_assert(PageCount::pageSize == 1 << shiftValue, "This must hold for the code below to be correct."); + Value* numPages = m_currentBlock->appendNew<Value>(m_proc, ZShr, Origin(), + memoryValue.size, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), shiftValue)); + + result = m_currentBlock->appendNew<Value>(m_proc, Trunc, Origin(), numPages); + + return { }; +} + +auto B3IRGenerator::setLocal(uint32_t index, ExpressionType value) -> PartialResult +{ + ASSERT(m_locals[index]); + m_currentBlock->appendNew<VariableValue>(m_proc, B3::Set, Origin(), m_locals[index], value); + return { }; +} + +auto B3IRGenerator::getGlobal(uint32_t index, ExpressionType& result) -> PartialResult +{ + Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals()); + result = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, toB3Type(m_info.globals[index].type), Origin(), globalsArray, index * sizeof(Register)); + return { }; +} + +auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialResult +{ + ASSERT(toB3Type(m_info.globals[index].type) == value->type()); + Value* globalsArray = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfGlobals()); + m_currentBlock->appendNew<MemoryValue>(m_proc, Store, Origin(), value, globalsArray, index * sizeof(Register)); + return { }; +} + +inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation) +{ + ASSERT(m_memoryBaseGPR && m_memorySizeGPR); + ASSERT(sizeOfOperation + offset > offset); + m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, Origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1); + pointer = m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), pointer); + return m_currentBlock->appendNew<WasmAddressValue>(m_proc, Origin(), pointer, m_memoryBaseGPR); +} + +inline uint32_t sizeOfLoadOp(LoadOpType op) +{ + switch (op) { + case LoadOpType::I32Load8S: + case LoadOpType::I32Load8U: + case LoadOpType::I64Load8S: + case LoadOpType::I64Load8U: + return 1; + case LoadOpType::I32Load16S: + case LoadOpType::I64Load16S: + case LoadOpType::I32Load16U: + case LoadOpType::I64Load16U: + return 2; + case LoadOpType::I32Load: + case LoadOpType::I64Load32S: + case LoadOpType::I64Load32U: + case LoadOpType::F32Load: + return 4; + case LoadOpType::I64Load: + case LoadOpType::F64Load: + return 8; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +inline Value* B3IRGenerator::emitLoadOp(LoadOpType op, Origin origin, ExpressionType pointer, uint32_t offset) +{ + switch (op) { + case LoadOpType::I32Load8S: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset); + } + + case LoadOpType::I64Load8S: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value); + } + + case LoadOpType::I32Load8U: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset); + } + + case LoadOpType::I64Load8U: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value); + } + + case LoadOpType::I32Load16S: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset); + } + case LoadOpType::I64Load16S: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value); + } + + case LoadOpType::I32Load: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset); + } + + case LoadOpType::I64Load32U: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value); + } + + case LoadOpType::I64Load32S: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value); + } + + case LoadOpType::I64Load: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin, pointer, offset); + } + + case LoadOpType::F32Load: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Float, origin, pointer, offset); + } + + case LoadOpType::F64Load: { + return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Double, origin, pointer, offset); + } + + // FIXME: B3 doesn't support Load16Z yet. We should lower to that value when + // it's added. https://bugs.webkit.org/show_bug.cgi?id=165884 + case LoadOpType::I32Load16U: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset); + return m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), value, + m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), 0x0000ffff)); + } + case LoadOpType::I64Load16U: { + Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset); + Value* partialResult = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), value, + m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), 0x0000ffff)); + + return m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), partialResult); + } + } + RELEASE_ASSERT_NOT_REACHED(); +} + +auto B3IRGenerator::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t offset) -> PartialResult +{ + ASSERT(pointer->type() == Int32); + + if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfLoadOp(op)))) { + // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it + // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435 + B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin()); + throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess); + }); + + switch (op) { + case LoadOpType::I32Load8S: + case LoadOpType::I32Load16S: + case LoadOpType::I32Load: + case LoadOpType::I32Load16U: + case LoadOpType::I32Load8U: + result = zeroForType(I32); + break; + case LoadOpType::I64Load8S: + case LoadOpType::I64Load8U: + case LoadOpType::I64Load16S: + case LoadOpType::I64Load32U: + case LoadOpType::I64Load32S: + case LoadOpType::I64Load: + case LoadOpType::I64Load16U: + result = zeroForType(I64); + break; + case LoadOpType::F32Load: + result = zeroForType(F32); + break; + case LoadOpType::F64Load: + result = zeroForType(F64); + break; + } + + } else + result = emitLoadOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfLoadOp(op)), offset); + + return { }; +} + +inline uint32_t sizeOfStoreOp(StoreOpType op) +{ + switch (op) { + case StoreOpType::I32Store8: + case StoreOpType::I64Store8: + return 1; + case StoreOpType::I32Store16: + case StoreOpType::I64Store16: + return 2; + case StoreOpType::I32Store: + case StoreOpType::I64Store32: + case StoreOpType::F32Store: + return 4; + case StoreOpType::I64Store: + case StoreOpType::F64Store: + return 8; + } + RELEASE_ASSERT_NOT_REACHED(); +} + + +inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, ExpressionType pointer, ExpressionType value, uint32_t offset) +{ + switch (op) { + case StoreOpType::I64Store8: + value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value); + FALLTHROUGH; + + case StoreOpType::I32Store8: + m_currentBlock->appendNew<MemoryValue>(m_proc, Store8, origin, value, pointer, offset); + return; + + case StoreOpType::I64Store16: + value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value); + FALLTHROUGH; + + case StoreOpType::I32Store16: + m_currentBlock->appendNew<MemoryValue>(m_proc, Store16, origin, value, pointer, offset); + return; + + case StoreOpType::I64Store32: + value = m_currentBlock->appendNew<Value>(m_proc, Trunc, origin, value); + FALLTHROUGH; + + case StoreOpType::I64Store: + case StoreOpType::I32Store: + case StoreOpType::F32Store: + case StoreOpType::F64Store: + m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin, value, pointer, offset); + return; + } + RELEASE_ASSERT_NOT_REACHED(); +} + +auto B3IRGenerator::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t offset) -> PartialResult +{ + ASSERT(pointer->type() == Int32); + + if (UNLIKELY(sumOverflows<uint32_t>(offset, sizeOfStoreOp(op)))) { + // FIXME: Even though this is provably out of bounds, it's not a validation error, so we have to handle it + // as a runtime exception. However, this may change: https://bugs.webkit.org/show_bug.cgi?id=166435 + B3::PatchpointValue* throwException = m_currentBlock->appendNew<B3::PatchpointValue>(m_proc, B3::Void, Origin()); + throwException->setGenerator([this] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsMemoryAccess); + }); + } else + emitStoreOp(op, Origin(), emitCheckAndPreparePointer(pointer, offset, sizeOfStoreOp(op)), value, offset); + + return { }; +} + +auto B3IRGenerator::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> PartialResult +{ + result = m_currentBlock->appendNew<Value>(m_proc, B3::Select, Origin(), condition, nonZero, zero); + return { }; +} + +B3IRGenerator::ExpressionType B3IRGenerator::addConstant(Type type, uint64_t value) +{ + switch (type) { + case Wasm::I32: + return m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), static_cast<int32_t>(value)); + case Wasm::I64: + return m_currentBlock->appendNew<Const64Value>(m_proc, Origin(), value); + case Wasm::F32: + return m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), bitwise_cast<float>(static_cast<int32_t>(value))); + case Wasm::F64: + return m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), bitwise_cast<double>(value)); + case Wasm::Void: + case Wasm::Func: + case Wasm::Anyfunc: + break; + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} + +B3IRGenerator::ControlData B3IRGenerator::addTopLevel(Type signature) +{ + return ControlData(m_proc, signature, BlockType::TopLevel, m_proc.addBlock()); +} + +B3IRGenerator::ControlData B3IRGenerator::addBlock(Type signature) +{ + return ControlData(m_proc, signature, BlockType::Block, m_proc.addBlock()); +} + +B3IRGenerator::ControlData B3IRGenerator::addLoop(Type signature) +{ + BasicBlock* body = m_proc.addBlock(); + BasicBlock* continuation = m_proc.addBlock(); + m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), body); + body->addPredecessor(m_currentBlock); + m_currentBlock = body; + return ControlData(m_proc, signature, BlockType::Loop, continuation, body); +} + +auto B3IRGenerator::addIf(ExpressionType condition, Type signature, ControlType& result) -> PartialResult +{ + // FIXME: This needs to do some kind of stack passing. + + BasicBlock* taken = m_proc.addBlock(); + BasicBlock* notTaken = m_proc.addBlock(); + BasicBlock* continuation = m_proc.addBlock(); + + m_currentBlock->appendNew<Value>(m_proc, B3::Branch, Origin(), condition); + m_currentBlock->setSuccessors(FrequentedBlock(taken), FrequentedBlock(notTaken)); + taken->addPredecessor(m_currentBlock); + notTaken->addPredecessor(m_currentBlock); + + m_currentBlock = taken; + result = ControlData(m_proc, signature, BlockType::If, continuation, notTaken); + return { }; +} + +auto B3IRGenerator::addElse(ControlData& data, const ExpressionList& currentStack) -> PartialResult +{ + unifyValuesWithBlock(currentStack, data.result); + m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), data.continuation); + return addElseToUnreachable(data); +} + +auto B3IRGenerator::addElseToUnreachable(ControlData& data) -> PartialResult +{ + ASSERT(data.type() == BlockType::If); + m_currentBlock = data.special; + data.convertIfToBlock(); + return { }; +} + +auto B3IRGenerator::addReturn(const ControlData&, const ExpressionList& returnValues) -> PartialResult +{ + ASSERT(returnValues.size() <= 1); + if (returnValues.size()) + m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin(), returnValues[0]); + else + m_currentBlock->appendNewControlValue(m_proc, B3::Return, Origin()); + return { }; +} + +auto B3IRGenerator::addBranch(ControlData& data, ExpressionType condition, const ExpressionList& returnValues) -> PartialResult +{ + if (data.type() != BlockType::Loop) + unifyValuesWithBlock(returnValues, data.result); + + BasicBlock* target = data.targetBlockForBranch(); + if (condition) { + BasicBlock* continuation = m_proc.addBlock(); + m_currentBlock->appendNew<Value>(m_proc, B3::Branch, Origin(), condition); + m_currentBlock->setSuccessors(FrequentedBlock(target), FrequentedBlock(continuation)); + target->addPredecessor(m_currentBlock); + continuation->addPredecessor(m_currentBlock); + m_currentBlock = continuation; + } else { + m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), FrequentedBlock(target)); + target->addPredecessor(m_currentBlock); + } + + return { }; +} + +auto B3IRGenerator::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> PartialResult +{ + for (size_t i = 0; i < targets.size(); ++i) + unifyValuesWithBlock(expressionStack, targets[i]->result); + unifyValuesWithBlock(expressionStack, defaultTarget.result); + + SwitchValue* switchValue = m_currentBlock->appendNew<SwitchValue>(m_proc, Origin(), condition); + switchValue->setFallThrough(FrequentedBlock(defaultTarget.targetBlockForBranch())); + for (size_t i = 0; i < targets.size(); ++i) + switchValue->appendCase(SwitchCase(i, FrequentedBlock(targets[i]->targetBlockForBranch()))); + + return { }; +} + +auto B3IRGenerator::endBlock(ControlEntry& entry, ExpressionList& expressionStack) -> PartialResult +{ + ControlData& data = entry.controlData; + + unifyValuesWithBlock(expressionStack, data.result); + m_currentBlock->appendNewControlValue(m_proc, Jump, Origin(), data.continuation); + data.continuation->addPredecessor(m_currentBlock); + + return addEndToUnreachable(entry); +} + + +auto B3IRGenerator::addEndToUnreachable(ControlEntry& entry) -> PartialResult +{ + ControlData& data = entry.controlData; + m_currentBlock = data.continuation; + + if (data.type() == BlockType::If) { + data.special->appendNewControlValue(m_proc, Jump, Origin(), m_currentBlock); + m_currentBlock->addPredecessor(data.special); + } + + for (Variable* result : data.result) + entry.enclosedExpressionStack.append(m_currentBlock->appendNew<VariableValue>(m_proc, B3::Get, Origin(), result)); + + // TopLevel does not have any code after this so we need to make sure we emit a return here. + if (data.type() == BlockType::TopLevel) + return addReturn(entry.controlData, entry.enclosedExpressionStack); + + return { }; +} + +auto B3IRGenerator::addCall(uint32_t functionIndex, const Signature* signature, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult +{ + ASSERT(signature->argumentCount() == args.size()); + + Type returnType = signature->returnType(); + Vector<UnlinkedWasmToWasmCall>* unlinkedWasmToWasmCalls = &m_unlinkedWasmToWasmCalls; + + if (m_info.isImportedFunctionFromFunctionIndexSpace(functionIndex)) { + // FIXME imports can be linked here, instead of generating a patchpoint, because all import stubs are generated before B3 compilation starts. https://bugs.webkit.org/show_bug.cgi?id=166462 + Value* functionImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), m_instanceValue, JSWebAssemblyInstance::offsetOfImportFunction(functionIndex)); + Value* jsTypeOfImport = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, Origin(), functionImport, JSCell::typeInfoTypeOffset()); + Value* isWasmCall = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), jsTypeOfImport, m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), WebAssemblyFunctionType)); + + BasicBlock* isWasmBlock = m_proc.addBlock(); + BasicBlock* isJSBlock = m_proc.addBlock(); + BasicBlock* continuation = m_proc.addBlock(); + m_currentBlock->appendNewControlValue(m_proc, B3::Branch, Origin(), isWasmCall, FrequentedBlock(isWasmBlock), FrequentedBlock(isJSBlock)); + + Value* wasmCallResult = wasmCallingConvention().setupCall(m_proc, isWasmBlock, Origin(), args, toB3Type(returnType), + [&] (PatchpointValue* patchpoint) { + patchpoint->effects.writesPinned = true; + patchpoint->effects.readsPinned = true; + patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + AllowMacroScratchRegisterUsage allowScratch(jit); + CCallHelpers::Call call = jit.call(); + jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) { + unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToWasm }); + }); + }); + }); + UpsilonValue* wasmCallResultUpsilon = returnType == Void ? nullptr : isWasmBlock->appendNew<UpsilonValue>(m_proc, Origin(), wasmCallResult); + isWasmBlock->appendNewControlValue(m_proc, Jump, Origin(), continuation); + + Value* jsCallResult = wasmCallingConvention().setupCall(m_proc, isJSBlock, Origin(), args, toB3Type(returnType), + [&] (PatchpointValue* patchpoint) { + patchpoint->effects.writesPinned = true; + patchpoint->effects.readsPinned = true; + patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + AllowMacroScratchRegisterUsage allowScratch(jit); + CCallHelpers::Call call = jit.call(); + jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) { + unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToJs }); + }); + }); + }); + UpsilonValue* jsCallResultUpsilon = returnType == Void ? nullptr : isJSBlock->appendNew<UpsilonValue>(m_proc, Origin(), jsCallResult); + isJSBlock->appendNewControlValue(m_proc, Jump, Origin(), continuation); + + m_currentBlock = continuation; + + if (returnType == Void) + result = nullptr; + else { + result = continuation->appendNew<Value>(m_proc, Phi, toB3Type(returnType), Origin()); + wasmCallResultUpsilon->setPhi(result); + jsCallResultUpsilon->setPhi(result); + } + + // The call could have been to another WebAssembly instance, and / or could have modified our Memory. + restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, continuation); + } else { + result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType), + [&] (PatchpointValue* patchpoint) { + patchpoint->effects.writesPinned = true; + patchpoint->effects.readsPinned = true; + patchpoint->setGenerator([unlinkedWasmToWasmCalls, functionIndex] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + AllowMacroScratchRegisterUsage allowScratch(jit); + CCallHelpers::Call call = jit.call(); + jit.addLinkTask([unlinkedWasmToWasmCalls, call, functionIndex] (LinkBuffer& linkBuffer) { + unlinkedWasmToWasmCalls->append({ linkBuffer.locationOf(call), functionIndex, UnlinkedWasmToWasmCall::Target::ToWasm }); + }); + }); + }); + } + + return { }; +} + +auto B3IRGenerator::addCallIndirect(const Signature* signature, SignatureIndex signatureIndex, Vector<ExpressionType>& args, ExpressionType& result) -> PartialResult +{ + ASSERT(signatureIndex != Signature::invalidIndex); + ExpressionType calleeIndex = args.takeLast(); + ASSERT(signature->argumentCount() == args.size()); + + ExpressionType callableFunctionBuffer; + ExpressionType callableFunctionBufferSize; + { + ExpressionType table = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), + m_instanceValue, JSWebAssemblyInstance::offsetOfTable()); + callableFunctionBuffer = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), + table, JSWebAssemblyTable::offsetOfFunctions()); + callableFunctionBufferSize = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, Origin(), + table, JSWebAssemblyTable::offsetOfSize()); + } + + // Check the index we are looking for is valid. + { + CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), + m_currentBlock->appendNew<Value>(m_proc, AboveEqual, Origin(), calleeIndex, callableFunctionBufferSize)); + + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsCallIndirect); + }); + } + + // Compute the offset in the table index space we are looking for. + ExpressionType offset = m_currentBlock->appendNew<Value>(m_proc, Mul, Origin(), + m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), calleeIndex), + m_currentBlock->appendIntConstant(m_proc, Origin(), pointerType(), sizeof(CallableFunction))); + ExpressionType callableFunction = m_currentBlock->appendNew<Value>(m_proc, Add, Origin(), callableFunctionBuffer, offset); + + // Check that the CallableFunction is initialized. We trap if it isn't. An "invalid" SignatureIndex indicates it's not initialized. + static_assert(sizeof(CallableFunction::signatureIndex) == sizeof(uint32_t), "Load codegen assumes i32"); + ExpressionType calleeSignatureIndex = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, signatureIndex)); + { + CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), + m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), + calleeSignatureIndex, + m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), Signature::invalidIndex))); + + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::NullTableEntry); + }); + } + + // Check the signature matches the value we expect. + { + ExpressionType expectedSignatureIndex = m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), signatureIndex); + CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), + m_currentBlock->appendNew<Value>(m_proc, NotEqual, Origin(), calleeSignatureIndex, expectedSignatureIndex)); + + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::BadSignature); + }); + } + + ExpressionType calleeCode = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, pointerType(), Origin(), callableFunction, OBJECT_OFFSETOF(CallableFunction, code)); + + Type returnType = signature->returnType(); + result = wasmCallingConvention().setupCall(m_proc, m_currentBlock, Origin(), args, toB3Type(returnType), + [&] (PatchpointValue* patchpoint) { + patchpoint->effects.writesPinned = true; + patchpoint->effects.readsPinned = true; + + patchpoint->append(calleeCode, ValueRep::SomeRegister); + + patchpoint->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) { + AllowMacroScratchRegisterUsage allowScratch(jit); + jit.call(params[returnType == Void ? 0 : 1].gpr()); + }); + }); + + // The call could have been to another WebAssembly instance, and / or could have modified our Memory. + restoreWebAssemblyGlobalState(m_vm, m_info.memory, m_instanceValue, m_proc, m_currentBlock); + + return { }; +} + +void B3IRGenerator::unify(Variable* variable, ExpressionType source) +{ + m_currentBlock->appendNew<VariableValue>(m_proc, Set, Origin(), variable, source); +} + +void B3IRGenerator::unifyValuesWithBlock(const ExpressionList& resultStack, ResultList& result) +{ + ASSERT(result.size() <= resultStack.size()); + + for (size_t i = 0; i < result.size(); ++i) + unify(result[result.size() - 1 - i], resultStack[resultStack.size() - 1 - i]); +} + +static void dumpExpressionStack(const CommaPrinter& comma, const B3IRGenerator::ExpressionList& expressionStack) +{ + dataLog(comma, "ExpressionStack:"); + for (const auto& expression : expressionStack) + dataLog(comma, *expression); +} + +void B3IRGenerator::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack) +{ + dataLogLn("Processing Graph:"); + dataLog(m_proc); + dataLogLn("With current block:", *m_currentBlock); + dataLogLn("Control stack:"); + ASSERT(controlStack.size()); + for (size_t i = controlStack.size(); i--;) { + dataLog(" ", controlStack[i].controlData, ": "); + CommaPrinter comma(", ", ""); + dumpExpressionStack(comma, *expressionStack); + expressionStack = &controlStack[i].enclosedExpressionStack; + dataLogLn(); + } + dataLogLn(); +} + +static void createJSToWasmWrapper(VM& vm, CompilationContext& compilationContext, WasmInternalFunction& function, const Signature* signature, const ModuleInformation& info) +{ + Procedure proc; + BasicBlock* block = proc.addBlock(); + + Origin origin; + + jscCallingConvention().setupFrameInPrologue(&function.jsToWasmCalleeMoveLocation, proc, origin, block); + + if (!ASSERT_DISABLED) { + // This should be guaranteed by our JS wrapper that handles calls to us. + // Just prevent against crazy when ASSERT is enabled. + Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin); + Value* offSetOfArgumentCount = block->appendNew<Const64Value>(proc, origin, CallFrameSlot::argumentCount * sizeof(Register)); + Value* argumentCount = block->appendNew<MemoryValue>(proc, Load, Int32, origin, + block->appendNew<Value>(proc, Add, origin, framePointer, offSetOfArgumentCount)); + + Value* expectedArgumentCount = block->appendNew<Const32Value>(proc, origin, signature->argumentCount()); + + CheckValue* argumentCountCheck = block->appendNew<CheckValue>(proc, Check, origin, + block->appendNew<Value>(proc, Above, origin, expectedArgumentCount, argumentCount)); + + argumentCountCheck->setGenerator([] (CCallHelpers& jit, const StackmapGenerationParams&) { + jit.breakpoint(); + }); + } + + // FIXME The instance is currently set by the C++ code in WebAssemblyFunction::call. We shouldn't go through the extra C++ hoop. https://bugs.webkit.org/show_bug.cgi?id=166486 + Value* instance = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), + block->appendNew<ConstPtrValue>(proc, Origin(), &vm.topJSWebAssemblyInstance)); + restoreWebAssemblyGlobalState(vm, info.memory, instance, proc, block); + + // Get our arguments. + Vector<Value*> arguments; + jscCallingConvention().loadArguments(signature, proc, block, origin, [&] (Value* argument, unsigned) { + arguments.append(argument); + }); + + // Move the arguments into place. + Value* result = wasmCallingConvention().setupCall(proc, block, origin, arguments, toB3Type(signature->returnType()), [&] (PatchpointValue* patchpoint) { + CompilationContext* context = &compilationContext; + + // wasm -> wasm calls clobber pinned registers unconditionally. This JS -> wasm transition must therefore restore these pinned registers (which are usually callee-saved) to account for this. + const PinnedRegisterInfo* pinnedRegs = &PinnedRegisterInfo::get(); + RegisterSet clobbers; + clobbers.set(pinnedRegs->baseMemoryPointer); + for (auto info : pinnedRegs->sizeRegisters) + clobbers.set(info.sizeRegister); + patchpoint->effects.writesPinned = true; + patchpoint->clobber(clobbers); + + patchpoint->setGenerator([context] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + AllowMacroScratchRegisterUsage allowScratch(jit); + + CCallHelpers::Call call = jit.call(); + context->jsEntrypointToWasmEntrypointCall = call; + }); + }); + + // Return the result, if needed. + switch (signature->returnType()) { + case Wasm::Void: + block->appendNewControlValue(proc, B3::Return, origin); + break; + case Wasm::F32: + case Wasm::F64: + result = block->appendNew<Value>(proc, BitwiseCast, origin, result); + FALLTHROUGH; + case Wasm::I32: + case Wasm::I64: + block->appendNewControlValue(proc, B3::Return, origin, result); + break; + case Wasm::Func: + case Wasm::Anyfunc: + RELEASE_ASSERT_NOT_REACHED(); + } + + B3::prepareForGeneration(proc); + B3::generate(proc, *compilationContext.jsEntrypointJIT); + compilationContext.jsEntrypointByproducts = proc.releaseByproducts(); + function.jsToWasmEntrypoint.calleeSaveRegisters = proc.calleeSaveRegisters(); +} + +Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM& vm, CompilationContext& compilationContext, const uint8_t* functionStart, size_t functionLength, const Signature* signature, Vector<UnlinkedWasmToWasmCall>& unlinkedWasmToWasmCalls, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices, unsigned optLevel) +{ + auto result = std::make_unique<WasmInternalFunction>(); + + compilationContext.jsEntrypointJIT = std::make_unique<CCallHelpers>(&vm); + compilationContext.wasmEntrypointJIT = std::make_unique<CCallHelpers>(&vm); + + Procedure procedure; + B3IRGenerator context(vm, info, procedure, result.get(), unlinkedWasmToWasmCalls); + FunctionParser<B3IRGenerator> parser(&vm, context, functionStart, functionLength, signature, info, moduleSignatureIndicesToUniquedSignatureIndices); + WASM_FAIL_IF_HELPER_FAILS(parser.parse()); + + procedure.resetReachability(); + validate(procedure, "After parsing:\n"); + + if (verbose) + dataLog("Pre SSA: ", procedure); + fixSSA(procedure); + if (verbose) + dataLog("Post SSA: ", procedure); + + { + B3::prepareForGeneration(procedure, optLevel); + B3::generate(procedure, *compilationContext.wasmEntrypointJIT); + compilationContext.wasmEntrypointByproducts = procedure.releaseByproducts(); + result->wasmEntrypoint.calleeSaveRegisters = procedure.calleeSaveRegisters(); + } + + createJSToWasmWrapper(vm, compilationContext, *result, signature, info); + return WTFMove(result); +} + +// Custom wasm ops. These are the ones too messy to do in wasm.json. + +void B3IRGenerator::emitChecksForModOrDiv(B3::Opcode operation, ExpressionType left, ExpressionType right) +{ + ASSERT(operation == Div || operation == Mod || operation == UDiv || operation == UMod); + const B3::Type type = left->type(); + + { + CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), + m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), right, + m_currentBlock->appendIntConstant(m_proc, Origin(), type, 0))); + + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::DivisionByZero); + }); + } + + if (operation == Div) { + int64_t min = type == Int32 ? std::numeric_limits<int32_t>::min() : std::numeric_limits<int64_t>::min(); + + CheckValue* check = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), + m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), left, + m_currentBlock->appendIntConstant(m_proc, Origin(), type, min)), + m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), right, + m_currentBlock->appendIntConstant(m_proc, Origin(), type, -1)))); + + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::IntegerOverflow); + }); + } +} + +template<> +auto B3IRGenerator::addOp<OpType::I32DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = Div; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = Mod; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, chill(op), Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = UDiv; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = UMod; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64DivS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = Div; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64RemS>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = Mod; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, chill(op), Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64DivU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = UDiv; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64RemU>(ExpressionType left, ExpressionType right, ExpressionType& result) -> PartialResult +{ + const B3::Opcode op = UMod; + emitChecksForModOrDiv(op, left, right); + result = m_currentBlock->appendNew<Value>(m_proc, op, Origin(), left, right); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.countTrailingZeros32(params[1].gpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64Ctz>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.countTrailingZeros64(params[1].gpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet. + // see: https://bugs.webkit.org/show_bug.cgi?id=165363 + uint32_t (*popcount)(int32_t) = [] (int32_t value) -> uint32_t { return __builtin_popcount(value); }; + Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount)); + result = m_currentBlock->appendNew<CCallValue>(m_proc, Int32, Origin(), Effects::none(), funcAddress, arg); + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64Popcnt>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + // FIXME: This should use the popcnt instruction if SSE4 is available but we don't have code to detect SSE4 yet. + // see: https://bugs.webkit.org/show_bug.cgi?id=165363 + uint64_t (*popcount)(int64_t) = [] (int64_t value) -> uint64_t { return __builtin_popcountll(value); }; + Value* funcAddress = m_currentBlock->appendNew<ConstPtrValue>(m_proc, Origin(), bitwise_cast<void*>(popcount)); + result = m_currentBlock->appendNew<CCallValue>(m_proc, Int64, Origin(), Effects::none(), funcAddress, arg); + return { }; +} + +template<> +auto B3IRGenerator::addOp<F64ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin()); + if (isX86()) + patchpoint->numGPScratchRegisters = 1; + patchpoint->append(ConstrainedValue(arg, ValueRep::SomeRegister)); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + AllowMacroScratchRegisterUsage allowScratch(jit); +#if CPU(X86_64) + jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr(), params.gpScratch(0)); +#else + jit.convertUInt64ToDouble(params[1].gpr(), params[0].fpr()); +#endif + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::F32ConvertUI64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin()); + if (isX86()) + patchpoint->numGPScratchRegisters = 1; + patchpoint->append(ConstrainedValue(arg, ValueRep::SomeRegister)); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + AllowMacroScratchRegisterUsage allowScratch(jit); +#if CPU(X86_64) + jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr(), params.gpScratch(0)); +#else + jit.convertUInt64ToFloat(params[1].gpr(), params[0].fpr()); +#endif + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::F64Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.roundTowardNearestIntDouble(params[1].fpr(), params[0].fpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::F32Nearest>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.roundTowardNearestIntFloat(params[1].fpr(), params[0].fpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::F64Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Double, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.roundTowardZeroDouble(params[1].fpr(), params[0].fpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::F32Trunc>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Float, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.roundTowardZeroFloat(params[1].fpr(), params[0].fpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int32_t>::min())); + Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min())); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateDoubleToInt32(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int32_t>::min())); + Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min())); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateFloatToInt32(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + + +template<> +auto B3IRGenerator::addOp<OpType::I32TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int32_t>::min()) * -2.0); + Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateDoubleToUint32(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I32TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int32_t>::min()) * -2.0); + Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int32, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateFloatToUint32(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64TruncSF64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -static_cast<double>(std::numeric_limits<int64_t>::min())); + Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min())); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateDoubleToInt64(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64TruncUF64>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<int64_t>::min()) * -2.0); + Value* min = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), -1.0); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + + Value* constant; + if (isX86()) { + // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if + // the numbers are would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it + // so we can pool them if needed. + constant = m_currentBlock->appendNew<ConstDoubleValue>(m_proc, Origin(), static_cast<double>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())); + } + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + if (isX86()) { + patchpoint->append(constant, ValueRep::SomeRegister); + patchpoint->numFPScratchRegisters = 1; + } + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + AllowMacroScratchRegisterUsage allowScratch(jit); + FPRReg scratch = InvalidFPRReg; + FPRReg constant = InvalidFPRReg; + if (isX86()) { + scratch = params.fpScratch(0); + constant = params[2].fpr(); + } + jit.truncateDoubleToUint64(params[1].fpr(), params[0].gpr(), scratch, constant); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64TruncSF32>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -static_cast<float>(std::numeric_limits<int64_t>::min())); + Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min())); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterEqual, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + jit.truncateFloatToInt64(params[1].fpr(), params[0].gpr()); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +template<> +auto B3IRGenerator::addOp<OpType::I64TruncUF32>(ExpressionType arg, ExpressionType& result) -> PartialResult +{ + Value* max = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<int64_t>::min()) * -2.0); + Value* min = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), -1.0); + Value* outOfBounds = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), + m_currentBlock->appendNew<Value>(m_proc, LessThan, Origin(), arg, max), + m_currentBlock->appendNew<Value>(m_proc, GreaterThan, Origin(), arg, min)); + outOfBounds = m_currentBlock->appendNew<Value>(m_proc, Equal, Origin(), outOfBounds, zeroForType(I32)); + CheckValue* trap = m_currentBlock->appendNew<CheckValue>(m_proc, Check, Origin(), outOfBounds); + trap->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams&) { + this->emitExceptionCheck(jit, ExceptionType::OutOfBoundsTrunc); + }); + + Value* constant; + if (isX86()) { + // Since x86 doesn't have an instruction to convert floating points to unsigned integers, we at least try to do the smart thing if + // the numbers are would be positive anyway as a signed integer. Since we cannot materialize constants into fprs we have b3 do it + // so we can pool them if needed. + constant = m_currentBlock->appendNew<ConstFloatValue>(m_proc, Origin(), static_cast<float>(std::numeric_limits<uint64_t>::max() - std::numeric_limits<int64_t>::max())); + } + PatchpointValue* patchpoint = m_currentBlock->appendNew<PatchpointValue>(m_proc, Int64, Origin()); + patchpoint->append(arg, ValueRep::SomeRegister); + if (isX86()) { + patchpoint->append(constant, ValueRep::SomeRegister); + patchpoint->numFPScratchRegisters = 1; + } + patchpoint->setGenerator([=] (CCallHelpers& jit, const StackmapGenerationParams& params) { + AllowMacroScratchRegisterUsage allowScratch(jit); + FPRReg scratch = InvalidFPRReg; + FPRReg constant = InvalidFPRReg; + if (isX86()) { + scratch = params.fpScratch(0); + constant = params[2].fpr(); + } + jit.truncateFloatToUint64(params[1].fpr(), params[0].gpr(), scratch, constant); + }); + patchpoint->effects = Effects::none(); + result = patchpoint; + return { }; +} + +} } // namespace JSC::Wasm + +#include "WasmB3IRGeneratorInlines.h" + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmB3IRGenerator.h b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.h new file mode 100644 index 000000000..249d57814 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmB3IRGenerator.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "CCallHelpers.h" +#include "VM.h" +#include "WasmFormat.h" +#include <wtf/Expected.h> + +extern "C" void dumpProcedure(void*); + +namespace JSC { namespace Wasm { + +class MemoryInformation; + +struct CompilationContext { + std::unique_ptr<CCallHelpers> jsEntrypointJIT; + std::unique_ptr<B3::OpaqueByproducts> jsEntrypointByproducts; + std::unique_ptr<CCallHelpers> wasmEntrypointJIT; + std::unique_ptr<B3::OpaqueByproducts> wasmEntrypointByproducts; + CCallHelpers::Call jsEntrypointToWasmEntrypointCall; +}; + +Expected<std::unique_ptr<WasmInternalFunction>, String> parseAndCompile(VM&, CompilationContext&, const uint8_t*, size_t, const Signature*, Vector<UnlinkedWasmToWasmCall>&, const ModuleInformation&, const Vector<SignatureIndex>&, unsigned optLevel = 1); + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmBinding.cpp b/Source/JavaScriptCore/wasm/WasmBinding.cpp new file mode 100644 index 000000000..33c1c9b0f --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmBinding.cpp @@ -0,0 +1,468 @@ +/* + * Copyright (C) 2016 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 "WasmBinding.h" + +#if ENABLE(WEBASSEMBLY) + +#include "CCallHelpers.h" +#include "FrameTracers.h" +#include "JITExceptions.h" +#include "JSCInlines.h" +#include "JSWebAssemblyInstance.h" +#include "LinkBuffer.h" +#include "NativeErrorConstructor.h" +#include "WasmCallingConvention.h" +#include "WasmExceptionType.h" + +namespace JSC { namespace Wasm { + +typedef CCallHelpers JIT; + +static void materializeImportJSCell(VM* vm, JIT& jit, unsigned importIndex, GPRReg result) +{ + // We're calling out of the current WebAssembly.Instance, which is identified on VM. That Instance has a list of all its import functions. + jit.loadPtr(&vm->topJSWebAssemblyInstance, result); + jit.loadPtr(JIT::Address(result, JSWebAssemblyInstance::offsetOfImportFunction(importIndex)), result); +} + +static MacroAssemblerCodeRef wasmToJs(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex) +{ + const WasmCallingConvention& wasmCC = wasmCallingConvention(); + const JSCCallingConvention& jsCC = jscCallingConvention(); + const Signature* signature = SignatureInformation::get(vm, signatureIndex); + unsigned argCount = signature->argumentCount(); + JIT jit(vm, nullptr); + + // Below, we assume that the JS calling convention is always on the stack. + ASSERT(!jsCC.m_gprArgs.size()); + ASSERT(!jsCC.m_fprArgs.size()); + + jit.emitFunctionPrologue(); + jit.store64(JIT::TrustedImm32(0), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::codeBlock * static_cast<int>(sizeof(Register)))); // FIXME Stop using 0 as codeBlocks. https://bugs.webkit.org/show_bug.cgi?id=165321 + jit.storePtr(JIT::TrustedImmPtr(vm->webAssemblyToJSCallee.get()), JIT::Address(GPRInfo::callFrameRegister, CallFrameSlot::callee * static_cast<int>(sizeof(Register)))); + + + { + bool hasBadI64Use = false; + hasBadI64Use |= signature->returnType() == I64; + for (unsigned argNum = 0; argNum < argCount && !hasBadI64Use; ++argNum) { + Type argType = signature->argument(argNum); + switch (argType) { + case Void: + case Func: + case Anyfunc: + RELEASE_ASSERT_NOT_REACHED(); + + case I64: { + hasBadI64Use = true; + break; + } + + default: + break; + } + } + + if (hasBadI64Use) { + jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(); + jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + auto call = jit.call(); + jit.jumpToExceptionHandler(); + + void (*throwBadI64)(ExecState*) = [] (ExecState* exec) -> void { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + + { + auto throwScope = DECLARE_THROW_SCOPE(*vm); + JSGlobalObject* globalObject = vm->topJSWebAssemblyInstance->globalObject(); + auto* error = ErrorInstance::create(exec, *vm, globalObject->typeErrorConstructor()->errorStructure(), ASCIILiteral("i64 not allowed as return type or argument to an imported function")); + throwException(exec, throwScope, error); + } + + genericUnwind(vm, exec); + ASSERT(!!vm->callFrameForCatch); + }; + + LinkBuffer linkBuffer(*vm, jit, GLOBAL_THUNK_ID); + linkBuffer.link(call, throwBadI64); + return FINALIZE_CODE(linkBuffer, ("WebAssembly->JavaScript invalid i64 use in import[%i]", importIndex)); + } + } + + // Here we assume that the JS calling convention saves at least all the wasm callee saved. We therefore don't need to save and restore more registers since the wasm callee already took care of this. + RegisterSet missingCalleeSaves = wasmCC.m_calleeSaveRegisters; + missingCalleeSaves.exclude(jsCC.m_calleeSaveRegisters); + ASSERT(missingCalleeSaves.isEmpty()); + + // FIXME perform a stack check before updating SP. https://bugs.webkit.org/show_bug.cgi?id=165546 + + unsigned numberOfParameters = argCount + 1; // There is a "this" argument. + unsigned numberOfRegsForCall = CallFrame::headerSizeInRegisters + numberOfParameters; + unsigned numberOfBytesForCall = numberOfRegsForCall * sizeof(Register) - sizeof(CallerFrameAndPC); + const unsigned stackOffset = WTF::roundUpToMultipleOf(stackAlignmentBytes(), numberOfBytesForCall); + jit.subPtr(MacroAssembler::TrustedImm32(stackOffset), MacroAssembler::stackPointerRegister); + JIT::Address calleeFrame = CCallHelpers::Address(MacroAssembler::stackPointerRegister, -static_cast<ptrdiff_t>(sizeof(CallerFrameAndPC))); + + // FIXME make these loops which switch on Signature if there are many arguments on the stack. It'll otherwise be huge for huge signatures. https://bugs.webkit.org/show_bug.cgi?id=165547 + + // First go through the integer parameters, freeing up their register for use afterwards. + { + unsigned marshalledGPRs = 0; + unsigned marshalledFPRs = 0; + unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register)); + unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register)); + for (unsigned argNum = 0; argNum < argCount; ++argNum) { + Type argType = signature->argument(argNum); + switch (argType) { + case Void: + case Func: + case Anyfunc: + case I64: + RELEASE_ASSERT_NOT_REACHED(); // Handled above. + case I32: { + GPRReg gprReg; + if (marshalledGPRs < wasmCC.m_gprArgs.size()) + gprReg = wasmCC.m_gprArgs[marshalledGPRs].gpr(); + else { + // We've already spilled all arguments, these registers are available as scratch. + gprReg = GPRInfo::argumentGPR0; + jit.load64(JIT::Address(GPRInfo::callFrameRegister, frOffset), gprReg); + frOffset += sizeof(Register); + } + ++marshalledGPRs; + jit.boxInt32(gprReg, JSValueRegs(gprReg), DoNotHaveTagRegisters); + jit.store64(gprReg, calleeFrame.withOffset(calleeFrameOffset)); + calleeFrameOffset += sizeof(Register); + break; + } + case F32: + case F64: + // Skipped: handled below. + if (marshalledFPRs >= wasmCC.m_fprArgs.size()) + frOffset += sizeof(Register); + ++marshalledFPRs; + calleeFrameOffset += sizeof(Register); + break; + } + } + } + + { + // Integer registers have already been spilled, these are now available. + GPRReg doubleEncodeOffsetGPRReg = GPRInfo::argumentGPR0; + GPRReg scratch = GPRInfo::argumentGPR1; + bool hasMaterializedDoubleEncodeOffset = false; + auto materializeDoubleEncodeOffset = [&hasMaterializedDoubleEncodeOffset, &jit] (GPRReg dest) { + if (!hasMaterializedDoubleEncodeOffset) { + static_assert(DoubleEncodeOffset == 1ll << 48, "codegen assumes this below"); + jit.move(JIT::TrustedImm32(1), dest); + jit.lshift64(JIT::TrustedImm32(48), dest); + hasMaterializedDoubleEncodeOffset = true; + } + }; + + unsigned marshalledGPRs = 0; + unsigned marshalledFPRs = 0; + unsigned calleeFrameOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register)); + unsigned frOffset = CallFrameSlot::firstArgument * static_cast<int>(sizeof(Register)); + for (unsigned argNum = 0; argNum < argCount; ++argNum) { + Type argType = signature->argument(argNum); + switch (argType) { + case Void: + case Func: + case Anyfunc: + case I64: + RELEASE_ASSERT_NOT_REACHED(); // Handled above. + case I32: + // Skipped: handled above. + if (marshalledGPRs < wasmCC.m_gprArgs.size()) + frOffset += sizeof(Register); + ++marshalledGPRs; + calleeFrameOffset += sizeof(Register); + break; + case F32: { + FPRReg fprReg; + if (marshalledFPRs < wasmCC.m_fprArgs.size()) + fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr(); + else { + // We've already spilled all arguments, these registers are available as scratch. + fprReg = FPRInfo::argumentFPR0; + jit.loadFloat(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg); + frOffset += sizeof(Register); + } + jit.convertFloatToDouble(fprReg, fprReg); + jit.purifyNaN(fprReg); + jit.moveDoubleTo64(fprReg, scratch); + materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg); + jit.add64(doubleEncodeOffsetGPRReg, scratch); + jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset)); + calleeFrameOffset += sizeof(Register); + ++marshalledFPRs; + break; + } + case F64: { + FPRReg fprReg; + if (marshalledFPRs < wasmCC.m_fprArgs.size()) + fprReg = wasmCC.m_fprArgs[marshalledFPRs].fpr(); + else { + // We've already spilled all arguments, these registers are available as scratch. + fprReg = FPRInfo::argumentFPR0; + jit.loadDouble(JIT::Address(GPRInfo::callFrameRegister, frOffset), fprReg); + frOffset += sizeof(Register); + } + jit.purifyNaN(fprReg); + jit.moveDoubleTo64(fprReg, scratch); + materializeDoubleEncodeOffset(doubleEncodeOffsetGPRReg); + jit.add64(doubleEncodeOffsetGPRReg, scratch); + jit.store64(scratch, calleeFrame.withOffset(calleeFrameOffset)); + calleeFrameOffset += sizeof(Register); + ++marshalledFPRs; + break; + } + } + } + } + + GPRReg importJSCellGPRReg = GPRInfo::regT0; // Callee needs to be in regT0 for slow path below. + ASSERT(!wasmCC.m_calleeSaveRegisters.get(importJSCellGPRReg)); + + materializeImportJSCell(vm, jit, importIndex, importJSCellGPRReg); + + jit.store64(importJSCellGPRReg, calleeFrame.withOffset(CallFrameSlot::callee * static_cast<int>(sizeof(Register)))); + jit.store32(JIT::TrustedImm32(numberOfParameters), calleeFrame.withOffset(CallFrameSlot::argumentCount * static_cast<int>(sizeof(Register)) + PayloadOffset)); + jit.store64(JIT::TrustedImm64(ValueUndefined), calleeFrame.withOffset(CallFrameSlot::thisArgument * static_cast<int>(sizeof(Register)))); + + // FIXME Tail call if the wasm return type is void and no registers were spilled. https://bugs.webkit.org/show_bug.cgi?id=165488 + + CallLinkInfo* callLinkInfo = callLinkInfos.add(); + callLinkInfo->setUpCall(CallLinkInfo::Call, CodeOrigin(), importJSCellGPRReg); + JIT::DataLabelPtr targetToCheck; + JIT::TrustedImmPtr initialRightValue(0); + JIT::Jump slowPath = jit.branchPtrWithPatch(MacroAssembler::NotEqual, importJSCellGPRReg, targetToCheck, initialRightValue); + JIT::Call fastCall = jit.nearCall(); + JIT::Jump done = jit.jump(); + slowPath.link(&jit); + // Callee needs to be in regT0 here. + jit.move(MacroAssembler::TrustedImmPtr(callLinkInfo), GPRInfo::regT2); // Link info needs to be in regT2. + JIT::Call slowCall = jit.nearCall(); + done.link(&jit); + + CCallHelpers::JumpList exceptionChecks; + + switch (signature->returnType()) { + case Void: + // Discard. + break; + case Func: + case Anyfunc: + // For the JavaScript embedding, imports with these types in their signature return are a WebAssembly.Module validation error. + RELEASE_ASSERT_NOT_REACHED(); + break; + case I64: { + RELEASE_ASSERT_NOT_REACHED(); // Handled above. + } + case I32: { + CCallHelpers::JumpList done; + CCallHelpers::JumpList slowPath; + + slowPath.append(jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters)); + slowPath.append(jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters)); + jit.zeroExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR); + done.append(jit.jump()); + + slowPath.link(&jit); + jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR); + auto call = jit.call(); + exceptionChecks.append(jit.emitJumpIfException()); + + int32_t (*convertToI32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> int32_t { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + return v.toInt32(exec); + }; + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { + linkBuffer.link(call, convertToI32); + }); + + done.link(&jit); + break; + } + case F32: { + CCallHelpers::JumpList done; + auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters); + auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters); + // We're an int32 + jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR); + jit.convertInt64ToFloat(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR); + done.append(jit.jump()); + + isDouble.link(&jit); + jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2); + jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); + jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR); + jit.convertDoubleToFloat(FPRInfo::returnValueFPR, FPRInfo::returnValueFPR); + done.append(jit.jump()); + + notANumber.link(&jit); + jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR); + auto call = jit.call(); + exceptionChecks.append(jit.emitJumpIfException()); + + float (*convertToF32)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> float { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + return static_cast<float>(v.toNumber(exec)); + }; + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { + linkBuffer.link(call, convertToF32); + }); + + done.link(&jit); + break; + } + case F64: { + CCallHelpers::JumpList done; + auto notANumber = jit.branchIfNotNumber(GPRInfo::returnValueGPR, DoNotHaveTagRegisters); + auto isDouble = jit.branchIfNotInt32(JSValueRegs(GPRInfo::returnValueGPR), DoNotHaveTagRegisters); + // We're an int32 + jit.signExtend32ToPtr(GPRInfo::returnValueGPR, GPRInfo::returnValueGPR); + jit.convertInt64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR); + done.append(jit.jump()); + + isDouble.link(&jit); + jit.move(JIT::TrustedImm64(TagTypeNumber), GPRInfo::returnValueGPR2); + jit.add64(GPRInfo::returnValueGPR2, GPRInfo::returnValueGPR); + jit.move64ToDouble(GPRInfo::returnValueGPR, FPRInfo::returnValueFPR); + done.append(jit.jump()); + + notANumber.link(&jit); + jit.setupArgumentsWithExecState(GPRInfo::returnValueGPR); + auto call = jit.call(); + exceptionChecks.append(jit.emitJumpIfException()); + + double (*convertToF64)(ExecState*, JSValue) = [] (ExecState* exec, JSValue v) -> double { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + return v.toNumber(exec); + }; + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { + linkBuffer.link(call, convertToF64); + }); + + done.link(&jit); + break; + } + } + + jit.emitFunctionEpilogue(); + jit.ret(); + + if (!exceptionChecks.empty()) { + exceptionChecks.link(&jit); + jit.copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(); + jit.move(GPRInfo::callFrameRegister, GPRInfo::argumentGPR0); + auto call = jit.call(); + jit.jumpToExceptionHandler(); + + void (*doUnwinding)(ExecState*) = [] (ExecState* exec) -> void { + VM* vm = &exec->vm(); + NativeCallFrameTracer tracer(vm, exec); + genericUnwind(vm, exec); + ASSERT(!!vm->callFrameForCatch); + }; + + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { + linkBuffer.link(call, doUnwinding); + }); + } + + LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID); + patchBuffer.link(slowCall, FunctionPtr(vm->getCTIStub(linkCallThunkGenerator).code().executableAddress())); + CodeLocationLabel callReturnLocation(patchBuffer.locationOfNearCall(slowCall)); + CodeLocationLabel hotPathBegin(patchBuffer.locationOf(targetToCheck)); + CodeLocationNearCall hotPathOther = patchBuffer.locationOfNearCall(fastCall); + callLinkInfo->setCallLocations(callReturnLocation, hotPathBegin, hotPathOther); +#if !defined(NDEBUG) + String signatureDescription = SignatureInformation::get(vm, signatureIndex)->toString(); +#else + String signatureDescription; +#endif + return FINALIZE_CODE(patchBuffer, ("WebAssembly->JavaScript import[%i] %s", importIndex, signatureDescription.ascii().data())); +} + +static MacroAssemblerCodeRef wasmToWasm(VM* vm, unsigned importIndex) +{ + const PinnedRegisterInfo& pinnedRegs = PinnedRegisterInfo::get(); + JIT jit(vm, nullptr); + + GPRReg scratch = GPRInfo::nonPreservedNonArgumentGPR; + + // B3's call codegen ensures that the JSCell is a WebAssemblyFunction. + materializeImportJSCell(vm, jit, importIndex, scratch); + + // Get the callee's WebAssembly.Instance and set it as vm.topJSWebAssemblyInstance. The caller will take care of restoring its own Instance. + GPRReg baseMemory = pinnedRegs.baseMemoryPointer; + ASSERT(baseMemory != scratch); + jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfInstance()), baseMemory); // Instance*. + jit.storePtr(baseMemory, &vm->topJSWebAssemblyInstance); + + // FIXME the following code assumes that all WebAssembly.Instance have the same pinned registers. https://bugs.webkit.org/show_bug.cgi?id=162952 + // Set up the callee's baseMemory register as well as the memory size registers. + jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyInstance::offsetOfMemory()), baseMemory); // JSWebAssemblyMemory*. + const auto& sizeRegs = pinnedRegs.sizeRegisters; + ASSERT(sizeRegs.size() >= 1); + ASSERT(sizeRegs[0].sizeRegister != baseMemory); + ASSERT(sizeRegs[0].sizeRegister != scratch); + ASSERT(!sizeRegs[0].sizeOffset); // The following code assumes we start at 0, and calculates subsequent size registers relative to 0. + jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfSize()), sizeRegs[0].sizeRegister); // Memory size. + jit.loadPtr(JIT::Address(baseMemory, JSWebAssemblyMemory::offsetOfMemory()), baseMemory); // WasmMemory::void*. + for (unsigned i = 1; i < sizeRegs.size(); ++i) { + ASSERT(sizeRegs[i].sizeRegister != baseMemory); + ASSERT(sizeRegs[i].sizeRegister != scratch); + jit.add64(JIT::TrustedImm32(-sizeRegs[i].sizeOffset), sizeRegs[0].sizeRegister, sizeRegs[i].sizeRegister); + } + + // Tail call into the callee WebAssembly function. + jit.loadPtr(JIT::Address(scratch, WebAssemblyFunction::offsetOfWasmEntryPointCode()), scratch); + jit.jump(scratch); + + LinkBuffer patchBuffer(*vm, jit, GLOBAL_THUNK_ID); + return FINALIZE_CODE(patchBuffer, ("WebAssembly->WebAssembly import[%i]", importIndex)); +} + +WasmExitStubs exitStubGenerator(VM* vm, Bag<CallLinkInfo>& callLinkInfos, SignatureIndex signatureIndex, unsigned importIndex) +{ + WasmExitStubs stubs; + stubs.wasmToJs = wasmToJs(vm, callLinkInfos, signatureIndex, importIndex); + stubs.wasmToWasm = wasmToWasm(vm, importIndex); + return stubs; +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmBinding.h b/Source/JavaScriptCore/wasm/WasmBinding.h new file mode 100644 index 000000000..4021f17ca --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmBinding.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "VM.h" +#include "WasmFormat.h" +#include <wtf/Bag.h> + +namespace JSC { + +class CallLinkInfo; + +namespace Wasm { + +WasmExitStubs exitStubGenerator(VM*, Bag<CallLinkInfo>&, SignatureIndex, unsigned); + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmCallingConvention.cpp b/Source/JavaScriptCore/wasm/WasmCallingConvention.cpp new file mode 100644 index 000000000..9f6ac670a --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmCallingConvention.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 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 "WasmCallingConvention.h" + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/NeverDestroyed.h> + +namespace JSC { namespace Wasm { + +const JSCCallingConvention& jscCallingConvention() +{ + static LazyNeverDestroyed<JSCCallingConvention> staticJSCCallingConvention; + static std::once_flag staticJSCCallingConventionFlag; + std::call_once(staticJSCCallingConventionFlag, [] () { + staticJSCCallingConvention.construct(Vector<Reg>(), Vector<Reg>(), RegisterSet::calleeSaveRegisters()); + }); + + return staticJSCCallingConvention; +} + +const WasmCallingConvention& wasmCallingConvention() +{ + static LazyNeverDestroyed<JSCCallingConvention> staticWasmCallingConvention; + static std::once_flag staticWasmCallingConventionFlag; + std::call_once(staticWasmCallingConventionFlag, [] () { + Vector<Reg> gprArgumentRegisters(GPRInfo::numberOfArgumentRegisters); + for (unsigned i = 0; i < GPRInfo::numberOfArgumentRegisters; ++i) + gprArgumentRegisters[i] = GPRInfo::toArgumentRegister(i); + + Vector<Reg> fprArgumentRegisters(FPRInfo::numberOfArgumentRegisters); + for (unsigned i = 0; i < FPRInfo::numberOfArgumentRegisters; ++i) + fprArgumentRegisters[i] = FPRInfo::toArgumentRegister(i); + + staticWasmCallingConvention.construct(WTFMove(gprArgumentRegisters), WTFMove(fprArgumentRegisters), RegisterSet::calleeSaveRegisters()); + }); + + return staticWasmCallingConvention; +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(B3_JIT) diff --git a/Source/JavaScriptCore/wasm/WasmCallingConvention.h b/Source/JavaScriptCore/wasm/WasmCallingConvention.h new file mode 100644 index 000000000..d79014f27 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmCallingConvention.h @@ -0,0 +1,205 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "AllowMacroScratchRegisterUsage.h" +#include "B3ArgumentRegValue.h" +#include "B3BasicBlock.h" +#include "B3Const64Value.h" +#include "B3ConstrainedValue.h" +#include "B3MemoryValue.h" +#include "B3PatchpointValue.h" +#include "B3Procedure.h" +#include "B3StackmapGenerationParams.h" +#include "CallFrame.h" +#include "LinkBuffer.h" +#include "RegisterSet.h" +#include "WasmFormat.h" + +namespace JSC { namespace Wasm { + +typedef unsigned (*NextOffset)(unsigned currentOffset, B3::Type type); + +template<unsigned headerSize, NextOffset updateOffset> +class CallingConvention { +public: + CallingConvention(Vector<Reg>&& gprArgs, Vector<Reg>&& fprArgs, RegisterSet&& calleeSaveRegisters) + : m_gprArgs(gprArgs) + , m_fprArgs(fprArgs) + , m_calleeSaveRegisters(calleeSaveRegisters) + { + } + +private: + B3::ValueRep marshallArgumentImpl(Vector<Reg> regArgs, B3::Type type, size_t& count, size_t& stackOffset) const + { + if (count < regArgs.size()) + return B3::ValueRep::reg(regArgs[count++]); + + count++; + B3::ValueRep result = B3::ValueRep::stackArgument(stackOffset); + stackOffset = updateOffset(stackOffset, type); + return result; + } + + B3::ValueRep marshallArgument(B3::Type type, size_t& gpArgumentCount, size_t& fpArgumentCount, size_t& stackOffset) const + { + switch (type) { + case B3::Int32: + case B3::Int64: + return marshallArgumentImpl(m_gprArgs, type, gpArgumentCount, stackOffset); + case B3::Float: + case B3::Double: + return marshallArgumentImpl(m_fprArgs, type, fpArgumentCount, stackOffset); + case B3::Void: + break; + } + RELEASE_ASSERT_NOT_REACHED(); + } + +public: + void setupFrameInPrologue(CodeLocationDataLabelPtr* calleeMoveLocation, B3::Procedure& proc, B3::Origin origin, B3::BasicBlock* block) const + { + static_assert(CallFrameSlot::callee * sizeof(Register) < headerSize, "We rely on this here for now."); + static_assert(CallFrameSlot::codeBlock * sizeof(Register) < headerSize, "We rely on this here for now."); + + B3::PatchpointValue* getCalleePatchpoint = block->appendNew<B3::PatchpointValue>(proc, B3::Int64, origin); + getCalleePatchpoint->resultConstraint = B3::ValueRep::SomeRegister; + getCalleePatchpoint->effects = B3::Effects::none(); + getCalleePatchpoint->setGenerator( + [=] (CCallHelpers& jit, const B3::StackmapGenerationParams& params) { + GPRReg result = params[0].gpr(); + MacroAssembler::DataLabelPtr moveLocation = jit.moveWithPatch(MacroAssembler::TrustedImmPtr(nullptr), result); + jit.addLinkTask([calleeMoveLocation, moveLocation] (LinkBuffer& linkBuffer) { + *calleeMoveLocation = linkBuffer.locationOf(moveLocation); + }); + }); + + B3::Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin); + B3::Value* offsetOfCallee = block->appendNew<B3::Const64Value>(proc, origin, CallFrameSlot::callee * sizeof(Register)); + block->appendNew<B3::MemoryValue>(proc, B3::Store, origin, + getCalleePatchpoint, + block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer, offsetOfCallee)); + + // FIXME: We shouldn't have to store zero into the CodeBlock* spot in the call frame, + // but there are places that interpret non-null CodeBlock slot to mean a valid CodeBlock. + // When doing unwinding, we'll need to verify that the entire runtime is OK with a non-null + // CodeBlock not implying that the CodeBlock is valid. + // https://bugs.webkit.org/show_bug.cgi?id=165321 + B3::Value* offsetOfCodeBlock = block->appendNew<B3::Const64Value>(proc, origin, CallFrameSlot::codeBlock * sizeof(Register)); + block->appendNew<B3::MemoryValue>(proc, B3::Store, origin, + block->appendNew<B3::Const64Value>(proc, origin, 0), + block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer, offsetOfCodeBlock)); + } + + template<typename Functor> + void loadArguments(const Signature* signature, B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Functor& functor) const + { + B3::Value* framePointer = block->appendNew<B3::Value>(proc, B3::FramePointer, origin); + + size_t gpArgumentCount = 0; + size_t fpArgumentCount = 0; + size_t stackOffset = headerSize; + + for (size_t i = 0; i < signature->argumentCount(); ++i) { + B3::Type type = toB3Type(signature->argument(i)); + B3::Value* argument; + B3::ValueRep rep = marshallArgument(type, gpArgumentCount, fpArgumentCount, stackOffset); + if (rep.isReg()) { + argument = block->appendNew<B3::ArgumentRegValue>(proc, origin, rep.reg()); + if (type == B3::Int32 || type == B3::Float) + argument = block->appendNew<B3::Value>(proc, B3::Trunc, origin, argument); + } else { + ASSERT(rep.isStackArgument()); + B3::Value* address = block->appendNew<B3::Value>(proc, B3::Add, origin, framePointer, + block->appendNew<B3::Const64Value>(proc, origin, rep.offsetFromSP())); + argument = block->appendNew<B3::MemoryValue>(proc, B3::Load, type, origin, address); + } + functor(argument, i); + } + } + + // It's expected that the pachpointFunctor sets the generator for the call operation. + template<typename Functor> + B3::Value* setupCall(B3::Procedure& proc, B3::BasicBlock* block, B3::Origin origin, const Vector<B3::Value*>& arguments, B3::Type returnType, const Functor& patchpointFunctor) const + { + size_t gpArgumentCount = 0; + size_t fpArgumentCount = 0; + size_t stackOffset = headerSize - sizeof(CallerFrameAndPC); + + Vector<B3::ConstrainedValue> constrainedArguments; + for (B3::Value* argument : arguments) { + B3::ValueRep rep = marshallArgument(argument->type(), gpArgumentCount, fpArgumentCount, stackOffset); + constrainedArguments.append(B3::ConstrainedValue(argument, rep)); + } + + proc.requestCallArgAreaSizeInBytes(WTF::roundUpToMultipleOf(stackAlignmentBytes(), stackOffset)); + + B3::PatchpointValue* patchpoint = block->appendNew<B3::PatchpointValue>(proc, returnType, origin); + patchpoint->clobberEarly(RegisterSet::macroScratchRegisters()); + patchpoint->clobberLate(RegisterSet::volatileRegistersForJSCall()); + patchpointFunctor(patchpoint); + patchpoint->appendVector(constrainedArguments); + + switch (returnType) { + case B3::Void: + return nullptr; + case B3::Float: + case B3::Double: + patchpoint->resultConstraint = B3::ValueRep::reg(FPRInfo::returnValueFPR); + break; + case B3::Int32: + case B3::Int64: + patchpoint->resultConstraint = B3::ValueRep::reg(GPRInfo::returnValueGPR); + break; + } + return patchpoint; + } + + const Vector<Reg> m_gprArgs; + const Vector<Reg> m_fprArgs; + const RegisterSet m_calleeSaveRegisters; + const RegisterSet m_callerSaveRegisters; +}; + +inline unsigned nextJSCOffset(unsigned currentOffset, B3::Type) +{ + return currentOffset + sizeof(Register); +} + +constexpr unsigned jscHeaderSize = ExecState::headerSizeInRegisters * sizeof(Register); +typedef CallingConvention<jscHeaderSize, nextJSCOffset> JSCCallingConvention; + +typedef JSCCallingConvention WasmCallingConvention; + +const JSCCallingConvention& jscCallingConvention(); +const WasmCallingConvention& wasmCallingConvention(); + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmExceptionType.h b/Source/JavaScriptCore/wasm/WasmExceptionType.h new file mode 100644 index 000000000..34bb3d210 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmExceptionType.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +namespace JSC { + +namespace Wasm { + +#define FOR_EACH_EXCEPTION(macro) \ + macro(OutOfBoundsMemoryAccess, "Out of bounds memory access") \ + macro(OutOfBoundsCallIndirect, "Out of bounds call_indirect") \ + macro(NullTableEntry, "call_indirect to a null table entry") \ + macro(BadSignature, "call_indirect to a signature that does not match") \ + macro(OutOfBoundsTrunc, "Out of bounds Trunc operation") \ + macro(Unreachable, "Unreachable code should not be executed") \ + macro(DivisionByZero, "Division by zero") \ + macro(IntegerOverflow, "Integer overflow") + +enum class ExceptionType : uint32_t { +#define MAKE_ENUM(enumName, error) enumName, + FOR_EACH_EXCEPTION(MAKE_ENUM) +#undef MAKE_ENUM +}; + +ALWAYS_INLINE const char* errorMessageForExceptionType(ExceptionType type) +{ + switch (type) { +#define SWITCH_CASE(enumName, error) \ + case ExceptionType::enumName: return error; + + FOR_EACH_EXCEPTION(SWITCH_CASE) +#undef SWITCH_CASE + } + ASSERT_NOT_REACHED(); + return ""; +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmFormat.cpp b/Source/JavaScriptCore/wasm/WasmFormat.cpp new file mode 100644 index 000000000..b047875b2 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmFormat.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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 "WasmFormat.h" + +#if ENABLE(WEBASSEMBLY) + +#include "WasmMemory.h" +#include <wtf/FastMalloc.h> + +namespace JSC { namespace Wasm { + +Segment* Segment::create(I32InitExpr offset, uint32_t sizeInBytes) +{ + auto allocated = tryFastCalloc(sizeof(Segment) + sizeInBytes, 1); + Segment* segment; + if (!allocated.getValue(segment)) + return nullptr; + segment->offset = offset; + segment->sizeInBytes = sizeInBytes; + return segment; +} + +void Segment::destroy(Segment *segment) +{ + fastFree(segment); +} + +Segment::Ptr Segment::adoptPtr(Segment* segment) +{ + return Ptr(segment, &Segment::destroy); +} + +JS_EXPORT_PRIVATE ModuleInformation::~ModuleInformation() { } + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmFormat.h b/Source/JavaScriptCore/wasm/WasmFormat.h new file mode 100644 index 000000000..dc6347442 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmFormat.h @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2015-2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "B3Type.h" +#include "CodeLocation.h" +#include "Identifier.h" +#include "MacroAssemblerCodeRef.h" +#include "RegisterAtOffsetList.h" +#include "WasmMemoryInformation.h" +#include "WasmOps.h" +#include "WasmPageCount.h" +#include "WasmSignature.h" +#include <limits> +#include <memory> +#include <wtf/Optional.h> +#include <wtf/Vector.h> + +namespace JSC { + +class JSFunction; + +namespace Wasm { + +inline bool isValueType(Type type) +{ + switch (type) { + case I32: + case I64: + case F32: + case F64: + return true; + default: + break; + } + return false; +} + +enum class ExternalKind : uint8_t { + // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231 + Function = 0, + Table = 1, + Memory = 2, + Global = 3, +}; + +template<typename Int> +static bool isValidExternalKind(Int val) +{ + switch (val) { + case static_cast<Int>(ExternalKind::Function): + case static_cast<Int>(ExternalKind::Table): + case static_cast<Int>(ExternalKind::Memory): + case static_cast<Int>(ExternalKind::Global): + return true; + default: + return false; + } +} + +static_assert(static_cast<int>(ExternalKind::Function) == 0, "Wasm needs Function to have the value 0"); +static_assert(static_cast<int>(ExternalKind::Table) == 1, "Wasm needs Table to have the value 1"); +static_assert(static_cast<int>(ExternalKind::Memory) == 2, "Wasm needs Memory to have the value 2"); +static_assert(static_cast<int>(ExternalKind::Global) == 3, "Wasm needs Global to have the value 3"); + +static inline const char* makeString(ExternalKind kind) +{ + switch (kind) { + case ExternalKind::Function: return "Function"; + case ExternalKind::Table: return "Table"; + case ExternalKind::Memory: return "Memory"; + case ExternalKind::Global: return "Global"; + } + RELEASE_ASSERT_NOT_REACHED(); + return "?"; +} + +struct Import { + Identifier module; + Identifier field; + ExternalKind kind; + unsigned kindIndex; // Index in the vector of the corresponding kind. +}; + +struct Export { + Identifier field; + ExternalKind kind; + unsigned kindIndex; // Index in the vector of the corresponding kind. +}; + +struct Global { + enum Mutability : uint8_t { + // FIXME auto-generate this. https://bugs.webkit.org/show_bug.cgi?id=165231 + Mutable = 1, + Immutable = 0 + }; + + enum InitializationType { + IsImport, + FromGlobalImport, + FromExpression + }; + + Mutability mutability; + Type type; + InitializationType initializationType { IsImport }; + uint64_t initialBitsOrImportNumber { 0 }; +}; + +struct FunctionLocationInBinary { + size_t start; + size_t end; +}; + +class I32InitExpr { + enum Type : uint8_t { + Global, + Const + }; + + I32InitExpr(Type type, uint32_t bits) + : m_bits(bits) + , m_type(type) + { } + +public: + I32InitExpr() = delete; + + static I32InitExpr globalImport(uint32_t globalImportNumber) { return I32InitExpr(Global, globalImportNumber); } + static I32InitExpr constValue(uint32_t constValue) { return I32InitExpr(Const, constValue); } + + bool isConst() const { return m_type == Const; } + bool isGlobalImport() const { return m_type == Global; } + uint32_t constValue() const + { + RELEASE_ASSERT(isConst()); + return m_bits; + } + uint32_t globalImportIndex() const + { + RELEASE_ASSERT(isGlobalImport()); + return m_bits; + } + +private: + uint32_t m_bits; + Type m_type; +}; + +struct Segment { + uint32_t sizeInBytes; + I32InitExpr offset; + // Bytes are allocated at the end. + uint8_t& byte(uint32_t pos) + { + ASSERT(pos < sizeInBytes); + return *reinterpret_cast<uint8_t*>(reinterpret_cast<char*>(this) + sizeof(Segment) + pos); + } + static Segment* create(I32InitExpr, uint32_t); + static void destroy(Segment*); + typedef std::unique_ptr<Segment, decltype(&Segment::destroy)> Ptr; + static Ptr adoptPtr(Segment*); +}; + +struct Element { + uint32_t offset; + Vector<uint32_t> functionIndices; +}; + +class TableInformation { +public: + TableInformation() + { + ASSERT(!*this); + } + + TableInformation(uint32_t initial, std::optional<uint32_t> maximum, bool isImport) + : m_initial(initial) + , m_maximum(maximum) + , m_isImport(isImport) + , m_isValid(true) + { + ASSERT(*this); + } + + explicit operator bool() const { return m_isValid; } + bool isImport() const { return m_isImport; } + uint32_t initial() const { return m_initial; } + std::optional<uint32_t> maximum() const { return m_maximum; } + +private: + uint32_t m_initial; + std::optional<uint32_t> m_maximum; + bool m_isImport { false }; + bool m_isValid { false }; +}; + +struct CustomSection { + String name; + Vector<uint8_t> payload; +}; + +struct ModuleInformation { + Vector<Import> imports; + Vector<SignatureIndex> importFunctionSignatureIndices; + Vector<SignatureIndex> internalFunctionSignatureIndices; + + MemoryInformation memory; + + Vector<Export> exports; + std::optional<uint32_t> startFunctionIndexSpace; + Vector<Segment::Ptr> data; + Vector<Element> elements; + TableInformation tableInformation; + Vector<Global> globals; + unsigned firstInternalGlobal { 0 }; + Vector<CustomSection> customSections; + + size_t functionIndexSpaceSize() const { return importFunctionSignatureIndices.size() + internalFunctionSignatureIndices.size(); } + bool isImportedFunctionFromFunctionIndexSpace(size_t functionIndex) const + { + ASSERT(functionIndex < functionIndexSpaceSize()); + return functionIndex < importFunctionSignatureIndices.size(); + } + SignatureIndex signatureIndexFromFunctionIndexSpace(size_t functionIndex) const + { + return isImportedFunctionFromFunctionIndexSpace(functionIndex) + ? importFunctionSignatureIndices[functionIndex] + : internalFunctionSignatureIndices[functionIndex - importFunctionSignatureIndices.size()]; + } + + uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); } + bool hasMemory() const { return !!memory; } + + ~ModuleInformation(); +}; + +struct UnlinkedWasmToWasmCall { + CodeLocationCall callLocation; + size_t functionIndex; + enum class Target : uint8_t { + ToJs, + ToWasm, + } target; +}; + +struct Entrypoint { + std::unique_ptr<B3::Compilation> compilation; + RegisterAtOffsetList calleeSaveRegisters; +}; + +struct WasmInternalFunction { + CodeLocationDataLabelPtr wasmCalleeMoveLocation; + CodeLocationDataLabelPtr jsToWasmCalleeMoveLocation; + + Entrypoint wasmEntrypoint; + Entrypoint jsToWasmEntrypoint; +}; + +struct WasmExitStubs { + MacroAssemblerCodeRef wasmToJs; + MacroAssemblerCodeRef wasmToWasm; +}; + +// WebAssembly direct calls and call_indirect use indices into "function index space". This space starts with all imports, and then all internal functions. +// CallableFunction and FunctionIndexSpace are only meant as fast lookup tables for these opcodes, and do not own code. +struct CallableFunction { + CallableFunction() = default; + + CallableFunction(SignatureIndex signatureIndex, void* code = nullptr) + : signatureIndex(signatureIndex) + , code(code) + { + } + + // FIXME pack the SignatureIndex and the code pointer into one 64-bit value. https://bugs.webkit.org/show_bug.cgi?id=165511 + SignatureIndex signatureIndex { Signature::invalidIndex }; + void* code { nullptr }; +}; +typedef Vector<CallableFunction> FunctionIndexSpace; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmFunctionParser.h b/Source/JavaScriptCore/wasm/WasmFunctionParser.h new file mode 100644 index 000000000..64e3b36d3 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmFunctionParser.h @@ -0,0 +1,636 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "WasmParser.h" +#include <wtf/DataLog.h> + +namespace JSC { namespace Wasm { + +enum class BlockType { + If, + Block, + Loop, + TopLevel +}; + +template<typename Context> +class FunctionParser : public Parser<void> { +public: + typedef typename Context::ExpressionType ExpressionType; + typedef typename Context::ControlType ControlType; + typedef typename Context::ExpressionList ExpressionList; + + FunctionParser(VM*, Context&, const uint8_t* functionStart, size_t functionLength, const Signature*, const ModuleInformation&, const Vector<SignatureIndex>&); + + Result WARN_UNUSED_RETURN parse(); + + struct ControlEntry { + ExpressionList enclosedExpressionStack; + ControlType controlData; + }; + +private: + static const bool verbose = false; + + PartialResult WARN_UNUSED_RETURN parseBody(); + PartialResult WARN_UNUSED_RETURN parseExpression(OpType); + PartialResult WARN_UNUSED_RETURN parseUnreachableExpression(OpType); + PartialResult WARN_UNUSED_RETURN unifyControl(Vector<ExpressionType>&, unsigned level); + +#define WASM_TRY_POP_EXPRESSION_STACK_INTO(result, what) do { \ + WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't pop empty stack in " what); \ + result = m_expressionStack.takeLast(); \ + } while (0) + + template<OpType> + PartialResult WARN_UNUSED_RETURN unaryCase(); + + template<OpType> + PartialResult WARN_UNUSED_RETURN binaryCase(); + +#define WASM_TRY_ADD_TO_CONTEXT(add_expression) WASM_FAIL_IF_HELPER_FAILS(m_context.add_expression) + + // FIXME add a macro as above for WASM_TRY_APPEND_TO_CONTROL_STACK https://bugs.webkit.org/show_bug.cgi?id=165862 + + Context& m_context; + ExpressionList m_expressionStack; + Vector<ControlEntry> m_controlStack; + const Signature* m_signature; + const ModuleInformation& m_info; + const Vector<SignatureIndex>& m_moduleSignatureIndicesToUniquedSignatureIndices; + unsigned m_unreachableBlocks { 0 }; +}; + +template<typename Context> +FunctionParser<Context>::FunctionParser(VM* vm, Context& context, const uint8_t* functionStart, size_t functionLength, const Signature* signature, const ModuleInformation& info, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices) + : Parser(vm, functionStart, functionLength) + , m_context(context) + , m_signature(signature) + , m_info(info) + , m_moduleSignatureIndicesToUniquedSignatureIndices(moduleSignatureIndicesToUniquedSignatureIndices) +{ + if (verbose) + dataLogLn("Parsing function starting at: ", (uintptr_t)functionStart, " of length: ", functionLength); +} + +template<typename Context> +auto FunctionParser<Context>::parse() -> Result +{ + uint32_t localCount; + + WASM_PARSER_FAIL_IF(!m_context.addArguments(m_signature), "can't add ", m_signature->argumentCount(), " arguments to Function"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(localCount), "can't get local count"); + WASM_PARSER_FAIL_IF(localCount == std::numeric_limits<uint32_t>::max(), "Function section's local count is too big ", localCount); + + for (uint32_t i = 0; i < localCount; ++i) { + uint32_t numberOfLocals; + Type typeOfLocal; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfLocals), "can't get Function's number of locals in group ", i); + WASM_PARSER_FAIL_IF(numberOfLocals == std::numeric_limits<uint32_t>::max(), "Function section's ", i, "th local group count is too big ", numberOfLocals); + WASM_PARSER_FAIL_IF(!parseValueType(typeOfLocal), "can't get Function local's type in group ", i); + WASM_PARSER_FAIL_IF(!m_context.addLocal(typeOfLocal, numberOfLocals), "can't add ", numberOfLocals, " Function locals from group ", i); + } + + WASM_FAIL_IF_HELPER_FAILS(parseBody()); + + return { }; +} + +template<typename Context> +auto FunctionParser<Context>::parseBody() -> PartialResult +{ + m_controlStack.append({ ExpressionList(), m_context.addTopLevel(m_signature->returnType()) }); + uint8_t op; + while (m_controlStack.size()) { + WASM_PARSER_FAIL_IF(!parseUInt8(op), "can't decode opcode"); + WASM_PARSER_FAIL_IF(!isValidOpType(op), "invalid opcode ", op); + + if (verbose) { + dataLogLn("processing op (", m_unreachableBlocks, "): ", RawPointer(reinterpret_cast<void*>(op)), ", ", makeString(static_cast<OpType>(op)), " at offset: ", RawPointer(reinterpret_cast<void*>(m_offset))); + m_context.dump(m_controlStack, &m_expressionStack); + } + + if (m_unreachableBlocks) + WASM_FAIL_IF_HELPER_FAILS(parseUnreachableExpression(static_cast<OpType>(op))); + else + WASM_FAIL_IF_HELPER_FAILS(parseExpression(static_cast<OpType>(op))); + } + + ASSERT(op == OpType::End); + return { }; +} + +template<typename Context> +template<OpType op> +auto FunctionParser<Context>::binaryCase() -> PartialResult +{ + ExpressionType right; + ExpressionType left; + ExpressionType result; + + WASM_TRY_POP_EXPRESSION_STACK_INTO(right, "binary right"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(left, "binary left"); + WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(left, right, result)); + + m_expressionStack.append(result); + return { }; +} + +template<typename Context> +template<OpType op> +auto FunctionParser<Context>::unaryCase() -> PartialResult +{ + ExpressionType value; + ExpressionType result; + + WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "unary"); + WASM_TRY_ADD_TO_CONTEXT(template addOp<op>(value, result)); + + m_expressionStack.append(result); + return { }; +} + +template<typename Context> +auto FunctionParser<Context>::parseExpression(OpType op) -> PartialResult +{ + switch (op) { +#define CREATE_CASE(name, id, b3op, inc) case OpType::name: return binaryCase<OpType::name>(); + FOR_EACH_WASM_BINARY_OP(CREATE_CASE) +#undef CREATE_CASE + +#define CREATE_CASE(name, id, b3op, inc) case OpType::name: return unaryCase<OpType::name>(); + FOR_EACH_WASM_UNARY_OP(CREATE_CASE) +#undef CREATE_CASE + + case Select: { + ExpressionType condition; + ExpressionType zero; + ExpressionType nonZero; + + WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "select condition"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(zero, "select zero"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(nonZero, "select non-zero"); + + ExpressionType result; + WASM_TRY_ADD_TO_CONTEXT(addSelect(condition, nonZero, zero, result)); + + m_expressionStack.append(result); + return { }; + } + +#define CREATE_CASE(name, id, b3op, inc) case OpType::name: + FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) { + uint32_t alignment; + uint32_t offset; + ExpressionType pointer; + ExpressionType result; + WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get load alignment"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get load offset"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "load pointer"); + WASM_TRY_ADD_TO_CONTEXT(load(static_cast<LoadOpType>(op), pointer, result, offset)); + m_expressionStack.append(result); + return { }; + } + + FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { + uint32_t alignment; + uint32_t offset; + ExpressionType value; + ExpressionType pointer; + WASM_PARSER_FAIL_IF(!parseVarUInt32(alignment), "can't get store alignment"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(offset), "can't get store offset"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "store value"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(pointer, "store pointer"); + WASM_TRY_ADD_TO_CONTEXT(store(static_cast<StoreOpType>(op), pointer, value, offset)); + return { }; + } +#undef CREATE_CASE + + case F32Const: { + uint32_t constant; + WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't parse 32-bit floating-point constant"); + m_expressionStack.append(m_context.addConstant(F32, constant)); + return { }; + } + + case I32Const: { + int32_t constant; + WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't parse 32-bit constant"); + m_expressionStack.append(m_context.addConstant(I32, constant)); + return { }; + } + + case F64Const: { + uint64_t constant; + WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant"); + m_expressionStack.append(m_context.addConstant(F64, constant)); + return { }; + } + + case I64Const: { + int64_t constant; + WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't parse 64-bit constant"); + m_expressionStack.append(m_context.addConstant(I64, constant)); + return { }; + } + + case GetLocal: { + uint32_t index; + ExpressionType result; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for get_local"); + WASM_PARSER_FAIL_IF(!m_context.getLocal(index, result), "can't get_local at index", index); + m_expressionStack.append(result); + return { }; + } + + case SetLocal: { + uint32_t index; + ExpressionType value; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for set_local"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_local"); + WASM_TRY_ADD_TO_CONTEXT(setLocal(index, value)); + return { }; + } + + case TeeLocal: { + uint32_t index; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get index for tee_local"); + WASM_PARSER_FAIL_IF(m_expressionStack.isEmpty(), "can't tee_local on empty expression stack"); + WASM_TRY_ADD_TO_CONTEXT(setLocal(index, m_expressionStack.last())); + return { }; + } + + case GetGlobal: { + uint32_t index; + ExpressionType result; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index"); + WASM_TRY_ADD_TO_CONTEXT(getGlobal(index, result)); + m_expressionStack.append(result); + return { }; + } + + case SetGlobal: { + uint32_t index; + ExpressionType value; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get set_global's index"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(value, "set_global value"); + WASM_TRY_ADD_TO_CONTEXT(setGlobal(index, value)); + return m_context.setGlobal(index, value); + } + + case Call: { + uint32_t functionIndex; + WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't parse call's function index"); + WASM_PARSER_FAIL_IF(functionIndex >= m_info.functionIndexSpaceSize(), "call function index ", functionIndex, " exceeds function index space ", m_info.functionIndexSpaceSize()); + + SignatureIndex calleeSignatureIndex = m_info.signatureIndexFromFunctionIndexSpace(functionIndex); + const Signature* calleeSignature = SignatureInformation::get(m_vm, calleeSignatureIndex); + WASM_PARSER_FAIL_IF(calleeSignature->argumentCount() > m_expressionStack.size(), "call function index ", functionIndex, " has ", calleeSignature->argumentCount(), " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values"); + + size_t firstArgumentIndex = m_expressionStack.size() - calleeSignature->argumentCount(); + Vector<ExpressionType> args; + WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(calleeSignature->argumentCount()), "can't allocate enough memory for call's ", calleeSignature->argumentCount(), " arguments"); + for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) + args.uncheckedAppend(m_expressionStack[i]); + m_expressionStack.shrink(firstArgumentIndex); + + ExpressionType result = Context::emptyExpression; + WASM_TRY_ADD_TO_CONTEXT(addCall(functionIndex, calleeSignature, args, result)); + + if (result != Context::emptyExpression) + m_expressionStack.append(result); + + return { }; + } + + case CallIndirect: { + uint32_t signatureIndex; + uint8_t reserved; + WASM_PARSER_FAIL_IF(!m_info.tableInformation, "call_indirect is only valid when a table is defined or imported"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(signatureIndex), "can't get call_indirect's signature index"); + WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't get call_indirect's reserved byte"); + WASM_PARSER_FAIL_IF(reserved, "call_indirect's 'reserved' varuint1 must be 0x0"); + WASM_PARSER_FAIL_IF(m_moduleSignatureIndicesToUniquedSignatureIndices.size() <= signatureIndex, "call_indirect's signature index ", signatureIndex, " exceeds known signatures ", m_moduleSignatureIndicesToUniquedSignatureIndices.size()); + + SignatureIndex calleeSignatureIndex = m_moduleSignatureIndicesToUniquedSignatureIndices[signatureIndex]; + const Signature* calleeSignature = SignatureInformation::get(m_vm, calleeSignatureIndex); + size_t argumentCount = calleeSignature->argumentCount() + 1; // Add the callee's index. + WASM_PARSER_FAIL_IF(argumentCount > m_expressionStack.size(), "call_indirect expects ", argumentCount, " arguments, but the expression stack currently holds ", m_expressionStack.size(), " values"); + + Vector<ExpressionType> args; + WASM_PARSER_FAIL_IF(!args.tryReserveCapacity(argumentCount), "can't allocate enough memory for ", argumentCount, " call_indirect arguments"); + size_t firstArgumentIndex = m_expressionStack.size() - argumentCount; + for (size_t i = firstArgumentIndex; i < m_expressionStack.size(); ++i) + args.uncheckedAppend(m_expressionStack[i]); + m_expressionStack.shrink(firstArgumentIndex); + + ExpressionType result = Context::emptyExpression; + WASM_TRY_ADD_TO_CONTEXT(addCallIndirect(calleeSignature, calleeSignatureIndex, args, result)); + + if (result != Context::emptyExpression) + m_expressionStack.append(result); + + return { }; + } + + case Block: { + Type inlineSignature; + WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get block's inline signature"); + m_controlStack.append({ WTFMove(m_expressionStack), m_context.addBlock(inlineSignature) }); + m_expressionStack = ExpressionList(); + return { }; + } + + case Loop: { + Type inlineSignature; + WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get loop's inline signature"); + m_controlStack.append({ WTFMove(m_expressionStack), m_context.addLoop(inlineSignature) }); + m_expressionStack = ExpressionList(); + return { }; + } + + case If: { + Type inlineSignature; + ExpressionType condition; + ControlType control; + WASM_PARSER_FAIL_IF(!parseResultType(inlineSignature), "can't get if's inline signature"); + WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "if condition"); + WASM_TRY_ADD_TO_CONTEXT(addIf(condition, inlineSignature, control)); + m_controlStack.append({ WTFMove(m_expressionStack), control }); + m_expressionStack = ExpressionList(); + return { }; + } + + case Else: { + WASM_PARSER_FAIL_IF(m_controlStack.size() == 1, "can't use else block at the top-level of a function"); + WASM_TRY_ADD_TO_CONTEXT(addElse(m_controlStack.last().controlData, m_expressionStack)); + m_expressionStack.shrink(0); + return { }; + } + + case Br: + case BrIf: { + uint32_t target; + ExpressionType condition = Context::emptyExpression; + WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get br / br_if's target"); + WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br / br_if's target ", target, " exceeds control stack size ", m_controlStack.size()); + if (op == BrIf) + WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br / br_if condition"); + else + m_unreachableBlocks = 1; + + ControlType& data = m_controlStack[m_controlStack.size() - 1 - target].controlData; + + WASM_TRY_ADD_TO_CONTEXT(addBranch(data, condition, m_expressionStack)); + return { }; + } + + case BrTable: { + uint32_t numberOfTargets; + uint32_t defaultTarget; + ExpressionType condition; + Vector<ControlType*> targets; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table"); + WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big ", numberOfTargets); + + WASM_PARSER_FAIL_IF(!targets.tryReserveCapacity(numberOfTargets), "can't allocate memory for ", numberOfTargets, " br_table targets"); + for (uint32_t i = 0; i < numberOfTargets; ++i) { + uint32_t target; + WASM_PARSER_FAIL_IF(!parseVarUInt32(target), "can't get ", i, "th target for br_table"); + WASM_PARSER_FAIL_IF(target >= m_controlStack.size(), "br_table's ", i, "th target ", target, " exceeds control stack size ", m_controlStack.size()); + targets.uncheckedAppend(&m_controlStack[m_controlStack.size() - 1 - target].controlData); + } + + WASM_PARSER_FAIL_IF(!parseVarUInt32(defaultTarget), "can't get default target for br_table"); + WASM_PARSER_FAIL_IF(defaultTarget >= m_controlStack.size(), "br_table's default target ", defaultTarget, " exceeds control stack size ", m_controlStack.size()); + + WASM_TRY_POP_EXPRESSION_STACK_INTO(condition, "br_table condition"); + WASM_TRY_ADD_TO_CONTEXT(addSwitch(condition, targets, m_controlStack[m_controlStack.size() - 1 - defaultTarget].controlData, m_expressionStack)); + + m_unreachableBlocks = 1; + return { }; + } + + case Return: { + ExpressionList returnValues; + if (m_signature->returnType() != Void) { + ExpressionType returnValue; + WASM_TRY_POP_EXPRESSION_STACK_INTO(returnValue, "return"); + returnValues.append(returnValue); + } + + WASM_TRY_ADD_TO_CONTEXT(addReturn(m_controlStack[0].controlData, returnValues)); + m_unreachableBlocks = 1; + return { }; + } + + case End: { + ControlEntry data = m_controlStack.takeLast(); + // FIXME: This is a little weird in that it will modify the expressionStack for the result of the block. + // That's a little too effectful for me but I don't have a better API right now. + // see: https://bugs.webkit.org/show_bug.cgi?id=164353 + WASM_TRY_ADD_TO_CONTEXT(endBlock(data, m_expressionStack)); + m_expressionStack.swap(data.enclosedExpressionStack); + return { }; + } + + case Unreachable: { + WASM_TRY_ADD_TO_CONTEXT(addUnreachable()); + m_unreachableBlocks = 1; + return { }; + } + + case Drop: { + WASM_PARSER_FAIL_IF(!m_expressionStack.size(), "can't drop on empty stack"); + m_expressionStack.takeLast(); + return { }; + } + + case Nop: { + return { }; + } + + case GrowMemory: { + WASM_PARSER_FAIL_IF(!m_info.memory, "grow_memory is only valid if a memory is defined or imported"); + + uint8_t reserved; + WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory"); + WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for grow_memory must be zero"); + + ExpressionType delta; + WASM_TRY_POP_EXPRESSION_STACK_INTO(delta, "expect an i32 argument to grow_memory on the stack"); + + ExpressionType result; + WASM_TRY_ADD_TO_CONTEXT(addGrowMemory(delta, result)); + m_expressionStack.append(result); + + return { }; + } + + case CurrentMemory: { + WASM_PARSER_FAIL_IF(!m_info.memory, "current_memory is only valid if a memory is defined or imported"); + + uint8_t reserved; + WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for current_memory"); + WASM_PARSER_FAIL_IF(reserved != 0, "reserved varUint1 for current_memory must be zero"); + + ExpressionType result; + WASM_TRY_ADD_TO_CONTEXT(addCurrentMemory(result)); + m_expressionStack.append(result); + + return { }; + } + } + + ASSERT_NOT_REACHED(); + return { }; +} + +// FIXME: We should try to use the same decoder function for both unreachable and reachable code. https://bugs.webkit.org/show_bug.cgi?id=165965 +template<typename Context> +auto FunctionParser<Context>::parseUnreachableExpression(OpType op) -> PartialResult +{ + ASSERT(m_unreachableBlocks); +#define CREATE_CASE(name, id, b3op, inc) case OpType::name: + switch (op) { + case Else: { + if (m_unreachableBlocks > 1) + return { }; + + ControlEntry& data = m_controlStack.last(); + m_unreachableBlocks = 0; + WASM_TRY_ADD_TO_CONTEXT(addElseToUnreachable(data.controlData)); + m_expressionStack.shrink(0); + return { }; + } + + case End: { + if (m_unreachableBlocks == 1) { + ControlEntry data = m_controlStack.takeLast(); + WASM_TRY_ADD_TO_CONTEXT(addEndToUnreachable(data)); + m_expressionStack.swap(data.enclosedExpressionStack); + } + m_unreachableBlocks--; + return { }; + } + + case Loop: + case If: + case Block: { + m_unreachableBlocks++; + Type unused; + WASM_PARSER_FAIL_IF(!parseResultType(unused), "can't get inline type for ", op, " in unreachable context"); + return { }; + } + + case BrTable: { + uint32_t numberOfTargets; + uint32_t unused; + WASM_PARSER_FAIL_IF(!parseVarUInt32(numberOfTargets), "can't get the number of targets for br_table in unreachable context"); + WASM_PARSER_FAIL_IF(numberOfTargets == std::numeric_limits<uint32_t>::max(), "br_table's number of targets is too big ", numberOfTargets); + + for (uint32_t i = 0; i < numberOfTargets; ++i) + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get ", i, "th target for br_table in unreachable context"); + + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get default target for br_table in unreachable context"); + return { }; + } + + case CallIndirect: { + uint32_t unused; + uint8_t unused2; + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get call_indirect's signature index in unreachable context"); + WASM_PARSER_FAIL_IF(!parseVarUInt1(unused2), "can't get call_indirect's reserved byte in unreachable context"); + return { }; + } + + case F32Const: { + uint32_t unused; + WASM_PARSER_FAIL_IF(!parseUInt32(unused), "can't parse 32-bit floating-point constant"); + return { }; + } + + case F64Const: { + uint64_t constant; + WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't parse 64-bit floating-point constant"); + return { }; + } + + // two immediate cases + FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_CASE) + FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_CASE) { + uint32_t unused; + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get first immediate for ", op, " in unreachable context"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get second immediate for ", op, " in unreachable context"); + return { }; + } + + // one immediate cases + case I32Const: + case I64Const: + case SetLocal: + case GetLocal: + case TeeLocal: + case GetGlobal: + case SetGlobal: + case Br: + case BrIf: + case Call: { + uint32_t unused; + WASM_PARSER_FAIL_IF(!parseVarUInt32(unused), "can't get immediate for ", op, " in unreachable context"); + return { }; + } + + case GrowMemory: + case CurrentMemory: { + uint8_t reserved; + WASM_PARSER_FAIL_IF(!parseVarUInt1(reserved), "can't parse reserved varUint1 for grow_memory/current_memory"); + return { }; + } + + // no immediate cases + FOR_EACH_WASM_BINARY_OP(CREATE_CASE) + FOR_EACH_WASM_UNARY_OP(CREATE_CASE) + case Unreachable: + case Nop: + case Return: + case Select: + case Drop: { + return { }; + } + } +#undef CREATE_CASE + RELEASE_ASSERT_NOT_REACHED(); +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmMemory.cpp b/Source/JavaScriptCore/wasm/WasmMemory.cpp new file mode 100644 index 000000000..aad91c13e --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmMemory.cpp @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2016 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 "WasmMemory.h" + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/HexNumber.h> +#include <wtf/PrintStream.h> +#include <wtf/text/WTFString.h> + +namespace JSC { namespace Wasm { + +namespace { +const bool verbose = false; +} + +void Memory::dump(PrintStream& out) const +{ + String memoryHex; + WTF::appendUnsigned64AsHex((uint64_t)(uintptr_t)m_memory, memoryHex); + out.print("Memory at 0x", memoryHex, ", size ", m_size, "B capacity ", m_mappedCapacity, "B, initial ", m_initial, " maximum ", m_maximum, " mode ", makeString(m_mode)); +} + +const char* Memory::makeString(Mode mode) const +{ + switch (mode) { + case Mode::BoundsChecking: return "BoundsChecking"; + } + RELEASE_ASSERT_NOT_REACHED(); + return ""; +} + +static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^32 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold."); + +Memory::Memory(PageCount initial, PageCount maximum, bool& failed) + : m_size(initial.bytes()) + , m_initial(initial) + , m_maximum(maximum) + , m_mode(Mode::BoundsChecking) + // FIXME: If we add signal based bounds checking then we need extra space for overflow on load. + // see: https://bugs.webkit.org/show_bug.cgi?id=162693 +{ + RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller. + + m_mappedCapacity = maximum ? maximum.bytes() : PageCount::max().bytes(); + if (!m_mappedCapacity) { + // This means we specified a zero as maximum (which means we also have zero as initial size). + RELEASE_ASSERT(m_size == 0); + m_memory = nullptr; + m_mappedCapacity = 0; + failed = false; + if (verbose) + dataLogLn("Memory::Memory allocating nothing ", *this); + return; + } + + // FIXME: It would be nice if we had a VM tag for wasm memory. https://bugs.webkit.org/show_bug.cgi?id=163600 + void* result = Options::simulateWebAssemblyLowMemory() ? MAP_FAILED : mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) { + // Try again with a different number. + if (verbose) + dataLogLn("Memory::Memory mmap failed once for capacity, trying again", *this); + m_mappedCapacity = m_size; + if (!m_mappedCapacity) { + m_memory = nullptr; + failed = false; + if (verbose) + dataLogLn("Memory::Memory mmap not trying again because size is zero ", *this); + return; + } + + result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (result == MAP_FAILED) { + if (verbose) + dataLogLn("Memory::Memory mmap failed twice ", *this); + failed = true; + return; + } + } + + ASSERT(m_size <= m_mappedCapacity); + { + bool success = !mprotect(result, static_cast<size_t>(m_size), PROT_READ | PROT_WRITE); + RELEASE_ASSERT(success); + } + + m_memory = result; + failed = false; + if (verbose) + dataLogLn("Memory::Memory mmap succeeded ", *this); +} + +Memory::~Memory() +{ + if (verbose) + dataLogLn("Memory::~Memory ", *this); + if (m_memory) { + if (munmap(m_memory, m_mappedCapacity)) + CRASH(); + } +} + +bool Memory::grow(PageCount newSize) +{ + RELEASE_ASSERT(newSize > PageCount::fromBytes(m_size)); + + if (verbose) + dataLogLn("Memory::grow to ", newSize, " from ", *this); + + if (maximum() && newSize > maximum()) + return false; + + uint64_t desiredSize = newSize.bytes(); + + if (m_memory && desiredSize <= m_mappedCapacity) { + bool success = !mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE); + RELEASE_ASSERT(success); + m_size = desiredSize; + if (verbose) + dataLogLn("Memory::grow in-place ", *this); + return true; + } + + // Otherwise, let's try to make some new memory. + void* newMemory = mmap(nullptr, desiredSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + if (newMemory == MAP_FAILED) + return false; + + if (m_memory) { + memcpy(newMemory, m_memory, m_size); + bool success = !munmap(m_memory, m_mappedCapacity); + RELEASE_ASSERT(success); + } + m_memory = newMemory; + m_mappedCapacity = desiredSize; + m_size = desiredSize; + + if (verbose) + dataLogLn("Memory::grow ", *this); + return true; +} + +} // namespace JSC + +} // namespace Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmMemory.h b/Source/JavaScriptCore/wasm/WasmMemory.h new file mode 100644 index 000000000..236bf8512 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmMemory.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "WasmCallingConvention.h" +#include "WasmPageCount.h" + +namespace WTF { +class PrintStream; +} + +namespace JSC { namespace Wasm { + +class Memory { + WTF_MAKE_NONCOPYABLE(Memory); + WTF_MAKE_FAST_ALLOCATED; +public: + void dump(WTF::PrintStream&) const; + + // FIXME: We should support other modes. see: https://bugs.webkit.org/show_bug.cgi?id=162693 + enum class Mode { + BoundsChecking + }; + const char* makeString(Mode) const; + + Memory() = default; + JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum, bool& failed); + Memory(Memory&& other) + : m_memory(other.m_memory) + , m_size(other.m_size) + , m_initial(other.m_initial) + , m_maximum(other.m_maximum) + , m_mappedCapacity(other.m_mappedCapacity) + , m_mode(other.m_mode) + { + // Moving transfers ownership of the allocated memory. + other.m_memory = nullptr; + } + ~Memory(); + + void* memory() const { return m_memory; } + uint64_t size() const { return m_size; } + PageCount sizeInPages() const { return PageCount::fromBytes(m_size); } + + PageCount initial() const { return m_initial; } + PageCount maximum() const { return m_maximum; } + + Mode mode() const { return m_mode; } + + bool grow(PageCount); + + static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Memory, m_memory); } + static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); } + +private: + void* m_memory { nullptr }; + uint64_t m_size { 0 }; + PageCount m_initial; + PageCount m_maximum; + uint64_t m_mappedCapacity { 0 }; + Mode m_mode { Mode::BoundsChecking }; +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMLY) diff --git a/Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp b/Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp new file mode 100644 index 000000000..6b063d351 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 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 "WasmMemoryInformation.h" + +#if ENABLE(WEBASSEMBLY) + +#include "WasmCallingConvention.h" +#include <wtf/NeverDestroyed.h> + +namespace JSC { namespace Wasm { + +const PinnedRegisterInfo& PinnedRegisterInfo::get() +{ + static LazyNeverDestroyed<PinnedRegisterInfo> staticPinnedRegisterInfo; + static std::once_flag staticPinnedRegisterInfoFlag; + std::call_once(staticPinnedRegisterInfoFlag, [] () { + Vector<PinnedSizeRegisterInfo> sizeRegisters; + GPRReg baseMemoryPointer; + + // FIXME: We should support more than one memory size register, and we should allow different + // WebAssembly.Instance to have different pins. Right now we take a vector with only one entry. + // If we have more than one size register, we can have one for each load size class. + // see: https://bugs.webkit.org/show_bug.cgi?id=162952 + Vector<unsigned> pinnedSizes = { 0 }; + unsigned remainingPinnedRegisters = pinnedSizes.size() + 1; + jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) { + GPRReg gpr = reg.gpr(); + if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg)) + return; + if (remainingPinnedRegisters == 1) { + baseMemoryPointer = gpr; + remainingPinnedRegisters--; + } else + sizeRegisters.append({ gpr, pinnedSizes[--remainingPinnedRegisters - 1] }); + }); + + ASSERT(!remainingPinnedRegisters); + staticPinnedRegisterInfo.construct(WTFMove(sizeRegisters), baseMemoryPointer); + }); + + return staticPinnedRegisterInfo.get(); +} + +PinnedRegisterInfo::PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&& sizeRegisters, GPRReg baseMemoryPointer) + : sizeRegisters(WTFMove(sizeRegisters)) + , baseMemoryPointer(baseMemoryPointer) +{ +} + +MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum, bool isImport) + : m_initial(initial) + , m_maximum(maximum) + , m_isImport(isImport) +{ + RELEASE_ASSERT(!!m_initial); + RELEASE_ASSERT(!m_maximum || m_maximum >= m_initial); + ASSERT(!!*this); +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmMemoryInformation.h b/Source/JavaScriptCore/wasm/WasmMemoryInformation.h new file mode 100644 index 000000000..6e7f52189 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmMemoryInformation.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "GPRInfo.h" +#include "WasmPageCount.h" +#include <wtf/Vector.h> + +namespace JSC { namespace Wasm { + +struct PinnedSizeRegisterInfo { + GPRReg sizeRegister; + unsigned sizeOffset; +}; + +struct PinnedRegisterInfo { + Vector<PinnedSizeRegisterInfo> sizeRegisters; + GPRReg baseMemoryPointer; + static const PinnedRegisterInfo& get(); + PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&&, GPRReg); +}; + +class MemoryInformation { +public: + MemoryInformation() + { + ASSERT(!*this); + } + + MemoryInformation(PageCount initial, PageCount maximum, bool isImport); + + PageCount initial() const { return m_initial; } + PageCount maximum() const { return m_maximum; } + bool isImport() const { return m_isImport; } + + explicit operator bool() const { return !!m_initial; } + +private: + PageCount m_initial { }; + PageCount m_maximum { }; + bool m_isImport { false }; +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WASM) diff --git a/Source/JavaScriptCore/wasm/WasmModuleParser.cpp b/Source/JavaScriptCore/wasm/WasmModuleParser.cpp new file mode 100644 index 000000000..00fac1648 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmModuleParser.cpp @@ -0,0 +1,629 @@ +/* + * Copyright (C) 2016 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 "WasmModuleParser.h" + +#if ENABLE(WEBASSEMBLY) + +#include "IdentifierInlines.h" +#include "JSWebAssemblyTable.h" +#include "WasmFormat.h" +#include "WasmMemoryInformation.h" +#include "WasmOps.h" +#include "WasmSections.h" + +#include <sys/mman.h> + +namespace JSC { namespace Wasm { + +ALWAYS_INLINE I32InitExpr makeI32InitExpr(uint8_t opcode, uint32_t bits) +{ + RELEASE_ASSERT(opcode == I32Const || opcode == GetGlobal); + if (opcode == I32Const) + return I32InitExpr::constValue(bits); + return I32InitExpr::globalImport(bits); +} + +auto ModuleParser::parse() -> Result +{ + m_result.module = std::make_unique<ModuleInformation>(); + const size_t minSize = 8; + uint32_t versionNumber; + + WASM_PARSER_FAIL_IF(length() < minSize, "expected a module of at least ", minSize, " bytes"); + WASM_PARSER_FAIL_IF(!consumeCharacter(0) || !consumeString("asm"), "modules doesn't start with '\\0asm'"); + WASM_PARSER_FAIL_IF(!parseUInt32(versionNumber), "can't parse version number"); + WASM_PARSER_FAIL_IF(versionNumber != expectedVersionNumber, "unexpected version number ", versionNumber, " expected ", expectedVersionNumber); + + Section previousSection = Section::Unknown; + while (m_offset < length()) { + uint8_t sectionByte; + + WASM_PARSER_FAIL_IF(!parseUInt7(sectionByte), "can't get section byte"); + + Section section = Section::Unknown; + if (sectionByte) { + if (isValidSection(sectionByte)) + section = static_cast<Section>(sectionByte); + } + + uint32_t sectionLength; + WASM_PARSER_FAIL_IF(!validateOrder(previousSection, section), "invalid section order, ", previousSection, " followed by ", section); + WASM_PARSER_FAIL_IF(!parseVarUInt32(sectionLength), "can't get ", section, " section's length"); + WASM_PARSER_FAIL_IF(sectionLength > length() - m_offset, section, "section of size ", sectionLength, " would overflow Module's size"); + + auto end = m_offset + sectionLength; + + switch (section) { +#define WASM_SECTION_PARSE(NAME, ID, DESCRIPTION) \ + case Section::NAME: { \ + WASM_FAIL_IF_HELPER_FAILS(parse ## NAME()); \ + break; \ + } + FOR_EACH_WASM_SECTION(WASM_SECTION_PARSE) +#undef WASM_SECTION_PARSE + + case Section::Unknown: { + WASM_FAIL_IF_HELPER_FAILS(parseCustom(sectionLength)); + break; + } + } + + WASM_PARSER_FAIL_IF(end != m_offset, "parsing ended before the end of ", section, " section"); + + previousSection = section; + } + + return WTFMove(m_result); +} + +auto ModuleParser::parseType() -> PartialResult +{ + uint32_t count; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Type section's count"); + WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Type section's count is too big ", count); + WASM_PARSER_FAIL_IF(!m_result.moduleSignatureIndicesToUniquedSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for Type section's ", count, " entries"); + + for (uint32_t i = 0; i < count; ++i) { + int8_t type; + uint32_t argumentCount; + Vector<Type> argumentTypes; + + WASM_PARSER_FAIL_IF(!parseInt7(type), "can't get ", i, "th Type's type"); + WASM_PARSER_FAIL_IF(type != Func, i, "th Type is non-Func ", type); + WASM_PARSER_FAIL_IF(!parseVarUInt32(argumentCount), "can't get ", i, "th Type's argument count"); + WASM_PARSER_FAIL_IF(argumentCount == std::numeric_limits<uint32_t>::max(), i, "th argument count is too big ", argumentCount); + std::unique_ptr<Signature, void (*)(Signature*)> signature(Signature::create(argumentCount), &Signature::destroy); + WASM_PARSER_FAIL_IF(!signature, "can't allocate enough memory for Type section's ", i, "th signature"); + + for (unsigned i = 0; i < argumentCount; ++i) { + Type argumentType; + WASM_PARSER_FAIL_IF(!parseResultType(argumentType), "can't get ", i, "th argument Type"); + signature->argument(i) = argumentType; + } + + uint8_t returnCount; + WASM_PARSER_FAIL_IF(!parseVarUInt1(returnCount), "can't get ", i, "th Type's return count"); + Type returnType; + if (returnCount) { + Type value; + WASM_PARSER_FAIL_IF(!parseValueType(value), "can't get ", i, "th Type's return value"); + returnType = static_cast<Type>(value); + } else + returnType = Type::Void; + signature->returnType() = returnType; + + SignatureIndex signatureIndex = SignatureInformation::adopt(m_vm, signature.release()); + m_result.moduleSignatureIndicesToUniquedSignatureIndices.uncheckedAppend(signatureIndex); + } + return { }; +} + +auto ModuleParser::parseImport() -> PartialResult +{ + uint32_t importCount; + WASM_PARSER_FAIL_IF(!parseVarUInt32(importCount), "can't get Import section's count"); + WASM_PARSER_FAIL_IF(importCount == std::numeric_limits<uint32_t>::max(), "Import section's count is too big ", importCount); + WASM_PARSER_FAIL_IF(!m_result.module->globals.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " globals"); // FIXME this over-allocates when we fix the FIXMEs below. + WASM_PARSER_FAIL_IF(!m_result.module->imports.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " imports"); // FIXME this over-allocates when we fix the FIXMEs below. + WASM_PARSER_FAIL_IF(!m_result.module->importFunctionSignatureIndices.tryReserveCapacity(importCount), "can't allocate enough memory for ", importCount, " import function signatures"); // FIXME this over-allocates when we fix the FIXMEs below. + + for (uint32_t importNumber = 0; importNumber < importCount; ++importNumber) { + Import imp; + uint32_t moduleLen; + uint32_t fieldLen; + String moduleString; + String fieldString; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(moduleLen), "can't get ", importNumber, "th Import's module name length"); + WASM_PARSER_FAIL_IF(!consumeUTF8String(moduleString, moduleLen), "can't get ", importNumber, "th Import's module name of length ", moduleLen); + imp.module = Identifier::fromString(m_vm, moduleString); + + WASM_PARSER_FAIL_IF(!parseVarUInt32(fieldLen), "can't get ", importNumber, "th Import's field name length in module '", moduleString, "'"); + WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", importNumber, "th Import's field name of length ", moduleLen, " in module '", moduleString, "'"); + imp.field = Identifier::fromString(m_vm, fieldString); + + WASM_PARSER_FAIL_IF(!parseExternalKind(imp.kind), "can't get ", importNumber, "th Import's kind in module '", moduleString, "' field '", fieldString, "'"); + switch (imp.kind) { + case ExternalKind::Function: { + uint32_t functionSignatureIndex; + WASM_PARSER_FAIL_IF(!parseVarUInt32(functionSignatureIndex), "can't get ", importNumber, "th Import's function signature in module '", moduleString, "' field '", fieldString, "'"); + WASM_PARSER_FAIL_IF(functionSignatureIndex >= m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), "invalid function signature for ", importNumber, "th Import, ", functionSignatureIndex, " is out of range of ", m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), " in module '", moduleString, "' field '", fieldString, "'"); + imp.kindIndex = m_result.module->importFunctionSignatureIndices.size(); + SignatureIndex signatureIndex = m_result.moduleSignatureIndicesToUniquedSignatureIndices[functionSignatureIndex]; + m_result.module->importFunctionSignatureIndices.uncheckedAppend(signatureIndex); + break; + } + case ExternalKind::Table: { + bool isImport = true; + PartialResult result = parseTableHelper(isImport); + if (UNLIKELY(!result)) + return result.getUnexpected(); + break; + } + case ExternalKind::Memory: { + bool isImport = true; + PartialResult result = parseMemoryHelper(isImport); + if (UNLIKELY(!result)) + return result.getUnexpected(); + break; + } + case ExternalKind::Global: { + Global global; + WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global)); + WASM_PARSER_FAIL_IF(global.mutability == Global::Mutable, "Mutable Globals aren't supported"); + + imp.kindIndex = m_result.module->globals.size(); + m_result.module->globals.uncheckedAppend(WTFMove(global)); + break; + } + } + + m_result.module->imports.uncheckedAppend(imp); + } + + m_result.module->firstInternalGlobal = m_result.module->globals.size(); + return { }; +} + +auto ModuleParser::parseFunction() -> PartialResult +{ + uint32_t count; + WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Function section's count"); + WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Function section's count is too big ", count); + WASM_PARSER_FAIL_IF(!m_result.module->internalFunctionSignatureIndices.tryReserveCapacity(count), "can't allocate enough memory for ", count, " Function signatures"); + WASM_PARSER_FAIL_IF(!m_result.functionLocationInBinary.tryReserveCapacity(count), "can't allocate enough memory for ", count, "Function locations"); + + for (uint32_t i = 0; i < count; ++i) { + uint32_t typeNumber; + WASM_PARSER_FAIL_IF(!parseVarUInt32(typeNumber), "can't get ", i, "th Function's type number"); + WASM_PARSER_FAIL_IF(typeNumber >= m_result.moduleSignatureIndicesToUniquedSignatureIndices.size(), i, "th Function type number is invalid ", typeNumber); + + SignatureIndex signatureIndex = m_result.moduleSignatureIndicesToUniquedSignatureIndices[typeNumber]; + // The Code section fixes up start and end. + size_t start = 0; + size_t end = 0; + m_result.module->internalFunctionSignatureIndices.uncheckedAppend(signatureIndex); + m_result.functionLocationInBinary.uncheckedAppend({ start, end }); + } + + return { }; +} + +auto ModuleParser::parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum) -> PartialResult +{ + ASSERT(!maximum); + + uint8_t flags; + WASM_PARSER_FAIL_IF(!parseVarUInt1(flags), "can't parse resizable limits flags"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(initial), "can't parse resizable limits initial page count"); + + if (flags) { + uint32_t maximumInt; + WASM_PARSER_FAIL_IF(!parseVarUInt32(maximumInt), "can't parse resizable limits maximum page count"); + WASM_PARSER_FAIL_IF(initial > maximumInt, "resizable limits has a initial page count of ", initial, " which is greater than its maximum ", maximumInt); + maximum = maximumInt; + } + + return { }; +} + +auto ModuleParser::parseTableHelper(bool isImport) -> PartialResult +{ + WASM_PARSER_FAIL_IF(m_hasTable, "Table section cannot exist if an Import has a table"); + + m_hasTable = true; + + int8_t type; + WASM_PARSER_FAIL_IF(!parseInt7(type), "can't parse Table type"); + WASM_PARSER_FAIL_IF(type != Wasm::Anyfunc, "Table type should be anyfunc, got ", type); + + uint32_t initial; + std::optional<uint32_t> maximum; + PartialResult limits = parseResizableLimits(initial, maximum); + if (UNLIKELY(!limits)) + return limits.getUnexpected(); + WASM_PARSER_FAIL_IF(!JSWebAssemblyTable::isValidSize(initial), "Table's initial page count of ", initial, " is invalid"); + + ASSERT(!maximum || *maximum >= initial); + + m_result.module->tableInformation = TableInformation(initial, maximum, isImport); + + return { }; +} + +auto ModuleParser::parseTable() -> PartialResult +{ + uint32_t count; + WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Table's count"); + WASM_PARSER_FAIL_IF(count != 1, "Table count of ", count, " is invalid, only 1 is allowed for now"); + + bool isImport = false; + PartialResult result = parseTableHelper(isImport); + if (UNLIKELY(!result)) + return result.getUnexpected(); + + return { }; +} + +auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult +{ + WASM_PARSER_FAIL_IF(!!m_result.module->memory, "Memory section cannot exist if an Import has a memory"); + + PageCount initialPageCount; + PageCount maximumPageCount; + { + uint32_t initial; + std::optional<uint32_t> maximum; + PartialResult limits = parseResizableLimits(initial, maximum); + if (UNLIKELY(!limits)) + return limits.getUnexpected(); + ASSERT(!maximum || *maximum >= initial); + WASM_PARSER_FAIL_IF(!PageCount::isValid(initial), "Memory's initial page count of ", initial, " is invalid"); + + initialPageCount = PageCount(initial); + + if (maximum) { + WASM_PARSER_FAIL_IF(!PageCount::isValid(*maximum), "Memory's maximum page count of ", *maximum, " is invalid"); + maximumPageCount = PageCount(*maximum); + } + } + ASSERT(initialPageCount); + ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount); + + m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport); + return { }; +} + +auto ModuleParser::parseMemory() -> PartialResult +{ + uint8_t count; + WASM_PARSER_FAIL_IF(!parseVarUInt1(count), "can't parse Memory section's count"); + + if (!count) + return { }; + + WASM_PARSER_FAIL_IF(count != 1, "Memory section has more than one memory, WebAssembly currently only allows zero or one"); + + bool isImport = false; + return parseMemoryHelper(isImport); +} + +auto ModuleParser::parseGlobal() -> PartialResult +{ + uint32_t globalCount; + WASM_PARSER_FAIL_IF(!parseVarUInt32(globalCount), "can't get Global section's count"); + WASM_PARSER_FAIL_IF(!m_result.module->globals.tryReserveCapacity(globalCount + m_result.module->firstInternalGlobal), "can't allocate memory for ", globalCount + m_result.module->firstInternalGlobal, " globals"); + + for (uint32_t globalIndex = 0; globalIndex < globalCount; ++globalIndex) { + Global global; + uint8_t initOpcode; + + WASM_FAIL_IF_HELPER_FAILS(parseGlobalType(global)); + Type typeForInitOpcode; + WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, global.initialBitsOrImportNumber, typeForInitOpcode)); + if (initOpcode == GetGlobal) + global.initializationType = Global::FromGlobalImport; + else + global.initializationType = Global::FromExpression; + WASM_PARSER_FAIL_IF(typeForInitOpcode != global.type, "Global init_expr opcode of type ", typeForInitOpcode, " doesn't match global's type ", global.type); + + m_result.module->globals.uncheckedAppend(WTFMove(global)); + } + + return { }; +} + +auto ModuleParser::parseExport() -> PartialResult +{ + uint32_t exportCount; + WASM_PARSER_FAIL_IF(!parseVarUInt32(exportCount), "can't get Export section's count"); + WASM_PARSER_FAIL_IF(exportCount == std::numeric_limits<uint32_t>::max(), "Export section's count is too big ", exportCount); + WASM_PARSER_FAIL_IF(!m_result.module->exports.tryReserveCapacity(exportCount), "can't allocate enough memory for ", exportCount, " exports"); + + HashSet<String> exportNames; + for (uint32_t exportNumber = 0; exportNumber < exportCount; ++exportNumber) { + Export exp; + uint32_t fieldLen; + String fieldString; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(fieldLen), "can't get ", exportNumber, "th Export's field name length"); + WASM_PARSER_FAIL_IF(!consumeUTF8String(fieldString, fieldLen), "can't get ", exportNumber, "th Export's field name of length ", fieldLen); + WASM_PARSER_FAIL_IF(exportNames.contains(fieldString), "duplicate export: '", fieldString, "'"); + exportNames.add(fieldString); + exp.field = Identifier::fromString(m_vm, fieldString); + + WASM_PARSER_FAIL_IF(!parseExternalKind(exp.kind), "can't get ", exportNumber, "th Export's kind, named '", fieldString, "'"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(exp.kindIndex), "can't get ", exportNumber, "th Export's kind index, named '", fieldString, "'"); + switch (exp.kind) { + case ExternalKind::Function: { + WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.module->functionIndexSpaceSize(), exportNumber, "th Export has invalid function number ", exp.kindIndex, " it exceeds the function index space ", m_result.module->functionIndexSpaceSize(), ", named '", fieldString, "'"); + break; + } + case ExternalKind::Table: { + WASM_PARSER_FAIL_IF(!m_hasTable, "can't export a non-existent Table"); + WASM_PARSER_FAIL_IF(exp.kindIndex, "can't export Table ", exp.kindIndex, " only zero-index Table is currently supported"); + break; + } + case ExternalKind::Memory: { + WASM_PARSER_FAIL_IF(!m_result.module->memory, "can't export a non-existent Memory"); + WASM_PARSER_FAIL_IF(exp.kindIndex, "can't export Memory ", exp.kindIndex, " only one Table is currently supported"); + break; + } + case ExternalKind::Global: { + WASM_PARSER_FAIL_IF(exp.kindIndex >= m_result.module->globals.size(), exportNumber, "th Export has invalid global number ", exp.kindIndex, " it exceeds the globals count ", m_result.module->globals.size(), ", named '", fieldString, "'"); + WASM_PARSER_FAIL_IF(m_result.module->globals[exp.kindIndex].mutability != Global::Immutable, exportNumber, "th Export isn't immutable, named '", fieldString, "'"); + break; + } + } + + m_result.module->exports.uncheckedAppend(exp); + } + + return { }; +} + +auto ModuleParser::parseStart() -> PartialResult +{ + uint32_t startFunctionIndex; + WASM_PARSER_FAIL_IF(!parseVarUInt32(startFunctionIndex), "can't get Start index"); + WASM_PARSER_FAIL_IF(startFunctionIndex >= m_result.module->functionIndexSpaceSize(), "Start index ", startFunctionIndex, " exceeds function index space ", m_result.module->functionIndexSpaceSize()); + SignatureIndex signatureIndex = m_result.module->signatureIndexFromFunctionIndexSpace(startFunctionIndex); + const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); + WASM_PARSER_FAIL_IF(signature->argumentCount(), "Start function can't have arguments"); + WASM_PARSER_FAIL_IF(signature->returnType() != Void, "Start function can't return a value"); + m_result.module->startFunctionIndexSpace = startFunctionIndex; + return { }; +} + +auto ModuleParser::parseElement() -> PartialResult +{ + WASM_PARSER_FAIL_IF(!m_hasTable, "Element section expects a Table to be present"); + + uint32_t elementCount; + WASM_PARSER_FAIL_IF(!parseVarUInt32(elementCount), "can't get Element section's count"); + WASM_PARSER_FAIL_IF(elementCount == std::numeric_limits<uint32_t>::max(), "Element section's count is too big ", elementCount); + WASM_PARSER_FAIL_IF(!m_result.module->elements.tryReserveCapacity(elementCount), "can't allocate memory for ", elementCount, " Elements"); + for (unsigned elementNum = 0; elementNum < elementCount; ++elementNum) { + uint32_t tableIndex; + uint64_t offset; + uint8_t initOpcode; + uint32_t indexCount; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(tableIndex), "can't get ", elementNum, "th Element table index"); + WASM_PARSER_FAIL_IF(tableIndex, "Element section can only have one Table for now"); + Type initExprType; + WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, offset, initExprType)); + WASM_PARSER_FAIL_IF(initOpcode != OpType::I32Const, "Element section doesn't support non-i32 init_expr opcode for now, got ", initOpcode); + WASM_PARSER_FAIL_IF(!parseVarUInt32(indexCount), "can't get ", elementNum, "th index count for Element section"); + WASM_PARSER_FAIL_IF(indexCount == std::numeric_limits<uint32_t>::max(), "Element section's ", elementNum, "th index count is too big ", indexCount); + + ASSERT(!!m_result.module->tableInformation); + if (std::optional<uint32_t> maximum = m_result.module->tableInformation.maximum()) { + // FIXME: should indexCount being zero be a validation error? + // https://bugs.webkit.org/show_bug.cgi?id=165826 + if (indexCount) { + // FIXME: right now, provably out of bounds writes are validation errors. + // Should they be though? + // https://bugs.webkit.org/show_bug.cgi?id=165827 + uint64_t lastWrittenIndex = static_cast<uint64_t>(indexCount) + static_cast<uint64_t>(offset) - 1; + WASM_PARSER_FAIL_IF(lastWrittenIndex >= static_cast<uint64_t>(*maximum), "Element section's ", elementNum, "th element writes to index ", lastWrittenIndex, " which exceeds the maximum ", *maximum); + } + } + + Element element; + WASM_PARSER_FAIL_IF(!element.functionIndices.tryReserveCapacity(indexCount), "can't allocate memory for ", indexCount, " Element indices"); + + element.offset = offset; + + for (unsigned index = 0; index < indexCount; ++index) { + uint32_t functionIndex; + WASM_PARSER_FAIL_IF(!parseVarUInt32(functionIndex), "can't get Element section's ", elementNum, "th element's ", index, "th index"); + WASM_PARSER_FAIL_IF(functionIndex >= m_result.module->functionIndexSpaceSize(), "Element section's ", elementNum, "th element's ", index, "th index is ", functionIndex, " which exceeds the function index space size of ", m_result.module->functionIndexSpaceSize()); + + element.functionIndices.uncheckedAppend(functionIndex); + } + + m_result.module->elements.uncheckedAppend(WTFMove(element)); + } + + return { }; +} + +auto ModuleParser::parseCode() -> PartialResult +{ + uint32_t count; + WASM_PARSER_FAIL_IF(!parseVarUInt32(count), "can't get Code section's count"); + WASM_PARSER_FAIL_IF(count == std::numeric_limits<uint32_t>::max(), "Code section's count is too big ", count); + WASM_PARSER_FAIL_IF(count != m_result.functionLocationInBinary.size(), "Code section count ", count, " exceeds the declared number of functions ", m_result.functionLocationInBinary.size()); + + for (uint32_t i = 0; i < count; ++i) { + uint32_t functionSize; + WASM_PARSER_FAIL_IF(!parseVarUInt32(functionSize), "can't get ", i, "th Code function's size"); + WASM_PARSER_FAIL_IF(functionSize > length(), "Code function's size ", functionSize, " exceeds the module's size ", length()); + WASM_PARSER_FAIL_IF(functionSize > length() - m_offset, "Code function's size ", functionSize, " exceeds the module's remaining size", length() - m_offset); + + m_result.functionLocationInBinary[i].start = m_offset; + m_result.functionLocationInBinary[i].end = m_offset + functionSize; + m_offset = m_result.functionLocationInBinary[i].end; + } + + return { }; +} + +auto ModuleParser::parseInitExpr(uint8_t& opcode, uint64_t& bitsOrImportNumber, Type& resultType) -> PartialResult +{ + WASM_PARSER_FAIL_IF(!parseUInt8(opcode), "can't get init_expr's opcode"); + + switch (opcode) { + case I32Const: { + int32_t constant; + WASM_PARSER_FAIL_IF(!parseVarInt32(constant), "can't get constant value for init_expr's i32.const"); + bitsOrImportNumber = static_cast<uint64_t>(constant); + resultType = I32; + break; + } + + case I64Const: { + int64_t constant; + WASM_PARSER_FAIL_IF(!parseVarInt64(constant), "can't get constant value for init_expr's i64.const"); + bitsOrImportNumber = constant; + resultType = I64; + break; + } + + case F32Const: { + uint32_t constant; + WASM_PARSER_FAIL_IF(!parseUInt32(constant), "can't get constant value for init_expr's f32.const"); + bitsOrImportNumber = constant; + resultType = F32; + break; + } + + case F64Const: { + uint64_t constant; + WASM_PARSER_FAIL_IF(!parseUInt64(constant), "can't get constant value for init_expr's f64.const"); + bitsOrImportNumber = constant; + resultType = F64; + break; + } + + case GetGlobal: { + uint32_t index; + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get get_global's index"); + WASM_PARSER_FAIL_IF(index >= m_result.module->imports.size(), "get_global's index ", index, " exceeds the number of imports ", m_result.module->imports.size()); + const Import& import = m_result.module->imports[index]; + WASM_PARSER_FAIL_IF(m_result.module->imports[index].kind != ExternalKind::Global, "get_global's import kind is ", m_result.module->imports[index].kind, " should be global"); + WASM_PARSER_FAIL_IF(import.kindIndex >= m_result.module->firstInternalGlobal, "get_global import kind index ", import.kindIndex, " exceeds the first internal global ", m_result.module->firstInternalGlobal); + + ASSERT(m_result.module->globals[import.kindIndex].mutability == Global::Immutable); + resultType = m_result.module->globals[index].type; + bitsOrImportNumber = index; + break; + } + + default: + WASM_PARSER_FAIL_IF(false, "unknown init_expr opcode ", opcode); + } + + uint8_t endOpcode; + WASM_PARSER_FAIL_IF(!parseUInt8(endOpcode), "can't get init_expr's end opcode"); + WASM_PARSER_FAIL_IF(endOpcode != OpType::End, "init_expr should end with end, ended with ", endOpcode); + + return { }; +} + +auto ModuleParser::parseGlobalType(Global& global) -> PartialResult +{ + uint8_t mutability; + WASM_PARSER_FAIL_IF(!parseValueType(global.type), "can't get Global's value type"); + WASM_PARSER_FAIL_IF(!parseVarUInt1(mutability), "can't get Global type's mutability"); + global.mutability = static_cast<Global::Mutability>(mutability); + return { }; +} + +auto ModuleParser::parseData() -> PartialResult +{ + uint32_t segmentCount; + WASM_PARSER_FAIL_IF(!m_result.module->memory, "Data section cannot exist without a Memory section or Import"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(segmentCount), "can't get Data section's count"); + WASM_PARSER_FAIL_IF(segmentCount == std::numeric_limits<uint32_t>::max(), "Data section's count is too big ", segmentCount); + WASM_PARSER_FAIL_IF(!m_result.module->data.tryReserveCapacity(segmentCount), "can't allocate enough memory for Data section's ", segmentCount, " segments"); + + for (uint32_t segmentNumber = 0; segmentNumber < segmentCount; ++segmentNumber) { + uint32_t index; + uint64_t initExprBits; + uint8_t initOpcode; + uint32_t dataByteLength; + + WASM_PARSER_FAIL_IF(!parseVarUInt32(index), "can't get ", segmentNumber, "th Data segment's index"); + WASM_PARSER_FAIL_IF(index, segmentNumber, "th Data segment has non-zero index ", index); + Type initExprType; + WASM_FAIL_IF_HELPER_FAILS(parseInitExpr(initOpcode, initExprBits, initExprType)); + WASM_PARSER_FAIL_IF(initExprType != I32, segmentNumber, "th Data segment's init_expr must produce an i32"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(dataByteLength), "can't get ", segmentNumber, "th Data segment's data byte length"); + WASM_PARSER_FAIL_IF(dataByteLength == std::numeric_limits<uint32_t>::max(), segmentNumber, "th Data segment's data byte length is too big ", dataByteLength); + + Segment* segment = Segment::create(makeI32InitExpr(initOpcode, initExprBits), dataByteLength); + WASM_PARSER_FAIL_IF(!segment, "can't allocate enough memory for ", segmentNumber, "th Data segment of size ", dataByteLength); + m_result.module->data.uncheckedAppend(Segment::adoptPtr(segment)); + for (uint32_t dataByte = 0; dataByte < dataByteLength; ++dataByte) { + uint8_t byte; + WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", dataByte, "th data byte from ", segmentNumber, "th Data segment"); + segment->byte(dataByte) = byte; + } + } + return { }; +} + +auto ModuleParser::parseCustom(uint32_t sectionLength) -> PartialResult +{ + const uint32_t customSectionStartOffset = m_offset; + + CustomSection section; + uint32_t customSectionNumber = m_result.module->customSections.size() + 1; + uint32_t nameLen; + WASM_PARSER_FAIL_IF(!m_result.module->customSections.tryReserveCapacity(customSectionNumber), "can't allocate enough memory for ", customSectionNumber, "th custom section"); + WASM_PARSER_FAIL_IF(!parseVarUInt32(nameLen), "can't get ", customSectionNumber, "th custom section's name length"); + WASM_PARSER_FAIL_IF(!consumeUTF8String(section.name, nameLen), "nameLen get ", customSectionNumber, "th custom section's name of length ", nameLen); + + uint32_t payloadBytes = sectionLength - (m_offset - customSectionStartOffset); + WASM_PARSER_FAIL_IF(!section.payload.tryReserveCapacity(payloadBytes), "can't allocate enough memory for ", customSectionNumber, "th custom section's ", payloadBytes, " bytes"); + for (uint32_t byteNumber = 0; byteNumber < payloadBytes; ++byteNumber) { + uint8_t byte; + WASM_PARSER_FAIL_IF(!parseUInt8(byte), "can't get ", byteNumber, "th data byte from ", customSectionNumber, "th custom section"); + section.payload.uncheckedAppend(byte); + } + + m_result.module->customSections.uncheckedAppend(WTFMove(section)); + + return { }; +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmModuleParser.h b/Source/JavaScriptCore/wasm/WasmModuleParser.h new file mode 100644 index 000000000..41b57fc82 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmModuleParser.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "WasmFormat.h" +#include "WasmOps.h" +#include "WasmParser.h" +#include <wtf/Vector.h> + +namespace JSC { namespace Wasm { + +struct ModuleParserResult { + std::unique_ptr<ModuleInformation> module; + Vector<FunctionLocationInBinary> functionLocationInBinary; + Vector<SignatureIndex> moduleSignatureIndicesToUniquedSignatureIndices; +}; + +class ModuleParser : public Parser<ModuleParserResult> { +public: + + ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength) + : Parser(vm, sourceBuffer, sourceLength) + { + } + ModuleParser(VM* vm, const Vector<uint8_t>& sourceBuffer) + : ModuleParser(vm, sourceBuffer.data(), sourceBuffer.size()) + { + } + + Result WARN_UNUSED_RETURN parse(); + +private: + +#define WASM_SECTION_DECLARE_PARSER(NAME, ID, DESCRIPTION) PartialResult WARN_UNUSED_RETURN parse ## NAME(); + FOR_EACH_WASM_SECTION(WASM_SECTION_DECLARE_PARSER) +#undef WASM_SECTION_DECLARE_PARSER + + PartialResult WARN_UNUSED_RETURN parseCustom(uint32_t); + PartialResult WARN_UNUSED_RETURN parseGlobalType(Global&); + PartialResult WARN_UNUSED_RETURN parseMemoryHelper(bool isImport); + PartialResult WARN_UNUSED_RETURN parseTableHelper(bool isImport); + PartialResult WARN_UNUSED_RETURN parseResizableLimits(uint32_t& initial, std::optional<uint32_t>& maximum); + PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&, Type& initExprType); + + ModuleParserResult m_result; + bool m_hasTable { false }; +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmPageCount.cpp b/Source/JavaScriptCore/wasm/WasmPageCount.cpp new file mode 100644 index 000000000..4b8770551 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmPageCount.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 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 "WasmPageCount.h" + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/PrintStream.h> +#include <wtf/text/WTFString.h> + +namespace JSC { namespace Wasm { + +void PageCount::dump(PrintStream& out) const +{ + out.print(String::number(bytes()), "B"); +} + +} // namespace JSC + +} // namespace Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmPageCount.h b/Source/JavaScriptCore/wasm/WasmPageCount.h new file mode 100644 index 000000000..e43847c01 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmPageCount.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include <limits.h> + +namespace WTF { +class PrintStream; +} + +namespace JSC { namespace Wasm { + +class PageCount { + +public: + PageCount() + : m_pageCount(UINT_MAX) + { + static_assert(maxPageCount < UINT_MAX, "We rely on this here."); + } + + PageCount(uint32_t pageCount) + : m_pageCount(pageCount) + { } + + void dump(WTF::PrintStream&) const; + + uint64_t bytes() const { return static_cast<uint64_t>(m_pageCount) * static_cast<uint64_t>(pageSize); } + uint32_t pageCount() const { return m_pageCount; } + + static bool isValid(uint32_t pageCount) + { + return pageCount <= maxPageCount; + } + + static PageCount fromBytes(uint64_t bytes) + { + RELEASE_ASSERT(bytes % pageSize == 0); + uint32_t numPages = bytes / pageSize; + RELEASE_ASSERT(PageCount::isValid(numPages)); + return PageCount(numPages); + } + + static PageCount max() + { + return PageCount(maxPageCount); + } + + explicit operator bool() const + { + return m_pageCount != UINT_MAX; + } + + bool operator<(const PageCount& other) const { return m_pageCount < other.m_pageCount; } + bool operator>(const PageCount& other) const { return m_pageCount > other.m_pageCount; } + bool operator>=(const PageCount& other) const { return m_pageCount >= other.m_pageCount; } + PageCount operator+(const PageCount& other) const + { + if (sumOverflows<uint32_t>(m_pageCount, other.m_pageCount)) + return PageCount(); + uint32_t newCount = m_pageCount + other.m_pageCount; + if (!PageCount::isValid(newCount)) + return PageCount(); + return PageCount(newCount); + } + + static constexpr uint32_t pageSize = 64 * KB; +private: + static constexpr uint32_t maxPageCount = static_cast<uint32_t>((1ull << 32) / pageSize); + + uint32_t m_pageCount; +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WASM) diff --git a/Source/JavaScriptCore/wasm/WasmParser.h b/Source/JavaScriptCore/wasm/WasmParser.h new file mode 100644 index 000000000..6a58d8835 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmParser.h @@ -0,0 +1,276 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "B3Procedure.h" +#include "WasmFormat.h" +#include "WasmOps.h" +#include "WasmSections.h" +#include <type_traits> +#include <wtf/Expected.h> +#include <wtf/LEBDecoder.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/WTFString.h> + +namespace JSC { namespace Wasm { + +namespace FailureHelper { +// FIXME We should move this to makeString. It's in its own namespace to enable C++ Argument Dependent Lookup à la std::swap: user code can deblare its own "boxFailure" and the fail() helper will find it. +static inline auto makeString(const char *failure) { return ASCIILiteral(failure); } +template <typename Int, typename = typename std::enable_if<std::is_integral<Int>::value>::type> +static inline auto makeString(Int failure) { return String::number(failure); } +} + +template<typename SuccessType> +class Parser { +public: + typedef String ErrorType; + typedef UnexpectedType<ErrorType> UnexpectedResult; + typedef Expected<void, ErrorType> PartialResult; + typedef Expected<SuccessType, ErrorType> Result; + +protected: + Parser(VM*, const uint8_t*, size_t); + + bool WARN_UNUSED_RETURN consumeCharacter(char); + bool WARN_UNUSED_RETURN consumeString(const char*); + bool WARN_UNUSED_RETURN consumeUTF8String(String&, size_t); + + bool WARN_UNUSED_RETURN parseVarUInt1(uint8_t&); + bool WARN_UNUSED_RETURN parseInt7(int8_t&); + bool WARN_UNUSED_RETURN parseUInt7(uint8_t&); + bool WARN_UNUSED_RETURN parseUInt8(uint8_t&); + bool WARN_UNUSED_RETURN parseUInt32(uint32_t&); + bool WARN_UNUSED_RETURN parseUInt64(uint64_t&); + bool WARN_UNUSED_RETURN parseVarUInt32(uint32_t&); + bool WARN_UNUSED_RETURN parseVarUInt64(uint64_t&); + + bool WARN_UNUSED_RETURN parseVarInt32(int32_t&); + bool WARN_UNUSED_RETURN parseVarInt64(int64_t&); + + bool WARN_UNUSED_RETURN parseResultType(Type&); + bool WARN_UNUSED_RETURN parseValueType(Type&); + bool WARN_UNUSED_RETURN parseExternalKind(ExternalKind&); + + const uint8_t* source() const { return m_source; } + size_t length() const { return m_sourceLength; } + + VM* m_vm; + size_t m_offset = 0; + + template <typename ...Args> + NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const + { + using namespace FailureHelper; // See ADL comment in namespace above. + return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't parse at byte "), String::number(m_offset), ASCIILiteral(" / "), String::number(m_sourceLength), ASCIILiteral(": "), makeString(args)...)); + } +#define WASM_PARSER_FAIL_IF(condition, ...) do { \ + if (UNLIKELY(condition)) \ + return fail(__VA_ARGS__); \ + } while (0) + +#define WASM_FAIL_IF_HELPER_FAILS(helper) do { \ + auto helperResult = helper; \ + if (UNLIKELY(!helperResult)) \ + return helperResult.getUnexpected(); \ + } while (0) + +private: + const uint8_t* m_source; + size_t m_sourceLength; +}; + +template<typename SuccessType> +ALWAYS_INLINE Parser<SuccessType>::Parser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength) + : m_vm(vm) + , m_source(sourceBuffer) + , m_sourceLength(sourceLength) +{ +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::consumeCharacter(char c) +{ + if (m_offset >= length()) + return false; + if (c == source()[m_offset]) { + m_offset++; + return true; + } + return false; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::consumeString(const char* str) +{ + unsigned start = m_offset; + if (m_offset >= length()) + return false; + for (size_t i = 0; str[i]; i++) { + if (!consumeCharacter(str[i])) { + m_offset = start; + return false; + } + } + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::consumeUTF8String(String& result, size_t stringLength) +{ + if (stringLength == 0) { + result = emptyString(); + return true; + } + if (length() < stringLength || m_offset > length() - stringLength) + return false; + result = String::fromUTF8(static_cast<const LChar*>(&source()[m_offset]), stringLength); + m_offset += stringLength; + if (result.isEmpty()) + return false; + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt32(uint32_t& result) +{ + return WTF::LEBDecoder::decodeUInt32(m_source, m_sourceLength, m_offset, result); +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt64(uint64_t& result) +{ + return WTF::LEBDecoder::decodeUInt64(m_source, m_sourceLength, m_offset, result); +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt32(int32_t& result) +{ + return WTF::LEBDecoder::decodeInt32(m_source, m_sourceLength, m_offset, result); +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseVarInt64(int64_t& result) +{ + return WTF::LEBDecoder::decodeInt64(m_source, m_sourceLength, m_offset, result); +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseUInt32(uint32_t& result) +{ + if (length() < 4 || m_offset > length() - 4) + return false; + result = *reinterpret_cast<const uint32_t*>(source() + m_offset); + m_offset += 4; + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseUInt64(uint64_t& result) +{ + if (length() < 8 || m_offset > length() - 8) + return false; + result = *reinterpret_cast<const uint64_t*>(source() + m_offset); + m_offset += 8; + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseUInt8(uint8_t& result) +{ + if (m_offset >= length()) + return false; + result = source()[m_offset++]; + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseInt7(int8_t& result) +{ + if (m_offset >= length()) + return false; + uint8_t v = source()[m_offset++]; + result = (v & 0x40) ? WTF::bitwise_cast<int8_t>(uint8_t(v | 0x80)) : v; + return (v & 0x80) == 0; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseUInt7(uint8_t& result) +{ + if (m_offset >= length()) + return false; + result = source()[m_offset++]; + return result < 0x80; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseVarUInt1(uint8_t& result) +{ + uint32_t temp; + if (!parseVarUInt32(temp)) + return false; + if (temp > 1) + return false; + result = static_cast<uint8_t>(temp); + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseResultType(Type& result) +{ + int8_t value; + if (!parseInt7(value)) + return false; + if (!isValidType(value)) + return false; + result = static_cast<Type>(value); + return true; +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseValueType(Type& result) +{ + return parseResultType(result) && isValueType(result); +} + +template<typename SuccessType> +ALWAYS_INLINE bool Parser<SuccessType>::parseExternalKind(ExternalKind& result) +{ + uint8_t value; + if (!parseUInt7(value)) + return false; + if (!isValidExternalKind(value)) + return false; + result = static_cast<ExternalKind>(value); + return true; +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmPlan.cpp b/Source/JavaScriptCore/wasm/WasmPlan.cpp new file mode 100644 index 000000000..5980b51e8 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmPlan.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 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 "WasmPlan.h" + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "JSCInlines.h" +#include "JSGlobalObject.h" +#include "JSWebAssemblyCallee.h" +#include "WasmB3IRGenerator.h" +#include "WasmBinding.h" +#include "WasmCallingConvention.h" +#include "WasmMemory.h" +#include "WasmModuleParser.h" +#include "WasmValidate.h" +#include <wtf/DataLog.h> +#include <wtf/Locker.h> +#include <wtf/MonotonicTime.h> +#include <wtf/NumberOfCores.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { namespace Wasm { + +static const bool verbose = false; + +Plan::Plan(VM* vm, Vector<uint8_t> source) + : Plan(vm, source.data(), source.size()) +{ +} + +Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength) + : m_vm(vm) + , m_source(source) + , m_sourceLength(sourceLength) +{ +} + +bool Plan::parseAndValidateModule() +{ + MonotonicTime startTime; + if (verbose || Options::reportCompileTimes()) + startTime = MonotonicTime::now(); + + { + ModuleParser moduleParser(m_vm, m_source, m_sourceLength); + auto parseResult = moduleParser.parse(); + if (!parseResult) { + m_errorMessage = parseResult.error(); + return false; + } + m_moduleInformation = WTFMove(parseResult->module); + m_functionLocationInBinary = WTFMove(parseResult->functionLocationInBinary); + m_moduleSignatureIndicesToUniquedSignatureIndices = WTFMove(parseResult->moduleSignatureIndicesToUniquedSignatureIndices); + } + + for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) { + if (verbose) + dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end); + const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start; + size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start; + ASSERT(functionLength <= m_sourceLength); + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); + + auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices); + if (!validationResult) { + if (verbose) { + for (unsigned i = 0; i < functionLength; ++i) + dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", "); + dataLogLn(); + } + m_errorMessage = makeString(validationResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected. + return false; + } + } + + if (verbose || Options::reportCompileTimes()) + dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to validate module"); + return true; +} + +// We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy. +// The reason this is OK is that we guarantee that the main thread doesn't continue until all threads +// that could touch its stack are done executing. +SUPPRESS_ASAN +void Plan::run() +{ + if (!parseAndValidateModule()) + return; + + auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) { + if (UNLIKELY(!vector.tryReserveCapacity(size))) { + StringBuilder builder; + builder.appendLiteral("Failed allocating enough space for "); + builder.appendNumber(size); + builder.append(what); + m_errorMessage = builder.toString(); + return false; + } + return true; + }; + + if (!tryReserveCapacity(m_wasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs") + || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls") + || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions") + || !tryReserveCapacity(m_compilationContexts, m_functionLocationInBinary.size(), " compilation contexts")) + return; + + m_unlinkedWasmToWasmCalls.resize(m_functionLocationInBinary.size()); + m_wasmInternalFunctions.resize(m_functionLocationInBinary.size()); + m_compilationContexts.resize(m_functionLocationInBinary.size()); + + for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) { + Import* import = &m_moduleInformation->imports[importIndex]; + if (import->kind != ExternalKind::Function) + continue; + unsigned importFunctionIndex = m_wasmExitStubs.size(); + if (verbose) + dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field); + SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex); + m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex)); + } + + m_currentIndex = 0; + + auto doWork = [this] { + while (true) { + uint32_t functionIndex; + { + auto locker = holdLock(m_lock); + if (m_currentIndex >= m_functionLocationInBinary.size()) + return; + functionIndex = m_currentIndex; + ++m_currentIndex; + } + + const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start; + size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start; + ASSERT(functionLength <= m_sourceLength); + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); + unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex; + ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex); + ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices)); + + m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>(); + auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices); + + if (UNLIKELY(!parseAndCompileResult)) { + auto locker = holdLock(m_lock); + if (!m_errorMessage) { + // Multiple compiles could fail simultaneously. We arbitrarily choose the first. + m_errorMessage = makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected. + } + m_currentIndex = m_functionLocationInBinary.size(); + + // We will terminate on the next execution. + continue; + } + + m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult); + } + }; + + MonotonicTime startTime; + if (verbose || Options::reportCompileTimes()) + startTime = MonotonicTime::now(); + + uint32_t threadCount = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1; + uint32_t numWorkerThreads = threadCount - 1; + Vector<ThreadIdentifier> threads; + threads.reserveCapacity(numWorkerThreads); + for (uint32_t i = 0; i < numWorkerThreads; i++) + threads.uncheckedAppend(createThread("jsc.wasm-b3-compilation.thread", doWork)); + + doWork(); // Let the main thread do some work too. + + for (uint32_t i = 0; i < numWorkerThreads; i++) + waitForThreadCompletion(threads[i]); + + for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) { + { + CompilationContext& context = m_compilationContexts[functionIndex]; + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString(); + { + LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr); + m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation = + std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts)); + } + + { + LinkBuffer linkBuffer(*m_vm, *context.jsEntrypointJIT, nullptr); + linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress())); + + m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation = + std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts)); + } + } + } + + if (verbose || Options::reportCompileTimes()) { + dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), + " us to compile and link the module"); + } + + // Patch the call sites for each WebAssembly function. + for (auto& unlinked : m_unlinkedWasmToWasmCalls) { + for (auto& call : unlinked) { + void* executableAddress; + if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) { + // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462 + executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs + ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress() + : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress(); + } else { + ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs); + executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress(); + } + MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress)); + } + } + + m_failed = false; +} + +void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)> callback) +{ + ASSERT(!failed()); + for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) { + WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get(); + + JSWebAssemblyCallee* jsEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->jsToWasmEntrypoint)); + MacroAssembler::repatchPointer(function->jsToWasmCalleeMoveLocation, jsEntrypointCallee); + + JSWebAssemblyCallee* wasmEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->wasmEntrypoint)); + MacroAssembler::repatchPointer(function->wasmCalleeMoveLocation, wasmEntrypointCallee); + + callback(internalFunctionIndex, jsEntrypointCallee, wasmEntrypointCallee); + } +} + +Plan::~Plan() { } + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmPlan.h b/Source/JavaScriptCore/wasm/WasmPlan.h new file mode 100644 index 000000000..6980bdbbb --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmPlan.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "CompilationResult.h" +#include "VM.h" +#include "WasmB3IRGenerator.h" +#include "WasmFormat.h" +#include <wtf/Bag.h> +#include <wtf/ThreadSafeRefCounted.h> +#include <wtf/Vector.h> + +namespace JSC { + +class CallLinkInfo; +class JSGlobalObject; +class JSWebAssemblyCallee; + +namespace Wasm { + +class Plan { +public: + JS_EXPORT_PRIVATE Plan(VM*, Vector<uint8_t>); + JS_EXPORT_PRIVATE Plan(VM*, const uint8_t*, size_t); + JS_EXPORT_PRIVATE ~Plan(); + + bool parseAndValidateModule(); + + JS_EXPORT_PRIVATE void run(); + + JS_EXPORT_PRIVATE void initializeCallees(JSGlobalObject*, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)>); + + bool WARN_UNUSED_RETURN failed() const { return m_failed; } + const String& errorMessage() const + { + RELEASE_ASSERT(failed()); + return m_errorMessage; + } + + Vector<Export>& exports() const + { + RELEASE_ASSERT(!failed()); + return m_moduleInformation->exports; + } + + size_t internalFunctionCount() const + { + RELEASE_ASSERT(!failed()); + return m_wasmInternalFunctions.size(); + } + + std::unique_ptr<ModuleInformation>&& takeModuleInformation() + { + RELEASE_ASSERT(!failed()); + return WTFMove(m_moduleInformation); + } + + Bag<CallLinkInfo>&& takeCallLinkInfos() + { + RELEASE_ASSERT(!failed()); + return WTFMove(m_callLinkInfos); + } + + Vector<WasmExitStubs>&& takeWasmExitStubs() + { + RELEASE_ASSERT(!failed()); + return WTFMove(m_wasmExitStubs); + } + +private: + std::unique_ptr<ModuleInformation> m_moduleInformation; + Vector<FunctionLocationInBinary> m_functionLocationInBinary; + Vector<SignatureIndex> m_moduleSignatureIndicesToUniquedSignatureIndices; + Bag<CallLinkInfo> m_callLinkInfos; + Vector<WasmExitStubs> m_wasmExitStubs; + Vector<std::unique_ptr<WasmInternalFunction>> m_wasmInternalFunctions; + Vector<CompilationContext> m_compilationContexts; + + VM* m_vm; + Vector<Vector<UnlinkedWasmToWasmCall>> m_unlinkedWasmToWasmCalls; + const uint8_t* m_source; + const size_t m_sourceLength; + bool m_failed { true }; + String m_errorMessage; + uint32_t m_currentIndex; + Lock m_lock; +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmSections.h b/Source/JavaScriptCore/wasm/WasmSections.h new file mode 100644 index 000000000..5ee7bccb5 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmSections.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +namespace JSC { namespace Wasm { + +#define FOR_EACH_WASM_SECTION(macro) \ + macro(Type, 1, "Function signature declarations") \ + macro(Import, 2, "Import declarations") \ + macro(Function, 3, "Function declarations") \ + macro(Table, 4, "Indirect function table and other tables") \ + macro(Memory, 5, "Memory attributes") \ + macro(Global, 6, "Global declarations") \ + macro(Export, 7, "Exports") \ + macro(Start, 8, "Start function declaration") \ + macro(Element, 9, "Elements section") \ + macro(Code, 10, "Function bodies (code)") \ + macro(Data, 11, "Data segments") + +enum class Section : uint8_t { +#define DEFINE_WASM_SECTION_ENUM(NAME, ID, DESCRIPTION) NAME = ID, + FOR_EACH_WASM_SECTION(DEFINE_WASM_SECTION_ENUM) +#undef DEFINE_WASM_SECTION_ENUM + Unknown +}; + +template<typename Int> +static inline bool isValidSection(Int section) +{ + switch (section) { +#define VALIDATE_SECTION(NAME, ID, DESCRIPTION) case static_cast<Int>(Section::NAME): return true; + FOR_EACH_WASM_SECTION(VALIDATE_SECTION) +#undef VALIDATE_SECTION + default: + return false; + } +} + +static inline bool validateOrder(Section previous, Section next) +{ + if (previous == Section::Unknown) + return true; + return static_cast<uint8_t>(previous) < static_cast<uint8_t>(next); +} + +static inline const char* makeString(Section section) +{ + switch (section) { +#define STRINGIFY_SECTION_NAME(NAME, ID, DESCRIPTION) case Section::NAME: return #NAME; + FOR_EACH_WASM_SECTION(STRINGIFY_SECTION_NAME) +#undef STRINGIFY_SECTION_NAME + default: + RELEASE_ASSERT_NOT_REACHED(); + return "?"; + } +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmSignature.cpp b/Source/JavaScriptCore/wasm/WasmSignature.cpp new file mode 100644 index 000000000..0966dab51 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmSignature.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2016 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 "WasmSignature.h" + +#if ENABLE(WEBASSEMBLY) + +#include "VM.h" +#include <wtf/FastMalloc.h> +#include <wtf/HashFunctions.h> +#include <wtf/PrintStream.h> + +namespace JSC { namespace Wasm { + +namespace { +const bool verbose = false; +} + +const constexpr SignatureIndex Signature::invalidIndex; + +String Signature::toString() const +{ + String result(makeString(returnType())); + result.append(" ("); + for (SignatureArgCount arg = 0; arg < argumentCount(); ++arg) { + if (arg) + result.append(", "); + result.append(makeString(argument(arg))); + } + result.append(')'); + return result; +} + +void Signature::dump(PrintStream& out) const +{ + out.print(toString()); +} + +unsigned Signature::hash() const +{ + uint32_t sizeToHash = allocatedSize(argumentCount()) / sizeof(allocationSizeRoundsUpTo); + // Assumes over-allocated memory was zero-initialized, and rounded-up to allocationSizeRoundsUpTo so that a wider hash can be performed. + ASSERT(sizeToHash * sizeof(allocationSizeRoundsUpTo) == allocatedSize(argumentCount())); + unsigned accumulator = 0xa1bcedd8u; + const auto* pos = reinterpret_cast<const allocationSizeRoundsUpTo*>(this); + for (uint32_t i = 0; i < sizeToHash; ++i) + accumulator = WTF::pairIntHash(accumulator, WTF::IntHash<allocationSizeRoundsUpTo>::hash(*pos)); + return accumulator; +} + +Signature* Signature::create(SignatureArgCount argumentCount) +{ + // Hashing relies on allocation zero-initializing trailing elements. + auto allocated = tryFastCalloc(allocatedSize(argumentCount), 1); + Signature* signature; + if (!allocated.getValue(signature)) + return nullptr; + new (signature) Signature(argumentCount); + return signature; +} + +Signature* Signature::createInvalid() +{ + Signature* signature = create(0); + RELEASE_ASSERT(signature); + new (signature) Signature(std::numeric_limits<SignatureArgCount>::max()); + return signature; +} + +void Signature::destroy(Signature* signature) +{ + fastFree(signature); +} + +SignatureInformation::~SignatureInformation() +{ + for (size_t i = 0; i < m_signatures.size(); ++i) + Signature::destroy(m_signatures[i]); +} + +SignatureInformation::SignatureInformation() +{ + // The zeroth entry is an invalid signature, to match invalidIndex. + ASSERT(!Signature::invalidIndex); + Signature* invalidSignature = Signature::createInvalid(); + auto addResult = m_signatureMap.add(SignatureHash { invalidSignature }, Signature::invalidIndex); + RELEASE_ASSERT(addResult.isNewEntry); + ASSERT(Signature::invalidIndex == addResult.iterator->value); + m_signatures.append(invalidSignature); +} + +SignatureInformation* SignatureInformation::get(VM* vm) +{ + std::call_once(vm->m_wasmSignatureInformationOnceFlag, [vm] { + vm->m_wasmSignatureInformation = std::unique_ptr<SignatureInformation>(new SignatureInformation()); + }); + return vm->m_wasmSignatureInformation.get(); +} + +SignatureIndex SignatureInformation::adopt(VM* vm, Signature* signature) +{ + SignatureInformation* info = get(vm); + LockHolder lock(info->m_lock); + + SignatureIndex nextValue = info->m_signatures.size(); + auto addResult = info->m_signatureMap.add(SignatureHash { signature }, nextValue); + if (addResult.isNewEntry) { + ASSERT(nextValue == addResult.iterator->value); + if (verbose) + dataLogLn("Adopt new signature ", *signature, " with index ", addResult.iterator->value, " hash: ", signature->hash()); + info->m_signatures.append(signature); + return nextValue; + } + if (verbose) + dataLogLn("Existing signature ", *signature, " with index ", addResult.iterator->value, " hash: ", signature->hash()); + Signature::destroy(signature); + ASSERT(addResult.iterator->value != Signature::invalidIndex); + return addResult.iterator->value; +} + +const Signature* SignatureInformation::get(VM* vm, SignatureIndex index) +{ + ASSERT(index != Signature::invalidIndex); + SignatureInformation* info = get(vm); + LockHolder lock(info->m_lock); + + if (verbose) + dataLogLn("Got signature ", *info->m_signatures.at(index), " at index ", index); + return info->m_signatures.at(index); +} + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmSignature.h b/Source/JavaScriptCore/wasm/WasmSignature.h new file mode 100644 index 000000000..4d43ffcfc --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmSignature.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "B3Type.h" +#include "WasmOps.h" +#include <cstdint> +#include <cstring> +#include <wtf/HashMap.h> +#include <wtf/HashTraits.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +namespace WTF { +class PrintStream; +} + +namespace JSC { + +class VM; + +namespace Wasm { + +typedef uint32_t SignatureArgCount; +typedef uint32_t SignatureIndex; + +class Signature { + static const constexpr SignatureArgCount s_retCount = 1; + typedef uint64_t allocationSizeRoundsUpTo; + + Signature() = delete; + Signature(const Signature&) = delete; + Signature(SignatureArgCount argCount) + : m_argCount(argCount) + { + } + + Type* storage(SignatureArgCount i) + { + return i + reinterpret_cast<Type*>(reinterpret_cast<char*>(this) + sizeof(Signature)); + } + Type* storage(SignatureArgCount i) const { return const_cast<Signature*>(this)->storage(i); } + static uint32_t allocatedSize(SignatureArgCount argCount) + { + return WTF::roundUpToMultipleOf<sizeof(allocationSizeRoundsUpTo)>(sizeof(Signature) + (s_retCount + argCount) * sizeof(Type)); + } + +public: + Type& returnType() { return *storage(0); } + Type returnType() const { return *storage(0); } + SignatureArgCount returnCount() const { return s_retCount; } + SignatureArgCount argumentCount() const { return m_argCount; } + Type& argument(SignatureArgCount i) + { + ASSERT(i < argumentCount()); + return *storage(returnCount() + i); + } + Type argument(SignatureArgCount i) const { return const_cast<Signature*>(this)->argument(i); } + + WTF::String toString() const; + void dump(WTF::PrintStream& out) const; + bool operator==(const Signature& rhs) const + { + return allocatedSize(argumentCount()) == allocatedSize(rhs.argumentCount()) && !memcmp(this, &rhs, allocatedSize(argumentCount())); + } + unsigned hash() const; + + static Signature* create(SignatureArgCount); + static void destroy(Signature*); + + // Signatures are uniqued and, for call_indirect, validated at runtime. Tables can create invalid SignatureIndex values which cause call_indirect to fail. We use 0 as the invalidIndex so that the codegen can easily test for it and trap, and we add a token invalid entry in SignatureInformation. + static const constexpr SignatureIndex invalidIndex = 0; + +private: + friend class SignatureInformation; + static Signature* createInvalid(); + SignatureArgCount m_argCount; + // Return Type and arguments are stored here. +}; + +struct SignatureHash { + const Signature* key; + static const Signature* empty() { return nullptr; } + static const Signature* deleted() { return reinterpret_cast<const Signature*>(1); } + SignatureHash() + : key(empty()) + { + } + explicit SignatureHash(const Signature* key) + : key(key) + { + ASSERT(key != empty()); + ASSERT(key != deleted()); + } + explicit SignatureHash(WTF::HashTableDeletedValueType) + : key(deleted()) + { + } + bool operator==(const SignatureHash& rhs) const { return equal(*this, rhs); } + static bool equal(const SignatureHash& lhs, const SignatureHash& rhs) { return lhs.key == rhs.key || (lhs.key && rhs.key && *lhs.key == *rhs.key); } + static unsigned hash(const SignatureHash& signature) { return signature.key->hash(); } + static const bool safeToCompareToEmptyOrDeleted = false; + bool isHashTableDeletedValue() const { return key == deleted(); } +}; + +} } // namespace JSC::Wasm + + +namespace WTF { + +template<typename T> struct DefaultHash; +template<> struct DefaultHash<JSC::Wasm::SignatureHash> { + typedef JSC::Wasm::SignatureHash Hash; +}; + +template<typename T> struct HashTraits; +template<> struct HashTraits<JSC::Wasm::SignatureHash> : SimpleClassHashTraits<JSC::Wasm::SignatureHash> { + static const bool emptyValueIsZero = true; +}; + +} // namespace WTF + + +namespace JSC { namespace Wasm { + +// Signature information is held globally on VM to allow all signatures to be unique. This is required when wasm calls another wasm instance. +// Note: signatures are never removed from VM because that would require accounting for all WebAssembly.Module and which signatures they use. The maximum number of signatures is bounded, and isn't worth the counting overhead. We could clear everything when we reach zero outstanding WebAssembly.Module. https://bugs.webkit.org/show_bug.cgi?id=166037 +class SignatureInformation { + HashMap<Wasm::SignatureHash, Wasm::SignatureIndex> m_signatureMap; + Vector<Signature*> m_signatures; + Lock m_lock; + static SignatureInformation* get(VM*); + SignatureInformation(); + SignatureInformation(const SignatureInformation&) = delete; + +public: + ~SignatureInformation(); + static SignatureIndex WARN_UNUSED_RETURN adopt(VM*, Signature*); + static const Signature* WARN_UNUSED_RETURN get(VM*, SignatureIndex); +}; + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmValidate.cpp b/Source/JavaScriptCore/wasm/WasmValidate.cpp new file mode 100644 index 000000000..75d3826ca --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmValidate.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2016 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 "WasmValidate.h" + +#if ENABLE(WEBASSEMBLY) + +#include "WasmFunctionParser.h" +#include <wtf/CommaPrinter.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { namespace Wasm { + +class Validate { +public: + class ControlData { + public: + ControlData(BlockType type, Type signature) + : m_blockType(type) + , m_signature(signature) + { + } + + ControlData() + { + } + + void dump(PrintStream& out) const + { + switch (type()) { + case BlockType::If: + out.print("If: "); + break; + case BlockType::Block: + out.print("Block: "); + break; + case BlockType::Loop: + out.print("Loop: "); + break; + case BlockType::TopLevel: + out.print("TopLevel: "); + break; + } + out.print(makeString(signature())); + } + + bool hasNonVoidSignature() const { return m_signature != Void; } + + BlockType type() const { return m_blockType; } + Type signature() const { return m_signature; } + private: + BlockType m_blockType; + Type m_signature; + }; + typedef String ErrorType; + typedef UnexpectedType<ErrorType> UnexpectedResult; + typedef Expected<void, ErrorType> Result; + typedef Type ExpressionType; + typedef ControlData ControlType; + typedef Vector<ExpressionType, 1> ExpressionList; + typedef FunctionParser<Validate>::ControlEntry ControlEntry; + + static const ExpressionType emptyExpression = Void; + + template <typename ...Args> + NEVER_INLINE UnexpectedResult WARN_UNUSED_RETURN fail(Args... args) const + { + using namespace FailureHelper; // See ADL comment in WasmParser.h. + return UnexpectedResult(makeString(ASCIILiteral("WebAssembly.Module doesn't validate: "), makeString(args)...)); + } +#define WASM_VALIDATOR_FAIL_IF(condition, ...) do { \ + if (UNLIKELY(condition)) \ + return fail(__VA_ARGS__); \ + } while (0) + + Result WARN_UNUSED_RETURN addArguments(const Signature*); + Result WARN_UNUSED_RETURN addLocal(Type, uint32_t); + ExpressionType addConstant(Type type, uint64_t) { return type; } + + // Locals + Result WARN_UNUSED_RETURN getLocal(uint32_t index, ExpressionType& result); + Result WARN_UNUSED_RETURN setLocal(uint32_t index, ExpressionType value); + + // Globals + Result WARN_UNUSED_RETURN getGlobal(uint32_t index, ExpressionType& result); + Result WARN_UNUSED_RETURN setGlobal(uint32_t index, ExpressionType value); + + // Memory + Result WARN_UNUSED_RETURN load(LoadOpType, ExpressionType pointer, ExpressionType& result, uint32_t offset); + Result WARN_UNUSED_RETURN store(StoreOpType, ExpressionType pointer, ExpressionType value, uint32_t offset); + + // Basic operators + template<OpType> + Result WARN_UNUSED_RETURN addOp(ExpressionType arg, ExpressionType& result); + template<OpType> + Result WARN_UNUSED_RETURN addOp(ExpressionType left, ExpressionType right, ExpressionType& result); + Result WARN_UNUSED_RETURN addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result); + + // Control flow + ControlData WARN_UNUSED_RETURN addTopLevel(Type signature); + ControlData WARN_UNUSED_RETURN addBlock(Type signature); + ControlData WARN_UNUSED_RETURN addLoop(Type signature); + Result WARN_UNUSED_RETURN addIf(ExpressionType condition, Type signature, ControlData& result); + Result WARN_UNUSED_RETURN addElse(ControlData&, const ExpressionList&); + Result WARN_UNUSED_RETURN addElseToUnreachable(ControlData&); + + Result WARN_UNUSED_RETURN addReturn(ControlData& topLevel, const ExpressionList& returnValues); + Result WARN_UNUSED_RETURN addBranch(ControlData&, ExpressionType condition, const ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN endBlock(ControlEntry&, ExpressionList& expressionStack); + Result WARN_UNUSED_RETURN addEndToUnreachable(ControlEntry&); + Result WARN_UNUSED_RETURN addGrowMemory(ExpressionType delta, ExpressionType& result); + Result WARN_UNUSED_RETURN addCurrentMemory(ExpressionType& result); + + Result WARN_UNUSED_RETURN addUnreachable() { return { }; } + + // Calls + Result WARN_UNUSED_RETURN addCall(unsigned calleeIndex, const Signature*, const Vector<ExpressionType>& args, ExpressionType& result); + Result WARN_UNUSED_RETURN addCallIndirect(const Signature*, SignatureIndex, const Vector<ExpressionType>& args, ExpressionType& result); + + bool hasMemory() const { return !!m_module.memory; } + + Validate(const ModuleInformation& module) + : m_module(module) + { + } + + void dump(const Vector<ControlEntry>&, const ExpressionList*); + +private: + Result WARN_UNUSED_RETURN unify(const ExpressionList&, const ControlData&); + + Result WARN_UNUSED_RETURN checkBranchTarget(ControlData& target, const ExpressionList& expressionStack); + + Vector<Type> m_locals; + const ModuleInformation& m_module; +}; + +auto Validate::addArguments(const Signature* signature) -> Result +{ + for (size_t i = 0; i < signature->argumentCount(); ++i) + WASM_FAIL_IF_HELPER_FAILS(addLocal(signature->argument(i), 1)); + return { }; +} + +auto Validate::addLocal(Type type, uint32_t count) -> Result +{ + size_t size = m_locals.size() + count; + WASM_VALIDATOR_FAIL_IF(!m_locals.tryReserveCapacity(size), "can't allocate memory for ", size, " locals"); + + for (uint32_t i = 0; i < count; ++i) + m_locals.uncheckedAppend(type); + return { }; +} + +auto Validate::getLocal(uint32_t index, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_locals.size(), "attempt to use unknown local ", index, " last one is ", m_locals.size()); + result = m_locals[index]; + return { }; +} + +auto Validate::setLocal(uint32_t index, ExpressionType value) -> Result +{ + ExpressionType localType; + WASM_FAIL_IF_HELPER_FAILS(getLocal(index, localType)); + WASM_VALIDATOR_FAIL_IF(localType != value, "set_local to type ", value, " expected ", localType); + return { }; +} + +auto Validate::getGlobal(uint32_t index, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "get_global ", index, " of unknown global, limit is ", m_module.globals.size()); + result = m_module.globals[index].type; + ASSERT(isValueType(result)); + return { }; +} + +auto Validate::setGlobal(uint32_t index, ExpressionType value) -> Result +{ + WASM_VALIDATOR_FAIL_IF(index >= m_module.globals.size(), "set_global ", index, " of unknown global, limit is ", m_module.globals.size()); + WASM_VALIDATOR_FAIL_IF(m_module.globals[index].mutability == Global::Immutable, "set_global ", index, " is immutable"); + + ExpressionType globalType = m_module.globals[index].type; + ASSERT(isValueType(globalType)); + WASM_VALIDATOR_FAIL_IF(globalType != value, "set_global ", index, " with type ", globalType, " with a variable of type ", value); + return { }; +} + +Validate::ControlType Validate::addTopLevel(Type signature) +{ + return ControlData(BlockType::TopLevel, signature); +} + +Validate::ControlType Validate::addBlock(Type signature) +{ + return ControlData(BlockType::Block, signature); +} + +Validate::ControlType Validate::addLoop(Type signature) +{ + return ControlData(BlockType::Loop, signature); +} + +auto Validate::addSelect(ExpressionType condition, ExpressionType nonZero, ExpressionType zero, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "select condition must be i32, got ", condition); + WASM_VALIDATOR_FAIL_IF(nonZero != zero, "select result types must match, got ", nonZero, " and ", zero); + result = zero; + return { }; +} + +auto Validate::addIf(ExpressionType condition, Type signature, ControlType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "if condition must be i32, got ", condition); + result = ControlData(BlockType::If, signature); + return { }; +} + +auto Validate::addElse(ControlType& current, const ExpressionList& values) -> Result +{ + WASM_FAIL_IF_HELPER_FAILS(unify(values, current)); + return addElseToUnreachable(current); +} + +auto Validate::addElseToUnreachable(ControlType& current) -> Result +{ + WASM_VALIDATOR_FAIL_IF(current.type() != BlockType::If, "else block isn't associated to an if"); + current = ControlData(BlockType::Block, current.signature()); + return { }; +} + +auto Validate::addReturn(ControlType& topLevel, const ExpressionList& returnValues) -> Result +{ + ASSERT(topLevel.type() == BlockType::TopLevel); + if (topLevel.signature() == Void) + return { }; + ASSERT(returnValues.size() == 1); + WASM_VALIDATOR_FAIL_IF(topLevel.signature() != returnValues[0], "return type ", returnValues[0], " doesn't match function's return type ", topLevel.signature()); + return { }; +} + +auto Validate::checkBranchTarget(ControlType& target, const ExpressionList& expressionStack) -> Result + { + if (target.type() == BlockType::Loop) + return { }; + + if (target.signature() == Void) + return { }; + + WASM_VALIDATOR_FAIL_IF(expressionStack.isEmpty(), "branch to block on empty expression stack"); + WASM_VALIDATOR_FAIL_IF(target.signature() != expressionStack.last(), "branch's stack type doesn't match block's type"); + + return { }; + } + +auto Validate::addBranch(ControlType& target, ExpressionType condition, const ExpressionList& stack) -> Result +{ + // Void means this is an unconditional branch. + WASM_VALIDATOR_FAIL_IF(condition != Void && condition != I32, "conditional branch with non-i32 condition ", condition); + return checkBranchTarget(target, stack); +} + +auto Validate::addSwitch(ExpressionType condition, const Vector<ControlData*>& targets, ControlData& defaultTarget, const ExpressionList& expressionStack) -> Result +{ + WASM_VALIDATOR_FAIL_IF(condition != I32, "br_table with non-i32 condition ", condition); + + for (auto target : targets) + WASM_VALIDATOR_FAIL_IF(defaultTarget.signature() != target->signature(), "br_table target type mismatch"); + + return checkBranchTarget(defaultTarget, expressionStack); +} + +auto Validate::addGrowMemory(ExpressionType delta, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(delta != I32, "grow_memory with non-i32 delta"); + result = I32; + return { }; +} + +auto Validate::addCurrentMemory(ExpressionType& result) -> Result +{ + result = I32; + return { }; +} + +auto Validate::endBlock(ControlEntry& entry, ExpressionList& stack) -> Result +{ + WASM_FAIL_IF_HELPER_FAILS(unify(stack, entry.controlData)); + return addEndToUnreachable(entry); +} + +auto Validate::addEndToUnreachable(ControlEntry& entry) -> Result +{ + auto block = entry.controlData; + if (block.signature() != Void) { + WASM_VALIDATOR_FAIL_IF(block.type() == BlockType::If, "If-block had a non-void result type: ", block.signature(), " but had no else-block"); + entry.enclosedExpressionStack.append(block.signature()); + } + return { }; +} + +auto Validate::addCall(unsigned, const Signature* signature, const Vector<ExpressionType>& args, ExpressionType& result) -> Result +{ + WASM_VALIDATOR_FAIL_IF(signature->argumentCount() != args.size(), "arity mismatch in call, got ", args.size(), " arguments, expected ", signature->argumentCount()); + + for (unsigned i = 0; i < args.size(); ++i) + WASM_VALIDATOR_FAIL_IF(args[i] != signature->argument(i), "argument type mismatch in call, got ", args[i], ", expected ", signature->argument(i)); + + result = signature->returnType(); + return { }; +} + +auto Validate::addCallIndirect(const Signature* signature, SignatureIndex signatureIndex, const Vector<ExpressionType>& args, ExpressionType& result) -> Result +{ + UNUSED_PARAM(signatureIndex); + ASSERT(signatureIndex != Signature::invalidIndex); + const auto argumentCount = signature->argumentCount(); + WASM_VALIDATOR_FAIL_IF(argumentCount != args.size() - 1, "arity mismatch in call_indirect, got ", args.size() - 1, " arguments, expected ", argumentCount); + + for (unsigned i = 0; i < argumentCount; ++i) + WASM_VALIDATOR_FAIL_IF(args[i] != signature->argument(i), "argument type mismatch in call_indirect, got ", args[i], ", expected ", signature->argument(i)); + + WASM_VALIDATOR_FAIL_IF(args.last() != I32, "non-i32 call_indirect index ", args.last()); + + result = signature->returnType(); + return { }; +} + +auto Validate::unify(const ExpressionList& values, const ControlType& block) -> Result +{ + if (block.signature() == Void) { + WASM_VALIDATOR_FAIL_IF(!values.isEmpty(), "void block should end with an empty stack"); + return { }; + } + + WASM_VALIDATOR_FAIL_IF(values.size() != 1, "block with type: ", block.signature(), " ends with a stack containing more than one value"); + WASM_VALIDATOR_FAIL_IF(values[0] != block.signature(), "control flow returns with unexpected type"); + return { }; +} + +static void dumpExpressionStack(const CommaPrinter& comma, const Validate::ExpressionList& expressionStack) +{ + dataLog(comma, " ExpressionStack:"); + for (const auto& expression : expressionStack) + dataLog(comma, makeString(expression)); +} + +void Validate::dump(const Vector<ControlEntry>& controlStack, const ExpressionList* expressionStack) +{ + for (size_t i = controlStack.size(); i--;) { + dataLog(" ", controlStack[i].controlData); + CommaPrinter comma(", ", ""); + dumpExpressionStack(comma, *expressionStack); + expressionStack = &controlStack[i].enclosedExpressionStack; + dataLogLn(); + } + dataLogLn(); +} + +Expected<void, String> validateFunction(VM* vm, const uint8_t* source, size_t length, const Signature* signature, const ModuleInformation& module, const Vector<SignatureIndex>& moduleSignatureIndicesToUniquedSignatureIndices) +{ + Validate context(module); + FunctionParser<Validate> validator(vm, context, source, length, signature, module, moduleSignatureIndicesToUniquedSignatureIndices); + WASM_FAIL_IF_HELPER_FAILS(validator.parse()); + return { }; +} + +} } // namespace JSC::Wasm + +#include "WasmValidateInlines.h" + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/WasmValidate.h b/Source/JavaScriptCore/wasm/WasmValidate.h new file mode 100644 index 000000000..73e7d871d --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmValidate.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "WasmFormat.h" +#include <wtf/Expected.h> + +namespace JSC { + +class VM; + +namespace Wasm { + +Expected<void, String> validateFunction(VM*, const uint8_t*, size_t, const Signature*, const ModuleInformation&, const Vector<SignatureIndex>&); + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/generateWasm.py b/Source/JavaScriptCore/wasm/generateWasm.py new file mode 100755 index 000000000..6e1ddc24b --- /dev/null +++ b/Source/JavaScriptCore/wasm/generateWasm.py @@ -0,0 +1,96 @@ +#! /usr/bin/python + +# Copyright (C) 2016 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 AND ITS CONTRIBUTORS "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 OR ITS 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. + +# This tool has a couple of helpful macros to process Wasm files from the wasm.json. + +import json +import re + +class Wasm: + def __init__(self, scriptName, jsonPath): + wasmFile = open(jsonPath, "r") + wasm = json.load(open(jsonPath, "r")) + wasmFile.close() + for pre in wasm["preamble"]: + if pre["name"] == "version": + self.expectedVersionNumber = str(pre["value"]) + self.preamble = wasm["preamble"] + self.types = wasm["type"] + self.opcodes = wasm["opcode"] + self.header = """/* + * Copyright (C) 2016 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 AND ITS CONTRIBUTORS "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 OR ITS 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. + */ + +// DO NO EDIT! - This file was generated by """ + scriptName + + def opcodeIterator(self, filter, ret=None): + # We need to do this because python is dumb and won't let me use self in the lambda, which is ridiculous. + if ret == None: + ret = lambda op: {"name": op, "opcode": self.opcodes[op]} + for op in self.opcodes.iterkeys(): + if filter(self.opcodes[op]): + yield ret(op) + + def toCpp(self, name): + camelCase = re.sub(r'([^a-z0-9].)', lambda c: c.group(0)[1].upper(), name) + CamelCase = camelCase[:1].upper() + camelCase[1:] + return CamelCase + + +def isNormal(op): + return op["category"] == "arithmetic" or op["category"] == "comparison" or op["category"] == "conversion" + + +def isUnary(op): + return isNormal(op) and len(op["parameter"]) == 1 + + +def isBinary(op): + return isNormal(op) and len(op["parameter"]) == 2 + + +def isSimple(op): + return "b3op" in op diff --git a/Source/JavaScriptCore/wasm/generateWasmB3IRGeneratorInlinesHeader.py b/Source/JavaScriptCore/wasm/generateWasmB3IRGeneratorInlinesHeader.py new file mode 100755 index 000000000..596d8597a --- /dev/null +++ b/Source/JavaScriptCore/wasm/generateWasmB3IRGeneratorInlinesHeader.py @@ -0,0 +1,218 @@ +#!/usr/bin/env python + +# Copyright (C) 2016 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:n +# +# 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 AND ITS CONTRIBUTORS "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 OR ITS 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. + +# This tool has a couple of helpful macros to process Wasm files from the wasm.json. + +from generateWasm import * +import optparse +import sys +import re + +parser = optparse.OptionParser(usage="usage: %prog <wasm.json> <WasmOps.h>") +(options, args) = parser.parse_args(sys.argv[0:]) +if len(args) != 3: + parser.error(parser.usage) + +wasm = Wasm(args[0], args[1]) +opcodes = wasm.opcodes +wasmB3IRGeneratorHFile = open(args[2], "w") + +opcodeRegex = re.compile('([a-zA-Z0-9]+)') +argumentRegex = re.compile('(\@[0-9]+)') +decimalRegex = re.compile('([-]?[0-9]+)') +whitespaceRegex = re.compile('\s+') +commaRegex = re.compile('(,)') +oparenRegex = re.compile('(\()') +cparenRegex = re.compile('(\))') + + +class Source: + def __init__(self, contents, offset=0): + self.contents = contents + self.offset = offset + + +def read(regex, source): + match = regex.match(source.contents, source.offset) + if not match: + return None + source.offset = match.end() + return match.group() + + +def lex(source): + result = [] + while source.offset != len(source.contents): + read(whitespaceRegex, source) + opcode = read(opcodeRegex, source) + if opcode: + result.append(opcode) + continue + + argument = read(argumentRegex, source) + if argument: + result.append(argument) + continue + + number = read(decimalRegex, source) + if number: + result.append(int(number)) + continue + + oparen = read(oparenRegex, source) + if oparen: + result.append(oparen) + continue + + cparen = read(cparenRegex, source) + if cparen: + result.append(cparen) + continue + + comma = read(commaRegex, source) + if comma: + # Skip commas + continue + + raise Exception("Lexing Error: could not lex token from: " + source.contents + " at offset: " + str(source.offset) + " (" + source.contents[source.offset:] + "). With tokens: [" + ", ".join(result) + "]") + return result + + +class CodeGenerator: + def __init__(self, tokens): + self.tokens = tokens + self.index = 0 + self.code = [] + + def advance(self): + self.index += 1 + + def token(self): + return self.tokens[self.index] + + def parseError(self, string): + raise Exception("Parse error " + string) + + def consume(self, string): + if self.token() != string: + self.parseError("Expected " + string + " but got " + self.token()) + self.advance() + + def generateParameters(self): + self.advance() + params = [] + tokens = self.tokens + while self.index < len(tokens): + if self.token() == ")": + self.advance() + return params + params.append(self.generateOpcode()) + self.parseError("Parsing arguments fell off end") + + def generateOpcode(self): + result = None + if self.token() == "i32" or self.token() == "i64": + type = "Int32" + if self.token() == "i64": + type = "Int64" + self.advance() + self.consume("(") + self.code.append(generateConstCode(self.index, self.token(), type)) + result = temp(self.index) + self.advance() + self.consume(")") + elif argumentRegex.match(self.token()): + result = "arg" + self.token()[1:] + self.advance() + else: + op = self.token() + index = self.index + self.advance() + params = self.generateParameters() + self.code.append(generateB3OpCode(index, op, params)) + result = temp(index) + + return result + + def generate(self, wasmOp): + if len(self.tokens) == 1: + params = ["arg" + str(param) for param in range(len(wasmOp["parameter"]))] + return " result = m_currentBlock->appendNew<Value>(m_proc, B3::" + self.token() + ", Origin(), " + ", ".join(params) + ")" + result = self.generateOpcode() + self.code.append("result = " + result) + return " " + " \n".join(self.code) + + +def temp(index): + return "temp" + str(index) + + +def generateB3OpCode(index, op, params): + return "Value* " + temp(index) + " = m_currentBlock->appendNew<Value>(m_proc, B3::" + op + ", Origin(), " + ", ".join(params) + ");" + + +def generateConstCode(index, value, type): + return "Value* " + temp(index) + " = m_currentBlock->appendIntConstant(m_proc, Origin(), B3::" + type + ", " + value + ");" + + +def generateB3Code(wasmOp, source): + tokens = lex(Source(source)) + parser = CodeGenerator(tokens) + return parser.generate(wasmOp) + + +def generateSimpleCode(op): + opcode = op["opcode"] + b3op = opcode["b3op"] + args = ["ExpressionType arg" + str(param) for param in range(len(opcode["parameter"]))] + args.append("ExpressionType& result") + return """ +template<> auto B3IRGenerator::addOp<OpType::""" + wasm.toCpp(op["name"]) + ">(" + ", ".join(args) + """) -> PartialResult +{ +""" + generateB3Code(opcode, b3op) + """; + return { }; +} +""" + + +definitions = [generateSimpleCode(op) for op in wasm.opcodeIterator(lambda op: isSimple(op) and (isBinary(op) or isUnary(op)))] +contents = wasm.header + """ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +namespace JSC { namespace Wasm { + +""" + "".join(definitions) + """ + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) + +""" + +wasmB3IRGeneratorHFile.write(contents) +wasmB3IRGeneratorHFile.close() diff --git a/Source/JavaScriptCore/wasm/generateWasmOpsHeader.py b/Source/JavaScriptCore/wasm/generateWasmOpsHeader.py new file mode 100755 index 000000000..de8aa2c69 --- /dev/null +++ b/Source/JavaScriptCore/wasm/generateWasmOpsHeader.py @@ -0,0 +1,285 @@ +#! /usr/bin/python + +# Copyright (C) 2016 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 AND ITS CONTRIBUTORS "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 OR ITS 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. + +# This tool has a couple of helpful macros to process Wasm files from the wasm.json. + +from generateWasm import * +import optparse +import sys + +parser = optparse.OptionParser(usage="usage: %prog <wasm.json> <WasmOps.h>") +(options, args) = parser.parse_args(sys.argv[0:]) +if len(args) != 3: + parser.error(parser.usage) + +wasm = Wasm(args[0], args[1]) +types = wasm.types +opcodes = wasm.opcodes +wasmOpsHFile = open(args[2], "w") + + +def cppMacro(wasmOpcode, value, b3, inc): + return " \\\n macro(" + wasm.toCpp(wasmOpcode) + ", " + hex(int(value)) + ", " + b3 + ", " + str(inc) + ")" + + +def typeMacroizer(): + inc = 0 + for ty in wasm.types: + yield cppMacro(ty, wasm.types[ty]["value"], wasm.types[ty]["b3type"], inc) + inc += 1 + +type_definitions = ["#define FOR_EACH_WASM_TYPE(macro)"] +type_definitions.extend([t for t in typeMacroizer()]) +type_definitions = "".join(type_definitions) + + +def opcodeMacroizer(filter): + inc = 0 + for op in wasm.opcodeIterator(filter): + b3op = "Oops" + if isSimple(op["opcode"]): + b3op = op["opcode"]["b3op"] + yield cppMacro(op["name"], op["opcode"]["value"], b3op, inc) + inc += 1 + +defines = ["#define FOR_EACH_WASM_SPECIAL_OP(macro)"] +defines.extend([op for op in opcodeMacroizer(lambda op: not (isUnary(op) or isBinary(op) or op["category"] == "control" or op["category"] == "memory"))]) +defines.append("\n\n#define FOR_EACH_WASM_CONTROL_FLOW_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: op["category"] == "control")]) +defines.append("\n\n#define FOR_EACH_WASM_SIMPLE_UNARY_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: isUnary(op) and isSimple(op))]) +defines.append("\n\n#define FOR_EACH_WASM_UNARY_OP(macro) \\\n FOR_EACH_WASM_SIMPLE_UNARY_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: isUnary(op) and not (isSimple(op)))]) +defines.append("\n\n#define FOR_EACH_WASM_SIMPLE_BINARY_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: isBinary(op) and isSimple(op))]) +defines.append("\n\n#define FOR_EACH_WASM_BINARY_OP(macro) \\\n FOR_EACH_WASM_SIMPLE_BINARY_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: isBinary(op) and not (isSimple(op)))]) +defines.append("\n\n#define FOR_EACH_WASM_MEMORY_LOAD_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: (op["category"] == "memory" and len(op["return"]) == 1))]) +defines.append("\n\n#define FOR_EACH_WASM_MEMORY_STORE_OP(macro)") +defines.extend([op for op in opcodeMacroizer(lambda op: (op["category"] == "memory" and len(op["return"]) == 0))]) +defines.append("\n\n") + +defines = "".join(defines) + +opValueSet = set([op for op in wasm.opcodeIterator(lambda op: True, lambda op: opcodes[op]["value"])]) +maxOpValue = max(opValueSet) + + +# Luckily, python does floor division rather than trunc division so this works. +def ceilDiv(a, b): + return -(-a // b) + + +def bitSet(): + v = "" + for i in range(ceilDiv(maxOpValue, 8)): + entry = 0 + for j in range(8): + if i * 8 + j in opValueSet: + entry |= 1 << j + v += (", " if i else "") + hex(entry) + return v + +validOps = bitSet() + +contents = wasm.header + """ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include <cstdint> + +namespace JSC { namespace Wasm { + +static constexpr unsigned expectedVersionNumber = """ + wasm.expectedVersionNumber + """; + +static constexpr unsigned numTypes = """ + str(len(types)) + """; + +""" + type_definitions + """ +#define CREATE_ENUM_VALUE(name, id, b3type, inc) name = id, +enum Type : int8_t { + FOR_EACH_WASM_TYPE(CREATE_ENUM_VALUE) +}; +#undef CREATE_ENUM_VALUE + +#define CREATE_CASE(name, id, b3type, inc) case id: return true; +template <typename Int> +inline bool isValidType(Int i) +{ + switch (i) { + default: return false; + FOR_EACH_WASM_TYPE(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return false; +} +#undef CREATE_CASE + +#define CREATE_CASE(name, id, b3type, inc) case name: return b3type; +inline B3::Type toB3Type(Type type) +{ + switch (type) { + FOR_EACH_WASM_TYPE(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return B3::Void; +} +#undef CREATE_CASE + +#define CREATE_CASE(name, id, b3type, inc) case name: return #name; +inline const char* makeString(Type type) +{ + switch (type) { + FOR_EACH_WASM_TYPE(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} +#undef CREATE_CASE + +#define CREATE_CASE(name, id, b3type, inc) case id: return inc; +inline int linearizeType(Type type) +{ + switch (type) { + FOR_EACH_WASM_TYPE(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return 0; +} +#undef CREATE_CASE + +#define CREATE_CASE(name, id, b3type, inc) case inc: return name; +inline Type linearizedToType(int i) +{ + switch (i) { + FOR_EACH_WASM_TYPE(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return Void; +} +#undef CREATE_CASE + + +""" + defines + """ +#define FOR_EACH_WASM_OP(macro) \\ + FOR_EACH_WASM_SPECIAL_OP(macro) \\ + FOR_EACH_WASM_CONTROL_FLOW_OP(macro) \\ + FOR_EACH_WASM_UNARY_OP(macro) \\ + FOR_EACH_WASM_BINARY_OP(macro) \\ + FOR_EACH_WASM_MEMORY_LOAD_OP(macro) \\ + FOR_EACH_WASM_MEMORY_STORE_OP(macro) + +#define CREATE_ENUM_VALUE(name, id, b3op, inc) name = id, + +enum OpType : uint8_t { + FOR_EACH_WASM_OP(CREATE_ENUM_VALUE) +}; + +template<typename Int> +inline bool isValidOpType(Int i) +{ + // Bitset of valid ops. + static const uint8_t valid[] = { """ + validOps + """ }; + return 0 <= i && i <= """ + str(maxOpValue) + """ && (valid[i / 8] & (1 << (i % 8))); +} + +enum class BinaryOpType : uint8_t { + FOR_EACH_WASM_BINARY_OP(CREATE_ENUM_VALUE) +}; + +enum class UnaryOpType : uint8_t { + FOR_EACH_WASM_UNARY_OP(CREATE_ENUM_VALUE) +}; + +enum class LoadOpType : uint8_t { + FOR_EACH_WASM_MEMORY_LOAD_OP(CREATE_ENUM_VALUE) +}; + +enum class StoreOpType : uint8_t { + FOR_EACH_WASM_MEMORY_STORE_OP(CREATE_ENUM_VALUE) +}; + +#undef CREATE_ENUM_VALUE + +inline bool isControlOp(OpType op) +{ + switch (op) { +#define CREATE_CASE(name, id, b3op, inc) case OpType::name: + FOR_EACH_WASM_CONTROL_FLOW_OP(CREATE_CASE) + return true; +#undef CREATE_CASE + default: + break; + } + return false; +} + +inline bool isSimple(UnaryOpType op) +{ + switch (op) { +#define CREATE_CASE(name, id, b3op, inc) case UnaryOpType::name: + FOR_EACH_WASM_SIMPLE_UNARY_OP(CREATE_CASE) + return true; +#undef CREATE_CASE + default: + break; + } + return false; +} + +inline bool isSimple(BinaryOpType op) +{ + switch (op) { +#define CREATE_CASE(name, id, b3op, inc) case BinaryOpType::name: + FOR_EACH_WASM_SIMPLE_BINARY_OP(CREATE_CASE) + return true; +#undef CREATE_CASE + default: + break; + } + return false; +} + +#define CREATE_CASE(name, id, b3type, inc) case name: return #name; +inline const char* makeString(OpType op) +{ + switch (op) { + FOR_EACH_WASM_OP(CREATE_CASE) + } + RELEASE_ASSERT_NOT_REACHED(); + return nullptr; +} +#undef CREATE_CASE + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) + +""" + +wasmOpsHFile.write(contents) +wasmOpsHFile.close() diff --git a/Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py b/Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py new file mode 100755 index 000000000..c3d371251 --- /dev/null +++ b/Source/JavaScriptCore/wasm/generateWasmValidateInlinesHeader.py @@ -0,0 +1,170 @@ +#!/usr/bin/env python + +# Copyright (C) 2016 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 AND ITS CONTRIBUTORS "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 OR ITS 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. + +# This tool has a couple of helpful macros to process Wasm files from the wasm.json. + +from generateWasm import * +import optparse +import sys + +parser = optparse.OptionParser(usage="usage: %prog <wasm.json> <WasmOps.h>") +(options, args) = parser.parse_args(sys.argv[0:]) +if len(args) != 3: + parser.error(parser.usage) + +wasm = Wasm(args[0], args[1]) +opcodes = wasm.opcodes +wasmValidateInlinesHFile = open(args[2], "w") + + +def cppType(name): + result = { + "bool": "I32", + "addr": "I32", + "i32": "I32", + "i64": "I64", + "f32": "F32", + "f64": "F64", + }.get(name, None) + if result == None: + raise ValueError("Unknown type name: " + name) + return result + + +def toCpp(name): + return wasm.toCpp(name) + + +def unaryMacro(name): + op = opcodes[name] + return """ +template<> auto Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType value, ExpressionType& result) -> Result +{ + if (UNLIKELY(value != """ + cppType(op["parameter"][0]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ value type mismatch"); + + result = """ + cppType(op["return"][0]) + """; + return { }; +} +""" + + +def binaryMacro(name): + op = opcodes[name] + return """ +template<> auto Validate::addOp<OpType::""" + toCpp(name) + """>(ExpressionType left, ExpressionType right, ExpressionType& result) -> Result +{ + if (UNLIKELY(left != """ + cppType(op["parameter"][0]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ left value type mismatch"); + + if (UNLIKELY(right != """ + cppType(op["parameter"][1]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ right value type mismatch"); + + result = """ + cppType(op["return"][0]) + """; + return { }; +} +""" + +def loadMacro(name): + op = opcodes[name] + return """ + case LoadOpType::""" + toCpp(name) + """: { + if (UNLIKELY(pointer != """ + cppType(op["parameter"][0]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ pointer type mismatch"); + + result = """ + cppType(op["return"][0]) + """; + return { }; + }""" + + +def storeMacro(name): + op = opcodes[name] + return """ + case StoreOpType::""" + toCpp(name) + """: { + if (UNLIKELY(pointer != """ + cppType(op["parameter"][0]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ pointer type mismatch"); + + if (UNLIKELY(value != """ + cppType(op["parameter"][1]) + """)) + return UnexpectedType<Result::ErrorType>("validation failed: """ + name + """ value type mismatch"); + + return { }; + }""" + + +unarySpecializations = "".join([op for op in wasm.opcodeIterator(isUnary, unaryMacro)]) +binarySpecializations = "".join([op for op in wasm.opcodeIterator(isBinary, binaryMacro)]) +loadCases = "".join([op for op in wasm.opcodeIterator(lambda op: op["category"] == "memory" and len(op["return"]) == 1, loadMacro)]) +storeCases = "".join([op for op in wasm.opcodeIterator(lambda op: op["category"] == "memory" and len(op["return"]) == 0, storeMacro)]) + +contents = wasm.header + """ +// This file is intended to be inlined by WasmValidate.cpp only! It should not be included elsewhere. + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include <wtf/StdLibExtras.h> + +#if COMPILER(GCC) && ASSERT_DISABLED +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wreturn-type" +#endif // COMPILER(GCC) && ASSERT_DISABLED + +namespace JSC { namespace Wasm { + +""" + unarySpecializations + binarySpecializations + """ + +auto Validate::load(LoadOpType op, ExpressionType pointer, ExpressionType& result, uint32_t) -> Result +{ + if (UNLIKELY(!hasMemory())) + return UnexpectedType<Result::ErrorType>("validation failed: load instruction without memory"); + + switch (op) { +""" + loadCases + """ + } +} + +auto Validate::store(StoreOpType op, ExpressionType pointer, ExpressionType value, uint32_t) -> Result +{ + if (UNLIKELY(!hasMemory())) + return UnexpectedType<Result::ErrorType>("validation failed: store instruction without memory"); + + switch (op) { +""" + storeCases + """ + } +} + +} } // namespace JSC::Wasm + +#if COMPILER(GCC) && ASSERT_DISABLED +#pragma GCC diagnostic pop +#endif // COMPILER(GCC) && ASSERT_DISABLED + +#endif // ENABLE(WEBASSEMBLY) + +""" + +wasmValidateInlinesHFile.write(contents) +wasmValidateInlinesHFile.close() diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp new file mode 100644 index 000000000..c27712a10 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyCallee.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo JSWebAssemblyCallee::s_info = { "WebAssemblyCallee", nullptr, 0, CREATE_METHOD_TABLE(JSWebAssemblyCallee) }; + +JSWebAssemblyCallee::JSWebAssemblyCallee(VM& vm) + : Base(vm, vm.webAssemblyCalleeStructure.get()) +{ } + +void JSWebAssemblyCallee::finishCreation(VM& vm, Wasm::Entrypoint&& entrypoint) +{ + Base::finishCreation(vm); + + m_entrypoint = WTFMove(entrypoint); +} + +void JSWebAssemblyCallee::destroy(JSCell* cell) +{ + JSWebAssemblyCallee* thisObject = static_cast<JSWebAssemblyCallee*>(cell); + thisObject->JSWebAssemblyCallee::~JSWebAssemblyCallee(); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.h new file mode 100644 index 000000000..b8777da0d --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSCell.h" +#include "RegisterAtOffsetList.h" +#include "Structure.h" +#include "WasmFormat.h" + +namespace JSC { + +class JSWebAssemblyCallee : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static JSWebAssemblyCallee* create(VM& vm, Wasm::Entrypoint&& entrypoint) + { + JSWebAssemblyCallee* callee = new (NotNull, allocateCell<JSWebAssemblyCallee>(vm.heap)) JSWebAssemblyCallee(vm); + callee->finishCreation(vm, WTFMove(entrypoint)); + return callee; + } + + static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); + } + + DECLARE_EXPORT_INFO; + static const bool needsDestruction = true; + static void destroy(JSCell*); + + void* entrypoint() { return m_entrypoint.compilation->code().executableAddress(); } + + RegisterAtOffsetList* calleeSaveRegisters() { return &m_entrypoint.calleeSaveRegisters; } + +private: + void finishCreation(VM&, Wasm::Entrypoint&&); + JSWebAssemblyCallee(VM&); + + Wasm::Entrypoint m_entrypoint; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.cpp new file mode 100644 index 000000000..e485889d1 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyCompileError.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +namespace JSC { + +JSWebAssemblyCompileError* JSWebAssemblyCompileError::create(ExecState* state, VM& vm, Structure* structure, const String& message) +{ + auto* instance = new (NotNull, allocateCell<JSWebAssemblyCompileError>(vm.heap)) JSWebAssemblyCompileError(vm, structure); + instance->m_sourceAppender = defaultSourceAppender; + bool useCurrentFrame = true; + instance->finishCreation(state, vm, message, useCurrentFrame); + return instance; +} + +JSWebAssemblyCompileError::JSWebAssemblyCompileError(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +const ClassInfo JSWebAssemblyCompileError::s_info = { "WebAssembly.CompileError", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyCompileError) }; + + +JSObject* createJSWebAssemblyCompileError(ExecState* state, VM& vm, const String& message) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = state->lexicalGlobalObject(); + return JSWebAssemblyCompileError::create(state, vm, globalObject->WebAssemblyCompileErrorStructure(), message); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.h new file mode 100644 index 000000000..04e3f2af4 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCompileError.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "ErrorInstance.h" + +namespace JSC { + +class JSWebAssemblyCompileError : public ErrorInstance { +public: + typedef ErrorInstance Base; + + static JSWebAssemblyCompileError* create(ExecState*, VM&, Structure*, const String&); + static JSWebAssemblyCompileError* create(ExecState* state, VM& vm, Structure* structure, JSValue message) + { + return create(state, vm, structure, message.isUndefined() ? String() : message.toWTFString(state)); + } + + DECLARE_INFO; + +protected: + JSWebAssemblyCompileError(VM&, Structure*); +}; + +JSObject* createJSWebAssemblyCompileError(ExecState*, VM&, const String&); + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h new file mode 100644 index 000000000..b645a5635 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyHelpers.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSArrayBuffer.h" +#include "JSCJSValue.h" + +namespace JSC { + +ALWAYS_INLINE uint32_t toNonWrappingUint32(ExecState* exec, JSValue value) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + double doubleValue = value.toInteger(exec); + RETURN_IF_EXCEPTION(throwScope, { }); + if (doubleValue < 0 || doubleValue > UINT_MAX) { + throwException(exec, throwScope, + createRangeError(exec, ASCIILiteral("Expect an integer argument in the range: [0, 2^32 - 1]"))); + return { }; + } + + return static_cast<uint32_t>(doubleValue); +} + +ALWAYS_INLINE uint8_t* getWasmBufferFromValue(ExecState* exec, JSValue value, size_t& byteOffset, size_t& byteSize) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + // If the given bytes argument is not a BufferSource, a TypeError exception is thrown. + JSArrayBuffer* arrayBuffer = value.getObject() ? jsDynamicCast<JSArrayBuffer*>(vm, value.getObject()) : nullptr; + JSArrayBufferView* arrayBufferView = value.getObject() ? jsDynamicCast<JSArrayBufferView*>(vm, value.getObject()) : nullptr; + if (!(arrayBuffer || arrayBufferView)) { + throwException(exec, throwScope, createTypeError(exec, + ASCIILiteral("first argument must be an ArrayBufferView or an ArrayBuffer"), defaultSourceAppender, runtimeTypeForValue(value))); + return nullptr; + } + + if (arrayBufferView ? arrayBufferView->isNeutered() : arrayBuffer->impl()->isNeutered()) { + throwException(exec, throwScope, createTypeError(exec, + ASCIILiteral("underlying TypedArray has been detatched from the ArrayBuffer"), defaultSourceAppender, runtimeTypeForValue(value))); + return nullptr; + } + + byteOffset = arrayBufferView ? arrayBufferView->byteOffset() : 0; + byteSize = arrayBufferView ? arrayBufferView->length() : arrayBuffer->impl()->byteLength(); + return arrayBufferView ? static_cast<uint8_t*>(arrayBufferView->vector()) : static_cast<uint8_t*>(arrayBuffer->impl()->data()); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp new file mode 100644 index 000000000..fbc1cebfb --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyInstance.h" + +#if ENABLE(WEBASSEMBLY) + +#include "AbstractModuleRecord.h" +#include "JSCInlines.h" +#include "JSModuleEnvironment.h" +#include "JSModuleNamespaceObject.h" +#include "JSWebAssemblyMemory.h" +#include "JSWebAssemblyModule.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject) +{ + // FIXME: These objects could be pretty big we should try to throw OOM here. + auto* instance = new (NotNull, allocateCell<JSWebAssemblyInstance>(vm.heap, allocationSize(module->moduleInformation().importFunctionSignatureIndices.size()))) JSWebAssemblyInstance(vm, structure, module->moduleInformation().importFunctionSignatureIndices.size()); + instance->finishCreation(vm, module, moduleNamespaceObject); + return instance; +} + +Structure* JSWebAssemblyInstance::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSWebAssemblyInstance::JSWebAssemblyInstance(VM& vm, Structure* structure, unsigned numImportFunctions) + : Base(vm, structure) + , m_numImportFunctions(numImportFunctions) +{ + memset(importFunctions(), 0, m_numImportFunctions * sizeof(WriteBarrier<JSCell>)); +} + +void JSWebAssemblyInstance::finishCreation(VM& vm, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + + const size_t extraMemorySize = module->moduleInformation().globals.size() * sizeof(Register); + m_globals = MallocPtr<uint64_t>::malloc(extraMemorySize); + heap()->reportExtraMemoryAllocated(extraMemorySize); + + m_module.set(vm, this, module); + m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject); + putDirect(vm, Identifier::fromString(&vm, "exports"), moduleNamespaceObject, None); +} + +void JSWebAssemblyInstance::destroy(JSCell* cell) +{ + static_cast<JSWebAssemblyInstance*>(cell)->JSWebAssemblyInstance::~JSWebAssemblyInstance(); +} + +void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<JSWebAssemblyInstance*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_module); + visitor.append(thisObject->m_moduleNamespaceObject); + visitor.append(thisObject->m_memory); + visitor.append(thisObject->m_table); + visitor.reportExtraMemoryVisited(thisObject->module()->moduleInformation().globals.size()); + for (unsigned i = 0; i < thisObject->m_numImportFunctions; ++i) + visitor.append(*thisObject->importFunction(i)); +} + +const ClassInfo JSWebAssemblyInstance::s_info = { "WebAssembly.Instance", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyInstance) }; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h new file mode 100644 index 000000000..99a2c90f7 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "JSWebAssemblyMemory.h" +#include "JSWebAssemblyTable.h" + +namespace JSC { + +class JSModuleNamespaceObject; +class JSWebAssemblyModule; + +class JSWebAssemblyInstance : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + + static JSWebAssemblyInstance* create(VM&, Structure*, JSWebAssemblyModule*, JSModuleNamespaceObject*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + JSWebAssemblyModule* module() + { + ASSERT(m_module); + return m_module.get(); + } + + WriteBarrier<JSCell>* importFunction(unsigned idx) + { + RELEASE_ASSERT(idx < m_numImportFunctions); + return &importFunctions()[idx]; + } + + WriteBarrier<JSCell>* importFunctions() + { + return bitwise_cast<WriteBarrier<JSCell>*>(bitwise_cast<char*>(this) + offsetOfImportFunctions()); + } + + void setImportFunction(VM& vm, JSCell* value, unsigned idx) + { + importFunction(idx)->set(vm, this, value); + } + + JSWebAssemblyMemory* memory() { return m_memory.get(); } + void setMemory(VM& vm, JSWebAssemblyMemory* memory) { m_memory.set(vm, this, memory); } + + JSWebAssemblyTable* table() { return m_table.get(); } + void setTable(VM& vm, JSWebAssemblyTable* table) { m_table.set(vm, this, table); } + + int32_t loadI32Global(unsigned i) const { return m_globals.get()[i]; } + int64_t loadI64Global(unsigned i) const { return m_globals.get()[i]; } + float loadF32Global(unsigned i) const { return bitwise_cast<float>(loadI32Global(i)); } + double loadF64Global(unsigned i) const { return bitwise_cast<double>(loadI64Global(i)); } + void setGlobal(unsigned i, int64_t bits) { m_globals.get()[i] = bits; } + + static size_t offsetOfImportFunction(unsigned idx) + { + return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * idx; + } + + static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_memory); } + static ptrdiff_t offsetOfTable() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_table); } + static ptrdiff_t offsetOfGlobals() { return OBJECT_OFFSETOF(JSWebAssemblyInstance, m_globals); } + static size_t offsetOfImportFunctions() { return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSCell>)>(sizeof(JSWebAssemblyInstance)); } + static size_t offsetOfImportFunction(size_t importFunctionNum) { return offsetOfImportFunctions() + importFunctionNum * sizeof(sizeof(WriteBarrier<JSCell>)); } + +protected: + JSWebAssemblyInstance(VM&, Structure*, unsigned numImportFunctions); + void finishCreation(VM&, JSWebAssemblyModule*, JSModuleNamespaceObject*); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + static size_t allocationSize(unsigned numImportFunctions) + { + return offsetOfImportFunctions() + sizeof(WriteBarrier<JSCell>) * numImportFunctions; + } + +private: + WriteBarrier<JSWebAssemblyModule> m_module; + WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject; + WriteBarrier<JSWebAssemblyMemory> m_memory; + WriteBarrier<JSWebAssemblyTable> m_table; + MallocPtr<uint64_t> m_globals; + unsigned m_numImportFunctions; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.cpp new file mode 100644 index 000000000..ef52fe8be --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyLinkError.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +namespace JSC { + +JSWebAssemblyLinkError* JSWebAssemblyLinkError::create(ExecState* state, VM& vm, Structure* structure, const String& message) +{ + auto* instance = new (NotNull, allocateCell<JSWebAssemblyLinkError>(vm.heap)) JSWebAssemblyLinkError(vm, structure); + instance->m_sourceAppender = defaultSourceAppender; + bool useCurrentFrame = true; + instance->finishCreation(state, vm, message, useCurrentFrame); + return instance; +} + +JSWebAssemblyLinkError::JSWebAssemblyLinkError(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +const ClassInfo JSWebAssemblyLinkError::s_info = { "WebAssembly.LinkError", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyLinkError) }; + + +JSObject* createJSWebAssemblyLinkError(ExecState* state, VM& vm, const String& message) +{ + ASSERT(!message.isEmpty()); + JSGlobalObject* globalObject = state->lexicalGlobalObject(); + return JSWebAssemblyLinkError::create(state, vm, globalObject->WebAssemblyLinkErrorStructure(), message); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.h new file mode 100644 index 000000000..f2651eb20 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyLinkError.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "ErrorInstance.h" + +namespace JSC { + +class JSWebAssemblyLinkError : public ErrorInstance { +public: + typedef ErrorInstance Base; + + static JSWebAssemblyLinkError* create(ExecState*, VM&, Structure*, const String&); + static JSWebAssemblyLinkError* create(ExecState* state, VM& vm, Structure* structure, JSValue message) + { + return create(state, vm, structure, message.isUndefined() ? String() : message.toWTFString(state)); + } + + DECLARE_INFO; + +protected: + JSWebAssemblyLinkError(VM&, Structure*); +}; + +JSObject* createJSWebAssemblyLinkError(ExecState*, VM&, const String&); + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp new file mode 100644 index 000000000..f4253b5bf --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyMemory.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +#include "ArrayBuffer.h" +#include "JSArrayBuffer.h" + +namespace JSC { + +const ClassInfo JSWebAssemblyMemory::s_info = { "WebAssembly.Memory", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyMemory) }; + +JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, Wasm::Memory&& memory) +{ + auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, std::forward<Wasm::Memory>(memory)); + instance->finishCreation(vm); + return instance; +} + +Structure* JSWebAssemblyMemory::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, Wasm::Memory&& memory) + : Base(vm, structure) + , m_memory(WTFMove(memory)) +{ +} + +JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject) +{ + if (m_bufferWrapper) + return m_bufferWrapper.get(); + + auto destructor = [] (void*) { + // We don't need to do anything here to destroy the memory. + // The ArrayBuffer backing the JSArrayBuffer is only owned by us, + // so we guarantee its lifecycle. + }; + m_buffer = ArrayBuffer::createFromBytes(memory()->memory(), memory()->size(), WTFMove(destructor)); + m_bufferWrapper.set(vm, this, JSArrayBuffer::create(vm, globalObject->m_arrayBufferStructure.get(), m_buffer.get())); + RELEASE_ASSERT(m_bufferWrapper); + return m_bufferWrapper.get(); +} + +Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool shouldThrowExceptionsOnFailure) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + Wasm::PageCount oldPageCount = memory()->sizeInPages(); + + if (!Wasm::PageCount::isValid(delta)) { + if (shouldThrowExceptionsOnFailure) + throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory.grow expects the delta to be a valid page count"))); + return Wasm::PageCount(); + } + + Wasm::PageCount newSize = oldPageCount + Wasm::PageCount(delta); + if (!newSize) { + if (shouldThrowExceptionsOnFailure) + throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory.grow expects the grown size to be a valid page count"))); + return Wasm::PageCount(); + } + + if (delta) { + bool success = memory()->grow(newSize); + if (!success) { + if (shouldThrowExceptionsOnFailure) + throwException(exec, throwScope, createOutOfMemoryError(exec)); + return Wasm::PageCount(); + } + } + + // We need to clear out the old array buffer because it might now be pointing + // to stale memory. + // Neuter the old array. + if (m_buffer) { + ArrayBufferContents dummyContents; + m_buffer->transferTo(vm, dummyContents); + m_buffer = nullptr; + m_bufferWrapper.clear(); + } + + return oldPageCount; +} + +void JSWebAssemblyMemory::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); +} + +void JSWebAssemblyMemory::destroy(JSCell* cell) +{ + auto memory = static_cast<JSWebAssemblyMemory*>(cell); + ASSERT(memory->classInfo() == info()); + VM& vm = *memory->vm(); + + if (memory->m_buffer) { + ArrayBufferContents dummyContents; + memory->m_buffer->transferTo(vm, dummyContents); + memory->m_buffer = nullptr; + } + + memory->JSWebAssemblyMemory::~JSWebAssemblyMemory(); +} + +void JSWebAssemblyMemory::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<JSWebAssemblyMemory*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_bufferWrapper); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h new file mode 100644 index 000000000..fff141570 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "WasmMemory.h" +#include <wtf/RefPtr.h> + +namespace JSC { + +class ArrayBuffer; +class JSArrayBuffer; + +class JSWebAssemblyMemory : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static JSWebAssemblyMemory* create(VM&, Structure*, Wasm::Memory&&); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + Wasm::Memory* memory() { return &m_memory; } + JSArrayBuffer* buffer(VM& vm, JSGlobalObject*); + Wasm::PageCount grow(ExecState*, uint32_t delta, bool shouldThrowExceptionsOnFailure); + + static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfMemory(); } + static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfSize(); } + +protected: + JSWebAssemblyMemory(VM&, Structure*, Wasm::Memory&&); + void finishCreation(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + Wasm::Memory m_memory; + WriteBarrier<JSArrayBuffer> m_bufferWrapper; + RefPtr<ArrayBuffer> m_buffer; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp new file mode 100644 index 000000000..08fa58462 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyModule.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" +#include "JSWebAssemblyCallee.h" +#include "WasmFormat.h" +#include "WasmMemory.h" +#include <wtf/StdLibExtras.h> + +namespace JSC { + +const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyModule) }; + +JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, SymbolTable* exportSymbolTable, unsigned calleeCount) +{ + auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmExitStubs>>(wasmExitStubs), calleeCount); + instance->finishCreation(vm, exportSymbolTable); + return instance; +} + +Structure* JSWebAssemblyModule::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, unsigned calleeCount) + : Base(vm, structure) + , m_moduleInformation(WTFMove(moduleInformation)) + , m_callLinkInfos(WTFMove(callLinkInfos)) + , m_wasmExitStubs(WTFMove(wasmExitStubs)) + , m_calleeCount(calleeCount) +{ + memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2); +} + +void JSWebAssemblyModule::finishCreation(VM& vm, SymbolTable* exportSymbolTable) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); + m_exportSymbolTable.set(vm, this, exportSymbolTable); +} + +void JSWebAssemblyModule::destroy(JSCell* cell) +{ + static_cast<JSWebAssemblyModule*>(cell)->JSWebAssemblyModule::~JSWebAssemblyModule(); +} + +void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWebAssemblyModule* thisObject = jsCast<JSWebAssemblyModule*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_exportSymbolTable); + for (unsigned i = 0; i < thisObject->m_calleeCount * 2; i++) + visitor.append(thisObject->callees()[i]); + + visitor.addUnconditionalFinalizer(&thisObject->m_unconditionalFinalizer); +} + +void JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally() +{ + JSWebAssemblyModule* thisObject = bitwise_cast<JSWebAssemblyModule*>( + bitwise_cast<char*>(this) - OBJECT_OFFSETOF(JSWebAssemblyModule, m_unconditionalFinalizer)); + for (auto iter = thisObject->m_callLinkInfos.begin(); !!iter; ++iter) + (*iter)->visitWeak(*thisObject->vm()); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h new file mode 100644 index 000000000..7e051f572 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "JSWebAssemblyCallee.h" +#include "UnconditionalFinalizer.h" +#include "WasmFormat.h" +#include <wtf/Bag.h> +#include <wtf/Vector.h> + +namespace JSC { + +class SymbolTable; + +class JSWebAssemblyModule : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, SymbolTable*, unsigned); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); } + SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); } + Wasm::SignatureIndex signatureIndexFromFunctionIndexSpace(unsigned functionIndexSpace) const + { + return m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace); + } + unsigned functionImportCount() const { return m_wasmExitStubs.size(); } + + JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace) + { + RELEASE_ASSERT(functionIndexSpace >= functionImportCount()); + unsigned calleeIndex = functionIndexSpace - functionImportCount(); + RELEASE_ASSERT(calleeIndex < m_calleeCount); + return callees()[calleeIndex].get(); + } + + JSWebAssemblyCallee* wasmEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace) + { + RELEASE_ASSERT(functionIndexSpace >= functionImportCount()); + unsigned calleeIndex = functionIndexSpace - functionImportCount(); + RELEASE_ASSERT(calleeIndex < m_calleeCount); + return callees()[calleeIndex + m_calleeCount].get(); + } + + void setJSEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee) + { + RELEASE_ASSERT(calleeIndex < m_calleeCount); + callees()[calleeIndex].set(vm, this, callee); + } + + void setWasmEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee) + { + RELEASE_ASSERT(calleeIndex < m_calleeCount); + callees()[calleeIndex + m_calleeCount].set(vm, this, callee); + } + + WriteBarrier<JSWebAssemblyCallee>* callees() + { + return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees()); + } + +protected: + JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, unsigned calleeCount); + void finishCreation(VM&, SymbolTable*); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + +private: + static size_t offsetOfCallees() + { + return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSWebAssemblyCallee>)>(sizeof(JSWebAssemblyModule)); + } + + static size_t allocationSize(unsigned numCallees) + { + return offsetOfCallees() + sizeof(WriteBarrier<JSWebAssemblyCallee>) * numCallees * 2; + } + + class UnconditionalFinalizer : public JSC::UnconditionalFinalizer { + void finalizeUnconditionally() override; + }; + + UnconditionalFinalizer m_unconditionalFinalizer; + std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation; + Bag<CallLinkInfo> m_callLinkInfos; + WriteBarrier<SymbolTable> m_exportSymbolTable; + Vector<Wasm::WasmExitStubs> m_wasmExitStubs; + unsigned m_calleeCount; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp new file mode 100644 index 000000000..e212a3776 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyRuntimeError.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +namespace JSC { + +JSWebAssemblyRuntimeError* JSWebAssemblyRuntimeError::create(ExecState* state, VM& vm, Structure* structure, const String& message) +{ + auto* instance = new (NotNull, allocateCell<JSWebAssemblyRuntimeError>(vm.heap)) JSWebAssemblyRuntimeError(vm, structure); + instance->m_sourceAppender = defaultSourceAppender; + bool useCurrentFrame = true; + instance->finishCreation(state, vm, message, useCurrentFrame); + return instance; +} + +JSWebAssemblyRuntimeError::JSWebAssemblyRuntimeError(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +const ClassInfo JSWebAssemblyRuntimeError::s_info = { "WebAssembly.RuntimeError", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyRuntimeError) }; + + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h new file mode 100644 index 000000000..5ac630d0f --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyRuntimeError.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "ErrorInstance.h" + +namespace JSC { + +class JSWebAssemblyRuntimeError : public ErrorInstance { +public: + typedef ErrorInstance Base; + + static JSWebAssemblyRuntimeError* create(ExecState*, VM&, Structure*, const String&); + static JSWebAssemblyRuntimeError* create(ExecState* state, VM& vm, Structure* structure, JSValue message) + { + return create(state, vm, structure, message.isUndefined() ? String() : message.toWTFString(state)); + } + + DECLARE_INFO; + +protected: + JSWebAssemblyRuntimeError(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp new file mode 100644 index 000000000..0aff9b924 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2016 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 "JSWebAssemblyTable.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" +#include "WasmFormat.h" + +namespace JSC { + +const ClassInfo JSWebAssemblyTable::s_info = { "WebAssembly.Table", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyTable) }; + +JSWebAssemblyTable* JSWebAssemblyTable::create(ExecState* exec, VM& vm, Structure* structure, uint32_t initial, std::optional<uint32_t> maximum) +{ + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (!isValidSize(initial)) { + throwException(exec, throwScope, createOutOfMemoryError(exec)); + return nullptr; + } + + auto* instance = new (NotNull, allocateCell<JSWebAssemblyTable>(vm.heap)) JSWebAssemblyTable(vm, structure, initial, maximum); + instance->finishCreation(vm); + return instance; +} + +Structure* JSWebAssemblyTable::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +JSWebAssemblyTable::JSWebAssemblyTable(VM& vm, Structure* structure, uint32_t initial, std::optional<uint32_t> maximum) + : Base(vm, structure) +{ + m_size = initial; + ASSERT(isValidSize(m_size)); + m_maximum = maximum; + ASSERT(!m_maximum || *m_maximum >= m_size); + + // FIXME: It might be worth trying to pre-allocate maximum here. The spec recommends doing so. + // But for now, we're not doing that. + m_functions = MallocPtr<Wasm::CallableFunction>::malloc(sizeof(Wasm::CallableFunction) * static_cast<size_t>(m_size)); + m_jsFunctions = MallocPtr<WriteBarrier<WebAssemblyFunction>>::malloc(sizeof(WriteBarrier<WebAssemblyFunction>) * static_cast<size_t>(m_size)); + for (uint32_t i = 0; i < m_size; ++i) { + new (&m_functions.get()[i]) Wasm::CallableFunction(); + ASSERT(m_functions.get()[i].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. + new (&m_jsFunctions.get()[i]) WriteBarrier<WebAssemblyFunction>(); + } +} + +void JSWebAssemblyTable::finishCreation(VM& vm) +{ + Base::finishCreation(vm); + ASSERT(inherits(vm, info())); +} + +void JSWebAssemblyTable::destroy(JSCell* cell) +{ + static_cast<JSWebAssemblyTable*>(cell)->JSWebAssemblyTable::~JSWebAssemblyTable(); +} + +void JSWebAssemblyTable::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + JSWebAssemblyTable* thisObject = jsCast<JSWebAssemblyTable*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + + Base::visitChildren(thisObject, visitor); + + for (unsigned i = 0; i < thisObject->m_size; ++i) + visitor.append(thisObject->m_jsFunctions.get()[i]); +} + +bool JSWebAssemblyTable::grow(uint32_t newSize) +{ + if (newSize < m_size) + return false; + if (newSize == m_size) + return true; + if (maximum() && newSize > *maximum()) + return false; + if (!isValidSize(newSize)) + return false; + + m_functions.realloc(sizeof(Wasm::CallableFunction) * static_cast<size_t>(newSize)); + m_jsFunctions.realloc(sizeof(WriteBarrier<WebAssemblyFunction>) * static_cast<size_t>(newSize)); + + for (uint32_t i = m_size; i < newSize; ++i) { + new (&m_functions.get()[i]) Wasm::CallableFunction(); + new (&m_jsFunctions.get()[i]) WriteBarrier<WebAssemblyFunction>(); + } + m_size = newSize; + return true; +} + +void JSWebAssemblyTable::clearFunction(uint32_t index) +{ + RELEASE_ASSERT(index < m_size); + m_jsFunctions.get()[index] = WriteBarrier<WebAssemblyFunction>(); + m_functions.get()[index] = Wasm::CallableFunction(); + ASSERT(m_functions.get()[index].signatureIndex == Wasm::Signature::invalidIndex); // We rely on this in compiled code. +} + +void JSWebAssemblyTable::setFunction(VM& vm, uint32_t index, WebAssemblyFunction* function) +{ + RELEASE_ASSERT(index < m_size); + m_jsFunctions.get()[index].set(vm, this, function); + m_functions.get()[index] = Wasm::CallableFunction(function->signatureIndex(), function->wasmEntrypoint()); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h new file mode 100644 index 000000000..468a027ce --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/JSWebAssemblyTable.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" +#include "WebAssemblyFunction.h" +#include <wtf/MallocPtr.h> + +namespace JSC { + +namespace Wasm { +struct CallableFunction; +} + +class JSWebAssemblyTable : public JSDestructibleObject { +public: + typedef JSDestructibleObject Base; + + static JSWebAssemblyTable* create(ExecState*, VM&, Structure*, uint32_t initial, std::optional<uint32_t> maximum); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + std::optional<uint32_t> maximum() const { return m_maximum; } + uint32_t size() const { return m_size; } + bool grow(uint32_t newSize) WARN_UNUSED_RETURN; + WebAssemblyFunction* getFunction(uint32_t index) + { + RELEASE_ASSERT(index < m_size); + return m_jsFunctions.get()[index].get(); + } + void clearFunction(uint32_t index); + void setFunction(VM&, uint32_t index, WebAssemblyFunction*); + + static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_size); } + static ptrdiff_t offsetOfFunctions() { return OBJECT_OFFSETOF(JSWebAssemblyTable, m_functions); } + + static bool isValidSize(uint32_t size) + { + // This tops out at ~384 MB worth of data in this class. + return size < (1 << 24); + } + +private: + JSWebAssemblyTable(VM&, Structure*, uint32_t initial, std::optional<uint32_t> maximum); + void finishCreation(VM&); + static void destroy(JSCell*); + static void visitChildren(JSCell*, SlotVisitor&); + + MallocPtr<Wasm::CallableFunction> m_functions; + MallocPtr<WriteBarrier<WebAssemblyFunction>> m_jsFunctions; + std::optional<uint32_t> m_maximum; + uint32_t m_size; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.cpp new file mode 100644 index 000000000..40effb65f --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyCompileErrorConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyCompileError.h" +#include "WebAssemblyCompileErrorPrototype.h" + +#include "WebAssemblyCompileErrorConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyCompileErrorConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyCompileError, CREATE_METHOD_TABLE(WebAssemblyCompileErrorConstructor) }; + +/* Source for WebAssemblyCompileErrorConstructor.lut.h + @begin constructorTableWebAssemblyCompileError + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyCompileError(ExecState* state) +{ + auto& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue message = state->argument(0); + auto* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), asInternalFunction(state->jsCallee())->globalObject()->WebAssemblyCompileErrorStructure()); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(JSWebAssemblyCompileError::create(state, vm, structure, message)); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyCompileError(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.CompileError")); +} + +WebAssemblyCompileErrorConstructor* WebAssemblyCompileErrorConstructor::create(VM& vm, Structure* structure, WebAssemblyCompileErrorPrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyCompileErrorConstructor>(vm.heap)) WebAssemblyCompileErrorConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyCompileErrorConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyCompileErrorConstructor::finishCreation(VM& vm, WebAssemblyCompileErrorPrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("CompileError")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyCompileErrorConstructor::WebAssemblyCompileErrorConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyCompileErrorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyCompileError; + return ConstructType::Host; +} + +CallType WebAssemblyCompileErrorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyCompileError; + return CallType::Host; +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.h new file mode 100644 index 000000000..34a0e78c0 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyCompileErrorPrototype; + +class WebAssemblyCompileErrorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyCompileErrorConstructor* create(VM&, Structure*, WebAssemblyCompileErrorPrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyCompileErrorPrototype*); + +private: + WebAssemblyCompileErrorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.cpp new file mode 100644 index 000000000..0522e1f30 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyCompileErrorPrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" + +#include "WebAssemblyCompileErrorPrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyCompileErrorPrototype::s_info = { "WebAssembly.CompileError.prototype", &Base::s_info, &prototypeTableWebAssemblyCompileError, CREATE_METHOD_TABLE(WebAssemblyCompileErrorPrototype) }; + +/* Source for WebAssemblyCompileErrorPrototype.lut.h + @begin prototypeTableWebAssemblyCompileError + @end + */ + +WebAssemblyCompileErrorPrototype* WebAssemblyCompileErrorPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyCompileErrorPrototype>(vm.heap)) WebAssemblyCompileErrorPrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyCompileErrorPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyCompileErrorPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyCompileErrorPrototype::WebAssemblyCompileErrorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.h new file mode 100644 index 000000000..d3296513a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyCompileErrorPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyCompileErrorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyCompileErrorPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyCompileErrorPrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp new file mode 100644 index 000000000..5ae817f4f --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyFunction.h" + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "JSCInlines.h" +#include "JSFunctionInlines.h" +#include "JSObject.h" +#include "JSWebAssemblyCallee.h" +#include "JSWebAssemblyInstance.h" +#include "JSWebAssemblyMemory.h" +#include "JSWebAssemblyRuntimeError.h" +#include "LLIntThunks.h" +#include "ProtoCallFrame.h" +#include "VM.h" +#include "WasmFormat.h" +#include "WasmMemory.h" + +namespace JSC { + +const ClassInfo WebAssemblyFunction::s_info = { "WebAssemblyFunction", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyFunction) }; + +static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + WebAssemblyFunction* wasmFunction = jsDynamicCast<WebAssemblyFunction*>(vm, exec->jsCallee()); + if (!wasmFunction) + return JSValue::encode(throwException(exec, scope, createTypeError(exec, "expected a WebAssembly function", defaultSourceAppender, runtimeTypeForValue(exec->jsCallee())))); + Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex(); + const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex); + + { + // Check if we have a disallowed I64 use. + + for (unsigned argIndex = 0; argIndex < signature->argumentCount(); ++argIndex) { + if (signature->argument(argIndex) == Wasm::I64) { + JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(), + "WebAssembly function with an i64 argument can't be called from JavaScript"); + return JSValue::encode(throwException(exec, scope, error)); + } + } + + if (signature->returnType() == Wasm::I64) { + JSWebAssemblyRuntimeError* error = JSWebAssemblyRuntimeError::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyRuntimeErrorStructure(), + "WebAssembly function that returns i64 can't be called from JavaScript"); + return JSValue::encode(throwException(exec, scope, error)); + } + } + + Vector<JSValue> boxedArgs; + for (unsigned argIndex = 0; argIndex < signature->argumentCount(); ++argIndex) { + JSValue arg = exec->argument(argIndex); + switch (signature->argument(argIndex)) { + case Wasm::I32: + arg = JSValue::decode(arg.toInt32(exec)); + break; + case Wasm::F32: + arg = JSValue::decode(bitwise_cast<uint32_t>(arg.toFloat(exec))); + break; + case Wasm::F64: + arg = JSValue::decode(bitwise_cast<uint64_t>(arg.toNumber(exec))); + break; + case Wasm::Void: + case Wasm::I64: + case Wasm::Func: + case Wasm::Anyfunc: + RELEASE_ASSERT_NOT_REACHED(); + } + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + boxedArgs.append(arg); + } + + JSValue firstArgument = JSValue(); + int argCount = 1; + JSValue* remainingArgs = nullptr; + if (boxedArgs.size()) { + remainingArgs = boxedArgs.data(); + firstArgument = *remainingArgs; + remainingArgs++; + argCount = boxedArgs.size(); + } + + // Note: we specifically use the WebAssemblyFunction as the callee to begin with in the ProtoCallFrame. + // The reason for this is that calling into the llint may stack overflow, and the stack overflow + // handler might read the global object from the callee. The JSWebAssemblyCallee doesn't have a + // global object, but the WebAssemblyFunction does. + ProtoCallFrame protoCallFrame; + protoCallFrame.init(nullptr, wasmFunction, firstArgument, argCount, remainingArgs); + + // FIXME Do away with this entire function, and only use the entrypoint generated by B3. https://bugs.webkit.org/show_bug.cgi?id=166486 + JSWebAssemblyInstance* prevJSWebAssemblyInstance = vm.topJSWebAssemblyInstance; + vm.topJSWebAssemblyInstance = wasmFunction->instance(); + ASSERT(wasmFunction->instance()); + EncodedJSValue rawResult = vmEntryToWasm(wasmFunction->jsEntrypoint(), &vm, &protoCallFrame); + vm.topJSWebAssemblyInstance = prevJSWebAssemblyInstance; + RETURN_IF_EXCEPTION(scope, { }); + + switch (signature->returnType()) { + case Wasm::Void: + return JSValue::encode(jsUndefined()); + case Wasm::I32: + return JSValue::encode(jsNumber(static_cast<int32_t>(rawResult))); + case Wasm::F32: + return JSValue::encode(jsNumber(purifyNaN(static_cast<double>(bitwise_cast<float>(static_cast<int32_t>(rawResult)))))); + case Wasm::F64: + return JSValue::encode(jsNumber(purifyNaN(bitwise_cast<double>(rawResult)))); + case Wasm::I64: + case Wasm::Func: + case Wasm::Anyfunc: + RELEASE_ASSERT_NOT_REACHED(); + } + + return EncodedJSValue(); +} + +WebAssemblyFunction* WebAssemblyFunction::create(VM& vm, JSGlobalObject* globalObject, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::SignatureIndex signatureIndex) +{ + NativeExecutable* executable = vm.getHostFunction(callWebAssemblyFunction, NoIntrinsic, callHostFunctionAsConstructor, nullptr, name); + Structure* structure = globalObject->webAssemblyFunctionStructure(); + WebAssemblyFunction* function = new (NotNull, allocateCell<WebAssemblyFunction>(vm.heap)) WebAssemblyFunction(vm, globalObject, structure, wasmEntrypoint, signatureIndex); + function->finishCreation(vm, executable, length, name, instance, jsEntrypoint, wasmEntrypoint); + return function; +} + +Structure* WebAssemblyFunction::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + ASSERT(globalObject); + return Structure::create(vm, globalObject, prototype, TypeInfo(WebAssemblyFunctionType, StructureFlags), info()); +} + +WebAssemblyFunction::WebAssemblyFunction(VM& vm, JSGlobalObject* globalObject, Structure* structure, JSWebAssemblyCallee* wasmEntrypoint, Wasm::SignatureIndex signatureIndex) + : Base(vm, globalObject, structure) + , m_wasmEntryPointCode(wasmEntrypoint->entrypoint()) + , m_signatureIndex(signatureIndex) +{ } + +void WebAssemblyFunction::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + WebAssemblyFunction* thisObject = jsCast<WebAssemblyFunction*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_instance); + visitor.append(thisObject->m_jsEntrypoint); + visitor.append(thisObject->m_wasmEntrypoint); +} + +void WebAssemblyFunction::finishCreation(VM& vm, NativeExecutable* executable, unsigned length, const String& name, JSWebAssemblyInstance* instance, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint) +{ + Base::finishCreation(vm, executable, length, name); + ASSERT(inherits(vm, info())); + m_instance.set(vm, this, instance); + ASSERT(jsEntrypoint != wasmEntrypoint); + m_jsEntrypoint.set(vm, this, jsEntrypoint); + m_wasmEntrypoint.set(vm, this, wasmEntrypoint); + ASSERT(m_wasmEntrypoint->entrypoint() == m_wasmEntryPointCode); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h new file mode 100644 index 000000000..26694c16d --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunction.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSFunction.h" +#include "JSWebAssemblyCallee.h" +#include <wtf/Noncopyable.h> + +namespace JSC { + +class JSGlobalObject; +struct ProtoCallFrame; +class WebAssemblyInstance; + +namespace B3 { +class Compilation; +} + +class WebAssemblyFunction : public JSFunction { +public: + typedef JSFunction Base; + + const static unsigned StructureFlags = Base::StructureFlags; + + DECLARE_EXPORT_INFO; + + JS_EXPORT_PRIVATE static WebAssemblyFunction* create(VM&, JSGlobalObject*, unsigned, const String&, JSWebAssemblyInstance*, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint, Wasm::SignatureIndex); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + JSWebAssemblyInstance* instance() const { return m_instance.get(); } + Wasm::SignatureIndex signatureIndex() const { return m_signatureIndex; } + void* wasmEntrypoint() { return m_wasmEntryPointCode; } + void* jsEntrypoint() { return m_jsEntrypoint->entrypoint(); } + + static ptrdiff_t offsetOfInstance() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_instance); } + static ptrdiff_t offsetOfWasmEntryPointCode() { return OBJECT_OFFSETOF(WebAssemblyFunction, m_wasmEntryPointCode); } + +protected: + static void visitChildren(JSCell*, SlotVisitor&); + + void finishCreation(VM&, NativeExecutable*, unsigned length, const String& name, JSWebAssemblyInstance*, JSWebAssemblyCallee* jsEntrypoint, JSWebAssemblyCallee* wasmEntrypoint); + +private: + WebAssemblyFunction(VM&, JSGlobalObject*, Structure*, JSWebAssemblyCallee*, Wasm::SignatureIndex); + + WriteBarrier<JSWebAssemblyInstance> m_instance; + void* m_wasmEntryPointCode; // Cache code pointer: allows the wasm -> wasm stub to do a single load and jump instead of having dependent loads. + WriteBarrier<JSWebAssemblyCallee> m_jsEntrypoint; + WriteBarrier<JSWebAssemblyCallee> m_wasmEntrypoint; + Wasm::SignatureIndex m_signatureIndex; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.cpp diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyFunctionCell.h diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp new file mode 100644 index 000000000..763dc494c --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyInstanceConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSModuleEnvironment.h" +#include "JSModuleNamespaceObject.h" +#include "JSWebAssemblyInstance.h" +#include "JSWebAssemblyLinkError.h" +#include "JSWebAssemblyMemory.h" +#include "JSWebAssemblyModule.h" +#include "WebAssemblyFunction.h" +#include "WebAssemblyInstancePrototype.h" +#include "WebAssemblyModuleRecord.h" + +#include "WebAssemblyInstanceConstructor.lut.h" + +namespace JSC { + +static const bool verbose = false; + +const ClassInfo WebAssemblyInstanceConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyInstance, CREATE_METHOD_TABLE(WebAssemblyInstanceConstructor) }; + +/* Source for WebAssemblyInstanceConstructor.lut.h + @begin constructorTableWebAssemblyInstance + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* exec) +{ + auto& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* globalObject = exec->lexicalGlobalObject(); + + // If moduleObject is not a WebAssembly.Module instance, a TypeError is thrown. + JSWebAssemblyModule* jsModule = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->argument(0)); + if (!jsModule) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("first argument to WebAssembly.Instance must be a WebAssembly.Module"), defaultSourceAppender, runtimeTypeForValue(exec->argument(0))))); + const Wasm::ModuleInformation& moduleInformation = jsModule->moduleInformation(); + + // If the importObject parameter is not undefined and Type(importObject) is not Object, a TypeError is thrown. + JSValue importArgument = exec->argument(1); + JSObject* importObject = importArgument.getObject(); + if (!importArgument.isUndefined() && !importObject) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be undefined or an Object"), defaultSourceAppender, runtimeTypeForValue(importArgument)))); + + // If the list of module.imports is not empty and Type(importObject) is not Object, a TypeError is thrown. + if (moduleInformation.imports.size() && !importObject) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("second argument to WebAssembly.Instance must be Object because the WebAssembly.Module has imports"), defaultSourceAppender, runtimeTypeForValue(importArgument)))); + + Identifier moduleKey = Identifier::fromUid(PrivateName(PrivateName::Description, "WebAssemblyInstance")); + WebAssemblyModuleRecord* moduleRecord = WebAssemblyModuleRecord::create(exec, vm, globalObject->webAssemblyModuleRecordStructure(), moduleKey, moduleInformation); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + Structure* instanceStructure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), globalObject->WebAssemblyInstanceStructure()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec)); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + { + // Always start with a dummy Memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers. + Wasm::Memory memory; + instance->setMemory(vm, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory))); + } + + // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively. + // Let imports be an initially-empty list of external values. + unsigned numImportFunctions = 0; + unsigned numImportGlobals = 0; + + bool hasMemoryImport = false; + bool hasTableImport = false; + // For each import i in module.imports: + for (auto& import : moduleInformation.imports) { + // 1. Let o be the resultant value of performing Get(importObject, i.module_name). + JSValue importModuleValue = importObject->get(exec, import.module); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + // 2. If Type(o) is not Object, throw a TypeError. + if (!importModuleValue.isObject()) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("import must be an object"), defaultSourceAppender, runtimeTypeForValue(importModuleValue)))); + + // 3. Let v be the value of performing Get(o, i.item_name) + JSObject* object = jsCast<JSObject*>(importModuleValue); + JSValue value = object->get(exec, import.field); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + switch (import.kind) { + case Wasm::ExternalKind::Function: { + // 4. If i is a function import: + // i. If IsCallable(v) is false, throw a WebAssembly.LinkError. + if (!value.isFunction()) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("import function must be callable")))); + JSCell* cell = value.asCell(); + // ii. If v is an Exported Function Exotic Object: + if (WebAssemblyFunction* importedExport = jsDynamicCast<WebAssemblyFunction*>(vm, cell)) { + // a. If the signature of v does not match the signature of i, throw a WebAssembly.LinkError. + Wasm::SignatureIndex importedSignatureIndex = importedExport->signatureIndex(); + Wasm::SignatureIndex expectedSignatureIndex = moduleInformation.importFunctionSignatureIndices[import.kindIndex]; + if (importedSignatureIndex != expectedSignatureIndex) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported function's signature doesn't match the provided WebAssembly function's signature")))); + // b. Let closure be v.[[Closure]]. + } + // iii. Otherwise: + // 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. + // Note: done as part of Plan compilation. + // iv. Append v to funcs. + // 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). + instance->setImportFunction(vm, cell, numImportFunctions++); + // v. Append closure to imports. + break; + } + case Wasm::ExternalKind::Table: { + RELEASE_ASSERT(!hasTableImport); // This should be guaranteed by a validation failure. + // 7. Otherwise (i is a table import): + hasTableImport = true; + JSWebAssemblyTable* table = jsDynamicCast<JSWebAssemblyTable*>(vm, value); + // i. If v is not a WebAssembly.Table object, throw a WebAssembly.LinkError. + if (!table) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import is not an instance of WebAssembly.Table")))); + + uint32_t expectedInitial = moduleInformation.tableInformation.initial(); + uint32_t actualInitial = table->size(); + if (actualInitial < expectedInitial) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import provided an 'initial' that is too small")))); + + if (std::optional<uint32_t> expectedMaximum = moduleInformation.tableInformation.maximum()) { + std::optional<uint32_t> actualMaximum = table->maximum(); + if (!actualMaximum) { + return JSValue::encode( + throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Table import does not have a 'maximum' but the module requires that it does")))); + } + if (*actualMaximum > *expectedMaximum) { + return JSValue::encode( + throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Imported Table's 'maximum' is larger than the module's expected 'maximum'")))); + } + } + + // ii. Append v to tables. + // iii. Append v.[[Table]] to imports. + instance->setTable(vm, table); + break; + } + case Wasm::ExternalKind::Memory: { + // 6. If i is a memory import: + RELEASE_ASSERT(!hasMemoryImport); // This should be guaranteed by a validation failure. + RELEASE_ASSERT(moduleInformation.memory); + hasMemoryImport = true; + JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value); + // i. If v is not a WebAssembly.Memory object, throw a WebAssembly.LinkError. + if (!memory) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory")))); + + Wasm::PageCount expectedInitial = moduleInformation.memory.initial(); + Wasm::PageCount actualInitial = memory->memory()->initial(); + if (actualInitial < expectedInitial) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided an 'initial' that is too small")))); + + if (Wasm::PageCount expectedMaximum = moduleInformation.memory.maximum()) { + Wasm::PageCount actualMaximum = memory->memory()->maximum(); + if (!actualMaximum) { + return JSValue::encode( + throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does")))); + } + + if (actualMaximum > expectedMaximum) { + return JSValue::encode( + throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory imports 'maximum' is larger than the module's expected 'maximum'")))); + } + } + // ii. Append v to memories. + // iii. Append v.[[Memory]] to imports. + instance->setMemory(vm, memory); + break; + } + case Wasm::ExternalKind::Global: { + // 5. If i is a global import: + // i. If i is not an immutable global, throw a TypeError. + ASSERT(moduleInformation.globals[import.kindIndex].mutability == Wasm::Global::Immutable); + // ii. If Type(v) is not Number, throw a TypeError. + if (!value.isNumber()) + return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("imported global must be a number")))); + // iii. Append ToWebAssemblyValue(v) to imports. + switch (moduleInformation.globals[import.kindIndex].type) { + case Wasm::I32: + instance->setGlobal(numImportGlobals++, value.toInt32(exec)); + break; + case Wasm::F32: + instance->setGlobal(numImportGlobals++, bitwise_cast<uint32_t>(value.toFloat(exec))); + break; + case Wasm::F64: + instance->setGlobal(numImportGlobals++, bitwise_cast<uint64_t>(value.asNumber())); + break; + default: + RELEASE_ASSERT_NOT_REACHED(); + } + ASSERT(!throwScope.exception()); + break; + } + } + } + + { + if (!!moduleInformation.memory && moduleInformation.memory.isImport()) { + // We should either have a Memory import or we should have thrown an exception. + RELEASE_ASSERT(hasMemoryImport); + } + + if (moduleInformation.memory && !hasMemoryImport) { + RELEASE_ASSERT(!moduleInformation.memory.isImport()); + // We create a memory when it's a memory definition. + bool failed; + Wasm::Memory memory(moduleInformation.memory.initial(), moduleInformation.memory.maximum(), failed); + if (failed) + return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec))); + instance->setMemory(vm, + JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory))); + } + } + + { + if (!!moduleInformation.tableInformation && moduleInformation.tableInformation.isImport()) { + // We should either have a Table import or we should have thrown an exception. + RELEASE_ASSERT(hasTableImport); + } + + if (!!moduleInformation.tableInformation && !hasTableImport) { + RELEASE_ASSERT(!moduleInformation.tableInformation.isImport()); + // We create a Table when it's a Table definition. + JSWebAssemblyTable* table = JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(), + moduleInformation.tableInformation.initial(), moduleInformation.tableInformation.maximum()); + // We should always be able to allocate a JSWebAssemblyTable we've defined. + // If it's defined to be too large, we should have thrown a validation error. + ASSERT(!throwScope.exception()); + ASSERT(table); + instance->setTable(vm, table); + } + } + + // Globals + { + ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal); + for (size_t globalIndex = numImportGlobals; globalIndex < moduleInformation.globals.size(); ++globalIndex) { + const auto& global = moduleInformation.globals[globalIndex]; + ASSERT(global.initializationType != Wasm::Global::IsImport); + if (global.initializationType == Wasm::Global::FromGlobalImport) { + ASSERT(global.initialBitsOrImportNumber < numImportGlobals); + instance->setGlobal(globalIndex, instance->loadI64Global(global.initialBitsOrImportNumber)); + } else + instance->setGlobal(globalIndex, global.initialBitsOrImportNumber); + } + } + + moduleRecord->link(exec, instance); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + if (verbose) + moduleRecord->dump(); + JSValue startResult = moduleRecord->evaluate(exec); + UNUSED_PARAM(startResult); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + return JSValue::encode(instance); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyInstance(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.Instance")); +} + +WebAssemblyInstanceConstructor* WebAssemblyInstanceConstructor::create(VM& vm, Structure* structure, WebAssemblyInstancePrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyInstanceConstructor>(vm.heap)) WebAssemblyInstanceConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyInstanceConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyInstanceConstructor::finishCreation(VM& vm, WebAssemblyInstancePrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("Instance")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyInstanceConstructor::WebAssemblyInstanceConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyInstanceConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyInstance; + return ConstructType::Host; +} + +CallType WebAssemblyInstanceConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyInstance; + return CallType::Host; +} + +void WebAssemblyInstanceConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<WebAssemblyInstanceConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.h new file mode 100644 index 000000000..2e857cfc4 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyInstancePrototype; + +class WebAssemblyInstanceConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyInstanceConstructor* create(VM&, Structure*, WebAssemblyInstancePrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyInstancePrototype*); + +private: + WebAssemblyInstanceConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.cpp new file mode 100644 index 000000000..4777a61c1 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyInstancePrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" + +#include "WebAssemblyInstancePrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyInstancePrototype::s_info = { "WebAssembly.Instance.prototype", &Base::s_info, &prototypeTableWebAssemblyInstance, CREATE_METHOD_TABLE(WebAssemblyInstancePrototype) }; + +/* Source for WebAssemblyInstancePrototype.lut.h + @begin prototypeTableWebAssemblyInstance + @end + */ + +WebAssemblyInstancePrototype* WebAssemblyInstancePrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyInstancePrototype>(vm.heap)) WebAssemblyInstancePrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyInstancePrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyInstancePrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyInstancePrototype::WebAssemblyInstancePrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.h new file mode 100644 index 000000000..ebdbe985a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyInstancePrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyInstancePrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyInstancePrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyInstancePrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.cpp new file mode 100644 index 000000000..ed0743bde --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyLinkErrorConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyLinkError.h" +#include "WebAssemblyLinkErrorPrototype.h" + +#include "WebAssemblyLinkErrorConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyLinkErrorConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyLinkError, CREATE_METHOD_TABLE(WebAssemblyLinkErrorConstructor) }; + +/* Source for WebAssemblyLinkErrorConstructor.lut.h + @begin constructorTableWebAssemblyLinkError + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyLinkError(ExecState* state) +{ + auto& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue message = state->argument(0); + auto* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), asInternalFunction(state->jsCallee())->globalObject()->WebAssemblyLinkErrorStructure()); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(JSWebAssemblyLinkError::create(state, vm, structure, message)); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyLinkError(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.LinkError")); +} + +WebAssemblyLinkErrorConstructor* WebAssemblyLinkErrorConstructor::create(VM& vm, Structure* structure, WebAssemblyLinkErrorPrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyLinkErrorConstructor>(vm.heap)) WebAssemblyLinkErrorConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyLinkErrorConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyLinkErrorConstructor::finishCreation(VM& vm, WebAssemblyLinkErrorPrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("LinkError")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyLinkErrorConstructor::WebAssemblyLinkErrorConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyLinkErrorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyLinkError; + return ConstructType::Host; +} + +CallType WebAssemblyLinkErrorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyLinkError; + return CallType::Host; +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.h new file mode 100644 index 000000000..c700c6330 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyLinkErrorPrototype; + +class WebAssemblyLinkErrorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyLinkErrorConstructor* create(VM&, Structure*, WebAssemblyLinkErrorPrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyLinkErrorPrototype*); + +private: + WebAssemblyLinkErrorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.cpp new file mode 100644 index 000000000..6860624f5 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyLinkErrorPrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" + +#include "WebAssemblyLinkErrorPrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyLinkErrorPrototype::s_info = { "WebAssembly.LinkError.prototype", &Base::s_info, &prototypeTableWebAssemblyLinkError, CREATE_METHOD_TABLE(WebAssemblyLinkErrorPrototype) }; + +/* Source for WebAssemblyLinkErrorPrototype.lut.h + @begin prototypeTableWebAssemblyLinkError + @end + */ + +WebAssemblyLinkErrorPrototype* WebAssemblyLinkErrorPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyLinkErrorPrototype>(vm.heap)) WebAssemblyLinkErrorPrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyLinkErrorPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyLinkErrorPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyLinkErrorPrototype::WebAssemblyLinkErrorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.h new file mode 100644 index 000000000..aa546c2f1 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyLinkErrorPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyLinkErrorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyLinkErrorPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyLinkErrorPrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp new file mode 100644 index 000000000..6c887af59 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyMemoryConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyHelpers.h" +#include "JSWebAssemblyMemory.h" +#include "WasmMemory.h" +#include "WasmPageCount.h" +#include "WebAssemblyMemoryPrototype.h" +#include <wtf/Optional.h> + +#include "WebAssemblyMemoryConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyMemoryConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyMemory, CREATE_METHOD_TABLE(WebAssemblyMemoryConstructor) }; + +/* Source for WebAssemblyMemoryConstructor.lut.h + @begin constructorTableWebAssemblyMemory + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (exec->argumentCount() != 1) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Memory expects exactly one argument")))); + + JSObject* memoryDescriptor; + { + JSValue argument = exec->argument(0); + if (!argument.isObject()) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Memory expects its first argument to be an object")))); + memoryDescriptor = jsCast<JSObject*>(argument); + } + + Wasm::PageCount initialPageCount; + { + Identifier initial = Identifier::fromString(&vm, "initial"); + JSValue minSizeValue = memoryDescriptor->get(exec, initial); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + uint32_t size = toNonWrappingUint32(exec, minSizeValue); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (!Wasm::PageCount::isValid(size)) + return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'initial' page count is too large")))); + initialPageCount = Wasm::PageCount(size); + } + + Wasm::PageCount maximumPageCount; + { + Identifier maximum = Identifier::fromString(&vm, "maximum"); + bool hasProperty = memoryDescriptor->hasProperty(exec, maximum); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (hasProperty) { + JSValue maxSizeValue = memoryDescriptor->get(exec, maximum); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + uint32_t size = toNonWrappingUint32(exec, maxSizeValue); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (!Wasm::PageCount::isValid(size)) + return JSValue::encode(throwException(exec, throwScope, createRangeError(exec, ASCIILiteral("WebAssembly.Memory 'maximum' page count is too large")))); + maximumPageCount = Wasm::PageCount(size); + + if (initialPageCount > maximumPageCount) { + return JSValue::encode(throwException(exec, throwScope, + createRangeError(exec, ASCIILiteral("'maximum' page count must be than greater than or equal to the 'initial' page count")))); + } + } + } + + bool failed; + Wasm::Memory memory(initialPageCount, maximumPageCount, failed); + if (failed) + return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec))); + + return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory))); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyMemory(ExecState* state) +{ + VM& vm = state->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, throwScope, "WebAssembly.Memory")); +} + +WebAssemblyMemoryConstructor* WebAssemblyMemoryConstructor::create(VM& vm, Structure* structure, WebAssemblyMemoryPrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyMemoryConstructor>(vm.heap)) WebAssemblyMemoryConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyMemoryConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyMemoryConstructor::finishCreation(VM& vm, WebAssemblyMemoryPrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("Memory")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyMemoryConstructor::WebAssemblyMemoryConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyMemoryConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyMemory; + return ConstructType::Host; +} + +CallType WebAssemblyMemoryConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyMemory; + return CallType::Host; +} + +void WebAssemblyMemoryConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<WebAssemblyMemoryConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.h new file mode 100644 index 000000000..466069335 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyMemoryPrototype; + +class WebAssemblyMemoryConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyMemoryConstructor* create(VM&, Structure*, WebAssemblyMemoryPrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyMemoryPrototype*); + +private: + WebAssemblyMemoryConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.cpp new file mode 100644 index 000000000..8d361aea6 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyMemoryPrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSArrayBuffer.h" +#include "JSCInlines.h" +#include "JSWebAssemblyMemory.h" +#include "JSWebAssemblyHelpers.h" + +namespace JSC { +static EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncGrow(ExecState*); +} + +#include "WebAssemblyMemoryPrototype.lut.h" + + +namespace JSC { + +const ClassInfo WebAssemblyMemoryPrototype::s_info = { "WebAssembly.Memory.prototype", &Base::s_info, &prototypeTableWebAssemblyMemory, CREATE_METHOD_TABLE(WebAssemblyMemoryPrototype) }; + +/* Source for WebAssemblyMemoryPrototype.lut.h +@begin prototypeTableWebAssemblyMemory + grow webAssemblyMemoryProtoFuncGrow DontEnum|Function 1 +@end +*/ + +EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState*); + +ALWAYS_INLINE JSWebAssemblyMemory* getMemory(ExecState* exec, JSValue value) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyMemory* memory = jsDynamicCast<JSWebAssemblyMemory*>(vm, value); + if (!memory) { + throwException(exec, throwScope, + createTypeError(exec, ASCIILiteral("WebAssembly.Memory.prototype.buffer getter called with non WebAssembly.Memory |this| value"))); + return nullptr; + } + return memory; +} + +EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncBuffer(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyMemory* memory = getMemory(exec, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, { }); + return JSValue::encode(memory->buffer(exec->vm(), exec->lexicalGlobalObject())); +} + +EncodedJSValue JSC_HOST_CALL webAssemblyMemoryProtoFuncGrow(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyMemory* memory = getMemory(exec, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, { }); + + uint32_t delta = toNonWrappingUint32(exec, exec->argument(0)); + RETURN_IF_EXCEPTION(throwScope, { }); + + bool shouldThrowExceptionsOnFailure = true; + Wasm::PageCount result = memory->grow(exec, delta, shouldThrowExceptionsOnFailure); + RETURN_IF_EXCEPTION(throwScope, { }); + + return JSValue::encode(jsNumber(result.pageCount())); +} + +WebAssemblyMemoryPrototype* WebAssemblyMemoryPrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyMemoryPrototype>(vm.heap)) WebAssemblyMemoryPrototype(vm, structure); + object->finishCreation(vm, globalObject); + return object; +} + +Structure* WebAssemblyMemoryPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyMemoryPrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + JSC_NATIVE_GETTER("buffer", webAssemblyMemoryProtoFuncBuffer, DontEnum | Accessor); +} + +WebAssemblyMemoryPrototype::WebAssemblyMemoryPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.h new file mode 100644 index 000000000..f061fcdec --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyMemoryPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyMemoryPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyMemoryPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + WebAssemblyMemoryPrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp new file mode 100644 index 000000000..81295923c --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyModuleConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "ExceptionHelpers.h" +#include "FunctionPrototype.h" +#include "JSArrayBuffer.h" +#include "JSCInlines.h" +#include "JSTypedArrays.h" +#include "JSWebAssemblyCallee.h" +#include "JSWebAssemblyCompileError.h" +#include "JSWebAssemblyHelpers.h" +#include "JSWebAssemblyModule.h" +#include "SymbolTable.h" +#include "WasmPlan.h" +#include "WebAssemblyModulePrototype.h" +#include <wtf/StdLibExtras.h> + +#include "WebAssemblyModuleConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyModuleConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyModule, CREATE_METHOD_TABLE(WebAssemblyModuleConstructor) }; + +/* Source for WebAssemblyModuleConstructor.lut.h + @begin constructorTableWebAssemblyModule + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyModule(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + auto* structure = InternalFunction::createSubclassStructure(exec, exec->newTarget(), exec->lexicalGlobalObject()->WebAssemblyModuleStructure()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + throwScope.release(); + return JSValue::encode(WebAssemblyModuleConstructor::createModule(exec, structure)); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyModule(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.Module")); +} + +JSValue WebAssemblyModuleConstructor::createModule(ExecState* state, Structure* structure) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + size_t byteOffset; + size_t byteSize; + uint8_t* base = getWasmBufferFromValue(state, state->argument(0), byteOffset, byteSize); + RETURN_IF_EXCEPTION(scope, { }); + + Wasm::Plan plan(&vm, base + byteOffset, byteSize); + // On failure, a new WebAssembly.CompileError is thrown. + plan.run(); + if (plan.failed()) + return throwException(state, scope, createJSWebAssemblyCompileError(state, vm, plan.errorMessage())); + + // On success, a new WebAssembly.Module object is returned with [[Module]] set to the validated Ast.module. + + // The export symbol table is the same for all Instances of a Module. + SymbolTable* exportSymbolTable = SymbolTable::create(vm); + for (auto& exp : plan.exports()) { + auto offset = exportSymbolTable->takeNextScopeOffset(NoLockingNecessary); + exportSymbolTable->set(NoLockingNecessary, exp.field.impl(), SymbolTableEntry(VarOffset(offset))); + } + + // Only wasm-internal functions have a callee, stubs to JS do not. + unsigned calleeCount = plan.internalFunctionCount(); + JSWebAssemblyModule* result = JSWebAssemblyModule::create(vm, structure, plan.takeModuleInformation(), plan.takeCallLinkInfos(), plan.takeWasmExitStubs(), exportSymbolTable, calleeCount); + plan.initializeCallees(state->jsCallee()->globalObject(), + [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) { + result->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee); + result->setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee); + }); + + return result; +} + +WebAssemblyModuleConstructor* WebAssemblyModuleConstructor::create(VM& vm, Structure* structure, WebAssemblyModulePrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyModuleConstructor>(vm.heap)) WebAssemblyModuleConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyModuleConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyModuleConstructor::finishCreation(VM& vm, WebAssemblyModulePrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("Module")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyModuleConstructor::WebAssemblyModuleConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyModuleConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyModule; + return ConstructType::Host; +} + +CallType WebAssemblyModuleConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyModule; + return CallType::Host; +} + +void WebAssemblyModuleConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<WebAssemblyModuleConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.h new file mode 100644 index 000000000..cf21addd1 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyModulePrototype; + +class WebAssemblyModuleConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyModuleConstructor* create(VM&, Structure*, WebAssemblyModulePrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + + static JSValue createModule(ExecState*, Structure*); + +protected: + void finishCreation(VM&, WebAssemblyModulePrototype*); + +private: + WebAssemblyModuleConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.cpp new file mode 100644 index 000000000..5d3c645fa --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyModulePrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "ArrayBuffer.h" +#include "FunctionPrototype.h" +#include "JSArrayBuffer.h" +#include "JSCInlines.h" +#include "JSWebAssemblyModule.h" + +namespace JSC { +static EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState*); +} + +#include "WebAssemblyModulePrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyModulePrototype::s_info = { "WebAssembly.Module.prototype", &Base::s_info, &prototypeTableWebAssemblyModule, CREATE_METHOD_TABLE(WebAssemblyModulePrototype) }; + +/* Source for WebAssemblyModulePrototype.lut.h + @begin prototypeTableWebAssemblyModule + customSections webAssemblyModuleProtoCustomSections DontEnum|Function 1 + @end + */ + +EncodedJSValue JSC_HOST_CALL webAssemblyModuleProtoCustomSections(ExecState* exec) +{ + VM& vm = exec->vm(); + auto* globalObject = exec->lexicalGlobalObject(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyModule* module = jsDynamicCast<JSWebAssemblyModule*>(vm, exec->thisValue()); + if (!module) + throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Module.prototype.customSections called with non WebAssembly.Module |this| value"))); + RETURN_IF_EXCEPTION(throwScope, { }); + + const String sectionNameString = exec->argument(0).getString(exec); + RETURN_IF_EXCEPTION(throwScope, { }); + + JSArray* result = constructEmptyArray(exec, nullptr, globalObject); + RETURN_IF_EXCEPTION(throwScope, { }); + + const auto& customSections = module->moduleInformation().customSections; + for (const Wasm::CustomSection& section : customSections) { + if (section.name == sectionNameString) { + auto buffer = ArrayBuffer::tryCreate(section.payload.data(), section.payload.size()); + if (!buffer) + throwException(exec, throwScope, createOutOfMemoryError(exec)); + + Structure* arrayBufferStructure = InternalFunction::createSubclassStructure(exec, JSValue(), globalObject->arrayBufferStructure(ArrayBufferSharingMode::Default)); + RETURN_IF_EXCEPTION(throwScope, { }); + + result->push(exec, JSArrayBuffer::create(vm, arrayBufferStructure, WTFMove(buffer))); + RETURN_IF_EXCEPTION(throwScope, { }); + } + } + + return JSValue::encode(result); +} + +WebAssemblyModulePrototype* WebAssemblyModulePrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyModulePrototype>(vm.heap)) WebAssemblyModulePrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyModulePrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyModulePrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyModulePrototype::WebAssemblyModulePrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.h new file mode 100644 index 000000000..d5b6279b3 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModulePrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyModulePrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyModulePrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyModulePrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp new file mode 100644 index 000000000..f17f29df9 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyModuleRecord.h" + +#if ENABLE(WEBASSEMBLY) + +#include "Error.h" +#include "JSCInlines.h" +#include "JSLexicalEnvironment.h" +#include "JSModuleEnvironment.h" +#include "JSWebAssemblyInstance.h" +#include "JSWebAssemblyLinkError.h" +#include "JSWebAssemblyModule.h" +#include "ProtoCallFrame.h" +#include "WasmFormat.h" +#include "WasmSignature.h" +#include "WebAssemblyFunction.h" +#include <limits> + +namespace JSC { + +const ClassInfo WebAssemblyModuleRecord::s_info = { "WebAssemblyModuleRecord", &Base::s_info, nullptr, CREATE_METHOD_TABLE(WebAssemblyModuleRecord) }; + +Structure* WebAssemblyModuleRecord::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +WebAssemblyModuleRecord* WebAssemblyModuleRecord::create(ExecState* exec, VM& vm, Structure* structure, const Identifier& moduleKey, const Wasm::ModuleInformation& moduleInformation) +{ + WebAssemblyModuleRecord* instance = new (NotNull, allocateCell<WebAssemblyModuleRecord>(vm.heap)) WebAssemblyModuleRecord(vm, structure, moduleKey); + instance->finishCreation(exec, vm, moduleInformation); + return instance; +} + +WebAssemblyModuleRecord::WebAssemblyModuleRecord(VM& vm, Structure* structure, const Identifier& moduleKey) + : Base(vm, structure, moduleKey) +{ +} + +void WebAssemblyModuleRecord::destroy(JSCell* cell) +{ + WebAssemblyModuleRecord* thisObject = static_cast<WebAssemblyModuleRecord*>(cell); + thisObject->WebAssemblyModuleRecord::~WebAssemblyModuleRecord(); +} + +void WebAssemblyModuleRecord::finishCreation(ExecState* exec, VM& vm, const Wasm::ModuleInformation& moduleInformation) +{ + Base::finishCreation(exec, vm); + ASSERT(inherits(vm, info())); + for (const auto& exp : moduleInformation.exports) + addExportEntry(ExportEntry::createLocal(exp.field, exp.field)); +} + +void WebAssemblyModuleRecord::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + WebAssemblyModuleRecord* thisObject = jsCast<WebAssemblyModuleRecord*>(cell); + Base::visitChildren(thisObject, visitor); + visitor.append(thisObject->m_instance); + visitor.append(thisObject->m_startFunction); +} + +void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* instance) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + UNUSED_PARAM(scope); + auto* globalObject = state->lexicalGlobalObject(); + + JSWebAssemblyModule* module = instance->module(); + const Wasm::ModuleInformation& moduleInformation = module->moduleInformation(); + + SymbolTable* exportSymbolTable = module->exportSymbolTable(); + unsigned functionImportCount = module->functionImportCount(); + + // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118 + + // Let exports be a list of (string, JS value) pairs that is mapped from each external value e in instance.exports as follows: + JSModuleEnvironment* moduleEnvironment = JSModuleEnvironment::create(vm, globalObject, nullptr, exportSymbolTable, JSValue(), this); + for (const auto& exp : moduleInformation.exports) { + JSValue exportedValue; + switch (exp.kind) { + case Wasm::ExternalKind::Function: { + // 1. If e is a closure c: + // i. If there is an Exported Function Exotic Object func in funcs whose func.[[Closure]] equals c, then return func. + // 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.) + if (exp.kindIndex < functionImportCount) { + // FIXME Implement re-exporting an import. https://bugs.webkit.org/show_bug.cgi?id=165510 + RELEASE_ASSERT_NOT_REACHED(); + } + // iii. Otherwise: + // a. Let func be an Exported Function Exotic Object created from c. + // b. Append func to funcs. + // c. Return func. + JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex); + JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex); + Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex); + const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex); + WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex); + exportedValue = function; + break; + } + case Wasm::ExternalKind::Table: { + // This should be guaranteed by module verification. + RELEASE_ASSERT(instance->table()); + ASSERT(exp.kindIndex == 0); + + exportedValue = instance->table(); + break; + } + case Wasm::ExternalKind::Memory: { + ASSERT(exp.kindIndex == 0); + + exportedValue = instance->memory(); + break; + } + case Wasm::ExternalKind::Global: { + // Assert: the global is immutable by MVP validation constraint. + const Wasm::Global& global = moduleInformation.globals[exp.kindIndex]; + ASSERT(global.mutability == Wasm::Global::Immutable); + // Return ToJSValue(v). + switch (global.type) { + case Wasm::I32: + exportedValue = JSValue(instance->loadI32Global(exp.kindIndex)); + break; + + case Wasm::F32: + exportedValue = JSValue(instance->loadF32Global(exp.kindIndex)); + break; + + case Wasm::F64: + exportedValue = JSValue(instance->loadF64Global(exp.kindIndex)); + break; + + default: + RELEASE_ASSERT_NOT_REACHED(); + } + break; + } + } + + bool shouldThrowReadOnlyError = false; + bool ignoreReadOnlyErrors = true; + bool putResult = false; + symbolTablePutTouchWatchpointSet(moduleEnvironment, state, exp.field, exportedValue, shouldThrowReadOnlyError, ignoreReadOnlyErrors, putResult); + RELEASE_ASSERT(putResult); + } + + bool hasStart = !!moduleInformation.startFunctionIndexSpace; + if (hasStart) { + auto startFunctionIndexSpace = moduleInformation.startFunctionIndexSpace.value_or(0); + Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(startFunctionIndexSpace); + const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex); + // The start function must not take any arguments or return anything. This is enforced by the parser. + ASSERT(!signature->argumentCount()); + ASSERT(signature->returnType() == Wasm::Void); + if (startFunctionIndexSpace < module->functionImportCount()) { + JSCell* startFunction = instance->importFunction(startFunctionIndexSpace)->get(); + m_startFunction.set(vm, this, startFunction); + } else { + JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace); + JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace); + WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), "start", instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex); + m_startFunction.set(vm, this, function); + } + } + + RELEASE_ASSERT(!m_instance); + m_instance.set(vm, this, instance); + m_moduleEnvironment.set(vm, this, moduleEnvironment); +} + +template <typename Scope, typename M, typename N, typename ...Args> +NEVER_INLINE static JSValue dataSegmentFail(ExecState* state, VM& vm, Scope& scope, M memorySize, N segmentSize, N offset, Args... args) +{ + return throwException(state, scope, createJSWebAssemblyLinkError(state, 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...))); +} + +JSValue WebAssemblyModuleRecord::evaluate(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + { + JSWebAssemblyModule* module = m_instance->module(); + const Wasm::ModuleInformation& moduleInformation = module->moduleInformation(); + JSWebAssemblyTable* table = m_instance->table(); + for (const Wasm::Element& element : moduleInformation.elements) { + // It should be a validation error to have any elements without a table. + // Also, it could be that a table wasn't imported, or that the table + // imported wasn't compatible. However, those should error out before + // getting here. + ASSERT(!!table); + if (!element.functionIndices.size()) + continue; + + uint32_t tableIndex = element.offset; + uint64_t lastWrittenIndex = static_cast<uint64_t>(tableIndex) + static_cast<uint64_t>(element.functionIndices.size()) - 1; + if (lastWrittenIndex >= table->size()) + return throwException(state, scope, createJSWebAssemblyLinkError(state, vm, ASCIILiteral("Element is trying to set an out of bounds table index"))); + + for (uint32_t i = 0; i < element.functionIndices.size(); ++i) { + // 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 = element.functionIndices[i]; + if (functionIndex < module->functionImportCount()) { + return JSValue::decode( + throwVMRangeError(state, scope, ASCIILiteral("Element is setting the table value with an import. This is not yet implemented. FIXME."))); + } + + JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(functionIndex); + JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(functionIndex); + Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex); + const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex); + // FIXME: Say we export local function "foo" at funciton 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, m_instance->globalObject(), signature->argumentCount(), String(), m_instance.get(), jsEntrypointCallee, wasmEntrypointCallee, signatureIndex); + + table->setFunction(vm, tableIndex, function); + ++tableIndex; + } + } + } + + { + const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data; + JSWebAssemblyMemory* jsMemory = m_instance->memory(); + if (!data.isEmpty()) { + uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory()); + uint64_t sizeInBytes = jsMemory->memory()->size(); + for (auto& segment : data) { + if (segment->sizeInBytes) { + uint32_t offset; + if (segment->offset.isGlobalImport()) + offset = static_cast<uint32_t>(m_instance->loadI32Global(segment->offset.globalImportIndex())); + else + offset = segment->offset.constValue(); + + if (UNLIKELY(sizeInBytes < segment->sizeInBytes)) + return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment is too big")); + if (UNLIKELY(offset > sizeInBytes - segment->sizeInBytes)) + return dataSegmentFail(state, vm, scope, sizeInBytes, segment->sizeInBytes, offset, ASCIILiteral(", segment writes outside of memory")); + RELEASE_ASSERT(memory); + memcpy(memory + offset, &segment->byte(0), segment->sizeInBytes); + } + } + } + } + + if (JSCell* startFunction = m_startFunction.get()) { + CallData callData; + CallType callType = JSC::getCallData(startFunction, callData); + call(state, startFunction, callType, callData, jsUndefined(), state->emptyList()); + RETURN_IF_EXCEPTION(scope, { }); + } + + return jsUndefined(); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h new file mode 100644 index 000000000..e12667067 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "AbstractModuleRecord.h" +#include "WasmFormat.h" + +namespace JSC { + +class JSWebAssemblyInstance; +class WebAssemblyFunction; + +// Based on the WebAssembly.Instance specification +// https://github.com/WebAssembly/design/blob/master/JS.md#webassemblyinstance-constructor +class WebAssemblyModuleRecord : public AbstractModuleRecord { + friend class LLIntOffsetsExtractor; +public: + typedef AbstractModuleRecord Base; + + DECLARE_EXPORT_INFO; + + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + static WebAssemblyModuleRecord* create(ExecState*, VM&, Structure*, const Identifier&, const Wasm::ModuleInformation&); + + void link(ExecState*, JSWebAssemblyInstance*); + JS_EXPORT_PRIVATE JSValue evaluate(ExecState*); + +private: + WebAssemblyModuleRecord(VM&, Structure*, const Identifier&); + + void finishCreation(ExecState*, VM&, const Wasm::ModuleInformation&); + static void destroy(JSCell*); + + static void visitChildren(JSCell*, SlotVisitor&); + + WriteBarrier<JSWebAssemblyInstance> m_instance; + WriteBarrier<JSCell> m_startFunction; +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.cpp new file mode 100644 index 000000000..2d4e004ad --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyPrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" + +namespace JSC { + +static EncodedJSValue JSC_HOST_CALL webAssemblyFunctionValidate(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwException(state, scope, createError(state, ASCIILiteral("WebAssembly doesn't yet implement the validate function property")))); +} + +static EncodedJSValue JSC_HOST_CALL webAssemblyFunctionCompile(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwException(state, scope, createError(state, ASCIILiteral("WebAssembly doesn't yet implement the compile function property")))); +} + +} + +#include "WebAssemblyPrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyPrototype::s_info = { "WebAssembly.prototype", &Base::s_info, &prototypeTableWebAssembly, CREATE_METHOD_TABLE(WebAssemblyPrototype) }; + +/* Source for WebAssemblyPrototype.lut.h + @begin prototypeTableWebAssembly + validate webAssemblyFunctionValidate DontEnum|Function 1 + compile webAssemblyFunctionCompile DontEnum|Function 1 + @end + */ + +WebAssemblyPrototype* WebAssemblyPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyPrototype>(vm.heap)) WebAssemblyPrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyPrototype::WebAssemblyPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.h new file mode 100644 index 000000000..287d5a51b --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyPrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.cpp new file mode 100644 index 000000000..04aaff30a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyRuntimeErrorConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyRuntimeError.h" +#include "WebAssemblyRuntimeErrorPrototype.h" + +#include "WebAssemblyRuntimeErrorConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyRuntimeErrorConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyRuntimeError, CREATE_METHOD_TABLE(WebAssemblyRuntimeErrorConstructor) }; + +/* Source for WebAssemblyRuntimeErrorConstructor.lut.h + @begin constructorTableWebAssemblyRuntimeError + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyRuntimeError(ExecState* state) +{ + auto& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + JSValue message = state->argument(0); + auto* structure = InternalFunction::createSubclassStructure(state, state->newTarget(), asInternalFunction(state->jsCallee())->globalObject()->WebAssemblyRuntimeErrorStructure()); + RETURN_IF_EXCEPTION(scope, encodedJSValue()); + return JSValue::encode(JSWebAssemblyRuntimeError::create(state, vm, structure, message)); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyRuntimeError(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.RuntimeError")); +} + +WebAssemblyRuntimeErrorConstructor* WebAssemblyRuntimeErrorConstructor::create(VM& vm, Structure* structure, WebAssemblyRuntimeErrorPrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyRuntimeErrorConstructor>(vm.heap)) WebAssemblyRuntimeErrorConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyRuntimeErrorConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyRuntimeErrorConstructor::finishCreation(VM& vm, WebAssemblyRuntimeErrorPrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("RuntimeError")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, ReadOnly | DontEnum | DontDelete); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyRuntimeErrorConstructor::WebAssemblyRuntimeErrorConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyRuntimeErrorConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyRuntimeError; + return ConstructType::Host; +} + +CallType WebAssemblyRuntimeErrorConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyRuntimeError; + return CallType::Host; +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.h new file mode 100644 index 000000000..77fb6e45a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorConstructor.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyRuntimeErrorPrototype; + +class WebAssemblyRuntimeErrorConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyRuntimeErrorConstructor* create(VM&, Structure*, WebAssemblyRuntimeErrorPrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyRuntimeErrorPrototype*); + +private: + WebAssemblyRuntimeErrorConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.cpp new file mode 100644 index 000000000..b374dbd52 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.cpp @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyRuntimeErrorPrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" + +#include "WebAssemblyRuntimeErrorPrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyRuntimeErrorPrototype::s_info = { "WebAssembly.RuntimeError.prototype", &Base::s_info, &prototypeTableWebAssemblyRuntimeError, CREATE_METHOD_TABLE(WebAssemblyRuntimeErrorPrototype) }; + +/* Source for WebAssemblyRuntimeErrorPrototype.lut.h + @begin prototypeTableWebAssemblyRuntimeError + @end + */ + +WebAssemblyRuntimeErrorPrototype* WebAssemblyRuntimeErrorPrototype::create(VM& vm, JSGlobalObject*, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyRuntimeErrorPrototype>(vm.heap)) WebAssemblyRuntimeErrorPrototype(vm, structure); + object->finishCreation(vm); + return object; +} + +Structure* WebAssemblyRuntimeErrorPrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyRuntimeErrorPrototype::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +WebAssemblyRuntimeErrorPrototype::WebAssemblyRuntimeErrorPrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.h new file mode 100644 index 000000000..2077fa92c --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyRuntimeErrorPrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyRuntimeErrorPrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyRuntimeErrorPrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&); + +private: + WebAssemblyRuntimeErrorPrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp new file mode 100644 index 000000000..12d27ab7c --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyTableConstructor.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyHelpers.h" +#include "JSWebAssemblyTable.h" +#include "WebAssemblyTablePrototype.h" + +#include "WebAssemblyTableConstructor.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyTableConstructor::s_info = { "Function", &Base::s_info, &constructorTableWebAssemblyTable, CREATE_METHOD_TABLE(WebAssemblyTableConstructor) }; + +/* Source for WebAssemblyTableConstructor.lut.h + @begin constructorTableWebAssemblyTable + @end + */ + +static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyTable(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSObject* memoryDescriptor; + { + JSValue argument = exec->argument(0); + if (!argument.isObject()) + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its first argument to be an object")))); + memoryDescriptor = jsCast<JSObject*>(argument); + } + + { + Identifier elementIdent = Identifier::fromString(&vm, "element"); + JSValue elementValue = memoryDescriptor->get(exec, elementIdent); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + String elementString = elementValue.toWTFString(exec); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (elementString != "anyfunc") + return JSValue::encode(throwException(exec, throwScope, createTypeError(exec, ASCIILiteral("WebAssembly.Table expects its 'element' field to be the string 'anyfunc'")))); + } + + Identifier initialIdent = Identifier::fromString(&vm, "initial"); + JSValue initialSizeValue = memoryDescriptor->get(exec, initialIdent); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + uint32_t initial = toNonWrappingUint32(exec, initialSizeValue); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + std::optional<uint32_t> maximum; + Identifier maximumIdent = Identifier::fromString(&vm, "maximum"); + bool hasProperty = memoryDescriptor->hasProperty(exec, maximumIdent); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (hasProperty) { + JSValue maxSizeValue = memoryDescriptor->get(exec, maximumIdent); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + maximum = toNonWrappingUint32(exec, maxSizeValue); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + if (initial > *maximum) { + return JSValue::encode(throwException(exec, throwScope, + createRangeError(exec, ASCIILiteral("'maximum' property must be greater than or equal to the 'initial' property")))); + } + } + + throwScope.release(); + return JSValue::encode(JSWebAssemblyTable::create(exec, vm, exec->lexicalGlobalObject()->WebAssemblyTableStructure(), initial, maximum)); +} + +static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyTable(ExecState* state) +{ + VM& vm = state->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + return JSValue::encode(throwConstructorCannotBeCalledAsFunctionTypeError(state, scope, "WebAssembly.Table")); +} + +WebAssemblyTableConstructor* WebAssemblyTableConstructor::create(VM& vm, Structure* structure, WebAssemblyTablePrototype* thisPrototype) +{ + auto* constructor = new (NotNull, allocateCell<WebAssemblyTableConstructor>(vm.heap)) WebAssemblyTableConstructor(vm, structure); + constructor->finishCreation(vm, thisPrototype); + return constructor; +} + +Structure* WebAssemblyTableConstructor::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyTableConstructor::finishCreation(VM& vm, WebAssemblyTablePrototype* prototype) +{ + Base::finishCreation(vm, ASCIILiteral("Table")); + putDirectWithoutTransition(vm, vm.propertyNames->prototype, prototype, DontEnum | DontDelete | ReadOnly); + putDirectWithoutTransition(vm, vm.propertyNames->length, jsNumber(1), ReadOnly | DontEnum | DontDelete); +} + +WebAssemblyTableConstructor::WebAssemblyTableConstructor(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +ConstructType WebAssemblyTableConstructor::getConstructData(JSCell*, ConstructData& constructData) +{ + constructData.native.function = constructJSWebAssemblyTable; + return ConstructType::Host; +} + +CallType WebAssemblyTableConstructor::getCallData(JSCell*, CallData& callData) +{ + callData.native.function = callJSWebAssemblyTable; + return CallType::Host; +} + +void WebAssemblyTableConstructor::visitChildren(JSCell* cell, SlotVisitor& visitor) +{ + auto* thisObject = jsCast<WebAssemblyTableConstructor*>(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, info()); + Base::visitChildren(thisObject, visitor); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) + diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.h b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.h new file mode 100644 index 000000000..a61c7acce --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTableConstructor.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "InternalFunction.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyTablePrototype; + +class WebAssemblyTableConstructor : public InternalFunction { +public: + typedef InternalFunction Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyTableConstructor* create(VM&, Structure*, WebAssemblyTablePrototype*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, WebAssemblyTablePrototype*); + +private: + WebAssemblyTableConstructor(VM&, Structure*); + static ConstructType getConstructData(JSCell*, ConstructData&); + static CallType getCallData(JSCell*, CallData&); + static void visitChildren(JSCell*, SlotVisitor&); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp new file mode 100644 index 000000000..0ca8a601a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyTablePrototype.h" + +#if ENABLE(WEBASSEMBLY) + +#include "FunctionPrototype.h" +#include "JSCInlines.h" +#include "JSWebAssemblyHelpers.h" +#include "JSWebAssemblyTable.h" + +#include "WebAssemblyTablePrototype.lut.h" + +namespace JSC { + +const ClassInfo WebAssemblyTablePrototype::s_info = { "WebAssembly.Table.prototype", &Base::s_info, &prototypeTableWebAssemblyTable, CREATE_METHOD_TABLE(WebAssemblyTablePrototype) }; + +/* Source for WebAssemblyTablePrototype.lut.h + @begin prototypeTableWebAssemblyTable + @end + */ + +static ALWAYS_INLINE JSWebAssemblyTable* getTable(ExecState* exec, VM& vm, JSValue v) +{ + auto throwScope = DECLARE_THROW_SCOPE(vm); + JSWebAssemblyTable* result = jsDynamicCast<JSWebAssemblyTable*>(vm, v); + if (!result) { + throwException(exec, throwScope, + createTypeError(exec, ASCIILiteral("expected |this| value to be an instance of WebAssembly.Table"))); + return nullptr; + } + return result; +} + +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncLength(ExecState*); +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGrow(ExecState*); +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGet(ExecState*); +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState*); + +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncLength(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + return JSValue::encode(jsNumber(table->size())); +} + +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGrow(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + uint32_t index = toNonWrappingUint32(exec, exec->argument(0)); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (!table->grow(index)) { + throwException(exec, throwScope, + createTypeError(exec, ASCIILiteral("WebAssembly.Table.prototype.grow could not grow the table"))); + return { }; + } + + return JSValue::encode(jsUndefined()); +} + +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncGet(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + uint32_t index = toNonWrappingUint32(exec, exec->argument(0)); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + if (index >= table->size()) { + throwException(exec, throwScope, + createRangeError(exec, ASCIILiteral("WebAssembly.Table.prototype.get expects an integer less than the size of the table"))); + return { }; + } + + if (WebAssemblyFunction* result = table->getFunction(index)) + return JSValue::encode(result); + return JSValue::encode(jsNull()); +} + +EncodedJSValue JSC_HOST_CALL webAssemblyTableProtoFuncSet(ExecState* exec) +{ + VM& vm = exec->vm(); + auto throwScope = DECLARE_THROW_SCOPE(vm); + + JSWebAssemblyTable* table = getTable(exec, vm, exec->thisValue()); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + JSValue value = exec->argument(1); + WebAssemblyFunction* function = jsDynamicCast<WebAssemblyFunction*>(vm, value); + if (!value.isNull() && !function) { + throwException(exec, throwScope, + createTypeError(exec, ASCIILiteral("WebAssembly.Table.prototype.set expects the second argument to be null or an instance of WebAssembly.Function"))); + return { }; + } + + uint32_t index = toNonWrappingUint32(exec, exec->argument(0)); + RETURN_IF_EXCEPTION(throwScope, encodedJSValue()); + + if (index >= table->size()) { + throwException(exec, throwScope, + createRangeError(exec, ASCIILiteral("WebAssembly.Table.prototype.set expects an integer less than the size of the table"))); + return { }; + } + + if (value.isNull()) + table->clearFunction(index); + else { + ASSERT(!!function); + table->setFunction(vm, index, function); + } + + return JSValue::encode(jsUndefined()); +} + +WebAssemblyTablePrototype* WebAssemblyTablePrototype::create(VM& vm, JSGlobalObject* globalObject, Structure* structure) +{ + auto* object = new (NotNull, allocateCell<WebAssemblyTablePrototype>(vm.heap)) WebAssemblyTablePrototype(vm, structure); + object->finishCreation(vm, globalObject); + return object; +} + +Structure* WebAssemblyTablePrototype::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info()); +} + +void WebAssemblyTablePrototype::finishCreation(VM& vm, JSGlobalObject* globalObject) +{ + Base::finishCreation(vm); + + JSC_NATIVE_GETTER("length", webAssemblyTableProtoFuncLength, DontEnum | Accessor); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("grow", webAssemblyTableProtoFuncGrow, DontEnum, 1); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("get", webAssemblyTableProtoFuncGet, DontEnum, 1); + JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("set", webAssemblyTableProtoFuncSet, DontEnum, 2); +} + +WebAssemblyTablePrototype::WebAssemblyTablePrototype(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.h b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.h new file mode 100644 index 000000000..5e0450578 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyTablePrototype.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSDestructibleObject.h" +#include "JSObject.h" + +namespace JSC { + +class WebAssemblyTablePrototype : public JSNonFinalObject { +public: + typedef JSNonFinalObject Base; + static const unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable; + + static WebAssemblyTablePrototype* create(VM&, JSGlobalObject*, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_INFO; + +protected: + void finishCreation(VM&, JSGlobalObject*); + +private: + WebAssemblyTablePrototype(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp new file mode 100644 index 000000000..4e891a09a --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2016 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 "WebAssemblyToJSCallee.h" + +#if ENABLE(WEBASSEMBLY) + +#include "JSCInlines.h" + +namespace JSC { + +const ClassInfo WebAssemblyToJSCallee::s_info = { "WebAssemblyToJSCallee", nullptr, 0, CREATE_METHOD_TABLE(WebAssemblyToJSCallee) }; + +WebAssemblyToJSCallee* WebAssemblyToJSCallee::create(VM& vm, Structure* structure) +{ + WebAssemblyToJSCallee* callee = new (NotNull, allocateCell<WebAssemblyToJSCallee>(vm.heap)) WebAssemblyToJSCallee(vm, structure); + callee->finishCreation(vm); + return callee; +} + +Structure* WebAssemblyToJSCallee::createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype) +{ + return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info()); +} + +WebAssemblyToJSCallee::WebAssemblyToJSCallee(VM& vm, Structure* structure) + : Base(vm, structure) +{ +} + +void WebAssemblyToJSCallee::finishCreation(VM& vm) +{ + Base::finishCreation(vm); +} + +void WebAssemblyToJSCallee::destroy(JSCell* cell) +{ + WebAssemblyToJSCallee* thisObject = static_cast<WebAssemblyToJSCallee*>(cell); + thisObject->WebAssemblyToJSCallee::~WebAssemblyToJSCallee(); +} + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h new file mode 100644 index 000000000..496c88576 --- /dev/null +++ b/Source/JavaScriptCore/wasm/js/WebAssemblyToJSCallee.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2016 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. + */ + +#pragma once + +#if ENABLE(WEBASSEMBLY) + +#include "JSCell.h" + +namespace JSC { + +class WebAssemblyToJSCallee : public JSCell { +public: + typedef JSCell Base; + static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal; + + static WebAssemblyToJSCallee* create(VM&, Structure*); + static Structure* createStructure(VM&, JSGlobalObject*, JSValue); + + DECLARE_EXPORT_INFO; + static const bool needsDestruction = true; + static void destroy(JSCell*); + +private: + void finishCreation(VM&); + WebAssemblyToJSCallee(VM&, Structure*); +}; + +} // namespace JSC + +#endif // ENABLE(WEBASSEMBLY) diff --git a/Source/JavaScriptCore/wasm/wasm.json b/Source/JavaScriptCore/wasm/wasm.json new file mode 100644 index 000000000..df3a637fb --- /dev/null +++ b/Source/JavaScriptCore/wasm/wasm.json @@ -0,0 +1,217 @@ +{ + "comments": ["This file describes the WebAssembly ISA.", + "Scripts in this folder auto-generate C++ code for JavaScriptCore as well as the testing DSL which WebKit's WebAssembly tests use." + ], + "preamble": [ + { "name": "magic number", "type": "uint32", "value": 1836278016, "description": "NULL character followed by 'asm'" }, + { "name": "version", "type": "uint32", "value": 13, "description": "Version number, will be reset to 1 for MVP" } + ], + "type" : { + "i32": { "type": "varint7", "value": -1, "b3type": "B3::Int32" }, + "i64": { "type": "varint7", "value": -2, "b3type": "B3::Int64" }, + "f32": { "type": "varint7", "value": -3, "b3type": "B3::Float" }, + "f64": { "type": "varint7", "value": -4, "b3type": "B3::Double" }, + "anyfunc": { "type": "varint7", "value": -16, "b3type": "B3::Void" }, + "func": { "type": "varint7", "value": -32, "b3type": "B3::Void" }, + "void": { "type": "varint7", "value": -64, "b3type": "B3::Void" } + }, + "value_type": ["i32", "i64", "f32", "f64"], + "block_type": ["i32", "i64", "f32", "f64", "void"], + "elem_type": ["anyfunc"], + "external_kind": { + "Function": { "type": "uint8", "value": 0 }, + "Table": { "type": "uint8", "value": 1 }, + "Memory": { "type": "uint8", "value": 2 }, + "Global": { "type": "uint8", "value": 3 } + }, + "section" : { + "Type": { "type": "varuint7", "value": 1, "description": "Function signature declarations" }, + "Import": { "type": "varuint7", "value": 2, "description": "Import declarations" }, + "Function": { "type": "varuint7", "value": 3, "description": "Function declarations" }, + "Table": { "type": "varuint7", "value": 4, "description": "Indirect function table and other tables" }, + "Memory": { "type": "varuint7", "value": 5, "description": "Memory attributes" }, + "Global": { "type": "varuint7", "value": 6, "description": "Global declarations" }, + "Export": { "type": "varuint7", "value": 7, "description": "Exports" }, + "Start": { "type": "varuint7", "value": 8, "description": "Start function declaration" }, + "Element": { "type": "varuint7", "value": 9, "description": "Elements section" }, + "Code": { "type": "varuint7", "value": 10, "description": "Function bodies (code)" }, + "Data": { "type": "varuint7", "value": 11, "description": "Data segments" } + }, + "opcode": { + "unreachable": { "category": "control", "value": 0, "return": [], "parameter": [], "immediate": [], "description": "trap immediately" }, + "block": { "category": "control", "value": 2, "return": ["control"], "parameter": [], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin a sequence of expressions, yielding 0 or 1 values" }, + "loop": { "category": "control", "value": 3, "return": ["control"], "parameter": [], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin a block which can also form control flow loops" }, + "if": { "category": "control", "value": 4, "return": ["control"], "parameter": ["bool"], "immediate": [{"name": "sig", "type": "block_type"}], "description": "begin if expression" }, + "else": { "category": "control", "value": 5, "return": ["control"], "parameter": [], "immediate": [], "description": "begin else expression of if" }, + "select": { "category": "control", "value": 27, "return": ["prev"], "parameter": ["any", "prev", "bool"], "immediate": [], "description": "select one of two values based on condition" }, + "br": { "category": "control", "value": 12, "return": [], "parameter": [], "immediate": [{"name": "relative_depth", "type": "varuint32"}], "description": "break that targets an outer nested block" }, + "br_if": { "category": "control", "value": 13, "return": [], "parameter": [], "immediate": [{"name": "relative_depth", "type": "varuint32"}], "description": "conditional break that targets an outer nested block" }, + "br_table": { "category": "control", "value": 14, "return": [], "parameter": [], "immediate": [{"name": "target_count", "type": "varuint32", "description": "number of entries in the target_table"}, + {"name": "target_table", "type": "varuint32*", "description": "target entries that indicate an outer block or loop to which to break"}, + {"name": "default_target", "type": "varuint32", "description": "an outer block or loop to which to break in the default case"}], + "description": "branch table control flow construct" }, + "return": { "category": "control", "value": 15, "return": [], "parameter": [], "immediate": [], "description": "return zero or one value from this function" }, + "drop": { "category": "control", "value": 26, "return": [], "parameter": ["any"], "immediate": [], "description": "ignore value" }, + "nop": { "category": "control", "value": 1, "return": [], "parameter": [], "immediate": [], "description": "no operation" }, + "end": { "category": "control", "value": 11, "return": [], "parameter": [], "immediate": [], "description": "end a block, loop, or if" }, + "i32.const": { "category": "special", "value": 65, "return": ["i32"], "parameter": [], "immediate": [{"name": "value", "type": "varint32"}], "description": "a constant value interpreted as i32" }, + "i64.const": { "category": "special", "value": 66, "return": ["i64"], "parameter": [], "immediate": [{"name": "value", "type": "varint64"}], "description": "a constant value interpreted as i64" }, + "f64.const": { "category": "special", "value": 68, "return": ["f64"], "parameter": [], "immediate": [{"name": "value", "type": "double"}], "description": "a constant value interpreted as f64" }, + "f32.const": { "category": "special", "value": 67, "return": ["f32"], "parameter": [], "immediate": [{"name": "value", "type": "float"}], "description": "a constant value interpreted as f32" }, + "get_local": { "category": "special", "value": 32, "return": ["any"], "parameter": [], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "read a local variable or parameter" }, + "set_local": { "category": "special", "value": 33, "return": [], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter" }, + "tee_local": { "category": "special", "value": 34, "return": ["any"], "parameter": ["any"], "immediate": [{"name": "local_index", "type": "varuint32"}], "description": "write a local variable or parameter and return the same value" }, + "get_global": { "category": "special", "value": 35, "return": ["any"], "parameter": [], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "read a global variable" }, + "set_global": { "category": "special", "value": 36, "return": [], "parameter": ["any"], "immediate": [{"name": "global_index", "type": "varuint32"}], "description": "write a global variable" }, + "call": { "category": "call", "value": 16, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "function_index", "type": "varuint32"}], "description": "call a function by its index" }, + "call_indirect": { "category": "call", "value": 17, "return": ["call"], "parameter": ["call"], "immediate": [{"name": "type_index", "type": "varuint32"}, {"name": "reserved", "type": "varuint1"}], "description": "call a function indirect with an expected signature" }, + "i32.load8_s": { "category": "memory", "value": 44, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i32.load8_u": { "category": "memory", "value": 45, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i32.load16_s": { "category": "memory", "value": 46, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i32.load16_u": { "category": "memory", "value": 47, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load8_s": { "category": "memory", "value": 48, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load8_u": { "category": "memory", "value": 49, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load16_s": { "category": "memory", "value": 50, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load16_u": { "category": "memory", "value": 51, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load32_s": { "category": "memory", "value": 52, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load32_u": { "category": "memory", "value": 53, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i32.load": { "category": "memory", "value": 40, "return": ["i32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i64.load": { "category": "memory", "value": 41, "return": ["i64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "f32.load": { "category": "memory", "value": 42, "return": ["f32"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "f64.load": { "category": "memory", "value": 43, "return": ["f64"], "parameter": ["addr"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "load from memory" }, + "i32.store8": { "category": "memory", "value": 58, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i32.store16": { "category": "memory", "value": 59, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i64.store8": { "category": "memory", "value": 60, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i64.store16": { "category": "memory", "value": 61, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i64.store32": { "category": "memory", "value": 62, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i32.store": { "category": "memory", "value": 54, "return": [], "parameter": ["addr", "i32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "i64.store": { "category": "memory", "value": 55, "return": [], "parameter": ["addr", "i64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "f32.store": { "category": "memory", "value": 56, "return": [], "parameter": ["addr", "f32"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "f64.store": { "category": "memory", "value": 57, "return": [], "parameter": ["addr", "f64"], "immediate": [{"name": "flags", "type": "varuint32"}, {"name": "offset", "type": "varuint32"}], "description": "store to memory" }, + "current_memory": { "category": "operation", "value": 63, "return": ["size"], "parameter": [], "immediate": [{"name": "flags", "type": "varuint32"}], "description": "query the size of memory" }, + "grow_memory": { "category": "operation", "value": 64, "return": ["size"], "parameter": ["size"], "immediate": [{"name": "flags", "type": "varuint32"}], "description": "grow the size of memory" }, + "i32.add": { "category": "arithmetic", "value": 106, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Add" }, + "i32.sub": { "category": "arithmetic", "value": 107, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Sub" }, + "i32.mul": { "category": "arithmetic", "value": 108, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Mul" }, + "i32.div_s": { "category": "arithmetic", "value": 109, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] }, + "i32.div_u": { "category": "arithmetic", "value": 110, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] }, + "i32.rem_s": { "category": "arithmetic", "value": 111, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] }, + "i32.rem_u": { "category": "arithmetic", "value": 112, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [] }, + "i32.and": { "category": "arithmetic", "value": 113, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitAnd" }, + "i32.or": { "category": "arithmetic", "value": 114, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitOr" }, + "i32.xor": { "category": "arithmetic", "value": 115, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BitXor" }, + "i32.shl": { "category": "arithmetic", "value": 116, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Shl" }, + "i32.shr_u": { "category": "arithmetic", "value": 118, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "ZShr" }, + "i32.shr_s": { "category": "arithmetic", "value": 117, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "SShr" }, + "i32.rotr": { "category": "arithmetic", "value": 120, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "RotR" }, + "i32.rotl": { "category": "arithmetic", "value": 119, "return": ["i32"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "RotL" }, + "i32.eq": { "category": "comparison", "value": 70, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Equal" }, + "i32.ne": { "category": "comparison", "value": 71, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "NotEqual" }, + "i32.lt_s": { "category": "comparison", "value": 72, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "LessThan" }, + "i32.le_s": { "category": "comparison", "value": 76, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "LessEqual" }, + "i32.lt_u": { "category": "comparison", "value": 73, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Below" }, + "i32.le_u": { "category": "comparison", "value": 77, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "BelowEqual" }, + "i32.gt_s": { "category": "comparison", "value": 74, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "GreaterThan" }, + "i32.ge_s": { "category": "comparison", "value": 78, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "GreaterEqual" }, + "i32.gt_u": { "category": "comparison", "value": 75, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "Above" }, + "i32.ge_u": { "category": "comparison", "value": 79, "return": ["bool"], "parameter": ["i32", "i32"], "immediate": [], "b3op": "AboveEqual" }, + "i32.clz": { "category": "arithmetic", "value": 103, "return": ["i32"], "parameter": ["i32"], "immediate": [], "b3op": "Clz" }, + "i32.ctz": { "category": "arithmetic", "value": 104, "return": ["i32"], "parameter": ["i32"], "immediate": [] }, + "i32.popcnt": { "category": "arithmetic", "value": 105, "return": ["i32"], "parameter": ["i32"], "immediate": [] }, + "i32.eqz": { "category": "comparison", "value": 69, "return": ["bool"], "parameter": ["i32"], "immediate": [], "b3op": "Equal(i32(0), @0)" }, + "i64.add": { "category": "arithmetic", "value": 124, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Add" }, + "i64.sub": { "category": "arithmetic", "value": 125, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Sub" }, + "i64.mul": { "category": "arithmetic", "value": 126, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Mul" }, + "i64.div_s": { "category": "arithmetic", "value": 127, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] }, + "i64.div_u": { "category": "arithmetic", "value": 128, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] }, + "i64.rem_s": { "category": "arithmetic", "value": 129, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] }, + "i64.rem_u": { "category": "arithmetic", "value": 130, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [] }, + "i64.and": { "category": "arithmetic", "value": 131, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitAnd" }, + "i64.or": { "category": "arithmetic", "value": 132, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitOr" }, + "i64.xor": { "category": "arithmetic", "value": 133, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BitXor" }, + "i64.shl": { "category": "arithmetic", "value": 134, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Shl(@0, Trunc(@1))" }, + "i64.shr_u": { "category": "arithmetic", "value": 136, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "ZShr(@0, Trunc(@1))" }, + "i64.shr_s": { "category": "arithmetic", "value": 135, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "SShr(@0, Trunc(@1))" }, + "i64.rotr": { "category": "arithmetic", "value": 138, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "RotR(@0, Trunc(@1))" }, + "i64.rotl": { "category": "arithmetic", "value": 137, "return": ["i64"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "RotL(@0, Trunc(@1))" }, + "i64.eq": { "category": "comparison", "value": 81, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Equal" }, + "i64.ne": { "category": "comparison", "value": 82, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "NotEqual" }, + "i64.lt_s": { "category": "comparison", "value": 83, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "LessThan" }, + "i64.le_s": { "category": "comparison", "value": 87, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "LessEqual" }, + "i64.lt_u": { "category": "comparison", "value": 84, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Below" }, + "i64.le_u": { "category": "comparison", "value": 88, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "BelowEqual" }, + "i64.gt_s": { "category": "comparison", "value": 85, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "GreaterThan" }, + "i64.ge_s": { "category": "comparison", "value": 89, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "GreaterEqual" }, + "i64.gt_u": { "category": "comparison", "value": 86, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "Above" }, + "i64.ge_u": { "category": "comparison", "value": 90, "return": ["bool"], "parameter": ["i64", "i64"], "immediate": [], "b3op": "AboveEqual" }, + "i64.clz": { "category": "arithmetic", "value": 121, "return": ["i64"], "parameter": ["i64"], "immediate": [], "b3op": "Clz" }, + "i64.ctz": { "category": "arithmetic", "value": 122, "return": ["i64"], "parameter": ["i64"], "immediate": [] }, + "i64.popcnt": { "category": "arithmetic", "value": 123, "return": ["i64"], "parameter": ["i64"], "immediate": [] }, + "i64.eqz": { "category": "comparison", "value": 80, "return": ["bool"], "parameter": ["i64"], "immediate": [], "b3op": "Equal(i64(0), @0)" }, + "f32.add": { "category": "arithmetic", "value": 146, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Add" }, + "f32.sub": { "category": "arithmetic", "value": 147, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Sub" }, + "f32.mul": { "category": "arithmetic", "value": 148, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Mul" }, + "f32.div": { "category": "arithmetic", "value": 149, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Div" }, + "f32.min": { "category": "arithmetic", "value": 150, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitOr(@0, @1), Select(LessThan(@0, @1), @0, @1))" }, + "f32.max": { "category": "arithmetic", "value": 151, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitAnd(@0, @1), Select(LessThan(@0, @1), @1, @0))" }, + "f32.abs": { "category": "arithmetic", "value": 139, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Abs" }, + "f32.neg": { "category": "arithmetic", "value": 140, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Neg" }, + "f32.copysign": { "category": "arithmetic", "value": 152, "return": ["f32"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "BitwiseCast(BitOr(BitAnd(BitwiseCast(@1), i32(0x80000000)), BitAnd(BitwiseCast(@0), i32(0x7fffffff))))" }, + "f32.ceil": { "category": "arithmetic", "value": 141, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Ceil" }, + "f32.floor": { "category": "arithmetic", "value": 142, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Floor" }, + "f32.trunc": { "category": "arithmetic", "value": 143, "return": ["f32"], "parameter": ["f32"], "immediate": [] }, + "f32.nearest": { "category": "arithmetic", "value": 144, "return": ["f32"], "parameter": ["f32"], "immediate": [] }, + "f32.sqrt": { "category": "arithmetic", "value": 145, "return": ["f32"], "parameter": ["f32"], "immediate": [], "b3op": "Sqrt" }, + "f32.eq": { "category": "comparison", "value": 91, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "Equal" }, + "f32.ne": { "category": "comparison", "value": 92, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "NotEqual" }, + "f32.lt": { "category": "comparison", "value": 93, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "LessThan" }, + "f32.le": { "category": "comparison", "value": 95, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "LessEqual" }, + "f32.gt": { "category": "comparison", "value": 94, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "GreaterThan" }, + "f32.ge": { "category": "comparison", "value": 96, "return": ["bool"], "parameter": ["f32", "f32"], "immediate": [], "b3op": "GreaterEqual" }, + "f64.add": { "category": "arithmetic", "value": 160, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Add" }, + "f64.sub": { "category": "arithmetic", "value": 161, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Sub" }, + "f64.mul": { "category": "arithmetic", "value": 162, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Mul" }, + "f64.div": { "category": "arithmetic", "value": 163, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Div" }, + "f64.min": { "category": "arithmetic", "value": 164, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitOr(@0, @1), Select(LessThan(@0, @1), @0, @1))" }, + "f64.max": { "category": "arithmetic", "value": 165, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Select(Equal(@0, @1), BitAnd(@0, @1), Select(LessThan(@0, @1), @1, @0))" }, + "f64.abs": { "category": "arithmetic", "value": 153, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Abs" }, + "f64.neg": { "category": "arithmetic", "value": 154, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Neg" }, + "f64.copysign": { "category": "arithmetic", "value": 166, "return": ["f64"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "BitwiseCast(BitOr(BitAnd(BitwiseCast(@1), i64(0x8000000000000000)), BitAnd(BitwiseCast(@0), i64(0x7fffffffffffffff))))" }, + "f64.ceil": { "category": "arithmetic", "value": 155, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Ceil" }, + "f64.floor": { "category": "arithmetic", "value": 156, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Floor" }, + "f64.trunc": { "category": "arithmetic", "value": 157, "return": ["f64"], "parameter": ["f64"], "immediate": [] }, + "f64.nearest": { "category": "arithmetic", "value": 158, "return": ["f64"], "parameter": ["f64"], "immediate": [] }, + "f64.sqrt": { "category": "arithmetic", "value": 159, "return": ["f64"], "parameter": ["f64"], "immediate": [], "b3op": "Sqrt" }, + "f64.eq": { "category": "comparison", "value": 97, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "Equal" }, + "f64.ne": { "category": "comparison", "value": 98, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "NotEqual" }, + "f64.lt": { "category": "comparison", "value": 99, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "LessThan" }, + "f64.le": { "category": "comparison", "value": 101, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "LessEqual" }, + "f64.gt": { "category": "comparison", "value": 100, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "GreaterThan" }, + "f64.ge": { "category": "comparison", "value": 102, "return": ["bool"], "parameter": ["f64", "f64"], "immediate": [], "b3op": "GreaterEqual" }, + "i32.trunc_s/f32": { "category": "conversion", "value": 168, "return": ["i32"], "parameter": ["f32"], "immediate": [] }, + "i32.trunc_s/f64": { "category": "conversion", "value": 170, "return": ["i32"], "parameter": ["f64"], "immediate": [] }, + "i32.trunc_u/f32": { "category": "conversion", "value": 169, "return": ["i32"], "parameter": ["f32"], "immediate": [] }, + "i32.trunc_u/f64": { "category": "conversion", "value": 171, "return": ["i32"], "parameter": ["f64"], "immediate": [] }, + "i32.wrap/i64": { "category": "conversion", "value": 167, "return": ["i32"], "parameter": ["i64"], "immediate": [], "b3op": "Trunc" }, + "i64.trunc_s/f32": { "category": "conversion", "value": 174, "return": ["i64"], "parameter": ["f32"], "immediate": [] }, + "i64.trunc_s/f64": { "category": "conversion", "value": 176, "return": ["i64"], "parameter": ["f64"], "immediate": [] }, + "i64.trunc_u/f32": { "category": "conversion", "value": 175, "return": ["i64"], "parameter": ["f32"], "immediate": [] }, + "i64.trunc_u/f64": { "category": "conversion", "value": 177, "return": ["i64"], "parameter": ["f64"], "immediate": [] }, + "i64.extend_s/i32": { "category": "conversion", "value": 172, "return": ["i64"], "parameter": ["i32"], "immediate": [], "b3op": "SExt32" }, + "i64.extend_u/i32": { "category": "conversion", "value": 173, "return": ["i64"], "parameter": ["i32"], "immediate": [], "b3op": "ZExt32" }, + "f32.convert_s/i32": { "category": "conversion", "value": 178, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "IToF" }, + "f32.convert_u/i32": { "category": "conversion", "value": 179, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "IToF(ZExt32(@0))" }, + "f32.convert_s/i64": { "category": "conversion", "value": 180, "return": ["f32"], "parameter": ["i64"], "immediate": [], "b3op": "IToF" }, + "f32.convert_u/i64": { "category": "conversion", "value": 181, "return": ["f32"], "parameter": ["i64"], "immediate": [] }, + "f32.demote/f64": { "category": "conversion", "value": 182, "return": ["f32"], "parameter": ["f64"], "immediate": [], "b3op": "DoubleToFloat"}, + "f32.reinterpret/i32": { "category": "conversion", "value": 190, "return": ["f32"], "parameter": ["i32"], "immediate": [], "b3op": "BitwiseCast" }, + "f64.convert_s/i32": { "category": "conversion", "value": 183, "return": ["f64"], "parameter": ["i32"], "immediate": [], "b3op": "IToD" }, + "f64.convert_u/i32": { "category": "conversion", "value": 184, "return": ["f64"], "parameter": ["i32"], "immediate": [], "b3op": "IToD(ZExt32(@0))" }, + "f64.convert_s/i64": { "category": "conversion", "value": 185, "return": ["f64"], "parameter": ["i64"], "immediate": [], "b3op": "IToD" }, + "f64.convert_u/i64": { "category": "conversion", "value": 186, "return": ["f64"], "parameter": ["i64"], "immediate": [] }, + "f64.promote/f32": { "category": "conversion", "value": 187, "return": ["f64"], "parameter": ["f32"], "immediate": [], "b3op": "FloatToDouble"}, + "f64.reinterpret/i64": { "category": "conversion", "value": 191, "return": ["f64"], "parameter": ["i64"], "immediate": [], "b3op": "BitwiseCast" }, + "i32.reinterpret/f32": { "category": "conversion", "value": 188, "return": ["i32"], "parameter": ["f32"], "immediate": [], "b3op": "BitwiseCast" }, + "i64.reinterpret/f64": { "category": "conversion", "value": 189, "return": ["i64"], "parameter": ["f64"], "immediate": [], "b3op": "BitwiseCast" } + } +} |