mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Compare commits
22 commits
e9a1ee42b2
...
b476f9a989
Author | SHA1 | Date | |
---|---|---|---|
|
b476f9a989 | ||
|
25a2d7a5a7 | ||
|
4e9a1cefb4 | ||
|
a2f0f29866 | ||
|
a1176e8dae | ||
|
d0efde8a04 | ||
|
9a1985369f | ||
|
f6dd56d01b | ||
|
0197c977ef | ||
|
fc438b853c | ||
|
56c53cb247 | ||
|
a23dfe27e8 | ||
|
f1614b1369 | ||
|
482ee3b6c6 | ||
|
d41c1ab442 | ||
|
5590871c87 | ||
|
99e963c57c | ||
|
32dece403c | ||
|
d0a5da52c2 | ||
|
a209171205 | ||
|
546334a6cd | ||
|
299ccfe2bf |
3 changed files with 437 additions and 2 deletions
|
@ -588,6 +588,13 @@ class Project extends HXProject {
|
|||
function configureCustomMacros() {
|
||||
// This macro allows addition of new functionality to existing Flixel. -->
|
||||
addHaxeMacro("addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')");
|
||||
|
||||
var abstracts:Array<String> = [
|
||||
'funkin.*',
|
||||
'flixel.*',
|
||||
'haxe.*'
|
||||
];
|
||||
addHaxeMacro("funkin.util.macro.PolymodMacro.buildPolymodAbstracts(['" + abstracts.join("', '") + "'])");
|
||||
}
|
||||
|
||||
function configureOutputDir() {
|
||||
|
|
|
@ -248,6 +248,11 @@ class PolymodHandler
|
|||
Polymod.addImportAlias('lime.utils.Assets', funkin.Assets);
|
||||
Polymod.addImportAlias('openfl.utils.Assets', funkin.Assets);
|
||||
|
||||
for (key => value in funkin.util.macro.PolymodMacro.aliases)
|
||||
{
|
||||
Polymod.addImportAlias(key, Type.resolveClass(value));
|
||||
}
|
||||
|
||||
// Add blacklisting for prohibited classes and packages.
|
||||
|
||||
// `Sys`
|
||||
|
@ -333,8 +338,19 @@ class PolymodHandler
|
|||
{
|
||||
return {
|
||||
assetLibraryPaths: [
|
||||
'default' => 'preload', 'shared' => 'shared', 'songs' => 'songs', 'videos' => 'videos', 'tutorial' => 'tutorial', 'week1' => 'week1',
|
||||
'week2' => 'week2', 'week3' => 'week3', 'week4' => 'week4', 'week5' => 'week5', 'week6' => 'week6', 'week7' => 'week7', 'weekend1' => 'weekend1',
|
||||
'default' => 'preload',
|
||||
'shared' => 'shared',
|
||||
'songs' => 'songs',
|
||||
'videos' => 'videos',
|
||||
'tutorial' => 'tutorial',
|
||||
'week1' => 'week1',
|
||||
'week2' => 'week2',
|
||||
'week3' => 'week3',
|
||||
'week4' => 'week4',
|
||||
'week5' => 'week5',
|
||||
'week6' => 'week6',
|
||||
'week7' => 'week7',
|
||||
'weekend1' => 'weekend1',
|
||||
],
|
||||
coreAssetRedirect: CORE_FOLDER,
|
||||
}
|
||||
|
|
412
source/funkin/util/macro/PolymodMacro.hx
Normal file
412
source/funkin/util/macro/PolymodMacro.hx
Normal file
|
@ -0,0 +1,412 @@
|
|||
package funkin.util.macro;
|
||||
|
||||
import haxe.macro.Context;
|
||||
import haxe.macro.Expr;
|
||||
import haxe.macro.Type;
|
||||
|
||||
using haxe.macro.ExprTools;
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* This macro creates aliases for abstracts.
|
||||
* That way we can use abstracts in hscript
|
||||
*/
|
||||
@SuppressWarnings(['checkstyle:CodeSimilarity', 'checkstyle:Dynamic'])
|
||||
class PolymodMacro
|
||||
{
|
||||
/**
|
||||
* The abstracts and their corresponding aliases
|
||||
* `key` => original class path
|
||||
* `value` => alias class path
|
||||
*/
|
||||
public static var aliases(get, never):Map<String, String>;
|
||||
|
||||
static function get_aliases():Map<String, String>
|
||||
{
|
||||
// truly a sight to behold
|
||||
return Reflect.callMethod(null, Reflect.field(Type.resolveClass('funkin.util.macro.AbstractAliases'), 'get'), []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Function that builds all the alias classes
|
||||
* @param abstractClasses An array of packages and classes
|
||||
* (`!` infront of the name will exclude classes from being built)
|
||||
*/
|
||||
public static macro function buildPolymodAbstracts(abstractClasses:Array<String>):Void
|
||||
{
|
||||
Context.onAfterTyping((types) -> {
|
||||
if (alreadyCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var sortedAbstractClasses:Array<String> = [];
|
||||
for (abstractCls in abstractClasses)
|
||||
{
|
||||
if (abstractCls.startsWith('!'))
|
||||
{
|
||||
sortedAbstractClasses.insert(0, abstractCls);
|
||||
}
|
||||
else
|
||||
{
|
||||
sortedAbstractClasses.push(abstractCls);
|
||||
}
|
||||
}
|
||||
|
||||
var abstractAliases:Map<String, String> = new Map<String, String>();
|
||||
|
||||
for (type in types)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ModuleType.TAbstract(a):
|
||||
var cls:AbstractType = a.get();
|
||||
if (cls.isPrivate)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (abstractCls in sortedAbstractClasses)
|
||||
{
|
||||
var negate:Bool = abstractCls.startsWith('!');
|
||||
var name:String = abstractCls.replace('!', '').replace('.*', '');
|
||||
if (!negate && !cls.module.startsWith(name) && moduleTypePath(cls) != name && packTypePath(cls) != name)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (negate)
|
||||
{
|
||||
if (cls.module.startsWith(name) || moduleTypePath(cls) == name || packTypePath(cls) == name)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
abstractAliases.set('${packTypePath(cls)}', 'polymod.abstracts.${packTypePath(cls)}');
|
||||
buildAbstract(cls);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
Context.defineModule('funkin.util.macro.PolymodMacro', [
|
||||
{
|
||||
pack: ['funkin', 'util', 'macro'],
|
||||
name: 'AbstractAliases',
|
||||
kind: TypeDefKind.TDClass(null, [], false, false, false),
|
||||
fields: [
|
||||
{
|
||||
name: 'get',
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FFun(
|
||||
{
|
||||
args: [],
|
||||
ret: (macro :Map<String, String>),
|
||||
expr: macro
|
||||
{
|
||||
return $v{abstractAliases};
|
||||
}
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
],
|
||||
pos: Context.currentPos()
|
||||
}
|
||||
]);
|
||||
|
||||
// the callback is called twice, which this leads to issues
|
||||
alreadyCalled = true;
|
||||
});
|
||||
}
|
||||
|
||||
#if macro
|
||||
static var alreadyCalled:Bool = false;
|
||||
static var skipFields:Array<String> = [];
|
||||
|
||||
static function buildAbstract(abstractCls:AbstractType):Void
|
||||
{
|
||||
if (abstractCls.impl == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
skipFields = [];
|
||||
|
||||
var cls:ClassType = abstractCls.impl.get();
|
||||
|
||||
// we use the functions to check whether we need to skip some fields
|
||||
// that is why we sort the fields, so that functions are handled first
|
||||
var sortedFields:Array<ClassField> = sortFields(cls.statics.get());
|
||||
|
||||
var fields:Array<Field> = [];
|
||||
for (field in sortedFields)
|
||||
{
|
||||
if (field.name == '_new')
|
||||
{
|
||||
fields.push(buildCreateField(abstractCls, field));
|
||||
continue;
|
||||
}
|
||||
|
||||
fields = fields.concat(createFields(abstractCls, field));
|
||||
};
|
||||
|
||||
Context.defineType(
|
||||
{
|
||||
pos: Context.currentPos(),
|
||||
pack: ['polymod', 'abstracts'].concat(abstractCls.pack),
|
||||
name: abstractCls.name,
|
||||
kind: TypeDefKind.TDClass(null, [], false, false, false),
|
||||
fields: fields,
|
||||
}, null);
|
||||
}
|
||||
|
||||
static function sortFields(fields:Array<ClassField>):Array<ClassField>
|
||||
{
|
||||
var sortedFields:Array<ClassField> = [];
|
||||
for (field in fields)
|
||||
{
|
||||
switch (field.type)
|
||||
{
|
||||
case Type.TLazy(f):
|
||||
switch (f())
|
||||
{
|
||||
case Type.TFun(_, _):
|
||||
sortedFields.insert(0, field);
|
||||
default:
|
||||
sortedFields.push(field);
|
||||
}
|
||||
case Type.TFun(_, _):
|
||||
sortedFields.insert(0, field);
|
||||
default:
|
||||
sortedFields.push(field);
|
||||
}
|
||||
}
|
||||
return sortedFields;
|
||||
}
|
||||
|
||||
static function buildCreateField(cls:AbstractType, field:ClassField):Field
|
||||
{
|
||||
var funcArgs:Array<FunctionArg> = [];
|
||||
var funcArgNames:Array<String> = [];
|
||||
switch (field.type)
|
||||
{
|
||||
case Type.TFun(args, _):
|
||||
for (arg in args)
|
||||
{
|
||||
funcArgs.push(
|
||||
{
|
||||
name: arg.name,
|
||||
type: (macro :Dynamic),
|
||||
opt: arg.opt
|
||||
});
|
||||
funcArgNames.push(arg.name);
|
||||
}
|
||||
default:
|
||||
throw 'how is this not a function';
|
||||
}
|
||||
|
||||
var expr:String = '${newExpr(cls, field)}(${funcArgNames.join(', ')})';
|
||||
|
||||
return {
|
||||
name: 'create',
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FFun(
|
||||
{
|
||||
args: funcArgs,
|
||||
ret: (macro :Dynamic),
|
||||
expr: macro
|
||||
{
|
||||
@:privateAccess
|
||||
return ${Context.parse(expr, Context.currentPos())};
|
||||
},
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
};
|
||||
}
|
||||
|
||||
static function newExpr(cls:AbstractType, field:ClassField):Null<String>
|
||||
{
|
||||
if ('${moduleTypePath(cls)}' == 'flixel.util.FlxSignal.FlxTypedSignal')
|
||||
{
|
||||
return 'new flixel.util.FlxSignal.FlxTypedSignal<Dynamic->Void>';
|
||||
}
|
||||
|
||||
if (cls.params.length <= 0)
|
||||
{
|
||||
return 'new ${moduleTypePath(cls)}';
|
||||
}
|
||||
|
||||
return 'new ${moduleTypePath(cls)}< ${[for (_ in 0...cls.params.length) 'Dynamic'].join(', ')} >';
|
||||
}
|
||||
|
||||
static function createFields(cls:AbstractType, field:ClassField):Array<Field>
|
||||
{
|
||||
if (skipFields.contains(field.name))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
switch (field.type)
|
||||
{
|
||||
case Type.TLazy(f):
|
||||
return _createFields(cls, field, f());
|
||||
default:
|
||||
return _createFields(cls, field, field.type);
|
||||
}
|
||||
}
|
||||
|
||||
static function _createFields(cls:AbstractType, field:ClassField, type:Type):Array<Field>
|
||||
{
|
||||
if (field.meta.has(':to'))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
var fields:Array<Field> = [];
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case Type.TFun(args, ret):
|
||||
var fieldArgs:Array<FunctionArg> = [];
|
||||
var exprArgs:Array<String> = [];
|
||||
for (arg in args)
|
||||
{
|
||||
if (arg.name == 'this')
|
||||
{
|
||||
var memberVariable:String = field.name.replace('get_', '').replace('set_', '');
|
||||
if (memberVariable != field.name)
|
||||
{
|
||||
skipFields.push(memberVariable);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
exprArgs.push(arg.name);
|
||||
fieldArgs.push(
|
||||
{
|
||||
name: arg.name,
|
||||
type: (macro :Dynamic),
|
||||
opt: arg.opt,
|
||||
});
|
||||
}
|
||||
|
||||
var returnStr:String = 'return ';
|
||||
var returnType:ComplexType = (macro :Dynamic);
|
||||
switch (ret)
|
||||
{
|
||||
case Type.TAbstract(t, _):
|
||||
if (t.get().name == 'Void')
|
||||
{
|
||||
returnStr = '';
|
||||
returnType = (macro :Void);
|
||||
}
|
||||
default:
|
||||
}
|
||||
|
||||
var expr:String = '${returnStr}${moduleTypePath(cls)}.${field.name}(${exprArgs.join(', ')})';
|
||||
|
||||
fields.push(
|
||||
{
|
||||
name: field.name,
|
||||
doc: field.doc,
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FFun(
|
||||
{
|
||||
args: fieldArgs,
|
||||
ret: returnType,
|
||||
expr: macro
|
||||
{
|
||||
@:privateAccess
|
||||
${Context.parse(expr, Context.currentPos())};
|
||||
},
|
||||
params: []
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
});
|
||||
case Type.TAbstract(t, params):
|
||||
fields.push(
|
||||
{
|
||||
name: field.name,
|
||||
doc: field.doc,
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
|
||||
pos: Context.currentPos()
|
||||
});
|
||||
|
||||
var expr:String = '${moduleTypePath(cls)}.${field.name}';
|
||||
|
||||
fields.push(
|
||||
{
|
||||
name: 'get_${field.name}',
|
||||
doc: field.doc,
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FFun(
|
||||
{
|
||||
args: [],
|
||||
ret: (macro :Dynamic),
|
||||
expr: macro
|
||||
{
|
||||
@:privateAccess
|
||||
return ${Context.parse(expr, Context.currentPos())};
|
||||
},
|
||||
params: []
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
});
|
||||
case TType(t, params):
|
||||
fields.push(
|
||||
{
|
||||
name: field.name,
|
||||
doc: field.doc,
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FProp('get', 'never', (macro :Dynamic), null),
|
||||
pos: Context.currentPos()
|
||||
});
|
||||
|
||||
var expr:String = '${moduleTypePath(cls)}.${field.name}';
|
||||
|
||||
fields.push(
|
||||
{
|
||||
name: 'get_${field.name}',
|
||||
doc: field.doc,
|
||||
access: [Access.APublic, Access.AStatic],
|
||||
kind: FieldType.FFun(
|
||||
{
|
||||
args: [],
|
||||
ret: (macro :Dynamic),
|
||||
expr: macro
|
||||
{
|
||||
@:privateAccess
|
||||
return ${Context.parse(expr, Context.currentPos())};
|
||||
},
|
||||
params: []
|
||||
}),
|
||||
pos: Context.currentPos()
|
||||
});
|
||||
default:
|
||||
return [];
|
||||
};
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
// Dynamic so any kind of type with `module` and `name` fields works
|
||||
static function moduleTypePath(type:Dynamic):String
|
||||
{
|
||||
var dot:String = type.module.length != 0 ? '.' : '';
|
||||
return '${type.module}${dot}${type.name}';
|
||||
}
|
||||
|
||||
// Dynamic so any kind of type with `pack` and `name` fields works
|
||||
static function packTypePath(type:Dynamic):String
|
||||
{
|
||||
var dot:String = type.pack.length != 0 ? '.' : '';
|
||||
return '${type.pack.join('.')}${dot}${type.name}';
|
||||
}
|
||||
#end
|
||||
}
|
Loading…
Reference in a new issue