Bug 1243252 - Baldr: refactor exports (r=bbouvier)

This commit is contained in:
Luke Wagner 2016-01-28 09:21:31 -06:00
parent 1cc2cd5959
commit 3ed0e03775
5 changed files with 129 additions and 161 deletions

View File

@ -7411,8 +7411,11 @@ CheckBuffer(JSContext* cx, AsmJSModule& module, HandleValue bufferVal,
}
static bool
DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
DynamicallyLinkModule(JSContext* cx, const CallArgs& args, Handle<WasmModuleObject*> moduleObj,
MutableHandleObject exportObj)
{
AsmJSModule& module = moduleObj->module().asAsmJS();
HandleValue globalVal = args.get(0);
HandleValue importVal = args.get(1);
HandleValue bufferVal = args.get(2);
@ -7469,7 +7472,7 @@ DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module)
return false;
}
return module.dynamicallyLink(cx, buffer, imports);
return module.dynamicallyLink(cx, moduleObj, buffer, imports, module.exportMap(), exportObj);
}
static bool
@ -7579,19 +7582,14 @@ LinkAsmJS(JSContext* cx, unsigned argc, JS::Value* vp)
// asm.js spec and then patching the generated module to associate it with
// the given heap (ArrayBuffer) and a new global data segment (the closure
// state shared by the inner asm.js functions).
if (!DynamicallyLinkModule(cx, args, *module)) {
RootedObject exportObj(cx);
if (!DynamicallyLinkModule(cx, args, moduleObj, &exportObj)) {
// Linking failed, so reparse the entire asm.js module from scratch to
// get normal interpreted bytecode which we can simply Invoke. Very slow.
RootedPropertyName name(cx, fun->name());
return HandleDynamicLinkFailure(cx, args, *module, name);
}
// Link-time validation succeed!
RootedObject exportObj(cx);
if (!module->createExportObject(cx, moduleObj, module->exportMap(), &exportObj))
return false;
args.rval().set(ObjectValue(*exportObj));
return true;
}

View File

@ -674,11 +674,8 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
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))
if (!module.dynamicallyLink(cx, moduleObj, heap, imports, exportMap, &exportObj))
return false;
args.rval().setObject(*exportObj);

View File

@ -1066,10 +1066,99 @@ Module::staticallyLink(ExclusiveContext* cx, const StaticLinkData& linkData)
return true;
}
bool
Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> importArgs)
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Module& module = ExportedFunctionToModuleObject(callee)->module();
uint32_t exportIndex = ExportedFunctionToIndex(callee);
return module.callExport(cx, exportIndex, args);
}
static JSFunction*
NewExportedFunction(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& exportMap,
uint32_t exportIndex)
{
unsigned numArgs = moduleObj->module().exports()[exportIndex].sig().args().length();
const char* chars = exportMap.exportNames[exportIndex].get();
RootedAtom name(cx, AtomizeUTF8Chars(cx, chars, strlen(chars)));
if (!name)
return nullptr;
JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
JSFunction::ASMJS_CTOR);
if (!fun)
return nullptr;
fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
return fun;
}
static bool
CreateExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& exportMap,
const ExportVector& exports, MutableHandleObject exportObj)
{
MOZ_ASSERT(exportMap.exportNames.length() == exports.length());
MOZ_ASSERT(exportMap.fieldNames.length() == exportMap.fieldsToExports.length());
for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
const char* fieldName = exportMap.fieldNames[fieldIndex].get();
if (!*fieldName) {
MOZ_ASSERT(!exportObj);
uint32_t exportIndex = exportMap.fieldsToExports[fieldIndex];
exportObj.set(NewExportedFunction(cx, moduleObj, exportMap, exportIndex));
if (!exportObj)
return false;
break;
}
}
Rooted<ValueVector> vals(cx, ValueVector(cx));
for (size_t exportIndex = 0; exportIndex < exports.length(); exportIndex++) {
JSFunction* fun = NewExportedFunction(cx, moduleObj, exportMap, exportIndex);
if (!fun || !vals.append(ObjectValue(*fun)))
return false;
}
if (!exportObj) {
exportObj.set(JS_NewPlainObject(cx));
if (!exportObj)
return false;
}
for (size_t fieldIndex = 0; fieldIndex < exportMap.fieldNames.length(); fieldIndex++) {
const char* fieldName = exportMap.fieldNames[fieldIndex].get();
if (!*fieldName)
continue;
JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
HandleValue val = vals[exportMap.fieldsToExports[fieldIndex]];
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
}
bool
Module::dynamicallyLink(JSContext* cx,
Handle<WasmModuleObject*> moduleObj,
Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> importArgs,
const ExportMap& exportMap,
MutableHandleObject exportObj)
{
MOZ_ASSERT(this == &moduleObj->module());
MOZ_ASSERT(staticallyLinked_);
MOZ_ASSERT(!dynamicallyLinked_);
dynamicallyLinked_ = true;
@ -1101,93 +1190,10 @@ Module::dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> hea
return false;
}
return sendCodeRangesToProfiler(cx);
}
if (!sendCodeRangesToProfiler(cx))
return false;
static bool
WasmCall(JSContext* cx, unsigned argc, Value* vp)
{
CallArgs args = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &args.callee().as<JSFunction>());
Module& module = ExportedFunctionToModuleObject(callee)->module();
uint32_t exportIndex = ExportedFunctionToIndex(callee);
return module.callExport(cx, exportIndex, args);
}
static JSFunction*
NewExportedFunction(JSContext* cx, Handle<WasmModuleObject*> moduleObj, const ExportMap& map,
uint32_t exportIndex)
{
unsigned numArgs = moduleObj->module().exports()[exportIndex].sig().args().length();
const char* chars = map.exportNames[exportIndex].get();
RootedAtom name(cx, AtomizeUTF8Chars(cx, chars, strlen(chars)));
if (!name)
return nullptr;
JSFunction* fun = NewNativeConstructor(cx, WasmCall, numArgs, name,
gc::AllocKind::FUNCTION_EXTENDED, GenericObject,
JSFunction::ASMJS_CTOR);
if (!fun)
return nullptr;
fun->setExtendedSlot(FunctionExtended::WASM_MODULE_SLOT, ObjectValue(*moduleObj));
fun->setExtendedSlot(FunctionExtended::WASM_EXPORT_INDEX_SLOT, Int32Value(exportIndex));
return fun;
}
bool
Module::createExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj,
const ExportMap& map, MutableHandleObject exportObj)
{
MOZ_ASSERT(this == &moduleObj->module());
MOZ_ASSERT(map.exportNames.length() == exports().length());
MOZ_ASSERT(map.fieldNames.length() == map.fieldsToExports.length());
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName) {
MOZ_ASSERT_IF(isAsmJS(), exports().length() == 1);
MOZ_ASSERT(!exportObj);
uint32_t exportIndex = map.fieldsToExports[fieldIndex];
exportObj.set(NewExportedFunction(cx, moduleObj, map, exportIndex));
if (!exportObj)
return false;
break;
}
}
Rooted<ValueVector> vals(cx, ValueVector(cx));
for (size_t exportIndex = 0; exportIndex < exports().length(); exportIndex++) {
JSFunction* fun = NewExportedFunction(cx, moduleObj, map, exportIndex);
if (!fun || !vals.append(ObjectValue(*fun)))
return false;
}
if (!exportObj) {
exportObj.set(JS_NewPlainObject(cx));
if (!exportObj)
return false;
}
for (size_t fieldIndex = 0; fieldIndex < map.fieldNames.length(); fieldIndex++) {
const char* fieldName = map.fieldNames[fieldIndex].get();
if (!*fieldName)
continue;
JSAtom* atom = AtomizeUTF8Chars(cx, fieldName, strlen(fieldName));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
HandleValue val = vals[map.fieldsToExports[fieldIndex]];
if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE))
return false;
}
return true;
return CreateExportObject(cx, moduleObj, exportMap, exports(), exportObj);
}
SharedMem<uint8_t*>

View File

@ -534,16 +534,14 @@ class Module
// This function transitions the module from a statically-linked state to a
// dynamically-linked state. If this module usesHeap(), a non-null heap
// buffer must be given. The given import vector must match the module's
// ImportVector.
// ImportVector. The function returns a new export object for this module.
bool dynamicallyLink(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> imports);
// This function creates and returns a new export object for this module.
// The lengths of exports() and map.exportNames must be the same.
bool createExportObject(JSContext* cx, Handle<WasmModuleObject*> moduleObj,
const ExportMap& map, MutableHandleObject exportObj);
bool dynamicallyLink(JSContext* cx,
Handle<WasmModuleObject*> moduleObj,
Handle<ArrayBufferObjectMaybeShared*> heap,
Handle<FunctionVector> imports,
const ExportMap& exportMap,
MutableHandleObject exportObj);
// The wasm heap, established by dynamicallyLink.

View File

@ -34,7 +34,6 @@ 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;
@ -216,31 +215,15 @@ class WasmAstFunc : public WasmAstNode
class WasmAstExport : public WasmAstNode
{
const char16_t* const externalName_;
const size_t externalNameLength_;
uint32_t internalIndex_;
TwoByteChars name_;
uint32_t funcIndex_;
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_;
}
WasmAstExport(TwoByteChars name, uint32_t funcIndex)
: WasmAstNode(WasmAstKind::Export), name_(name), funcIndex_(funcIndex)
{}
TwoByteChars name() const { return name_; }
size_t funcIndex() const { return funcIndex_; }
};
class WasmAstModule : public WasmAstNode
@ -372,15 +355,12 @@ class WasmToken
const char16_t* end() const {
return end_;
}
const char16_t* textBegin() const {
TwoByteChars text() 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;
MOZ_ASSERT(end_ - begin_ >= 2);
return TwoByteChars(begin_ + 1, end_ - begin_ - 2);
}
uint32_t integer() const {
MOZ_ASSERT(kind_ == Integer);
@ -818,25 +798,15 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
static WasmAstExport*
ParseExport(WasmParseContext& c)
{
WasmToken externalName;
if (!c.ts.match(WasmToken::Text, &externalName, c.error))
WasmToken name;
if (!c.ts.match(WasmToken::Text, &name, c.error))
return nullptr;
auto exp = new(c.lifo) WasmAstExport(externalName.textBegin(), externalName.textEnd());
if (!exp)
WasmToken funcIndex;
if (!c.ts.match(WasmToken::Integer, &funcIndex, c.error))
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;
return new(c.lifo) WasmAstExport(name.text(), funcIndex.integer());
}
static WasmAstModule*
@ -1024,15 +994,14 @@ EncodeExport(Encoder& e, WasmAstExport& exp)
if (!e.writeCString(FuncSubsection))
return false;
if (!e.writeVarU32(exp.internalIndex()))
if (!e.writeVarU32(exp.funcIndex()))
return false;
Range<const char16_t> twoByte(exp.externalName(), exp.externalNameLength());
UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, twoByte).c_str());
if (!utf8)
UniqueChars utf8Name(JS::CharsToNewUTF8CharsZ(nullptr, exp.name()).c_str());
if (!utf8Name)
return false;
if (!e.writeCString(utf8.get()))
if (!e.writeCString(utf8Name.get()))
return false;
return true;