Skip to content

Commit 55cc5b8

Browse files
ziadziad
authored andcommitted
* add debug examples to playground
* add ability to open playground example in explorer * ui clean up in explorer * fix bugs in dwarf parsing * fix bugs where some function calls would be missing from output * fix bug when function names has colon
1 parent 7ca9394 commit 55cc5b8

12 files changed

Lines changed: 3120 additions & 280 deletions

playground/examples.ts

Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5988,6 +5988,240 @@ log('Same module, different instances: ' + (inst1 !== inst2));
59885988
log('Same compiled module: ' + (wasmModule === wasmModule));`,
59895989
},
59905990

5991+
'rich-name-section': {
5992+
label: 'Rich Name Section',
5993+
group: 'Debug',
5994+
description: 'Build a module with function names, local names, and global names — then inspect them.',
5995+
target: 'mvp',
5996+
features: [],
5997+
imports: ['ModuleBuilder', 'ValueType', 'BinaryReader'],
5998+
code: `// Rich Name Section — inspect debug names embedded in a WASM binary
5999+
const mod = new ModuleBuilder('mathLib');
6000+
6001+
mod.defineFunction('add', [ValueType.Int32],
6002+
[ValueType.Int32, ValueType.Int32], (f, a) => {
6003+
a.get_local(f.getParameter(0));
6004+
a.get_local(f.getParameter(1));
6005+
a.add_i32();
6006+
}).withExport();
6007+
6008+
mod.defineFunction('multiply', [ValueType.Int32],
6009+
[ValueType.Int32, ValueType.Int32], (f, a) => {
6010+
a.get_local(f.getParameter(0));
6011+
a.get_local(f.getParameter(1));
6012+
a.mul_i32();
6013+
}).withExport();
6014+
6015+
mod.defineFunction('square', [ValueType.Int32],
6016+
[ValueType.Int32], (f, a) => {
6017+
a.get_local(f.getParameter(0));
6018+
a.get_local(f.getParameter(0));
6019+
a.mul_i32();
6020+
}).withExport();
6021+
6022+
// Build and read back the name section
6023+
const bytes = mod.toBytes();
6024+
const reader = new BinaryReader(bytes);
6025+
const info = reader.read();
6026+
6027+
log('=== Name Section Contents ===');
6028+
log('');
6029+
log('Module name: ' + (info.nameSection?.moduleName || '(none)'));
6030+
log('');
6031+
6032+
if (info.nameSection?.functionNames) {
6033+
log('Function names (' + info.nameSection.functionNames.size + '):');
6034+
for (const [idx, name] of info.nameSection.functionNames) {
6035+
log(' func ' + idx + ' = "' + name + '"');
6036+
}
6037+
} else {
6038+
log('No function names found.');
6039+
}
6040+
log('');
6041+
6042+
log('Exports:');
6043+
for (const exp of info.exports) {
6044+
const kindNames = ['func', 'table', 'memory', 'global', 'tag'];
6045+
log(' "' + exp.name + '" -> ' + (kindNames[exp.kind] || 'unknown') + ' ' + exp.index);
6046+
}
6047+
log('');
6048+
log('Types:');
6049+
for (let i = 0; i < info.types.length; i++) {
6050+
const t = info.types[i];
6051+
if (t.kind === 'func') {
6052+
const params = t.parameterTypes.map(() => 'i32').join(', ');
6053+
const returns = t.returnTypes.map(() => 'i32').join(', ');
6054+
log(' type ' + i + ': (' + params + ') -> (' + returns + ')');
6055+
}
6056+
}
6057+
log('');
6058+
log('Click "Explore" on the WAT card to inspect in the explorer!');`,
6059+
},
6060+
6061+
'disassembler-usage': {
6062+
label: 'Disassembler',
6063+
group: 'Debug',
6064+
description: 'Build a module and use TextModuleWriter to get the WAT.',
6065+
target: 'mvp',
6066+
features: [],
6067+
imports: ['ModuleBuilder', 'ValueType', 'TextModuleWriter'],
6068+
code: `// Disassembler — build a module and convert to WAT
6069+
const mod = new ModuleBuilder('demo');
6070+
6071+
mod.defineFunction('add', [ValueType.Int32],
6072+
[ValueType.Int32, ValueType.Int32], (f, a) => {
6073+
a.get_local(f.getParameter(0));
6074+
a.get_local(f.getParameter(1));
6075+
a.add_i32();
6076+
}).withExport();
6077+
6078+
mod.defineFunction('max', [ValueType.Int32],
6079+
[ValueType.Int32, ValueType.Int32], (f, a) => {
6080+
const x = f.getParameter(0);
6081+
const y = f.getParameter(1);
6082+
a.get_local(x);
6083+
a.get_local(y);
6084+
a.gt_i32();
6085+
a.if(ValueType.Int32);
6086+
a.get_local(x);
6087+
a.else();
6088+
a.get_local(y);
6089+
a.end();
6090+
}).withExport();
6091+
6092+
// Get full WAT via TextModuleWriter
6093+
const wat = mod.toString();
6094+
log('Full WAT:');
6095+
log(wat);
6096+
6097+
// Get binary and inspect
6098+
const bytes = mod.toBytes();
6099+
log('');
6100+
log('Binary size: ' + bytes.length + ' bytes');
6101+
log('Magic: 0x' + Array.from(bytes.slice(0, 4)).map(b => b.toString(16).padStart(2, '0')).join(''));
6102+
log('Version: ' + (bytes[4] | (bytes[5] << 8) | (bytes[6] << 16) | (bytes[7] << 24)));
6103+
log('');
6104+
log('Click "Explore" on the WAT card to inspect the binary structure!');`,
6105+
},
6106+
6107+
'binary-structure': {
6108+
label: 'Binary Structure',
6109+
group: 'Debug',
6110+
description: 'Build a module and inspect its raw binary structure section by section.',
6111+
target: 'mvp',
6112+
features: [],
6113+
imports: ['ModuleBuilder', 'ValueType', 'BinaryReader'],
6114+
code: `// Binary Structure — inspect the section layout of a WASM binary
6115+
const mod = new ModuleBuilder('sections');
6116+
6117+
mod.defineMemory(1, 4);
6118+
mod.defineGlobal(ValueType.Int32, true, 0);
6119+
6120+
mod.defineFunction('store', [],
6121+
[ValueType.Int32, ValueType.Int32], (f, a) => {
6122+
a.get_local(f.getParameter(0));
6123+
a.get_local(f.getParameter(1));
6124+
a.store_i32();
6125+
}).withExport();
6126+
6127+
mod.defineFunction('load', [ValueType.Int32],
6128+
[ValueType.Int32], (f, a) => {
6129+
a.get_local(f.getParameter(0));
6130+
a.load_i32();
6131+
}).withExport();
6132+
6133+
const bytes = mod.toBytes();
6134+
const reader = new BinaryReader(bytes);
6135+
const info = reader.read();
6136+
6137+
const sectionNames = {
6138+
1: 'Type', 2: 'Import', 3: 'Function', 4: 'Table', 5: 'Memory',
6139+
6: 'Global', 7: 'Export', 8: 'Start', 9: 'Element', 10: 'Code',
6140+
11: 'Data', 12: 'DataCount', 0: 'Custom',
6141+
};
6142+
6143+
log('Module version: ' + info.version);
6144+
log('Total size: ' + bytes.length + ' bytes');
6145+
log('');
6146+
log('Types: ' + info.types.length);
6147+
log('Functions: ' + info.functions.length);
6148+
log('Memories: ' + info.memories.length);
6149+
log('Globals: ' + info.globals.length);
6150+
log('Exports: ' + info.exports.length);
6151+
log('');
6152+
6153+
// Parse sections manually
6154+
let offset = 8;
6155+
while (offset < bytes.length) {
6156+
const sectionId = bytes[offset];
6157+
let sizeOffset = offset + 1;
6158+
let sectionSize = 0;
6159+
let shift = 0;
6160+
while (sizeOffset < bytes.length) {
6161+
const byte = bytes[sizeOffset++];
6162+
sectionSize |= (byte & 0x7f) << shift;
6163+
shift += 7;
6164+
if (!(byte & 0x80)) break;
6165+
}
6166+
const name = sectionNames[sectionId] || 'Unknown(' + sectionId + ')';
6167+
log(name + ' section: offset 0x' + offset.toString(16) + ', ' + sectionSize + ' bytes');
6168+
offset = sizeOffset + sectionSize;
6169+
}`,
6170+
},
6171+
6172+
'custom-metadata': {
6173+
label: 'Structured Custom Section',
6174+
group: 'Debug',
6175+
description: 'Embed structured metadata in a custom section and read it back.',
6176+
target: 'mvp',
6177+
features: [],
6178+
imports: ['ModuleBuilder', 'ValueType', 'BinaryReader'],
6179+
code: `// Structured Custom Section — embed and read back build metadata
6180+
const mod = new ModuleBuilder('app');
6181+
6182+
mod.defineFunction('main', [], [], (f, a) => {
6183+
a.nop();
6184+
}).withExport();
6185+
6186+
// Embed build metadata as a custom section
6187+
const metadata = {
6188+
buildTime: new Date().toISOString(),
6189+
compiler: 'webasmjs playground',
6190+
version: '1.0.0',
6191+
features: ['debug-names', 'custom-sections'],
6192+
};
6193+
6194+
const jsonStr = JSON.stringify(metadata);
6195+
const encoder = new TextEncoder();
6196+
const metadataBytes = encoder.encode(jsonStr);
6197+
mod.defineCustomSection('build-metadata', metadataBytes);
6198+
6199+
// Also add a producers-style section
6200+
const producerStr = 'webasmjs;1.0.0';
6201+
mod.defineCustomSection('source-info', encoder.encode(producerStr));
6202+
6203+
const bytes = mod.toBytes();
6204+
const reader = new BinaryReader(bytes);
6205+
const info = reader.read();
6206+
6207+
log('Custom sections found: ' + info.customSections.length);
6208+
log('');
6209+
for (const section of info.customSections) {
6210+
if (section.name === 'name') { continue; }
6211+
log('Section: "' + section.name + '" (' + section.data.length + ' bytes)');
6212+
const decoder = new TextDecoder();
6213+
const content = decoder.decode(section.data);
6214+
try {
6215+
const parsed = JSON.parse(content);
6216+
log(' ' + JSON.stringify(parsed, null, 2));
6217+
} catch {
6218+
log(' ' + content);
6219+
}
6220+
log('');
6221+
}
6222+
log('Click "Explore" to see the custom sections in the explorer!');`,
6223+
},
6224+
59916225
// ─── Imports ───
59926226

59936227
'import-memory': {

0 commit comments

Comments
 (0)