Repository: WebAssembly/binaryen
Pull request #7525: Migrating from `getExpressionInfo` to expression wrappers
Author: login: GulgDev
Created: 2025-04-18T07:12:41Z | Merged: 2025-04-23T17:05:25Z

Replace the obsolete `getExpressionInfo` with expression wrapper functions.


Diff:
diff --git a/src/js/binaryen.js-post.js b/src/js/binaryen.js-post.js
index 4695a5a4d85..1743c97388e 100644
--- a/src/js/binaryen.js-post.js
+++ b/src/js/binaryen.js-post.js
@@ -3058,600 +3058,48 @@ Module['getExpressionType'] = function(expr) {
 Module['getExpressionInfo'] = function(expr) {
   const id = Module['_BinaryenExpressionGetId'](expr);
   const type = Module['_BinaryenExpressionGetType'](expr);
+  const info = { id, type };
   switch (id) {
-    case Module['BlockId']: {
-      const name = Module['_BinaryenBlockGetName'](expr);
-      return {
-        'id': id,
-        'type': type,
-        'name': name ? UTF8ToString(name) : null,
-        'children': getAllNested(expr, Module['_BinaryenBlockGetNumChildren'], Module['_BinaryenBlockGetChildAt'])
-      };
-    }
-    case Module['IfId']:
-      return {
-        'id': id,
-        'type': type,
-        'condition': Module['_BinaryenIfGetCondition'](expr),
-        'ifTrue': Module['_BinaryenIfGetIfTrue'](expr),
-        'ifFalse': Module['_BinaryenIfGetIfFalse'](expr)
-      };
-    case Module['LoopId']: {
-      const name = Module['_BinaryenLoopGetName'](expr);
-      return {
-        'id': id,
-        'type': type,
-        'name': name ? UTF8ToString(name) : null,
-        'body': Module['_BinaryenLoopGetBody'](expr)
-      };
-    }
-    case Module['BreakId']: {
-      const name = Module['_BinaryenBreakGetName'](expr);
-      return {
-        'id': id,
-        'type': type,
-        'name': name ? UTF8ToString(name) : null,
-        'condition': Module['_BinaryenBreakGetCondition'](expr),
-        'value': Module['_BinaryenBreakGetValue'](expr)
-      };
-    }
-    case Module['SwitchId']:
-      return {
-        'id': id,
-        'type': type,
-         // Do not pass the index as the second parameter to UTF8ToString as that will cut off the string.
-        'names': getAllNested(expr, Module['_BinaryenSwitchGetNumNames'], Module['_BinaryenSwitchGetNameAt']).map(p => UTF8ToString(p)),
-        'defaultName': UTF8ToString(Module['_BinaryenSwitchGetDefaultName'](expr)),
-        'condition': Module['_BinaryenSwitchGetCondition'](expr),
-        'value': Module['_BinaryenSwitchGetValue'](expr)
-      };
-    case Module['CallId']:
-      return {
-        'id': id,
-        'type': type,
-        'isReturn': Boolean(Module['_BinaryenCallIsReturn'](expr)),
-        'target': UTF8ToString(Module['_BinaryenCallGetTarget'](expr)),
-        'operands': getAllNested(expr, Module[ '_BinaryenCallGetNumOperands'], Module['_BinaryenCallGetOperandAt'])
-      };
-    case Module['CallIndirectId']:
-      return {
-        'id': id,
-        'type': type,
-        'isReturn': Boolean(Module['_BinaryenCallIndirectIsReturn'](expr)),
-        'target': Module['_BinaryenCallIndirectGetTarget'](expr),
-        'table': UTF8ToString(Module['_BinaryenCallIndirectGetTable'](expr)),
-        'operands': getAllNested(expr, Module['_BinaryenCallIndirectGetNumOperands'], Module['_BinaryenCallIndirectGetOperandAt']),
-        'params': Module['_BinaryenCallIndirectGetParams'](expr),
-        'results': Module['_BinaryenCallIndirectGetResults'](expr)
-      };
-    case Module['LocalGetId']:
-      return {
-        'id': id,
-        'type': type,
-        'index': Module['_BinaryenLocalGetGetIndex'](expr)
-      };
-    case Module['LocalSetId']:
-      return {
-        'id': id,
-        'type': type,
-        'isTee': Boolean(Module['_BinaryenLocalSetIsTee'](expr)),
-        'index': Module['_BinaryenLocalSetGetIndex'](expr),
-        'value': Module['_BinaryenLocalSetGetValue'](expr)
-      };
-    case Module['GlobalGetId']:
-      return {
-        'id': id,
-        'type': type,
-        'name': UTF8ToString(Module['_BinaryenGlobalGetGetName'](expr))
-      };
-    case Module['GlobalSetId']:
-      return {
-        'id': id,
-        'type': type,
-        'name': UTF8ToString(Module['_BinaryenGlobalSetGetName'](expr)),
-        'value': Module['_BinaryenGlobalSetGetValue'](expr)
-      };
-    case Module['TableGetId']:
-      return {
-        'id': id,
-        'type': type,
-        'table': UTF8ToString(Module['_BinaryenTableGetGetTable'](expr)),
-        'index': Module['_BinaryenTableGetGetIndex'](expr)
-      };
-    case Module['TableSetId']:
-      return {
-        'id': id,
-        'type': type,
-        'table': UTF8ToString(Module['_BinaryenTableSetGetTable'](expr)),
-        'index': Module['_BinaryenTableSetGetIndex'](expr),
-        'value': Module['_BinaryenTableSetGetValue'](expr)
-      };
-    case Module['TableSizeId']:
-      return {
-        'id': id,
-        'type': type,
-        'table': UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr)),
-      };
-    case Module['TableGrowId']:
-      return {
-        'id': id,
-        'type': type,
-        'table': UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr)),
-        'value': Module['_BinaryenTableGrowGetValue'](expr),
-        'delta': Module['_BinaryenTableGrowGetDelta'](expr),
-      };
-    case Module['LoadId']:
-      return {
-        'id': id,
-        'type': type,
-        'isAtomic': Boolean(Module['_BinaryenLoadIsAtomic'](expr)),
-        'isSigned': Boolean(Module['_BinaryenLoadIsSigned'](expr)),
-        'offset': Module['_BinaryenLoadGetOffset'](expr),
-        'bytes': Module['_BinaryenLoadGetBytes'](expr),
-        'align': Module['_BinaryenLoadGetAlign'](expr),
-        'ptr': Module['_BinaryenLoadGetPtr'](expr)
-      };
-    case Module['StoreId']:
-      return {
-        'id': id,
-        'type': type,
-        'isAtomic': Boolean(Module['_BinaryenStoreIsAtomic'](expr)),
-        'offset': Module['_BinaryenStoreGetOffset'](expr),
-        'bytes': Module['_BinaryenStoreGetBytes'](expr),
-        'align': Module['_BinaryenStoreGetAlign'](expr),
-        'ptr': Module['_BinaryenStoreGetPtr'](expr),
-        'value': Module['_BinaryenStoreGetValue'](expr),
-        'valueType': Module['_BinaryenStoreGetValueType'](expr)
-      };
-    case Module['ConstId']: {
-      let value;
+    case Module['ConstId']:
       switch (type) {
-        case Module['i32']: value = Module['_BinaryenConstGetValueI32'](expr); break;
-        case Module['i64']: value = {
+        case Module['i32']: info.value = Module['_BinaryenConstGetValueI32'](expr); break;
+        case Module['i64']: info.value = {
           'low':  Module['_BinaryenConstGetValueI64Low'](expr),
           'high': Module['_BinaryenConstGetValueI64High'](expr)
         }; break;
-        case Module['f32']: value = Module['_BinaryenConstGetValueF32'](expr); break;
-        case Module['f64']: value = Module['_BinaryenConstGetValueF64'](expr); break;
+        case Module['f32']: info.value = Module['_BinaryenConstGetValueF32'](expr); break;
+        case Module['f64']: info.value = Module['_BinaryenConstGetValueF64'](expr); break;
         case Module['v128']: {
           preserveStack(() => {
             const tempBuffer = stackAlloc(16);
             Module['_BinaryenConstGetValueV128'](expr, tempBuffer);
-            value = new Array(16);
+            info.value = new Array(16);
             for (let i = 0; i < 16; i++) {
-              value[i] = HEAPU8[tempBuffer + i];
+              info.value[i] = HEAPU8[tempBuffer + i];
             }
           });
           break;
         }
         default: throw Error('unexpected type: ' + type);
       }
-      return {
-        'id': id,
-        'type': type,
-        'value': value
-      };
-    }
-    case Module['UnaryId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenUnaryGetOp'](expr),
-        'value': Module['_BinaryenUnaryGetValue'](expr)
-      };
-    case Module['BinaryId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenBinaryGetOp'](expr),
-        'left': Module['_BinaryenBinaryGetLeft'](expr),
-        'right':  Module['_BinaryenBinaryGetRight'](expr)
-      };
-    case Module['SelectId']:
-      return {
-        'id': id,
-        'type': type,
-        'ifTrue': Module['_BinaryenSelectGetIfTrue'](expr),
-        'ifFalse': Module['_BinaryenSelectGetIfFalse'](expr),
-        'condition': Module['_BinaryenSelectGetCondition'](expr)
-      };
-    case Module['DropId']:
-      return {
-        'id': id,
-        'type': type,
-        'value': Module['_BinaryenDropGetValue'](expr)
-      };
-    case Module['ReturnId']:
-      return {
-        'id': id,
-        'type': type,
-        'value': Module['_BinaryenReturnGetValue'](expr)
-      };
-    case Module['NopId']:
-    case Module['UnreachableId']:
-    case Module['PopId']:
-      return {
-        'id': id,
-        'type': type
-      };
-    case Module['MemorySizeId']:
-      return {
-        'id': id,
-        'type': type
-      };
-    case Module['MemoryGrowId']:
-      return {
-        'id': id,
-        'type': type,
-        'delta': Module['_BinaryenMemoryGrowGetDelta'](expr)
-      }
-    case Module['AtomicRMWId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenAtomicRMWGetOp'](expr),
-        'bytes': Module['_BinaryenAtomicRMWGetBytes'](expr),
-        'offset': Module['_BinaryenAtomicRMWGetOffset'](expr),
-        'ptr': Module['_BinaryenAtomicRMWGetPtr'](expr),
-        'value': Module['_BinaryenAtomicRMWGetValue'](expr)
-      };
-    case Module['AtomicCmpxchgId']:
-      return {
-        'id': id,
-        'type': type,
-        'bytes': Module['_BinaryenAtomicCmpxchgGetBytes'](expr),
-        'offset': Module['_BinaryenAtomicCmpxchgGetOffset'](expr),
-        'ptr': Module['_BinaryenAtomicCmpxchgGetPtr'](expr),
-        'expected': Module['_BinaryenAtomicCmpxchgGetExpected'](expr),
-        'replacement': Module['_BinaryenAtomicCmpxchgGetReplacement'](expr)
-      };
-    case Module['AtomicWaitId']:
-      return {
-        'id': id,
-        'type': type,
-        'ptr': Module['_BinaryenAtomicWaitGetPtr'](expr),
-        'expected': Module['_BinaryenAtomicWaitGetExpected'](expr),
-        'timeout': Module['_BinaryenAtomicWaitGetTimeout'](expr),
-        'expectedType': Module['_BinaryenAtomicWaitGetExpectedType'](expr)
-      };
-    case Module['AtomicNotifyId']:
-      return {
-        'id': id,
-        'type': type,
-        'ptr': Module['_BinaryenAtomicNotifyGetPtr'](expr),
-        'notifyCount': Module['_BinaryenAtomicNotifyGetNotifyCount'](expr)
-      };
-    case Module['AtomicFenceId']:
-      return {
-        'id': id,
-        'type': type,
-        'order': Module['_BinaryenAtomicFenceGetOrder'](expr)
-      };
-    case Module['SIMDExtractId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDExtractGetOp'](expr),
-        'vec': Module['_BinaryenSIMDExtractGetVec'](expr),
-        'index': Module['_BinaryenSIMDExtractGetIndex'](expr)
-      };
-    case Module['SIMDReplaceId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDReplaceGetOp'](expr),
-        'vec': Module['_BinaryenSIMDReplaceGetVec'](expr),
-        'index': Module['_BinaryenSIMDReplaceGetIndex'](expr),
-        'value': Module['_BinaryenSIMDReplaceGetValue'](expr)
-      };
-    case Module['SIMDShuffleId']:
-      return preserveStack(() => {
-        const tempBuffer = stackAlloc(16);
-        Module['_BinaryenSIMDShuffleGetMask'](expr, tempBuffer);
-        const mask = new Array(16);
-        for (let i = 0; i < 16; i++) {
-          mask[i] = HEAPU8[tempBuffer + i];
+      break;
+    default: {
+      const staticMembers = expressionWrappers[id];
+      Object.keys(staticMembers).forEach(memberName => {
+        const member = staticMembers[memberName];
+        if (typeof member === "function") {
+          let match;
+          if (member.length === 1 && (match = memberName.match(/(^get|^(?=is|has))/))) {
+            const index = match[1].length;
+            const propertyName = memberName.charAt(index).toLowerCase() + memberName.substring(index + 1);
+            info[propertyName] = member(expr);
+          }
         }
-        return {
-          'id': id,
-          'type': type,
-          'left': Module['_BinaryenSIMDShuffleGetLeft'](expr),
-          'right': Module['_BinaryenSIMDShuffleGetRight'](expr),
-          'mask': mask
-        };
       });
-    case Module['SIMDTernaryId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDTernaryGetOp'](expr),
-        'a': Module['_BinaryenSIMDTernaryGetA'](expr),
-        'b': Module['_BinaryenSIMDTernaryGetB'](expr),
-        'c': Module['_BinaryenSIMDTernaryGetC'](expr)
-      };
-    case Module['SIMDShiftId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDShiftGetOp'](expr),
-        'vec': Module['_BinaryenSIMDShiftGetVec'](expr),
-        'shift': Module['_BinaryenSIMDShiftGetShift'](expr)
-      };
-    case Module['SIMDLoadId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDLoadGetOp'](expr),
-        'offset': Module['_BinaryenSIMDLoadGetOffset'](expr),
-        'align': Module['_BinaryenSIMDLoadGetAlign'](expr),
-        'ptr': Module['_BinaryenSIMDLoadGetPtr'](expr)
-      };
-    case Module['SIMDLoadStoreLaneId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenSIMDLoadStoreLaneGetOp'](expr),
-        'offset': Module['_BinaryenSIMDLoadStoreLaneGetOffset'](expr),
-        'align': Module['_BinaryenSIMDLoadStoreLaneGetAlign'](expr),
-        'index': Module['_BinaryenSIMDLoadStoreLaneGetIndex'](expr),
-        'ptr': Module['_BinaryenSIMDLoadStoreLaneGetPtr'](expr),
-        'vec': Module['_BinaryenSIMDLoadStoreLaneGetVec'](expr),
-        'isStore': Boolean(Module['_BinaryenSIMDLoadStoreLaneIsStore'](expr))
-      };
-    case Module['MemoryInitId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenMemoryInitGetSegment'](expr)),
-        'dest': Module['_BinaryenMemoryInitGetDest'](expr),
-        'offset': Module['_BinaryenMemoryInitGetOffset'](expr),
-        'size': Module['_BinaryenMemoryInitGetSize'](expr)
-      };
-    case Module['DataDropId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenDataDropGetSegment'](expr)),
-      };
-    case Module['MemoryCopyId']:
-      return {
-        'id': id,
-        'type': type,
-        'dest': Module['_BinaryenMemoryCopyGetDest'](expr),
-        'source': Module['_BinaryenMemoryCopyGetSource'](expr),
-        'size': Module['_BinaryenMemoryCopyGetSize'](expr)
-      };
-    case Module['MemoryFillId']:
-      return {
-        'id': id,
-        'type': type,
-        'dest': Module['_BinaryenMemoryFillGetDest'](expr),
-        'value': Module['_BinaryenMemoryFillGetValue'](expr),
-        'size': Module['_BinaryenMemoryFillGetSize'](expr)
-      };
-    case Module['RefNullId']:
-      return {
-        'id': id,
-        'type': type
-      };
-    case Module['RefIsNullId']:
-      return {
-        'id': id,
-        'type': type,
-        'value': Module['_BinaryenRefIsNullGetValue'](expr)
-      };
-    case Module['RefAsId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenRefAsGetOp'](expr),
-        'value': Module['_BinaryenRefAsGetValue'](expr)
-      };
-    case Module['RefFuncId']:
-      return {
-        'id': id,
-        'type': type,
-        'func': UTF8ToString(Module['_BinaryenRefFuncGetFunc'](expr)),
-      };
-    case Module['RefEqId']:
-      return {
-        'id': id,
-        'type': type,
-        'left': Module['_BinaryenRefEqGetLeft'](expr),
-        'right': Module['_BinaryenRefEqGetRight'](expr)
-      };
-    case Module['RefTestId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenRefTestGetRef'](expr),
-        'castType': Module['_BinaryenRefTestGetCastType'](expr)
-      };
-    case Module['RefCastId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenRefCastGetRef'](expr)
-      };
-    case Module['BrOnId']:
-      return {
-        'id': id,
-        'type': type,
-        'op': Module['_BinaryenBrOnGetOp'](expr),
-        'name': UTF8ToString(Module['_BinaryenBrOnGetName'](expr)),
-        'ref': Module['_BinaryenBrOnGetRef'](expr),
-        'castType': Module['_BinaryenBrOnGetCastType'](expr)
-      };
-    case Module['StructNewId']:
-      return {
-        'id': id,
-        'type': type,
-        'operands': getAllNested(expr, Module['_BinaryenStructNewGetNumOperands'], Module['_BinaryenStructNewGetOperandAt']),
-      };
-    case Module['StructGetId']:
-      return {
-        'id': id,
-        'type': type,
-        'index': Module['_BinaryenStructGetGetIndex'](expr),
-        'ref': Module['_BinaryenStructGetGetRef'](expr),
-        'isSigned': Boolean(Module['_BinaryenStructGetIsSigned'](expr))
-      };
-    case Module['StructSetId']:
-      return {
-        'id': id,
-        'type': type,
-        'index': Module['_BinaryenStructSetGetIndex'](expr),
-        'ref': Module['_BinaryenStructSetGetRef'](expr),
-        'value': Module['_BinaryenStructSetGetValue'](expr)
-      };
-    case Module['ArrayNewId']:
-      return {
-        'id': id,
-        'type': type,
-        'init': Module['_BinaryenArrayNewGetInit'](expr),
-        'size': Module['_BinaryenArrayNewGetSize'](expr)
-      };
-    case Module['ArrayNewFixedId']:
-      return {
-        'id': id,
-        'type': type,
-        'values': getAllNested(expr, Module['_BinaryenArrayNewFixedGetNumValues'], Module['_BinaryenArrayNewFixedGetValueAt'])
-      };
-    case Module['ArrayNewDataId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenArrayNewDataGetSegment'](expr)),
-        'offset': Module['_BinaryenArrayNewDataGetOffset'](expr),
-        'size': Module['_BinaryenArrayNewDataGetSize'](expr)
-      };
-    case Module['ArrayNewElemId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenArrayNewElemGetSegment'](expr)),
-        'offset': Module['_BinaryenArrayNewElemGetOffset'](expr),
-        'size': Module['_BinaryenArrayNewElemGetSize'](expr)
-      };
-    case Module['ArrayGetId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenArrayGetGetRef'](expr),
-        'index': Module['_BinaryenArrayGetGetIndex'](expr),
-        'isSigned': Boolean(Module['_BinaryenArrayGetIsSigned'](expr))
-      };
-    case Module['ArraySetId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenArraySetGetRef'](expr),
-        'index': Module['_BinaryenArraySetGetIndex'](expr),
-        'value': Module['_BinaryenArraySetGetValue'](expr)
-      };
-    case Module['ArrayLenId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenArrayLenGetRef'](expr)
-      };
-    case Module['ArrayFillId']:
-      return {
-        'id': id,
-        'type': type,
-        'ref': Module['_BinaryenArrayFillGetRef'](expr),
-        'index': Module['_BinaryenArrayFillGetIndex'](expr),
-        'value': Module['_BinaryenArrayFillGetValue'](expr),
-        'size': Module['_BinaryenArrayFillGetSize'](expr)
-      };
-    case Module['ArrayCopyId']:
-      return {
-        'id': id,
-        'type': type,
-        'destRef': Module['_BinaryenArrayCopyGetDestRef'](expr),
-        'destIndex': Module['_BinaryenArrayCopyGetDestIndex'](expr),
-        'srcRef': Module['_BinaryenArrayCopyGetSrcRef'](expr),
-        'srcIndex': Module['_BinaryenArrayCopyGetSrcIndex'](expr),
-        'length': Module['_BinaryenArrayCopyGetLength'](expr)
-      };
-    case Module['ArrayInitDataId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenArrayInitDataGetSegment'](expr)),
-        'ref': Module['_BinaryenArrayInitDataGetRef'](expr),
-        'index': Module['_BinaryenArrayInitDataGetIndex'](expr),
-        'offset': Module['_BinaryenArrayInitDataGetOffset'](expr),
-        'size': Module['_BinaryenArrayInitDataGetSize'](expr)
-      };
-    case Module['ArrayInitElemId']:
-      return {
-        'id': id,
-        'type': type,
-        'segment': UTF8ToString(Module['_BinaryenArrayInitElemGetSegment'](expr)),
-        'ref': Module['_BinaryenArrayInitElemGetRef'](expr),
-        'index': Module['_BinaryenArrayInitElemGetIndex'](expr),
-        'offset': Module['_BinaryenArrayInitElemGetOffset'](expr),
-        'size': Module['_BinaryenArrayInitElemGetSize'](expr)
-      };
-    case Module['TryId']: {
-      const name = Module['_BinaryenTryGetName'](expr);
-      const delegateTarget = Module['_BinaryenTryGetDelegateTarget'](expr);
-      return {
-        'id': id,
-        'type': type,
-        'name': name ? UTF8ToString(name) : null,
-        'body': Module['_BinaryenTryGetBody'](expr),
-        'catchTags': getAllNested(expr, Module['_BinaryenTryGetNumCatchTags'], Module['_BinaryenTryGetCatchTagAt']).map(p => UTF8ToString(p)),
-        'catchBodies': getAllNested(expr, Module['_BinaryenTryGetNumCatchBodies'], Module['_BinaryenTryGetCatchBodyAt']),
-        'hasCatchAll': Boolean(Module['_BinaryenTryHasCatchAll'](expr)),
-        'delegateTarget': delegateTarget ? UTF8ToString(delegateTarget) : null,
-        'isDelegate': Boolean(Module['_BinaryenTryIsDelegate'](expr))
-      };
+      break;
     }
-    case Module['ThrowId']:
-      return {
-        'id': id,
-        'type': type,
-        'tag': UTF8ToString(Module['_BinaryenThrowGetTag'](expr)),
-        'operands': getAllNested(expr, Module['_BinaryenThrowGetNumOperands'], Module['_BinaryenThrowGetOperandAt'])
-      };
-    case Module['RethrowId']:
-      return {
-        'id': id,
-        'type': type,
-        'target': UTF8ToString(Module['_BinaryenRethrowGetTarget'](expr))
-      };
-    case Module['TupleMakeId']:
-      return {
-        'id': id,
-        'type': type,
-        'operands': getAllNested(expr, Module['_BinaryenTupleMakeGetNumOperands'], Module['_BinaryenTupleMakeGetOperandAt'])
-      };
-    case Module['TupleExtractId']:
-      return {
-        'id': id,
-        'type': type,
-        'tuple': Module['_BinaryenTupleExtractGetTuple'](expr),
-        'index': Module['_BinaryenTupleExtractGetIndex'](expr)
-      };
-    case Module['RefI31Id']:
-      return {
-        'id': id,
-        'type': type,
-        'value': Module['_BinaryenRefI31GetValue'](expr)
-      };
-    case Module['I31GetId']:
-      return {
-        'id': id,
-        'type': type,
-        'i31': Module['_BinaryenI31GetGetI31'](expr),
-        'isSigned': Boolean(Module['_BinaryenI31GetIsSigned'](expr))
-      };
-
-    default:
-      throw Error('unexpected id: ' + id);
   }
+  return info;
 };
 
 // Gets the side effects of the specified expression
@@ -3979,12 +3427,15 @@ Module['setAllowInliningFunctionsWithLoops'] = function(value) {
 
 // Expression wrappers
 
+// Expression ID-to-wrapper map
+let expressionWrappers = {};
+
 // Private symbol used to store the underlying C-API pointer of a wrapped object.
 const thisPtr = Symbol();
 
 // Makes a specific expression wrapper class with the specified static members
 // while automatically deriving instance methods and accessors.
-function makeExpressionWrapper(ownStaticMembers) {
+function makeExpressionWrapper(expressionId, ownStaticMembers) {
   /**
    * @constructor
    * @extends Expression
@@ -4005,6 +3456,8 @@ function makeExpressionWrapper(ownStaticMembers) {
   (SpecificExpression.prototype = Object.create(Expression.prototype)).constructor = SpecificExpression;
   // derive own instance members
   deriveWrapperInstanceMembers(SpecificExpression.prototype, ownStaticMembers);
+  // register the expression wrapper
+  expressionWrappers[expressionId] = SpecificExpression;
   return SpecificExpression;
 }
 
@@ -4052,6 +3505,12 @@ function deriveWrapperInstanceMembers(prototype, staticMembers) {
 // Base class of all expression wrappers
 /** @constructor */
 function Expression(expr) {
+  // Returns the specific wrapper if called without `new`
+  if (!(this instanceof Expression)) {
+    if (!expr) return null;
+    const id = Module['_BinaryenExpressionGetId'](expr);
+    return expressionWrappers[id](expr);
+  }
   if (!expr) throw Error("expression reference must not be null");
   this[thisPtr] = expr;
 }
@@ -4077,7 +3536,7 @@ Expression.prototype['valueOf'] = function() {
 
 Module['Expression'] = Expression;
 
-Module['Block'] = makeExpressionWrapper({
+Module['Block'] = makeExpressionWrapper(Module['_BinaryenBlockId'](), {
   'getName'(expr) {
     const name = Module['_BinaryenBlockGetName'](expr);
     return name ? UTF8ToString(name) : null;
@@ -4111,7 +3570,7 @@ Module['Block'] = makeExpressionWrapper({
   }
 });
 
-Module['If'] = makeExpressionWrapper({
+Module['If'] = makeExpressionWrapper(Module['_BinaryenIfId'](), {
   'getCondition'(expr) {
     return Module['_BinaryenIfGetCondition'](expr);
   },
@@ -4132,7 +3591,7 @@ Module['If'] = makeExpressionWrapper({
   }
 });
 
-Module['Loop'] = makeExpressionWrapper({
+Module['Loop'] = makeExpressionWrapper(Module['_BinaryenLoopId'](), {
   'getName'(expr) {
     const name = Module['_BinaryenLoopGetName'](expr);
     return name ? UTF8ToString(name) : null;
@@ -4148,7 +3607,7 @@ Module['Loop'] = makeExpressionWrapper({
   }
 });
 
-Module['Break'] = makeExpressionWrapper({
+Module['Break'] = makeExpressionWrapper(Module['_BinaryenBreakId'](), {
   'getName'(expr) {
     const name = Module['_BinaryenBreakGetName'](expr);
     return name ? UTF8ToString(name) : null;
@@ -4170,7 +3629,7 @@ Module['Break'] = makeExpressionWrapper({
   }
 });
 
-Module['Switch'] = makeExpressionWrapper({
+Module['Switch'] = makeExpressionWrapper(Module['_BinaryenSwitchId'](), {
   'getNumNames'(expr) {
     return Module['_BinaryenSwitchGetNumNames'](expr);
   },
@@ -4218,7 +3677,7 @@ Module['Switch'] = makeExpressionWrapper({
   },
 });
 
-Module['Call'] = makeExpressionWrapper({
+Module['Call'] = makeExpressionWrapper(Module['_BinaryenCallId'](), {
   'getTarget'(expr) {
     return UTF8ToString(Module['_BinaryenCallGetTarget'](expr));
   },
@@ -4257,7 +3716,7 @@ Module['Call'] = makeExpressionWrapper({
   }
 });
 
-Module['CallIndirect'] = makeExpressionWrapper({
+Module['CallIndirect'] = makeExpressionWrapper(Module['_BinaryenCallIndirectId'](), {
   'getTarget'(expr) {
     return Module['_BinaryenCallIndirectGetTarget'](expr);
   },
@@ -4314,7 +3773,7 @@ Module['CallIndirect'] = makeExpressionWrapper({
   }
 });
 
-Module['LocalGet'] = makeExpressionWrapper({
+Module['LocalGet'] = makeExpressionWrapper(Module['_BinaryenLocalGetId'](), {
   'getIndex'(expr) {
     return Module['_BinaryenLocalGetGetIndex'](expr);
   },
@@ -4323,7 +3782,7 @@ Module['LocalGet'] = makeExpressionWrapper({
   }
 });
 
-Module['LocalSet'] = makeExpressionWrapper({
+Module['LocalSet'] = makeExpressionWrapper(Module['_BinaryenLocalSetId'](), {
   'getIndex'(expr) {
     return Module['_BinaryenLocalSetGetIndex'](expr);
   },
@@ -4341,7 +3800,7 @@ Module['LocalSet'] = makeExpressionWrapper({
   }
 });
 
-Module['GlobalGet'] = makeExpressionWrapper({
+Module['GlobalGet'] = makeExpressionWrapper(Module['_BinaryenGlobalGetId'](), {
   'getName'(expr) {
     return UTF8ToString(Module['_BinaryenGlobalGetGetName'](expr));
   },
@@ -4350,7 +3809,7 @@ Module['GlobalGet'] = makeExpressionWrapper({
   }
 });
 
-Module['GlobalSet'] = makeExpressionWrapper({
+Module['GlobalSet'] = makeExpressionWrapper(Module['_BinaryenGlobalSetId'](), {
   'getName'(expr) {
     return UTF8ToString(Module['_BinaryenGlobalSetGetName'](expr));
   },
@@ -4365,7 +3824,7 @@ Module['GlobalSet'] = makeExpressionWrapper({
   }
 });
 
-Module['TableGet'] = makeExpressionWrapper({
+Module['TableGet'] = makeExpressionWrapper(Module['_BinaryenTableGetId'](), {
   'getTable'(expr) {
     return UTF8ToString(Module['_BinaryenTableGetGetTable'](expr));
   },
@@ -4380,7 +3839,7 @@ Module['TableGet'] = makeExpressionWrapper({
   }
 });
 
-Module['TableSet'] = makeExpressionWrapper({
+Module['TableSet'] = makeExpressionWrapper(Module['_BinaryenTableSetId'](), {
   'getTable'(expr) {
     return UTF8ToString(Module['_BinaryenTableSetGetTable'](expr));
   },
@@ -4401,7 +3860,7 @@ Module['TableSet'] = makeExpressionWrapper({
   }
 });
 
-Module['TableSize'] = makeExpressionWrapper({
+Module['TableSize'] = makeExpressionWrapper(Module['_BinaryenTableSizeId'](), {
   'getTable'(expr) {
     return UTF8ToString(Module['_BinaryenTableSizeGetTable'](expr));
   },
@@ -4410,7 +3869,7 @@ Module['TableSize'] = makeExpressionWrapper({
   },
 });
 
-Module['TableGrow'] = makeExpressionWrapper({
+Module['TableGrow'] = makeExpressionWrapper(Module['_BinaryenTableGrowId'](), {
   'getTable'(expr) {
     return UTF8ToString(Module['_BinaryenTableGrowGetTable'](expr));
   },
@@ -4431,9 +3890,9 @@ Module['TableGrow'] = makeExpressionWrapper({
   }
 });
 
-Module['MemorySize'] = makeExpressionWrapper({});
+Module['MemorySize'] = makeExpressionWrapper(Module['_BinaryenMemorySizeId'](), {});
 
-Module['MemoryGrow'] = makeExpressionWrapper({
+Module['MemoryGrow'] = makeExpressionWrapper(Module['_BinaryenMemoryGrowId'](), {
   'getDelta'(expr) {
     return Module['_BinaryenMemoryGrowGetDelta'](expr);
   },
@@ -4442,7 +3901,7 @@ Module['MemoryGrow'] = makeExpressionWrapper({
   }
 });
 
-Module['Load'] = makeExpressionWrapper({
+Module['Load'] = makeExpressionWrapper(Module['_BinaryenLoadId'](), {
   'isAtomic'(expr) {
     return Boolean(Module['_BinaryenLoadIsAtomic'](expr));
   },
@@ -4481,7 +3940,7 @@ Module['Load'] = makeExpressionWrapper({
   }
 });
 
-Module['Store'] = makeExpressionWrapper({
+Module['Store'] = makeExpressionWrapper(Module['_BinaryenStoreId'](), {
   'isAtomic'(expr) {
     return Boolean(Module['_BinaryenStoreIsAtomic'](expr));
   },
@@ -4526,7 +3985,7 @@ Module['Store'] = makeExpressionWrapper({
   }
 });
 
-Module['Const'] = makeExpressionWrapper({
+Module['Const'] = makeExpressionWrapper(Module['_BinaryenConstId'](), {
   'getValueI32'(expr) {
     return Module['_BinaryenConstGetValueI32'](expr);
   },
@@ -4580,7 +4039,7 @@ Module['Const'] = makeExpressionWrapper({
   }
 });
 
-Module['Unary'] = makeExpressionWrapper({
+Module['Unary'] = makeExpressionWrapper(Module['_BinaryenUnaryId'](), {
   'getOp'(expr) {
     return Module['_BinaryenUnaryGetOp'](expr);
   },
@@ -4595,7 +4054,7 @@ Module['Unary'] = makeExpressionWrapper({
   }
 });
 
-Module['Binary'] = makeExpressionWrapper({
+Module['Binary'] = makeExpressionWrapper(Module['_BinaryenBinaryId'](), {
   'getOp'(expr) {
     return Module['_BinaryenBinaryGetOp'](expr);
   },
@@ -4616,7 +4075,7 @@ Module['Binary'] = makeExpressionWrapper({
   }
 });
 
-Module['Select'] = makeExpressionWrapper({
+Module['Select'] = makeExpressionWrapper(Module['_BinaryenSelectId'](), {
   'getIfTrue'(expr) {
     return Module['_BinaryenSelectGetIfTrue'](expr);
   },
@@ -4637,7 +4096,7 @@ Module['Select'] = makeExpressionWrapper({
   }
 });
 
-Module['Drop'] = makeExpressionWrapper({
+Module['Drop'] = makeExpressionWrapper(Module['_BinaryenDropId'](), {
   'getValue'(expr) {
     return Module['_BinaryenDropGetValue'](expr);
   },
@@ -4646,7 +4105,7 @@ Module['Drop'] = makeExpressionWrapper({
   }
 });
 
-Module['Return'] = makeExpressionWrapper({
+Module['Return'] = makeExpressionWrapper(Module['_BinaryenReturnId'](), {
   'getValue'(expr) {
     return Module['_BinaryenReturnGetValue'](expr);
   },
@@ -4655,7 +4114,7 @@ Module['Return'] = makeExpressionWrapper({
   }
 });
 
-Module['AtomicRMW'] = makeExpressionWrapper({
+Module['AtomicRMW'] = makeExpressionWrapper(Module['_BinaryenAtomicRMWId'](), {
   'getOp'(expr) {
     return Module['_BinaryenAtomicRMWGetOp'](expr);
   },
@@ -4688,7 +4147,7 @@ Module['AtomicRMW'] = makeExpressionWrapper({
   }
 });
 
-Module['AtomicCmpxchg'] = makeExpressionWrapper({
+Module['AtomicCmpxchg'] = makeExpressionWrapper(Module['_BinaryenAtomicCmpxchgId'](), {
   'getBytes'(expr) {
     return Module['_BinaryenAtomicCmpxchgGetBytes'](expr);
   },
@@ -4721,7 +4180,7 @@ Module['AtomicCmpxchg'] = makeExpressionWrapper({
   }
 });
 
-Module['AtomicWait'] = makeExpressionWrapper({
+Module['AtomicWait'] = makeExpressionWrapper(Module['_BinaryenAtomicWaitId'](), {
   'getPtr'(expr) {
     return Module['_BinaryenAtomicWaitGetPtr'](expr);
   },
@@ -4748,7 +4207,7 @@ Module['AtomicWait'] = makeExpressionWrapper({
   }
 });
 
-Module['AtomicNotify'] = makeExpressionWrapper({
+Module['AtomicNotify'] = makeExpressionWrapper(Module['_BinaryenAtomicNotifyId'](), {
   'getPtr'(expr) {
     return Module['_BinaryenAtomicNotifyGetPtr'](expr);
   },
@@ -4763,7 +4222,7 @@ Module['AtomicNotify'] = makeExpressionWrapper({
   }
 });
 
-Module['AtomicFence'] = makeExpressionWrapper({
+Module['AtomicFence'] = makeExpressionWrapper(Module['_BinaryenAtomicFenceId'](), {
   'getOrder'(expr) {
     return Module['_BinaryenAtomicFenceGetOrder'](expr);
   },
@@ -4772,7 +4231,7 @@ Module['AtomicFence'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDExtract'] = makeExpressionWrapper({
+Module['SIMDExtract'] = makeExpressionWrapper(Module['_BinaryenSIMDExtractId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDExtractGetOp'](expr);
   },
@@ -4793,7 +4252,7 @@ Module['SIMDExtract'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDReplace'] = makeExpressionWrapper({
+Module['SIMDReplace'] = makeExpressionWrapper(Module['_BinaryenSIMDReplaceId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDReplaceGetOp'](expr);
   },
@@ -4820,7 +4279,7 @@ Module['SIMDReplace'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDShuffle'] = makeExpressionWrapper({
+Module['SIMDShuffle'] = makeExpressionWrapper(Module['_BinaryenSIMDShuffleId'](), {
   'getLeft'(expr) {
     return Module['_BinaryenSIMDShuffleGetLeft'](expr);
   },
@@ -4856,7 +4315,7 @@ Module['SIMDShuffle'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDTernary'] = makeExpressionWrapper({
+Module['SIMDTernary'] = makeExpressionWrapper(Module['_BinaryenSIMDTernaryId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDTernaryGetOp'](expr);
   },
@@ -4883,7 +4342,7 @@ Module['SIMDTernary'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDShift'] = makeExpressionWrapper({
+Module['SIMDShift'] = makeExpressionWrapper(Module['_BinaryenSIMDShiftId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDShiftGetOp'](expr);
   },
@@ -4904,7 +4363,7 @@ Module['SIMDShift'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDLoad'] = makeExpressionWrapper({
+Module['SIMDLoad'] = makeExpressionWrapper(Module['_BinaryenSIMDLoadId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDLoadGetOp'](expr);
   },
@@ -4931,7 +4390,7 @@ Module['SIMDLoad'] = makeExpressionWrapper({
   }
 });
 
-Module['SIMDLoadStoreLane'] = makeExpressionWrapper({
+Module['SIMDLoadStoreLane'] = makeExpressionWrapper(Module['_BinaryenSIMDLoadStoreLaneId'](), {
   'getOp'(expr) {
     return Module['_BinaryenSIMDLoadStoreLaneGetOp'](expr);
   },
@@ -4973,7 +4432,7 @@ Module['SIMDLoadStoreLane'] = makeExpressionWrapper({
   }
 });
 
-Module['MemoryInit'] = makeExpressionWrapper({
+Module['MemoryInit'] = makeExpressionWrapper(Module['_BinaryenMemoryInitId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenMemoryInitGetSegment'](expr));
   },
@@ -5000,7 +4459,7 @@ Module['MemoryInit'] = makeExpressionWrapper({
   }
 });
 
-Module['DataDrop'] = makeExpressionWrapper({
+Module['DataDrop'] = makeExpressionWrapper(Module['_BinaryenDataDropId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenDataDropGetSegment'](expr));
   },
@@ -5009,7 +4468,7 @@ Module['DataDrop'] = makeExpressionWrapper({
   }
 });
 
-Module['MemoryCopy'] = makeExpressionWrapper({
+Module['MemoryCopy'] = makeExpressionWrapper(Module['_BinaryenMemoryCopyId'](), {
   'getDest'(expr) {
     return Module['_BinaryenMemoryCopyGetDest'](expr);
   },
@@ -5030,7 +4489,7 @@ Module['MemoryCopy'] = makeExpressionWrapper({
   }
 });
 
-Module['MemoryFill'] = makeExpressionWrapper({
+Module['MemoryFill'] = makeExpressionWrapper(Module['_BinaryenMemoryFillId'](), {
   'getDest'(expr) {
     return Module['_BinaryenMemoryFillGetDest'](expr);
   },
@@ -5051,7 +4510,7 @@ Module['MemoryFill'] = makeExpressionWrapper({
   }
 });
 
-Module['RefIsNull'] = makeExpressionWrapper({
+Module['RefIsNull'] = makeExpressionWrapper(Module['_BinaryenRefIsNullId'](), {
   'getValue'(expr) {
     return Module['_BinaryenRefIsNullGetValue'](expr);
   },
@@ -5060,7 +4519,7 @@ Module['RefIsNull'] = makeExpressionWrapper({
   }
 });
 
-Module['RefAs'] = makeExpressionWrapper({
+Module['RefAs'] = makeExpressionWrapper(Module['_BinaryenRefAsId'](), {
   'getOp'(expr) {
     return Module['_BinaryenRefAsGetOp'](expr);
   },
@@ -5075,7 +4534,7 @@ Module['RefAs'] = makeExpressionWrapper({
   }
 });
 
-Module['RefFunc'] = makeExpressionWrapper({
+Module['RefFunc'] = makeExpressionWrapper(Module['_BinaryenRefFuncId'](), {
   'getFunc'(expr) {
     return UTF8ToString(Module['_BinaryenRefFuncGetFunc'](expr));
   },
@@ -5084,7 +4543,7 @@ Module['RefFunc'] = makeExpressionWrapper({
   }
 });
 
-Module['RefEq'] = makeExpressionWrapper({
+Module['RefEq'] = makeExpressionWrapper(Module['_BinaryenRefEqId'](), {
   'getLeft'(expr) {
     return Module['_BinaryenRefEqGetLeft'](expr);
   },
@@ -5099,7 +4558,7 @@ Module['RefEq'] = makeExpressionWrapper({
   }
 });
 
-Module['RefTest'] = makeExpressionWrapper({
+Module['RefTest'] = makeExpressionWrapper(Module['_BinaryenRefTestId'](), {
   'getRef'(expr) {
     return Module['_BinaryenRefTestGetRef'](expr);
   },
@@ -5114,7 +4573,7 @@ Module['RefTest'] = makeExpressionWrapper({
   }
 });
 
-Module['RefCast'] = makeExpressionWrapper({
+Module['RefCast'] = makeExpressionWrapper(Module['_BinaryenRefCastId'](), {
   'getRef'(expr) {
     return Module['_BinaryenRefCastGetRef'](expr);
   },
@@ -5126,7 +4585,7 @@ Module['RefCast'] = makeExpressionWrapper({
 // TODO: any.convert_extern
 // TODO: extern.convert_any
 
-Module['BrOn'] = makeExpressionWrapper({
+Module['BrOn'] = makeExpressionWrapper(Module['_BinaryenBrOnId'](), {
   'getOp'(expr) {
     return Module['_BinaryenBrOnGetOp'](expr);
   },
@@ -5153,7 +4612,7 @@ Module['BrOn'] = makeExpressionWrapper({
   }
 });
 
-Module['StructNew'] = makeExpressionWrapper({
+Module['StructNew'] = makeExpressionWrapper(Module['_BinaryenStructNewId'](), {
   'getNumOperands'(expr) {
     return Module['_BinaryenStructNewGetNumOperands'](expr);
   },
@@ -5187,7 +4646,7 @@ Module['StructNew'] = makeExpressionWrapper({
   }
 });
 
-Module['StructGet'] = makeExpressionWrapper({
+Module['StructGet'] = makeExpressionWrapper(Module['_BinaryenStructGetId'](), {
   'getIndex'(expr) {
     return Module['_BinaryenStructGetGetIndex'](expr);
   },
@@ -5208,7 +4667,7 @@ Module['StructGet'] = makeExpressionWrapper({
   }
 });
 
-Module['StructSet'] = makeExpressionWrapper({
+Module['StructSet'] = makeExpressionWrapper(Module['_BinaryenStructSetId'](), {
   'getIndex'(expr) {
     return Module['_BinaryenStructSetGetIndex'](expr);
   },
@@ -5229,7 +4688,7 @@ Module['StructSet'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayNew'] = makeExpressionWrapper({
+Module['ArrayNew'] = makeExpressionWrapper(Module['_BinaryenArrayNewId'](), {
   'getInit'(expr) {
     return Module['_BinaryenArrayNewGetInit'](expr);
   },
@@ -5244,7 +4703,7 @@ Module['ArrayNew'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayNewFixed'] = makeExpressionWrapper({
+Module['ArrayNewFixed'] = makeExpressionWrapper(Module['_BinaryenArrayNewFixedId'](), {
   'getNumValues'(expr) {
     return Module['_BinaryenArrayNewFixedGetNumValues'](expr);
   },
@@ -5280,7 +4739,7 @@ Module['ArrayNewFixed'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayNewData'] = makeExpressionWrapper({
+Module['ArrayNewData'] = makeExpressionWrapper(Module['_BinaryenArrayNewDataId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenArrayNewDataGetSegment'](expr));
   },
@@ -5301,7 +4760,7 @@ Module['ArrayNewData'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayNewElem'] = makeExpressionWrapper({
+Module['ArrayNewElem'] = makeExpressionWrapper(Module['_BinaryenArrayNewElemId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenArrayNewElemGetSegment'](expr));
   },
@@ -5322,7 +4781,7 @@ Module['ArrayNewElem'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayGet'] = makeExpressionWrapper({
+Module['ArrayGet'] = makeExpressionWrapper(Module['_BinaryenArrayGetId'](), {
   'getRef'(expr) {
     return Module['_BinaryenArrayGetGetRef'](expr);
   },
@@ -5343,7 +4802,7 @@ Module['ArrayGet'] = makeExpressionWrapper({
   }
 });
 
-Module['ArraySet'] = makeExpressionWrapper({
+Module['ArraySet'] = makeExpressionWrapper(Module['_BinaryenArraySetId'](), {
   'getRef'(expr) {
     return Module['_BinaryenArraySetGetRef'](expr);
   },
@@ -5364,7 +4823,7 @@ Module['ArraySet'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayLen'] = makeExpressionWrapper({
+Module['ArrayLen'] = makeExpressionWrapper(Module['_BinaryenArrayLenId'](), {
   'getRef'(expr) {
     return Module['_BinaryenArrayLenGetRef'](expr);
   },
@@ -5373,7 +4832,7 @@ Module['ArrayLen'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayFill'] = makeExpressionWrapper({
+Module['ArrayFill'] = makeExpressionWrapper(Module['_BinaryenArrayFillId'](), {
   'getRef'(expr) {
     return Module['_BinaryenArrayFillGetRef'](expr);
   },
@@ -5400,7 +4859,7 @@ Module['ArrayFill'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayCopy'] = makeExpressionWrapper({
+Module['ArrayCopy'] = makeExpressionWrapper(Module['_BinaryenArrayCopyId'](), {
   'getDestRef'(expr) {
     return Module['_BinaryenArrayCopyGetDestRef'](expr);
   },
@@ -5433,7 +4892,7 @@ Module['ArrayCopy'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayInitData'] = makeExpressionWrapper({
+Module['ArrayInitData'] = makeExpressionWrapper(Module['_BinaryenArrayInitDataId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenArrayInitDataGetSegment'](expr));
   },
@@ -5466,7 +4925,7 @@ Module['ArrayInitData'] = makeExpressionWrapper({
   }
 });
 
-Module['ArrayInitElem'] = makeExpressionWrapper({
+Module['ArrayInitElem'] = makeExpressionWrapper(Module['_BinaryenArrayInitElemId'](), {
   'getSegment'(expr) {
     return UTF8ToString(Module['_BinaryenArrayInitElemGetSegment'](expr));
   },
@@ -5499,7 +4958,7 @@ Module['ArrayInitElem'] = makeExpressionWrapper({
   }
 });
 
-Module['Try'] = makeExpressionWrapper({
+Module['Try'] = makeExpressionWrapper(Module['_BinaryenTryId'](), {
   'getName'(expr) {
     const name = Module['_BinaryenTryGetName'](expr);
     return name ? UTF8ToString(name) : null;
@@ -5578,7 +5037,7 @@ Module['Try'] = makeExpressionWrapper({
   }
 });
 
-Module['Throw'] = makeExpressionWrapper({
+Module['Throw'] = makeExpressionWrapper(Module['_BinaryenThrowId'](), {
   'getTag'(expr) {
     return UTF8ToString(Module['_BinaryenThrowGetTag'](expr));
   },
@@ -5611,7 +5070,7 @@ Module['Throw'] = makeExpressionWrapper({
   },
 });
 
-Module['Rethrow'] = makeExpressionWrapper({
+Module['Rethrow'] = makeExpressionWrapper(Module['_BinaryenRethrowId'](), {
   'getTarget'(expr) {
     const target = Module['_BinaryenRethrowGetTarget'](expr);
     return target ? UTF8ToString(target) : null;
@@ -5621,7 +5080,7 @@ Module['Rethrow'] = makeExpressionWrapper({
   }
 });
 
-Module['TupleMake'] = makeExpressionWrapper({
+Module['TupleMake'] = makeExpressionWrapper(Module['_BinaryenTupleMakeId'](), {
   'getNumOperands'(expr) {
     return Module['_BinaryenTupleMakeGetNumOperands'](expr);
   },
@@ -5648,7 +5107,7 @@ Module['TupleMake'] = makeExpressionWrapper({
   }
 });
 
-Module['TupleExtract'] = makeExpressionWrapper({
+Module['TupleExtract'] = makeExpressionWrapper(Module['_BinaryenTupleExtractId'](), {
   'getTuple'(expr) {
     return Module['_BinaryenTupleExtractGetTuple'](expr);
   },
@@ -5663,7 +5122,7 @@ Module['TupleExtract'] = makeExpressionWrapper({
   }
 });
 
-Module['RefI31'] = makeExpressionWrapper({
+Module['RefI31'] = makeExpressionWrapper(Module['_BinaryenRefI31Id'](), {
   'getValue'(expr) {
     return Module['_BinaryenRefI31GetValue'](expr);
   },
@@ -5672,7 +5131,7 @@ Module['RefI31'] = makeExpressionWrapper({
   }
 });
 
-Module['I31Get'] = makeExpressionWrapper({
+Module['I31Get'] = makeExpressionWrapper(Module['_BinaryenI31GetId'](), {
   'getI31'(expr) {
     return Module['_BinaryenI31GetGetI31'](expr);
   },
diff --git a/test/binaryen.js/expressions.js b/test/binaryen.js/expressions.js
index 3cbe7e59448..f249f95493c 100644
--- a/test/binaryen.js/expressions.js
+++ b/test/binaryen.js/expressions.js
@@ -9,7 +9,11 @@ function assertDeepEqual(x, y) {
 
 console.log("# Expression");
 (function testWrapper() {
-  var theExpression = binaryen.Block(42); // works without new
+  const module = new binaryen.Module();
+
+  const ptr = module.block(null, []);
+
+  var theExpression = binaryen.Expression(ptr); // works without new
   assert(theExpression instanceof binaryen.Block);
   assert(theExpression instanceof binaryen.Expression);
   assert(theExpression.constructor === binaryen.Block);
@@ -17,7 +21,9 @@ console.log("# Expression");
   assert(typeof binaryen.Block.getName === "function"); // own
   assert(typeof theExpression.getId === "function"); // proto
   assert(typeof theExpression.getName === "function"); // own
-  assert((theExpression | 0) === 42); // via valueOf
+  assert((theExpression | 0) === ptr); // via valueOf
+
+  module.dispose();
 })();
 
 console.log("# Block");
diff --git a/test/binaryen.js/kitchen-sink.js.txt b/test/binaryen.js/kitchen-sink.js.txt
index bee1d298937..be7a06f7ad8 100644
--- a/test/binaryen.js/kitchen-sink.js.txt
+++ b/test/binaryen.js/kitchen-sink.js.txt
@@ -2821,4 +2821,4 @@ sizeof Literal: 24
 )
 
 getExpressionInfo(memory.grow)={"id":21,"type":2,"delta":1}
-getExpressionInfo(switch)={"id":5,"type":1,"names":["label"],"defaultName":"label","condition":0,"value":0}
+getExpressionInfo(switch)={"id":5,"type":1,"numNames":1,"names":["label"],"defaultName":"label","condition":0,"value":0}


Reviews:
user: login: kripken
body: Nice!
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-18T17:46:05Z
user: login: GulgDev
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-19T08:01:38Z
user: login: kripken
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-23T17:02:27Z

Review comments:
diff_hunk: @@ -4055,6 +3511,7 @@ function Expression(expr) {
   if (!expr) throw Error("expression reference must not be null");
   this[thisPtr] = expr;
 }
+Expression['wrappers'] = {};
path: src/js/binaryen.js-post.js
user: login: kripken
body: This is meant for internal use, I think? Or did you imagine users would want to access it directly?

(If it is for internal use then we don't need the quoting, and actually could just do `let expressionWrappers = {}` as a global)
created_at: 2025-04-18T17:45:25Z
author_association: MEMBER
diff_hunk: @@ -4055,6 +3511,7 @@ function Expression(expr) {
   if (!expr) throw Error("expression reference must not be null");
   this[thisPtr] = expr;
 }
+Expression['wrappers'] = {};
path: src/js/binaryen.js-post.js
user: login: GulgDev
body: Yes, it is intended for internal use only.
created_at: 2025-04-19T08:01:38Z
author_association: CONTRIBUTOR

PR comments:
user: login: GulgDev
created_at: 2025-04-18T15:52:29Z
body: Related discussion: https://github.com/WebAssembly/binaryen/discussions/7515
author_association: CONTRIBUTOR
user: login: GulgDev
created_at: 2025-04-19T08:02:22Z
body: Maybe we can make Expression constructor return the specific instance? Can we just make the instruction-building functions return expression wrappers? That shouldn't be a breaking change, as expression wrappers can be implicitly converted to Wasm pointers via valueOf (e.g. +expr or expr | 0).
author_association: CONTRIBUTOR
user: login: kripken
created_at: 2025-04-21T15:31:54Z
body: > Maybe we can make Expression constructor return the specific instance?

Sorry, what do you mean here? What type of code can construct something with only Expression?
author_association: MEMBER
user: login: GulgDev
created_at: 2025-04-21T15:52:35Z
body: I mean that the `Expression` constructor could possibly act as `wrapExpression`. https://github.com/WebAssembly/binaryen/blob/90ad796de7cb43d9a6a0d891f2ceea14b880526a/src/js/binaryen.js-post.js#L4052-L4057
author_association: CONTRIBUTOR
user: login: kripken
created_at: 2025-04-21T21:09:09Z
body: I see, thanks. Yes, I guess it could. Initially it felt slightly odd to me, maybe because in C++ `Expression(..)` returns an `Expression`, not the more specific subclass... but in JS it seems ok, and I can't think of anything simpler. Let's go with that.
author_association: MEMBER