blob: 8ce35f483eab71a585cf2de149626e34fb473481 [file] [log] [blame]
/*
* Copyright (C) 2015 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 "JSArrayBuffer.h"
#include "JSCInlines.h"
#include "JSWASMModule.h"
#include "StrongInlines.h"
#include "WASMConstants.h"
#include "WASMFunctionParser.h"
#include <wtf/MathExtras.h>
#define FAIL_WITH_MESSAGE(errorMessage) do { m_errorMessage = errorMessage; return; } while (0)
#define READ_UINT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readUInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_FLOAT_OR_FAIL(result, errorMessage) do { if (!m_reader.readFloat(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_DOUBLE_OR_FAIL(result, errorMessage) do { if (!m_reader.readDouble(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_COMPACT_UINT32_OR_FAIL(result, errorMessage) do { if (!m_reader.readCompactUInt32(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_STRING_OR_FAIL(result, errorMessage) do { if (!m_reader.readString(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_TYPE_OR_FAIL(result, errorMessage) do { if (!m_reader.readType(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_EXPRESSION_TYPE_OR_FAIL(result, errorMessage) do { if (!m_reader.readExpressionType(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define READ_EXPORT_FORMAT_OR_FAIL(result, errorMessage) do { if (!m_reader.readExportFormat(result)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define FAIL_IF_FALSE(condition, errorMessage) do { if (!(condition)) FAIL_WITH_MESSAGE(errorMessage); } while (0)
#define PROPAGATE_ERROR() do { if (!m_errorMessage.isNull()) return; } while (0)
namespace JSC {
WASMModuleParser::WASMModuleParser(VM& vm, JSGlobalObject* globalObject, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer)
: m_vm(vm)
, m_globalObject(vm, globalObject)
, m_source(source)
, m_imports(vm, imports)
, m_reader(static_cast<WebAssemblySourceProvider*>(source.provider())->data())
, m_module(vm, JSWASMModule::create(vm, globalObject->wasmModuleStructure(), arrayBuffer))
{
}
JSWASMModule* WASMModuleParser::parse(ExecState* exec, String& errorMessage)
{
parseModule(exec);
if (!m_errorMessage.isNull()) {
errorMessage = m_errorMessage;
return nullptr;
}
return m_module.get();
}
void WASMModuleParser::parseModule(ExecState* exec)
{
uint32_t magicNumber;
READ_UINT32_OR_FAIL(magicNumber, "Cannot read the magic number.");
FAIL_IF_FALSE(magicNumber == wasmMagicNumber, "The magic number is incorrect.");
uint32_t outputSizeInASMJS;
READ_UINT32_OR_FAIL(outputSizeInASMJS, "Cannot read the output size in asm.js format.");
parseConstantPoolSection();
PROPAGATE_ERROR();
parseSignatureSection();
PROPAGATE_ERROR();
parseFunctionImportSection(exec);
PROPAGATE_ERROR();
parseGlobalSection(exec);
PROPAGATE_ERROR();
parseFunctionDeclarationSection();
PROPAGATE_ERROR();
parseFunctionPointerTableSection();
PROPAGATE_ERROR();
parseFunctionDefinitionSection();
PROPAGATE_ERROR();
parseExportSection();
PROPAGATE_ERROR();
FAIL_IF_FALSE(!m_module->arrayBuffer() || m_module->arrayBuffer()->impl()->byteLength() < (1u << 31), "The ArrayBuffer's length must be less than 2^31.");
}
void WASMModuleParser::parseConstantPoolSection()
{
uint32_t numberOfI32Constants;
uint32_t numberOfF32Constants;
uint32_t numberOfF64Constants;
READ_COMPACT_UINT32_OR_FAIL(numberOfI32Constants, "Cannot read the number of int32 constants.");
READ_COMPACT_UINT32_OR_FAIL(numberOfF32Constants, "Cannot read the number of float32 constants.");
READ_COMPACT_UINT32_OR_FAIL(numberOfF64Constants, "Cannot read the number of float64 constants.");
m_module->i32Constants().reserveInitialCapacity(numberOfI32Constants);
m_module->f32Constants().reserveInitialCapacity(numberOfF32Constants);
m_module->f64Constants().reserveInitialCapacity(numberOfF64Constants);
for (uint32_t i = 0; i < numberOfI32Constants; ++i) {
uint32_t constant;
READ_COMPACT_UINT32_OR_FAIL(constant, "Cannot read an int32 constant.");
m_module->i32Constants().uncheckedAppend(constant);
}
for (uint32_t i = 0; i < numberOfF32Constants; ++i) {
float constant;
READ_FLOAT_OR_FAIL(constant, "Cannot read a float32 constant.");
m_module->f32Constants().uncheckedAppend(constant);
}
for (uint32_t i = 0; i < numberOfF64Constants; ++i) {
double constant;
READ_DOUBLE_OR_FAIL(constant, "Cannot read a float64 constant.");
m_module->f64Constants().uncheckedAppend(constant);
}
}
void WASMModuleParser::parseSignatureSection()
{
uint32_t numberOfSignatures;
READ_COMPACT_UINT32_OR_FAIL(numberOfSignatures, "Cannot read the number of signatures.");
m_module->signatures().reserveInitialCapacity(numberOfSignatures);
for (uint32_t signatureIndex = 0; signatureIndex < numberOfSignatures; ++signatureIndex) {
WASMSignature signature;
READ_EXPRESSION_TYPE_OR_FAIL(signature.returnType, "Cannot read the return type.");
uint32_t argumentCount;
READ_COMPACT_UINT32_OR_FAIL(argumentCount, "Cannot read the number of arguments.");
signature.arguments.reserveInitialCapacity(argumentCount);
for (uint32_t argumentIndex = 0; argumentIndex < argumentCount; ++argumentIndex) {
WASMType type;
READ_TYPE_OR_FAIL(type, "Cannot read the type of an argument.");
signature.arguments.uncheckedAppend(type);
}
m_module->signatures().uncheckedAppend(signature);
}
}
void WASMModuleParser::parseFunctionImportSection(ExecState* exec)
{
uint32_t numberOfFunctionImports;
uint32_t numberOfFunctionImportSignatures;
READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionImports, "Cannot read the number of function imports.");
READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionImportSignatures, "Cannot read the number of function import signatures.");
m_module->functionImports().reserveInitialCapacity(numberOfFunctionImports);
m_module->functionImportSignatures().reserveInitialCapacity(numberOfFunctionImportSignatures);
m_module->importedFunctions().reserveInitialCapacity(numberOfFunctionImports);
for (uint32_t functionImportIndex = 0; functionImportIndex < numberOfFunctionImports; ++functionImportIndex) {
WASMFunctionImport functionImport;
READ_STRING_OR_FAIL(functionImport.functionName, "Cannot read the function import name.");
m_module->functionImports().uncheckedAppend(functionImport);
uint32_t numberOfSignatures;
READ_COMPACT_UINT32_OR_FAIL(numberOfSignatures, "Cannot read the number of signatures.");
FAIL_IF_FALSE(numberOfSignatures <= numberOfFunctionImportSignatures - m_module->functionImportSignatures().size(), "The number of signatures is incorrect.");
for (uint32_t i = 0; i < numberOfSignatures; ++i) {
WASMFunctionImportSignature functionImportSignature;
READ_COMPACT_UINT32_OR_FAIL(functionImportSignature.signatureIndex, "Cannot read the signature index.");
FAIL_IF_FALSE(functionImportSignature.signatureIndex < m_module->signatures().size(), "The signature index is incorrect.");
functionImportSignature.functionImportIndex = functionImportIndex;
m_module->functionImportSignatures().uncheckedAppend(functionImportSignature);
}
JSValue value;
getImportedValue(exec, functionImport.functionName, value);
PROPAGATE_ERROR();
FAIL_IF_FALSE(value.isFunction(), "\"" + functionImport.functionName + "\" is not a function.");
JSFunction* function = jsCast<JSFunction*>(value.asCell());
m_module->importedFunctions().uncheckedAppend(WriteBarrier<JSFunction>(m_vm, m_module.get(), function));
}
FAIL_IF_FALSE(m_module->functionImportSignatures().size() == numberOfFunctionImportSignatures, "The number of function import signatures is incorrect.");
}
void WASMModuleParser::parseGlobalSection(ExecState* exec)
{
uint32_t numberOfInternalI32GlobalVariables;
uint32_t numberOfInternalF32GlobalVariables;
uint32_t numberOfInternalF64GlobalVariables;
uint32_t numberOfImportedI32GlobalVariables;
uint32_t numberOfImportedF32GlobalVariables;
uint32_t numberOfImportedF64GlobalVariables;
READ_COMPACT_UINT32_OR_FAIL(numberOfInternalI32GlobalVariables, "Cannot read the number of internal int32 global variables.");
READ_COMPACT_UINT32_OR_FAIL(numberOfInternalF32GlobalVariables, "Cannot read the number of internal float32 global variables.");
READ_COMPACT_UINT32_OR_FAIL(numberOfInternalF64GlobalVariables, "Cannot read the number of internal float64 global variables.");
READ_COMPACT_UINT32_OR_FAIL(numberOfImportedI32GlobalVariables, "Cannot read the number of imported int32 global variables.");
READ_COMPACT_UINT32_OR_FAIL(numberOfImportedF32GlobalVariables, "Cannot read the number of imported float32 global variables.");
READ_COMPACT_UINT32_OR_FAIL(numberOfImportedF64GlobalVariables, "Cannot read the number of imported float64 global variables.");
uint32_t numberOfGlobalVariables = numberOfInternalI32GlobalVariables + numberOfInternalF32GlobalVariables + numberOfInternalF64GlobalVariables +
numberOfImportedI32GlobalVariables + numberOfImportedF32GlobalVariables + numberOfImportedF64GlobalVariables;
Vector<WASMType>& globalVariableTypes = m_module->globalVariableTypes();
globalVariableTypes.reserveInitialCapacity(numberOfGlobalVariables);
Vector<JSWASMModule::GlobalVariable>& globalVariables = m_module->globalVariables();
globalVariables.reserveInitialCapacity(numberOfGlobalVariables);
for (uint32_t i = 0; i < numberOfInternalI32GlobalVariables; ++i) {
globalVariableTypes.uncheckedAppend(WASMType::I32);
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0));
}
for (uint32_t i = 0; i < numberOfInternalF32GlobalVariables; ++i) {
globalVariableTypes.uncheckedAppend(WASMType::F32);
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0.0f));
}
for (uint32_t i = 0; i < numberOfInternalF64GlobalVariables; ++i) {
globalVariableTypes.uncheckedAppend(WASMType::F64);
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(0.0));
}
for (uint32_t i = 0; i < numberOfImportedI32GlobalVariables; ++i) {
String importName;
READ_STRING_OR_FAIL(importName, "Cannot read the import name of an int32 global variable.");
globalVariableTypes.uncheckedAppend(WASMType::I32);
JSValue value;
getImportedValue(exec, importName, value);
PROPAGATE_ERROR();
FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol.");
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(value.toInt32(exec)));
}
for (uint32_t i = 0; i < numberOfImportedF32GlobalVariables; ++i) {
String importName;
READ_STRING_OR_FAIL(importName, "Cannot read the import name of a float32 global variable.");
globalVariableTypes.uncheckedAppend(WASMType::F32);
JSValue value;
getImportedValue(exec, importName, value);
PROPAGATE_ERROR();
FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol.");
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(static_cast<float>(value.toNumber(exec))));
}
for (uint32_t i = 0; i < numberOfImportedF64GlobalVariables; ++i) {
String importName;
READ_STRING_OR_FAIL(importName, "Cannot read the import name of a float64 global variable.");
globalVariableTypes.uncheckedAppend(WASMType::F64);
JSValue value;
getImportedValue(exec, importName, value);
PROPAGATE_ERROR();
FAIL_IF_FALSE(value.isPrimitive() && !value.isSymbol(), "\"" + importName + "\" is not a primitive or is a Symbol.");
globalVariables.uncheckedAppend(JSWASMModule::GlobalVariable(value.toNumber(exec)));
}
}
void WASMModuleParser::parseFunctionDeclarationSection()
{
uint32_t numberOfFunctionDeclarations;
READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionDeclarations, "Cannot read the number of function declarations.");
m_module->functionDeclarations().reserveInitialCapacity(numberOfFunctionDeclarations);
m_module->functions().reserveInitialCapacity(numberOfFunctionDeclarations);
m_module->functionStartOffsetsInSource().reserveInitialCapacity(numberOfFunctionDeclarations);
m_module->functionStackHeights().reserveInitialCapacity(numberOfFunctionDeclarations);
for (uint32_t i = 0; i < numberOfFunctionDeclarations; ++i) {
WASMFunctionDeclaration functionDeclaration;
READ_COMPACT_UINT32_OR_FAIL(functionDeclaration.signatureIndex, "Cannot read the signature index.");
FAIL_IF_FALSE(functionDeclaration.signatureIndex < m_module->signatures().size(), "The signature index is incorrect.");
m_module->functionDeclarations().uncheckedAppend(functionDeclaration);
}
}
void WASMModuleParser::parseFunctionPointerTableSection()
{
uint32_t numberOfFunctionPointerTables;
READ_COMPACT_UINT32_OR_FAIL(numberOfFunctionPointerTables, "Cannot read the number of function pointer tables.");
m_module->functionPointerTables().reserveInitialCapacity(numberOfFunctionPointerTables);
for (uint32_t i = 0; i < numberOfFunctionPointerTables; ++i) {
WASMFunctionPointerTable functionPointerTable;
READ_COMPACT_UINT32_OR_FAIL(functionPointerTable.signatureIndex, "Cannot read the signature index.");
FAIL_IF_FALSE(functionPointerTable.signatureIndex < m_module->signatures().size(), "The signature index is incorrect.");
uint32_t numberOfFunctions;
READ_COMPACT_UINT32_OR_FAIL(numberOfFunctions, "Cannot read the number of functions of a function pointer table.");
FAIL_IF_FALSE(hasOneBitSet(numberOfFunctions), "The number of functions must be a power of two.");
functionPointerTable.functionIndices.reserveInitialCapacity(numberOfFunctions);
functionPointerTable.functions.reserveInitialCapacity(numberOfFunctions);
for (uint32_t j = 0; j < numberOfFunctions; ++j) {
uint32_t functionIndex;
READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read a function index of a function pointer table.");
FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect.");
FAIL_IF_FALSE(m_module->functionDeclarations()[functionIndex].signatureIndex == functionPointerTable.signatureIndex, "The signature of the function doesn't match that of the function pointer table.");
functionPointerTable.functionIndices.uncheckedAppend(functionIndex);
}
m_module->functionPointerTables().uncheckedAppend(functionPointerTable);
}
}
void WASMModuleParser::parseFunctionDefinitionSection()
{
for (size_t functionIndex = 0; functionIndex < m_module->functionDeclarations().size(); ++functionIndex) {
parseFunctionDefinition(functionIndex);
PROPAGATE_ERROR();
}
for (WASMFunctionPointerTable& functionPointerTable : m_module->functionPointerTables()) {
for (size_t i = 0; i < functionPointerTable.functionIndices.size(); ++i)
functionPointerTable.functions.uncheckedAppend(m_module->functions()[functionPointerTable.functionIndices[i]].get());
}
}
void WASMModuleParser::parseFunctionDefinition(size_t functionIndex)
{
unsigned startOffsetInSource = m_reader.offset();
unsigned endOffsetInSource;
unsigned stackHeight;
String errorMessage;
if (!WASMFunctionParser::checkSyntax(m_module.get(), m_source, functionIndex, startOffsetInSource, endOffsetInSource, stackHeight, errorMessage)) {
m_errorMessage = errorMessage;
return;
}
m_reader.setOffset(endOffsetInSource);
WebAssemblyExecutable* webAssemblyExecutable = WebAssemblyExecutable::create(m_vm, m_source, m_module.get(), functionIndex);
JSFunction* function = JSFunction::create(m_vm, webAssemblyExecutable, m_globalObject.get());
m_module->functions().uncheckedAppend(WriteBarrier<JSFunction>(m_vm, m_module.get(), function));
m_module->functionStartOffsetsInSource().uncheckedAppend(startOffsetInSource);
m_module->functionStackHeights().uncheckedAppend(stackHeight);
}
void WASMModuleParser::parseExportSection()
{
WASMExportFormat exportFormat;
READ_EXPORT_FORMAT_OR_FAIL(exportFormat, "Cannot read the export format.");
switch (exportFormat) {
case WASMExportFormat::Default: {
uint32_t functionIndex;
READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read the function index.");
FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect.");
// FIXME: Export the function.
break;
}
case WASMExportFormat::Record: {
uint32_t numberOfExports;
READ_COMPACT_UINT32_OR_FAIL(numberOfExports, "Cannot read the number of exports.");
for (uint32_t exportIndex = 0; exportIndex < numberOfExports; ++exportIndex) {
String exportName;
READ_STRING_OR_FAIL(exportName, "Cannot read the function export name.");
uint32_t functionIndex;
READ_COMPACT_UINT32_OR_FAIL(functionIndex, "Cannot read the function index.");
FAIL_IF_FALSE(functionIndex < m_module->functionDeclarations().size(), "The function index is incorrect.");
Identifier identifier = Identifier::fromString(&m_vm, exportName);
m_module->putDirect(m_vm, identifier, m_module->functions()[functionIndex].get());
}
break;
}
default:
ASSERT_NOT_REACHED();
}
}
void WASMModuleParser::getImportedValue(ExecState* exec, const String& importName, JSValue& value)
{
FAIL_IF_FALSE(m_imports, "Accessing property of non-object.");
Identifier identifier = Identifier::fromString(&m_vm, importName);
PropertySlot slot(m_imports.get(), PropertySlot::InternalMethodType::Get);
if (!m_imports->getPropertySlot(exec, identifier, slot))
FAIL_WITH_MESSAGE("Can't find a property named \"" + importName + '"');
FAIL_IF_FALSE(slot.isValue(), "\"" + importName + "\" is not a data property.");
// We only retrieve data properties. So, this does not cause any user-observable effect.
value = slot.getValue(exec, identifier);
}
JSWASMModule* parseWebAssembly(ExecState* exec, const SourceCode& source, JSObject* imports, JSArrayBuffer* arrayBuffer, String& errorMessage)
{
WASMModuleParser moduleParser(exec->vm(), exec->lexicalGlobalObject(), source, imports, arrayBuffer);
return moduleParser.parse(exec, errorMessage);
}
} // namespace JSC
#endif // ENABLE(WEBASSEMBLY)