Skip to content

Commit 1871606

Browse files
authored
Implement module and local names in name section (#3115)
Adds support for the module and local subsections of the name section plus the respective C and JS APIs to populate and obtain local names. C API: * BinaryenFunctionGetNumLocals(func) * BinaryenFunctionHasLocalName(func, index) * BinaryenFunctionGetLocalName(func, index) * BinaryenFunctionSetLocalName(func, index, name) JS API: * Function.getNumLocals(func) * Function.hasLocalName(func, index) * Function.getLocalName(func, index) * Function.setLocalName(func, index, name)
1 parent 0ade3f2 commit 1871606

19 files changed

+447
-212
lines changed

src/binaryen-c.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3709,6 +3709,22 @@ BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func,
37093709
assert(index < vars.size());
37103710
return vars[index].getID();
37113711
}
3712+
BinaryenIndex BinaryenFunctionGetNumLocals(BinaryenFunctionRef func) {
3713+
return ((Function*)func)->getNumLocals();
3714+
}
3715+
int BinaryenFunctionHasLocalName(BinaryenFunctionRef func,
3716+
BinaryenIndex index) {
3717+
return ((Function*)func)->hasLocalName(index);
3718+
}
3719+
const char* BinaryenFunctionGetLocalName(BinaryenFunctionRef func,
3720+
BinaryenIndex index) {
3721+
return ((Function*)func)->getLocalName(index).str;
3722+
}
3723+
void BinaryenFunctionSetLocalName(BinaryenFunctionRef func,
3724+
BinaryenIndex index,
3725+
const char* name) {
3726+
((Function*)func)->setLocalName(index, name);
3727+
}
37123728
BinaryenExpressionRef BinaryenFunctionGetBody(BinaryenFunctionRef func) {
37133729
return ((Function*)func)->body;
37143730
}

src/binaryen-c.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2236,6 +2236,19 @@ BINARYEN_API BinaryenIndex BinaryenFunctionGetNumVars(BinaryenFunctionRef func);
22362236
// specified `Function`.
22372237
BINARYEN_API BinaryenType BinaryenFunctionGetVar(BinaryenFunctionRef func,
22382238
BinaryenIndex index);
2239+
// Gets the number of locals within the specified function. Includes parameters.
2240+
BINARYEN_API BinaryenIndex
2241+
BinaryenFunctionGetNumLocals(BinaryenFunctionRef func);
2242+
// Tests if the local at the specified index has a name.
2243+
BINARYEN_API int BinaryenFunctionHasLocalName(BinaryenFunctionRef func,
2244+
BinaryenIndex index);
2245+
// Gets the name of the local at the specified index.
2246+
BINARYEN_API const char* BinaryenFunctionGetLocalName(BinaryenFunctionRef func,
2247+
BinaryenIndex index);
2248+
// Sets the name of the local at the specified index.
2249+
BINARYEN_API void BinaryenFunctionSetLocalName(BinaryenFunctionRef func,
2250+
BinaryenIndex index,
2251+
const char* name);
22392252
// Gets the body of the specified `Function`.
22402253
BINARYEN_API BinaryenExpressionRef
22412254
BinaryenFunctionGetBody(BinaryenFunctionRef func);

src/js/binaryen.js-post.js

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3061,18 +3061,18 @@ function makeExpressionWrapper(ownStaticMembers) {
30613061
// inherit from Expression
30623062
(SpecificExpression.prototype = Object.create(Expression.prototype)).constructor = SpecificExpression;
30633063
// make own instance members
3064-
makeExpressionWrapperInstanceMembers(SpecificExpression.prototype, ownStaticMembers);
3064+
makeWrapperInstanceMembers(SpecificExpression.prototype, ownStaticMembers);
30653065
return SpecificExpression;
30663066
}
30673067

30683068
// Makes instance members from the given static members
3069-
function makeExpressionWrapperInstanceMembers(prototype, staticMembers) {
3069+
function makeWrapperInstanceMembers(prototype, staticMembers, ref = 'expr') {
30703070
Object.keys(staticMembers).forEach(memberName => {
30713071
const member = staticMembers[memberName];
30723072
if (typeof member === "function") {
30733073
// Instance method calls the respective static method
30743074
prototype[memberName] = function(...args) {
3075-
return this.constructor[memberName](this['expr'], ...args);
3075+
return this.constructor[memberName](this[ref], ...args);
30763076
};
30773077
// Instance accessor calls the respective static methods
30783078
let match;
@@ -3082,10 +3082,10 @@ function makeExpressionWrapperInstanceMembers(prototype, staticMembers) {
30823082
const setterIfAny = staticMembers["set" + memberName.substring(index)];
30833083
Object.defineProperty(prototype, propertyName, {
30843084
get() {
3085-
return member(this['expr']);
3085+
return member(this[ref]);
30863086
},
30873087
set(value) {
3088-
if (setterIfAny) setterIfAny(this['expr'], value);
3088+
if (setterIfAny) setterIfAny(this[ref], value);
30893089
else throw Error("property '" + propertyName + "' has no setter");
30903090
}
30913091
});
@@ -3114,7 +3114,7 @@ Expression['finalize'] = function(expr) {
31143114
Expression['toText'] = function(expr) {
31153115
return Module['emitText'](expr);
31163116
};
3117-
makeExpressionWrapperInstanceMembers(Expression.prototype, Expression);
3117+
makeWrapperInstanceMembers(Expression.prototype, Expression);
31183118
Expression.prototype['valueOf'] = function() {
31193119
return this['expr'];
31203120
};
@@ -4281,6 +4281,42 @@ Module['TupleExtract'] = makeExpressionWrapper({
42814281
}
42824282
});
42834283

4284+
// Function wrapper
4285+
4286+
Module['Function'] = (() => {
4287+
function Function(func) {
4288+
if (!(this instanceof Function)) {
4289+
if (!func) return null;
4290+
return new Function(func);
4291+
}
4292+
if (!func) throw Error("function reference must not be null");
4293+
this['func'] = func;
4294+
}
4295+
Function['getName'] = function(func) {
4296+
return Module['_BinaryenFunctionGetName'](func);
4297+
};
4298+
Function['getNumLocals'] = function(func) {
4299+
return Module['_BinaryenFunctionGetNumLocals'](func);
4300+
};
4301+
Function['hasLocalName'] = function(func, index) {
4302+
return Boolean(Module['_BinaryenFunctionHasLocalName'](func, index));
4303+
};
4304+
Function['getLocalName'] = function(func, index) {
4305+
return UTF8ToString(Module['_BinaryenFunctionGetLocalName'](func, index));
4306+
};
4307+
Function['setLocalName'] = function(func, index, name) {
4308+
preserveStack(() => {
4309+
Module['_BinaryenFunctionSetLocalName'](func, index, strToStack(name));
4310+
});
4311+
};
4312+
// TODO: add more methods
4313+
makeWrapperInstanceMembers(Function.prototype, Function, 'func');
4314+
Function.prototype['valueOf'] = function() {
4315+
return this['func'];
4316+
};
4317+
return Function;
4318+
})();
4319+
42844320
// Additional customizations
42854321

42864322
Module['exit'] = function(status) {

src/passes/Print.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2398,6 +2398,10 @@ struct PrintSExpression : public OverriddenVisitor<PrintSExpression> {
23982398
currModule = curr;
23992399
o << '(';
24002400
printMajor(o, "module");
2401+
if (curr->name.is()) {
2402+
o << ' ';
2403+
printName(curr->name, o);
2404+
}
24012405
incIndent();
24022406
std::vector<Signature> signatures;
24032407
std::unordered_map<Signature, Index> indices;

src/tools/wasm-shell.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,9 @@ int main(int argc, const char* argv[]) {
304304
std::cerr << "BUILDING MODULE [line: " << curr.line << "]\n";
305305
Colors::normal(std::cerr);
306306
auto module = wasm::make_unique<Module>();
307-
Name moduleName;
308307
auto builder = wasm::make_unique<SExpressionWasmBuilder>(
309-
*module, *root[i], IRProfile::Normal, &moduleName);
308+
*module, *root[i], IRProfile::Normal);
309+
auto moduleName = module->name;
310310
builders[moduleName].swap(builder);
311311
modules[moduleName].swap(module);
312312
i++;

src/wasm-binary.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ extern const char* MultivalueFeature;
386386
extern const char* AnyrefFeature;
387387

388388
enum Subsection {
389+
NameModule = 0,
389390
NameFunction = 1,
390391
NameLocal = 2,
391392
};

src/wasm-s-parser.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,10 +126,7 @@ class SExpressionWasmBuilder {
126126

127127
public:
128128
// Assumes control of and modifies the input.
129-
SExpressionWasmBuilder(Module& wasm,
130-
Element& module,
131-
IRProfile profile,
132-
Name* moduleName = nullptr);
129+
SExpressionWasmBuilder(Module& wasm, Element& module, IRProfile profile);
133130

134131
private:
135132
// pre-parse types and function definitions, so we know function return types

src/wasm.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1263,7 +1263,7 @@ using StackIR = std::vector<StackInst*>;
12631263
class Function : public Importable {
12641264
public:
12651265
Name name;
1266-
Signature sig; // parameters and return value
1266+
Signature sig; // parameters and return value
12671267
IRProfile profile = IRProfile::Normal;
12681268
std::vector<Type> vars; // non-param locals
12691269

@@ -1328,6 +1328,7 @@ class Function : public Importable {
13281328
Name getLocalNameOrGeneric(Index index);
13291329

13301330
bool hasLocalName(Index index) const;
1331+
void setLocalName(Index index, Name name);
13311332

13321333
void clearNames();
13331334
void clearDebugInfo();
@@ -1502,6 +1503,9 @@ class Module {
15021503
FeatureSet features = FeatureSet::MVP;
15031504
bool hasFeaturesSection = false;
15041505

1506+
// Module name, if specified. Serves a documentary role only.
1507+
Name name;
1508+
15051509
MixedArena allocator;
15061510

15071511
private:

src/wasm/wasm-binary.cpp

Lines changed: 136 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -555,20 +555,79 @@ void WasmBinaryWriter::writeNames() {
555555
BYN_TRACE("== writeNames\n");
556556
auto start = startSection(BinaryConsts::Section::User);
557557
writeInlineString(BinaryConsts::UserSections::Name);
558-
auto substart =
559-
startSubsection(BinaryConsts::UserSections::Subsection::NameFunction);
560-
o << U32LEB(indexes.functionIndexes.size());
561-
Index emitted = 0;
562-
auto add = [&](Function* curr) {
563-
o << U32LEB(emitted);
564-
writeEscapedName(curr->name.str);
565-
emitted++;
566-
};
567-
ModuleUtils::iterImportedFunctions(*wasm, add);
568-
ModuleUtils::iterDefinedFunctions(*wasm, add);
569-
assert(emitted == indexes.functionIndexes.size());
570-
finishSubsection(substart);
571-
/* TODO: locals */
558+
559+
// module name
560+
if (wasm->name.is()) {
561+
auto substart =
562+
startSubsection(BinaryConsts::UserSections::Subsection::NameModule);
563+
writeEscapedName(wasm->name.str);
564+
finishSubsection(substart);
565+
}
566+
567+
// function names
568+
{
569+
auto substart =
570+
startSubsection(BinaryConsts::UserSections::Subsection::NameFunction);
571+
o << U32LEB(indexes.functionIndexes.size());
572+
Index emitted = 0;
573+
auto add = [&](Function* curr) {
574+
o << U32LEB(emitted);
575+
writeEscapedName(curr->name.str);
576+
emitted++;
577+
};
578+
ModuleUtils::iterImportedFunctions(*wasm, add);
579+
ModuleUtils::iterDefinedFunctions(*wasm, add);
580+
assert(emitted == indexes.functionIndexes.size());
581+
finishSubsection(substart);
582+
}
583+
584+
// local names
585+
{
586+
// Find all functions with at least one local name and only emit the
587+
// subsection if there is at least one.
588+
std::vector<std::pair<Index, Function*>> functionsWithLocalNames;
589+
Index checked = 0;
590+
auto check = [&](Function* curr) {
591+
auto numLocals = curr->getNumLocals();
592+
for (Index i = 0; i < numLocals; ++i) {
593+
if (curr->hasLocalName(i)) {
594+
functionsWithLocalNames.push_back({checked, curr});
595+
break;
596+
}
597+
}
598+
checked++;
599+
};
600+
ModuleUtils::iterImportedFunctions(*wasm, check);
601+
ModuleUtils::iterDefinedFunctions(*wasm, check);
602+
assert(checked == indexes.functionIndexes.size());
603+
if (functionsWithLocalNames.size() > 0) {
604+
// Otherwise emit those functions but only include locals with a name.
605+
auto substart =
606+
startSubsection(BinaryConsts::UserSections::Subsection::NameLocal);
607+
o << U32LEB(functionsWithLocalNames.size());
608+
Index emitted = 0;
609+
for (auto& indexedFunc : functionsWithLocalNames) {
610+
std::vector<std::pair<Index, Name>> localsWithNames;
611+
auto numLocals = indexedFunc.second->getNumLocals();
612+
for (Index i = 0; i < numLocals; ++i) {
613+
if (indexedFunc.second->hasLocalName(i)) {
614+
localsWithNames.push_back({i, indexedFunc.second->getLocalName(i)});
615+
}
616+
}
617+
assert(localsWithNames.size());
618+
o << U32LEB(indexedFunc.first);
619+
o << U32LEB(localsWithNames.size());
620+
for (auto& indexedLocal : localsWithNames) {
621+
o << U32LEB(indexedLocal.first);
622+
writeEscapedName(indexedLocal.second.str);
623+
}
624+
emitted++;
625+
}
626+
assert(emitted == functionsWithLocalNames.size());
627+
finishSubsection(substart);
628+
}
629+
}
630+
572631
finishSection(start);
573632
}
574633

@@ -2126,33 +2185,72 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) {
21262185
auto nameType = getU32LEB();
21272186
auto subsectionSize = getU32LEB();
21282187
auto subsectionPos = pos;
2129-
if (nameType != BinaryConsts::UserSections::Subsection::NameFunction) {
2130-
// TODO: locals
2131-
std::cerr << "warning: unknown name subsection at " << pos << std::endl;
2132-
pos = subsectionPos + subsectionSize;
2133-
continue;
2134-
}
2135-
auto num = getU32LEB();
2136-
std::set<Name> usedNames;
2137-
for (size_t i = 0; i < num; i++) {
2138-
auto index = getU32LEB();
2139-
auto rawName = getInlineString();
2140-
rawName = escape(rawName);
2141-
auto name = rawName;
2142-
// De-duplicate names by appending .1, .2, etc.
2143-
for (int i = 1; !usedNames.insert(name).second; ++i) {
2144-
name = rawName.str + std::string(".") + std::to_string(i);
2188+
if (nameType == BinaryConsts::UserSections::Subsection::NameModule) {
2189+
wasm.name = getInlineString();
2190+
} else if (nameType ==
2191+
BinaryConsts::UserSections::Subsection::NameFunction) {
2192+
auto num = getU32LEB();
2193+
std::set<Name> usedNames;
2194+
for (size_t i = 0; i < num; i++) {
2195+
auto index = getU32LEB();
2196+
auto rawName = getInlineString();
2197+
rawName = escape(rawName);
2198+
auto name = rawName;
2199+
// De-duplicate names by appending .1, .2, etc.
2200+
for (int i = 1; !usedNames.insert(name).second; ++i) {
2201+
name = rawName.str + std::string(".") + std::to_string(i);
2202+
}
2203+
// note: we silently ignore errors here, as name section errors
2204+
// are not fatal. should we warn?
2205+
auto numFunctionImports = functionImports.size();
2206+
if (index < numFunctionImports) {
2207+
functionImports[index]->name = name;
2208+
} else if (index - numFunctionImports < functions.size()) {
2209+
functions[index - numFunctionImports]->name = name;
2210+
} else {
2211+
std::cerr << "warning: function index out of bounds in name section, "
2212+
"function subsection: "
2213+
<< std::string(name.str) << " at index "
2214+
<< std::to_string(index) << std::endl;
2215+
}
21452216
}
2146-
// note: we silently ignore errors here, as name section errors
2147-
// are not fatal. should we warn?
2217+
} else if (nameType == BinaryConsts::UserSections::Subsection::NameLocal) {
2218+
auto numFuncs = getU32LEB();
21482219
auto numFunctionImports = functionImports.size();
2149-
if (index < numFunctionImports) {
2150-
functionImports[index]->name = name;
2151-
} else if (index - numFunctionImports < functions.size()) {
2152-
functions[index - numFunctionImports]->name = name;
2153-
} else {
2154-
throwError("index out of bounds: " + std::string(name.str));
2220+
for (size_t i = 0; i < numFuncs; i++) {
2221+
auto funcIndex = getU32LEB();
2222+
Function* func = nullptr;
2223+
if (funcIndex < numFunctionImports) {
2224+
func = functionImports[funcIndex];
2225+
} else if (funcIndex - numFunctionImports < functions.size()) {
2226+
func = functions[funcIndex - numFunctionImports];
2227+
} else {
2228+
std::cerr
2229+
<< "warning: function index out of bounds in name section, local "
2230+
"subsection: "
2231+
<< std::to_string(funcIndex) << std::endl;
2232+
}
2233+
auto numLocals = getU32LEB();
2234+
for (size_t j = 0; j < numLocals; j++) {
2235+
auto localIndex = getU32LEB();
2236+
auto localName = getInlineString();
2237+
if (!func) {
2238+
continue; // read and discard in case of prior error
2239+
}
2240+
if (localIndex < func->getNumLocals()) {
2241+
func->localNames[localIndex] = localName;
2242+
} else {
2243+
std::cerr << "warning: local index out of bounds in name "
2244+
"section, local subsection: "
2245+
<< std::string(localName.str) << " at index "
2246+
<< std::to_string(localIndex) << " in function "
2247+
<< std::string(func->name.str) << std::endl;
2248+
}
2249+
}
21552250
}
2251+
} else {
2252+
std::cerr << "warning: unknown name subsection at " << pos << std::endl;
2253+
pos = subsectionPos + subsectionSize;
21562254
}
21572255
if (pos != subsectionPos + subsectionSize) {
21582256
throwError("bad names subsection position change");

0 commit comments

Comments
 (0)