diff --git a/polymod/hscript/_internal/PolymodInterpEx.hx b/polymod/hscript/_internal/PolymodInterpEx.hx index ae572f52..2f11d675 100644 --- a/polymod/hscript/_internal/PolymodInterpEx.hx +++ b/polymod/hscript/_internal/PolymodInterpEx.hx @@ -324,7 +324,7 @@ class PolymodInterpEx extends Interp continue; } - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import ${imp.fullPath}', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import ${imp.fullPath}. Check to ensure the module exists and is spelled correctly.', clsPath); } // Check if the scripted classes extend the right type. @@ -338,7 +338,7 @@ class PolymodInterpEx extends Interp case CTPath(path, params): if (params != null && params.length > 0) { - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, do not include type parameters in super class name', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, do not include type parameters in super class name.', clsPath); } default: @@ -346,7 +346,7 @@ class PolymodInterpEx extends Interp } // Default - Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}, is the type imported?', clsPath); + Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not extend ${superClassPath}. Make sure the type to extend has been imported.', clsPath); } else { @@ -1852,8 +1852,14 @@ class PolymodInterpEx extends Interp } else if (PolymodScriptClass.abstractClassImpls.exists(importedClass.fullPath)) { // We used a macro to map each abstract to its implementation. importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); - trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - trace(Type.getClassFields(importedClass.cls)); + + if (importedClass.cls == null) { + Polymod.warning(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Abstract type ${importedClass.fullPath} could not be resolved. Try using the underlying type instead.', origin); + } else { + // trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); + // trace(Type.getClassFields(importedClass.cls)); + } + } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { // do nothing } else { @@ -1866,7 +1872,6 @@ class PolymodInterpEx extends Interp // If the class is still not found, skip this import entirely. if (resultCls == null && resultEnm == null) { - //Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import class ${importedClass.fullPath}', origin); // this could be a scripted class or enum that hasn't been registered yet importsToValidate.set(importedClass.name, importedClass); continue; @@ -1908,8 +1913,6 @@ class PolymodInterpEx extends Interp } else if (PolymodScriptClass.abstractClassImpls.exists(importedClass.fullPath)) { // We used a macro to map each abstract to its implementation. importedClass.cls = PolymodScriptClass.abstractClassImpls.get(importedClass.fullPath); - trace('RESOLVED ABSTRACT CLASS ${importedClass.fullPath} -> ${Type.getClassName(importedClass.cls)}'); - trace(Type.getClassFields(importedClass.cls)); } else if (_scriptEnumDescriptors.exists(importedClass.fullPath)) { // do nothing } else { @@ -1917,7 +1920,6 @@ class PolymodInterpEx extends Interp // If the class is still not found, skip this import entirely. if (resultCls == null) { - //Polymod.error(SCRIPT_CLASS_MODULE_NOT_FOUND, 'Could not import class ${importedClass.fullPath}', origin); // this could be a scripted class that hasn't been registered yet importsToValidate.set(importedClass.name, importedClass); continue; diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index d80049c8..f2c53a5d 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -19,7 +19,7 @@ using StringTools; class PolymodScriptClassMacro { /** * Returns a `Map>` which maps superclass paths to scripted classes. - * So `class ScriptedStage extends Stage implements HScriptable` will be `"Stage" -> ScriptedStage` + * So `class ScriptedStage extends Stage implements HScriptable` will be `"Stage" -> ScriptedStage` */ public static macro function listHScriptedClasses():ExprOf>> { if (!onGenerateCallbackRegistered) @@ -62,13 +62,17 @@ class PolymodScriptClassMacro { static var onAfterTypingCallbackRegistered:Bool = false; static function onGenerate(allTypes:Array) { - // Reset these, since onGenerate persists across multiple builds. + // Reset these, since onGenerate persists across multiple builds. var hscriptedClassType:ClassType = MacroUtil.getClassType('polymod.hscript.HScriptedClass'); - var hscriptedClassEntries:Array = []; + var hscriptedClassEntries:Array = []; var abstractImplEntries:Array = []; var abstractStaticEntries:Array = []; + Context.info('PolymodScriptClassMacro: Processing abstracts...', Context.currentPos()); + + var startTime:Float = Sys.time(); + for (type in allTypes) { switch (type) { // Class instances @@ -76,8 +80,8 @@ class PolymodScriptClassMacro { var classType:ClassType = t.get(); var classPath:String = '${classType.pack.concat([classType.name]).join(".")}'; - if (classType.isInterface) { - // Ignore interfaces. + if (classType.isInterface) { + // Ignore interfaces. } else if (MacroUtil.implementsInterface(classType, hscriptedClassType)) { // Context.info('${classPath} implements HScriptedClass? YEAH', Context.currentPos()); // TODO: Do we need to parameterize? @@ -95,54 +99,58 @@ class PolymodScriptClassMacro { } else { } case TAbstract(t, _params): var abstractPath:String = t.toString(); - if (abstractPath == 'flixel.util.FlxColor') { - var abstractType = t.get(); - var abstractImpl = abstractType.impl.get(); - var abstractImplPath = abstractType.impl.toString(); - // Context.info('${abstractImplPath} implements FlxColor', Context.currentPos()); + var abstractType = t.get(); + var abstractImpl = abstractType.impl?.get(); + var abstractImplPath:String = abstractType.impl?.toString() ?? ''; - var entryData = [ - macro $v{abstractPath}, - macro $v{abstractImplPath} - ]; + if (abstractImpl == null) { + // If the abstract doesn't have an implementation, it's usually an extern or something, so we always want to ignore it. + continue; + } - abstractImplEntries.push(macro $a{entryData}); + var entryData = [ + macro $v{abstractPath}, + macro $v{abstractImplPath} + ]; - for (field in abstractImpl.statics.get()) { - switch (field.type) { - case TAbstract(_, _): - // - case TType(_, _): - // - default: - continue; - } - - var key:String = '${abstractImplPath}.${field.name}'; + abstractImplEntries.push(macro $a{entryData}); - if (!staticFieldToClass.exists(key)) { + for (field in abstractImpl.statics.get()) { + switch (field.type) { + case TAbstract(_, _): + // + case TType(_, _): + // + default: continue; - } - - var staticEntryData = [ - macro $v{key}, - macro $v{staticFieldToClass[key]}, - ]; + } + + var key:String = '${abstractImplPath}.${field.name}'; - abstractStaticEntries.push(macro $a{staticEntryData}); + if (!staticFieldToClass.exists(key)) { + continue; } - // Try to apply RTTI? - abstractType.meta.add(':rtti', [], Context.currentPos()); - abstractImpl.meta.add(':rtti', [], Context.currentPos()); + var staticEntryData = [ + macro $v{key}, + macro $v{staticFieldToClass[key]}, + ]; + + abstractStaticEntries.push(macro $a{staticEntryData}); } default: - continue; + continue; } } - var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); - polymodScriptClassClassType.meta.remove('hscriptedClasses'); + var endTime:Float = Sys.time(); + + var duration:Float = endTime - startTime; + + Context.info('PolymodScriptClassMacro: Registered ${hscriptedClassEntries.length} HScriptedClasses, ${abstractImplEntries.length} abstract impls, ${abstractStaticEntries.length} abstract statics in ${duration} sec.', Context.currentPos()); + + var polymodScriptClassClassType:ClassType = MacroUtil.getClassType('polymod.hscript._internal.PolymodScriptClassMacro'); + polymodScriptClassClassType.meta.remove('hscriptedClasses'); polymodScriptClassClassType.meta.add('hscriptedClasses', hscriptedClassEntries, Context.currentPos()); polymodScriptClassClassType.meta.remove('abstractImpls'); polymodScriptClassClassType.meta.add('abstractImpls', abstractImplEntries, Context.currentPos()); @@ -155,124 +163,146 @@ class PolymodScriptClassMacro { static function onAfterTyping(types: Array):Void { var fields:Array = []; + Context.info('PolymodScriptClassMacro: Processing abstract static fields...', Context.currentPos()); + + var startTime:Float = Sys.time(); + for (type in types) { switch (type) { case TAbstract(a): var abstractPath = a.toString(); var abstractType = a.get(); - if (abstractPath != 'flixel.util.FlxColor') { - continue; - } - - if (abstractType.impl == null) { + // If the abstract is private, the implementation won't be accessible. + var abstractIsPrivate = abstractType.isPrivate; + if (abstractIsPrivate) { continue; } - var abstractImplPath = abstractType.impl.toString(); - var abstractImplType = abstractType.impl.get(); + // Check if, for other reasons, the implementation is missing. + var abstractHasNoImpl = abstractType.impl == null; + if (abstractHasNoImpl) { + // Only a few classes end up here, generally ones that are implemented directly in code. + // Includes like StdTypes.Float, StdTypes.Dynamic, StdTypes.Void, cpp.Int16, cpp.SizeT, Class, Enum + continue; + } else { + var abstractImplPath = abstractType.impl.toString(); + var abstractImplType = abstractType.impl.get(); + var abstractImplStatics = abstractImplType.statics.get(); + var underlyingType = abstractType.type; + + for (field in abstractImplStatics) { + switch (field.kind) { + case FVar(read, write): + var canGet:Bool = read == AccInline || read == AccNormal; + if (read == AccCall) { + var getter:Null = null; + for (f in abstractImplStatics) { + if (f.name == 'get_${field.name}'){ + getter = f; + break; + } + } - for (field in abstractImplType.statics.get()) { - switch (field.kind) { - case FVar(read, write): - trace(field.name, read, write); + if (getter == null) { + throw 'Getter is null?'; + } - var canGet:Bool = read == AccInline || read == AccNormal; - if (read == AccCall) { - var getter:Null = null; - for (f in abstractImplType.statics.get()) { - if (f.name == 'get_${field.name}'){ - getter = f; + switch (getter.type) { + case TFun(args, _): + if (args.length != 0) + continue; + default: + throw 'Getter has an unknown type?'; } - } - if (getter == null) { - throw 'This should not happen'; + canGet = true; } - switch (getter.type) { - case TFun(args, _): - if (args.length != 0) - continue; - default: - throw 'This should not happen'; - } + var canSet:Bool = write == AccNormal; + if (write == AccCall) { + var setter:Null = null; + for (f in abstractImplType.statics.get()){ + if (f.name == 'set_${field.name}'){ + setter = f; + break; + } + } - canGet = true; - } + if (setter == null) { + throw 'Setter is null?'; + } - var canSet:Bool = write == AccNormal; - if (write == AccCall) { - var setter:Null = null; - for (f in abstractImplType.statics.get()){ - if (f.name == 'set_${field.name}'){ - setter = f; + switch (setter.type) { + case TFun(args, _): + if (args.length != 1) + continue; + default: + throw 'Setter has an unknown type?'; } - } - if (setter == null) { - throw 'This should not happen'; + canSet = true; } - switch (setter.type) { - case TFun(args, _): - if (args.length != 1) - continue; - default: - throw 'This should not happen'; - } + if (canGet) { + var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; + + fields.push({ + pos: Context.currentPos(), + name: fieldName, + access: [Access.APublic, Access.AStatic], + kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', (macro: Dynamic), null) + }); + + var fieldExpr:Expr = null; + try { + // when this fails, this should mean that we are dealing with an enum abstract + // so we need to handle it differently + var fullPath:String = '${abstractType.module}.${abstractType.name}'; + Context.getType(fullPath); + fieldExpr = Context.parse('${fullPath}.${field.name}', Context.currentPos()); + } catch (_) { + fieldExpr = Context.getTypedExpr(field.expr()); + } - canSet = true; - } + if (canGet) { + fields.push({ + pos: Context.currentPos(), + name: 'get_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [], + ret: null, + expr: macro { + @:privateAccess + return ${fieldExpr}; + } + }) + }); + } - if (!canGet && !canSet) { - continue; - } - - var fieldName:String = '${abstractImplPath.replace('.', '_')}_${field.name}'; - - fields.push({ - pos: Context.currentPos(), - name: fieldName, - access: [Access.APublic, Access.AStatic], - kind: FProp(canGet ? 'get' : 'never', canSet ? 'set' : 'never', Context.toComplexType(field.type), null) - }); - - if (canGet) { - fields.push({ - pos: Context.currentPos(), - name: 'get_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [], - ret: null, - expr: macro { - @:privateAccess - return ${Context.parse(abstractPath + '.' + field.name, Context.currentPos())}; - } - }) - }); - } - - if (canSet) { - fields.push({ - pos: Context.currentPos(), - name: 'set_${fieldName}', - access: [Access.APublic, Access.AStatic], - kind: FFun({ - args: [{name: 'value'}], - ret: null, - expr: macro { - @:privateAccess - return ${Context.parse(abstractPath + '.' + field.name, Context.currentPos())} = value; - } - }) - }); - } + if (canSet) { + fields.push({ + pos: Context.currentPos(), + name: 'set_${fieldName}', + access: [Access.APublic, Access.AStatic], + kind: FFun({ + args: [{name: 'value'}], + ret: null, + expr: macro { + @:privateAccess + return ${fieldExpr} = value; + } + }) + }); + } - staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); - default: - continue; + staticFieldToClass.set('${abstractImplPath}.${field.name}', 'polymod.hscript._internal.AbstractStaticMembers_${iteration}'); + } + + default: + continue; + } } } default: @@ -292,6 +322,12 @@ class PolymodScriptClassMacro { fields: fields }); + var endTime:Float = Sys.time(); + + var duration:Float = endTime - startTime; + + Context.info('PolymodScriptClassMacro: Processed ${fields.length} static fields in ${duration} sec (iteration #${iteration}).', Context.currentPos()); + iteration++; } #end @@ -299,25 +335,21 @@ class PolymodScriptClassMacro { public static function fetchHScriptedClasses():Map> { var metaData = Meta.getType(PolymodScriptClassMacro); - // trace('Got metaData: ' + metaData); - if (metaData.hscriptedClasses != null) { - trace('Got hscriptedClasses: ' + metaData.hscriptedClasses); - var result:Map> = []; // Each element is formatted as `[superClassPath, classPath]`. for (element in metaData.hscriptedClasses) { - if (element.length != 2) { - throw 'Malformed element in hscriptedClasses: ' + element; - } + if (element.length != 2) { + throw 'Malformed element in hscriptedClasses: ' + element; + } - var superClassPath:String = element[0]; - var classPath:String = element[1]; + var superClassPath:String = element[0]; + var classPath:String = element[1]; var classType:Class = cast Type.resolveClass(classPath); - result.set(superClassPath, classType); - } + result.set(superClassPath, classType); + } return result; } else { @@ -340,20 +372,17 @@ class PolymodScriptClassMacro { var abstractPath:String = element[0]; var abstractImplPath:String = element[1]; - // var abstractType:Class = cast Type.resolveClass(abstractPath); #if js - trace('Resolving using JS method'); var abstractImplType:Class = resolveClass(abstractPath); if (abstractImplType == null) { throw 'Could not resolve ' + abstractPath; } #else - // trace('Resolving using native method'); var abstractImplType:Class = cast Type.resolveClass(abstractImplPath); if (abstractImplType == null) { - throw 'Could not resolve ' + abstractImplPath; + // trace('POLYMOD ABSTRACTS: Could not resolve $abstractImplPath'); } #end