mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
Moved all inheritance code to ScriptTypeInfo
This commit is contained in:
1 parent
35af0bbd2d
commit
861cdcd533
4 files changed
+222
-163
No files matched your search
@@ -4217,7 +4217,7 @@ public class ScriptDocument {
|
||||
for (ScriptTypeInfo scriptType : scriptTypes.values()) {
|
||||
scriptType.clearErrors(); // Clear previous errors
|
||||
detectMethodInheritanceForType(scriptType);
|
||||
validateScriptType(scriptType); // Validate after detecting inheritance
|
||||
scriptType.validate(); // Validate after detecting inheritance
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4255,126 +4255,6 @@ public class ScriptDocument {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a script type for missing interface methods and constructor matching.
|
||||
*/
|
||||
private void validateScriptType(ScriptTypeInfo scriptType) {
|
||||
// Skip validation for interfaces - they don't implement methods
|
||||
if (scriptType.getKind() == TypeInfo.Kind.INTERFACE)
|
||||
return;
|
||||
|
||||
// Check that all interface methods are implemented
|
||||
if (scriptType.hasImplementedInterfaces()) {
|
||||
for (TypeInfo iface : scriptType.getImplementedInterfaces()) {
|
||||
if (iface == null || !iface.isResolved()) {
|
||||
// Mark unresolved interface error
|
||||
scriptType.setError(ScriptTypeInfo.ErrorType.UNRESOLVED_INTERFACE,
|
||||
"Cannot resolve interface");
|
||||
continue;
|
||||
}
|
||||
validateInterfaceImplementation(scriptType, iface);
|
||||
}
|
||||
}
|
||||
|
||||
// Check that extending class has a matching constructor
|
||||
if (scriptType.hasSuperClass()) {
|
||||
TypeInfo superClass = scriptType.getSuperClass();
|
||||
if (superClass == null || !superClass.isResolved()) {
|
||||
scriptType.setError(ScriptTypeInfo.ErrorType.UNRESOLVED_PARENT,
|
||||
"Cannot resolve parent class " + scriptType.getSuperClassName());
|
||||
} else {
|
||||
validateConstructorChain(scriptType, superClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a script type implements all methods from an interface.
|
||||
*/
|
||||
private void validateInterfaceImplementation(ScriptTypeInfo scriptType, TypeInfo iface) {
|
||||
// Get all methods from the interface
|
||||
Class<?> javaClass = iface.getJavaClass();
|
||||
if (javaClass == null || !javaClass.isInterface())
|
||||
return;
|
||||
|
||||
try {
|
||||
for (java.lang.reflect.Method javaMethod : javaClass.getMethods()) {
|
||||
// Skip static and default methods
|
||||
if (java.lang.reflect.Modifier.isStatic(javaMethod.getModifiers()))
|
||||
continue;
|
||||
|
||||
// Check if scriptType has a matching method
|
||||
String methodName = javaMethod.getName();
|
||||
int paramCount = javaMethod.getParameterCount();
|
||||
|
||||
boolean found = false;
|
||||
List<MethodInfo> overloads = scriptType.getAllMethodOverloads(methodName);
|
||||
for (MethodInfo method : overloads) {
|
||||
if (parameterTypesMatch(method, javaMethod)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
// Build signature string for error message
|
||||
StringBuilder sig = new StringBuilder("(");
|
||||
Class<?>[] paramTypes = javaMethod.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (i > 0)
|
||||
sig.append(", ");
|
||||
sig.append(paramTypes[i].getSimpleName());
|
||||
}
|
||||
sig.append(")");
|
||||
|
||||
scriptType.addMissingMethodError(iface, methodName, sig.toString());
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that a script type has a constructor compatible with its parent class.
|
||||
* This checks that for each parent constructor, there's a matching constructor in the child.
|
||||
*/
|
||||
private void validateConstructorChain(ScriptTypeInfo scriptType, TypeInfo superClass) {
|
||||
// If no constructors defined in script type, it has an implicit default constructor
|
||||
// Check if parent has a no-arg constructor
|
||||
if (!scriptType.hasConstructors()) {
|
||||
boolean parentHasNoArg = false;
|
||||
|
||||
if (superClass instanceof ScriptTypeInfo) {
|
||||
ScriptTypeInfo parentScript = (ScriptTypeInfo) superClass;
|
||||
if (!parentScript.hasConstructors() || parentScript.findConstructor(0) != null) {
|
||||
parentHasNoArg = true;
|
||||
}
|
||||
} else {
|
||||
Class<?> javaClass = superClass.getJavaClass();
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
for (java.lang.reflect.Constructor<?> ctor : javaClass.getConstructors()) {
|
||||
if (ctor.getParameterCount() == 0) {
|
||||
parentHasNoArg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentHasNoArg) {
|
||||
scriptType.addConstructorMismatchError(superClass,
|
||||
superClass.getSimpleName() + "()");
|
||||
}
|
||||
}
|
||||
// If script type has constructors, we'd need to check super() calls - that's more complex
|
||||
// For now, we just validate the implicit default constructor case
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for a matching method in a type hierarchy (class inheritance).
|
||||
* @param type The type to search in (and its superclasses)
|
||||
|
||||
+14
@@ -2,6 +2,7 @@ package noppes.npcs.client.gui.util.script.interpreter.method;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -78,4 +79,17 @@ public final class MethodSignature {
|
||||
sb.append(")");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static String asString(Method javaMethod) {
|
||||
StringBuilder sig = new StringBuilder(javaMethod.getName());
|
||||
sig.append("(");
|
||||
Class<?>[] paramTypes = javaMethod.getParameterTypes();
|
||||
for (int i = 0; i < paramTypes.length; i++) {
|
||||
if (i > 0)
|
||||
sig.append(", ");
|
||||
sig.append(paramTypes[i].getSimpleName());
|
||||
}
|
||||
sig.append(")");
|
||||
return sig.toString();
|
||||
}
|
||||
}
|
||||
+199
-42
@@ -2,8 +2,11 @@ package noppes.npcs.client.gui.util.script.interpreter.type;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodSignature;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -347,45 +350,6 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
/**
|
||||
* Represents a missing interface method error.
|
||||
*/
|
||||
public static class MissingMethodError {
|
||||
private final TypeInfo interfaceType;
|
||||
private final String methodName;
|
||||
private final String signature;
|
||||
|
||||
public MissingMethodError(TypeInfo interfaceType, String methodName, String signature) {
|
||||
this.interfaceType = interfaceType;
|
||||
this.methodName = methodName;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public TypeInfo getInterfaceType() { return interfaceType; }
|
||||
public String getMethodName() { return methodName; }
|
||||
public String getSignature() { return signature; }
|
||||
|
||||
public String getMessage() {
|
||||
return "Class must implement method '" + methodName + signature + "' from interface " + interfaceType.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a constructor mismatch error.
|
||||
*/
|
||||
public static class ConstructorMismatchError {
|
||||
private final TypeInfo parentType;
|
||||
private final String parentConstructorSignature;
|
||||
|
||||
public ConstructorMismatchError(TypeInfo parentType, String parentConstructorSignature) {
|
||||
this.parentType = parentType;
|
||||
this.parentConstructorSignature = parentConstructorSignature;
|
||||
}
|
||||
|
||||
public TypeInfo getParentType() { return parentType; }
|
||||
public String getParentConstructorSignature() { return parentConstructorSignature; }
|
||||
|
||||
public String getMessage() {
|
||||
return "Class extends " + parentType.getSimpleName() + " but has no constructor matching " + parentConstructorSignature;
|
||||
}
|
||||
}
|
||||
|
||||
// Error tracking
|
||||
private ErrorType errorType = ErrorType.NONE;
|
||||
@@ -411,8 +375,8 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
/**
|
||||
* Add a missing interface method error.
|
||||
*/
|
||||
public void addMissingMethodError(TypeInfo interfaceType, String methodName, String signature) {
|
||||
missingMethodErrors.add(new MissingMethodError(interfaceType, methodName, signature));
|
||||
public void addMissingMethodError(TypeInfo interfaceType, String signature) {
|
||||
missingMethodErrors.add(new MissingMethodError(interfaceType, signature));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -431,4 +395,197 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
missingMethodErrors.clear();
|
||||
constructorMismatchErrors.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MissingMethodError {
|
||||
private final TypeInfo interfaceType;
|
||||
private final String signature;
|
||||
|
||||
public MissingMethodError(TypeInfo interfaceType, String signature) {
|
||||
this.interfaceType = interfaceType;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Class must implement method '" + signature + "' from interface " + interfaceType.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a constructor mismatch error.
|
||||
*/
|
||||
public static class ConstructorMismatchError {
|
||||
private final TypeInfo parentType;
|
||||
private final String parentConstructorSignature;
|
||||
|
||||
public ConstructorMismatchError(TypeInfo parentType, String parentConstructorSignature) {
|
||||
this.parentType = parentType;
|
||||
this.parentConstructorSignature = parentConstructorSignature;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Class extends " + parentType.getSimpleName() + " but has no constructor matching " + parentConstructorSignature;
|
||||
}
|
||||
}
|
||||
|
||||
// ==================== VALIDATION ====================
|
||||
|
||||
/**
|
||||
* Validate this script type for missing interface methods and constructor matching.
|
||||
*/
|
||||
@Override
|
||||
public void validate() {
|
||||
// Check that extending class has a matching constructor
|
||||
if (hasSuperClass()) {
|
||||
TypeInfo superClass = getSuperClass();
|
||||
if (superClass == null || !superClass.isResolved()) {
|
||||
setError(ErrorType.UNRESOLVED_PARENT,
|
||||
"Cannot resolve parent class " + getSuperClassName());
|
||||
} else {
|
||||
validateConstructorChain(superClass);
|
||||
}
|
||||
}
|
||||
|
||||
// Skip validation for interfaces - they don't implement methods
|
||||
if (getKind() == Kind.INTERFACE)
|
||||
return;
|
||||
|
||||
// Check that all interface methods are implemented
|
||||
if (hasImplementedInterfaces()) {
|
||||
for (TypeInfo iface : getImplementedInterfaces()) {
|
||||
if (iface == null || !iface.isResolved()) {
|
||||
// Mark unresolved interface error
|
||||
setError(ErrorType.UNRESOLVED_INTERFACE, "Cannot resolve interface");
|
||||
continue;
|
||||
}
|
||||
validateInterfaceImplementation(iface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that this script type implements all methods from an interface.
|
||||
*/
|
||||
private void validateInterfaceImplementation(TypeInfo iface) {
|
||||
// Handle Java interfaces
|
||||
Class<?> javaClass = iface.getJavaClass();
|
||||
if (javaClass != null && javaClass.isInterface()) {
|
||||
try {
|
||||
for (Method javaMethod : javaClass.getMethods()) {
|
||||
// Skip static and default methods
|
||||
if (Modifier.isStatic(javaMethod.getModifiers()))
|
||||
continue;
|
||||
|
||||
// Check if this type has a matching method
|
||||
String methodName = javaMethod.getName();
|
||||
int paramCount = javaMethod.getParameterCount();
|
||||
|
||||
boolean found = false;
|
||||
List<MethodInfo> overloads = getAllMethodOverloads(methodName);
|
||||
for (MethodInfo method : overloads) {
|
||||
if (parameterTypesMatch(method, javaMethod)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Limit to one error at a time to not spam all missing methods
|
||||
if (!found && missingMethodErrors.isEmpty())
|
||||
addMissingMethodError(iface, MethodSignature.asString(javaMethod));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle script-defined interfaces (ScriptType)
|
||||
if (iface instanceof ScriptTypeInfo) {
|
||||
ScriptTypeInfo ifaceType = (ScriptTypeInfo) iface;
|
||||
|
||||
// Check all methods declared in the interface
|
||||
for (MethodInfo ifaceMethod : ifaceType.getAllMethodsFlat()) {
|
||||
MethodSignature ifaceSignature = ifaceMethod.getSignature();
|
||||
String methodName = ifaceMethod.getName();
|
||||
|
||||
boolean found = false;
|
||||
List<MethodInfo> overloads = getAllMethodOverloads(methodName);
|
||||
for (MethodInfo method : overloads) {
|
||||
if (method.getSignature().equals(ifaceSignature)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Limit to one error at a time to not spam all missing methods
|
||||
if (!found && missingMethodErrors.isEmpty())
|
||||
addMissingMethodError(iface, ifaceSignature.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that this script type has a constructor compatible with its parent class.
|
||||
* This checks that for each parent constructor, there's a matching constructor in the child.
|
||||
*/
|
||||
private void validateConstructorChain(TypeInfo superClass) {
|
||||
// If no constructors defined in script type, it has an implicit default constructor
|
||||
// Check if parent has a no-arg constructor
|
||||
if (!hasConstructors()) {
|
||||
boolean parentHasNoArg = false;
|
||||
|
||||
if (superClass instanceof ScriptTypeInfo) {
|
||||
ScriptTypeInfo parentScript = (ScriptTypeInfo) superClass;
|
||||
if (!parentScript.hasConstructors() || parentScript.findConstructor(0) != null) {
|
||||
parentHasNoArg = true;
|
||||
}
|
||||
} else {
|
||||
Class<?> javaClass = superClass.getJavaClass();
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
for (java.lang.reflect.Constructor<?> ctor : javaClass.getConstructors()) {
|
||||
if (ctor.getParameterCount() == 0) {
|
||||
parentHasNoArg = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!parentHasNoArg) {
|
||||
addConstructorMismatchError(superClass, superClass.getSimpleName());
|
||||
}
|
||||
}
|
||||
// If script type has constructors, we'd need to check super() calls - that's more complex
|
||||
// For now, we just validate the implicit default constructor case
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a MethodInfo's parameter types match a Java reflection Method's parameter types.
|
||||
*/
|
||||
private boolean parameterTypesMatch(MethodInfo methodInfo, Method javaMethod) {
|
||||
List<FieldInfo> params = methodInfo.getParameters();
|
||||
Class<?>[] javaParams = javaMethod.getParameterTypes();
|
||||
|
||||
if (params.size() != javaParams.length)
|
||||
return false;
|
||||
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
TypeInfo paramType = params.get(i).getDeclaredType();
|
||||
if (paramType == null)
|
||||
continue; // Unresolved param, skip check
|
||||
|
||||
Class<?> javaParamClass = javaParams[i];
|
||||
String javaParamName = javaParamClass.getName();
|
||||
|
||||
// Compare type names
|
||||
if (!paramType.getFullName().equals(javaParamName) &&
|
||||
!paramType.getSimpleName().equals(javaParamClass.getSimpleName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -461,6 +461,14 @@ public class TypeInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate this type. Default implementation does nothing (for Java types).
|
||||
* Override in ScriptTypeInfo to validate script-defined types.
|
||||
*/
|
||||
public void validate() {
|
||||
// Default: no validation for Java types
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TypeInfo{" + fullName + ", " + kind + ", resolved=" + resolved + "}";
|
||||
|
||||
Reference in new issue
Block a user