• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS of ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16package std.core;
17
18export type TypeDesc = string
19
20/*
21    Type intrinsics
22*/
23native function TypeAPIGetTypeDescriptor(o: NullishType): string
24
25native function TypeAPIGetTypeKind(td: TypeDesc): byte
26
27native function TypeAPIIsValueType(td: TypeDesc): boolean
28
29native function TypeAPIGetTypeName(td: TypeDesc): string
30
31native function TypeAPIGetTypeId(td: TypeDesc): long
32
33/*
34    Class type intrinsics
35*/
36native function TypeAPIGetClassAttributes(td: TypeDesc): int
37
38native function TypeAPIGetFieldsNum(td: TypeDesc): long
39
40native function TypeAPIGetOwnFieldsNum(td: TypeDesc): long
41
42native function TypeAPIGetField(td: TypeDesc, i: long): Field
43
44native function TypeAPIGetOwnField(td: TypeDesc, i: long): Field
45
46native function TypeAPIGetFieldByName(td: TypeDesc, name: string): Field
47
48native function TypeAPIGetStaticFieldValue(ownerTD: TypeDesc, name: string): NullishType
49
50native function TypeAPIGetMethodsNum(td: TypeDesc): long
51
52native function TypeAPIGetMethod(td: TypeDesc, i: long): Method
53
54native function TypeAPIGetConstructorsNum(td: TypeDesc): long
55
56native function TypeAPIGetConstructor(td: TypeDesc, i: long): Method
57
58native function TypeAPIGetBaseType(td: TypeDesc): TypeDesc
59
60native function TypeAPIGetInterfacesNum(td: TypeDesc): long
61
62native function TypeAPIGetInterface(td: TypeDesc, i: long): TypeDesc
63
64native function TypeAPISetStaticFieldValue(ownerTD: TypeDesc, name: string, val: NullishType): void
65
66native function TypeAPIIsInheritedFrom(ltd: TypeDesc, rtd:TypeDesc): boolean
67
68/*
69    Function (Lambda or Method) type intrinsics
70*/
71native function TypeAPIGetFunctionAttributes(td: TypeDesc): int
72
73native function TypeAPIGetReceiverType(td: TypeDesc): TypeDesc
74
75native function TypeAPIGetParametersNum(td: TypeDesc): long
76
77native function TypeAPIGetParameter(td: TypeDesc, i: long): Parameter
78
79native function TypeAPIGetResultType(td: TypeDesc): TypeDesc
80
81/*
82    Array type intrinsics
83*/
84native function TypeAPIGetArrayElementType(td: TypeDesc): TypeDesc
85
86native function TypeAPIMakeArrayInstance(td: TypeDesc, len: long): Object
87
88// NOTE(shumilov-petr): replace to enum, enum not available now
89export class TypeKind {
90    public static readonly NONE: byte       = 0x0
91    public static readonly VOID: byte       = 0x1
92
93    public static readonly CHAR: byte       = 0x2
94    public static readonly BOOLEAN: byte    = 0x3
95    public static readonly BYTE: byte       = 0x4
96    public static readonly SHORT: byte      = 0x5
97    public static readonly INT: byte        = 0x6
98    public static readonly LONG: byte       = 0x7
99    public static readonly FLOAT: byte      = 0x8
100    public static readonly DOUBLE: byte     = 0x9
101
102    public static readonly CLASS: byte      = 0xA
103    public static readonly STRING: byte     = 0xB
104    public static readonly INTERFACE: byte  = 0xC
105    public static readonly ARRAY: byte      = 0xD
106    public static readonly TUPLE: byte      = 0xE
107    public static readonly LAMBDA: byte     = 0xF
108    public static readonly METHOD: byte     = 0x10
109
110    public static readonly UNION: byte      = 0x11
111    public static readonly UNDEFINED: byte  = 0x12
112    public static readonly NULL: byte       = 0x13
113
114    public static readonly ENUM: byte       = 0x14
115
116    private constructor() {}
117}
118
119// 5 lower bits stores kind's id
120const TypeKindMask: byte = (1 << 6) - 1
121
122// NOTE(shumilov-petr): replace to enum, enum not available now
123export class ValueTypeDesc {
124    public static readonly BOOLEAN: TypeDesc   = "Z"
125    public static readonly BYTE: TypeDesc      = "B"
126    public static readonly SHORT: TypeDesc     = "S"
127    public static readonly CHAR: TypeDesc      = "C"
128    public static readonly INT: TypeDesc       = "I"
129    public static readonly LONG: TypeDesc      = "J"
130    public static readonly FLOAT: TypeDesc     = "F"
131    public static readonly DOUBLE: TypeDesc    = "D"
132}
133
134const ObjectTD = "Lstd/core/Object;" // NOTE(shumilov-petr): get td from runtime
135
136export const ObjectType: ClassType = new ClassType(ObjectTD)
137
138export class Attributes {
139    public static readonly STATIC: int      = 1 << 0  // Field, Method
140    public static readonly INHERITED: int   = 1 << 1  // Field, Method
141    public static readonly READONLY: int    = 1 << 2  // Field
142    public static readonly FINAL: int       = 1 << 3  // Method, Class
143    public static readonly ABSTRACT: int    = 1 << 4  // Method
144    public static readonly CONSTRUCTOR: int = 1 << 5  // Method
145    public static readonly REST: int        = 1 << 6  // Parameter
146    public static readonly OPTIONAL: int    = 1 << 7  // Parameter
147    public static readonly THROWING: int    = 1 << 8  // Method, Lambda
148    public static readonly NATIVE: int      = 1 << 9  // Method, Lambda
149    public static readonly ASYNC: int       = 1 << 10 // Method, Lambda
150    public static readonly NEVERRESULT: int = 1 << 11 // Method, Lambda
151    public static readonly GETTER: int      = 1 << 12 // Method
152    public static readonly SETTER: int      = 1 << 13 // Method
153
154    private constructor() {}
155}
156
157export class AccessModifier {
158    public static readonly PUBLIC: byte       = 0
159    public static readonly PRIVATE: byte      = 1
160    public static readonly PROTECTED: byte    = 2
161
162    private constructor() {}
163}
164
165/**
166 * Runtime ArkTS type representation
167 */
168export abstract class Type {
169    internal td: TypeDesc
170
171
172    /**
173     * Resolves type by descriptor
174     *
175     * @param td type descriptor
176     *
177     * @returns instance of appropriate type or null if resolving is failed
178     */
179    public static resolve(td: TypeDesc): Type | null {
180        let kind = (TypeAPIGetTypeKind(td) & TypeKindMask) as byte
181        switch (kind) {
182            case TypeKind.NONE:
183                return null
184            case TypeKind.VOID:
185                return VoidType.REF
186            case TypeKind.CHAR:
187                return TypeAPIIsValueType(td) ? CharType.VAL : CharType.REF
188            case TypeKind.BOOLEAN:
189                return TypeAPIIsValueType(td) ? BooleanType.VAL : BooleanType.REF
190            case TypeKind.BYTE:
191                return TypeAPIIsValueType(td) ? ByteType.VAL : ByteType.REF
192            case TypeKind.SHORT:
193                return TypeAPIIsValueType(td) ? ShortType.VAL : ShortType.REF
194            case TypeKind.INT:
195                return TypeAPIIsValueType(td) ? IntType.VAL : IntType.REF
196            case TypeKind.LONG:
197                return TypeAPIIsValueType(td) ? LongType.VAL : LongType.REF
198            case TypeKind.FLOAT:
199                return TypeAPIIsValueType(td) ? FloatType.VAL : FloatType.REF
200            case TypeKind.DOUBLE:
201                return TypeAPIIsValueType(td) ? DoubleType.VAL : DoubleType.REF
202
203            case TypeKind.CLASS:
204                return new ClassType(td)
205            case TypeKind.STRING:
206                return StringType.REF
207            case TypeKind.INTERFACE:
208                return new InterfaceType(td)
209            case TypeKind.ARRAY:
210                return ArrayType.getInstance(td)
211            case TypeKind.TUPLE:
212                return new TupleType(td)
213            case TypeKind.LAMBDA:
214                return new LambdaType(td)
215            case TypeKind.METHOD:
216                return new MethodType(td)
217            case TypeKind.UNION:
218                return new UnionType(td)
219            case TypeKind.UNDEFINED:
220                return UndefinedType.REF
221            case TypeKind.NULL:
222                return NullType.REF
223
224            case TypeKind.ENUM:
225                return new EnumType(td)
226            default:
227                // NOTE(shumilov-petr): unknown type, need error
228                assert(false)
229        }
230
231        return null;
232    }
233
234    /**
235     * Returns Type of value
236     *
237     * @param v value
238     *
239     * @returns Type instance of this value
240     */
241    public static of(v: boolean): Type {
242        return BooleanType.VAL
243    }
244
245    /**
246     * Returns Type of value
247     *
248     * @param v value
249     *
250     * @returns Type instance of this value
251     */
252    public static of(v: char): Type {
253        return CharType.VAL
254    }
255
256    /**
257     * Returns Type of value
258     *
259     * @param v value
260     *
261     * @returns Type instance of this value
262     */
263    public static of(v: byte): Type {
264        return ByteType.VAL
265    }
266
267    /**
268     * Returns Type of value
269     *
270     * @param v value
271     *
272     * @returns Type instance of this value
273     */
274    public static of(v: short): Type {
275        return ShortType.VAL
276    }
277
278    /**
279     * Returns Type of value
280     *
281     * @param v value
282     *
283     * @returns Type instance of this value
284     */
285    public static of(v: int): Type {
286        return IntType.VAL
287    }
288
289    /**
290     * Returns Type of value
291     *
292     * @param v value
293     *
294     * @returns Type instance of this value
295     */
296    public static of(v: long): Type {
297        return LongType.VAL
298    }
299
300    /**
301     * Returns Type of value
302     *
303     * @param v value
304     *
305     * @returns Type instance of this value
306     */
307    public static of(v: float): Type {
308        return FloatType.VAL
309    }
310
311    /**
312     * Returns Type of value
313     *
314     * @param v value
315     *
316     * @returns Type instance of this value
317     */
318    public static of(v: double): Type {
319        return DoubleType.VAL
320    }
321
322    // -----
323
324    public static of(o: NullishType): Type {
325        let td = TypeAPIGetTypeDescriptor(o)
326        return Type.resolve(td)!
327    }
328
329    // -----
330
331    public static of(v: Boolean): Type {
332        return BooleanType.REF
333    }
334
335    /**
336     * Returns Type of value
337     *
338     * @param v value
339     *
340     * @returns Type instance of this value
341     */
342    public static of(v: Char): Type {
343        return CharType.REF
344    }
345
346    /**
347     * Returns Type of value
348     *
349     * @param v value
350     *
351     * @returns Type instance of this value
352     */
353    public static of(v: Byte): Type {
354        return ByteType.REF
355    }
356
357    /**
358     * Returns Type of value
359     *
360     * @param v value
361     *
362     * @returns Type instance of this value
363     */
364    public static of(v: Short): Type {
365        return ShortType.REF
366    }
367
368    /**
369     * Returns Type of value
370     *
371     * @param v value
372     *
373     * @returns Type instance of this value
374     */
375    public static of(v: Int): Type {
376        return IntType.REF
377    }
378
379    /**
380     * Returns Type of value
381     *
382     * @param v value
383     *
384     * @returns Type instance of this value
385     */
386    public static of(v: Long): Type {
387        return LongType.REF
388    }
389
390    /**
391     * Returns Type of value
392     *
393     * @param v value
394     *
395     * @returns Type instance of this value
396     */
397    public static of(v: Float): Type {
398        return FloatType.REF
399    }
400
401    /**
402     * Returns Type of value
403     *
404     * @param v value
405     *
406     * @returns Type instance of this value
407     */
408    public static of(v: Double): Type {
409        return DoubleType.REF
410    }
411
412    // -----
413
414    /**
415     * Returns Type of value
416     *
417     * @param v value
418     *
419     * @returns Type instance of this value
420     */
421    public static of(v: string): Type {
422        return StringType.REF
423    }
424
425    // -----
426
427    /**
428     * Returns Type of value
429     *
430     * @param v value
431     *
432     * @returns Type instance of this value
433     */
434    public static of(v: boolean[]): Type {
435        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.BOOLEAN)
436    }
437
438    /**
439     * Returns Type of value
440     *
441     * @param v value
442     *
443     * @returns Type instance of this value
444     */
445    public static of(v: char[]): Type {
446        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.CHAR)
447    }
448
449    /**
450     * Returns Type of value
451     *
452     * @param v value
453     *
454     * @returns Type instance of this value
455     */
456    public static of(v: byte[]): Type {
457        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.BYTE)
458    }
459
460    /**
461     * Returns Type of value
462     *
463     * @param v value
464     *
465     * @returns Type instance of this value
466     */
467    public static of(v: short[]): Type {
468        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.SHORT)
469    }
470
471    /**
472     * Returns Type of value
473     *
474     * @param v value
475     *
476     * @returns Type instance of this value
477     */
478    public static of(v: int[]): Type {
479        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.INT)
480    }
481
482    /**
483     * Returns Type of value
484     *
485     * @param v value
486     *
487     * @returns Type instance of this value
488     */
489    public static of(v: long[]): Type {
490        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.LONG)
491    }
492
493    /**
494     * Returns Type of value
495     *
496     * @param v value
497     *
498     * @returns Type instance of this value
499     */
500    public static of(v: float[]): Type {
501        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.FLOAT)
502    }
503
504    /**
505     * Returns Type of value
506     *
507     * @param v value
508     *
509     * @returns Type instance of this value
510     */
511    public static of(v: double[]): Type {
512        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v), ValueTypeDesc.DOUBLE)
513    }
514
515    // -----
516
517    /**
518     * Returns Type of value
519     *
520     * @param v value
521     *
522     * @returns Type instance of this value
523     */
524    public static of(v: Object[]): Type {
525        return ArrayType.getInstance(TypeAPIGetTypeDescriptor(v))
526    }
527
528    public abstract isPrimitive(): boolean // Or Composite
529
530    public abstract isReference(): boolean // Or Value
531
532    public abstract hasName(): boolean
533
534    // T.subTypeOf(U) means that `T extends U`
535    protected subTypeOf(other: Type): boolean {
536        if (this.equals(other)) {
537            return true
538        } else if (other.equals(ObjectType)) {
539            let isNullish = (this) instanceof UndefinedType || (this) instanceof NullType
540            return this.isReference() && !isNullish
541        }
542        return false
543    }
544
545    // T.assignableFrom(U) means that `T <- U`
546    public assignableFrom(other: Type): boolean {
547        if (other.subTypeOf(this)) {
548            return true
549        }
550        if (this.isNumericType() && other.isNumericType()) {
551            return true
552        }
553        return false
554    }
555
556    internal abstract convertObject(obj: NullishType): NullishType;
557
558    private isNumericType(): boolean {
559        return (this) instanceof ByteType
560            || (this) instanceof ShortType
561            || (this) instanceof IntType
562            || (this) instanceof LongType
563            || (this) instanceof FloatType
564            || (this) instanceof DoubleType
565    }
566
567    public getId(): long {
568        return TypeAPIGetTypeId(this.td);
569    }
570
571    public abstract getName(): string
572
573    /**
574     * Returns literal of type if exists
575     *
576     * @returns type literal
577     */
578    public abstract getLiteral(): string
579
580    public override toString(): string {
581        if (this.hasName()) {
582            return this.getName()
583        }
584        return this.getLiteral()
585    }
586
587    abstract equals(other: Type): boolean
588}
589
590/**
591 * Represents null type
592 */
593export final class NullType extends Type {
594    public static readonly REF: NullType = new NullType()
595
596    private constructor() {
597        this.td = TypeAPIGetTypeDescriptor(null)
598    }
599
600    /**
601     * Checks whether type is primitive or composite
602     *
603     * @returns true if type is primitive and false otherwise
604     */
605    public override isPrimitive(): boolean {
606        return true
607    }
608
609    /**
610     * Checks whether type is reference or composite
611     *
612     * @returns true if type is reference and false otherwise
613     */
614    public override isReference(): boolean {
615        return true
616    }
617
618    /**
619     * Checks whether type has name
620     *
621     * @returns true if type has name
622     */
623    public override hasName(): boolean {
624        return true
625    }
626
627    /**
628     * Returns name of type
629     *
630     * @throws error in case of absence of name
631     *
632     * @returns type name
633     */
634    public override getName(): string {
635        return "null"
636    }
637
638    /**
639     * Returns literal of type if exists
640     *
641     * @returns type literal
642     */
643    public override getLiteral(): string {
644        return "null"
645    }
646
647    /**
648     * Checks for equality this instance with provided object, treated as a DoubleType
649     *
650     * @param other type to be checked against
651     *
652     * @returns true if object also has NullType
653     */
654    public override equals(other: Type): boolean {
655        return other instanceof NullType
656    }
657
658    internal override convertObject(obj: NullishType): NullishType {
659        if (obj != null) {
660            throw new Error("invalid conversion")
661        }
662        return null
663    }
664}
665
666export final class UndefinedType extends Type {
667    public static readonly REF: UndefinedType = new UndefinedType()
668
669    private constructor() {
670        this.td = TypeAPIGetTypeDescriptor(undefined)
671    }
672
673    /**
674     * Checks whether type is primitive or composite
675     *
676     * @returns true if type is primitive and false otherwise
677     */
678    public override isPrimitive(): boolean {
679        return true
680    }
681
682    /**
683     * Checks whether type is reference or composite
684     *
685     * @returns true if type is reference and false otherwise
686     */
687    public override isReference(): boolean {
688        return true
689    }
690
691    /**
692     * Checks whether type has name
693     *
694     * @returns true if type has name
695     */
696    public override hasName(): boolean {
697        return true
698    }
699
700    /**
701     * Returns name of type if exists and empty string otherwise
702     *
703     * @returns type name
704     */
705    public override getName(): string {
706        return "undefined"
707    }
708
709    /**
710     * Returns literal of type if exists
711     *
712     * @returns type literal
713     */
714    public override getLiteral(): string {
715        return "undefined"
716    }
717
718    /**
719     * Checks for equality this instance with provided object, treated as a UndefinedType
720     *
721     * @param other type to be checked against
722     *
723     * @returns true if object also has UndefinedType
724     */
725    public override equals(other: Type): boolean {
726        return other instanceof UndefinedType
727    }
728
729    internal override convertObject(obj: NullishType): NullishType {
730        throw new Error("todo(kprokopenko): add when undefined becomes available")
731    }
732}
733
734/**
735 * Represents void type
736 */
737export final class VoidType extends Type {
738    public static readonly REF: VoidType = new VoidType()
739
740    private constructor() {
741        this.td = TypeAPIGetTypeDescriptor(Void.void_instance)
742    }
743
744    /**
745     * Checks whether type is primitive or composite
746     *
747     * @returns true if type is primitive and false otherwise
748     */
749    public override isPrimitive(): boolean {
750        return true
751    }
752
753    /**
754     * Checks whether type is reference or composite
755     *
756     * @returns true if type is reference and false otherwise
757     */
758    public override isReference(): boolean {
759        return true
760    }
761
762    /**
763     * Checks whether type has name
764     *
765     * @returns true if type has name
766     */
767    public override hasName(): boolean {
768        return true
769    }
770
771    /**
772     * Returns name of type if exists and empty string otherwise
773     *
774     * @returns type name
775     */
776    public override getName(): string {
777        return "void"
778    }
779
780    /**
781     * Returns literal of type if exists
782     *
783     * @returns type literal
784     */
785    public override getLiteral(): string {
786        return "void"
787    }
788
789    /**
790     * Checks for equality this instance with provided object, treated as a VoidType
791     *
792     * @param other type to be checked against
793     *
794     * @returns true if object also has VoidType
795     */
796    public override equals(other: Type): boolean {
797        return other instanceof VoidType
798    }
799
800    internal override convertObject(obj: NullishType): NullishType {
801        if (!this.assignableFrom(Type.of(obj))) {
802            throw new Error("invalid conversion")
803        }
804        return Void.void_instance
805    }
806}
807
808/**
809 * Represents char type
810 *
811 * @note Boxed Char and primitive char both have CharType
812 */
813
814export final class CharType extends Type {
815    public static readonly VAL: CharType = new CharType(ValueTypeDesc.CHAR, true)
816    public static readonly REF: CharType = new CharType(TypeAPIGetTypeDescriptor(new Char()), false)
817
818    private isValue: boolean
819
820    private constructor(td: TypeDesc, isValue: boolean) {
821        this.td = td
822        this.isValue = isValue
823    }
824
825    /**
826     * Checks whether type is primitive or composite
827     *
828     * @returns true if type is primitive and false otherwise
829     */
830    public override isPrimitive(): boolean {
831        return true
832    }
833
834    /**
835     * Checks whether type is reference or composite
836     *
837     * @returns true if type is reference and false otherwise
838     */
839    public override isReference(): boolean {
840        return !this.isValue
841    }
842
843    /**
844     * Checks whether type has name
845     *
846     * @returns true if type has name
847     */
848    public override hasName(): boolean {
849        return !this.isValue
850    }
851
852    /**
853     * Returns name of type
854     *
855     * @throws error in case of absence of name
856     *
857     * @returns name of type
858     */
859    //NOTE(kirill-mitkin): add error
860    public override getName(): string {
861        if (this.isValue) {
862            return ""
863        }
864        return TypeAPIGetTypeName(this.td)
865    }
866
867    /**
868     * Returns literal of type if exists
869     *
870     * @returns type literal
871     */
872    public override getLiteral(): string {
873        if (this.isValue) {
874            return "char"
875        }
876        return "Char"
877    }
878
879    public override equals(other: Type): boolean {
880        return other instanceof CharType && this.isValue != (other as CharType).isReference()
881    }
882
883    internal override convertObject(obj: NullishType): NullishType {
884        const objType = Type.of(obj)
885        if (!this.equals(objType)) {
886            throw new Error("invalid type for conversion")
887        }
888        return obj
889    }
890}
891
892/**
893 * Represents boolean type
894 *
895 * @note Boxed Boolean and primitive boolean both have BooleanType
896 */
897export final class BooleanType extends Type {
898    public static readonly VAL: BooleanType = new BooleanType(ValueTypeDesc.BOOLEAN, true)
899    public static readonly REF: BooleanType = new BooleanType(TypeAPIGetTypeDescriptor(new Boolean()), false)
900
901    private isValue: boolean
902
903    private constructor(td: TypeDesc, isValue: boolean) {
904        this.td = td
905        this.isValue = isValue
906    }
907
908    /**
909     * Checks whether type is primitive or composite
910     *
911     * @returns true if type is primitive and false otherwise
912     */
913    public override isPrimitive(): boolean {
914        return true
915    }
916
917    /**
918     * Checks whether type is reference or composite
919     *
920     * @returns true if type is reference and false otherwise
921     */
922    public override isReference(): boolean {
923        return !this.isValue
924    }
925
926    /**
927     * Checks whether type has name
928     *
929     * @returns true if type has name
930     */
931    public override hasName(): boolean {
932        return !this.isValue
933    }
934
935    /**
936     * Returns name of type
937     *
938     * @throws error in case of absence of name
939     *
940     * @returns name of type
941     */
942    //NOTE(kirill-mitkin): add error
943    public override getName(): string {
944        if (this.isValue) {
945            return ""
946        }
947        return TypeAPIGetTypeName(this.td)
948    }
949
950    /**
951     * Returns literal of type if exists
952     *
953     * @returns type literal
954     */
955    public override getLiteral(): string {
956        if (this.isValue) {
957            return "boolean"
958        }
959        return "Boolean"
960    }
961
962    override equals(other: Type): boolean {
963        return other instanceof BooleanType && this.isValue != (other as BooleanType).isReference()
964    }
965
966    internal override convertObject(obj: NullishType): NullishType {
967        const objType = Type.of(obj)
968        if (!this.equals(objType)) {
969            throw new Error("invalid type for conversion")
970        }
971        return obj
972    }
973}
974
975/**
976 * Represents byte type
977 *
978 * @note Boxed Byte and primitive byte both have ByteType
979 */
980export final class ByteType extends Type {
981    public static readonly VAL: ByteType = new ByteType(ValueTypeDesc.BYTE, true)
982    public static readonly REF: ByteType = new ByteType(TypeAPIGetTypeDescriptor(new Byte()), false)
983
984    private isValue: boolean
985
986    private constructor(td: TypeDesc, isValue: boolean) {
987        this.td = td
988        this.isValue = isValue
989    }
990
991    /**
992     * Checks whether type is primitive or composite
993     *
994     * @returns true if type is primitive and false otherwise
995     */
996    public override isPrimitive(): boolean {
997        return true
998    }
999
1000    /**
1001     * Checks whether type is reference or composite
1002     *
1003     * @returns true if type is reference and false otherwise
1004     */
1005    public override isReference(): boolean {
1006        return !this.isValue
1007    }
1008
1009    /**
1010     * Checks whether type has name
1011     *
1012     * @returns true if type has name
1013     */
1014    public override hasName(): boolean {
1015        return !this.isValue
1016    }
1017
1018    /**
1019     * Returns name of type
1020     *
1021     * @throws error in case of absence of name
1022     *
1023     * @returns name of type
1024     */
1025    //NOTE(kirill-mitkin): add error
1026    public override getName(): string {
1027        if (this.isValue) {
1028            return ""
1029        }
1030        return TypeAPIGetTypeName(this.td)
1031    }
1032
1033    /**
1034     * Returns literal of type if exists
1035     *
1036     * @returns type literal
1037     */
1038    public override getLiteral(): string {
1039        if (this.isValue) {
1040            return "byte"
1041        }
1042        return "Byte"
1043    }
1044
1045    public override equals(other: Type): boolean {
1046        return other instanceof ByteType && this.isValue != (other as ByteType).isReference()
1047    }
1048
1049    internal override convertObject(obj: NullishType): NullishType {
1050        const objType = Type.of(obj)
1051        if (this.equals(objType)) {
1052            return obj
1053        }
1054        if (!this.assignableFrom(objType)) {
1055            throw new Error("invalid conversion")
1056        }
1057        return (obj as Numeric).byteValue()
1058    }
1059}
1060
1061/**
1062 * Represents short type
1063 *
1064 * @note Boxed Short and primitive short both have ShortType
1065 */
1066export final class ShortType extends Type {
1067    public static readonly VAL: ShortType = new ShortType(ValueTypeDesc.SHORT, true)
1068    public static readonly REF: ShortType = new ShortType(TypeAPIGetTypeDescriptor(new Short()), false)
1069
1070    private isValue: boolean
1071
1072    private constructor(td: TypeDesc, isValue: boolean) {
1073        this.td = td
1074        this.isValue = isValue
1075    }
1076
1077    /**
1078     * Checks whether type is primitive or composite
1079     *
1080     * @returns true if type is primitive and false otherwise
1081     */
1082    public override isPrimitive(): boolean {
1083        return true
1084    }
1085
1086    /**
1087     * Checks whether type is reference or composite
1088     *
1089     * @returns true if type is reference and false otherwise
1090     */
1091    public override isReference(): boolean {
1092        return !this.isValue
1093    }
1094
1095    /**
1096     * Checks whether type has name
1097     *
1098     * @returns true if type has name
1099     */
1100    public override hasName(): boolean {
1101        return !this.isValue
1102    }
1103
1104    /**
1105     * Returns name of type
1106     *
1107     * @throws error in case of absence of name
1108     *
1109     * @returns name of type
1110     */
1111    //NOTE(kirill-mitkin): add error
1112    public override getName(): string {
1113        if (this.isValue) {
1114            return ""
1115        }
1116        return TypeAPIGetTypeName(this.td)
1117    }
1118
1119    /**
1120     * Returns literal of type
1121     *
1122     * @returns type literal
1123     */
1124    public override getLiteral(): string {
1125        if (this.isValue) {
1126            return "short"
1127        }
1128        return "Short"
1129    }
1130
1131    /**
1132     * Checks for equality this instance with provided object, treated as a ShortType
1133     *
1134     * @param other type to be checked against
1135     *
1136     * @returns true if object also has ShortType and
1137     * this type and other type both are reference or value
1138     */
1139    public override equals(other: Type): boolean {
1140        return other instanceof ShortType && this.isValue != (other as ShortType).isReference()
1141    }
1142
1143    internal override convertObject(obj: NullishType): NullishType {
1144        const objType = Type.of(obj)
1145        if (this.equals(objType)) {
1146            return obj
1147        }
1148        if (!this.assignableFrom(objType)) {
1149            throw new Error("invalid conversion")
1150        }
1151        return (obj as Numeric).shortValue()
1152    }
1153}
1154
1155/**
1156 * Represents int type
1157 *
1158 * @note Boxed Int and primitive int both have IntType
1159 */
1160export final class IntType extends Type {
1161    public static readonly VAL: IntType = new IntType(ValueTypeDesc.INT, true)
1162    public static readonly REF: IntType = new IntType(TypeAPIGetTypeDescriptor(new Int()), false)
1163
1164    private isValue: boolean
1165
1166    private constructor(td: TypeDesc, isValue: boolean) {
1167        this.td = td
1168        this.isValue = isValue
1169    }
1170
1171    /**
1172     * Checks whether type is primitive or composite
1173     *
1174     * @returns true if type is primitive and false otherwise
1175     */
1176    public override isPrimitive(): boolean {
1177        return true
1178    }
1179
1180    /**
1181     * Checks whether type is reference or composite
1182     *
1183     * @returns true if type is reference and false otherwise
1184     */
1185    public override isReference(): boolean {
1186        return !this.isValue
1187    }
1188
1189    /**
1190     * Checks whether type has name
1191     *
1192     * @returns true if type has name
1193     */
1194    public override hasName(): boolean {
1195        return !this.isValue
1196    }
1197
1198    /**
1199     * Returns name of type if exists and empty string otherwise
1200     *
1201     * @throws error in case of absence of name
1202     *
1203     * @returns type name
1204     */
1205    //NOTE(kirill-mitkin): add error
1206    public override getName(): string {
1207        if (this.isValue) {
1208            return ""
1209        }
1210        return TypeAPIGetTypeName(this.td)
1211    }
1212
1213    /**
1214     * Returns literal of type if exists
1215     *
1216     * @returns type literal
1217     */
1218    public override getLiteral(): string {
1219        if (this.isValue) {
1220            return "int"
1221        }
1222        return "Int"
1223    }
1224
1225    /**
1226     * Checks for equality this instance with provided object, treated as a IntType
1227     *
1228     * @param other type to be checked against
1229     *
1230     * @returns true if object also has IntType and
1231     * this type and other type both are reference or value
1232     */
1233    public override equals(other: Type): boolean {
1234        return other instanceof IntType && this.isValue != (other as IntType).isReference()
1235    }
1236
1237    internal override convertObject(obj: NullishType): NullishType {
1238        const objType = Type.of(obj)
1239        if (this.equals(objType)) {
1240            return obj
1241        }
1242        if (!this.assignableFrom(objType)) {
1243            throw new Error("invalid conversion")
1244        }
1245        return (obj as Numeric).intValue()
1246    }
1247}
1248
1249/**
1250 * Represents long type
1251 *
1252 * @note Boxed Long and primitive long both have LongType
1253 */
1254export final class LongType extends Type {
1255    public static readonly VAL = new LongType(ValueTypeDesc.LONG, true)
1256    public static readonly REF = new LongType(TypeAPIGetTypeDescriptor(new Long()), false)
1257
1258    private isValue: boolean
1259
1260    private constructor(td: TypeDesc, isValue: boolean) {
1261        this.td = td
1262        this.isValue = isValue
1263    }
1264
1265    /**
1266     * Checks whether type is primitive or composite
1267     *
1268     * @returns true if type is primitive and false otherwise
1269     */
1270    public override isPrimitive(): boolean {
1271        return true
1272    }
1273
1274    /**
1275     * Checks whether type is reference or composite
1276     *
1277     * @returns true if type is reference and false otherwise
1278     */
1279    public override isReference(): boolean {
1280        return !this.isValue
1281    }
1282
1283    /**
1284     * Checks whether type has name
1285     *
1286     * @returns true if type has name
1287     */
1288    public override hasName(): boolean {
1289        return !this.isValue
1290    }
1291
1292    /**
1293     * Returns name of type if exists and empty string otherwise
1294     *
1295     * @throws error in case of absence of name
1296     *
1297     * @returns type name
1298     */
1299    //NOTE(kirill-mitkin): add error
1300    public override getName(): string {
1301        if (this.isValue) {
1302            return ""
1303        }
1304        return TypeAPIGetTypeName(this.td)
1305    }
1306
1307    /**
1308     * Returns literal of type if exists
1309     *
1310     * @returns type literal
1311     */
1312    public override getLiteral(): string {
1313        if (this.isValue) {
1314            return "long"
1315        }
1316        return "Long"
1317    }
1318
1319    /**
1320     * Checks for equality this instance with provided object, treated as a LongType
1321     *
1322     * @param other type to be checked against
1323     *
1324     * @returns true if object also has LongType and
1325     * this type and other type both are reference or value
1326     */
1327    public override equals(other: Type): boolean {
1328        return other instanceof LongType && this.isValue != (other as LongType).isReference()
1329    }
1330
1331    internal override convertObject(obj: NullishType): NullishType {
1332        const objType = Type.of(obj)
1333        if (this.equals(objType)) {
1334            return obj
1335        }
1336        if (!this.assignableFrom(objType)) {
1337            throw new Error("invalid conversion")
1338        }
1339        return (obj as Numeric).longValue()
1340    }
1341}
1342
1343/**
1344 * Represents float type
1345 *
1346 * @note Boxed Float and primitive float both have FloatType
1347 */
1348export final class FloatType extends Type {
1349    public static readonly VAL: FloatType = new FloatType(ValueTypeDesc.FLOAT, true)
1350    public static readonly REF: FloatType = new FloatType(TypeAPIGetTypeDescriptor(new Float()), false)
1351
1352    private isValue: boolean
1353
1354    private constructor(td: TypeDesc, isValue: boolean) {
1355        this.td = td
1356        this.isValue = isValue
1357    }
1358
1359    /**
1360     * Checks whether type is primitive or composite
1361     *
1362     * @returns true if type is primitive and false otherwise
1363     */
1364    public override isPrimitive(): boolean {
1365        return true
1366    }
1367
1368    /**
1369     * Checks whether type is reference or composite
1370     *
1371     * @returns true if type is reference and false otherwise
1372     */
1373    public override isReference(): boolean {
1374        return !this.isValue
1375    }
1376
1377    /**
1378     * Checks whether type has name
1379     *
1380     * @returns true if type has name
1381     */
1382    public override hasName(): boolean {
1383        return !this.isValue
1384    }
1385
1386    /**
1387     * Returns name of type
1388     *
1389     * @throws error in case of absence of name
1390     *
1391     * @returns name of type
1392     */
1393    //NOTE(kirill-mitkin): add error
1394    public override getName(): string {
1395        if (this.isValue) {
1396            return ""
1397        }
1398        return TypeAPIGetTypeName(this.td)
1399    }
1400
1401    /**
1402     * Returns literal of type if exists
1403     *
1404     * @returns type literal
1405     */
1406    public override getLiteral(): string {
1407        if (this.isValue) {
1408            return "float"
1409        }
1410        return "Float"
1411    }
1412
1413    /**
1414     * Checks for equality this instance with provided object, treated as a FloatType
1415     *
1416     * @param other type to be checked against
1417     *
1418     * @returns true if object also has FloatType and
1419     * this type and other type both are reference or value
1420     */
1421    public override equals(other: Type): boolean {
1422        return other instanceof FloatType && this.isValue != (other as FloatType).isReference()
1423    }
1424
1425    internal override convertObject(obj: NullishType): NullishType {
1426        const objType = Type.of(obj)
1427        if (this.equals(objType)) {
1428            return obj
1429        }
1430        if (!this.assignableFrom(objType)) {
1431            throw new Error("invalid conversion")
1432        }
1433        return (obj as Numeric).floatValue()
1434    }
1435}
1436
1437/**
1438 * Represents double type
1439 *
1440 * @note Boxed Double and primitive double both have DoubleType
1441 */
1442export final class DoubleType extends Type {
1443    public static readonly VAL: DoubleType = new DoubleType(ValueTypeDesc.DOUBLE, true)
1444    public static readonly REF: DoubleType = new DoubleType(TypeAPIGetTypeDescriptor(new Double()), false)
1445
1446    private isValue: boolean
1447
1448    private constructor(td: TypeDesc, isValue: boolean) {
1449        this.td = td
1450        this.isValue = isValue
1451    }
1452
1453    /**
1454     * Checks whether type is primitive or composite
1455     *
1456     * @returns true if type is primitive and false otherwise
1457     */
1458    public override isPrimitive(): boolean {
1459        return true
1460    }
1461
1462    /**
1463     * Checks whether type is reference or composite
1464     *
1465     * @returns true if type is reference and false otherwise
1466     */
1467    public override isReference(): boolean {
1468        return !this.isValue
1469    }
1470
1471    /**
1472     * Checks whether type has name
1473     *
1474     * @returns true if type has name
1475     */
1476    public override hasName(): boolean {
1477        return !this.isValue
1478    }
1479
1480    /**
1481     * Returns name of type
1482     *
1483     * @throws error in case of absence of name
1484     *
1485     * @returns name of type
1486     */
1487    //NOTE(kirill-mitkin): add error
1488    public override getName(): string {
1489        if (this.isValue) {
1490            return ""
1491        }
1492        return TypeAPIGetTypeName(this.td)
1493    }
1494
1495    /**
1496     * Returns literal of type if exists
1497     *
1498     * @returns type literal
1499     */
1500    public override getLiteral(): string {
1501        if (this.isValue) {
1502            return "double"
1503        }
1504        return "Double"
1505    }
1506
1507    /**
1508     * Checks for equality this instance with provided object, treated as a DoubleType
1509     *
1510     * @param other type to be checked against
1511     *
1512     * @returns true if object also has DoubleType and
1513     * this type and other type both are reference or value
1514     */
1515    public override equals(other: Type): boolean {
1516        return other instanceof DoubleType && this.isValue != (other as DoubleType).isReference()
1517    }
1518
1519    internal override convertObject(obj: NullishType): NullishType {
1520        const objType = Type.of(obj)
1521        if (this.equals(objType)) {
1522            return obj
1523        }
1524        if (!this.assignableFrom(objType)) {
1525            throw new Error("invalid conversion")
1526        }
1527        return (obj as Numeric).doubleValue()
1528    }
1529}
1530
1531/**
1532 * Represents type of classes
1533 */
1534export final class ClassType extends Type {
1535    private readonly attrs: int
1536
1537    internal constructor(td: TypeDesc) {
1538        this.td = td
1539        this.attrs = TypeAPIGetClassAttributes(td)
1540    }
1541
1542    /**
1543     * Checks whether type is primitive or composite
1544     *
1545     * @returns true if type is primitive and false otherwise
1546     */
1547    public override isPrimitive(): boolean {
1548        return false
1549    }
1550
1551    /**
1552     * Checks whether type is reference or composite
1553     *
1554     * @returns true if type is reference and false otherwise
1555     */
1556    public override isReference(): boolean {
1557        return true
1558    }
1559
1560    /**
1561     * Checks whether type has name
1562     *
1563     * @returns true if type has name
1564     */
1565    public override hasName(): boolean {
1566        return true
1567    }
1568
1569    /**
1570     * Returns name of type
1571     *
1572     * @returns type name
1573     */
1574    public override getName(): string {
1575        return TypeAPIGetTypeName(this.td)
1576    }
1577
1578    /**
1579     * Returns literal of type if exists
1580     *
1581     * @returns type literal
1582     */
1583    public override getLiteral(): string {
1584        // NOTE(shumilov-petr): not implemented
1585        return "class{...}"
1586    }
1587
1588    /**
1589     * Checks for equality this instance with provided object, treated as a ClassType
1590     *
1591     * @param other type to be checked against
1592     *
1593     * @returns true if object also has ClassType and their names are the same
1594     */
1595    public override equals(other: Type): boolean {
1596        return (other instanceof ClassType) && (other as ClassType).td == this.td
1597    }
1598
1599    /**
1600     * Returns base type of this class
1601     * If this type is the type of Object class then returns this
1602     *
1603     * @returns base type of class
1604     */
1605    public getBaseType(): ClassType {
1606        return Type.resolve(TypeAPIGetBaseType(this.td))! as ClassType
1607    }
1608
1609    /**
1610     * Returns number of direct superinterfaces of this class
1611     *
1612     * @returns number of interfaces that was implemented by this class directly
1613     */
1614    public getInterfacesNum(): long {
1615        return TypeAPIGetInterfacesNum(this.td)
1616    }
1617
1618    /**
1619     * Returns ith direct superinterface of this class
1620     *
1621     * @param i index
1622     *
1623     * @throws error when i greater then num of interfaces
1624     *
1625     * @returns type of ith superinterface
1626     */
1627    public getInterface(i: long): InterfaceType {
1628        return Type.resolve(TypeAPIGetInterface(this.td, i))! as InterfaceType
1629    }
1630
1631    /**
1632     * Returns number of all fields
1633     * including static, instance and also fields of all its superclasses
1634     *
1635     * @returns number of fields
1636     *
1637     * @example
1638     *
1639     * ```
1640     * class A {
1641     *     a : int
1642     * }
1643     *
1644     * class B extends A {
1645     *     b : int
1646     * }
1647     * ```
1648     * let bType class type of B, then `bType.getFieldsNum()` returns 2
1649     * Note that Object class also is super class of B
1650     */
1651    public getFieldsNum(): long {
1652        return TypeAPIGetFieldsNum(this.td)
1653    }
1654
1655    public getOwnFieldsNum(): long {
1656        return TypeAPIGetOwnFieldsNum(this.td)
1657    }
1658
1659    /**
1660     * Returns ith Field of this class
1661     *
1662     * @param i index (using flat semantic)
1663     *
1664     * @throws error when i greater then number of fields
1665     *
1666     * @returns ith Field
1667     */
1668    //NOTE(kirill-mitkin): add error
1669    public getField(i: long): Field {
1670        return TypeAPIGetField(this.td, i)
1671    }
1672
1673    public getOwnField(i: long): Field {
1674        return TypeAPIGetOwnField(this.td, i)
1675    }
1676
1677    /**
1678     * Find Field by name
1679     *
1680     * @param name name of field
1681     *
1682     * @throws error when class doesn't have field with this name
1683     *
1684     * @returns Field instance with this name
1685     */
1686    //NOTE(kirill-mitkin): add error
1687    public getFieldByName(name: string): Field {
1688        return TypeAPIGetFieldByName(this.td, name)
1689    }
1690
1691    /**
1692     * Returns number of methods of this class
1693     * including static methods and methods of super classes
1694     * @example
1695     *
1696     * ```
1697     * class A {
1698     *     a(): void {}
1699     * }
1700     *
1701     * class B extends A {
1702     *     b(): void {}
1703     * }
1704     * ```
1705     * let bType class type of B, then `bType.getMethodsNum()` returns at least 2
1706     * Note that Object class also super class of B
1707     * @returns number of methods
1708     */
1709    public getMethodsNum(): long {
1710        return TypeAPIGetMethodsNum(this.td)
1711    }
1712
1713    /**
1714     * Returns ith Method of this class
1715     *
1716     * @param i index (using flat semantic)
1717     *
1718     * @throws error when i greater then number of methods
1719     *
1720     * @returns ith method
1721     */
1722    //NOTE(kirill-mitkin): add error
1723    public getMethod(i: long): Method {
1724        return TypeAPIGetMethod(this.td, i)
1725    }
1726
1727    /**
1728     * Returns number of constructors of this class
1729     * Note that constructors of super class isn't considered as constructors of this class
1730     *
1731     * @returns number of constructors
1732     */
1733    public getConstructorsNum(): long {
1734        return TypeAPIGetConstructorsNum(this.td)
1735    }
1736
1737    /**
1738     * Returns ith constructor of this class
1739     *
1740     * @param i index
1741     *
1742     * @throws error then i greater then number of constructors
1743     *
1744     * @returns {@link Method} instance representing ith constructor
1745     */
1746    //NOTE(kirill-mitkin): add error
1747    public getConstructor(i: long): Method {
1748        return TypeAPIGetConstructor(this.td, i)
1749    }
1750
1751    /**
1752     * Checks for existence of empty constructor of this class
1753     *
1754     * @returns true if there is empty constructor of this class
1755     */
1756    public hasEmptyConstructor(): boolean {
1757        let num = this.getConstructorsNum()
1758        for (let i = 0; i < num; i++) {
1759            if (this.getConstructor(i).getType().getParametersNum() == 0) {
1760                return true
1761            }
1762        }
1763        return false
1764    }
1765
1766    public isFinal(): boolean {
1767        return (this.attrs & Attributes.FINAL) != 0
1768    }
1769
1770    public hasField(name: string): boolean {
1771        // NOTE(shumilov-petr): may be faster if implement via intrinsic
1772        let fnum = this.getFieldsNum()
1773        for (let i = 0; i < fnum; i++) {
1774            if (this.getField(i).getName() == name) {
1775                return true
1776            }
1777        }
1778        return false
1779    }
1780
1781    /**
1782     * Returns number of type parameters of this class
1783     *
1784     * @returns number of type parameters
1785     */
1786    public getTypeParametersNum(): long {
1787        // NOTE(shumilov-petr): not implemented
1788        throw new Error("Not implemented")
1789    }
1790
1791    /**
1792     * Returns ith type parameter of this class
1793     *
1794     * @param i index
1795     *
1796     * @throws error then i greater then number of type parameters
1797     *
1798     * @returns NOTE(kirill-mitkin): we cannot return concrete type in this method
1799     */
1800    public getTypeParameter(i: long): Type {
1801        // NOTE(shumilov-petr): not implemented
1802        throw new Error("Not implemented")
1803    }
1804
1805    /**
1806     * Makes instance of this type by invoking
1807     * empty constructor of this class
1808     *
1809     * @throws error when class doesn't have an empty constructor
1810     */
1811    public make(): Object {
1812        const emptyArgs = new NullishType[0]
1813        return this.make(emptyArgs)
1814    }
1815
1816    // todo(kprokopenko): make varargs
1817    public make(args: NullishType[]): Object {
1818        const argTypes = new Array<Type>(args.length)
1819        for (let i = 0; i < args.length; i++) {
1820            argTypes[i] = Type.of(args[i])
1821        }
1822        // collect all applicable constructors
1823        const ctors = new Array<Method>()
1824        const ctorNum = this.getConstructorsNum()
1825        for (let i = 0; i < ctorNum; i++) {
1826            const c = this.getConstructor(i)
1827            const ct = c.getType()
1828            if (ct.getParametersNum() == args.length) {
1829                let ok = true
1830                for (let arg = 0; arg < args.length; arg++) {
1831                    if (!ct.getParameter(arg).getType().assignableFrom(argTypes[arg])) {
1832                        ok = false
1833                        break
1834                    }
1835                }
1836                if (ok) {
1837                    ctors.push(c)
1838                }
1839            }
1840        }
1841        // inspect if constructor has a more specific one (O(n^2))
1842        for (let inspect = 0; inspect < ctors.length; inspect++) {
1843            const toRem = ctors.at(inspect)!.getType()
1844            let rem = false
1845            for (let c = 0; c < ctors.length; c++) {
1846                if (c == inspect) {
1847                    continue
1848                }
1849                rem = true
1850                const cur = ctors.at(c)!.getType()
1851                for (let a = 0; a < args.length; a++) {
1852                    if (!toRem.getParameter(a).getType().assignableFrom(cur.getParameter(a).getType())) {
1853                        rem = false;
1854                        break;
1855                    }
1856                }
1857                if (rem) {
1858                    break
1859                }
1860            }
1861            if (rem) {
1862                ctors.splice(inspect, 1)
1863                inspect--
1864            }
1865        }
1866        if (ctors.length != 1) {
1867            throw new Error("can't select consturctor: " + ctors.length + " left")
1868        }
1869        return ctors.at(0)!.invoke(null, args)!
1870    }
1871
1872    // class.subTypeOf(U) means that `class extends U` or `class implements U`
1873    protected override subTypeOf(other: Type): boolean {
1874        if (super.subTypeOf(other)) {
1875            return true
1876        }
1877        // `this` is derived of `other` class?
1878        if (other instanceof ClassType) {
1879            let bt = this;
1880            while (!bt.equals(bt.getBaseType())) {
1881                if (bt.equals(other)) {
1882                    return true
1883                }
1884                bt = bt.getBaseType()
1885            }
1886            return false
1887        }
1888        // `this` implements `other` interface?
1889        if (other instanceof InterfaceType) {
1890            let iface = other as InterfaceType
1891            let ifaceNum = this.getInterfacesNum()
1892            for (let i = 0; i < ifaceNum; i++) {
1893                if (this.getInterface(i).hasSuperInterface(iface)) {
1894                    return true
1895                }
1896            }
1897        }
1898        return false
1899    }
1900
1901    internal override convertObject(obj: NullishType): NullishType {
1902        const objType = Type.of(obj)
1903        if (!this.assignableFrom(objType)) {
1904            throw new Error("invalid conversion")
1905        }
1906        return obj
1907    }
1908}
1909
1910/**
1911 * Represents interface type
1912 */
1913export final class InterfaceType extends Type {
1914    public constructor() {}
1915
1916    public constructor(td: TypeDesc) {
1917        this.td = td
1918    }
1919
1920    /**
1921     * Checks whether type is primitive or composite
1922     *
1923     * @returns true if type is primitive and false otherwise
1924     */
1925    public override isPrimitive(): boolean {
1926        return false
1927    }
1928
1929    /**
1930     * Checks whether type is reference or composite
1931     *
1932     * @returns true if type is reference and false otherwise
1933     */
1934    public override isReference(): boolean {
1935        return true
1936    }
1937
1938    /**
1939     * Checks whether type has name
1940     *
1941     * @returns true if type has name
1942     */
1943    public override hasName(): boolean {
1944        return true
1945    }
1946
1947    /**
1948     * Returns name of type
1949     *
1950     * @returns name of type
1951     */
1952    public override getName(): string {
1953        return TypeAPIGetTypeName(this.td)
1954    }
1955
1956    /**
1957     * Returns literal of type if exists
1958     *
1959     * @returns type literal
1960     */
1961    public override getLiteral(): string {
1962        // NOTE(shumilov-petr): not implemented
1963        return "interface{...}"
1964    }
1965
1966    /**
1967     * Checks for equality this instance with provided object, treated as a InterfaceType
1968     *
1969     * @param other type to be checked against
1970     *
1971     * @returns true if object also has InterfaceType and
1972     * their names are the same
1973     */
1974    public override equals(other: Type): boolean {
1975        return other instanceof InterfaceType && this.td == (other as InterfaceType).td
1976    }
1977
1978    // `this` extends of `other` interface?
1979    protected override subTypeOf(other: Type): boolean {
1980        if (super.subTypeOf(other)) {
1981            return true
1982        }
1983        if (other instanceof InterfaceType) {
1984            return this.hasSuperInterface(other as InterfaceType)
1985        }
1986        return false
1987    }
1988
1989    public getInterfacesNum(): long {
1990        return TypeAPIGetInterfacesNum(this.td)
1991    }
1992
1993    public getInterface(i: long): InterfaceType {
1994        return Type.resolve(TypeAPIGetInterface(this.td, i))! as InterfaceType
1995    }
1996
1997    /**
1998     * Returns number of methods of this interface
1999     * including static methods and methods of super interfaces
2000     * @example
2001     *
2002     * ```
2003     * class A {
2004     *     a(): void {}
2005     * }
2006     *
2007     * class B extends A {
2008     *     b(): void {}
2009     * }
2010     * ```
2011     * let bType interface type of B, then `bType.getMethodsNum()` returns at least 2
2012     * Note that Object class also super class of B
2013     * @returns number of methods
2014     */
2015    public getMethodsNum(): long {
2016        return TypeAPIGetMethodsNum(this.td)
2017    }
2018
2019    /**
2020     * Returns ith Method of this interface
2021     *
2022     * @param i index (using flat semantic)
2023     *
2024     * @throws error when i greater then number of methods
2025     *
2026     * @returns ith method
2027     */
2028    public getMethod(i: long): Method {
2029        return TypeAPIGetMethod(this.td, i)
2030    }
2031
2032    /**
2033     * Returns number of type parameters of this class
2034     *
2035     * @returns number of type parameters
2036     */
2037    public getTypeParametersNum(): long {
2038        // NOTE(shumilov-petr): not implemented
2039        throw new Error("Not implemented")
2040    }
2041
2042    /**
2043     * Returns ith type parameter of this class
2044     *
2045     * @param i index
2046     *
2047     * @throws error then i greater then number of type parameters
2048     *
2049     * @returns NOTE(kirill-mitkin): we cannot return concrete type in this method
2050     */
2051    public getTypeParameter(i: long): Type {
2052        // NOTE(shumilov-petr): not implemented
2053        throw new Error("Not implemented")
2054    }
2055
2056    internal hasSuperInterface(expected: InterfaceType): boolean {
2057        if (this.equals(expected)) {
2058            return true
2059        }
2060        return TypeAPIIsInheritedFrom(this.td, expected.td)
2061    }
2062
2063    internal override convertObject(obj: NullishType): NullishType {
2064        const objType = Type.of(obj)
2065        if (!this.assignableFrom(objType)) {
2066            throw new Error("invalid conversion")
2067        }
2068        return obj
2069    }
2070}
2071
2072/**
2073 * Represents array type
2074 */
2075export final class ArrayType extends Type {
2076    private elemTD: TypeDesc
2077
2078    public static readonly BOOLEAN_VAL: ArrayType   = new ArrayType(TypeAPIGetTypeDescriptor(new boolean[0]), ValueTypeDesc.BOOLEAN)
2079    public static readonly BOOLEAN_REF: ArrayType   = new ArrayType(TypeAPIGetTypeDescriptor(new Boolean[0]), TypeAPIGetTypeDescriptor(new Boolean()))
2080    public static readonly CHAR_VAL: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new char[0]), ValueTypeDesc.CHAR)
2081    public static readonly CHAR_REF: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new Char[0]), TypeAPIGetTypeDescriptor(new Char()))
2082    public static readonly BYTE_VAL: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new byte[0]), ValueTypeDesc.BYTE)
2083    public static readonly BYTE_REF: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new Byte[0]), TypeAPIGetTypeDescriptor(new Byte()))
2084    public static readonly SHORT_VAL: ArrayType     = new ArrayType(TypeAPIGetTypeDescriptor(new short[0]), ValueTypeDesc.SHORT)
2085    public static readonly SHORT_REF: ArrayType     = new ArrayType(TypeAPIGetTypeDescriptor(new Short[0]), TypeAPIGetTypeDescriptor(new Short()))
2086    public static readonly INT_VAL: ArrayType       = new ArrayType(TypeAPIGetTypeDescriptor(new int[0]), ValueTypeDesc.INT)
2087    public static readonly INT_REF: ArrayType       = new ArrayType(TypeAPIGetTypeDescriptor(new Int[0]), TypeAPIGetTypeDescriptor(new Int()))
2088    public static readonly LONG_VAL: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new long[0]), ValueTypeDesc.LONG)
2089    public static readonly LONG_REF: ArrayType      = new ArrayType(TypeAPIGetTypeDescriptor(new Long[0]), TypeAPIGetTypeDescriptor(new Long()))
2090    public static readonly FLOAT_VAL: ArrayType     = new ArrayType(TypeAPIGetTypeDescriptor(new float[0]), ValueTypeDesc.FLOAT)
2091    public static readonly FLOAT_REF: ArrayType     = new ArrayType(TypeAPIGetTypeDescriptor(new Float[0]), TypeAPIGetTypeDescriptor(new Float()))
2092    public static readonly DOUBLE_VAL: ArrayType    = new ArrayType(TypeAPIGetTypeDescriptor(new double[0]), ValueTypeDesc.DOUBLE)
2093    public static readonly DOUBLE_REF: ArrayType    = new ArrayType(TypeAPIGetTypeDescriptor(new Double[0]), TypeAPIGetTypeDescriptor(new Double()))
2094
2095    private constructor(td: TypeDesc, elemTD: TypeDesc) {
2096        this.td = td
2097        this.elemTD = elemTD
2098    }
2099
2100    public override assignableFrom(other: Type): boolean {
2101        if (super.assignableFrom(other)) {
2102            return true
2103        }
2104        return other instanceof ArrayType && (other as ArrayType).getElementType().subTypeOf(this.getElementType())
2105    }
2106
2107    internal static getInstance(td: TypeDesc, elemTD: TypeDesc): ArrayType {
2108        let ek = (TypeAPIGetTypeKind(elemTD) & TypeKindMask) as byte
2109        switch (ek) {
2110            case TypeKind.BOOLEAN:
2111                return (TypeAPIIsValueType(elemTD)) ? ArrayType.BOOLEAN_VAL : ArrayType.BOOLEAN_REF
2112            case TypeKind.CHAR:
2113                return (TypeAPIIsValueType(elemTD)) ? ArrayType.CHAR_VAL : ArrayType.CHAR_REF
2114            case TypeKind.BYTE:
2115                return (TypeAPIIsValueType(elemTD)) ? ArrayType.BYTE_VAL : ArrayType.BYTE_REF
2116            case TypeKind.SHORT:
2117                return (TypeAPIIsValueType(elemTD)) ? ArrayType.SHORT_VAL : ArrayType.SHORT_REF
2118            case TypeKind.INT:
2119                return (TypeAPIIsValueType(elemTD)) ? ArrayType.INT_VAL : ArrayType.INT_REF
2120            case TypeKind.LONG:
2121                return (TypeAPIIsValueType(elemTD)) ? ArrayType.LONG_VAL : ArrayType.LONG_REF
2122            case TypeKind.FLOAT:
2123                return (TypeAPIIsValueType(elemTD)) ? ArrayType.FLOAT_VAL : ArrayType.FLOAT_REF
2124            case TypeKind.DOUBLE:
2125                return (TypeAPIIsValueType(elemTD)) ? ArrayType.DOUBLE_VAL : ArrayType.DOUBLE_REF
2126            case TypeKind.CLASS:
2127            case TypeKind.STRING:
2128            case TypeKind.INTERFACE:
2129            case TypeKind.ARRAY:
2130            case TypeKind.TUPLE:
2131            case TypeKind.LAMBDA:
2132            case TypeKind.METHOD:
2133            case TypeKind.UNION:
2134                return new ArrayType(td, elemTD)
2135            default:
2136                // NOTE(shumilov-petr): need throw exception
2137                assert(false)
2138        }
2139
2140        throw new Error("Invalid object")
2141    }
2142
2143    internal static getInstance(td: TypeDesc): ArrayType {
2144        return ArrayType.getInstance(td, TypeAPIGetArrayElementType(td))
2145    }
2146
2147    /**
2148     * Checks whether type is primitive or composite
2149     *
2150     * @returns true if type is primitive and false otherwise
2151     */
2152    public override isPrimitive(): boolean {
2153        return false
2154    }
2155
2156    /**
2157     * Checks whether type is reference or composite
2158     *
2159     * @returns true if type is reference and false otherwise
2160     */
2161    public override isReference(): boolean {
2162        return true
2163    }
2164
2165    /**
2166     * Checks whether type has name
2167     *
2168     * @returns true if type has name
2169     */
2170    public override hasName(): boolean {
2171        return false
2172    }
2173
2174    /**
2175     * Returns name of type
2176     *
2177     * @throws error in case of absence of name
2178     *
2179     * @returns name of type
2180     */
2181    public override getName(): string {
2182        return ""
2183    }
2184
2185    /**
2186     * Returns literal of type if exists
2187     *
2188     * @returns type literal
2189     */
2190    public override getLiteral(): string {
2191        return this.getElementType().toString() + "[]"
2192    }
2193
2194    /**
2195     * Checks for equality this instance with provided object, treated as a ArrayType
2196     *
2197     * @param other type to be checked against
2198     *
2199     * @returns true if object also has ArrayType and
2200     * their element types are the same
2201     */
2202    public override equals(other: Type): boolean {
2203        return other instanceof ArrayType && (other as ArrayType).getElementType().equals(this.getElementType())
2204    }
2205
2206    /**
2207     * Returns element type of this array
2208     *
2209     * @returns element type
2210     */
2211    public getElementType(): Type {
2212        return Type.resolve(this.elemTD)!
2213    }
2214
2215    /**
2216     * Makes instance of this array with provided length
2217     * Each element are instantiated using default value
2218     * If element type is class value then empty constructor are called
2219     *
2220     * @param length of array
2221     *
2222     * @throws error if element type doesn't have default value
2223     *
2224     * @returns new instance of array
2225     */
2226    public make(length: long): Object {
2227        return TypeAPIMakeArrayInstance(this.elemTD, length)
2228    }
2229
2230    internal override convertObject(obj: NullishType): NullishType {
2231        const objType = Type.of(obj)
2232        if (this.equals(objType)) {
2233            return obj
2234        }
2235        if (!this.assignableFrom(objType)) {
2236            throw new Error("invalid conversion")
2237        }
2238        return obj
2239    }
2240}
2241
2242export final class TupleType extends Type {
2243    public constructor(td: TypeDesc) {
2244        this.td = td
2245    }
2246
2247    public override isPrimitive(): boolean {
2248        throw new Error("Not implemented")
2249    }
2250
2251    public override isReference(): boolean {
2252        throw new Error("Not implemented")
2253    }
2254
2255    public override hasName(): boolean {
2256        throw new Error("Not implemented")
2257    }
2258
2259    public override getName(): string {
2260        throw new Error("Not implemented")
2261    }
2262
2263    public override getLiteral(): string {
2264        throw new Error("Not implemented")
2265    }
2266
2267    public override equals(other: Type): boolean {
2268        throw new Error("Not implemented")
2269    }
2270
2271    internal override convertObject(obj: NullishType): NullishType {
2272        throw new Error("todo(kprokopenko): add when tuple becomes available")
2273    }
2274}
2275
2276/**
2277 * Represents function type
2278 *
2279 * @note lambdas, functions and methods have function type
2280 */
2281export abstract class FunctionType extends Type {
2282    private readonly attrs: int
2283
2284    protected constructor(td: TypeDesc) {
2285        this.td = td
2286        this.attrs = TypeAPIGetFunctionAttributes(td)
2287    }
2288
2289    /**
2290     * Checks whether type is primitive or composite
2291     *
2292     * @returns true if type is primitive and false otherwise
2293     */
2294    public override isPrimitive(): boolean {
2295        return false
2296    }
2297
2298    /**
2299     * Checks whether type is reference or composite
2300     *
2301     * @returns true if type is reference and false otherwise
2302     */
2303    public override isReference(): boolean {
2304        return true
2305    }
2306
2307    /**
2308     * Checks whether type has name
2309     *
2310     * @returns true if type has name
2311     */
2312    public override hasName(): boolean {
2313        return false
2314    }
2315
2316    /**
2317     * Returns name of type
2318     *
2319     * @returns name of type
2320     */
2321    public override getName(): string {
2322        return ""
2323    }
2324
2325    /**
2326     * Returns return type of function type
2327     *
2328     * @returns result type
2329     */
2330    public getResultType(): Type {
2331        return Type.resolve(TypeAPIGetResultType(this.td))!
2332    }
2333
2334    /**
2335     * Checks whether function type throws some exception
2336     *
2337     * @returns true if function type throws some exception
2338     */
2339    public isThrowing(): boolean {
2340        return (this.attrs & Attributes.THROWING) != 0
2341    }
2342
2343    /**
2344     * Checks whether function type has native modifier
2345     *
2346     * @returns true if function type has native modifier
2347     */
2348    public isNative(): boolean {
2349        return (this.attrs & Attributes.NATIVE) != 0
2350    }
2351
2352    /**
2353     * Checks whether function type has async modifier
2354     *
2355     * @returns true if function type has async modifier
2356     */
2357    public isAsync(): boolean {
2358        return (this.attrs & Attributes.ASYNC) != 0
2359    }
2360
2361    /**
2362     * Checks whether function result type is never
2363     *
2364     * @returns true if function result type is never
2365     */
2366    public isNeverResult(): boolean {
2367        return (this.attrs & Attributes.NEVERRESULT) != 0
2368    }
2369
2370    /**
2371     * Checks for equality this instance with provided object, treated as a FunctionType
2372     *
2373     * @param other type to be checked against
2374     *
2375     * @returns true if object also has FunctionType and
2376     * they both has same signatures
2377     */
2378    public override equals(other: Type): boolean {
2379        return (other instanceof FunctionType) && (other as FunctionType).td == this.td
2380    }
2381
2382    /**
2383     * Returns number of parameters
2384     * For static methods reciever type counted as parameter
2385     * For instance methods reciever type isn't counted as parameter
2386     *
2387     * @returns number of parameters
2388     */
2389    public getParametersNum(): long {
2390        return TypeAPIGetParametersNum(this.td)
2391    }
2392
2393    /**
2394     * Returns ith parameter of function type
2395     *
2396     * @return Parameter, corresponding ith parameter in signature
2397     */
2398    public getParameter(i: long): Parameter {
2399        return TypeAPIGetParameter(this.td, i)
2400    }
2401
2402    /**
2403     * Returns number of type parameters of this class
2404     *
2405     * @returns number of type parameters
2406     */
2407    public getTypeParametersNum(): long {
2408        // NOTE(shumilov-petr): not implemented
2409        return 0
2410    }
2411
2412    /**
2413     * Returns ith type parameter of this class
2414     *
2415     * @param i index
2416     *
2417     * @throws error then i greater then number of type parameters
2418     *
2419     * @returns NOTE(kirill-mitkin): we cannot return concrete type in this method
2420     */
2421    public getTypeParameter(i: long): Type {
2422        // NOTE(shumilov-petr): not implemented
2423        throw new Error("not implemented")
2424    }
2425}
2426
2427
2428export final class LambdaType extends FunctionType {
2429    public constructor(td: TypeDesc) {
2430        super(td)
2431    }
2432
2433    public override getLiteral(): string {
2434        let sb = new StringBuilder("(")
2435        const paramsNum = this.getParametersNum()
2436        for (let i = 0; i < paramsNum; ++i) {
2437            sb.append(this.getParameter(i).toString())
2438            if (i != paramsNum - 1) {
2439                sb.append(", ")
2440            }
2441        }
2442        sb.append("): ")
2443        sb.append(this.getResultType().toString())
2444        return sb.toString()
2445    }
2446
2447    /**
2448     * Make an instance of LambdaType
2449     *
2450     * @returns LambdaType instance
2451     */
2452    public make(): Object {
2453        // NOTE(kprokopenko): not implemented
2454        throw new Error("Not implemented")
2455    }
2456
2457    public override assignableFrom(other: Type): boolean {
2458        if (super.assignableFrom(other)) {
2459            return true
2460        }
2461        if (!(other instanceof LambdaType)) {
2462            return false
2463        }
2464        let l = (this)
2465        let r = other as LambdaType
2466        if (l.getParametersNum() != r.getParametersNum()) {
2467            return false
2468        }
2469        // Parameter types are using contravariance
2470        for (let i = 0; i < l.getParametersNum(); i++) {
2471            let lt = l.getParameter(i).getType()
2472            let rt = r.getParameter(i).getType()
2473            if (!lt.subTypeOf(rt)) {
2474                return false
2475            }
2476        }
2477        // Return types are using covariance
2478        return r.getResultType().subTypeOf(l.getResultType())
2479    }
2480
2481    internal override convertObject(obj: NullishType): NullishType {
2482        const objType = Type.of(obj)
2483        if (this.equals(objType)) {
2484            return obj
2485        }
2486        if (!this.assignableFrom(objType)) {
2487            throw new Error("invalid conversion")
2488        }
2489        // NOTE(shumilov-petr): Need to use Create API
2490        throw new Error("todo(kprokopenko): unequal labmda type conversion")
2491    }
2492}
2493
2494
2495export final class MethodType extends FunctionType {
2496    public constructor(td: TypeDesc) {
2497        super(td)
2498    }
2499
2500    public override getLiteral(): string {
2501        let sb = new StringBuilder("(this: ")
2502        sb.append(this.getReceiverType().toString())
2503        const paramsNum = this.getParametersNum()
2504        if (paramsNum > 0) {
2505            sb.append(", ")
2506        }
2507        for (let i = 0; i < paramsNum; ++i) {
2508            sb.append(this.getParameter(i).toString())
2509            if (i != paramsNum - 1) {
2510                sb.append(", ")
2511            }
2512        }
2513        sb.append("): ")
2514        sb.append(this.getResultType().toString())
2515        return sb.toString()
2516    }
2517
2518    public getReceiverType(): Type {
2519        return Type.resolve(TypeAPIGetReceiverType(this.td))!
2520    }
2521
2522    public override assignableFrom(other: Type): boolean {
2523        return false
2524    }
2525
2526    internal override convertObject(obj: NullishType): NullishType {
2527        throw new Error("Only LambdaType can be converted")
2528    }
2529}
2530
2531/**
2532 * Represents string type
2533 */
2534export final class StringType extends Type {
2535    public static readonly REF: StringType = new StringType()
2536
2537    internal constructor() {
2538        this.td = TypeAPIGetTypeDescriptor("")
2539    }
2540
2541    /**
2542     * Checks whether type is primitive or composite
2543     *
2544     * @returns true if type is primitive and false otherwise
2545     */
2546    public override isPrimitive(): boolean {
2547        return true
2548    }
2549
2550    /**
2551     * Checks whether type is reference or composite
2552     *
2553     * @returns true if type is reference and false otherwise
2554     */
2555    public override isReference(): boolean {
2556        return true
2557    }
2558
2559    /**
2560     * Checks whether type has name
2561     *
2562     * @returns true if type has name
2563     */
2564    public override hasName(): boolean {
2565        return false
2566    }
2567
2568    /**
2569     * Returns name of type
2570     *
2571     * @returns type name
2572     */
2573    public override getName(): string {
2574        // NOTE(shumilov-petr): not implemented
2575        return ""
2576    }
2577
2578    /**
2579     * Returns literal of type if exists
2580     *
2581     * @returns type literal
2582     */
2583    public override getLiteral(): string {
2584        // NOTE(shumilov-petr): not implemented
2585        return "string"
2586    }
2587
2588
2589    /**
2590     * Checks for equality this instance with provided object, treated as a StringType
2591     *
2592     * @param other object to be checked against
2593     *
2594     * @returns true if object also has StringType
2595     */
2596    public override equals(other: Type): boolean {
2597        return other instanceof StringType
2598    }
2599
2600    internal override convertObject(obj: NullishType): NullishType {
2601        const objType = Type.of(obj)
2602        if (!this.equals(objType)) {
2603            throw new Error("invalid conversion")
2604        }
2605        return obj
2606    }
2607}
2608
2609/**
2610 * Represents enum type
2611 */
2612export final class EnumType extends Type {
2613    internal constructor(td: TypeDesc) {
2614        this.td = td
2615    }
2616
2617    public override assignableFrom(other: Type): boolean {
2618        if (super.assignableFrom(other)) {
2619            return true
2620        }
2621        if (!(other instanceof EnumType)) {
2622            return false
2623        }
2624        let rt = other as EnumType
2625        return this.getName() == rt.getName()
2626    }
2627
2628    /**
2629     * Checks whether type is primitive or composite
2630     *
2631     * @returns true if type is primitive and false otherwise
2632     */
2633    public override isPrimitive(): boolean {
2634        return false
2635    }
2636
2637    /**
2638     * Checks whether type is reference or composite
2639     *
2640     * @returns true if type is reference and false otherwise
2641     */
2642    public override isReference(): boolean {
2643        return false
2644    }
2645
2646    /**
2647     * Checks whether type has name
2648     *
2649     * @returns true if type has name
2650     */
2651    public override hasName(): boolean {
2652        return true
2653    }
2654
2655    /**
2656     * Returns name of type
2657     *
2658     * @returns type name
2659     */
2660    public override getName(): string {
2661        // NOTE(shumilov-petr): not implemented
2662        return ""
2663    }
2664
2665    /**
2666     * Returns literal of type if exists
2667     *
2668     * @returns type literal
2669     */
2670    public override getLiteral(): string {
2671        // NOTE(shumilov-petr): not implemented
2672        return "enum {...}"
2673    }
2674
2675    /**
2676     * Checks for equality this instance with provided object, treated as a EnumType
2677     *
2678     * @param other type to be checked against
2679     *
2680     * @returns true if object also has EnumType and their names are the same
2681     */
2682    public override equals(other: Type): boolean {
2683        // NOTE(shumilov-petr): not implemented
2684        return false
2685    }
2686
2687    public getConstantsNum(): long {
2688        // NOTE(shumilov-petr): not implemented
2689        return 0
2690    }
2691
2692    public getConstant(i: long): EnumConstant {
2693        // NOTE(shumilov-petr): not implemented
2694        throw new Error("not implemented")
2695    }
2696
2697    public getConstantByName(name: string): EnumConstant {
2698        // NOTE(shumilov-petr): not implemented
2699        throw new Error("not implemented")
2700    }
2701
2702    public create(consts: EnumConstant[]): EnumType {
2703        // NOTE(shumilov-petr): not implemented
2704        throw new Error("not implemented")
2705    }
2706
2707    public make(constantName: string): Object {
2708        // NOTE(shumilov-petr): not implemented
2709        throw new Error("not implemented")
2710    }
2711
2712    internal override convertObject(obj: NullishType): NullishType {
2713        throw new Error("todo(kprokopenko): enum conversion")
2714    }
2715}
2716
2717/**
2718 * Represents union type
2719 */
2720export final class UnionType extends Type {
2721    internal constructor(td: TypeDesc) {
2722        this.td = td
2723    }
2724
2725    public override assignableFrom(other: Type): boolean {
2726        if (other instanceof UnionType) {
2727            const otherUnion = other as UnionType
2728            // all cases of other are assignable into this
2729            const otherCasesN = this.getCasesNum()
2730            for (let i = 0; i < otherCasesN; i++) {
2731                let cas = otherUnion.getCase(i)
2732                if (!this.assignableFrom(cas.getType())) {
2733                    return false
2734                }
2735            }
2736            return true
2737        }
2738        // assignable to any case
2739        const selfCasesN = this.getCasesNum()
2740        for (let i = 0; i < selfCasesN; i++) {
2741            let cas = this.getCase(i)
2742            if (cas.getType().assignableFrom(other)) {
2743                return true
2744            }
2745        }
2746        return false
2747    }
2748
2749    /**
2750     * Checks whether type is primitive or composite
2751     *
2752     * @returns true if type is primitive and false otherwise
2753     */
2754    public override isPrimitive(): boolean {
2755        return false
2756    }
2757
2758    /**
2759     * Checks whether type is reference or composite
2760     *
2761     * @returns true if type is reference and false otherwise
2762     */
2763    public override isReference(): boolean {
2764        return true
2765    }
2766
2767    /**
2768     * Checks whether type has name
2769     *
2770     * @returns true if type has name
2771     */
2772    public override hasName(): boolean {
2773        return false
2774    }
2775
2776    /**
2777     * Returns name of type
2778     *
2779     * @returns type name
2780     */
2781    public override getName(): string {
2782        // NOTE(shumilov-petr): not implemented
2783        return ""
2784    }
2785
2786    /**
2787     * Returns literal of type if exists
2788     *
2789     * @returns type literal
2790     */
2791    public override getLiteral(): string {
2792        // NOTE(shumilov-petr): not implemented
2793        return "(... | ...)"
2794    }
2795
2796    public getCasesNum(): long {
2797        // NOTE(kirill-mitkin): not implemented
2798        return 0
2799    }
2800
2801    public getCase(i: long): UnionCase {
2802        // NOTE(kirill-mitkin): not implemented
2803        throw new Error("Not implemented")
2804    }
2805
2806    /**
2807     * Checks for equality this instance with provided object, treated as a UnionType
2808     *
2809     * @param other type to be checked against
2810     *
2811     * @returns true if object also has UnionType and their cases are the same
2812     */
2813    public override equals(other: Type): boolean {
2814        // NOTE(shumilov-petr): not implemented
2815        return false
2816    }
2817
2818    public make(default: Type): Object {
2819        // NOTE(shumilov-petr): not implemented
2820        throw new Error("not implemented")
2821    }
2822
2823    public make(default: Value): Object {
2824        // NOTE(shumilov-petr): not implemented
2825        throw new Error("not implemented")
2826    }
2827
2828    internal override convertObject(obj: NullishType): NullishType {
2829        const objType = Type.of(obj)
2830        if (this.equals(objType)) {
2831            return obj
2832        }
2833        if (!this.assignableFrom(objType)) {
2834            throw new Error("invalid conversion")
2835        }
2836        return obj
2837    }
2838}
2839