This changes the calling convention for calling a Javascript function from Python so that any proxied arguments are automatically destroyed at the end of the function call. This is a breaking change. It should make it much more convenient to use the foreign function interface. 

The docs need to be updated to explain the new calling convention before this can be merged. We should put a link to the appropriate section in the docs into the error message emitted when a borrowed proxy is used after it is free so that people have an easy time learning the model by trial and error.
ENH Destroy proxies created in JsProxy.__call__ when done with call. This changes the calling convention for calling a Javascript function from Python so that any proxied arguments are automatically destroyed at the end of the function call. This is a breaking change. It should make it much more convenient to use the foreign function interface. 

The docs need to be updated to explain the new calling convention before this can be merged. We should put a link to the appropriate section in the docs into the error message emitted when a borrowed proxy is used after it is free so that people have an easy time learning the model by trial and error.
// Macros to access memory from Javascript

#define DEREF_U8(addr, offset) HEAPU8[addr + offset]
#define DEREF_I8(addr, offset) HEAP8[addr + offset]

#define DEREF_U16(addr, offset) HEAPU16[(addr >> 1) + offset]
#define DEREF_I16(addr, offset) HEAP16[(addr >> 1) + offset]

#define DEREF_U32(addr, offset) HEAPU32[(addr >> 2) + offset]
#define DEREF_I32(addr, offset) HEAP32[(addr >> 2) + offset]

#define DEREF_F32(addr, offset) HEAPF32[(addr >> 2) + offset]
#define DEREF_F64(addr, offset) HEAPF64[(addr >> 3) + offset]

#if WASM_BIGINT
// We have HEAPU64 / HEAPI64 in this case.
#define DEREF_U64(addr, offset) HEAPU64[(addr >> 3) + offset]
#define DEREF_I64(addr, offset) HEAP64[(addr >> 3) + offset]

#define LOAD_U64(addr, offset) DEREF_U64(addr, offset)
#define LOAD_I64(addr, offset) DEREF_I64(addr, offset)

#define STORE_U64(addr, offset, val) (DEREF_U64(addr, offset) = val)
#define STORE_I64(addr, offset, val) (DEREF_U64(addr, offset) = val)

#else

// No BigUint64Array, have to manually split / join lower and upper byte
//
#define BIGINT_LOWER(x) (Number((x)&BigInt(0xffffffff)) | 0)
#define BIGINT_UPPER(x) (Number((x) >> BigInt(32)) | 0)
#define UBIGINT_FROM_PAIR(lower, upper)                                        \
  (BigInt(lower) | (BigInt(upper) << BigInt(32)))

#define IBIGINT_FROM_PAIR(lower, upper)                                        \
  (BigInt(lower) | (BigInt(upper - 2 * (upper & 0x80000000)) << BigInt(32)))

#define LOAD_U64(addr, offset)                                                 \
  UBIGINT_FROM_PAIR(DEREF_U32(addr, offset * 2),                               \
                    DEREF_U32(addr, offset * 2 + 1))
#define LOAD_I64(addr, offset)                                                 \
  IBIGINT_FROM_PAIR(DEREF_U32(addr, offset * 2),                               \
                    DEREF_U32(addr, offset * 2 + 1))

#define STORE_U64(addr, offset, val)                                           \
  ((DEREF_U32(addr, offset * 2) = BIGINT_LOWER(val)),                          \
   (DEREF_U32(addr, offset * 2 + 1) = BIGINT_UPPER(val)))
#define STORE_I64 STORE_U64
#endif

// Macros to access memory from Javascript

#define DEREF_U8(addr, offset) HEAPU8[addr + offset]
#define DEREF_I8(addr, offset) HEAP8[addr + offset]

#define DEREF_U16(addr, offset) HEAPU16[(addr >> 1) + offset]
#define DEREF_I16(addr, offset) HEAP16[(addr >> 1) + offset]

#define DEREF_U32(addr, offset) HEAPU32[(addr >> 2) + offset]
#define DEREF_I32(addr, offset) HEAP32[(addr >> 2) + offset]

#define DEREF_F32(addr, offset) HEAPF32[(addr >> 2) + offset]
#define DEREF_F64(addr, offset) HEAPF64[(addr >> 3) + offset]

#if WASM_BIGINT
// We have HEAPU64 / HEAPI64 in this case.
#define DEREF_U64(addr, offset) HEAPU64[(addr >> 3) + offset]
#define DEREF_I64(addr, offset) HEAP64[(addr >> 3) + offset]

#define LOAD_U64(addr, offset) DEREF_U64(addr, offset)
#define LOAD_I64(addr, offset) DEREF_I64(addr, offset)

#define STORE_U64(addr, offset, val) (DEREF_U64(addr, offset) = val)
#define STORE_I64(addr, offset, val) (DEREF_U64(addr, offset) = val)

#else

// No BigUint64Array, have to manually split / join lower and upper byte
//
#define BIGINT_LOWER(x) (Number((x)&BigInt(0xffffffff)) | 0)
#define BIGINT_UPPER(x) (Number((x) >> BigInt(32)) | 0)
#define UBIGINT_FROM_PAIR(lower, upper)                                        \
  (BigInt(lower) | (BigInt(upper) << BigInt(32)))

#define IBIGINT_FROM_PAIR(lower, upper)                                        \
  (BigInt(lower) | (BigInt(upper - 2 * (upper & 0x80000000)) << BigInt(32)))

#define LOAD_U64(addr, offset)                                                 \
  UBIGINT_FROM_PAIR(DEREF_U32(addr, offset * 2),                               \
                    DEREF_U32(addr, offset * 2 + 1))
#define LOAD_I64(addr, offset)                                                 \
  IBIGINT_FROM_PAIR(DEREF_U32(addr, offset * 2),                               \
                    DEREF_U32(addr, offset * 2 + 1))

#define STORE_U64(addr, offset, val)                                           \
  ((DEREF_U32(addr, offset * 2) = BIGINT_LOWER(val)),                          \
   (DEREF_U32(addr, offset * 2 + 1) = BIGINT_UPPER(val)))
#define STORE_I64 STORE_U64
#endif
