# Copyright (c) 2021-2022 Huawei Device Co., Ltd.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

definitions:
  - name: PandaAssembly_header
    template: |
      .language PandaAssembly

      .record panda.Class <external>
      .record panda.Object <external>
      .record panda.String <external>
      .record panda.CharSequence <external>
      .record panda.io.Serializable <external>
      .record panda.NullPointerException <external>
      .record panda.ClassCastException <external>

      .record R <> {}
      .record I <panda.interface> {}
      .record Q <panda.extends=R, panda.implements=I> {}
      .record A <panda.annotation, panda.annotation.type=runtime> {}
      .record J <panda.interface, panda.implements=I> {}

  - name: pandasm_header
    template: |
      .language PandaAssembly

      .record panda.Class <external>
      .record panda.Object <external>
      .record panda.String <external>
      .record panda.ClassCastException <external>

      .record R {}
      .record Q {}


tests:
  - file-name: "isinstance"
    isa:
      title: Is instance
      description: >
        Resolve object type by specified id and if an object in accumulator is an instance of the resolved type,
        put 1 into accumulator, otherwise put 0.
        Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root
        type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements.
      instructions:
        - sig: isinstance type_id
          acc: inout:ref->i32
          format: [op_id_16]
    commands:

      - file-name: "of_null_pa"
        description: Check that null object reference is not an instance of any class
        isa:
          description: >
            'null' object is not an instance of any class.
        header-template: ['pandasm_header']
        check-type: empty
        code-template: |

          .function i32 main() {
            lda.null
            isinstance %s
            return
          }
        cases:
          - values: ['panda.Class']
          - values: ['panda.Object']
          - values: ['R']


      - file-name: "of_null_j"
        description: Check that null object reference is not an instance of any class
        isa:
          description: >
            'null' object is not an instance of any class.
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa']
        check-type: empty
        code-template: |

          .function i32 main() {
            lda.null
            isinstance %s
            return
          }
        cases:
          - values: ['panda.Class']
          - values: ['panda.Object']
          - values: ['R']
          - values: ['I']
          - values: ['Q']
          - values: ['A']


      - file-name: "with_wrong_typeid_pa"
        description: Check that compiler fails when type_id cannot be resolved to object type
        isa:
          verification:
            - type_id_any_object
        header-template: ['pandasm_header']
        runner-options: ['compile-failure']
        check-type: exit-positive
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
        cases:
          - values:
            - lda.null
            - panda.XYZObj
          - values:
            - lda.null
            - "null"
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - XYZObj
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - "\"xyz\""
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - i32
          - values:
            - ldai 1
            - 1
          - values:
            - ldai.64 0xC0BAC0BAC0BA
            - 0xC0BAC0BAC0BA
          - values:
            - fldai.64 2.7182818284590452353603
            - 2.7182818284590452353603

      - file-name: "with_valid_typeid_pa"
        description: Check that verifier succeeds on valid typeid.
        isa:
          verification:
            - type_id_any_object
        header-template: ['pandasm_header']
        check-type: empty
        code-template: |

          .function i32 main() {
            newobj v0, R
            lda.obj v0
            isinstance panda.Object
            jeqz set_failure
            lda.obj v0
            isinstance panda.Class
            jnez set_failure
            lda.obj v0
            isinstance panda.String
            jnez set_failure
            lda.obj v0
            isinstance panda.ClassCastException
            jnez set_failure
            lda.obj v0
            isinstance R
            jeqz set_failure
            ldai 0
            return
          set_failure:
            ldai 1
            return
          }

      - file-name: "with_wrong_typeid_j"
        description: Check that compiler fails when type_id cannot be resolved to object type
        isa:
          verification:
            - type_id_any_object
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa', 'compile-failure']
        check-type: exit-positive
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
        cases:
          - values:
            - lda.null
            - panda.XYZObj
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - panda.XYZObj
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - "null"
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - "\"\""
          - values:
            - |
              newobj v0, panda.Object
              lda.obj v0
            - f64
          - values:
            - ldai 0
            - 0
          - values:
            - ldai.64 0x01
            - 0x01
          - values:
            - fldai.64 2.7182818284590452353603
            - 2.7182818284590452353603

      - file-name: "with_valid_typeid_j"
        description: Check that verifier succeeds on valid typeid.
        isa:
          verification:
            - type_id_any_object
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa']
        tags: ['irtoc_ignore']
        check-type: empty
        code-template: |

          .function i32 main() {
            newobj v0, Q
            lda.obj v0
            isinstance panda.Object
            jeqz set_failure
            lda.obj v0
            isinstance panda.Class
            jnez set_failure
            lda.obj v0
            isinstance panda.String
            jnez set_failure
            lda.obj v0
            isinstance panda.ClassCastException
            jnez set_failure
            lda.obj v0
            isinstance Q
            jeqz set_failure
            lda.obj v0
            isinstance R
            jeqz set_failure
            lda.obj v0
            isinstance I
            jeqz set_failure
            ldai 0
            return
          set_failure:
            ldai 1
            return
          }

      - file-name: "with_wrong_acc_value_pa"
        description: Check that verifier fails when accumulator contains wrong value - neither object, nor null
        isa:
          verification:
            - acc_obj_or_null
        header-template: ['pandasm_header']
        runner-options: ['verifier-failure', 'verifier-config']
        tags: ['verifier']
        check-type: exit-positive
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
        cases:
          - values:
            - ldai 1
            - panda.Object
          - values:
            - ldai.64 0xC0BAC0BAC0BA
            - panda.Object
          - values:
            - fldai.64 2.7182818284590452353603
            - panda.Object


      - file-name: "with_wrong_acc_value_j"
        description: Check that verifier fails when accumulator contains wrong value - neither object, nor null
        isa:
          verification:
            - acc_obj_or_null
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa', 'verifier-failure', 'verifier-config']
        tags: [verifier, pa-verifier]
        bugid: ['3293']
        check-type: exit-positive
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
        cases:
          - values:
            - ldai 1
            - panda.Object
          - values:
            - ldai.64 0xC0BAC0BAC0BA
            - panda.Object
          - values:
            - fldai.64 2.7182818284590452353603
            - panda.Object


      - file-name: "when_1_is_expected_pa"
        description: Check that the instruction sets accumulator to 1 when expected
        isa:
          description: >
            Resolve object type by specified id and if an object in accumulator is an instance of the resolved type,
            put 1 into accumulator, otherwise put 0.
            Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root
            type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements.
        header-template: ['pandasm_header']
        check-type: empty
        tags: ['tsan', 'irtoc_ignore']
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
            subi 1
            return
          }
        cases:
          # O has a type of T
          - values:
            - |
              # O and T are of the same type, std object
                newobj v0, panda.Object
                lda.obj v0
            - panda.Object
          - values:
            - |
              # O and T are of the same type, custom object
                newobj v0, R
                lda.obj v0
            - R
          - values:
            - |
              # O and T are of the same type, string
                lda.str "xyz"
            - panda.String
          - values:
            - |
              # O and T are of the same type, type
                lda.type R
            - panda.Class
          # O is a subtype of T
          - values:
            - |
              # O is a subtype of T, std object
                newobj v0, panda.ClassCastException
                lda.obj v0
            - panda.Object
          - values:
            - |
              # O is a subtype of T, custom object
                newobj v0, R
                lda.obj v0
            - panda.Object
          - values:
            - |
              # O is a subtype of T, string
                lda.str "xyz"
            - panda.Object
          - values:
            - |
              # O is a subtype of T, type
                lda.type R
            - panda.Object
          # O[] has a type of T[]
          - values:
            - |
              # O and T are of the same type, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.Object[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of the same type, both arrays, custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - R[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of the same type, both arrays, string
                movi v0, 10
                newarr v0, v0, panda.String[]
                lda.obj v0
            - panda.String[]
            bugid: ["1805"]
          # O[] is a subtype of T[]
          - values:
            - |
              # O is a subtype of T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.ClassCastException[]
                lda.obj v0
            - panda.Object[]
            bugid: ["1805"]
          - values:
            - |
              # O is a subtype of T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - panda.Object[]
            bugid: ["1805"]
          # T is a root type for O[]
          - values:
            - |
              # T is a root type for O[], std object
                movi v0, 10
                newarr v0, v0, panda.ClassCastException[]
                lda.obj v0
            - panda.Object
          - values:
            - |
              # T is a root type for O[], custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - panda.Object


      - file-name: "when_1_is_expected_j"
        description: Check that the instruction sets accumulator to 1 when expected
        isa:
          description: >
            Resolve object type by specified id and if an object in accumulator is an instance of the resolved type,
            put 1 into accumulator, otherwise put 0.
            Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root
            type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements.
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa']
        check-type: empty
        tags: ['tsan', 'irtoc_ignore']
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
            subi 1
            return
          }
        cases:
          # O type is T
          - values:
            - |
              # O and T are of the same type, std object
                newobj v0, panda.Object
                lda.obj v0
            - panda.Object
          - values:
            - |
              # O and T are of the same type, custom object
                newobj v0, R
                lda.obj v0
            - R
          - values:
            - |
              # O and T are of the same type, string
                lda.str "xyz"
            - panda.String
          - values:
            - |
              # O and T are of the same type, type
                lda.type R
            - panda.Class
          # O extends or implements T
          - values:
            - |
              # O is a subtype of T, std object
                newobj v0, panda.NullPointerException
                lda.obj v0
            - panda.Object
          - values:
            - |
              # O implements T, std object
                newobj v0, panda.NullPointerException
                lda.obj v0
            - panda.io.Serializable
            ignore: true
            bugid: ["1806"]
          - values:
            - |
              # O is a subtype of T, custom object
                newobj v0, Q
                lda.obj v0
            - R
          - values:
            - |
              # O implements T, std object
                newobj v0, Q
                lda.obj v0
            - I
          - values:
            - |
              # O is a subtype of T, string
                lda.str "xyz"
            - panda.Object
          - values:
            - |
              # O implements T, string
                lda.str "xyz"
            - panda.CharSequence
            bugid: ["1806"]
          - values:
            - |
              # O is a subtype of T, type
                lda.type R
            - panda.Object
          - values:
            - |
              # O implements T , type
                lda.type R
            - panda.io.Serializable
          # O[] type is T[]
          - values:
            - |
              # O and T are of the same type, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.Object[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of the same type, both arrays, custom object
                movi v0, 10
                newarr v0, v0, A[]
                lda.obj v0
            - A[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of the same type, both arrays, string
                movi v0, 10
                newarr v0, v0, panda.String[]
                lda.obj v0
            - panda.String[]
            bugid: ["1805"]
          # O[] extends or implements T[]
          - values:
            - |
              # O is a subtype of T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - panda.Object[]
            bugid: ["1805"]
          - values:
            - |
              # O implements T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - panda.io.Serializable[]
            ignore: true
            bugid: ["1806"]
          - values:
            - |
              # O is a subtype of T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, Q[]
                lda.obj v0
            - R[]
            bugid: ["1805"]
          - values:
            - |
              # O implements T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, Q[]
                lda.obj v0
            - I[]
            bugid: ["1805"]
          # T is a root type for O[]
          - values:
            - |
              # T is a root type for O[], std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - panda.Object
          - values:
            - |
              # T is a root type for O[], custom object
              movi v0, 10
              newarr v0, v0, R[]
              lda.obj v0
            - panda.Object


      - file-name: "when_0_is_expected_pa"
        description: Check that the instruction sets accumulator to 0 when expected
        isa:
          description: >
            Resolve object type by specified id and if an object in accumulator is an instance of the resolved type,
            put 1 into accumulator, otherwise put 0.
            Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root
            type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements.
        header-template: ['pandasm_header']
        check-type: empty
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
            return
          }
        cases:
          # O is not related to T
          - values:
            - |
              # O and T are of unrelated types, std object
                newobj v0, panda.ClassCastException
                lda.obj v0
            - R
          - values:
            - |
              # O and T are of unrelated types, custom object
                newobj v0, R
                lda.obj v0
            - Q
          - values:
            - |
              # O and T are of unrelated types, string
                lda.str "xyz"
            - R
          - values:
            - |
              # O and T are of unrelated types, type
                lda.type R
            - panda.ClassCastException
          # O is a supertype of T
          - values:
            - |
              # O is a supertype of T, std object
                newobj v0, panda.Object
                lda.obj v0
            - panda.ClassCastException
          - values:
            - |
              # O is a supertype of T, custom object
                newobj v0, panda.Object
                lda.obj v0
            - R
          # O[] is not related to T[]
          - values:
            - |
              # O and T are of unrelated types, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.ClassCastException[]
                lda.obj v0
            - R[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of unrelated types, both arrays, custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - Q[]
            bugid: ["1805"]
          # O[] is a supertype of T[]
          - values:
            - |
              # O is a supertype of T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.ClassCastException[]
            bugid: ["1805"]
          - values:
            - |
              # O is a supertype of T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - R[]
            bugid: ["1805"]
          # T is not a root type for O[]
          - values:
            - |
              # T is a not a root type for O[], std object
                movi v0, 10
                newarr v0, v0, panda.ClassCastException[]
                lda.obj v0
            - panda.String
          - values:
            - |
              # T is a not a root type for O[], custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - panda.ClassCastException


      - file-name: "when_0_is_expected_j"
        description: Check that the instruction sets accumulator to 0 when expected
        isa:
          description: >
            Resolve object type by specified id and if an object in accumulator is an instance of the resolved type,
            put 1 into accumulator, otherwise put 0.
            Object of type O is instance of type T if O is the same as T or is subtype of T. For arrays T should be a root
            type in type hierarchy or T is such array that O array elements are the same or subtype of T array elements.
        header-template: ['PandaAssembly_header']
        runner-options: ['use-pa']
        check-type: empty
        tags: ['tsan', 'irtoc_ignore']
        code-template: |

          .function i32 main() {
            %s
            isinstance %s
            return
          }
        cases:
          # O is not related to T
          - values:
            - |
              # O and T are of unrelated types, std object
                newobj v0, panda.NullPointerException
                lda.obj v0
            - panda.Class
          - values:
            - |
              # O and T are of unrelated types, custom object
                newobj v0, R
                lda.obj v0
            - A
          - values:
            - |
              # O and T are of unrelated types, string
                lda.str "xyz"
            - panda.NullPointerException
          - values:
            - |
              # O and T are of unrelated types, type
                lda.type R
            - panda.String
          # O is a supertype of T
          - values:
            - |
              # O is a supertype of T, std object
                newobj v0, panda.Object
                lda.obj v0
            - panda.NullPointerException
          - values:
            - |
              # O doesn't implement T, std object
                newobj v0, panda.NullPointerException
                lda.obj v0
            - I
          - values:
            - |
              # O is a supertype of T, custom object
                newobj v0, R
                lda.obj v0
            - Q
          - values:
            - |
              # O doesn't implement T, custom object
                newobj v0, Q
                lda.obj v0
            - J
          - values:
            - |
              # O doesn't implement T, string
                lda.str "xyz"
            - I
          - values:
            - |
              # O doesn't implement T, type
                lda.type R
            - I
          # O[] is not related to T[]
          - values:
            - |
              # O and T are of unrelated types, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - R[]
            bugid: ["1805"]
          - values:
            - |
              # O and T are of unrelated types, both arrays, custom object
                movi v0, 10
                newarr v0, v0, A[]
                lda.obj v0
            - panda.Class[]
            bugid: ["1805"]
          # O[] is a supertype of T[]
          - values:
            - |
              # O is a supertype of T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.NullPointerException[]
            bugid: ["1805"]
          - values:
            - |
              # O doesn't implement T, both arrays, std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - I[]
            bugid: ["1805"]
          - values:
            - |
              # O is a supertype of T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, R[]
                lda.obj v0
            - Q[]
            bugid: ["1805"]
          - values:
            - |
              # O doesn't implement T, both arrays, custom object
                movi v0, 10
                newarr v0, v0, Q[]
                lda.obj v0
            - J[]
            bugid: ["1805"]
          # T is not a root type for O[]
          - values:
            - |
              # T is not a root type for O[], std object
                movi v0, 10
                newarr v0, v0, panda.NullPointerException[]
                lda.obj v0
            - panda.String
          - values:
            - |
              # T is not a root type for O[], custom object
                movi v0, 10
                newarr v0, v0, Q[]
                lda.obj v0
            - R

      - file-name: "with_nodef_typeid_pa"
        description: Check that NoClassDefFoundError is thrown when expected
        isa:
          exceptions:
            - x_classdef
        header-template: ['pandasm_header']
        tags: ['irtoc_ignore']
        check-type: empty
        bugid: ['5385']
        code-template: |
          .record panda.NoClassDefFoundError <external>
          .record panda.XYZObj <external>

          .function i32 main() {
          begin:
            %s
            isinstance %s
            ldai 1
            return
          end:
          catch_NCDFE:
            ldai 0
            return
          catch_all:
            ldai 2
            return
          .catch panda.NoClassDefFoundError, begin, end, catch_NCDFE
          .catchall begin, end, catch_all
          }
        cases:
          - values:
            - lda.null
            - panda.XYZObj
          - values:
            - lda.null
            - panda.XYZObj[]
          - values:
            - |
              #
                newobj v0, panda.Object
                lda.obj v0
            - panda.XYZObj
          - values:
            - |
              #
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.XYZObj[]

      - file-name: "with_nodef_typeid_j"
        description: Check that NoClassDefFoundError is thrown when expected
        isa:
          exceptions:
            - x_classdef
        header-template: ['PandaAssembly_header']
        tags: ['irtoc_ignore']
        runner-options: ['use-pa']
        check-type: empty
        bugid: ['5385']
        code-template: |
          .record panda.NoClassDefFoundError <external>
          .record panda.XYZObj <external>
          .record panda.XYZSub <panda.extends=panda.XYZObj, panda.implements=I> {}
          .record panda.XYZInf <panda.interface, external>

          .function i32 main() {
          begin:
            %s
            isinstance %s
            ldai 1
            return
          end:
          catch_NCDFE:
            ldai 0
            return
          catch_all:
            ldai 2
            return
          .catch panda.NoClassDefFoundError, begin, end, catch_NCDFE
          .catchall begin, end, catch_all
          }
        cases:
          - values:
            - lda.null
            - panda.XYZObj
          - values:
            - lda.null
            - panda.XYZObj[]
          - values:
            - |
              #
                newobj v0, panda.Object
                lda.obj v0
            - panda.XYZObj
          - values:
            - |
              #
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.XYZObj[]
          - values:
            - lda.null
            - panda.XYZSub
          - values:
            - lda.null
            - panda.XYZSub[]
          - values:
            - |
              #
                newobj v0, panda.Object
                lda.obj v0
            - panda.XYZSub
          - values:
            - |
              #
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.XYZSub[]
          - values:
            - lda.null
            - panda.XYZInf
          - values:
            - lda.null
            - panda.XYZInf[]
          - values:
            - |
              #
                newobj v0, panda.Object
                lda.obj v0
            - panda.XYZInf
          - values:
            - |
              #
                movi v0, 10
                newarr v0, v0, panda.Object[]
                lda.obj v0
            - panda.XYZInf[]