@@ -10,6 +10,9 @@ import haxe.DynamicAccess;
1010using haxe .macro .TypeTools ;
1111using haxe .macro .ComplexTypeTools ;
1212using Lambda ;
13+ using StringTools ;
14+
15+ typedef Either <T , V > = Dynamic ;
1316
1417typedef WrapperParams =
1518{
@@ -47,7 +50,7 @@ typedef WrapperParams =
4750/**
4851 * Generates fields that wrap functions from the provided classes in a way that
4952 * they'll throw an error if accessed or call the original function if whitelisted.
50- * It is only intended to be used with classes with only static functions .
53+ * It is best to be used with classes with only static fields .
5154 *
5255 * You can add your own sandboxed implementations of the fields and make aliases to them (see `BlacklistParams.aliases`).
5356 */
@@ -63,42 +66,52 @@ class BlacklistClassMacro
6366 params .whitelistMode ?? = true ;
6467 params .fieldList ?? = [];
6568
69+ final classes : Array <ClassType > = [for (c in params .classes ) MacroUtil .getClassType (c )];
70+ if (classes .length == 0 ) Context .fatalError (' Invalid class amount, no classes were provided.' , Context .currentPos ());
71+
6672 final buildFields : Array <Field > = Context .getBuildFields ();
6773 final generatedFields : Array <Field > = [];
6874
69- final classes : Array <ClassType > = [for (c in params .classes ) MacroUtil .getClassType (c )];
70- if (classes .length == 0 ) Context .fatalError (' Invalid class amount, no classes were provided.' , Context .currentPos ());
75+ // For convenience...
76+ inline function containsField (fieldName : String ): Bool
77+ {
78+ return buildFields .exists (f -> f .name == fieldName );
79+ }
80+ inline function getField (fieldName : String ): Null <Field >
81+ {
82+ return buildFields .find (f -> f .name == fieldName );
83+ }
7184
7285 // NOTE: As much as I wish these could be a map seems like Haxe is unable to parse them as part of the metadata.
7386 final aliases : DynamicAccess <Array <String >> = cast params .aliases ;
74- final aliasedFields : Array <String > = params .ignoreList ?. copy () ?? [];
75- final fieldsToWrap : Array <String > = [];
87+ final fieldsToSkip : Array <String > = params .ignoreList ?. copy () ?? [];
88+ final pendingFieldsToWrap : Array <String > = [];
7689 if (aliases != null )
7790 {
7891 for (field => aliasFields in aliases )
7992 {
8093 if (aliasFields .length == 0 ) Context .warning (' No alias fields specified to be generated for " $field "' , Context .currentPos ());
8194
82- final wrappedField : Null <Field > = getField (buildFields , field );
95+ final wrappedField : Null <Field > = getField (field );
8396 if (wrappedField == null )
8497 {
8598 // Field might be on the provided classes, put it on queue.
86- fieldsToWrap .push (field );
99+ pendingFieldsToWrap .push (field );
87100 continue ;
88101 }
89102
90103 for (aliasName in aliasFields )
91104 {
92- if (containsField (buildFields , aliasName ))
105+ if (containsField (aliasName ))
93106 {
94- Context .error (' Tried to generate " ${aliasName }" alias but it already exists in the class.' , getField (buildFields , aliasName ).pos );
107+ Context .error (' Tried to generate " ${aliasName }" alias but it already exists in the class.' , getField (aliasName ).pos );
95108 }
96109
97- final wrapper : Null <Field > = generateWrapperFunction (aliasName , wrappedField );
110+ final wrapper : Null <Field > = generateWrapperField (aliasName , wrappedField );
98111 if (wrapper != null )
99112 {
100113 generatedFields .push (wrapper );
101- aliasedFields .push (aliasName );
114+ fieldsToSkip .push (aliasName );
102115 }
103116 else
104117 Context .error (' Could not generate alias for field " $field "; it may not be a function.' , wrappedField .pos );
@@ -110,174 +123,164 @@ class BlacklistClassMacro
110123 {
111124 for (field in c .statics .get ())
112125 {
113- if (! field .isPublic || aliasedFields .contains (field .name )) continue ;
126+ if (! field .isPublic || fieldsToSkip .contains (field .name )) continue ;
114127
115- if (containsField (buildFields , field .name ))
128+ if (containsField (field .name ))
116129 {
117- if (! getField (buildFields , field .name ).meta .exists (m -> m .name == ' :blacklistOverride' ))
130+ if (! getField (field .name ).meta .exists (m -> m .name == ' :blacklistOverride' ))
118131 {
119132 // 'reportError' doesn't abort compilation, so it allows us to see all the duplicate fields!
120133 Context .reportError (' Tried to generate " ${field .name }" but it already exists in the class. \n '
121- + ' Add @:blacklistOverride or add it to the ignoreList to ignore.' ,
122- Context . currentPos () );
134+ + ' Add @:blacklistOverride or add it to " ignoreList" to ignore.' ,
135+ getField ( field . name ). pos );
123136 }
124137 continue ;
125138 }
126139
127140 final blacklisted : Bool = params .whitelistMode != params .fieldList .contains (field .name );
128- final wrapper : Null <Field > = generateWrapperFunction (field .name , extractField ( field ) , c .name , blacklisted );
141+ final wrapper : Null <Field > = generateWrapperField (field .name , field , c .name , blacklisted );
129142 generatedFields .push (wrapper );
130143
131144 // TODO: When this happens should it make the field whitelisted (or vice-versa)?
132- if (fieldsToWrap .contains (field .name ))
145+ if (pendingFieldsToWrap .contains (field .name ))
133146 {
134147 for (alias in aliases .get (field .name ))
135148 {
136- generatedFields .push (generateWrapperFunction (alias , wrapper ));
137- fieldsToWrap .remove (field .name );
149+ generatedFields .push (generateWrapperField (alias , wrapper ));
150+ pendingFieldsToWrap .remove (field .name );
138151 }
139152 }
140153 }
141154 }
142155
143- for (f in fieldsToWrap )
156+ for (f in pendingFieldsToWrap )
144157 {
145158 Context .reportError (' Tried to generate alias fields for " $f " but it does not exist.' , Context .currentPos ());
146159 }
147160
148161 return buildFields .concat (generatedFields );
149162 }
150163
151- static function generateWrapperFunction ( funcName : String , wrappedField : Field , ? className : String , blacklist : Bool = false ): Null <Field >
164+ static function generateWrapperField ( fieldName : String , wrappedField : Either < Field , ClassField > , ? className : String , blacklist : Bool = false ): Null <Field >
152165 {
153- var kind = switch (wrappedField .kind )
154- {
155- case FFun (f ):
156- final wrapFunc : Function = Reflect .copy (f );
157- final params : Array <Expr > = [for (a in wrapFunc .args ) macro $i {a .name }];
166+ final pack : Array <String > = [wrappedField .name ];
167+ if (className != null ) pack .unshift (className );
158168
159- final pack = [wrappedField .name ];
160- if (className != null ) pack .unshift (className );
161-
162- wrapFunc .expr = if (blacklist )
163- {
164- macro throw $v {' Function ${pack .join (' .' )} is blacklisted.' };
165- }
166- else
167- {
168- returnsVoid (wrapFunc .ret ) ? macro $p {pack }($a {params }) : macro return $p {pack }($a {params });
169- }
170- FFun (wrapFunc );
171- default :
172- return null ;
169+ inline function getWrapperExpr (args : Array <{name : String }>, ? retType : ComplexType ): Expr
170+ {
171+ return if (blacklist )
172+ {
173+ macro throw $v {' Function ${pack .join (' .' )} is blacklisted.' };
174+ }
175+ else
176+ {
177+ final params : Array <Expr > = [for (a in args ) macro $i {a .name }];
178+ retType .toString () == ' StdTypes.Void' ? macro $p {pack }($a {params }) : macro return $p {pack }($a {params });
179+ }
173180 }
174181
175- return {
176- name : funcName ,
177- pos : wrappedField .pos ,
178- doc : blacklist ? BLACKLISTED_FUNCTION_DOC : wrappedField .doc ,
179- access : [APublic , AInline , AStatic ],
180- kind : kind
181- };
182- }
183-
184- /**
185- * Generates a dummy version of a function field. Only used to retrieve its argument data.
186- * @param field The `ClassField` to "clone" into a `Field`.
187- * @return May return null if it's not a function.
188- */
189- static function extractField (field : ClassField ): Null <Field >
190- {
191- var success : Bool = false ;
192- final fieldFunc : Function = {args : [], params : getParamDecls (field .params ), expr : macro null }
193- final tExpr : Null <TypedExpr > = field .expr ();
194- if (tExpr == null )
182+ var wrapperKind : Null <FieldType >;
183+ if (wrappedField .kind is FieldType )
195184 {
196- // Field may be extern
197- switch (field . type . followWithAbstracts () )
185+ var wrappedField : Field = wrappedField ;
186+ wrapperKind = switch (wrappedField . kind )
198187 {
199- case TFun ( args , ret ):
200- fieldFunc . args = [ for ( a in args ) { name : a . name , opt : a . opt , type : a . t . toComplexType ()}] ;
201- fieldFunc . ret = ret . toComplexType ( );
202- success = true ;
188+ case FFun ( f ):
189+ final wrapFunc : Function = Reflect . copy ( f ) ;
190+ wrapFunc . expr = getWrapperExpr ( wrapFunc . args , wrapFunc . ret );
191+ FFun ( wrapFunc ) ;
203192 default :
193+ Context .error (' Blacklist Macro: Making aliases for anything other than functions is not supported.' , wrappedField .pos );
204194 }
205195 }
206196 else
207197 {
208- switch (tExpr .expr )
198+ var wrappedField : ClassField = wrappedField ;
199+ switch (wrappedField .kind )
209200 {
210- case TFunction (tfunc ):
211- final tArgs : Array <FunctionArg > = [
212- for (a in tfunc .args )
213- {
214- name : a .v .name ,
215- meta : a .v .meta ?. get (),
216- value : a .value != null ? Context .getTypedExpr (a .value ) : null ,
217- type : a .v .t .toComplexType ()
218- }
219- ];
220- fieldFunc .args = tArgs ;
221- fieldFunc .ret = tfunc .t .toComplexType ();
222- success = true ;
201+ case FVar (_ , _ ):
202+ wrapperKind = FProp (' default' , ' never' , wrappedField .type .toComplexType (), macro $p {pack });
223203 default :
224204 }
205+
206+ if (wrapperKind == null )
207+ {
208+ if (wrappedField .expr () != null )
209+ {
210+ switch (wrappedField .expr ().expr )
211+ {
212+ case TFunction (tfunc ):
213+ final args : Array <FunctionArg > = [
214+ for (a in tfunc .args )
215+ {
216+ name : a .v .name ,
217+ value : a .value != null ? Context .getTypedExpr (a .value ) : null ,
218+ type : a .v .t .toComplexType ()
219+ }
220+ ];
221+ wrapperKind = FFun (
222+ {
223+ args : args ,
224+ params : getParamDecls (wrappedField .params ),
225+ ret : tfunc .t .toComplexType (),
226+ expr : getWrapperExpr (args , tfunc .t .toComplexType ()),
227+ });
228+ default :
229+ }
230+ }
231+ else
232+ {
233+ // Some targets have core types with externs as fields and those don't have a TypedExpr.
234+ switch (wrappedField .type .follow (true ))
235+ {
236+ case TFun (args , ret ):
237+ wrapperKind = FFun (
238+ {
239+ args : [for (a in args ) {name : a .name , opt : a .opt , type : a .t .toComplexType ()}],
240+ params : getParamDecls (wrappedField .params ),
241+ ret : ret .toComplexType (),
242+ expr : getWrapperExpr (args , ret .toComplexType ())
243+ });
244+ default :
245+ }
246+ }
247+
248+ if (wrapperKind == null )
249+ {
250+ Context .error (' Blacklist Macro: Invalid field type, it should be a variable or a function.' , wrappedField .pos );
251+ }
252+ }
225253 }
226254
227- if (! success )
255+ final access = [APublic , AStatic ];
256+ if (wrapperKind .match (FFun (_ )))
228257 {
229- Context . error ( ' Attempted to convert " ${ field . name } but it is not a function. ' , Context . currentPos () );
258+ access . push ( AInline );
230259 }
231260
232261 return {
233- name : field .name ,
234- access : [APublic , AStatic , AInline ],
235- doc : field .doc ,
236- pos : field .pos ,
237- kind : FFun (fieldFunc )
238- }
239- }
240-
241- static inline function containsField (fields : Array <Field >, fieldName : String ): Bool
242- {
243- return fields .exists (f -> f .name == fieldName );
244- }
245-
246- static inline function getField (fields : Array <Field >, fieldName : String ): Null <Field >
247- {
248- return fields .find (f -> f .name == fieldName );
262+ name : fieldName ,
263+ pos : wrappedField .pos ,
264+ doc : blacklist ? BLACKLISTED_FUNCTION_DOC : wrappedField .doc ,
265+ access : access ,
266+ kind : wrapperKind
267+ };
249268 }
250269
251270 static function getParamDecls (params : Array <TypeParameter >): Array <TypeParamDecl >
252271 {
253272 final result : Array <TypeParamDecl > = [];
254273 for (p in params )
255274 {
256- switch (p .t )
275+ switch (p .t . getClass () ?. kind )
257276 {
258- case TInst (_ .get () => cls , _ ):
259- switch (cls .kind )
260- {
261- case KTypeParameter (constraints ):
262- result .push ({name : p .name , constraints : [for (c in constraints ) c .toComplexType ()]});
263- default :
264- Context .error (" Provided type parameters are not of the KTypeParameter kind, this shouldn't happen!" , Context .currentPos ());
265- }
277+ case KTypeParameter (constraints ):
278+ result .push ({name : p .name , constraints : [for (c in constraints ) c .toComplexType ()]});
266279 default :
267- Context .error (" Provided type parameters are not of a class type. " , Context .currentPos ());
280+ Context .error (" Provided type parameters are not of the KTypeParameter kind, this shouldn't happen! " , Context .currentPos ());
268281 }
269282 }
270283 return result ;
271284 }
272-
273- static function returnsVoid (? ct : ComplexType ): Bool
274- {
275- if (ct == null ) return true ;
276- return switch (ct )
277- {
278- case TPath (p ): p .name == ' Void' ;
279- default : false ;
280- }
281- }
282285}
283286#end
0 commit comments