diff --git a/checkstyle.json b/checkstyle.json index 4c2510ee..45c27f8b 100644 --- a/checkstyle.json +++ b/checkstyle.json @@ -172,7 +172,9 @@ "ignoreOverride": true, "tokens": ["ABSTRACT_DEF", "CLASS_DEF", "ENUM_DEF", "INTERFACE_DEF"], "modifier": "PUBLIC", - "excludeNames": ["new", "toString"] + "excludeNames": ["new", "toString"], + + "severity": "IGNORE" }, "type": "FieldDocComment" }, @@ -494,7 +496,9 @@ "props": { "enforceReturnTypeForAnonymous": false, "allowEmptyReturn": true, - "enforceReturnType": true + "enforceReturnType": true, + + "severity": "IGNORE" }, "type": "Return" }, diff --git a/include.xml b/include.xml index 16e21ce5..3833d7dd 100644 --- a/include.xml +++ b/include.xml @@ -4,7 +4,5 @@ - + diff --git a/polymod/hscript/_internal/HScriptedClassMacro.hx b/polymod/hscript/_internal/HScriptedClassMacro.hx index c6b87522..8c86d10a 100644 --- a/polymod/hscript/_internal/HScriptedClassMacro.hx +++ b/polymod/hscript/_internal/HScriptedClassMacro.hx @@ -145,6 +145,8 @@ class HScriptedClassMacro { // Context.info(' Building scripted class init() function', Context.currentPos()); var clsTypeName:String = cls.pack.join('.') != '' ? '${cls.pack.join('.')}.${cls.name}' : cls.name; + // scriptInit returns null if the script throws an error while initializing. + var clsType:ComplexType = Context.toComplexType(Context.getType(clsTypeName)); var function_init:Field = { name: 'scriptInit', @@ -157,7 +159,7 @@ class HScriptedClassMacro args: [ {name: 'clsName', type: macro :String}, {name: 'args', type: macro :...Dynamic}], params: null, - ret: Context.toComplexType(Context.getType(clsTypeName)), + ret: polymod.util.MacroUtil.nullable(clsType), expr: macro { var clsRef = polymod.hscript._internal.PolymodStaticClassReference.tryBuild(clsName); @@ -185,8 +187,11 @@ class HScriptedClassMacro } catch (error) { + var callStack:String = polymod.util.Util.fetchCallStack(); + polymod.Polymod.error(SCRIPT_RUNTIME_EXCEPTION, - 'Could not construct instance of scripted class (${clsName} extends ' + $v{clsTypeName} + '):\n${error}', SCRIPT_RUNTIME); + 'An uncaught exception was thrown while constructing an instance of scripted class (${clsName} extends ' + $v{clsTypeName} + '):\n$error\n$callStack', + SCRIPT_RUNTIME); return null; } }, @@ -524,7 +529,7 @@ class HScriptedClassMacro { if (_asc == null) { - return $v{'PolymodScriptedClass<${cls.name} extends ${cls.superClass.t.get().name}>(NO ASC)'}; + return $v{'PolymodScriptClass<${cls.name} extends ${cls.superClass.t.get().name}>(NO ASC)'}; } else { diff --git a/polymod/hscript/_internal/Interp.hx b/polymod/hscript/_internal/Interp.hx index a1c45e8e..e37c0bec 100644 --- a/polymod/hscript/_internal/Interp.hx +++ b/polymod/hscript/_internal/Interp.hx @@ -424,7 +424,11 @@ class Interp } catch (error) { - Polymod.error(SCRIPT_RUNTIME_EXCEPTION, 'Could not construct instance of scripted class ($clsToInit extends ' + clsName + '):\n${error}'); + var callStack:String = polymod.util.Util.fetchCallStack(); + + Polymod.error(SCRIPT_RUNTIME_EXCEPTION, + 'An uncaught exception was thrown while constructing an instance of scripted class ($clsToInit extends ' + clsName + '):\n$error\n$callStack', + SCRIPT_RUNTIME); return null; } } @@ -508,7 +512,7 @@ class Interp } } - private static function registerScriptClass(c:ClassDecl) + static function registerScriptClass(c:ClassDecl) { var name = Util.getFullClassName(c); @@ -674,10 +678,10 @@ class Interp { var superClass:PolymodAbstractScriptClass = cast(_proxy.superClass, PolymodScriptClass); return superClass.fieldWrite(id, v); + } else { + set(_proxy.superClass, id, v); + return v; } - - Reflect.setProperty(_proxy.superClass, id, v); - return v; } // Fallback to setting in local scope. @@ -747,7 +751,9 @@ class Interp return superClass.fieldWrite(id, v); } - Reflect.setProperty(_proxy.superClass, id, v); + // Directly assign the value. + // This is needed because `assignValue` may sometimes be called from the constructor. + PolymodAbstractScriptClass.setClassObjectField(_proxy.superClass, id, v); return v; } @@ -759,6 +765,7 @@ class Interp switch (decl?.set) { case "set": + // Allow assigning to "null" only for local fields. final setName = 'set_$id'; if (!_propTrack.exists(setName)) { @@ -771,11 +778,16 @@ class Interp case "never": error(EInvalidPropSet(id)); return null; + + case "null": + // If the property setter is "null", it can only be assigned on local fields. + // Thankfully, this is a local field! + // So we can just fallthrough to the default case. } if ((decl?.isfinal ?? false) && decl?.expr != null) { - error(EInvalidAccess(id)); + error(EInvalidFinalSet(id)); return null; } } @@ -802,7 +814,9 @@ class Interp return superClass.fieldWrite(id, v); } - Reflect.setProperty(_proxy.superClass, id, v); + // Directly assign the value. + // This is needed because `assignValue` may sometimes be called from the constructor. + PolymodAbstractScriptClass.setClassObjectField(_proxy.superClass, id, v); return v; } } @@ -816,13 +830,21 @@ class Interp for (imp in _proxy._c.imports) { if (imp.name != id0) continue; - var finals = PolymodFinalMacro.getAllFinals().get(imp.fullPath) ?? []; + var finals = PolymodFinalMacro.getFinals(imp.fullPath); if (finals.contains(id)) { error(EInvalidFinalSet(id)); return null; } + + var privates = PolymodFinalMacro.getPrivateProperties(imp.fullPath); + + if (privates.contains(id)) + { + error(EInvalidPropSet(id)); + return null; + } } } } @@ -976,7 +998,7 @@ class Interp var l = locals.get(id); var v:Dynamic = (l == null) ? resolve(id) : l.r; - if (l != null && l.isfinal && l.r != null) return error(EInvalidAccess(id)); + if (l != null && l.isfinal && l.r != null) return error(EInvalidFinalSet(id)); if (prefix) { v += delta; @@ -1112,7 +1134,7 @@ class Interp } } - inline function error(e:#if hscriptPos ErrorDef #else Error #end, rethrow = false):Dynamic + public inline function error(e:#if hscriptPos ErrorDef #else Error #end, rethrow = false):Dynamic { #if hscriptPos var e = new Error(e, curExpr?.pmin ?? 0, curExpr?.pmax ?? 0, curExpr?.origin ?? 'unknown', curExpr?.line ?? 0); #end if (rethrow) this.rethrow(e) @@ -1480,7 +1502,7 @@ class Interp { case EField(e, f): var obj = expr(e); - if (obj == null) error(EInvalidAccess(f)); + if (obj == null) error(ENullObjectReference(f)); return fcall(obj, f, args); default: return call(null, expr(e), args); @@ -2294,22 +2316,6 @@ class Interp } error(EInvalidScriptedVarGet(f)); - - // var result = Reflect.getProperty(o, f); - // To save a bit of performance, we only query for the existence of the property - // if the value is reported as null, AND only in debug builds. - - // #if debug - // if (!Reflect.hasField(o, f)) - // { - // var propertyList = Type.getInstanceFields(Type.getClass(o)); - // if (propertyList.indexOf(f) == -1) - // { - // error(EInvalidScriptedVarGet(f)); - // } - // } - // #end - // return result; } #if (hl && haxe4) else if (Std.isOfType(o, Enum)) @@ -2348,23 +2354,6 @@ class Interp } } #end - // if (o == null) error(EInvalidAccess(f)); - // return - // { - // #if php - // // https://github.com/HaxeFoundation/haxe/issues/4915 - // try - // { - // Reflect.getProperty(o, f); - // } - // catch (e:Dynamic) - // { - // Reflect.field(o, f); - // } - // #else - // Reflect.getProperty(o, f); - // #end - // } } function set(o:Dynamic, f:String, v:Dynamic):Dynamic @@ -2402,14 +2391,21 @@ class Interp } catch (e:Dynamic) { - error(EInvalidFinalSet(f)); + error(EInvalidAccess(f)); } } else if (Std.isOfType(o, PolymodStaticClassReference)) { var ref:PolymodStaticClassReference = cast(o, PolymodStaticClassReference); - return ref.setField(f, v); + try + { + return ref.setField(f, v); + } + catch (e:Dynamic) + { + error(EInvalidAccess(f)); + } } else if (Std.isOfType(o, PolymodScriptClass)) { @@ -2426,7 +2422,7 @@ class Interp return superClass.fieldWrite(f, v); } - Reflect.setProperty(proxy.superClass, f, v); + set(proxy.superClass, f, v); } else { @@ -2442,14 +2438,11 @@ class Interp } error(EInvalidScriptedVarSet(f)); - - // Reflect.setProperty(o, f, v); - // return v; } try { - Reflect.setProperty(o, f, v); + PolymodAbstractScriptClass.setClassObjectField(o, f, v); } catch (e) { @@ -2910,45 +2903,47 @@ class Interp var fieldDecl = getScriptClassStaticFieldDecl(clsName, fieldName); if (fieldDecl != null) { - if (!this.variables.exists(prefixedName)) + switch (fieldDecl.kind) { - switch (fieldDecl.kind) - { - case KFunction(_fn): - throw 'Cannot override function ${prefixedName}'; - case KVar(v): - if (v.set != null) + case KFunction(_fn): + throw 'Cannot override function ${prefixedName}'; + case KVar(v): + if (v.isfinal) { + throw 'Cannot override final static field ${prefixedName}'; + } + if (v.set != null) + { + switch (v.set) { - switch (v.set) - { - case 'set': - var setterFunc = 'set_${fieldName}'; - if (hasScriptClassStaticFunction(clsName, setterFunc)) - { - return callScriptClassStaticFunction(clsName, setterFunc, [value]); - } - else - { - throw 'Could not resolve setter for property ${prefixedName}'; - } - case 'default': - this.variables.set(prefixedName, value); - return value; - default: + case 'set': + var setterFunc = 'set_${fieldName}'; + if (hasScriptClassStaticFunction(clsName, setterFunc)) + { + return callScriptClassStaticFunction(clsName, setterFunc, [value]); + } + else + { throw 'Could not resolve setter for property ${prefixedName}'; - } - } - else - { - this.variables.set(prefixedName, value); - return value; + } + + case 'never': + throw 'Cannot assign to property ${prefixedName}'; + + case 'null': + throw 'Cannot assign to property ${prefixedName}'; + + case 'default': + this.variables.set(prefixedName, value); + return value; + default: + throw 'Could not resolve setter for property ${prefixedName}'; } - } - } - else - { - this.variables.set(prefixedName, value); - return value; + } + else + { + this.variables.set(prefixedName, value); + return value; + } } } else @@ -3011,4 +3006,4 @@ private class ArrayIterator { return a[pos++]; } -} \ No newline at end of file +} diff --git a/polymod/hscript/_internal/PolymodAbstractScriptClass.hx b/polymod/hscript/_internal/PolymodAbstractScriptClass.hx index b3bcbab3..532f6c83 100644 --- a/polymod/hscript/_internal/PolymodAbstractScriptClass.hx +++ b/polymod/hscript/_internal/PolymodAbstractScriptClass.hx @@ -1,6 +1,9 @@ package polymod.hscript._internal; import haxe.ds.ObjectMap; +import polymod.util.Util; + +using StringTools; @:forward @:access(polymod.hscript._internal.PolymodScriptClass) @@ -78,8 +81,7 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass } else if (this.superClass == null) { - // @:privateAccess this._interp.error(EInvalidAccess(name)); - throw 'field "$name" does not exist in script class ${this.fullyQualifiedName}"'; + return this._interp.error(EInvalidAccess(name)); } else if (Type.getClass(this.superClass) == null) { @@ -90,8 +92,7 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass } else { - // @:privateAccess this._interp.error(EInvalidAccess(name)); - throw 'field "$name" does not exist in script class ${this.fullyQualifiedName}" or super class "${Type.getClassName(Type.getClass(this.superClass))}"'; + return this._interp.error(EInvalidAccess(name)); } } else if (Std.isOfType(this.superClass, PolymodScriptClass)) @@ -113,8 +114,7 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass } catch (e:String) { - @:privateAccess this._interp.error(EInvalidAccess(name)); - // throw "field '" + name + "' does not exist in script class '" + this.fullyQualifiedName + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'"; + return this._interp.error(EInvalidAccess(name)); } } } @@ -171,84 +171,88 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass @:op(a.b) public function fieldWrite(name:String, value:Dynamic):Dynamic { - switch (name) + if (this.findVar(name) != null) { - case _: - if (this.findVar(name) != null) - { - var decl = this.findVar(name, true); - if (decl.isfinal && decl.expr != null) // The variable already exists and has a set value. - { - throw "Invalid access to field " + name; - return null; - } + var decl = this.findVar(name, true); + if (decl.isfinal && decl.expr != null) // The variable already exists and has a set value. + { + return this._interp.error(EInvalidFinalSet(name)); + } - @:privateAccess - switch (decl.set) + @:privateAccess + switch (decl.set) + { + case "set": + final setName = 'set_$name'; + if (!this._interp._propTrack.exists(setName)) { - case "set": - final setName = 'set_$name'; - if (!this._interp._propTrack.exists(setName)) - { - this._interp._propTrack.set(setName, true); - var r:Dynamic = null; - // Children may override it - if (this.topASC != null && this.topASC.findFunction(setName) != null) - { - r = this.topASC.callFunction(setName, [value]); - } - else - { - r = this.callFunction(setName, [value]); - } - this._interp._propTrack.remove(setName); - return r; - } - - case "never" | "null": - return this._interp.error(EInvalidPropSet(name)); + this._interp._propTrack.set(setName, true); + var r:Dynamic = null; + // Children may override it + if (this.topASC != null && this.topASC.findFunction(setName) != null) + { + r = this.topASC.callFunction(setName, [value]); + } + else + { + r = this.callFunction(setName, [value]); + } + this._interp._propTrack.remove(setName); + return r; } - this._interp.variables.set(name, value); - return value; - } - else if (this.superClass != null && Std.isOfType(this.superClass, PolymodScriptClass)) + case "never" | "null": + return this._interp.error(EInvalidPropSet(name)); + } + + this._interp.variables.set(name, value); + return value; + } + else if (this.superClass != null && Std.isOfType(this.superClass, PolymodScriptClass)) + { + var superScriptClass:PolymodAbstractScriptClass = cast(this.superClass, PolymodScriptClass); + try + { + return superScriptClass.fieldWrite(name, value); + } + catch (e:Dynamic) {} + } + else + { + // Class object + try { + if (setClassObjectField(this.superClass, name, value)) { - var superScriptClass:PolymodAbstractScriptClass = cast(this.superClass, PolymodScriptClass); - try - { - return superScriptClass.fieldWrite(name, value); - } - catch (e:Dynamic) {} + return value; } - else - { - // Class object - if (setClassObjectField(this.superClass, name, value)) - { - return value; - } - - @:privateAccess this._interp.error(EInvalidAccess(name)); - // throw "field '" + name + "' does not exist in script class '" + this.fullyQualifiedName + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'"; + } catch (e:String) { + if (e.contains('final')) { + return this._interp.error(EInvalidFinalSet(name)); + } else if (e.contains('private')) { + return this._interp.error(EInvalidPropSet(name)); + } else { + return this._interp.error(EInvalidAccess(name)); } + } } if (this.superClass == null) { - @:privateAccess this._interp.error(EInvalidAccess(name)); - // throw "field '" + name + "' does not exist in script class '" + this.fullyQualifiedName + "'"; + return this._interp.error(EInvalidAccess(name)); } else { - @:privateAccess this._interp.error(EInvalidAccess(name)); - // throw "field '" + name + "' does not exist in script class '" + this.fullyQualifiedName + "' or super class '" + Type.getClassName(Type.getClass(this.superClass)) + "'"; + return this._interp.error(EInvalidAccess(name)); } + + trace('fieldWrite() fallthrough???'); return null; } - private static function retrieveClassObjectFields(o:Dynamic):Array + static function retrieveClassObjectFields(o:Dynamic):Array { + if (Util.getTypeNameOf(o) == "Object") return []; + final superClassCls = Type.getClass(o); if (superClassCls == null) throw "Provided object isn't a class"; @@ -262,27 +266,58 @@ abstract PolymodAbstractScriptClass(PolymodScriptClass) from PolymodScriptClass return fields; } - private static function getClassObjectField(o:Dynamic, field:String):Null + static function getClassObjectField(o:Dynamic, field:String):Null { + if (Util.getTypeNameOf(o) == "Object") return Reflect.getProperty(o, field); + var fields = retrieveClassObjectFields(o); if (fields.contains(field) || fields.contains('get_$field')) return Reflect.getProperty(o, field); throw 'No such field $field'; } - private static function setClassObjectField(o:Dynamic, field:String, value:Dynamic):Bool + /** + * Assign a field of a class object. + * Checks for `final` and `private` variables and fails to assign to immutable fields. + * + * @param o The object to modify. + * @param field The name of the field to modify. + * @param value The value to assign to the field. + * @return `true` for success. + */ + public static function setClassObjectField(o:Dynamic, field:String, value:Dynamic):Bool { + if (Util.getTypeNameOf(o) == "Object") { + Reflect.setProperty(o, field, value); + return true; + } + + var finals = PolymodFinalMacro.getFinalsOf(o); + if (finals.contains(field)) + { + throw 'Cannot set final field $field'; + } + + var privates = PolymodFinalMacro.getPrivatePropertiesOf(o); + if (privates.contains(field)) + { + throw 'Cannot set private field $field'; + } + var fields = retrieveClassObjectFields(o); if (fields.contains(field) || fields.contains('set_$field')) { Reflect.setProperty(o, field, value); return true; } - return false; + + throw 'No such field $field'; } - private static function hasClassObjectField(o:Dynamic, field:String):Bool + static function hasClassObjectField(o:Dynamic, field:String):Bool { + if (Util.getTypeNameOf(o) == "Object") return Reflect.hasField(o, field); + var fields = retrieveClassObjectFields(o); if (fields.contains(field) || fields.contains('get_$field') || fields.contains('set_$field')) return true; diff --git a/polymod/hscript/_internal/PolymodFinalMacro.hx b/polymod/hscript/_internal/PolymodFinalMacro.hx index dd7b6a00..dc0f8a31 100644 --- a/polymod/hscript/_internal/PolymodFinalMacro.hx +++ b/polymod/hscript/_internal/PolymodFinalMacro.hx @@ -1,20 +1,60 @@ package polymod.hscript._internal; +#if macro import haxe.macro.Context; import haxe.macro.Expr; import haxe.macro.Type; +#else +#end +import polymod.util.Util; import haxe.rtti.Meta; +@:nullSafety class PolymodFinalMacro { - private static var _allFinals:Map> = null; + /** + * The name for the Haxe resource that stores Generation Metadata. + */ + static inline final METADATA_RESOURCE_NAME:String = 'PolymodFinalMacro_METADATA'; + + public static inline function getFinals(fullPath:String):Array { + return getAllFinals().get(fullPath) ?? []; + } + + public static inline function getFinalsOf(obj:Dynamic):Array { + while (Std.isOfType(obj, PolymodScriptClass)) obj = obj.superClass; + + var typeName:String = Util.getTypeNameOf(obj); + var result = getFinals(typeName); + return result; + } + + public static inline function getPrivateProperties(fullPath:String):Array { + return getAllPrivateProperties().get(fullPath) ?? []; + } + + public static inline function getPrivatePropertiesOf(obj:Dynamic):Array { + while (Std.isOfType(obj, PolymodScriptClass)) obj = obj.superClass; + + var typeName:String = Util.getTypeNameOf(obj); + var result = getPrivateProperties(typeName); + return result; + } + + private static var _allFinals:Null>> = null; public static function getAllFinals():Map> { - // if (_allFinals == null) - // _allFinals = PolymodFinalMacro.fetchAllFinals(); - // return _allFinals; - return []; + if (_allFinals == null) _allFinals = PolymodFinalMacro.fetchAllFinals(); + return _allFinals; + } + + private static var _allPrivates:Null>> = null; + + public static function getAllPrivateProperties():Map> + { + if (_allPrivates == null) _allPrivates = PolymodFinalMacro.fetchAllPrivateProperties(); + return _allPrivates; } public static macro function locateAllFinals():Void @@ -22,7 +62,10 @@ class PolymodFinalMacro Context.onAfterTyping((types) -> { if (calledBefore) return; - var allFinals:Array = []; + var startTime:Float = Sys.time(); + + var allFinals:Array = []; + var allPrivates:Array = []; for (type in types) { @@ -33,52 +76,134 @@ class PolymodFinalMacro var classPath:String = t.toString(); if (classType.isInterface) continue; - var finals:Array = []; - for (field in classType.statics.get()) - { - if (!field.isFinal) continue; - finals.push(field.name); + var finals:Array = listFinalsOfClassType(classType); + if (finals.length > 0) { + var entryData:Array = [classPath, finals]; + allFinals.push(entryData); } - if (finals.length == 0) continue; - - var entryData = [macro $v{classPath}, macro $v{finals}]; + var privates:Array = listPrivatesOfClassType(classType); + if (privates.length > 0) { + var entryData:Array = [classPath, privates]; + allPrivates.push(entryData); + } - allFinals.push(macro $a{entryData}); default: continue; } } - var finalMacroType:Type = Context.getType('polymod.hscript._internal.PolymodFinalMacro'); + var metadataHXSF = haxe.Serializer.run({ + finals: allFinals, + privates: allPrivates + }); + Context.addResource(METADATA_RESOURCE_NAME, haxe.io.Bytes.ofString(metadataHXSF)); - switch (finalMacroType) - { - case TInst(t, _): - var finalMacroClassType:ClassType = t.get(); - finalMacroClassType.meta.remove('finals'); - finalMacroClassType.meta.add('finals', allFinals, Context.currentPos()); - default: - throw 'Could not find PolymodFinalMacro type'; - } + var endTime:Float = Sys.time(); + + var duration:Float = endTime - startTime; + + Context.info('PolymodFinalMacro: ' + + 'Detected ${allFinals.length} classes with final variables, ' + + '${allPrivates.length} classes with (default,null) properties ' + + 'in ${duration} sec.', + Context.currentPos()); calledBefore = true; }); } #if macro + static function listFinalsOfClassType(classType:Null):Array { + if (classType == null) return []; + + var result:Array = []; + + for (field in classType.fields.get()) + { + // Add final variables. + if (field.isFinal) result.push(field.name); + + // Add properties with `never` accessors. + switch (field.kind) { + case FVar(read, write): + switch (write) { + case AccNever: + result.push(field.name); + default: // Do nothing + } + default: // Do nothing + } + } + + for (field in classType.statics.get()) + { + // Add final variables. + if (field.isFinal) result.push(field.name); + + // Add properties with `never` accessors. + switch (field.kind) { + case FVar(read, write): + switch (write) { + case AccNever: + result.push(field.name); + default: // Do nothing + } + default: // Do nothing + } + } + + return result.concat(listFinalsOfClassType(classType?.superClass?.t?.get())); + } + + static function listPrivatesOfClassType(classType:Null):Array { + if (classType == null) return []; + + var result:Array = []; + + for (field in classType.fields.get()) { + // Add properties with `null` accessors. + switch (field.kind) { + case FVar(read, write): + switch (write) { + case AccNo: + result.push(field.name); + default: // Do nothing + } + default: // Do nothing + } + } + + for (field in classType.statics.get()) + { + // Add properties with `null` accessors. + switch (field.kind) { + case FVar(read, write): + switch (write) { + case AccNo: + result.push(field.name); + default: // Do nothing + } + default: // Do nothing + } + } + + return result.concat(listPrivatesOfClassType(classType?.superClass?.t?.get())); + } + static var calledBefore:Bool = false; #end public static function fetchAllFinals():Map> { - var metaData = Meta.getType(PolymodFinalMacro); + var metaData = fetchMetadata(); + var finals:Array = cast metaData.finals; - if (metaData.finals != null) + if (finals != null) { var result:Map> = []; - for (element in metaData.finals) + for (element in finals) { if (element.length != 2) throw 'Malformed element in finals: ' + element; @@ -95,4 +220,41 @@ class PolymodFinalMacro throw 'No finals found in PolymodFinalMacro'; } } + + public static function fetchAllPrivateProperties():Map> + { + var metaData = fetchMetadata(); + var privates:Array = cast metaData.privates; + + if (privates != null) + { + var result:Map> = []; + + for (element in privates) + { + if (element.length != 2) throw 'Malformed element in privates: ' + element; + + var classPath:String = element[0]; + var privates:Array = element[1]; + + result.set(classPath, privates); + } + + return result; + } + else + { + throw 'No private properties found in PolymodFinalMacro'; + } + } + + static var _metadata:Dynamic = null; + static function fetchMetadata():Dynamic + { + if (_metadata != null) return _metadata; + + var metaDataHXSF:String = haxe.Resource.getString(METADATA_RESOURCE_NAME); + _metadata = haxe.Unserializer.run(metaDataHXSF); + return _metadata; + } } diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 8b559706..93c8f52f 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -150,35 +150,33 @@ class PolymodScriptClass */ static function registerScriptClassByPath(path:String):Void { - @:privateAccess { - var scriptBody = Polymod.assetLibrary.getText(path); - if (scriptBody == null) - { - Polymod.error(SCRIPT_PARSE_FAILED, 'Error while loading script "${path}", could not retrieve script contents!', SCRIPT_RUNTIME); - return; - } - try - { - registerScriptClassByString(scriptBody, path); - } - catch (err:Expr.Error) + var scriptBody = Polymod.assetLibrary.getText(path); + if (scriptBody == null) + { + Polymod.error(SCRIPT_PARSE_FAILED, 'Error while loading script "${path}", could not retrieve script contents!', SCRIPT_RUNTIME); + return; + } + try + { + registerScriptClassByString(scriptBody, path); + } + catch (err:Expr.Error) + { + var errLine:String = #if hscriptPos '${err.line}' #else "#???" #end; + #if hscriptPos + switch (err.e) + #else + switch (err) + #end { - var errLine:String = #if hscriptPos '${err.line}' #else "#???" #end; - #if hscriptPos - switch (err.e) - #else - switch (err) - #end - { - case EUnexpected(s): - Polymod.error(SCRIPT_PARSE_FAILED, - 'Error while parsing function ${path}#${errLine}: EUnexpected' + '\n' + 'Unexpected token "${s}", is there invalid syntax on this line?', SCRIPT_RUNTIME); - case EClassUnresolvedSuperclass(cls, reason): - Polymod.error(SCRIPT_PARSE_FAILED, - 'Error while parsing class ${path}#${errLine}: EClassUnresolvedSuperclass' + '\n' + 'Unresolved superclass "${cls}", ${reason}', SCRIPT_RUNTIME); - default: - Polymod.error(SCRIPT_PARSE_FAILED, 'Error while parsing script ${path}#${errLine}: ' + '\n' + 'An unknown error occurred: ${err}', SCRIPT_RUNTIME); - } + case EUnexpected(s): + Polymod.error(SCRIPT_PARSE_FAILED, + 'Error while parsing function ${path}#${errLine}: EUnexpected' + '\n' + 'Unexpected token "${s}", is there invalid syntax on this line?', SCRIPT_RUNTIME); + case EClassUnresolvedSuperclass(cls, reason): + Polymod.error(SCRIPT_PARSE_FAILED, + 'Error while parsing class ${path}#${errLine}: EClassUnresolvedSuperclass' + '\n' + 'Unresolved superclass "${cls}", ${reason}', SCRIPT_RUNTIME); + default: + Polymod.error(SCRIPT_PARSE_FAILED, 'Error while parsing script ${path}#${errLine}: ' + '\n' + 'An unknown error occurred: ${err}', SCRIPT_RUNTIME); } } } @@ -454,7 +452,7 @@ class PolymodScriptClass callFunction("new", args); if (superClass == null && _c.extend != null) { - @:privateAccess _interp.error(EClassSuperNotCalled); + _interp.error(EClassSuperNotCalled); } } else if (_c.extend != null) @@ -533,7 +531,7 @@ class PolymodScriptClass if (clsToCreate == null) { - @:privateAccess _interp.error(EClassUnresolvedSuperclass(fullExtendString, 'WHY?')); + _interp.error(EClassUnresolvedSuperclass(fullExtendString, 'WHY?')); } } else if (_c.imports.exists(extendString)) @@ -542,12 +540,12 @@ class PolymodScriptClass if (clsToCreate == null) { - @:privateAccess _interp.error(EClassUnresolvedSuperclass(extendString, 'target class blacklisted')); + _interp.error(EClassUnresolvedSuperclass(extendString, 'target class blacklisted')); } } else { - @:privateAccess _interp.error(EClassUnresolvedSuperclass(extendString, 'missing import')); + _interp.error(EClassUnresolvedSuperclass(extendString, 'missing import')); } superClass = Type.createInstance(clsToCreate, args); diff --git a/polymod/hscript/_internal/PolymodScriptClassMacro.hx b/polymod/hscript/_internal/PolymodScriptClassMacro.hx index 2437f016..7d4e6cb0 100644 --- a/polymod/hscript/_internal/PolymodScriptClassMacro.hx +++ b/polymod/hscript/_internal/PolymodScriptClassMacro.hx @@ -115,7 +115,6 @@ class PolymodScriptClassMacro } case TType(t, _params): - var typedefType:DefType = t.get(); var typedefPath:String = t.toString(); var typedefTarget:Type = Context.followWithAbstracts(type); @@ -241,7 +240,6 @@ class PolymodScriptClassMacro } else { - var abstractImplPath = abstractType.impl.toString(); var abstractImplType = abstractType.impl.get(); var abstractImplStatics:Array = abstractImplType.statics.get(); diff --git a/polymod/hscript/_internal/PolymodStaticAbstractReference.hx b/polymod/hscript/_internal/PolymodStaticAbstractReference.hx index 657ce288..97406bbc 100644 --- a/polymod/hscript/_internal/PolymodStaticAbstractReference.hx +++ b/polymod/hscript/_internal/PolymodStaticAbstractReference.hx @@ -107,7 +107,7 @@ class PolymodStaticAbstractReference { if (Reflect.hasField(this.absImpl, fieldName)) { - Reflect.setProperty(this.absImpl, fieldName, fieldValue); + PolymodAbstractScriptClass.setClassObjectField(this.absImpl, fieldName, fieldValue); return fieldValue; } var setterName:String = 'set_$fieldName'; diff --git a/polymod/util/MacroUtil.hx b/polymod/util/MacroUtil.hx index 68c1ebc5..84a4943c 100644 --- a/polymod/util/MacroUtil.hx +++ b/polymod/util/MacroUtil.hx @@ -39,6 +39,10 @@ class MacroUtil } } + public static function nullable(complexType : ComplexType):ComplexType { + return macro: Null<$complexType>; + } + public static function areClassesEqual(class1:ClassType, class2:ClassType):Bool { return class1.pack.join('.') == class2.pack.join('.') && class1.name == class2.name; diff --git a/polymod/util/Util.hx b/polymod/util/Util.hx index 6a265873..55ed0f97 100644 --- a/polymod/util/Util.hx +++ b/polymod/util/Util.hx @@ -694,6 +694,32 @@ class Util return indexOfInsens(arr, x, ignoreConfig) != -1; } + public static function fetchCallStack(exception:Bool = true):String { + var errorMessage:String = ""; + var callStack:Array = haxe.CallStack.exceptionStack(true); + + for (stackItem in callStack) + { + switch (stackItem) + { + case FilePos(innerStackItem, file, line, column): + errorMessage += ' in ${file}#${line}'; + if (column != null) errorMessage += ':${column}'; + case CFunction: + errorMessage += '[Function] '; + case Module(m): + errorMessage += '[Module(${m})] '; + case Method(classname, method): + errorMessage += '[Function(${classname}.${method})] '; + case LocalFunction(v): + errorMessage += '[LocalFunction(${v})] '; + } + errorMessage += '\n'; + } + + return errorMessage; + } + public static function getTypeNameOf(obj:Dynamic):String { var type = Type.typeof(obj);