Skip to content

wasm-validator error: i64 != i32: binary child types must be equal #1911

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
yjhmelody opened this issue Jun 17, 2021 · 8 comments · Fixed by #1921
Closed

wasm-validator error: i64 != i32: binary child types must be equal #1911

yjhmelody opened this issue Jun 17, 2021 · 8 comments · Fixed by #1921
Labels

Comments

@yjhmelody
Copy link
Contributor

  [TestFile.ts] ..\..\node_modules\@as-pect\assembly\assembly\index.ts --runtime stub --debug --binaryFile output.wasm --explicitStart --use ASC_RTRACE=1 --exportTable --importMemory --transform D:\code\serde-as\node_modules\@as-pect\core\lib\transform\index.js

[wasm-validator error in function start:assembly/__tests__/compactInt.spec~anonymous|0~anonymous|0] i64 != i32: binary child types must be equal, on
(i32.and
 (i64.extend_i32_u
  (i32.load16_u
   (local.get $15)
  )
 )
 (i32.const 65535)
)
[wasm-validator error in function start:assembly/__tests__/compactInt.spec~anonymous|0~anonymous|0] i64 != i32: i32 op, on
(i32.and
 (i64.extend_i32_u
  (i32.load16_u
   (local.get $15)
  )
 )
 (i32.const 65535)
)
[Error] There was a compilation error when trying to create the wasm binary for file: assembly/__tests__/compactInt.spec.ts.
Error: validate error
    at Object.main (D:\code\serde-as\node_modules\assemblyscript\cli\asc.js:876:23)
    at D:\code\serde-as\node_modules\@as-pect\cli\src\run.ts:633:11
    at Array.forEach (<anonymous>)
    at run (D:\code\serde-as\node_modules\@as-pect\cli\src\run.ts:630:32)
    at Object.asp (D:\code\serde-as\node_modules\@as-pect\cli\src\index.ts:61:5)
    at Object.<anonymous> (D:\code\serde-as\node_modules\@as-pect\cli\src\test.ts:1:28)
    at Module._compile (internal/modules/cjs/loader.js:1068:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1097:10)
    at Module.load (internal/modules/cjs/loader.js:933:32)
    at Function.Module._load (internal/modules/cjs/loader.js:774:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:72:12)
    at internal/main/run_main_module.js:17:47
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
@MaxGraey
Copy link
Member

That's strange. Could you share compactInt code?

@yjhmelody
Copy link
Contributor Author

That's strange. Could you share compactInt code?

It' really long.

@yjhmelody
Copy link
Contributor Author

yjhmelody commented Jun 18, 2021

class TestData<T1, T2> {
    constructor(
        public readonly input: T1,
        public readonly output: T2,
    ) { }
}

describe("Compact", () => {
    it("serialize", () => {
        let ser = new ScaleSerializer();
        let tests: Array<TestData<u64, Array<u8>>> = [
            new TestData(0, [0x00]),
            // we can omit the following tests.
            // new TestData(63, [0xfc]),
            // new TestData(16383, [0xfd, 0xff]),
            // new TestData(16384, [0x02, 0x00, 0x01, 0x00]),
            // new TestData(1073741823, [0xfe, 0xff, 0xff, 0xff]),
            // new TestData(1073741824, [0x03, 0x00, 0x00, 0x00, 0x40]),
            // new TestData((1 << 32) - 1, [0x03, 0xff, 0xff, 0xff, 0xff]),
            // new TestData((1 << 32), [0x07, 0x00, 0x00, 0x00, 0x00, 0x01]),
            // new TestData((1 << 40), [0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
            // new TestData((1 << 48), [0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
            // new TestData((1 << 56) - 1, [0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),
            // new TestData((1 << 56), [0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01]),
            // new TestData(u64.MAX_VALUE, [0x13, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff]),        ];
        for (let i = 0; i < tests.length; i++) {
            let test = tests[i];
            let num = new Compact<u64>(test.input);
            ser.clear();
            let res = num.serialize<BytesBuffer, ScaleSerializer>(ser).toArray();
            expect(res).toStrictEqual(test.output);

            // meet the wasm-validator error
            // if (test.input <= (u8.MAX_VALUE as u64)) {
            //     let num = new Compact<u8>(test.input as u8);
            //     ser.clear();
            //     let res = num.serialize<BytesBuffer, ScaleSerializer>(ser).toArray();
            //     expect(res).toStrictEqual(test.output);
            // }

            // meet the wasm-validator error
            // if (test.input <= (u16.MAX_VALUE as u64)) {
            //     let num = new Compact<u16>(test.input as u16);
            //     ser.clear();
            //     let res = num.serialize<BytesBuffer, ScaleSerializer>(ser).toArray();
            //     expect(res).toStrictEqual(test.output);
            // }

            // It's ok
            if (test.input <= (u32.MAX_VALUE as u64)) {
                let num = new Compact<u32>(test.input as u32);
                ser.clear();
                let res = num.serialize<BytesBuffer, ScaleSerializer>(ser).toArray();
                expect(res).toStrictEqual(test.output);
            }
        }
    });
});
export interface CompactLen {
    compactLen(): i32;
}

export class Compact<T extends number> implements CompactLen {
    // @ts-ignore
    constructor(protected _value: T = 0) {
        if (!isInteger<T>()) {
            unreachable();
        }
    }

    @inline
    unwrap(): T {
        return this._value;
    }

    @inline
    compactLen(): i32 {
        return Compact.computeCompactLen(this._value as u64);
    }

    @inline
    static computeCompactLen(val: u64): i32 {
        if (val <= 0b0011_1111) return 1;
        else if (val <= 0b0011_1111_1111_1111) return 2;
        else if (val <= 0b0011_1111_1111_1111_1111_1111_1111_1111) return 4;
        else {
            return (8 - clz(val) / 8) as i32 + 1;
        }
    }

    @inline
    serialize<R, S extends Serializer<R>>(serializer: S): R {
        const len = this.compactLen();
        switch (len) {
            case 1: {
                return serializer.serialize<u8>((this._value as u8) << 2);
            }

            case 2: {
                return serializer.serialize<u16>(((this._value as u16) << 2) | 0b01);
            }
            case 4: {
                return serializer.serialize<u32>(((this._value as u32) << 2) | 0b10);
            }

            default: {
                let bytesNeeded = 8 - clz(this._value as u64) / 8;
                assert(bytesNeeded >= 4, "Previous match arm matches anyting less than 2^30; qed");
                let ret = serializer.serializeU8(0b11 + ((bytesNeeded - 4) << 2) as u8);
                let v = this._value;
                for (let i: u8 = 0; i < u8(bytesNeeded); i++) {
                    ret = serializer.serializeU8(v as u8);
                    // @ts-ignore
                    v >>= 8;
                }
                // @ts-ignore
                assert(v == 0, "shifted sufficient bits right to lead only leading zeros; qed");
                return ret;
            }
        }
    }
}

This is still incomplete.

@MaxGraey
Copy link
Member

MaxGraey commented Jun 18, 2021

it will be great if you reduce this to minimal reproducible example

@yjhmelody
Copy link
Contributor Author

it will be great if you reduce this to minimal reproducible example

I try my best.

@yjhmelody
Copy link
Contributor Author

@MaxGraey The minimal example:

class N<T> {
    constructor(public n: T) {
    }
}

describe("N", () => {
    it("validate-error", () => {
        {
            let testData: u64 = 0;
            if (testData <= (u16.MAX_VALUE as u64)) {
                let num = new N<u16>(testData as u16);
                let bytesNeeded = 8 - clz(num.n as u64) / 8;
                expect(bytesNeeded).toBeGreaterThan(1);
            }
        }

        {
            let testData: u64 = 1;
            if (testData <= (u8.MAX_VALUE as u64)) {
                let num = new N<u8>(testData as u8);
                let bytesNeeded = 8 - clz(num.n as u64) / 8;
                expect(bytesNeeded).toBeGreaterThan(1);
            }
        }
    });
});

@MaxGraey
Copy link
Member

MaxGraey commented Jun 21, 2021

Thanks! I reduced it more:

export function test(x: u16): u64 {
  return x as u64;
}

playground

@dcodeIO could you check this?

@jtenner
Copy link
Contributor

jtenner commented Jun 21, 2021

Whenever aspect is involved, best to remove it altogether. Because sometimes it's an aspect bug :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants