зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1234985 - BaldrMonkey: testing-only (not content visible) wasm hello world (r=bbouvier)
--HG-- extra : commitid : 15rM27QYH2 extra : rebase_source : 13a60db81c799c697d0cd25deb51ccc5a8806ad4
This commit is contained in:
Родитель
eaac9e2bed
Коммит
7f6b1fc7e9
|
@ -0,0 +1,556 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/Wasm.h"
|
||||
|
||||
#include "asmjs/WasmGenerator.h"
|
||||
#include "asmjs/WasmText.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
|
||||
typedef Handle<WasmModuleObject*> HandleWasmModule;
|
||||
typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule;
|
||||
|
||||
/*****************************************************************************/
|
||||
// reporting
|
||||
|
||||
static bool
|
||||
Fail(JSContext* cx, const char* str)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Fail(JSContext* cx, Decoder& d, const char* str)
|
||||
{
|
||||
uint32_t offset = d.currentOffset();
|
||||
char offsetStr[sizeof "4294967295"];
|
||||
JS_snprintf(offsetStr, sizeof offsetStr, "%" PRIu32, offset);
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_DECODE_FAIL, offsetStr, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm function body validation
|
||||
|
||||
class FunctionDecoder
|
||||
{
|
||||
JSContext* cx_;
|
||||
Decoder& d_;
|
||||
ModuleGenerator& mg_;
|
||||
FunctionGenerator& fg_;
|
||||
uint32_t funcIndex_;
|
||||
|
||||
public:
|
||||
FunctionDecoder(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
|
||||
uint32_t funcIndex)
|
||||
: cx_(cx), d_(d), mg_(mg), fg_(fg), funcIndex_(funcIndex)
|
||||
{}
|
||||
JSContext* cx() const { return cx_; }
|
||||
Decoder& d() const { return d_; }
|
||||
ModuleGenerator& mg() const { return mg_; }
|
||||
FunctionGenerator& fg() const { return fg_; }
|
||||
uint32_t funcIndex() const { return funcIndex_; }
|
||||
ExprType ret() const { return mg_.funcSig(funcIndex_).ret(); }
|
||||
|
||||
bool fail(const char* str) {
|
||||
return Fail(cx_, d_, str);
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
CheckType(FunctionDecoder& f, ExprType actual, ExprType expected)
|
||||
{
|
||||
if (actual == expected || expected == ExprType::Void)
|
||||
return true;
|
||||
|
||||
return f.fail("type mismatch");
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeExpr(FunctionDecoder& f, ExprType expected)
|
||||
{
|
||||
Expr expr;
|
||||
if (!f.d().readExpr(&expr))
|
||||
return f.fail("unable to read expression");
|
||||
|
||||
switch (expr) {
|
||||
case Expr::Nop:
|
||||
return CheckType(f, ExprType::Void, expected);
|
||||
case Expr::I32Const:
|
||||
if (!f.d().readVarU32())
|
||||
return f.fail("unable to read i32.const immediate");
|
||||
return CheckType(f, ExprType::I32, expected);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return f.fail("bad expression code");
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator& fg,
|
||||
uint32_t funcIndex)
|
||||
{
|
||||
const uint8_t* bodyBegin = d.currentPosition();
|
||||
|
||||
FunctionDecoder f(cx, d, mg, fg, funcIndex);
|
||||
if (!DecodeExpr(f, f.ret()))
|
||||
return false;
|
||||
|
||||
const uint8_t* bodyEnd = d.currentPosition();
|
||||
uintptr_t bodyLength = bodyEnd - bodyBegin;
|
||||
if (!fg.bytecode().resize(bodyLength))
|
||||
return false;
|
||||
|
||||
PodCopy(fg.bytecode().begin(), bodyBegin, bodyLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm decoding and generation
|
||||
|
||||
static bool
|
||||
DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
||||
{
|
||||
if (!d.readCStringIf(SigSection))
|
||||
return true;
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected decl section byte size");
|
||||
|
||||
uint32_t numSigs;
|
||||
if (!d.readVarU32(&numSigs))
|
||||
return Fail(cx, d, "expected number of declarations");
|
||||
|
||||
if (!init->sigs.resize(numSigs))
|
||||
return false;
|
||||
|
||||
for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
|
||||
uint32_t numArgs;
|
||||
if (!d.readVarU32(&numArgs))
|
||||
return Fail(cx, d, "bad number of signature args");
|
||||
|
||||
ExprType result;
|
||||
if (!d.readExprType(&result))
|
||||
return Fail(cx, d, "bad result type");
|
||||
|
||||
ValTypeVector args;
|
||||
if (!args.resize(numArgs))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < numArgs; i++) {
|
||||
if (!d.readValType(&args[i]))
|
||||
return Fail(cx, d, "bad arg type");
|
||||
}
|
||||
|
||||
init->sigs[sigIndex] = Sig(Move(args), result);
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "decls section byte size mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
||||
{
|
||||
if (!d.readCStringIf(DeclSection))
|
||||
return true;
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected decl section byte size");
|
||||
|
||||
uint32_t numDecls;
|
||||
if (!d.readVarU32(&numDecls))
|
||||
return Fail(cx, d, "expected number of declarations");
|
||||
|
||||
if (!init->funcSigs.resize(numDecls))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < numDecls; i++) {
|
||||
uint32_t sigIndex;
|
||||
if (!d.readVarU32(&sigIndex))
|
||||
return Fail(cx, d, "expected declaration signature index");
|
||||
|
||||
if (sigIndex > init->sigs.length())
|
||||
return Fail(cx, d, "declaration signature index out of range");
|
||||
|
||||
init->funcSigs[i] = &init->sigs[sigIndex];
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "decls section byte size mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap)
|
||||
{
|
||||
if (!d.readCStringIf(FuncSubsection))
|
||||
return Fail(cx, d, "expected 'func' tag");
|
||||
|
||||
uint32_t funcIndex;
|
||||
if (!d.readVarU32(&funcIndex))
|
||||
return Fail(cx, d, "expected export internal index");
|
||||
|
||||
if (funcIndex >= mg.numFuncSigs())
|
||||
return Fail(cx, d, "export function index out of range");
|
||||
|
||||
uint32_t exportIndex;
|
||||
if (!mg.declareExport(funcIndex, &exportIndex))
|
||||
return false;
|
||||
|
||||
MOZ_ASSERT(exportIndex <= exportMap->exportNames.length());
|
||||
if (exportIndex == exportMap->exportNames.length()) {
|
||||
UniqueChars funcName(JS_smprintf("%u", unsigned(funcIndex)));
|
||||
if (!funcName || !exportMap->exportNames.emplaceBack(Move(funcName)))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!exportMap->fieldsToExports.append(exportIndex))
|
||||
return false;
|
||||
|
||||
const char* chars;
|
||||
if (!d.readCString(&chars))
|
||||
return Fail(cx, d, "expected export external name string");
|
||||
|
||||
return exportMap->fieldNames.emplaceBack(DuplicateString(chars));
|
||||
}
|
||||
|
||||
typedef HashSet<const char*, CStringHasher> CStringSet;
|
||||
|
||||
static bool
|
||||
DecodeExportsSection(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap)
|
||||
{
|
||||
if (!d.readCStringIf(ExportSection))
|
||||
return true;
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected export section byte size");
|
||||
|
||||
uint32_t numExports;
|
||||
if (!d.readVarU32(&numExports))
|
||||
return Fail(cx, d, "expected number of exports");
|
||||
|
||||
for (uint32_t i = 0; i < numExports; i++) {
|
||||
if (!DecodeExport(cx, d, mg, exportMap))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "export section byte size mismatch");
|
||||
|
||||
CStringSet dupSet(cx);
|
||||
if (!dupSet.init())
|
||||
return false;
|
||||
for (const UniqueChars& prevName : exportMap->fieldNames) {
|
||||
CStringSet::AddPtr p = dupSet.lookupForAdd(prevName.get());
|
||||
if (p)
|
||||
return Fail(cx, d, "duplicate export");
|
||||
if (!dupSet.add(p, prevName.get()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeFunc(JSContext* cx, Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
||||
{
|
||||
int64_t before = PRMJ_Now();
|
||||
|
||||
FunctionGenerator fg;
|
||||
if (!mg.startFuncDef(d.currentOffset(), &fg))
|
||||
return false;
|
||||
|
||||
if (!d.readCStringIf(FuncSubsection))
|
||||
return Fail(cx, d, "expected 'func' tag");
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected func section byte size");
|
||||
|
||||
if (!DecodeFuncBody(cx, d, mg, fg, funcIndex))
|
||||
return false;
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "func section byte size mismatch");
|
||||
|
||||
int64_t after = PRMJ_Now();
|
||||
unsigned generateTime = (after - before) / PRMJ_USEC_PER_MSEC;
|
||||
|
||||
return mg.finishFuncDef(funcIndex, generateTime, &fg);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
|
||||
{
|
||||
if (!mg.startFuncDefs())
|
||||
return false;
|
||||
|
||||
uint32_t funcIndex = 0;
|
||||
while (d.readCStringIf(CodeSection)) {
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected code section byte size");
|
||||
|
||||
uint32_t numFuncs;
|
||||
if (!d.readVarU32(&numFuncs))
|
||||
return Fail(cx, d, "expected number of functions");
|
||||
|
||||
if (funcIndex + numFuncs > mg.numFuncSigs())
|
||||
return Fail(cx, d, "more function definitions than declarations");
|
||||
|
||||
for (uint32_t i = 0; i < numFuncs; i++) {
|
||||
if (!DecodeFunc(cx, d, mg, funcIndex++))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "code section byte size mismatch");
|
||||
}
|
||||
|
||||
if (!mg.finishFuncDefs())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeUnknownSection(JSContext* cx, Decoder& d)
|
||||
{
|
||||
const char* unused;
|
||||
if (!d.readCString(&unused))
|
||||
return Fail(cx, d, "failed to read section name");
|
||||
|
||||
if (!d.skipSection())
|
||||
return Fail(cx, d, "unable to skip unknown section");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t length,
|
||||
MutableHandle<WasmModuleObject*> moduleObj, ExportMap* exportMap)
|
||||
{
|
||||
Decoder d(bytes, bytes + length);
|
||||
|
||||
uint32_t u32;
|
||||
if (!d.readU32(&u32) || u32 != MagicNumber)
|
||||
return Fail(cx, d, "failed to match magic number");
|
||||
|
||||
if (!d.readU32(&u32) || u32 != EncodingVersion)
|
||||
return Fail(cx, d, "failed to match binary version");
|
||||
|
||||
UniqueModuleGeneratorData init = MakeUnique<ModuleGeneratorData>();
|
||||
if (!init)
|
||||
return false;
|
||||
|
||||
if (!DecodeSignatureSection(cx, d, init.get()))
|
||||
return false;
|
||||
|
||||
if (!DecodeDeclarationSection(cx, d, init.get()))
|
||||
return false;
|
||||
|
||||
ModuleGenerator mg(cx);
|
||||
if (!mg.init(Move(init)))
|
||||
return false;
|
||||
|
||||
if (!DecodeExportsSection(cx, d, mg, exportMap))
|
||||
return false;
|
||||
|
||||
HeapUsage heapUsage = HeapUsage::None;
|
||||
|
||||
if (!DecodeCodeSection(cx, d, mg))
|
||||
return false;
|
||||
|
||||
CacheableCharsVector funcNames;
|
||||
|
||||
while (!d.readCStringIf(EndSection)) {
|
||||
if (!DecodeUnknownSection(cx, d))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d.done())
|
||||
return Fail(cx, d, "failed to consume all bytes of module");
|
||||
|
||||
UniqueModuleData module;
|
||||
UniqueStaticLinkData link;
|
||||
SlowFunctionVector slowFuncs(cx);
|
||||
if (!mg.finish(heapUsage, Move(filename), Move(funcNames), &module, &link, &slowFuncs))
|
||||
return false;
|
||||
|
||||
moduleObj.set(WasmModuleObject::create(cx));
|
||||
if (!moduleObj)
|
||||
return false;
|
||||
|
||||
if (!moduleObj->init(cx->new_<Module>(Move(module))))
|
||||
return false;
|
||||
|
||||
return moduleObj->module().staticallyLink(cx, *link);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// JS entry poitns
|
||||
|
||||
static bool
|
||||
WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() < 1 || args.length() > 2) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
|
||||
ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args.get(1).isUndefined() && !args.get(1).isObject()) {
|
||||
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
|
||||
return false;
|
||||
}
|
||||
|
||||
UniqueChars filename;
|
||||
if (!DescribeScriptedCaller(cx, &filename))
|
||||
return false;
|
||||
|
||||
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
|
||||
const uint8_t* bytes = code->dataPointer();
|
||||
uint32_t length = code->byteLength();
|
||||
|
||||
Vector<uint8_t> copy(cx);
|
||||
if (code->hasInlineData()) {
|
||||
if (!copy.append(bytes, length))
|
||||
return false;
|
||||
bytes = copy.begin();
|
||||
}
|
||||
|
||||
Rooted<WasmModuleObject*> moduleObj(cx);
|
||||
ExportMap exportMap;
|
||||
if (!DecodeModule(cx, Move(filename), bytes, length, &moduleObj, &exportMap)) {
|
||||
if (!cx->isExceptionPending())
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
Module& module = moduleObj->module();
|
||||
|
||||
Rooted<ArrayBufferObject*> heap(cx);
|
||||
if (module.usesHeap())
|
||||
return Fail(cx, "Heap not implemented yet");
|
||||
|
||||
Rooted<FunctionVector> imports(cx, FunctionVector(cx));
|
||||
if (module.imports().length() > 0)
|
||||
return Fail(cx, "Imports not implemented yet");
|
||||
|
||||
if (!module.dynamicallyLink(cx, heap, imports))
|
||||
return false;
|
||||
|
||||
RootedObject exportObj(cx);
|
||||
if (!module.createExportObject(cx, moduleObj, exportMap, &exportObj))
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*exportObj);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
RootedObject callee(cx, &args.callee());
|
||||
|
||||
if (args.length() != 1) {
|
||||
ReportUsageError(cx, callee, "Wrong number of arguments");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!args[0].isString()) {
|
||||
ReportUsageError(cx, callee, "First argument must be a String");
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoStableStringChars twoByteChars(cx);
|
||||
if (!twoByteChars.initTwoByte(cx, args[0].toString()))
|
||||
return false;
|
||||
|
||||
UniqueChars error;
|
||||
wasm::UniqueBytecode bytes = wasm::TextToBinary(twoByteChars.twoByteChars(), &error);
|
||||
if (!bytes) {
|
||||
JS_ReportError(cx, error.get() ? error.get() : "out of memory");
|
||||
return false;
|
||||
}
|
||||
|
||||
Rooted<ArrayBufferObject*> buffer(cx, ArrayBufferObject::create(cx, bytes->length()));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
||||
memcpy(buffer->dataPointer(), bytes->begin(), bytes->length());
|
||||
|
||||
args.rval().setObject(*buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
static const JSFunctionSpecWithHelp WasmTestingFunctions[] = {
|
||||
JS_FN_HELP("wasmEval", WasmEval, 2, 0,
|
||||
"wasmEval(buffer, imports)",
|
||||
" Compiles the given binary wasm module given by 'buffer' (which must be an ArrayBuffer)\n"
|
||||
" and links the module's imports with the given 'imports' object."),
|
||||
|
||||
JS_FN_HELP("wasmTextToBinary", WasmTextToBinary, 1, 0,
|
||||
"wasmTextToBinary(str)",
|
||||
" Translates the given text wasm module into its binary encoding."),
|
||||
|
||||
JS_FS_HELP_END
|
||||
};
|
||||
|
||||
static bool
|
||||
SupportsWasm(JSContext* cx)
|
||||
{
|
||||
#if defined(JS_CODEGEN_NONE) || defined(JS_CODEGEN_ARM64)
|
||||
return false;
|
||||
#endif
|
||||
|
||||
if (!cx->jitSupportsFloatingPoint())
|
||||
return false;
|
||||
|
||||
if (cx->gcSystemPageSize() != AsmJSPageSize)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::DefineTestingFunctions(JSContext* cx, HandleObject globalObj)
|
||||
{
|
||||
if (SupportsWasm(cx))
|
||||
return JS_DefineFunctionsWithHelp(cx, globalObj, WasmTestingFunctions);
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_h
|
||||
#define wasm_h
|
||||
|
||||
#include "gc/Rooting.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// Add wasm testing JS functions to the given JS global object.
|
||||
bool
|
||||
DefineTestingFunctions(JSContext* cx, HandleObject globalObj);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // namespace wasm_h
|
|
@ -27,6 +27,20 @@ class PropertyName;
|
|||
|
||||
namespace wasm {
|
||||
|
||||
// Module header constants
|
||||
static const uint32_t MagicNumber = 0x6d736100; // "\0asm"
|
||||
static const uint32_t EncodingVersion = -1; // experimental
|
||||
|
||||
// Module section names:
|
||||
static const char SigSection[] = "sig";
|
||||
static const char DeclSection[] = "decl";
|
||||
static const char ExportSection[] = "export";
|
||||
static const char CodeSection[] = "code";
|
||||
static const char EndSection[] = "";
|
||||
|
||||
// Subsection names:
|
||||
static const char FuncSubsection[] = "func";
|
||||
|
||||
enum class Expr : uint8_t
|
||||
{
|
||||
// Control opcodes
|
||||
|
@ -331,6 +345,15 @@ class Encoder
|
|||
bytecode_[pc] = uint8_t(v);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static const T load(const uint8_t* p) {
|
||||
T t;
|
||||
memcpy(&t, p, sizeof(T));
|
||||
return t;
|
||||
}
|
||||
|
||||
static const uint32_t BadSectionLength = uint32_t(-1);
|
||||
|
||||
public:
|
||||
explicit Encoder(Bytecode& bytecode)
|
||||
: bytecode_(bytecode)
|
||||
|
@ -355,9 +378,11 @@ class Encoder
|
|||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
writeExpr(Expr expr, size_t* offset = nullptr) {
|
||||
return writeEnum(expr, offset);
|
||||
}
|
||||
writeExpr(Expr expr, size_t* offset = nullptr) { return writeEnum(expr, offset); }
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
writeValType(ValType type, size_t* offset = nullptr) { return writeEnum(type, offset); }
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
writeExprType(ExprType type, size_t* offset = nullptr) { return writeEnum(type, offset); }
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
writeU8(uint8_t i, size_t* offset = nullptr) { return write<uint8_t>(i, offset); }
|
||||
|
@ -391,6 +416,25 @@ class Encoder
|
|||
return true;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool writeCString(const char* cstr) {
|
||||
MOZ_ASSERT(cstr);
|
||||
return bytecode_.append(reinterpret_cast<const uint8_t*>(cstr), strlen(cstr) + 1);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) {
|
||||
if (!writeU32(BadSectionLength))
|
||||
return false;
|
||||
*offset = bytecode_.length();
|
||||
return true;
|
||||
}
|
||||
void finishSection(size_t offset) {
|
||||
uint8_t* patchAt = bytecode_.begin() + offset - sizeof(uint32_t);
|
||||
MOZ_ASSERT(patchAt <= bytecode_.end() - sizeof(uint32_t));
|
||||
MOZ_ASSERT(load<uint32_t>(patchAt) == BadSectionLength);
|
||||
uint32_t numBytes = bytecode_.length() - offset;
|
||||
memcpy(patchAt, &numBytes, sizeof(uint32_t));
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool pcIsPatchable(size_t pc, unsigned size) const {
|
||||
bool patchable = true;
|
||||
|
@ -427,7 +471,8 @@ class Decoder
|
|||
read(T* out) {
|
||||
if (uintptr_t(end_ - cur_) < sizeof(T))
|
||||
return false;
|
||||
memcpy((void*)out, cur_, sizeof(T));
|
||||
if (out)
|
||||
memcpy((void*)out, cur_, sizeof(T));
|
||||
cur_ += sizeof(T);
|
||||
return true;
|
||||
}
|
||||
|
@ -440,7 +485,8 @@ class Decoder
|
|||
uint8_t u8;
|
||||
if (!read(&u8))
|
||||
return false;
|
||||
*out = T(u8);
|
||||
if (out)
|
||||
*out = T(u8);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -474,6 +520,13 @@ class Decoder
|
|||
}
|
||||
|
||||
public:
|
||||
Decoder(const uint8_t* begin, const uint8_t* end)
|
||||
: beg_(begin),
|
||||
end_(end),
|
||||
cur_(begin)
|
||||
{
|
||||
MOZ_ASSERT(begin <= end);
|
||||
}
|
||||
explicit Decoder(const Bytecode& bytecode)
|
||||
: beg_(bytecode.begin()),
|
||||
end_(bytecode.end()),
|
||||
|
@ -485,59 +538,121 @@ class Decoder
|
|||
return cur_ == end_;
|
||||
}
|
||||
|
||||
const uint8_t* currentPosition() const {
|
||||
return cur_;
|
||||
}
|
||||
size_t currentOffset() const {
|
||||
return cur_ - beg_;
|
||||
}
|
||||
void assertCurrentIs(const DebugOnly<size_t> offset) const {
|
||||
MOZ_ASSERT(size_t(cur_ - beg_) == offset);
|
||||
MOZ_ASSERT(currentOffset() == offset);
|
||||
}
|
||||
|
||||
// The fallible unpacking API should be used when we're not assuming
|
||||
// anything about the bytecode, in particular if it is well-formed.
|
||||
MOZ_WARN_UNUSED_RESULT bool readU8 (uint8_t* i) { return read(i); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readI32(int32_t* i) { return read(i); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readF32(float* f) { return read(f); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readU32(uint32_t* u) { return read(u); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readF64(double* d) { return read(d); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readU8 (uint8_t* i = nullptr) { return read(i); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readI32(int32_t* i = nullptr) { return read(i); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readF32(float* f = nullptr) { return read(f); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readU32(uint32_t* u = nullptr) { return read(u); }
|
||||
MOZ_WARN_UNUSED_RESULT bool readF64(double* d = nullptr) { return read(d); }
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool readI32X4(jit::SimdConstant* c) {
|
||||
MOZ_WARN_UNUSED_RESULT bool readI32X4(jit::SimdConstant* c = nullptr) {
|
||||
int32_t v[4] = { 0, 0, 0, 0 };
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (!readI32(&v[i]))
|
||||
return false;
|
||||
}
|
||||
*c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]);
|
||||
if (c)
|
||||
*c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]);
|
||||
return true;
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool readF32X4(jit::SimdConstant* c) {
|
||||
MOZ_WARN_UNUSED_RESULT bool readF32X4(jit::SimdConstant* c = nullptr) {
|
||||
float v[4] = { 0., 0., 0., 0. };
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
if (!readF32(&v[i]))
|
||||
return false;
|
||||
}
|
||||
*c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]);
|
||||
if (c)
|
||||
*c = jit::SimdConstant::CreateX4(v[0], v[1], v[2], v[3]);
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* decoded) {
|
||||
*decoded = 0;
|
||||
MOZ_WARN_UNUSED_RESULT bool readVarU32(uint32_t* out = nullptr) {
|
||||
uint32_t u32 = 0;
|
||||
uint8_t byte;
|
||||
uint32_t shift = 0;
|
||||
do {
|
||||
if (!readU8(&byte))
|
||||
return false;
|
||||
if (!(byte & 0x80)) {
|
||||
*decoded |= uint32_t(byte & 0x7F) << shift;
|
||||
if (out)
|
||||
*out = u32 | uint32_t(byte & 0x7F) << shift;
|
||||
return true;
|
||||
}
|
||||
*decoded |= uint32_t(byte & 0x7F) << shift;
|
||||
u32 |= uint32_t(byte & 0x7F) << shift;
|
||||
shift += 7;
|
||||
} while (shift != 28);
|
||||
if (!readU8(&byte) || (byte & 0xF0))
|
||||
return false;
|
||||
*decoded |= uint32_t(byte) << 28;
|
||||
if (out)
|
||||
*out = u32 | uint32_t(byte) << 28;
|
||||
return true;
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr) {
|
||||
MOZ_WARN_UNUSED_RESULT bool readExpr(Expr* expr = nullptr) {
|
||||
return readEnum(expr);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool readValType(ValType* type = nullptr) {
|
||||
return readEnum(type);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool readExprType(ExprType* type = nullptr) {
|
||||
return readEnum(type);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool readCString(const char** cstr = nullptr) {
|
||||
if (cstr)
|
||||
*cstr = reinterpret_cast<const char*>(cur_);
|
||||
for (; cur_ != end_; cur_++) {
|
||||
if (!*cur_) {
|
||||
cur_++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool readCStringIf(const char* tag) {
|
||||
for (const uint8_t* p = cur_; p != end_; p++, tag++) {
|
||||
if (*p != *tag)
|
||||
return false;
|
||||
if (!*p) {
|
||||
cur_ = p + 1;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* offset) {
|
||||
uint32_t unused;
|
||||
if (!readU32(&unused))
|
||||
return false;
|
||||
*offset = currentOffset();
|
||||
return true;
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool finishSection(uint32_t offset) {
|
||||
const uint8_t* start = beg_ + offset;
|
||||
uint32_t numBytes;
|
||||
memcpy(&numBytes, start - sizeof(uint32_t), sizeof(uint32_t));
|
||||
return numBytes == uintptr_t(cur_ - start);
|
||||
}
|
||||
MOZ_WARN_UNUSED_RESULT bool skipSection() {
|
||||
uint32_t numBytes;
|
||||
if (!readU32(&numBytes))
|
||||
return false;
|
||||
if (uintptr_t(end_ - cur_) < numBytes)
|
||||
return false;
|
||||
cur_ += numBytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The infallible unpacking API should be used when we are sure that the
|
||||
// bytecode is well-formed.
|
||||
|
|
|
@ -105,6 +105,9 @@ ParallelCompilationEnabled(ExclusiveContext* cx)
|
|||
bool
|
||||
ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind)
|
||||
{
|
||||
if (!funcIndexToExport_.init())
|
||||
return false;
|
||||
|
||||
module_ = MakeUnique<ModuleData>();
|
||||
if (!module_)
|
||||
return false;
|
||||
|
@ -385,9 +388,6 @@ ModuleGenerator::startFuncDefs()
|
|||
if (!threadView_)
|
||||
return false;
|
||||
|
||||
if (!funcIndexToExport_.init())
|
||||
return false;
|
||||
|
||||
uint32_t numTasks;
|
||||
if (ParallelCompilationEnabled(cx_) &&
|
||||
HelperThreadState().wasmCompilationInProgress.compareExchange(false, true))
|
||||
|
|
|
@ -0,0 +1,957 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asmjs/WasmText.h"
|
||||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
|
||||
#include "jsnum.h"
|
||||
#include "jsprf.h"
|
||||
|
||||
#include "asmjs/WasmBinary.h"
|
||||
#include "ds/LifoAlloc.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/HashTable.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
using mozilla::CheckedInt;
|
||||
using mozilla::Maybe;
|
||||
using mozilla::Range;
|
||||
|
||||
static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm AST
|
||||
|
||||
template <class T>
|
||||
using WasmAstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
|
||||
|
||||
template <class K, class V, class HP>
|
||||
using WasmAstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
|
||||
|
||||
typedef WasmAstVector<ValType> WasmAstValTypeVector;
|
||||
|
||||
struct WasmAstBase
|
||||
{
|
||||
void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
|
||||
return astLifo.alloc(numBytes);
|
||||
}
|
||||
};
|
||||
|
||||
class WasmAstSig : public WasmAstBase
|
||||
{
|
||||
WasmAstValTypeVector args_;
|
||||
ExprType ret_;
|
||||
|
||||
public:
|
||||
WasmAstSig(WasmAstValTypeVector&& args, ExprType ret)
|
||||
: args_(Move(args)),
|
||||
ret_(ret)
|
||||
{}
|
||||
WasmAstSig(WasmAstSig&& rhs)
|
||||
: args_(Move(rhs.args_)),
|
||||
ret_(rhs.ret_)
|
||||
{}
|
||||
const WasmAstValTypeVector& args() const {
|
||||
return args_;
|
||||
}
|
||||
ExprType ret() const {
|
||||
return ret_;
|
||||
}
|
||||
|
||||
typedef const WasmAstSig& Lookup;
|
||||
static HashNumber hash(Lookup sig) {
|
||||
return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
|
||||
}
|
||||
static bool match(const WasmAstSig* lhs, Lookup rhs) {
|
||||
return lhs->ret() == rhs.ret() && EqualContainers(lhs->args(), rhs.args());
|
||||
}
|
||||
};
|
||||
|
||||
enum class WasmAstKind
|
||||
{
|
||||
Nop,
|
||||
Const,
|
||||
Func,
|
||||
Export,
|
||||
Module
|
||||
};
|
||||
|
||||
class WasmAstNode : public WasmAstBase
|
||||
{
|
||||
const WasmAstKind kind_;
|
||||
|
||||
public:
|
||||
explicit WasmAstNode(WasmAstKind kind)
|
||||
: kind_(kind)
|
||||
{}
|
||||
WasmAstKind kind() const { return kind_; }
|
||||
};
|
||||
|
||||
class WasmAstExpr : public WasmAstNode
|
||||
{
|
||||
protected:
|
||||
explicit WasmAstExpr(WasmAstKind kind)
|
||||
: WasmAstNode(kind)
|
||||
{}
|
||||
|
||||
public:
|
||||
template <class T>
|
||||
T& as() {
|
||||
MOZ_ASSERT(kind() == T::Kind);
|
||||
return static_cast<T&>(*this);
|
||||
}
|
||||
};
|
||||
|
||||
struct WasmAstNop : WasmAstExpr
|
||||
{
|
||||
WasmAstNop()
|
||||
: WasmAstExpr(WasmAstKind::Nop)
|
||||
{}
|
||||
};
|
||||
|
||||
class WasmAstConst : public WasmAstExpr
|
||||
{
|
||||
const Val val_;
|
||||
|
||||
public:
|
||||
static const WasmAstKind Kind = WasmAstKind::Const;
|
||||
explicit WasmAstConst(Val val)
|
||||
: WasmAstExpr(Kind),
|
||||
val_(val)
|
||||
{}
|
||||
Val val() const { return val_; }
|
||||
};
|
||||
|
||||
class WasmAstFunc : public WasmAstNode
|
||||
{
|
||||
const uint32_t sigIndex_;
|
||||
WasmAstExpr* const maybeBody_;
|
||||
|
||||
public:
|
||||
WasmAstFunc(uint32_t sigIndex, WasmAstExpr* maybeBody)
|
||||
: WasmAstNode(WasmAstKind::Func),
|
||||
sigIndex_(sigIndex),
|
||||
maybeBody_(maybeBody)
|
||||
{}
|
||||
uint32_t sigIndex() const { return sigIndex_; }
|
||||
WasmAstExpr* maybeBody() const { return maybeBody_; }
|
||||
};
|
||||
|
||||
class WasmAstExport : public WasmAstNode
|
||||
{
|
||||
const char16_t* const externalName_;
|
||||
const size_t externalNameLength_;
|
||||
uint32_t internalIndex_;
|
||||
|
||||
public:
|
||||
WasmAstExport(const char16_t* externalNameBegin,
|
||||
const char16_t* externalNameEnd)
|
||||
: WasmAstNode(WasmAstKind::Export),
|
||||
externalName_(externalNameBegin),
|
||||
externalNameLength_(externalNameEnd - externalNameBegin),
|
||||
internalIndex_(UINT32_MAX)
|
||||
{
|
||||
MOZ_ASSERT(externalNameBegin <= externalNameEnd);
|
||||
}
|
||||
const char16_t* externalName() const { return externalName_; }
|
||||
size_t externalNameLength() const { return externalNameLength_; }
|
||||
|
||||
void initInternalIndex(uint32_t internalIndex) {
|
||||
MOZ_ASSERT(internalIndex_ == UINT32_MAX);
|
||||
internalIndex_ = internalIndex;
|
||||
}
|
||||
size_t internalIndex() const {
|
||||
MOZ_ASSERT(internalIndex_ != UINT32_MAX);
|
||||
return internalIndex_;
|
||||
}
|
||||
};
|
||||
|
||||
class WasmAstModule : public WasmAstNode
|
||||
{
|
||||
typedef WasmAstVector<WasmAstFunc*> FuncVector;
|
||||
typedef WasmAstVector<WasmAstExport*> ExportVector;
|
||||
typedef WasmAstVector<WasmAstSig*> SigVector;
|
||||
typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
|
||||
|
||||
LifoAlloc& lifo_;
|
||||
FuncVector funcs_;
|
||||
ExportVector exports_;
|
||||
SigVector sigs_;
|
||||
SigMap sigMap_;
|
||||
|
||||
public:
|
||||
explicit WasmAstModule(LifoAlloc& lifo)
|
||||
: WasmAstNode(WasmAstKind::Module),
|
||||
lifo_(lifo),
|
||||
funcs_(lifo),
|
||||
exports_(lifo),
|
||||
sigs_(lifo),
|
||||
sigMap_(lifo)
|
||||
{}
|
||||
bool init() {
|
||||
return sigMap_.init();
|
||||
}
|
||||
bool declare(WasmAstSig&& sig, uint32_t* sigIndex) {
|
||||
SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
|
||||
if (p) {
|
||||
*sigIndex = p->value();
|
||||
return true;
|
||||
}
|
||||
*sigIndex = sigs_.length();
|
||||
return sigs_.append(new (lifo_) WasmAstSig(Move(sig))) &&
|
||||
sigMap_.add(p, sigs_.back(), *sigIndex);
|
||||
}
|
||||
const SigVector& sigs() const {
|
||||
return sigs_;
|
||||
}
|
||||
bool append(WasmAstFunc* func) {
|
||||
return funcs_.append(func);
|
||||
}
|
||||
const FuncVector& funcs() const {
|
||||
return funcs_;
|
||||
}
|
||||
bool append(WasmAstExport* exp) {
|
||||
return exports_.append(exp);
|
||||
}
|
||||
const ExportVector& exports() const {
|
||||
return exports_;
|
||||
}
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm text token stream
|
||||
|
||||
class WasmToken
|
||||
{
|
||||
public:
|
||||
enum Kind
|
||||
{
|
||||
OpenParen,
|
||||
CloseParen,
|
||||
Name,
|
||||
Text,
|
||||
Integer,
|
||||
ValueType,
|
||||
Const,
|
||||
Module,
|
||||
Func,
|
||||
Param,
|
||||
Result,
|
||||
Export,
|
||||
Nop,
|
||||
EndOfFile,
|
||||
Error
|
||||
};
|
||||
private:
|
||||
Kind kind_;
|
||||
const char16_t* begin_;
|
||||
const char16_t* end_;
|
||||
union {
|
||||
uint32_t integer_;
|
||||
ValType valueType_;
|
||||
} u;
|
||||
public:
|
||||
explicit WasmToken() = default;
|
||||
WasmToken(Kind kind, const char16_t* begin, const char16_t* end)
|
||||
: kind_(kind),
|
||||
begin_(begin),
|
||||
end_(end)
|
||||
{
|
||||
MOZ_ASSERT(kind_ != Error);
|
||||
MOZ_ASSERT((kind == EndOfFile) == (begin == end));
|
||||
}
|
||||
explicit WasmToken(uint32_t integer, const char16_t* begin, const char16_t* end)
|
||||
: kind_(Integer),
|
||||
begin_(begin),
|
||||
end_(end)
|
||||
{
|
||||
MOZ_ASSERT(begin != end);
|
||||
u.integer_ = integer;
|
||||
}
|
||||
explicit WasmToken(Kind kind, ValType valueType, const char16_t* begin, const char16_t* end)
|
||||
: kind_(kind),
|
||||
begin_(begin),
|
||||
end_(end)
|
||||
{
|
||||
MOZ_ASSERT(begin != end);
|
||||
MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
|
||||
u.valueType_ = valueType;
|
||||
}
|
||||
explicit WasmToken(const char16_t* begin)
|
||||
: kind_(Error),
|
||||
begin_(begin),
|
||||
end_(begin)
|
||||
{}
|
||||
Kind kind() const {
|
||||
return kind_;
|
||||
}
|
||||
const char16_t* begin() const {
|
||||
return begin_;
|
||||
}
|
||||
const char16_t* end() const {
|
||||
return end_;
|
||||
}
|
||||
const char16_t* textBegin() const {
|
||||
MOZ_ASSERT(kind_ == Text);
|
||||
MOZ_ASSERT(begin_[0] == '"');
|
||||
return begin_ + 1;
|
||||
}
|
||||
const char16_t* textEnd() const {
|
||||
MOZ_ASSERT(kind_ == Text);
|
||||
MOZ_ASSERT(end_[-1] == '"');
|
||||
return end_ - 1;
|
||||
}
|
||||
uint32_t integer() const {
|
||||
MOZ_ASSERT(kind_ == Integer);
|
||||
return u.integer_;
|
||||
}
|
||||
ValType valueType() const {
|
||||
MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
|
||||
return u.valueType_;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
IsWasmNewLine(char16_t c)
|
||||
{
|
||||
return c == '\n';
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWasmSpace(char16_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case ' ':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
case '\v':
|
||||
case '\f':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWasmDigit(char16_t c)
|
||||
{
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWasmLetter(char16_t c)
|
||||
{
|
||||
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||
}
|
||||
|
||||
static bool
|
||||
IsNameAfterDollar(char16_t c)
|
||||
{
|
||||
return c == '_' || IsWasmDigit(c) || IsWasmLetter(c);
|
||||
}
|
||||
|
||||
class WasmTokenStream
|
||||
{
|
||||
static const uint32_t LookaheadSize = 2;
|
||||
|
||||
const char16_t* cur_;
|
||||
const char16_t* const end_;
|
||||
const char16_t* lineStart_;
|
||||
unsigned line_;
|
||||
uint32_t lookaheadIndex_;
|
||||
uint32_t lookaheadDepth_;
|
||||
WasmToken lookahead_[LookaheadSize];
|
||||
|
||||
bool consume(const char16_t* end, const char16_t* match) {
|
||||
const char16_t* p = cur_;
|
||||
for (; *match; p++, match++) {
|
||||
if (p == end || *p != *match)
|
||||
return false;
|
||||
}
|
||||
cur_ = p;
|
||||
return true;
|
||||
}
|
||||
WasmToken fail(const char16_t* begin) const {
|
||||
return WasmToken(begin);
|
||||
}
|
||||
WasmToken next() {
|
||||
while (cur_ != end_ && IsWasmSpace(*cur_)) {
|
||||
if (IsWasmNewLine(*cur_++)) {
|
||||
lineStart_ = cur_;
|
||||
line_++;
|
||||
}
|
||||
}
|
||||
|
||||
if (cur_ == end_)
|
||||
return WasmToken(WasmToken::EndOfFile, cur_, cur_);
|
||||
|
||||
const char16_t* begin = cur_++;
|
||||
switch (*begin) {
|
||||
case '"':
|
||||
while (cur_ != end_ && *cur_ != '"')
|
||||
cur_++;
|
||||
return WasmToken(WasmToken::Text, begin, ++cur_);
|
||||
|
||||
case '$':
|
||||
while (cur_ != end_ && IsNameAfterDollar(*cur_))
|
||||
cur_++;
|
||||
return WasmToken(WasmToken::Name, begin, cur_);
|
||||
|
||||
case '(':
|
||||
return WasmToken(WasmToken::OpenParen, begin, cur_);
|
||||
|
||||
case ')':
|
||||
return WasmToken(WasmToken::CloseParen, begin, cur_);
|
||||
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
CheckedInt<uint32_t> u32 = *begin - '0';
|
||||
while (cur_ != end_ && IsWasmDigit(*cur_)) {
|
||||
u32 *= 10;
|
||||
u32 += *cur_ - '0';
|
||||
if (!u32.isValid())
|
||||
return fail(begin);
|
||||
cur_++;
|
||||
}
|
||||
return WasmToken(u32.value(), begin, cur_);
|
||||
}
|
||||
|
||||
case 'e':
|
||||
if (consume(end_, MOZ_UTF16("xport")))
|
||||
return WasmToken(WasmToken::Export, begin, cur_);
|
||||
break;
|
||||
|
||||
case 'f':
|
||||
if (consume(end_, MOZ_UTF16("unc")))
|
||||
return WasmToken(WasmToken::Func, begin, cur_);
|
||||
if (consume(end_, MOZ_UTF16("32"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
return WasmToken(WasmToken::Const, ValType::F32, begin, cur_);
|
||||
else
|
||||
return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
|
||||
}
|
||||
if (consume(end_, MOZ_UTF16("64"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
return WasmToken(WasmToken::Const, ValType::F64, begin, cur_);
|
||||
else
|
||||
return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'i':
|
||||
if (consume(end_, MOZ_UTF16("32"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
return WasmToken(WasmToken::Const, ValType::I32, begin, cur_);
|
||||
else
|
||||
return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
|
||||
}
|
||||
if (consume(end_, MOZ_UTF16("64"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
return WasmToken(WasmToken::Const, ValType::I64, begin, cur_);
|
||||
else
|
||||
return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
if (consume(end_, MOZ_UTF16("aram")))
|
||||
return WasmToken(WasmToken::Param, begin, cur_);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
if (consume(end_, MOZ_UTF16("odule")))
|
||||
return WasmToken(WasmToken::Module, begin, cur_);
|
||||
break;
|
||||
|
||||
case 'n':
|
||||
if (consume(end_, MOZ_UTF16("op")))
|
||||
return WasmToken(WasmToken::Nop, begin, cur_);
|
||||
break;
|
||||
|
||||
case 'r':
|
||||
if (consume(end_, MOZ_UTF16("esult")))
|
||||
return WasmToken(WasmToken::Result, begin, cur_);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return fail(begin);
|
||||
}
|
||||
|
||||
public:
|
||||
WasmTokenStream(const char16_t* text, UniqueChars* error)
|
||||
: cur_(text),
|
||||
end_(text + js_strlen(text)),
|
||||
lineStart_(text),
|
||||
line_(0),
|
||||
lookaheadIndex_(0),
|
||||
lookaheadDepth_(0)
|
||||
{}
|
||||
void generateError(WasmToken token, UniqueChars* error) {
|
||||
unsigned column = token.begin() - lineStart_ + 1;
|
||||
error->reset(JS_smprintf("parsing wasm text at at %u:%u", line_, column));
|
||||
}
|
||||
|
||||
WasmToken peek() {
|
||||
if (!lookaheadDepth_) {
|
||||
lookahead_[lookaheadIndex_] = next();
|
||||
lookaheadDepth_ = 1;
|
||||
}
|
||||
return lookahead_[lookaheadIndex_];
|
||||
}
|
||||
WasmToken get() {
|
||||
static_assert(LookaheadSize == 2, "can just flip");
|
||||
if (lookaheadDepth_) {
|
||||
lookaheadDepth_--;
|
||||
WasmToken ret = lookahead_[lookaheadIndex_];
|
||||
lookaheadIndex_ ^= 1;
|
||||
return ret;
|
||||
}
|
||||
return next();
|
||||
}
|
||||
void unget(WasmToken token) {
|
||||
static_assert(LookaheadSize == 2, "can just flip");
|
||||
lookaheadDepth_++;
|
||||
lookaheadIndex_ ^= 1;
|
||||
lookahead_[lookaheadIndex_] = token;
|
||||
}
|
||||
|
||||
// Helpers:
|
||||
bool getIf(WasmToken::Kind kind) {
|
||||
if (peek().kind() == kind) {
|
||||
get();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool match(WasmToken::Kind expect, WasmToken* token, UniqueChars* error) {
|
||||
*token = get();
|
||||
if (token->kind() == expect)
|
||||
return true;
|
||||
generateError(*token, error);
|
||||
return false;
|
||||
}
|
||||
bool match(WasmToken::Kind expect, UniqueChars* error) {
|
||||
WasmToken token;
|
||||
return match(expect, &token, error);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm text format parser
|
||||
|
||||
struct WasmParseContext
|
||||
{
|
||||
WasmTokenStream ts;
|
||||
LifoAlloc& lifo;
|
||||
UniqueChars* error;
|
||||
|
||||
WasmParseContext(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
||||
: ts(text, error),
|
||||
lifo(lifo),
|
||||
error(error)
|
||||
{}
|
||||
};
|
||||
|
||||
static WasmAstConst*
|
||||
ParseConst(WasmParseContext& c, WasmToken constToken)
|
||||
{
|
||||
switch (constToken.valueType()) {
|
||||
case ValType::I32: {
|
||||
WasmToken val;
|
||||
if (!c.ts.match(WasmToken::Integer, &val, c.error))
|
||||
return nullptr;
|
||||
return new(c.lifo) WasmAstConst(Val(val.integer()));
|
||||
}
|
||||
default:
|
||||
c.ts.generateError(constToken, c.error);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static WasmAstExpr*
|
||||
ParseExpr(WasmParseContext& c)
|
||||
{
|
||||
WasmToken expr = c.ts.get();
|
||||
|
||||
switch (expr.kind()) {
|
||||
case WasmToken::Nop:
|
||||
return new(c.lifo) WasmAstNop;
|
||||
case WasmToken::Const:
|
||||
return ParseConst(c, expr);
|
||||
default:
|
||||
c.ts.generateError(expr, c.error);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static WasmAstFunc*
|
||||
ParseFunc(WasmParseContext& c, WasmAstModule* module)
|
||||
{
|
||||
WasmAstValTypeVector args(c.lifo);
|
||||
ExprType result = ExprType::Void;
|
||||
|
||||
WasmAstExpr* maybeBody = nullptr;
|
||||
while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) {
|
||||
WasmToken field = c.ts.get();
|
||||
|
||||
switch (field.kind()) {
|
||||
case WasmToken::Param: {
|
||||
WasmToken valueType;
|
||||
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
|
||||
return nullptr;
|
||||
if (!args.append(valueType.valueType()))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Result: {
|
||||
if (result != ExprType::Void) {
|
||||
c.ts.generateError(field, c.error);
|
||||
return nullptr;
|
||||
}
|
||||
WasmToken valueType;
|
||||
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
|
||||
return nullptr;
|
||||
result = ToExprType(valueType.valueType());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
c.ts.unget(field);
|
||||
maybeBody = ParseExpr(c);
|
||||
if (!maybeBody)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t sigIndex;
|
||||
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
|
||||
return nullptr;
|
||||
|
||||
return new(c.lifo) WasmAstFunc(sigIndex, maybeBody);
|
||||
}
|
||||
|
||||
static WasmAstExport*
|
||||
ParseExport(WasmParseContext& c)
|
||||
{
|
||||
WasmToken externalName;
|
||||
if (!c.ts.match(WasmToken::Text, &externalName, c.error))
|
||||
return nullptr;
|
||||
|
||||
auto exp = new(c.lifo) WasmAstExport(externalName.textBegin(), externalName.textEnd());
|
||||
if (!exp)
|
||||
return nullptr;
|
||||
|
||||
WasmToken internalName = c.ts.get();
|
||||
switch (internalName.kind()) {
|
||||
case WasmToken::Integer:
|
||||
exp->initInternalIndex(internalName.integer());
|
||||
break;
|
||||
default:
|
||||
c.ts.generateError(internalName, c.error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return exp;
|
||||
}
|
||||
|
||||
static WasmAstModule*
|
||||
TextToAst(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
||||
{
|
||||
WasmParseContext c(text, lifo, error);
|
||||
|
||||
if (!c.ts.match(WasmToken::OpenParen, c.error))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::Module, c.error))
|
||||
return nullptr;
|
||||
|
||||
auto module = new(c.lifo) WasmAstModule(c.lifo);
|
||||
if (!module || !module->init())
|
||||
return nullptr;
|
||||
|
||||
while (c.ts.getIf(WasmToken::OpenParen)) {
|
||||
WasmToken section = c.ts.get();
|
||||
|
||||
switch (section.kind()) {
|
||||
case WasmToken::Export: {
|
||||
WasmAstExport* exp = ParseExport(c);
|
||||
if (!exp || !module->append(exp))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Func: {
|
||||
WasmAstFunc* func = ParseFunc(c, module);
|
||||
if (!func || !module->append(func))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
c.ts.generateError(section, c.error);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::EndOfFile, c.error))
|
||||
return nullptr;
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm function body serialization
|
||||
|
||||
static bool
|
||||
EncodeConst(Encoder& e, WasmAstConst& c)
|
||||
{
|
||||
switch (c.val().type()) {
|
||||
case ValType::I32:
|
||||
return e.writeExpr(Expr::I32Const) &&
|
||||
e.writeVarU32(c.val().i32());
|
||||
default:
|
||||
break;
|
||||
}
|
||||
MOZ_CRASH("Bad value type");
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeExpr(Encoder& e, WasmAstExpr& expr)
|
||||
{
|
||||
switch (expr.kind()) {
|
||||
case WasmAstKind::Nop:
|
||||
return e.writeExpr(Expr::Nop);
|
||||
case WasmAstKind::Const:
|
||||
return EncodeConst(e, expr.as<WasmAstConst>());
|
||||
default:;
|
||||
}
|
||||
MOZ_CRASH("Bad expr kind");
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
// wasm AST binary serialization
|
||||
|
||||
static bool
|
||||
EncodeSignatureSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (module.funcs().empty())
|
||||
return true;
|
||||
|
||||
if (!e.writeCString(SigSection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(module.sigs().length()))
|
||||
return false;
|
||||
|
||||
for (WasmAstSig* sig : module.sigs()) {
|
||||
if (!e.writeVarU32(sig->args().length()))
|
||||
return false;
|
||||
|
||||
if (!e.writeExprType(sig->ret()))
|
||||
return false;
|
||||
|
||||
for (ValType t : sig->args()) {
|
||||
if (!e.writeValType(t))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (module.funcs().empty())
|
||||
return true;
|
||||
|
||||
if (!e.writeCString(DeclSection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(module.funcs().length()))
|
||||
return false;
|
||||
|
||||
for (WasmAstFunc* func : module.funcs()) {
|
||||
if (!e.writeVarU32(func->sigIndex()))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeExport(Encoder& e, WasmAstExport& exp)
|
||||
{
|
||||
if (!e.writeCString(FuncSubsection))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(exp.internalIndex()))
|
||||
return false;
|
||||
|
||||
Range<const char16_t> twoByte(exp.externalName(), exp.externalNameLength());
|
||||
UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, twoByte).c_str());
|
||||
if (!utf8)
|
||||
return false;
|
||||
|
||||
if (!e.writeCString(utf8.get()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeExportSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (module.exports().empty())
|
||||
return true;
|
||||
|
||||
if (!e.writeCString(ExportSection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(module.exports().length()))
|
||||
return false;
|
||||
|
||||
for (WasmAstExport* exp : module.exports()) {
|
||||
if (!EncodeExport(e, *exp))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeFunc(Encoder& e, WasmAstFunc& func)
|
||||
{
|
||||
if (!e.writeCString(FuncSubsection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (func.maybeBody()) {
|
||||
if (!EncodeExpr(e, *func.maybeBody()))
|
||||
return false;
|
||||
} else {
|
||||
if (!e.writeExpr(Expr::Nop))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeCodeSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (module.funcs().empty())
|
||||
return true;
|
||||
|
||||
if (!e.writeCString(CodeSection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(module.funcs().length()))
|
||||
return false;
|
||||
|
||||
for (WasmAstFunc* func : module.funcs()) {
|
||||
if (!EncodeFunc(e, *func))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static UniqueBytecode
|
||||
AstToBinary(WasmAstModule& module)
|
||||
{
|
||||
UniqueBytecode bytecode = MakeUnique<Bytecode>();
|
||||
if (!bytecode)
|
||||
return nullptr;
|
||||
|
||||
Encoder e(*bytecode);
|
||||
|
||||
if (!e.writeU32(MagicNumber))
|
||||
return nullptr;
|
||||
|
||||
if (!e.writeU32(EncodingVersion))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeSignatureSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeDeclarationSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeExportSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeCodeSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!e.writeCString(EndSection))
|
||||
return nullptr;
|
||||
|
||||
return Move(bytecode);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
UniqueBytecode
|
||||
wasm::TextToBinary(const char16_t* text, UniqueChars* error)
|
||||
{
|
||||
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
|
||||
WasmAstModule* module = TextToAst(text, lifo, error);
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
return AstToBinary(*module);
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||
*
|
||||
* Copyright 2015 Mozilla Foundation
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef wasm_text_h
|
||||
#define wasm_text_h
|
||||
|
||||
#include "asmjs/WasmBinary.h"
|
||||
#include "js/Utility.h"
|
||||
|
||||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// Translate the textual representation of a wasm module (given by a
|
||||
// null-terminated char16_t array) into a Bytecode object. If there is an error
|
||||
// other than out-of-memory an error message string will be stored in 'error'.
|
||||
|
||||
extern UniqueBytecode
|
||||
TextToBinary(const char16_t* text, UniqueChars* error);
|
||||
|
||||
} // namespace wasm
|
||||
} // namespace js
|
||||
|
||||
#endif // wasm_text_h
|
|
@ -226,21 +226,10 @@ class Sig
|
|||
const ExprType& ret() const { return ret_; }
|
||||
|
||||
HashNumber hash() const {
|
||||
HashNumber hn = HashNumber(ret_);
|
||||
for (unsigned i = 0; i < args_.length(); i++)
|
||||
hn = mozilla::AddToHash(hn, HashNumber(args_[i]));
|
||||
return hn;
|
||||
return AddContainerToHash(args_, HashNumber(ret_));
|
||||
}
|
||||
bool operator==(const Sig& rhs) const {
|
||||
if (ret() != rhs.ret())
|
||||
return false;
|
||||
if (args().length() != rhs.args().length())
|
||||
return false;
|
||||
for (unsigned i = 0; i < args().length(); i++) {
|
||||
if (arg(i) != rhs.arg(i))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
|
||||
}
|
||||
bool operator!=(const Sig& rhs) const {
|
||||
return !(*this == rhs);
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "jswrapper.h"
|
||||
|
||||
#include "asmjs/AsmJS.h"
|
||||
#include "asmjs/Wasm.h"
|
||||
#include "jit/InlinableNatives.h"
|
||||
#include "jit/JitFrameIterator.h"
|
||||
#include "js/Debug.h"
|
||||
|
@ -3672,6 +3673,9 @@ js::DefineTestingFunctions(JSContext* cx, HandleObject obj, bool fuzzingSafe_,
|
|||
|
||||
disableOOMFunctions = disableOOMFunctions_;
|
||||
|
||||
if (!wasm::DefineTestingFunctions(cx, obj))
|
||||
return false;
|
||||
|
||||
if (!JS_DefineProperties(cx, obj, TestingProperties))
|
||||
return false;
|
||||
|
||||
|
|
|
@ -75,10 +75,10 @@ if (typeof assertErrorMessage === 'undefined') {
|
|||
throw new Error("Assertion failed: expected exception " + ctor.name + ", got " + e);
|
||||
if (typeof test == "string") {
|
||||
if (test != e.message)
|
||||
throw new Error("Assertion failed: expeceted " + test + ", got " + e.message);
|
||||
throw new Error("Assertion failed: expected " + test + ", got " + e.message);
|
||||
} else {
|
||||
if (!test.test(e.message))
|
||||
throw new Error("Assertion failed: expeceted " + test.toString() + ", got " + e.message);
|
||||
throw new Error("Assertion failed: expected " + test.toString() + ", got " + e.message);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
load(libdir + "asserts.js");
|
||||
|
||||
function wasmEvalText(str, imports) {
|
||||
return wasmEval(wasmTextToBinary(str), imports);
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
load(libdir + "wasm.js");
|
||||
|
||||
if (!this.wasmEval)
|
||||
quit();
|
||||
|
||||
function mismatchError(expect, actual) {
|
||||
return /type mismatch/;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// exports
|
||||
|
||||
var o = wasmEvalText('(module)');
|
||||
assertEq(Object.getOwnPropertyNames(o).length, 0);
|
||||
|
||||
var o = wasmEvalText('(module (func))');
|
||||
assertEq(Object.getOwnPropertyNames(o).length, 0);
|
||||
|
||||
var o = wasmEvalText('(module (func) (export "" 0))');
|
||||
assertEq(typeof o, "function");
|
||||
assertEq(o.name, "0");
|
||||
assertEq(o.length, 0);
|
||||
assertEq(o(), undefined);
|
||||
|
||||
var o = wasmEvalText('(module (func) (export "a" 0))');
|
||||
var names = Object.getOwnPropertyNames(o);
|
||||
assertEq(names.length, 1);
|
||||
assertEq(names[0], 'a');
|
||||
var desc = Object.getOwnPropertyDescriptor(o, 'a');
|
||||
assertEq(typeof desc.value, "function");
|
||||
assertEq(desc.value.name, "0");
|
||||
assertEq(desc.value.length, 0);
|
||||
assertEq(desc.value(), undefined);
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.enumerable, true);
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.value(), undefined);
|
||||
|
||||
var o = wasmEvalText('(module (func) (func) (export "" 0) (export "a" 1))');
|
||||
assertEq(typeof o, "function");
|
||||
assertEq(o.name, "0");
|
||||
assertEq(o.length, 0);
|
||||
assertEq(o(), undefined);
|
||||
var desc = Object.getOwnPropertyDescriptor(o, 'a');
|
||||
assertEq(typeof desc.value, "function");
|
||||
assertEq(desc.value.name, "1");
|
||||
assertEq(desc.value.length, 0);
|
||||
assertEq(desc.value(), undefined);
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.enumerable, true);
|
||||
assertEq(desc.configurable, true);
|
||||
assertEq(desc.value(), undefined);
|
||||
|
||||
wasmEvalText('(module (func) (func) (export "a" 0))');
|
||||
wasmEvalText('(module (func) (func) (export "a" 1))');
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), Error, /export function index out of range/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), Error, /export function index out of range/);
|
||||
|
||||
var o = wasmEvalText('(module (func) (export "a" 0) (export "b" 0))');
|
||||
assertEq(Object.getOwnPropertyNames(o).sort().toString(), "a,b");
|
||||
assertEq(o.a.name, "0");
|
||||
assertEq(o.b.name, "0");
|
||||
assertEq(o.a === o.b, true);
|
||||
|
||||
var o = wasmEvalText('(module (func) (func) (export "a" 0) (export "b" 1))');
|
||||
assertEq(Object.getOwnPropertyNames(o).sort().toString(), "a,b");
|
||||
assertEq(o.a.name, "0");
|
||||
assertEq(o.b.name, "1");
|
||||
assertEq(o.a === o.b, false);
|
||||
|
||||
var o = wasmEvalText('(module (func (result i32) (i32.const 1)) (func (result i32) (i32.const 2)) (export "a" 0) (export "b" 1))');
|
||||
assertEq(o.a(), 1);
|
||||
assertEq(o.b(), 2);
|
||||
var o = wasmEvalText('(module (func (result i32) (i32.const 1)) (func (result i32) (i32.const 2)) (export "a" 1) (export "b" 0))');
|
||||
assertEq(o.a(), 2);
|
||||
assertEq(o.b(), 1);
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 0) (export "a" 0))'), Error, /duplicate export/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 0) (export "a" 1))'), Error, /duplicate export/);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// signatures
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (result i32)))'), Error, mismatchError("i32", "void"));
|
||||
assertErrorMessage(() => wasmEvalText('(module (func (result i32) (nop)))'), Error, mismatchError("i32", "void"));
|
||||
wasmEvalText('(module (func (nop)))');
|
||||
wasmEvalText('(module (func (result i32) (i32.const 42)))');
|
||||
wasmEvalText('(module (func (param i32)))');
|
||||
wasmEvalText('(module (func (param i32) (result i32) (i32.const 42)))');
|
||||
wasmEvalText('(module (func (result i32) (param i32) (i32.const 42)))');
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
load(libdir + "wasm.js");
|
||||
|
||||
if (!this.wasmEval)
|
||||
quit();
|
||||
|
||||
// MagicNumber = 0x4d534100
|
||||
var magic0 = 0;
|
||||
var magic1 = 97; // 'a'
|
||||
var magic2 = 115; // 's'
|
||||
var magic3 = 109; // 'm'
|
||||
|
||||
// EncodingVersion = -1
|
||||
var ver0 = 0xff;
|
||||
var ver1 = 0xff;
|
||||
var ver2 = 0xff;
|
||||
var ver3 = 0xff;
|
||||
|
||||
var magicError = /failed to match magic number/;
|
||||
var versionError = /failed to match binary version/;
|
||||
var extraError = /failed to consume all bytes of module/;
|
||||
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of().buffer), Error, magicError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(42).buffer), Error, magicError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2).buffer), Error, magicError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(1,2,3,4).buffer), Error, magicError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3).buffer), Error, versionError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, 1).buffer), Error, versionError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0).buffer), Error, versionError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2).buffer), Error, versionError);
|
||||
assertErrorMessage(() => wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, 0, 1).buffer), Error, extraError);
|
||||
|
||||
var o = wasmEval(Uint8Array.of(magic0, magic1, magic2, magic3, ver0, ver1, ver2, ver3, 0).buffer);
|
||||
assertEq(Object.getOwnPropertyNames(o).length, 0);
|
|
@ -0,0 +1,15 @@
|
|||
load(libdir + "wasm.js");
|
||||
|
||||
if (!this.wasmEval)
|
||||
quit();
|
||||
|
||||
var parsingError = /parsing wasm text at/;
|
||||
|
||||
assertErrorMessage(() => wasmEvalText(""), Error, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText("("), Error, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText("(m"), Error, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText("(module"), Error, parsingError);
|
||||
assertErrorMessage(() => wasmEvalText("(moduler"), Error, parsingError);
|
||||
|
||||
// Note: the s-expression text format is temporary, this file is mostly just to
|
||||
// hold basic error smoke tests.
|
|
@ -360,6 +360,10 @@ MSG_DEF(JSMSG_USE_ASM_TYPE_FAIL, 1, JSEXN_TYPEERR, "asm.js type error: {0}
|
|||
MSG_DEF(JSMSG_USE_ASM_LINK_FAIL, 1, JSEXN_TYPEERR, "asm.js link error: {0}")
|
||||
MSG_DEF(JSMSG_USE_ASM_TYPE_OK, 1, JSEXN_NONE, "Successfully compiled asm.js code ({0})")
|
||||
|
||||
// wasm
|
||||
MSG_DEF(JSMSG_WASM_FAIL, 1, JSEXN_TYPEERR, "wasm error: {0}")
|
||||
MSG_DEF(JSMSG_WASM_DECODE_FAIL, 2, JSEXN_TYPEERR, "wasm validation error at offset {0}: {1}")
|
||||
|
||||
// Proxy
|
||||
MSG_DEF(JSMSG_BAD_TRAP_RETURN_VALUE, 2, JSEXN_TYPEERR,"trap {1} for {0} returned a primitive value")
|
||||
MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 0, JSEXN_TYPEERR, "can't change object's extensibility")
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/Compiler.h"
|
||||
#include "mozilla/GuardObjects.h"
|
||||
#include "mozilla/HashFunctions.h"
|
||||
#include "mozilla/PodOperations.h"
|
||||
|
||||
#include <limits.h>
|
||||
|
@ -134,6 +135,28 @@ ForEach(InputIterT begin, InputIterT end, CallableT f)
|
|||
f(*begin);
|
||||
}
|
||||
|
||||
template <class Container1, class Container2>
|
||||
static inline bool
|
||||
EqualContainers(const Container1& lhs, const Container2& rhs)
|
||||
{
|
||||
if (lhs.length() != rhs.length())
|
||||
return false;
|
||||
for (size_t i = 0, n = lhs.length(); i < n; i++) {
|
||||
if (lhs[i] != rhs[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class Container>
|
||||
static inline HashNumber
|
||||
AddContainerToHash(const Container& c, HashNumber hn = 0)
|
||||
{
|
||||
for (size_t i = 0; i < c.length(); i++)
|
||||
hn = mozilla::AddToHash(hn, HashNumber(c[i]));
|
||||
return hn;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
static inline T
|
||||
Min(T t1, T t2)
|
||||
|
|
|
@ -144,12 +144,14 @@ EXPORTS.js += [
|
|||
|
||||
UNIFIED_SOURCES += [
|
||||
'asmjs/AsmJS.cpp',
|
||||
'asmjs/Wasm.cpp',
|
||||
'asmjs/WasmFrameIterator.cpp',
|
||||
'asmjs/WasmGenerator.cpp',
|
||||
'asmjs/WasmIonCompile.cpp',
|
||||
'asmjs/WasmModule.cpp',
|
||||
'asmjs/WasmSignalHandlers.cpp',
|
||||
'asmjs/WasmStubs.cpp',
|
||||
'asmjs/WasmText.cpp',
|
||||
'asmjs/WasmTypes.cpp',
|
||||
'builtin/AtomicsObject.cpp',
|
||||
'builtin/Eval.cpp',
|
||||
|
|
|
@ -273,12 +273,6 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
|
|||
void setNewOwnedData(FreeOp* fop, BufferContents newContents);
|
||||
void changeContents(JSContext* cx, BufferContents newContents);
|
||||
|
||||
/*
|
||||
* Ensure data is not stored inline in the object. Used when handing back a
|
||||
* GC-safe pointer.
|
||||
*/
|
||||
static bool ensureNonInline(JSContext* cx, Handle<ArrayBufferObject*> buffer);
|
||||
|
||||
/* Neuter this buffer and all its views. */
|
||||
static MOZ_WARN_UNUSED_RESULT bool
|
||||
neuter(JSContext* cx, Handle<ArrayBufferObject*> buffer, BufferContents newContents);
|
||||
|
|
|
@ -33,7 +33,7 @@ static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 342;
|
|||
static const uint32_t XDR_BYTECODE_VERSION =
|
||||
uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
|
||||
|
||||
static_assert(JSErr_Limit == 438,
|
||||
static_assert(JSErr_Limit == 440,
|
||||
"GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
|
||||
"removed MSG_DEFs from js.msg, you should increment "
|
||||
"XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
|
||||
|
|
Загрузка…
Ссылка в новой задаче