From 5899186445dc3c6cd71ce4baf1c992d25bb8124f Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Tue, 30 Dec 2025 18:51:22 +0200 Subject: [PATCH] Code optimization (for erorring static access of non static members) --- .../script/interpreter/ScriptDocument.java | 68 ++++++------------- .../interpreter/type/ScriptTypeInfo.java | 26 ++----- .../script/interpreter/type/TypeInfo.java | 57 +++------------- 3 files changed, 38 insertions(+), 113 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java index a22da1a40..46aa5bc97 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/ScriptDocument.java @@ -3496,9 +3496,7 @@ public class ScriptDocument { currentType = null; // We'll use globalFields for the next segment } else if (Character.isUpperCase(firstSegment.charAt(0))) { // Static field access like SomeClass.field - TypeInfo classType = resolveType(firstSegment); - // Class names are always in static context (can only access static members) - currentType = (classType != null) ? classType.asStaticContext() : null; + currentType = resolveType(firstSegment); } else { // Variable or field access if (firstIsPrecededByDot) { @@ -3507,8 +3505,7 @@ public class ScriptDocument { TypeInfo receiverType = resolveReceiverChain(chainStart); if (receiverType != null && receiverType.hasField(firstSegment)) { FieldInfo varInfo = receiverType.getFieldInfo(firstSegment); - // Field values are always instances, even if field itself is static - currentType = (varInfo != null) ? varInfo.getTypeInfo().asInstanceContext() : null; + currentType = (varInfo != null) ? varInfo.getTypeInfo() : null; } else { currentType = null; // Can't resolve } @@ -3516,8 +3513,7 @@ public class ScriptDocument { // This is a variable starting the chain FieldInfo varInfo = resolveVariable(firstSegment, chainStart); if (varInfo != null) { - // Variables are always instances - currentType = varInfo.getTypeInfo().asInstanceContext(); + currentType = varInfo.getTypeInfo(); } else { currentType = null; } @@ -3549,8 +3545,7 @@ public class ScriptDocument { FieldAccessInfo accessInfo = createFieldAccessInfo(segment, segPos[0], segPos[1], receiverType, fieldInfo, isLastSegment, isStaticAccessSeg); marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, accessInfo)); - // Field values are always instances - currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo().asInstanceContext() : null; + currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo() : null; } else { marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR)); currentType = null; @@ -3560,52 +3555,33 @@ public class ScriptDocument { if (globalFields.containsKey(segment)) { FieldInfo fieldInfo = globalFields.get(segment); marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, fieldInfo)); - // Field values are always instances - currentType = fieldInfo.getTypeInfo().asInstanceContext(); + currentType = fieldInfo.getTypeInfo(); } else { marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR)); currentType = null; } } else if (currentType != null && currentType.isResolved()) { // Check if this type has this field - // First check if field exists at all (ignoring static context) - FieldInfo fieldInfo = null; - boolean fieldExists = false; - boolean isStaticAccessError = false; + if (currentType.hasField(segment)) { + FieldInfo fieldInfo = currentType.getFieldInfo(segment); - // Check if field exists by temporarily using instance context - TypeInfo instanceContext = currentType.asInstanceContext(); - if (instanceContext.hasField(segment)) { - fieldExists = true; - // Now check if accessible in actual context - if (currentType.hasField(segment)) { - fieldInfo = currentType.getFieldInfo(segment); + // If accessing from a class name (isStaticAccessSeg) and field is not static, that's an error + if (isStaticAccessSeg && fieldInfo != null && !fieldInfo.isStatic()) { + // Mark as error - trying to access non-static field from static context + TokenErrorMessage errorMsg = TokenErrorMessage + .from("Cannot access non-static field '" + segment + "' from static context '" + currentType.getSimpleName() + "'") + .clearOtherErrors(); + marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR, errorMsg)); + currentType = null; // Can't continue resolving } else { - // Field exists but not accessible in static context - isStaticAccessError = true; - fieldInfo = instanceContext.getFieldInfo(segment); + // Valid access + FieldAccessInfo accessInfo = createFieldAccessInfo(segment, segPos[0], segPos[1], + currentType, fieldInfo, isLastSegment, isStaticAccessSeg); + marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, accessInfo)); + + // Update currentType for next segment + currentType = (fieldInfo != null && fieldInfo.getTypeInfo() != null) ? fieldInfo.getTypeInfo() : null; } - } - - if (fieldInfo != null && !isStaticAccessError) { - // Check if this is the last segment and if it's in an assignment context - // Create and mark FieldAccessInfo for this segment (last or intermediate) - FieldAccessInfo accessInfo = createFieldAccessInfo(segment, segPos[0], segPos[1], currentType, fieldInfo, isLastSegment, isStaticAccessSeg); - marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.GLOBAL_FIELD, accessInfo)); - - // Update currentType for next segment - field values are always instances - currentType = (fieldInfo != null) ? fieldInfo.getTypeInfo().asInstanceContext() : null; - - /*TODO THIS ELSE OVERRIDES FOR CHAINED METHOD CALLS LIKE "Minecraft.getMinecraft()" - Do we resolve this by increasing METHOD_CALL PRIORITY or how exactly? - Think of the best fitting solution that doesnt regress other parts of the whole framework. - */ - } else if (isStaticAccessError && fieldInfo != null) { - // Mark as error - trying to access non-static field from static context - TokenErrorMessage errorMsg = TokenErrorMessage.from( - "Cannot access non-static field '" + segment + "' from static context '" + currentType.getSimpleName() + "'").clearOtherErrors(); - marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR, errorMsg)); - currentType = null; // Can't continue resolving } else { marks.add(new ScriptLine.Mark(segPos[0], segPos[1], TokenType.UNDEFINED_VAR)); currentType = null; // Can't continue resolving diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/ScriptTypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/ScriptTypeInfo.java index 094257938..9f1f04a53 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/ScriptTypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/ScriptTypeInfo.java @@ -34,8 +34,8 @@ public class ScriptTypeInfo extends TypeInfo { private ScriptTypeInfo outerClass; private ScriptTypeInfo(String simpleName, String fullName, Kind kind, - int declarationOffset, int bodyStart, int bodyEnd, int modifiers, boolean staticContext) { - super(simpleName, fullName, "", kind, null, true, null, staticContext, true); + int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { + super(simpleName, fullName, "", kind, null, true, null, true); this.scriptClassName = simpleName; this.declarationOffset = declarationOffset; this.bodyStart = bodyStart; @@ -46,13 +46,13 @@ public class ScriptTypeInfo extends TypeInfo { // Factory method public static ScriptTypeInfo create(String simpleName, Kind kind, int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { - return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd, modifiers, false); + return new ScriptTypeInfo(simpleName, simpleName, kind, declarationOffset, bodyStart, bodyEnd, modifiers); } public static ScriptTypeInfo createInner(String simpleName, Kind kind, ScriptTypeInfo outer, int declarationOffset, int bodyStart, int bodyEnd, int modifiers) { String fullName = outer.getFullName() + "$" + simpleName; - ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd, modifiers, false); + ScriptTypeInfo inner = new ScriptTypeInfo(simpleName, fullName, kind, declarationOffset, bodyStart, bodyEnd, modifiers); inner.outerClass = outer; outer.innerClasses.add(inner); return inner; @@ -82,26 +82,12 @@ public class ScriptTypeInfo extends TypeInfo { @Override public boolean hasField(String fieldName) { - FieldInfo field = fields.get(fieldName); - if (field == null) return false; - - // In static context, only static fields are accessible - if (isStaticContext() && !field.isStatic()) { - return false; - } - return true; + return fields.containsKey(fieldName); } @Override public FieldInfo getFieldInfo(String fieldName) { - FieldInfo field = fields.get(fieldName); - if (field == null) return null; - - // In static context, only static fields are accessible - if (isStaticContext() && !field.isStatic()) { - return null; // Non-static field not accessible from static context - } - return field; + return fields.get(fieldName); } public Map getFields() { diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java index b9dfa1c2c..9fdf8dac4 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeInfo.java @@ -27,10 +27,9 @@ public class TypeInfo { private final Class javaClass; // The actual resolved Java class (null if unresolved) private final boolean resolved; // Whether this type was successfully resolved private final TypeInfo enclosingType; // For inner classes, the outer type (null if top-level) - private final boolean staticContext; // true if this is a class reference (can only access static members), false if instance private TypeInfo(String simpleName, String fullName, String packageName, - Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType, boolean staticContext) { + Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType) { this.simpleName = simpleName; this.fullName = fullName; this.packageName = packageName; @@ -38,13 +37,12 @@ public class TypeInfo { this.javaClass = javaClass; this.resolved = resolved; this.enclosingType = enclosingType; - this.staticContext = staticContext; } // Protected constructor for subclasses (like ScriptTypeInfo) protected TypeInfo(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType, - boolean staticContext, @SuppressWarnings("unused") boolean subclass) { + @SuppressWarnings("unused") boolean subclass) { this.simpleName = simpleName; this.fullName = fullName; this.packageName = packageName; @@ -52,24 +50,23 @@ public class TypeInfo { this.javaClass = javaClass; this.resolved = resolved; this.enclosingType = enclosingType; - this.staticContext = staticContext; } // Factory methods public static TypeInfo resolved(String simpleName, String fullName, String packageName, Kind kind, Class javaClass) { - return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, null, false); + return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, null); } public static TypeInfo resolvedInner(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, TypeInfo enclosing) { - return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, enclosing, false); + return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, true, enclosing); } public static TypeInfo unresolved(String simpleName, String fullPath) { int lastDot = fullPath.lastIndexOf('.'); String pkg = lastDot > 0 ? fullPath.substring(0, lastDot) : ""; - return new TypeInfo(simpleName, fullPath, pkg, Kind.UNKNOWN, null, false, null, false); + return new TypeInfo(simpleName, fullPath, pkg, Kind.UNKNOWN, null, false, null); } public static TypeInfo fromClass(Class clazz) { @@ -102,7 +99,7 @@ public class TypeInfo { enclosing = fromClass(clazz.getEnclosingClass()); } - return new TypeInfo(simpleName, fullName, packageName, kind, clazz, true, enclosing, false); + return new TypeInfo(simpleName, fullName, packageName, kind, clazz, true, enclosing); } /** @@ -121,7 +118,7 @@ public class TypeInfo { case "double": primitiveClass = double.class; break; case "void": primitiveClass = void.class; break; } - return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null, false); + return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null); } // Getters @@ -133,25 +130,6 @@ public class TypeInfo { public boolean isResolved() { return resolved; } public TypeInfo getEnclosingType() { return enclosingType; } public boolean isInnerClass() { return enclosingType != null; } - public boolean isStaticContext() { return staticContext; } - - /** - * Create a new TypeInfo with static context (for class references). - * Used when referencing a class name directly, which can only access static members. - */ - public TypeInfo asStaticContext() { - if (staticContext) return this; - return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, true); - } - - /** - * Create a new TypeInfo with instance context (for object instances). - * Used when referencing an instance of a class, which can access both static and instance members. - */ - public TypeInfo asInstanceContext() { - if (!staticContext) return this; - return new TypeInfo(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, false); - } /** * Get the appropriate TokenType for highlighting this type. @@ -259,19 +237,13 @@ public class TypeInfo { } /** - * Check if this type has a field with the given name that is accessible in the current context. - * In static context (class reference), only static fields are accessible. - * In instance context, both static and instance fields are accessible. + * Check if this type has a field with the given name. */ public boolean hasField(String fieldName) { if (javaClass == null) return false; try { for (java.lang.reflect.Field f : javaClass.getFields()) { if (f.getName().equals(fieldName)) { - // In static context, only static fields are accessible - if (staticContext && !java.lang.reflect.Modifier.isStatic(f.getModifiers())) { - return false; - } return true; } } @@ -469,9 +441,7 @@ public class TypeInfo { } /** - * Get FieldInfo for a field by name. Returns null if not found or not accessible. - * In static context (class reference), only static fields are accessible. - * In instance context, both static and instance fields are accessible. + * Get FieldInfo for a field by name. Returns null if not found. * Creates a synthetic FieldInfo based on reflection data. */ public FieldInfo getFieldInfo(String fieldName) { @@ -479,15 +449,8 @@ public class TypeInfo { try { for (java.lang.reflect.Field f : javaClass.getFields()) { if (f.getName().equals(fieldName)) { - // In static context, only static fields are accessible - if (staticContext && !java.lang.reflect.Modifier.isStatic(f.getModifiers())) { - return null; // Non-static field not accessible from static context - } // Create a synthetic FieldInfo from reflection - // Always return instance context for the field's type (field values are instances) - FieldInfo fieldInfo = FieldInfo.fromReflection(f, this); - // The field's type should be in instance context since we're accessing a value - return fieldInfo; + return FieldInfo.fromReflection(f, this); } } } catch (Exception e) {