private callMethod()

in WorkbookApps/Xamarin.Workbooks.WebAssembly/Client/runtime.ts [132:207]


    private callMethod(method: MonoMethod, thisArg: (Handle | null), argsMarshal: MarshalType[], ...args: (Handle | number | string)[]) {
        let extraArgsMemSize = 0
        for (var i = 0; i < args.length; ++i) {
            // long/double memory must be 8 bytes aligned and I'm being lazy here. - kumpera
            // allocate extra memory size for all but string and handle, to keep the conditional more readable - bojan
            if (argsMarshal[i] !== MarshalType.String && argsMarshal[i] !== MarshalType.Handle)
                extraArgsMemSize += 8
        }

        const extraArgsMem = extraArgsMemSize ? Module._malloc(extraArgsMemSize) : 0
        let extraArgIndex = 0;

        const argsMemory = Module._malloc(args.length * 4)
        var ehThrow = Module._malloc(4)
        for (var i = 0; i < args.length; ++i) {
            if (argsMarshal[i] === MarshalType.String) {
                if (typeof args[i] !== "string")
                    throw new Error(`Type of argument ${i} is ${typeof args[i]}, but string marshalling was requested!`)
                Module.setValue(argsMemory + i * 4, this.runtime.convertStringToMonoString(<string>args[i]), "i32")
            } else if (argsMarshal[i] === MarshalType.Handle) {
                if (typeof args[i] === "string" || typeof args[i] === "number")
                    throw new Error(`Type of argument ${i} is ${typeof args[i]}, but handle marshalling was requested!`)
                Module.setValue(argsMemory + i * 4, (<Handle>args[i]).value, "i32")
            } else {
                if (typeof args[i] !== "number")
                    throw new Error(`Type of argument ${i} is ${typeof args[i]}, but number marshalling was requested!`)

                // upstream has an else if here with a bigger conditional, but having limited the
                // enum here, we don't need to do that. we'll need to treat the extra cell
                // specially for int/long/float/double values, but for handles (the remaining enum member)
                // we can ignore the extra cell bit and just set the arg memory normally - bojan
                const extraCell = extraArgsMemSize + extraArgIndex
                extraArgIndex += 8

                if (argsMarshal[i] === MarshalType.Int)
                    Module.setValue(extraCell, <number>args[i], "i32")
                else if (argsMarshal[i] === MarshalType.Long)
                    Module.setValue(extraCell, <number>args[i], "i64")
                else if (argsMarshal[i] === MarshalType.Float)
                    Module.setValue(extraCell, <number>args[i], "float")
                else if (argsMarshal[i] === MarshalType.Double)
                    Module.setValue(extraCell, <number>args[i], "double")

                Module.setValue(argsMemory + i * 4, extraCell, "i32")
            }
        }
        Module.setValue(ehThrow, 0, "i32")

        const res = this.runtime.invokeMethod(method.handle.value, (thisArg ? thisArg.value : null), argsMemory, ehThrow)
        const ehRes = Module.getValue(ehThrow, "i32")

        if (extraArgsMemSize)
            Module._free(extraArgsMem)
        Module._free(argsMemory)
        Module._free(ehThrow)

        if (ehRes != 0) {
            const msg = new MonoString(new Handle(res), this.runtime).toString()
            throw new Error(msg)
        }

        if (!res)
            return res

        const objectType = this.runtime.getObjectType(res)
        switch (objectType) {
            case MonoObjectType.Integer:
                return this.runtime.unboxInteger(res)
            case MonoObjectType.FloatingPoint:
                return this.runtime.unboxFloat(res)
            case MonoObjectType.String:
                return new MonoString(new Handle(res), this.runtime).toString()
            default:
                return new Handle(res);
        }
    }