Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion source/funkin/modding/PolymodHandler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import funkin.util.FileUtil;
import funkin.util.macro.ClassMacro;
import polymod.backends.PolymodAssets.PolymodAssetType;
import polymod.format.ParseRules.TextFileFormat;
import polymod.hscript._internal.PolymodScriptClass;
import polymod.Polymod;

/**
Expand Down Expand Up @@ -387,7 +388,7 @@ class PolymodHandler
{
if (cls == null) continue;
var className:String = Type.getClassName(cls);
if (polymod.hscript._internal.PolymodScriptClass.importOverrides.exists(className)) continue;
if (PolymodScriptClass.importOverrides.exists(className)) continue;
Polymod.blacklistImport(className);
}

Expand Down Expand Up @@ -450,6 +451,16 @@ class PolymodHandler
Polymod.blacklistImport('funkin.external.android.CallbackUtil');
Polymod.blacklistImport('funkin.external.android.DataFolderUtil');
Polymod.blacklistImport('funkin.external.android.JNIUtil');

// Add import aliases for Typedefs that extend classes and abstracts like FlxSpriteGroup or FlxSignal.
// Doing this last so that we don't set an alias for an already blacklisted import.
for (name => cls in ClassMacro.listTypedefWrappers())
{
if (PolymodScriptClass.importOverrides.exists(name)) continue;

var resolvedCls:Class<Dynamic> = Type.resolveClass(cls);
Polymod.addImportAlias(name, resolvedCls);
}
}

/**
Expand Down
112 changes: 112 additions & 0 deletions source/funkin/util/macro/ClassMacro.hx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package funkin.util.macro;

import haxe.macro.Context;
import haxe.macro.Expr;
import haxe.macro.Type.AbstractType;
import haxe.macro.Type.DefType;
import haxe.macro.Type.ClassType;

/**
Expand Down Expand Up @@ -64,6 +66,20 @@ class ClassMacro
return macro funkin.util.macro.CompiledClassList.getTyped($v{request}, ${targetClassExpr});
}

/**
* Get a list of Typedefs that redirect to classes such as `FlxSpriteGroup`, along with the classes they redirect to.
*/
public static macro function listTypedefWrappers():ExprOf<Map<String, String>>
{
if (!onGenerateCallbackRegistered)
{
onGenerateCallbackRegistered = true;
Context.onGenerate(onGenerate);
}

return macro funkin.util.macro.CompiledClassList.getTypedefWrappers();
}

#if macro
/**
* Callback executed after the typing phase but before the generation phase.
Expand All @@ -75,6 +91,7 @@ class ClassMacro
{
// Reset these, since onGenerate persists across multiple builds.
classListsRaw = [];
typedefList.clear();

for (request in classListsToGenerate)
{
Expand Down Expand Up @@ -104,6 +121,90 @@ class ClassMacro
}
}
}

case TType(t1, _params):
var defType:DefType = t1.get();
var className:String = "";

switch (defType.type)
{
case TInst(t2, _):
className = t2.toString();

case TAbstract(t2, params):
var absType:AbstractType = t2.get();

// See if the abstract has any implicit casts that we can use for the wrapper class.
for (to in absType.to)
{
if (to.field == null) continue;

switch (to.field.type)
{
case TFun(args, ret):
// If there are no args, assume this is the only implicit cast.
if (args.length == 0)
{
switch (ret)
{
case TInst(t3, _):
className = t3.toString();

default:
// Do nothing.
}
break;
}

switch (args[0].t)
{
case TInst(_, checkParams):
// If the params match, use this instance.
var paramsMatch:Bool = (params.length == checkParams.length);
if (paramsMatch)
{
for (i in 0...params.length)
{
if (Std.string(params[i]) != Std.string(checkParams[i]))
{
paramsMatch = false;
break;
}
}
}

if (!paramsMatch) continue;

switch (ret)
{
case TInst(t3, _):
className = t3.toString();

default:
}
break;

default:
// Do nothing.
}

default:
// Do nothing.
}
}

// Otherwise resort to using the impl class.
if (className == "") className = absType.impl?.toString() ?? "";

default:
// Do nothing.
}

if (className == "") continue; // The class is an anonymous object; skip.

var classKey:String = defType.pack.join(".") + (defType.pack.length > 0 ? "." : "") + defType.name;
typedefList.set(classKey, className);

// Other types (things like enums)
default:
continue;
Expand All @@ -128,6 +229,7 @@ class ClassMacro

// Reset outdated metadata.
if (compiledClassList.meta.has('classLists')) compiledClassList.meta.remove('classLists');
if (compiledClassList.meta.has('typedefList')) compiledClassList.meta.remove('typedefList');

var classLists:Array<Expr> = [];
// Generate classLists.
Expand All @@ -145,8 +247,17 @@ class ClassMacro
classLists.push(macro $a{classListEntries});
}

// Generate typedef wrappers. They're structured like [wrapperName, wrapperClass].
var typedefWrappers:Array<Expr> = [];
for (name => cls in typedefList)
{
var entry:Array<Expr> = [macro $v{name}, macro $v{cls}];
typedefWrappers.push(macro $a{entry});
}

// Insert classLists into metadata.
compiledClassList.meta.add('classLists', classLists, Context.currentPos());
compiledClassList.meta.add('typedefList', typedefWrappers, Context.currentPos());
}

static function doesClassMatchRequest(classType:ClassType, request:String):Bool
Expand Down Expand Up @@ -197,5 +308,6 @@ class ClassMacro

static var classListsRaw:Map<String, Array<String>> = [];
static var classListsToGenerate:Array<String> = [];
static var typedefList:Map<String, String> = [];
#end
}
17 changes: 16 additions & 1 deletion source/funkin/util/macro/CompiledClassList.hx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ using funkin.util.AnsiUtil;
class CompiledClassList
{
static var classLists:Map<String, List<Class<Dynamic>>> = [];
static var typedefWrappers:Map<String, String> = [];
static var initialized:Bool = false;

/**
Expand All @@ -24,7 +25,7 @@ class CompiledClassList
// Meta.getType returns Dynamic<Array<Dynamic>>.
var metaData = Meta.getType(CompiledClassList);

if (metaData.classLists != null)
if (metaData.classLists != null && metaData.typedefList != null)
{
for (list in metaData.classLists)
{
Expand All @@ -45,6 +46,14 @@ class CompiledClassList

classLists.set(id, classes);
}

for (list in metaData.typedefList)
{
var data:Array<String> = cast list;

// The first element is the wrapper name, whereas the second is the wrapper class behind it.
typedefWrappers.set(data[0], data[1]);
}
}
else
{
Expand All @@ -67,6 +76,12 @@ class CompiledClassList
return classLists.get(request);
}

public static function getTypedefWrappers():Map<String, String>
{
if (!initialized) init();
return typedefWrappers;
}

public static inline function getTyped<T>(request:String, type:Class<T>):List<Class<T>>
{
return cast get(request);
Expand Down
Loading