Skip to content

Commit 4430b16

Browse files
committed
shit from an ass that refuses to work with FileUtil
1 parent 4145f6f commit 4430b16

File tree

2 files changed

+123
-122
lines changed

2 files changed

+123
-122
lines changed

source/funkin/util/ReflectUtil.hx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ package funkin.util;
55
* Used for sandboxing in scripts.
66
*/
77
@:nullSafety
8-
@SuppressWarnings("checkstyle:VarTypeHint")
98
@:build(funkin.util.macro.BlacklistClassMacro.build(
109
{
1110
classes: ["Reflect", "Type"],
@@ -34,8 +33,7 @@ package funkin.util;
3433
"isObject",
3534
"setField",
3635
"setProperty"
37-
],
38-
whitelistMode: true
36+
]
3937
}))
4038
class ReflectUtil
4139
{

source/funkin/util/macro/BlacklistClassMacro.hx

Lines changed: 122 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ import haxe.DynamicAccess;
1010
using haxe.macro.TypeTools;
1111
using haxe.macro.ComplexTypeTools;
1212
using Lambda;
13+
using StringTools;
14+
15+
typedef Either<T, V> = Dynamic;
1316

1417
typedef 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

Comments
 (0)