blob: a2893827773f0ff6c48da1d79e9d8b1272433f18 [file] [log] [blame]
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_WASM_MODULE_H_
#define V8_WASM_MODULE_H_
#include <memory>
#include "src/debug/debug-interface.h"
#include "src/globals.h"
#include "src/handles.h"
#include "src/managed.h"
#include "src/parsing/preparse-data.h"
#include "src/wasm/decoder.h"
#include "src/wasm/signature-map.h"
#include "src/wasm/wasm-opcodes.h"
namespace v8 {
namespace internal {
class WasmCompiledModule;
class WasmDebugInfo;
class WasmModuleObject;
class WasmInstanceObject;
class WasmTableObject;
class WasmMemoryObject;
namespace compiler {
class CallDescriptor;
}
namespace wasm {
class ErrorThrower;
enum WasmExternalKind {
kExternalFunction = 0,
kExternalTable = 1,
kExternalMemory = 2,
kExternalGlobal = 3
};
// Static representation of a wasm function.
struct WasmFunction {
FunctionSig* sig; // signature of the function.
uint32_t func_index; // index into the function table.
uint32_t sig_index; // index into the signature table.
WireBytesRef name; // function name, if any.
WireBytesRef code; // code of this function.
bool imported;
bool exported;
};
// Static representation of a wasm global variable.
struct WasmGlobal {
ValueType type; // type of the global.
bool mutability; // {true} if mutable.
WasmInitExpr init; // the initialization expression of the global.
uint32_t offset; // offset into global memory.
bool imported; // true if imported.
bool exported; // true if exported.
};
// Note: An exception signature only uses the params portion of a
// function signature.
typedef FunctionSig WasmExceptionSig;
struct WasmException {
explicit WasmException(const WasmExceptionSig* sig = &empty_sig_)
: sig(sig) {}
FunctionSig* ToFunctionSig() const { return const_cast<FunctionSig*>(sig); }
const WasmExceptionSig* sig; // type signature of the exception.
// Used to hold data on runtime exceptions.
static constexpr const char* kRuntimeIdStr = "WasmExceptionRuntimeId";
static constexpr const char* kRuntimeValuesStr = "WasmExceptionValues";
private:
static const WasmExceptionSig empty_sig_;
};
// Static representation of a wasm data segment.
struct WasmDataSegment {
WasmInitExpr dest_addr; // destination memory address of the data.
WireBytesRef source; // start offset in the module bytes.
};
// Static representation of a wasm indirect call table.
struct WasmIndirectFunctionTable {
MOVE_ONLY_WITH_DEFAULT_CONSTRUCTORS(WasmIndirectFunctionTable);
uint32_t initial_size = 0; // initial table size.
uint32_t maximum_size = 0; // maximum table size.
bool has_maximum_size = false; // true if there is a maximum size.
// TODO(titzer): Move this to WasmInstance. Needed by interpreter only.
std::vector<int32_t> values; // function table, -1 indicating invalid.
bool imported = false; // true if imported.
bool exported = false; // true if exported.
SignatureMap map; // canonicalizing map for sig indexes.
};
// Static representation of how to initialize a table.
struct WasmTableInit {
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmTableInit);
WasmTableInit(uint32_t table_index, WasmInitExpr offset)
: table_index(table_index), offset(offset) {}
uint32_t table_index;
WasmInitExpr offset;
std::vector<uint32_t> entries;
};
// Static representation of a wasm import.
struct WasmImport {
WireBytesRef module_name; // module name.
WireBytesRef field_name; // import name.
WasmExternalKind kind; // kind of the import.
uint32_t index; // index into the respective space.
};
// Static representation of a wasm export.
struct WasmExport {
WireBytesRef name; // exported name.
WasmExternalKind kind; // kind of the export.
uint32_t index; // index into the respective space.
};
enum ModuleOrigin : uint8_t { kWasmOrigin, kAsmJsOrigin };
struct ModuleWireBytes;
// Static representation of a module.
struct V8_EXPORT_PRIVATE WasmModule {
MOVE_ONLY_NO_DEFAULT_CONSTRUCTOR(WasmModule);
static const uint32_t kPageSize = 0x10000; // Page size, 64kb.
static const uint32_t kMinMemPages = 1; // Minimum memory size = 64kb
static constexpr int kInvalidExceptionTag = -1;
std::unique_ptr<Zone> signature_zone;
uint32_t initial_pages = 0; // initial size of the memory in 64k pages
uint32_t maximum_pages = 0; // maximum size of the memory in 64k pages
bool has_shared_memory = false; // true if memory is a SharedArrayBuffer
bool has_maximum_pages = false; // true if there is a maximum memory size
bool has_memory = false; // true if the memory was defined or imported
bool mem_export = false; // true if the memory is exported
int start_function_index = -1; // start function, >= 0 if any
std::vector<WasmGlobal> globals;
uint32_t globals_size = 0;
uint32_t num_imported_functions = 0;
uint32_t num_declared_functions = 0;
uint32_t num_exported_functions = 0;
WireBytesRef name = {0, 0};
// TODO(wasm): Add url here, for spec'ed location information.
std::vector<FunctionSig*> signatures;
std::vector<WasmFunction> functions;
std::vector<WasmDataSegment> data_segments;
std::vector<WasmIndirectFunctionTable> function_tables;
std::vector<WasmImport> import_table;
std::vector<WasmExport> export_table;
std::vector<WasmException> exceptions;
std::vector<WasmTableInit> table_inits;
WasmModule() : WasmModule(nullptr) {}
WasmModule(std::unique_ptr<Zone> owned);
ModuleOrigin origin() const { return origin_; }
void set_origin(ModuleOrigin new_value) { origin_ = new_value; }
bool is_wasm() const { return origin_ == kWasmOrigin; }
bool is_asm_js() const { return origin_ == kAsmJsOrigin; }
private:
// TODO(kschimpf) - Encapsulate more fields.
ModuleOrigin origin_ = kWasmOrigin; // origin of the module
};
typedef Managed<WasmModule> WasmModuleWrapper;
// Interface to the storage (wire bytes) of a wasm module.
// It is illegal for anyone receiving a ModuleWireBytes to store pointers based
// on module_bytes, as this storage is only guaranteed to be alive as long as
// this struct is alive.
struct V8_EXPORT_PRIVATE ModuleWireBytes {
ModuleWireBytes(Vector<const byte> module_bytes)
: module_bytes_(module_bytes) {}
ModuleWireBytes(const byte* start, const byte* end)
: module_bytes_(start, static_cast<int>(end - start)) {
DCHECK_GE(kMaxInt, end - start);
}
// Get a string stored in the module bytes representing a name.
WasmName GetName(WireBytesRef ref) const {
if (ref.is_empty()) return {"<?>", 3}; // no name.
CHECK(BoundsCheck(ref.offset(), ref.length()));
return Vector<const char>::cast(
module_bytes_.SubVector(ref.offset(), ref.end_offset()));
}
// Get a string stored in the module bytes representing a function name.
WasmName GetName(const WasmFunction* function) const {
return GetName(function->name);
}
// Get a string stored in the module bytes representing a name.
WasmName GetNameOrNull(WireBytesRef ref) const {
if (!ref.is_set()) return {NULL, 0}; // no name.
CHECK(BoundsCheck(ref.offset(), ref.length()));
return Vector<const char>::cast(
module_bytes_.SubVector(ref.offset(), ref.end_offset()));
}
// Get a string stored in the module bytes representing a function name.
WasmName GetNameOrNull(const WasmFunction* function) const {
return GetNameOrNull(function->name);
}
// Checks the given offset range is contained within the module bytes.
bool BoundsCheck(uint32_t offset, uint32_t length) const {
uint32_t size = static_cast<uint32_t>(module_bytes_.length());
return offset <= size && length <= size - offset;
}
Vector<const byte> GetFunctionBytes(const WasmFunction* function) const {
return module_bytes_.SubVector(function->code.offset(),
function->code.end_offset());
}
Vector<const byte> module_bytes() const { return module_bytes_; }
const byte* start() const { return module_bytes_.start(); }
const byte* end() const { return module_bytes_.end(); }
size_t length() const { return module_bytes_.length(); }
private:
Vector<const byte> module_bytes_;
};
// A helper for printing out the names of functions.
struct WasmFunctionName {
WasmFunctionName(const WasmFunction* function, WasmName name)
: function_(function), name_(name) {}
const WasmFunction* function_;
WasmName name_;
};
std::ostream& operator<<(std::ostream& os, const WasmFunctionName& name);
// Get the debug info associated with the given wasm object.
// If no debug info exists yet, it is created automatically.
Handle<WasmDebugInfo> GetDebugInfo(Handle<JSObject> wasm);
V8_EXPORT_PRIVATE MaybeHandle<WasmModuleObject> CreateModuleObjectFromBytes(
Isolate* isolate, const byte* start, const byte* end, ErrorThrower* thrower,
ModuleOrigin origin, Handle<Script> asm_js_script,
Vector<const byte> asm_offset_table);
V8_EXPORT_PRIVATE bool IsWasmCodegenAllowed(Isolate* isolate,
Handle<Context> context);
V8_EXPORT_PRIVATE Handle<JSArray> GetImports(Isolate* isolate,
Handle<WasmModuleObject> module);
V8_EXPORT_PRIVATE Handle<JSArray> GetExports(Isolate* isolate,
Handle<WasmModuleObject> module);
V8_EXPORT_PRIVATE Handle<JSArray> GetCustomSections(
Isolate* isolate, Handle<WasmModuleObject> module, Handle<String> name,
ErrorThrower* thrower);
// Decode local variable names from the names section. Return FixedArray of
// FixedArray of <undefined|String>. The outer fixed array is indexed by the
// function index, the inner one by the local index.
Handle<FixedArray> DecodeLocalNames(Isolate*, Handle<WasmCompiledModule>);
// If the target is an export wrapper, return the {WasmFunction*} corresponding
// to the wrapped wasm function; in all other cases, return nullptr.
// The returned pointer is owned by the wasm instance target belongs to. The
// result is alive as long as the instance exists.
WasmFunction* GetWasmFunctionForExport(Isolate* isolate, Handle<Object> target);
// {export_wrapper} is known to be an export.
Handle<Code> UnwrapExportWrapper(Handle<JSFunction> export_wrapper);
void UpdateDispatchTables(Isolate* isolate, Handle<FixedArray> dispatch_tables,
int index, WasmFunction* function, Handle<Code> code);
void UnpackAndRegisterProtectedInstructions(Isolate* isolate,
Handle<FixedArray> code_table);
const char* ExternalKindName(WasmExternalKind);
// TruncatedUserString makes it easy to output names up to a certain length, and
// output a truncation followed by '...' if they exceed a limit.
// Use like this:
// TruncatedUserString<> name (pc, len);
// printf("... %.*s ...", name.length(), name.start())
template <int kMaxLen = 50>
class TruncatedUserString {
static_assert(kMaxLen >= 4, "minimum length is 4 (length of '...' plus one)");
public:
template <typename T>
explicit TruncatedUserString(Vector<T> name)
: TruncatedUserString(name.start(), name.length()) {}
TruncatedUserString(const byte* start, size_t len)
: TruncatedUserString(reinterpret_cast<const char*>(start), len) {}
TruncatedUserString(const char* start, size_t len)
: start_(start), length_(std::min(kMaxLen, static_cast<int>(len))) {
if (len > static_cast<size_t>(kMaxLen)) {
memcpy(buffer_, start, kMaxLen - 3);
memset(buffer_ + kMaxLen - 3, '.', 3);
start_ = buffer_;
}
}
const char* start() const { return start_; }
int length() const { return length_; }
private:
const char* start_;
int length_;
char buffer_[kMaxLen];
};
namespace testing {
void ValidateInstancesChain(Isolate* isolate,
Handle<WasmModuleObject> module_obj,
int instance_count);
void ValidateModuleState(Isolate* isolate, Handle<WasmModuleObject> module_obj);
void ValidateOrphanedInstance(Isolate* isolate,
Handle<WasmInstanceObject> instance);
} // namespace testing
} // namespace wasm
} // namespace internal
} // namespace v8
#endif // V8_WASM_MODULE_H_