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:
Luke Wagner 2016-01-25 13:24:41 -06:00
Родитель eaac9e2bed
Коммит 7f6b1fc7e9
18 изменённых файлов: 1906 добавлений и 46 удалений

556
js/src/asmjs/Wasm.cpp Normal file
Просмотреть файл

@ -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(&sectionStart))
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(&sectionStart))
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(&sectionStart))
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(&sectionStart))
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(&sectionStart))
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;
}

34
js/src/asmjs/Wasm.h Normal file
Просмотреть файл

@ -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))

957
js/src/asmjs/WasmText.cpp Normal file
Просмотреть файл

@ -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);
}

38
js/src/asmjs/WasmText.h Normal file
Просмотреть файл

@ -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 "