diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java index 5863d0412..c7c06e715 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeInfo.java @@ -1,5 +1,7 @@ package noppes.npcs.client.gui.util.script.interpreter.js_parser; +import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; + import java.util.*; /** @@ -8,34 +10,6 @@ import java.util.*; */ public class JSTypeInfo { - /** - * Represents a generic type parameter like "T extends Entity". - * Stores the parameter name, the bound type, and optionally the full Java class name. - */ - public static class TypeParamInfo { - private final String name; // e.g., "T" - private final String boundType; // e.g., "EntityPlayerMP" - private final String fullBoundType; // e.g., "net.minecraft.entity.player.EntityPlayerMP" - - public TypeParamInfo(String name, String boundType, String fullBoundType) { - this.name = name; - this.boundType = boundType; - this.fullBoundType = fullBoundType; - } - - public String getName() { return name; } - public String getBoundType() { return boundType; } - public String getFullBoundType() { return fullBoundType; } - - @Override - public String toString() { - if (boundType != null) { - return name + " extends " + boundType + (fullBoundType != null ? " (" + fullBoundType + ")" : ""); - } - return name; - } - } - private final String simpleName; // e.g., "InteractEvent" private final String fullName; // e.g., "IPlayerEvent.InteractEvent" private final String namespace; // e.g., "IPlayerEvent" (parent namespace, null for top-level) @@ -102,11 +76,7 @@ public class JSTypeInfo { public void setResolvedParent(JSTypeInfo parent) { this.resolvedParent = parent; } - - public void addTypeParam(TypeParamInfo param) { - typeParams.add(param); - } - + // Getters public String getSimpleName() { return simpleName; } public String getFullName() { return fullName; } @@ -133,16 +103,43 @@ public class JSTypeInfo { } return null; } + + public void addTypeParam(TypeParamInfo param) { + typeParams.add(param); + } + + /** + * Resolve all type parameters for this type. + * Called during Phase 2 after all types are loaded into the registry. + */ + public void resolveTypeParameters() { + for (TypeParamInfo param : typeParams) { + param.resolveBoundType(); + } + } /** - * Resolves a type parameter to its bound type. - * For example, if this type has "T extends EntityPlayerMP", resolveTypeParam("T") returns "EntityPlayerMP". - * If no type parameter is found with that name, returns the input. + * Resolves a type parameter to its bound TypeInfo. + * For example, if this type has "T extends EntityPlayerMP", resolveTypeParam("T") returns the TypeInfo for EntityPlayerMP. + * If no type parameter is found with that name, returns null. */ + public TypeInfo resolveTypeParamToTypeInfo(String typeName) { + TypeParamInfo param = getTypeParam(typeName); + if (param != null) { + return param.getBoundTypeInfo(); + } + return null; + } + + /** + * Resolves a type parameter to its bound type name (for backward compatibility). + * @deprecated Use resolveTypeParamToTypeInfo instead + */ + @Deprecated public String resolveTypeParam(String typeName) { TypeParamInfo param = getTypeParam(typeName); - if (param != null && param.getBoundType() != null) { - return param.getBoundType(); + if (param != null && param.getBoundTypeInfo() != null) { + return param.getBoundTypeInfo().getSimpleName(); } return typeName; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java index 8dfec22cf..a4565b5eb 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/JSTypeRegistry.java @@ -83,6 +83,9 @@ public class JSTypeRegistry { } } + // Phase 2: Resolve all type parameters now that all types are loaded + resolveAllTypeParameters(); + resolveInheritance(); registerEngineGlobalObjects(); @@ -223,6 +226,7 @@ public class JSTypeRegistry { try { TypeScriptDefinitionParser parser = new TypeScriptDefinitionParser(this); parser.parseDirectory(directory); + resolveAllTypeParameters(); resolveInheritance(); initialized = true; System.out.println("[JSTypeRegistry] Loaded " + types.size() + " types, " + hooks.size() + " hooks"); @@ -241,6 +245,7 @@ public class JSTypeRegistry { try { TypeScriptDefinitionParser parser = new TypeScriptDefinitionParser(this); parser.parseVsixArchive(vsixFile); + resolveAllTypeParameters(); resolveInheritance(); initialized = true; System.out.println("[JSTypeRegistry] Loaded " + types.size() + " types, " + hooks.size() + " hooks from VSIX"); @@ -360,6 +365,17 @@ public class JSTypeRegistry { return null; } + /** + * Resolve all type parameters for all types. + * Called after all .d.ts files are loaded (Phase 2). + * This ensures that type parameters can reference any type in the registry. + */ + public void resolveAllTypeParameters() { + for (JSTypeInfo type : types.values()) { + type.resolveTypeParameters(); + } + } + /** * Resolve inheritance relationships between types. * For each type, walks up the parent chain and resolves all ancestors. diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeParamInfo.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeParamInfo.java new file mode 100644 index 000000000..d41005a6e --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeParamInfo.java @@ -0,0 +1,68 @@ +package noppes.npcs.client.gui.util.script.interpreter.js_parser; + +import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; +import noppes.npcs.client.gui.util.script.interpreter.type.TypeResolver; + +/** + * Represents a generic type parameter like "T extends Entity". + * Stores the parameter name and both the string representation (for deferred resolution) + * and the resolved TypeInfo for the bound. + */ +public class TypeParamInfo { + private final String name; // e.g., "T" + private final String boundType; // Simple name like "EntityPlayerMP" (may be null) + private final String fullBoundType; // Full name like "net.minecraft.entity.player.EntityPlayerMP" (may be null) + private TypeInfo boundTypeInfo; // Resolved TypeInfo for the bound (resolved in Phase 2) + + /** + * Constructor for parse-time (Phase 1) - stores string names only. + */ + public TypeParamInfo(String name, String boundType, String fullBoundType) { + this.name = name; + this.boundType = boundType; + this.fullBoundType = fullBoundType; + this.boundTypeInfo = null; // Will be resolved later + } + + public String getName() { return name; } + + public TypeInfo getBoundTypeInfo() { + return boundTypeInfo; + } + + /** + * Resolve the bound type using TypeResolver. + * Called during Phase 2 after all types are loaded. + */ + public void resolveBoundType() { + if (boundTypeInfo != null) + return; // Already resolved + + if (fullBoundType != null && !fullBoundType.isEmpty()) { + // Try to load the Java class using the full name + boundTypeInfo = TypeResolver.getInstance().resolveFullName(fullBoundType); + } else if (boundType != null && !boundType.isEmpty()) { + // Try to resolve using the simple name + boundTypeInfo = TypeResolver.getInstance().resolveJSType(boundType); + } + } + + /** + * Get simple display name for the bound type. + */ + public String getBoundTypeName() { + if (boundTypeInfo == null) + return null; + return boundTypeInfo.getSimpleName(); + } + + @Override + public String toString() { + if (boundTypeInfo != null) { + return name + " extends " + boundTypeInfo.getSimpleName(); + } else if (boundType != null || fullBoundType != null) { + return name + " extends " + (boundType != null ? boundType : fullBoundType); + } + return name; + } +} diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java index 5403f2ce8..c507cf8f2 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/js_parser/TypeScriptDefinitionParser.java @@ -311,6 +311,7 @@ public class TypeScriptDefinitionParser { /** * Parse type parameters from a string like "T extends EntityPlayerMP /* net.minecraft.entity.player.EntityPlayerMP *`/". * Handles multiple parameters separated by commas. + * Stores strings only - resolution happens in Phase 2 after all types are loaded. */ private void parseTypeParameters(String typeParamsStr, JSTypeInfo typeInfo) { // Split by comma, but be careful with nested generics (shouldn't happen at this level, but be safe) @@ -322,10 +323,11 @@ public class TypeScriptDefinitionParser { Matcher m = TYPE_PARAM_PATTERN.matcher(param); if (m.find()) { String name = m.group(1); - String boundType = m.group(2); - String fullBoundType = m.group(3); + String boundType = m.group(2); // Simple name like "EntityPlayerMP" + String fullBoundType = m.group(3); // Full name like "net.minecraft.entity.player.EntityPlayerMP" - typeInfo.addTypeParam(new JSTypeInfo.TypeParamInfo(name, boundType, fullBoundType)); + // Store strings only - resolution happens in Phase 2 + typeInfo.addTypeParam(new TypeParamInfo(name, boundType, fullBoundType)); } } } 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 cc192e38c..d00a84c32 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 @@ -5,6 +5,7 @@ import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo; import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSFieldInfo; import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSMethodInfo; import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo; +import noppes.npcs.client.gui.util.script.interpreter.js_parser.TypeParamInfo; import noppes.npcs.client.gui.util.script.interpreter.method.MethodInfo; import noppes.npcs.client.gui.util.script.interpreter.token.TokenType; @@ -60,6 +61,9 @@ public class TypeInfo { // JavaScript/TypeScript type info (for types from .d.ts files) private final JSTypeInfo jsTypeInfo; // The JS type info (null if Java type) + + // Type parameters (generics) + private final List typeParams = new ArrayList<>(); private TypeInfo(String simpleName, String fullName, String packageName, Kind kind, Class javaClass, boolean resolved, TypeInfo enclosingType) { @@ -477,7 +481,8 @@ public class TypeInfo { // Check JS type first if (jsTypeInfo != null) { for (JSMethodInfo jsMethod : jsTypeInfo.getMethodOverloads(methodName)) { - overloads.add(MethodInfo.fromJSMethod(jsMethod, this)); + // Pass jsTypeInfo as context for type parameter resolution + overloads.add(MethodInfo.fromJSMethod(jsMethod, this, jsTypeInfo)); } return overloads; } @@ -679,6 +684,79 @@ public class TypeInfo { public void validate() { // Default: no validation for Java types } + + // ==================== Type Parameter Methods ==================== + + /** + * Add a type parameter to this type. + * Used during parsing/construction of types with generics. + */ + public void addTypeParam(TypeParamInfo param) { + // If this is a JS type, delegate to JSTypeInfo + if (jsTypeInfo != null) { + jsTypeInfo.addTypeParam(param); + } else { + typeParams.add(param); + } + } + + /** + * Get all type parameters for this type. + * @return List of type parameters (empty if none) + */ + public List getTypeParams() { + // If this is a JS type, delegate to JSTypeInfo + if (jsTypeInfo != null) { + return jsTypeInfo.getTypeParams(); + } + return typeParams; + } + + /** + * Resolve all type parameters for this type. + * Called during Phase 2 after all types are loaded into the registry. + */ + public void resolveTypeParameters() { + // If this is a JS type, delegate to JSTypeInfo + if (jsTypeInfo != null) { + jsTypeInfo.resolveTypeParameters(); + } else { + for (TypeParamInfo param : typeParams) { + param.resolveBoundType(); + } + } + } + + /** + * Get the type parameter info for a given parameter name (e.g., "T"). + * @return TypeParamInfo or null if not found + */ + public TypeParamInfo getTypeParam(String name) { + // If this is a JS type, delegate to JSTypeInfo + if (jsTypeInfo != null) { + return jsTypeInfo.getTypeParam(name); + } + + for (TypeParamInfo param : typeParams) { + if (param.getName().equals(name)) { + return param; + } + } + return null; + } + + /** + * Resolves a type parameter to its bound TypeInfo. + * For example, if this type has "T extends EntityPlayerMP", resolveTypeParamToTypeInfo("T") returns the TypeInfo for EntityPlayerMP. + * If no type parameter is found with that name, returns null. + */ + public TypeInfo resolveTypeParamToTypeInfo(String typeName) { + TypeParamInfo param = getTypeParam(typeName); + if (param != null) { + return param.getBoundTypeInfo(); + } + return null; + } @Override public String toString() {