import { ArrayBufferView } from "arraybuffer" ; import { Set } from "set" ; import { assert } from "./assert" ; import { ReflectedValueType } from "./ReflectedValueType" ; import { OBJECT , OBJECT_OVERHEAD } from "rt/common" ; function pairSeen ( a1 , a2 , b1 , b2 ) { return bool ( ( i32 ( a1 == b1 ) & i32 ( a2 == b2 ) ) | ( i32 ( a1 == b2 ) & i32 ( a2 == b1 ) ) ) ; } @ external ( "__aspect" , "attachStackTraceToReflectedValue" ) declare function attachStackTraceToReflectedValue ( id ) ; @ external ( "__aspect" , "createReflectedValue" ) declare function createReflectedValue ( isNull , hasKeys , nullable , offset , pointer , signed , size , reflectedTypeValue , typeId , typeName , value , hasValues , isManaged ) ; @ external ( "__aspect" , "createReflectedNumber" ) declare function createReflectedNumber ( signed , size , reflectedTypeValue , typeName , value ) ; @ external ( "__aspect" , "createReflectedLong" ) declare function createReflectedLong ( signed , size , reflectedTypeValue , typeName , lowValue , highValue ) ; @ external ( "__aspect" , "pushReflectedObjectValue" ) @ global declare function __aspectPushReflectedObjectValue ( parentID , value ) ; @ external ( "__aspect" , "pushReflectedObjectKey" ) @ global declare function __aspectPushReflectedObjectKey ( parentID , value ) ; @ inline function sizeOf ( ptr ) { return changetype < OBJECT > ( ptr - OBJECT_OVERHEAD ) . rtSize ; } @ global export class Reflect { public static toReflectedValue < T > ( value , seen = new Map < usize , i32 > ( ) ) { if ( isReference < T > ( ) ) { if ( changetype < usize > ( value ) == 0 ) { return createReflectedValue ( true , false , isNullable < T > ( ) , 0 , 0 , false , sizeof < T > ( ) , isFunction < T > ( ) ? ReflectedValueType . Function : ReflectedValueType . Class , isManaged < T > ( ) ? idof < T > ( ) : 0 , isFunction < T > ( ) ? "Function" : nameof < T > ( ) , 0 , false , isManaged < T > ( ) ) ; } if ( ! isFunction < T > ( ) ) { if ( seen . has ( changetype < usize > ( value ) ) ) { return seen . get ( changetype < usize > ( value ) ) ; } } if ( isDefined ( value . __aspectReflectAs ( ) ) ) { let displayValue = value . __aspectReflectAs ( ) ; if ( ! isInteger ( displayValue ) ) { if ( ! isFloat ( displayValue ) ) { if ( ! isManaged ( displayValue ) ) { ERROR ( "__aspectReflectAs() function should return a managed type or a number" ) ; } } } return Reflect . toReflectedValue ( displayValue , seen ) ; } if ( value instanceof ArrayBuffer ) { let reflectedValue = createReflectedValue ( false , false , isNullable < T > ( ) , value . byteLength , changetype < usize > ( value ) , false , value . byteLength , ReflectedValueType . ArrayBuffer , idof < T > ( ) , nameof < T > ( ) , 0 , true , true ) ; seen . set ( changetype < usize > ( value ) , reflectedValue ) ; let length = value . byteLength ; for ( let i = 0 ; i < length ; i ++ ) { __aspectPushReflectedObjectValue ( reflectedValue , Reflect . toReflectedValue ( load < u8 > ( changetype < usize > ( value ) + < usize > i ) , seen ) ) ; } return reflectedValue ; } else if ( isFunction < T > ( ) ) { let func = value ; let reflectedValue = createReflectedValue ( false , false , isNullable < T > ( ) , 0 , func . index , false , 0 , ReflectedValueType . Function , 0 , "Function" , func . index , false , isManaged < T > ( ) ) ; return reflectedValue ; } else if ( value instanceof Set ) { let reflectedObject = createReflectedValue ( false , false , isNullable < T > ( ) , 0 , changetype < usize > ( value ) , false , value . size , ReflectedValueType . Set , idof < T > ( ) , nameof < T > ( ) , 0 , true , true ) ; seen . set ( changetype < usize > ( value ) , reflectedObject ) ; let values = value . values ( ) ; let length = values . length ; for ( let i = 0 ; i < length ; i ++ ) { let value = unchecked ( values [ i ] ) ; let reflectedValueID = Reflect . toReflectedValue ( value , seen ) ; __aspectPushReflectedObjectValue ( reflectedObject , reflectedValueID ) ; } } else if ( value instanceof Map ) { let reflectedValue = createReflectedValue ( false , true , isNullable < T > ( ) , 0 , changetype < usize > ( value ) , false , value . size , ReflectedValueType . Map , idof < T > ( ) , nameof < T > ( ) , 0 , true , true ) ; seen . set ( changetype < usize > ( value ) , reflectedValue ) ; let keys = value . keys ( ) ; let length = keys . length ; for ( let i = 0 ; i < length ; i ++ ) { let mapKey = unchecked ( keys [ i ] ) ; let reflectedKeyID = Reflect . toReflectedValue ( mapKey , seen ) ; __aspectPushReflectedObjectKey ( reflectedValue , reflectedKeyID ) ; let mapValue = value . get ( mapKey ) ; let reflectedValueID = Reflect . toReflectedValue ( mapValue , seen ) ; __aspectPushReflectedObjectValue ( reflectedValue , reflectedValueID ) ; } return reflectedValue ; } else if ( value instanceof ArrayBufferView ) { let length = value . length ; let reflectedValue = createReflectedValue ( false , false , isNullable < T > ( ) , 0 , changetype < usize > ( value ) , false , length , ReflectedValueType . TypedArray , idof < T > ( ) , nameof < T > ( ) , 0 , true , isManaged < T > ( ) ) ; seen . set ( changetype < usize > ( value ) , reflectedValue ) ; for ( let i = 0 ; i < length ; i ++ ) { let arrayValue = unchecked ( value [ i ] ) ; let reflectedArrayValueID = Reflect . toReflectedValue ( arrayValue , seen ) ; __aspectPushReflectedObjectValue ( reflectedValue , reflectedArrayValueID ) ; } return reflectedValue ; } else if ( value instanceof String ) { let reflectedStringID = createReflectedValue ( false , false , isNullable < T > ( ) , 0 , changetype < usize > ( value ) , false , value . length , ReflectedValueType . String , idof < T > ( ) , nameof < T > ( ) , changetype < usize > ( value ) , false , true ) ; seen . set ( changetype < usize > ( value ) , reflectedStringID ) ; return reflectedStringID ; } else if ( isArrayLike < T > ( ) ) { let length = < i32 > value . length ; let reflectedValue = createReflectedValue ( false , false , isNullable < T > ( ) , 0 , changetype < usize > ( value ) , false , length , ReflectedValueType . Array , isManaged < T > ( ) ? idof < T > ( ) : 0 , nameof < T > ( ) , 0 , true , isManaged < T > ( ) ) ; seen . set ( changetype < usize > ( value ) , reflectedValue ) ; for ( let i = 0 ; i < length ; i ++ ) { let arrayValue = unchecked ( value [ i ] ) ; let reflectedArrayValueID = Reflect . toReflectedValue ( arrayValue , seen ) ; __aspectPushReflectedObjectValue ( reflectedValue , reflectedArrayValueID ) ; } return reflectedValue ; } else { let reflectedObjectID = createReflectedValue ( false , true , isNullable < T > ( ) , offsetof < T > ( ) , changetype < usize > ( value ) , false , sizeOf ( changetype < usize > ( value ) ) , ReflectedValueType . Class , isManaged < T > ( ) ? idof < T > ( ) : 0 , nameof < T > ( ) , 0 , true , isManaged < T > ( ) ) ; seen . set ( changetype < usize > ( value ) , reflectedObjectID ) ; value . __aspectAddReflectedValueKeyValuePairs ( reflectedObjectID , seen , [ ] as StaticArray < i64 > ) ; return reflectedObjectID ; } } else if ( alignof < T > ( ) === 3 ) { if ( isInteger < T > ( ) ) { let reflectedValue = createReflectedLong ( isSigned < T > ( ) , sizeof < T > ( ) , ReflectedValueType . Integer , nameof < T > ( ) , < i32 > ( value & 0xffffffff ) , < i32 > ( value >>> 32 ) ) ; return reflectedValue ; } let reflectedValue = createReflectedNumber ( isSigned < T > ( ) , sizeof < T > ( ) , ReflectedValueType . Float , nameof < T > ( ) , < f64 > value ) ; return reflectedValue ; } else { let reflectedValue = createReflectedNumber ( isSigned < T > ( ) , sizeof < T > ( ) , isBoolean < T > ( ) ? ReflectedValueType . Boolean : isInteger < T > ( ) ? ReflectedValueType . Integer : ReflectedValueType . Float , nameof < T > ( ) , < f64 > value ) ; return reflectedValue ; } return 0 ; } public static equals < T > ( left , right , stack = [ ] , cache = [ ] ) { if ( left == right ) return Reflect . SUCCESSFUL_MATCH ; if ( isFloat < T > ( ) ) { if ( i32 ( isNaN ( left ) ) & i32 ( isNaN ( right ) ) ) return Reflect . SUCCESSFUL_MATCH ; } if ( isReference < T > ( ) ) { if ( i32 ( changetype < usize > ( left ) == 0 ) ^ i32 ( changetype < usize > ( right ) == 0 ) ) { return Reflect . FAILED_MATCH ; } } if ( isReference < T > ( ) ) { if ( ! isFunction < T > ( ) ) { if ( isNullable < T > ( ) ) { return referencesEqual ( left ! , right ! , stack , cache ) ; } else { return referencesEqual ( left , right , stack , cache ) ; } } } return Reflect . FAILED_MATCH ; } public static attachStackTrace ( id ) { attachStackTraceToReflectedValue ( id ) ; } } function referencesEqual < T > ( left , right , stack , cache ) { let a = changetype < usize > ( left ) ; let b = changetype < usize > ( right ) ; let cacheLength = cache . length ; assert ( i32 ( ( cacheLength & 0x00000001 ) == 0 ) , "cacheLength should be even" ) ; if ( left instanceof String ) { return Reflect . FAILED_MATCH ; } for ( let i = 0 ; i < cacheLength ; i += 2 ) { if ( pairSeen ( a , b , unchecked ( cache [ i ] ) , unchecked ( cache [ i + 1 ] ) ) ) return Reflect . SUCCESSFUL_MATCH ; } let length = stack . length ; for ( let i = 0 ; i < length ; i += 2 ) { if ( pairSeen ( a , b , unchecked ( stack [ i ] ) , unchecked ( stack [ i + 1 ] ) ) ) return Reflect . DEFER_MATCH ; } if ( left instanceof ArrayBuffer ) { if ( left . byteLength != right . byteLength ) return Reflect . FAILED_MATCH ; let result = memory . compare ( a , b , left . byteLength ) ; if ( result == 0 ) { cache . push ( a ) ; cache . push ( b ) ; return Reflect . SUCCESSFUL_MATCH ; } else return Reflect . FAILED_MATCH ; } if ( isDefined ( left [ 0 ] ) ) { if ( left instanceof Set ) { if ( left . size != right . size ) return Reflect . FAILED_MATCH ; stack . push ( a ) ; stack . push ( b ) ; let leftValues = left . values ( ) ; let rightValues = right . values ( ) ; let length = leftValues . length ; let leftoverLength = length ; for ( let i = 0 ; i < length ; i ++ ) { let leftItem = unchecked ( leftValues [ i ] ) ; let continueOuter = false ; for ( let j = 0 ; j < leftoverLength ; j ++ ) { let rightItem = unchecked ( rightValues [ j ] ) ; if ( Reflect . equals ( leftItem , rightItem , stack , cache ) != Reflect . FAILED_MATCH ) { rightValues . splice ( j , 1 ) ; leftoverLength -- ; continueOuter = true ; break ; } } if ( continueOuter ) continue ; stack . pop ( ) ; stack . pop ( ) ; return Reflect . FAILED_MATCH ; } cache . push ( a ) ; cache . push ( b ) ; stack . pop ( ) ; stack . pop ( ) ; return Reflect . SUCCESSFUL_MATCH ; } if ( left instanceof Map ) { if ( left . size != right . size ) return Reflect . FAILED_MATCH ; stack . push ( a ) ; stack . push ( b ) ; let leftKeys = left . keys ( ) ; let rightKeys = right . keys ( ) ; let keyLength = leftKeys . length ; let leftoverKeyLength = keyLength ; let result = Reflect . SUCCESSFUL_MATCH ; for ( let i = 0 ; i < keyLength ; i ++ ) { let leftKey = unchecked ( leftKeys [ i ] ) ; let found = false ; for ( let j = 0 ; j < leftoverKeyLength ; j ++ ) { let rightKey = unchecked ( rightKeys [ j ] ) ; if ( Reflect . equals ( leftKey , rightKey , stack , cache ) != Reflect . FAILED_MATCH ) { let leftValue = left . get ( leftKey ) ; let rightValue = right . get ( rightKey ) ; if ( Reflect . equals ( leftValue , rightValue , stack , cache ) != Reflect . FAILED_MATCH ) { leftoverKeyLength -- ; rightKeys . splice ( j , 1 ) ; found = true ; break ; } } } if ( ! found ) { result = Reflect . FAILED_MATCH ; break ; } } if ( result == Reflect . SUCCESSFUL_MATCH ) { cache . push ( a ) ; cache . push ( b ) ; } stack . pop ( ) ; stack . pop ( ) ; return result ; } if ( isDefined ( left . length ) ) { let aLength = left . length ; let bLength = right . length ; if ( aLength != bLength ) return Reflect . FAILED_MATCH ; for ( let i = 0 ; i < aLength ; i ++ ) { let result = Reflect . equals ( unchecked ( left [ i ] ) , unchecked ( right [ i ] ) , stack , cache ) ; if ( result == Reflect . FAILED_MATCH ) return Reflect . FAILED_MATCH ; } cache . push ( a ) ; cache . push ( b ) ; return Reflect . SUCCESSFUL_MATCH ; } } stack . push ( a ) ; stack . push ( b ) ; let result = false ; if ( isNullable ( left ) ) { result = left ! . __aspectStrictEquals ( right , stack , cache , [ ] as StaticArray < i64 > ) ; } else { result = left . __aspectStrictEquals ( right , stack , cache , [ ] as StaticArray < i64 > ) ; } if ( result ) { cache . push ( a ) ; cache . push ( b ) ; } stack . pop ( ) ; stack . pop ( ) ; return select ( Reflect . SUCCESSFUL_MATCH , Reflect . FAILED_MATCH , result ) ; } export namespace Reflect { export const FAILED_MATCH = 0 ; export const SUCCESSFUL_MATCH = 1 ; export const DEFER_MATCH = 2 ; }