@@ -452,13 +452,13 @@ void WasmBinaryWriter::writeNames() {
452452 for (auto & import : wasm->imports ) {
453453 if (import ->kind == ExternalKind::Function) {
454454 o << U32LEB (emitted);
455- writeInlineString (import ->name .str );
455+ writeEscapedName (import ->name .str );
456456 emitted++;
457457 }
458458 }
459459 for (auto & curr : wasm->functions ) {
460460 o << U32LEB (emitted);
461- writeInlineString (curr->name .str );
461+ writeEscapedName (curr->name .str );
462462 emitted++;
463463 }
464464 assert (emitted == mappedFunctions.size ());
@@ -565,6 +565,35 @@ void WasmBinaryWriter::writeInlineString(const char* name) {
565565 }
566566}
567567
568+ static bool isHexDigit (char ch) {
569+ return (ch >= ' 0' && ch <= ' 9' ) || (ch >= ' a' && ch <= ' f' ) || (ch >= ' A' && ch <= ' F' );
570+ }
571+
572+ static int decodeHexNibble (char ch) {
573+ return ch <= ' 9' ? ch & 15 : (ch & 15 ) + 9 ;
574+ }
575+
576+ void WasmBinaryWriter::writeEscapedName (const char * name) {
577+ if (!strpbrk (name, " \\ " )) {
578+ writeInlineString (name);
579+ return ;
580+ }
581+ // decode escaped by escapeName (see below) function names
582+ std::string unescaped;
583+ int32_t size = strlen (name);
584+ for (int32_t i = 0 ; i < size;) {
585+ char ch = name[i++];
586+ // support only `\xx` escapes; ignore invalid or unsupported escapes
587+ if (ch != ' \\ ' || i + 1 >= size || !isHexDigit (name[i]) || !isHexDigit (name[i + 1 ])) {
588+ unescaped.push_back (ch);
589+ continue ;
590+ }
591+ unescaped.push_back (char ((decodeHexNibble (name[i]) << 4 ) | decodeHexNibble (name[i + 1 ])));
592+ i += 2 ;
593+ }
594+ writeInlineString (unescaped.c_str ());
595+ }
596+
568597void WasmBinaryWriter::writeInlineBuffer (const char * data, size_t size) {
569598 o << U32LEB (size);
570599 for (size_t i = 0 ; i < size; i++) {
@@ -1515,6 +1544,42 @@ void WasmBinaryBuilder::readTableElements() {
15151544 }
15161545}
15171546
1547+ static bool isIdChar (char ch) {
1548+ return (ch >= ' 0' && ch <= ' 9' ) || (ch >= ' A' && ch <= ' Z' ) || (ch >= ' a' && ch <= ' z' ) ||
1549+ ch == ' !' || ch == ' #' || ch == ' $' || ch == ' %' || ch == ' &' || ch == ' \' ' || ch == ' *' ||
1550+ ch == ' +' || ch == ' -' || ch == ' .' || ch == ' /' || ch == ' :' || ch == ' <' || ch == ' =' ||
1551+ ch == ' >' || ch == ' ?' || ch == ' @' || ch == ' ^' || ch == ' _' || ch == ' `' || ch == ' |' ||
1552+ ch == ' ~' ;
1553+ }
1554+
1555+ static char formatNibble (int nibble) {
1556+ return nibble < 10 ? ' 0' + nibble : ' a' - 10 + nibble;
1557+ }
1558+
1559+ static void escapeName (Name &name) {
1560+ bool allIdChars = true ;
1561+ for (const char *p = name.str ; allIdChars && *p; p++) {
1562+ allIdChars = isIdChar (*p);
1563+ }
1564+ if (allIdChars) {
1565+ return ;
1566+ }
1567+ // encode name, if at least one non-idchar (per WebAssembly spec) was found
1568+ std::string escaped;
1569+ for (const char *p = name.str ; *p; p++) {
1570+ char ch = *p;
1571+ if (isIdChar (ch)) {
1572+ escaped.push_back (ch);
1573+ continue ;
1574+ }
1575+ // replace non-idchar with `\xx` escape
1576+ escaped.push_back (' \\ ' );
1577+ escaped.push_back (formatNibble (ch >> 4 ));
1578+ escaped.push_back (formatNibble (ch & 15 ));
1579+ }
1580+ name = escaped;
1581+ }
1582+
15181583void WasmBinaryBuilder::readNames (size_t payloadLen) {
15191584 if (debug) std::cerr << " == readNames" << std::endl;
15201585 auto sectionPos = pos;
@@ -1533,6 +1598,7 @@ void WasmBinaryBuilder::readNames(size_t payloadLen) {
15331598 for (size_t i = 0 ; i < num; i++) {
15341599 auto index = getU32LEB ();
15351600 auto rawName = getInlineString ();
1601+ escapeName (rawName);
15361602 auto name = rawName;
15371603 // De-duplicate names by appending .1, .2, etc.
15381604 for (int i = 1 ; !usedNames.insert (name).second ; ++i) {
0 commit comments