From 7cd8e9d7675f4c18c8ed6029fbd7fe41031b1ec1 Mon Sep 17 00:00:00 2001
From: Jono Wenger <jono@elixxir.io>
Date: Fri, 19 Aug 2022 08:35:49 -0700
Subject: [PATCH] Make WrapCB panic if m is not a function

---
 .gitignore             |  1 +
 go.mod                 |  6 ++++--
 wasm/backup.go         |  4 ++--
 wasm/broadcast.go      |  2 +-
 wasm/connect.go        |  2 +-
 wasm/delivery.go       |  2 +-
 wasm/e2e.go            |  6 +++---
 wasm/e2eHandler.go     |  4 ++--
 wasm/fileTransfer.go   |  8 ++++----
 wasm/follow.go         |  4 ++--
 wasm/group.go          |  4 ++--
 wasm/restlikeSingle.go |  2 +-
 wasm/single.go         |  4 ++--
 wasm/ud.go             |  8 ++++----
 wasm/utils.go          | 15 +++++++++++----
 15 files changed, 41 insertions(+), 31 deletions(-)

diff --git a/.gitignore b/.gitignore
index d437b375..d5bc9b31 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,5 @@ localdev_*
 # Ignore temp files
 *.bak
 
+vendor/
 *.wasm
\ No newline at end of file
diff --git a/go.mod b/go.mod
index 3df9c59c..6513eb8d 100644
--- a/go.mod
+++ b/go.mod
@@ -2,7 +2,10 @@ module gitlab.com/elixxir/xxdk-wasm
 
 go 1.17
 
-require gitlab.com/elixxir/client v1.5.1-0.20220812185448-678f627877c6
+require (
+	github.com/spf13/jwalterweatherman v1.1.0
+	gitlab.com/elixxir/client v1.5.1-0.20220812185448-678f627877c6
+)
 
 require (
 	github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
@@ -17,7 +20,6 @@ require (
 	github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
 	github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
-	github.com/spf13/jwalterweatherman v1.1.0 // indirect
 	github.com/stretchr/testify v1.7.2 // indirect
 	github.com/ttacon/builder v0.0.0-20170518171403-c099f663e1c2 // indirect
 	github.com/ttacon/libphonenumber v1.2.1 // indirect
diff --git a/wasm/backup.go b/wasm/backup.go
index 7b1e8081..c715cfca 100644
--- a/wasm/backup.go
+++ b/wasm/backup.go
@@ -101,7 +101,7 @@ func NewCmixFromBackup(_ js.Value, args []js.Value) interface{} {
 //  - Javascript representation of the Backup object
 //  - Throws a TypeError if initializing the Backup fails.
 func InitializeBackup(_ js.Value, args []js.Value) interface{} {
-	cb := &updateBackupFunc{WrapCB(args[3].Call, "UpdateBackup")}
+	cb := &updateBackupFunc{WrapCB(args[3], "UpdateBackup")}
 	api, err := bindings.InitializeBackup(
 		args[0].Int(), args[1].Int(), args[2].String(), cb)
 	if err != nil {
@@ -130,7 +130,7 @@ func InitializeBackup(_ js.Value, args []js.Value) interface{} {
 //  - Javascript representation of the Backup object
 //  - Throws a TypeError if initializing the Backup fails.
 func ResumeBackup(_ js.Value, args []js.Value) interface{} {
-	cb := &updateBackupFunc{WrapCB(args[2].Call, "UpdateBackup")}
+	cb := &updateBackupFunc{WrapCB(args[2], "UpdateBackup")}
 	api, err := bindings.ResumeBackup(args[0].Int(), args[1].Int(), cb)
 	if err != nil {
 		Throw(TypeError, err.Error())
diff --git a/wasm/broadcast.go b/wasm/broadcast.go
index 5e73ba56..53065426 100644
--- a/wasm/broadcast.go
+++ b/wasm/broadcast.go
@@ -82,7 +82,7 @@ func (bl *broadcastListener) Callback(payload []byte, err error) {
 //  - Throws a TypeError if registering the listener fails.
 func (c *Channel) Listen(_ js.Value, args []js.Value) interface{} {
 	err := c.api.Listen(
-		&broadcastListener{WrapCB(args[0].Call, "Callback")}, args[1].Int())
+		&broadcastListener{WrapCB(args[0], "Callback")}, args[1].Int())
 	if err != nil {
 		Throw(TypeError, err.Error())
 		return nil
diff --git a/wasm/connect.go b/wasm/connect.go
index 5c02040a..274be8ba 100644
--- a/wasm/connect.go
+++ b/wasm/connect.go
@@ -137,7 +137,7 @@ func (l *listener) Name() string     { return l.name().String() }
 //  - throws a TypeError is registering the listener fails
 func (c *Connection) RegisterListener(_ js.Value, args []js.Value) interface{} {
 	err := c.api.RegisterListener(args[0].Int(),
-		&listener{WrapCB(args[1].Call, "Hear"), WrapCB(args[1].Call, "Name")})
+		&listener{WrapCB(args[1], "Hear"), WrapCB(args[1], "Name")})
 	if err != nil {
 		Throw(TypeError, err.Error())
 		return nil
diff --git a/wasm/delivery.go b/wasm/delivery.go
index 56f60a69..ed2c55ea 100644
--- a/wasm/delivery.go
+++ b/wasm/delivery.go
@@ -49,7 +49,7 @@ func (mdc *messageDeliveryCallback) EventCallback(
 //    fails
 func (c *Cmix) WaitForRoundResult(_ js.Value, args []js.Value) interface{} {
 	roundList := CopyBytesToGo(args[0])
-	mdc := &messageDeliveryCallback{WrapCB(args[1].Call, "EventCallback")}
+	mdc := &messageDeliveryCallback{WrapCB(args[1], "EventCallback")}
 
 	err := c.api.WaitForRoundResult(roundList, mdc, args[2].Int())
 	if err != nil {
diff --git a/wasm/e2e.go b/wasm/e2e.go
index 7a57391a..27c71a51 100644
--- a/wasm/e2e.go
+++ b/wasm/e2e.go
@@ -193,15 +193,15 @@ func newAuthCallbacks(value js.Value) *authCallbacks {
 	a := &authCallbacks{}
 
 	if value.Get("Request").Type() != js.TypeFunction {
-		a.request = WrapCB(value.Call, "Request")
+		a.request = WrapCB(value, "Request")
 	}
 
 	if value.Get("Confirm").Type() != js.TypeFunction {
-		a.confirm = WrapCB(value.Call, "Confirm")
+		a.confirm = WrapCB(value, "Confirm")
 	}
 
 	if value.Get("Reset").Type() != js.TypeFunction {
-		a.reset = WrapCB(value.Call, "Reset")
+		a.reset = WrapCB(value, "Reset")
 	}
 
 	return a
diff --git a/wasm/e2eHandler.go b/wasm/e2eHandler.go
index e59b9c56..a6968818 100644
--- a/wasm/e2eHandler.go
+++ b/wasm/e2eHandler.go
@@ -201,7 +201,7 @@ func (p *processor) String() string {
 // Returns:
 //  - Throws TypeError if registering the service fails
 func (e *E2e) AddService(_ js.Value, args []js.Value) interface{} {
-	p := &processor{WrapCB(args[1].Call, "Process"), WrapCB(args[1].Call, "String")}
+	p := &processor{WrapCB(args[1], "Process"), WrapCB(args[1], "String")}
 
 	err := e.api.AddService(args[0].String(), p)
 	if err != nil {
@@ -226,7 +226,7 @@ func (e *E2e) AddService(_ js.Value, args []js.Value) interface{} {
 //  - Throws TypeError if registering the service fails
 func (e *E2e) RegisterListener(_ js.Value, args []js.Value) interface{} {
 	recipientId := CopyBytesToGo(args[0])
-	l := &listener{WrapCB(args[1].Call, "Hear"), WrapCB(args[1].Call, "Name")}
+	l := &listener{WrapCB(args[1], "Hear"), WrapCB(args[1], "Name")}
 
 	err := e.api.RegisterListener(recipientId, args[1].Int(), l)
 	if err != nil {
diff --git a/wasm/fileTransfer.go b/wasm/fileTransfer.go
index 4d56a629..3220f6f8 100644
--- a/wasm/fileTransfer.go
+++ b/wasm/fileTransfer.go
@@ -97,7 +97,7 @@ func (rpc *fileTransferReceiveProgressCallback) Callback(
 //  - Javascript representation of the FileTransfer object.
 //  - Throws a TypeError initialising the file transfer manager fails.
 func InitFileTransfer(_ js.Value, args []js.Value) interface{} {
-	rfc := &receiveFileCallback{WrapCB(args[1].Call, "Callback")}
+	rfc := &receiveFileCallback{WrapCB(args[1], "Callback")}
 	e2eFileTransferParamsJson := CopyBytesToGo(args[2])
 	fileTransferParamsJson := CopyBytesToGo(args[3])
 
@@ -129,7 +129,7 @@ func (f *FileTransfer) Send(_ js.Value, args []js.Value) interface{} {
 	payload := CopyBytesToGo(args[0])
 	recipientID := CopyBytesToGo(args[1])
 	retry := float32(args[2].Float())
-	spc := &fileTransferSentProgressCallback{WrapCB(args[3].Call, "Callback")}
+	spc := &fileTransferSentProgressCallback{WrapCB(args[3], "Callback")}
 
 	ftID, err := f.api.Send(payload, recipientID, retry, spc, args[4].String())
 	if err != nil {
@@ -209,7 +209,7 @@ func (f *FileTransfer) CloseSend(_ js.Value, args []js.Value) interface{} {
 func (f *FileTransfer) RegisterSentProgressCallback(
 	_ js.Value, args []js.Value) interface{} {
 	tidBytes := CopyBytesToGo(args[0])
-	spc := &fileTransferSentProgressCallback{WrapCB(args[1].Call, "Callback")}
+	spc := &fileTransferSentProgressCallback{WrapCB(args[1], "Callback")}
 
 	err := f.api.RegisterSentProgressCallback(tidBytes, spc, args[2].String())
 	if err != nil {
@@ -237,7 +237,7 @@ func (f *FileTransfer) RegisterSentProgressCallback(
 func (f *FileTransfer) RegisterReceivedProgressCallback(
 	_ js.Value, args []js.Value) interface{} {
 	tidBytes := CopyBytesToGo(args[0])
-	rpc := &fileTransferReceiveProgressCallback{WrapCB(args[1].Call, "Callback")}
+	rpc := &fileTransferReceiveProgressCallback{WrapCB(args[1], "Callback")}
 
 	err := f.api.RegisterReceivedProgressCallback(
 		tidBytes, rpc, args[2].String())
diff --git a/wasm/follow.go b/wasm/follow.go
index 2eff83b2..3f9bcc37 100644
--- a/wasm/follow.go
+++ b/wasm/follow.go
@@ -163,7 +163,7 @@ func (nhc *networkHealthCallback) Callback(health bool) { nhc.callback(health) }
 //  - Returns a registration ID that can be used to unregister (int)
 func (c *Cmix) AddHealthCallback(_ js.Value, args []js.Value) interface{} {
 	return c.api.AddHealthCallback(
-		&networkHealthCallback{WrapCB(args[0].Call, "Callback")})
+		&networkHealthCallback{WrapCB(args[0], "Callback")})
 }
 
 // RemoveHealthCallback removes a health callback using its registration ID.
@@ -192,6 +192,6 @@ func (ce *clientError) Report(source, message, trace string) {
 //  - args[0] - Javascript object that has functions that implement the
 //    [bindings.ClientError] interface
 func (c *Cmix) RegisterClientErrorCallback(_ js.Value, args []js.Value) interface{} {
-	c.api.RegisterClientErrorCallback(&clientError{WrapCB(args[0].Call, "Report")})
+	c.api.RegisterClientErrorCallback(&clientError{WrapCB(args[0], "Report")})
 	return nil
 }
diff --git a/wasm/group.go b/wasm/group.go
index 08bfd28e..3dd0bf76 100644
--- a/wasm/group.go
+++ b/wasm/group.go
@@ -55,9 +55,9 @@ func newGroupChatJS(api *bindings.GroupChat) map[string]interface{} {
 //  - Javascript representation of the GroupChat object.
 //  - Throws a TypeError if creating the GroupChat fails.
 func NewGroupChat(_ js.Value, args []js.Value) interface{} {
-	requestFunc := &groupRequest{WrapCB(args[1].Call, "Callback")}
+	requestFunc := &groupRequest{WrapCB(args[1], "Callback")}
 	p := &groupChatProcessor{
-		WrapCB(args[2].Call, "Process"), WrapCB(args[2].Call, "String")}
+		WrapCB(args[2], "Process"), WrapCB(args[2], "String")}
 
 	api, err := bindings.NewGroupChat(args[0].Int(), requestFunc, p)
 	if err != nil {
diff --git a/wasm/restlikeSingle.go b/wasm/restlikeSingle.go
index 2fc4f416..00963408 100644
--- a/wasm/restlikeSingle.go
+++ b/wasm/restlikeSingle.go
@@ -71,7 +71,7 @@ func AsyncRequestRestLike(_ js.Value, args []js.Value) interface{} {
 	recipient := CopyBytesToGo(args[1])
 	request := CopyBytesToGo(args[2])
 	paramsJSON := CopyBytesToGo(args[3])
-	cb := &restlikeCallback{WrapCB(args[4].Call, "Callback")}
+	cb := &restlikeCallback{WrapCB(args[4], "Callback")}
 
 	err := bindings.AsyncRequestRestLike(
 		e2eID, recipient, request, paramsJSON, cb)
diff --git a/wasm/single.go b/wasm/single.go
index 4684a6b6..31bddeac 100644
--- a/wasm/single.go
+++ b/wasm/single.go
@@ -40,7 +40,7 @@ func TransmitSingleUse(_ js.Value, args []js.Value) interface{} {
 	tag := args[2].String()
 	payload := CopyBytesToGo(args[3])
 	paramsJSON := CopyBytesToGo(args[4])
-	responseCB := &singleUseResponse{WrapCB(args[5].Call, "Callback")}
+	responseCB := &singleUseResponse{WrapCB(args[5], "Callback")}
 
 	report, err := bindings.TransmitSingleUse(
 		e2eID, recipient, tag, payload, paramsJSON, responseCB)
@@ -67,7 +67,7 @@ func TransmitSingleUse(_ js.Value, args []js.Value) interface{} {
 //    function used to stop the listener.
 //  - Throws a TypeError if listening fails.
 func Listen(_ js.Value, args []js.Value) interface{} {
-	cb := &singleUseCallback{WrapCB(args[2].Call, "Callback")}
+	cb := &singleUseCallback{WrapCB(args[2], "Callback")}
 	api, err := bindings.Listen(args[0].Int(), args[1].String(), cb)
 	if err != nil {
 		Throw(TypeError, err.Error())
diff --git a/wasm/ud.go b/wasm/ud.go
index 9d472705..14aa3273 100644
--- a/wasm/ud.go
+++ b/wasm/ud.go
@@ -97,7 +97,7 @@ func (uns *udNetworkStatus) UdNetworkStatus() int {
 //  - Throws a TypeError if creating or loading fails.
 func NewOrLoadUd(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	follower := &udNetworkStatus{WrapCB(args[1].Call, "UdNetworkStatus")}
+	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
 	username := args[2].String()
 	registrationValidationSignature := CopyBytesToGo(args[3])
 	cert := CopyBytesToGo(args[4])
@@ -147,7 +147,7 @@ func NewOrLoadUd(_ js.Value, args []js.Value) interface{} {
 //  - Throws a TypeError if getting UD from backup fails.
 func NewUdManagerFromBackup(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
-	follower := &udNetworkStatus{WrapCB(args[1].Call, "UdNetworkStatus")}
+	follower := &udNetworkStatus{WrapCB(args[1], "UdNetworkStatus")}
 	emailFactJson := CopyBytesToGo(args[2])
 	phoneFactJson := CopyBytesToGo(args[3])
 	cert := CopyBytesToGo(args[4])
@@ -303,7 +303,7 @@ func (ulc *udLookupCallback) Callback(contactBytes []byte, err error) {
 func LookupUD(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
 	udContact := CopyBytesToGo(args[1])
-	cb := &udLookupCallback{WrapCB(args[2].Call, "Callback")}
+	cb := &udLookupCallback{WrapCB(args[2], "Callback")}
 	lookupId := CopyBytesToGo(args[3])
 	singleRequestParamsJSON := CopyBytesToGo(args[4])
 
@@ -350,7 +350,7 @@ func (usc *udSearchCallback) Callback(contactListJSON []byte, err error) {
 func SearchUD(_ js.Value, args []js.Value) interface{} {
 	e2eID := args[0].Int()
 	udContact := CopyBytesToGo(args[1])
-	cb := &udSearchCallback{WrapCB(args[2].Call, "Callback")}
+	cb := &udSearchCallback{WrapCB(args[2], "Callback")}
 	factListJSON := CopyBytesToGo(args[3])
 	singleRequestParamsJSON := CopyBytesToGo(args[4])
 
diff --git a/wasm/utils.go b/wasm/utils.go
index 163b8b19..0774ec2e 100644
--- a/wasm/utils.go
+++ b/wasm/utils.go
@@ -11,6 +11,7 @@ package wasm
 
 import (
 	"encoding/json"
+	jww "github.com/spf13/jwalterweatherman"
 	"syscall/js"
 )
 
@@ -30,11 +31,17 @@ func CopyBytesToJS(src []byte) js.Value {
 	return dst
 }
 
-// WrapCB wraps a js.Call so that it can be called later with only the arguments
-// and without specifying the function name
-func WrapCB(call func(m string, args ...interface{}) js.Value, m string) func(args ...interface{}) js.Value {
+// WrapCB wraps a Javascript function in an object so that it can be called
+// later with only the arguments and without specifying the function name.
+//
+// Panics if m is not a function.
+func WrapCB(parent js.Value, m string) func(args ...interface{}) js.Value {
+	if parent.Get("m").Type() != js.TypeFunction {
+		jww.FATAL.Panicf("Function %q is not of type %s", m, js.TypeFunction)
+	}
+
 	return func(args ...interface{}) js.Value {
-		return call(m, args)
+		return parent.Call(m, args)
 	}
 }
 
-- 
GitLab