diff --git a/polymod/Polymod.hx b/polymod/Polymod.hx index cf706a04..6fecf5be 100644 --- a/polymod/Polymod.hx +++ b/polymod/Polymod.hx @@ -1314,6 +1314,35 @@ class Polymod PolymodScriptClass.bumpBlacklistGeneration(); } + + /** + * When a scripted class defines an import for another scripted class, you can blacklist it from being imported. + * @param importPath The full import path to blacklist, as a string. + */ + public static function blacklistScriptClassImport(importPath:String):Void + { + PolymodScriptClass.blacklistedScriptClasses.push(importPath); + } + + /** + * When an instance field from a script calss is being called, you can blacklist it from being called. + * @param cls The class type with the fields. + * @param fields The instance fields you want to blacklist. + */ + public static function blacklistScriptClassInstanceFields(cls:String, fields:Array):Void + { + PolymodScriptClass.blacklistedScriptClassInstanceFields.set(cls, fields); + } + + /** + * When a static field from a script class is being called, you can blacklist it from being used. + * @param cls The class type with the fields. + * @param fields The static fields you want to blacklist. + */ + public static function blacklistScriptClassStaticFields(cls:String, fields:Array):Void + { + PolymodScriptClass.blacklistedScriptClassStaticFields.set(cls, fields); + } } /** diff --git a/polymod/hscript/_internal/Interp.hx b/polymod/hscript/_internal/Interp.hx index fa2dd663..e92ae2d1 100644 --- a/polymod/hscript/_internal/Interp.hx +++ b/polymod/hscript/_internal/Interp.hx @@ -301,7 +301,16 @@ class Interp } #end - else if (Std.isOfType(o, PolymodStaticAbstractReference)) + // Check for script class blacklisted fields. + var oScriptCls:Null = Util.getScriptClassName(o); + if (oScriptCls != null && ((PolymodScriptClass.blacklistedScriptClassStaticFields.get(oScriptCls)?.contains(f) ?? false) + || (PolymodScriptClass.blacklistedScriptClassInstanceFields.get(oScriptCls)?.contains(f) ?? false))) + { + error(EBlacklistedField(f)); + return null; + } + + if (Std.isOfType(o, PolymodStaticAbstractReference)) { var ref:PolymodStaticAbstractReference = cast(o, PolymodStaticAbstractReference); return ref.callFunction(f, args); @@ -574,6 +583,31 @@ class Interp } } + + /** + * Given a class declaration, fetches all fields with the `@:blacklisted` metadata and adds it to the script class blacklist. + * @param cls The class declaration. + */ + static function registerScriptClassBlacklist(cls:ClassDecl):Void + { + var clsName:String = Util.getFullClassName(cls); + if (cls.meta.length > 0 && cls.meta.findIndex((m) -> return m.name == ':blacklisted') != -1) + { + Polymod.blacklistScriptClassImport(clsName); + } + + // Filter fields to see which have the `@:blacklisted` metadata. + var staticFields:Array = [for (field in cls.staticFields.filter((f) -> f.meta.length > 0 && f.meta.findIndex((m) -> return m.name == ':blacklisted') != -1)) field.name]; + var instanceFields:Array = [for (field in cls.fields.filter((f) -> f.meta.length > 0 && f.meta.findIndex((m) -> return m.name == ':blacklisted') != -1)) field.name]; + + if (staticFields.length > 0) + Polymod.blacklistScriptClassStaticFields(clsName, staticFields); + + if (instanceFields.length > 0) + Polymod.blacklistScriptClassInstanceFields(clsName, instanceFields); + } + + static function registerScriptClass(c:ClassDecl) { var name = Util.getFullClassName(c); @@ -2612,6 +2646,15 @@ class Interp return null; } + // Check for script class blacklisted fields. + var oScriptCls:Null = Util.getScriptClassName(o); + if (oScriptCls != null && ((PolymodScriptClass.blacklistedScriptClassStaticFields.get(oScriptCls)?.contains(f) ?? false) + || (PolymodScriptClass.blacklistedScriptClassInstanceFields.get(oScriptCls)?.contains(f) ?? false))) + { + error(EBlacklistedField(f)); + return null; + } + // If not, check if it is a blacklisted instance field. if (oCls.length > 0 && oCls != 'Object') { @@ -2746,6 +2789,15 @@ class Interp } } + // Check for script class blacklisted. + var oScriptCls:Null = Util.getScriptClassName(o); + if (oScriptCls != null && ((PolymodScriptClass.blacklistedScriptClassStaticFields.get(oScriptCls)?.contains(f) ?? false) + || (PolymodScriptClass.blacklistedScriptClassInstanceFields.get(oScriptCls)?.contains(f) ?? false))) + { + error(EBlacklistedField(f)); + return null; + } + // Otherwise, we assume the field is fine to use. if (Std.isOfType(o, PolymodStaticAbstractReference)) { @@ -3024,6 +3076,7 @@ class Interp isExtern: c.isExtern, staticFields: staticFields, }; + registerScriptClassBlacklist(classDecl); registerScriptClass(classDecl); case DEnum(e): if (isImportFile) continue; @@ -3064,6 +3117,19 @@ class Interp public static function validateImports():Void { + function tryImport(cls:ClassDecl, clsImport:ClassImport):Void + { + if (PolymodScriptClass.blacklistedScriptClasses.contains(clsImport.fullPath)) + { + // Set as `null` so it's registered as blacklisted. + cls.imports.set(clsImport.name, null); + } + else + { + cls.imports.set(clsImport.name, clsImport); + } + } + for (cls in _scriptClassDescriptors) { var clsPath = Util.getFullClassName(cls); @@ -3081,7 +3147,7 @@ class Interp if ((imp.pkg?.length ?? 0) == 0) { - cls.imports.set(imp.name, classImport); + tryImport(cls, classImport); continue; } @@ -3089,7 +3155,7 @@ class Interp var fullPackage:String = hasPackage ? cls.pkg.join(".") + "." : ""; if (hasPackage && clsPath.indexOf(fullPackage) == 0) { - cls.imports.set(imp.name, classImport); + tryImport(cls, classImport); } } @@ -3105,7 +3171,7 @@ class Interp if (imp.wildcard) importWildcard(cls, imp); else - cls.imports.set(imp.name, imp); + tryImport(cls, imp); } } @@ -3127,7 +3193,7 @@ class Interp if (_scriptClassDescriptors.exists(imp.fullPath)) { - cls.imports.set(key, imp); + tryImport(cls, imp); continue; } @@ -3247,6 +3313,11 @@ class Interp // Check if this is a scripted class. if (_scriptClassDescriptors.exists(classImport.fullPath) || _scriptEnumDescriptors.exists(classImport.fullPath)) { + if (PolymodScriptClass.blacklistedScriptClasses.contains(classImport.fullPath) && !_scriptEnumDescriptors.exists(classImport.fullPath)) + { + cls.imports.set(classImport.name, null); + continue; + } cls.imports.set(classImport.name, classImport); continue; } diff --git a/polymod/hscript/_internal/PolymodScriptClass.hx b/polymod/hscript/_internal/PolymodScriptClass.hx index 0a8e481c..8c657a72 100644 --- a/polymod/hscript/_internal/PolymodScriptClass.hx +++ b/polymod/hscript/_internal/PolymodScriptClass.hx @@ -50,6 +50,24 @@ class PolymodScriptClass */ public static final blacklistedInstanceFields:Map> = new Map>(); + /* + * List of blacklisted full package script classes that aren't able to be imported. + * Cleared everytime scripts are reset. + */ + public static var blacklistedScriptClasses:Array = []; + + /** + * Provide a scripted class with an array of its static fields to blacklist them. + * Blacklisted fields cannot be gotten or set. + */ + public static final blacklistedScriptClassStaticFields:Map> = new Map>(); + + /** + * Provide a scripted class with an array of its static fields to blacklist them. + * Blacklisted fields cannot be gotten or set. + */ + public static final blacklistedScriptClassInstanceFields:Map> = new Map>(); + /** * Field names that may not be reached even through a receiver whose type is not known. */ @@ -460,6 +478,10 @@ class PolymodScriptClass public static function clearScriptedClasses():Void { scriptInterp.clearScriptClassDescriptors(); + + blacklistedScriptClasses = []; + blacklistedScriptClassStaticFields.clear(); + blacklistedScriptClassInstanceFields.clear(); } /** diff --git a/polymod/hscript/_internal/PolymodStaticClassReference.hx b/polymod/hscript/_internal/PolymodStaticClassReference.hx index 1a4c0e5c..cb39d3b7 100644 --- a/polymod/hscript/_internal/PolymodStaticClassReference.hx +++ b/polymod/hscript/_internal/PolymodStaticClassReference.hx @@ -42,7 +42,7 @@ class PolymodStaticClassReference #end @:privateAccess { - if (Interp._scriptClassDescriptors.exists(clsName)) + if (Interp._scriptClassDescriptors.exists(clsName) && !PolymodScriptClass.blacklistedScriptClasses.contains(clsName)) { return new PolymodStaticClassReference(Interp._scriptClassDescriptors.get(clsName)); } diff --git a/polymod/util/Util.hx b/polymod/util/Util.hx index 4c9fc409..b55e6867 100644 --- a/polymod/util/Util.hx +++ b/polymod/util/Util.hx @@ -7,6 +7,9 @@ import polymod.format.BaseParseFormat; import polymod.format.ParseRules; import polymod.fs.PolymodFileSystem.IFileSystem; import polymod.hscript._internal.Expr; +import polymod.hscript._internal.Interp; +import polymod.hscript._internal.PolymodScriptClass; +import polymod.hscript._internal.PolymodStaticClassReference; #if unifill import unifill.Unifill; #end @@ -780,6 +783,24 @@ class Util } } + public static function getScriptClassName(cls:Dynamic):Null + { + var clsName:Null = null; + if (Std.isOfType(cls, PolymodStaticClassReference)) + { + clsName = cast(cls, PolymodStaticClassReference).getFullyQualifiedName(); + } + else if (Std.isOfType(cls, PolymodScriptClass)) + { + clsName = cast(cls, PolymodScriptClass).fullyQualifiedName; + } + else if (Std.isOfType(cls, String)) + { + clsName = getFullClassName(Interp.findScriptClassDescriptor(cast cls)); + } + return clsName; + } + /** * Retrieves the full qualified name of a class declaration. * @param clsDecl The class declaration.