@@ -94,62 +94,78 @@ private JSReference ExportType(Type type)
9494
9595 private JSReference ExportClass ( Type type )
9696 {
97+ string typeName = type . Name ;
98+ Trace ( $ "### ExportClass({ typeName } ") ;
99+
97100 if ( _exportedTypes . TryGetValue ( type , out JSReference ? classObjectReference ) )
98101 {
99102 return classObjectReference ;
100103 }
101104
102105 Trace ( $ "> { nameof ( TypeExporter ) } .ExportClass({ type . FormatName ( ) } )") ;
103106
104- bool isStatic = type . IsAbstract && type . IsSealed ;
105- Type classBuilderType =
106- ( type . IsValueType ? typeof ( JSStructBuilder < > ) : typeof ( JSClassBuilder < > ) )
107- . MakeGenericType ( isStatic ? typeof ( object ) : type ) ;
108-
109- object classBuilder ;
110- if ( type . IsInterface || isStatic || type . IsValueType )
111- {
112- classBuilder = classBuilderType . CreateInstance (
113- new [ ] { typeof ( string ) } , new [ ] { type . Name } ) ;
114- }
115- else
107+ // Add a temporary null entry to the dictionary while exporting this type, in case the
108+ // type is encountered while exporting members. It will be non-null by the time this method returns
109+ // (or removed if an exception is thrown).
110+ _exportedTypes . Add ( type , null ! ) ;
111+ try
116112 {
117- ConstructorInfo [ ] constructors =
118- type . GetConstructors ( BindingFlags . Public | BindingFlags . Instance )
119- . Where ( IsSupportedConstructor )
120- . ToArray ( ) ;
121- JSCallbackDescriptor constructorDescriptor ;
122- if ( constructors . Length == 1 &&
123- ! constructors [ 0 ] . GetParameters ( ) . Any ( ( p ) => p . IsOptional ) )
113+ bool isStatic = type . IsAbstract && type . IsSealed ;
114+ Type classBuilderType =
115+ ( type . IsValueType ? typeof ( JSStructBuilder < > ) : typeof ( JSClassBuilder < > ) )
116+ . MakeGenericType ( isStatic ? typeof ( object ) : type ) ;
117+
118+ object classBuilder ;
119+ if ( type . IsInterface || isStatic || type . IsValueType )
124120 {
125- constructorDescriptor =
126- _marshaller . BuildFromJSConstructorExpression ( constructors [ 0 ] ) . Compile ( ) ;
121+ classBuilder = classBuilderType . CreateInstance (
122+ new [ ] { typeof ( string ) } , new [ ] { type . Name } ) ;
127123 }
128124 else
129125 {
130- // Multiple constructors or optional parameters require overload resolution.
131- constructorDescriptor =
132- _marshaller . BuildConstructorOverloadDescriptor ( constructors ) ;
133- }
126+ ConstructorInfo [ ] constructors =
127+ type . GetConstructors ( BindingFlags . Public | BindingFlags . Instance )
128+ . Where ( IsSupportedConstructor )
129+ . ToArray ( ) ;
130+ JSCallbackDescriptor constructorDescriptor ;
131+ if ( constructors . Length == 1 &&
132+ ! constructors [ 0 ] . GetParameters ( ) . Any ( ( p ) => p . IsOptional ) )
133+ {
134+ constructorDescriptor =
135+ _marshaller . BuildFromJSConstructorExpression ( constructors [ 0 ] ) . Compile ( ) ;
136+ }
137+ else
138+ {
139+ // Multiple constructors or optional parameters require overload resolution.
140+ constructorDescriptor =
141+ _marshaller . BuildConstructorOverloadDescriptor ( constructors ) ;
142+ }
134143
135- classBuilder = classBuilderType . CreateInstance (
136- new [ ] { typeof ( string ) , typeof ( JSCallbackDescriptor ) } ,
137- new object [ ] { type . Name , constructorDescriptor } ) ;
138- }
144+ classBuilder = classBuilderType . CreateInstance (
145+ new [ ] { typeof ( string ) , typeof ( JSCallbackDescriptor ) } ,
146+ new object [ ] { type . Name , constructorDescriptor } ) ;
147+ }
139148
140- ExportProperties ( type , classBuilder ) ;
141- ExportMethods ( type , classBuilder ) ;
142- ExportNestedTypes ( type , classBuilder ) ;
149+ ExportProperties ( type , classBuilder ) ;
150+ ExportMethods ( type , classBuilder ) ;
151+ ExportNestedTypes ( type , classBuilder ) ;
143152
144- string defineMethodName = type . IsInterface ? "DefineInterface" :
145- isStatic ? "DefineStaticClass" : type . IsValueType ? "DefineStruct" : "DefineClass" ;
146- MethodInfo defineClassMethod = classBuilderType . GetInstanceMethod ( defineMethodName ) ;
147- JSValue classObject = ( JSValue ) defineClassMethod . Invoke (
148- classBuilder ,
149- defineClassMethod . GetParameters ( ) . Select ( ( _ ) => ( object ? ) null ) . ToArray ( ) ) ! ;
153+ string defineMethodName = type . IsInterface ? "DefineInterface" :
154+ isStatic ? "DefineStaticClass" : type . IsValueType ? "DefineStruct" : "DefineClass" ;
155+ MethodInfo defineClassMethod = classBuilderType . GetInstanceMethod ( defineMethodName ) ;
156+ JSValue classObject = ( JSValue ) defineClassMethod . Invoke (
157+ classBuilder ,
158+ defineClassMethod . GetParameters ( ) . Select ( ( _ ) => ( object ? ) null ) . ToArray ( ) ) ! ;
150159
151- classObjectReference = new JSReference ( classObject ) ;
152- _exportedTypes . Add ( type , classObjectReference ) ;
160+ classObjectReference = new JSReference ( classObject ) ;
161+ _exportedTypes [ type ] = classObjectReference ;
162+ }
163+ catch
164+ {
165+ // Clean up the temporary null entry.
166+ _exportedTypes . Remove ( type ) ;
167+ throw ;
168+ }
153169
154170 // Also export any types returned by properties or methods of this type, because
155171 // they might otherwise not be referenced by JS before they are used.
0 commit comments