mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-14 19:25:16 -05:00
Compare commits
24 commits
693d8c4ae4
...
4a11e3ec78
Author | SHA1 | Date | |
---|---|---|---|
|
4a11e3ec78 | ||
|
e9a1ee42b2 | ||
|
9ee4b0b94e | ||
|
907f300267 | ||
|
ba1dbb5f68 | ||
|
d27433318d | ||
|
ee63e8d8d6 | ||
|
ba72fee609 | ||
|
42d93c616a | ||
|
374f6837eb | ||
|
f3b4cac92d | ||
|
918292d5a8 | ||
|
07749285f1 | ||
|
dcc58e9e33 | ||
|
6d35d07245 | ||
|
807373c2d2 | ||
|
c75112b998 | ||
|
b26b1bf4e8 | ||
|
0e36e9261d | ||
|
c00e4e8316 | ||
|
2d68f60c9b | ||
|
a01a5907e7 | ||
|
ad23a1c6d0 | ||
|
d105686251 |
3 changed files with 442 additions and 2 deletions
|
@ -588,6 +588,15 @@ 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> = [
|
||||
'.*',
|
||||
'!thx.Set',
|
||||
'!cpp.*',
|
||||
'!lime.*',
|
||||
'!openfl.*'
|
||||
];
|
||||
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,
|
||||
}
|
||||
|
|
415
source/funkin/util/macro/PolymodMacro.hx
Normal file
415
source/funkin/util/macro/PolymodMacro.hx
Normal file
|
@ -0,0 +1,415 @@
|
|||
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)}${classSuffix}');
|
||||
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 classSuffix:String = '_';
|
||||
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 + classSuffix,
|
||||
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: null,
|
||||
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: null,
|
||||
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: null,
|
||||
opt: arg.opt,
|
||||
});
|
||||
}
|
||||
|
||||
var returnStr:String = switch (ret)
|
||||
{
|
||||
case Type.TAbstract(t, _):
|
||||
if (t.get().name == 'Void')
|
||||
{
|
||||
'';
|
||||
}
|
||||
else
|
||||
{
|
||||
'return ';
|
||||
}
|
||||
default:
|
||||
'return ';
|
||||
};
|
||||
|
||||
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: null,
|
||||
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: null,
|
||||
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: null,
|
||||
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