From 98885faaa21cfb83deea1495fd5e16048859e4f5 Mon Sep 17 00:00:00 2001 From: bigguy345 Date: Fri, 6 Mar 2026 20:25:12 +0200 Subject: [PATCH] (WIP) Properly handled merging Object.d.ts members into current java.lang.Object handling in type resolution & autocomplete --- .../script/autocomplete/AutocompleteItem.java | 52 +++++------ .../autocomplete/JSAutocompleteProvider.java | 65 +++++++++++-- .../script/interpreter/type/TypeInfo.java | 93 +++++++++---------- .../script/interpreter/type/TypeResolver.java | 9 ++ 4 files changed, 132 insertions(+), 87 deletions(-) diff --git a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteItem.java b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteItem.java index 965faaaf1..d64a60300 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteItem.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/AutocompleteItem.java @@ -3,6 +3,7 @@ package noppes.npcs.client.gui.util.script.autocomplete; 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.method.MethodInfo; import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; import noppes.npcs.client.gui.util.script.interpreter.type.synthetic.SyntheticField; @@ -655,41 +656,32 @@ public class AutocompleteItem implements Comparable { * These methods should be deprioritized in autocomplete. */ public boolean isInheritedObjectMethod() { - if (kind != Kind.METHOD || !(sourceData instanceof MethodInfo)) { - return false; - } - - MethodInfo methodInfo = (MethodInfo) sourceData; - - // For Java reflection methods, check the declaring class - if (methodInfo.getJavaMethod() != null) { + if (kind != Kind.METHOD) return false; + + if (sourceData instanceof MethodInfo) { + MethodInfo methodInfo = (MethodInfo) sourceData; java.lang.reflect.Method javaMethod = methodInfo.getJavaMethod(); - Class declaringClass = javaMethod.getDeclaringClass(); - - // If the method is declared in Object, it's an inherited Object method - // unless the containing type is Object itself - if (declaringClass.getName().equals("java.lang.Object")) { + if (javaMethod != null) { + if (!Object.class.equals(javaMethod.getDeclaringClass())) return false; TypeInfo containingType = methodInfo.getContainingType(); - // If we're showing methods for Object itself, don't treat them as "inherited" - return containingType != null && !containingType.getFullName().equals("java.lang.Object"); + return containingType == null || !containingType.getFullName().equals("java.lang.Object"); } - } else { - // For script-defined methods, use the original logic TypeInfo containingType = methodInfo.getContainingType(); - - // Check if declaring class is Object - if (containingType != null && containingType.getFullName().equals("java.lang.Object")) { - return true; - } - - // Check if method overrides from Object (meaning it's overridden, so NOT inherited) - if (methodInfo.isOverride()) { - TypeInfo overridesFrom = methodInfo.getOverridesFrom(); - // If it overrides from Object, it means this class overrode it, so it's NOT just inherited - return false; - } + return containingType != null + && containingType.getFullName().equals("java.lang.Object") + && !methodInfo.isStatic() + && !methodInfo.isOverride(); } - + + if (sourceData instanceof JSMethodInfo) { + JSMethodInfo jsMethod = (JSMethodInfo) sourceData; + JSTypeInfo definingType = jsMethod.getContainingType(); + return definingType != null + && "Object".equals(definingType.getSimpleName()) + && !jsMethod.isStatic() + && inheritanceDepth > 0; + } + return false; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java index d45f08549..dc26cd555 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/autocomplete/JSAutocompleteProvider.java @@ -8,6 +8,9 @@ import noppes.npcs.client.gui.util.script.interpreter.type.TypeChecker; import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo; import noppes.npcs.config.ConfigScript; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.*; /** @@ -55,17 +58,51 @@ public class JSAutocompleteProvider extends JavaAutocompleteProvider { return; } - // If this is a Java type (has a Java class), delegate to parent's Java handling - // This preserves static/instance context, modifiers, and all Java-specific behavior + boolean isStaticContext = isStaticAccess(receiverExpr, getMemberAccessResolvePosition(context)); + Class javaClass = receiverType.getJavaClass(); if (javaClass != null) { - super.addMemberSuggestions(context, items); + Set addedMethods = new HashSet<>(); + for (Method method : javaClass.getMethods()) { + if (!Modifier.isPublic(method.getModifiers())) continue; + if (isStaticContext && !Modifier.isStatic(method.getModifiers())) continue; + String sig = method.getName() + "(" + method.getParameterCount() + ")"; + if (addedMethods.add(sig)) { + items.add(AutocompleteItem.fromMethod(MethodInfo.fromReflection(method, receiverType), context.methodsOnly)); + } + } + if (!receiverType.syntheticMethods.isEmpty()) { + for (MethodInfo method : receiverType.syntheticMethods) { + if (isStaticContext && !method.isStatic()) continue; + items.add(AutocompleteItem.fromMethod(method, context.methodsOnly)); + } + } else { + JSTypeInfo jsType = getMergeRegistryType(javaClass); + if (jsType != null) { + addMethodsFromType(jsType, receiverType, items, new HashSet<>(), context.methodsOnly, isStaticContext); + } + } + if (!context.methodsOnly) { + for (Field field : javaClass.getFields()) { + if (!Modifier.isPublic(field.getModifiers())) continue; + if (isStaticContext && !Modifier.isStatic(field.getModifiers())) continue; + items.add(AutocompleteItem.fromField(FieldInfo.fromReflection(field, receiverType))); + } + if (!receiverType.syntheticFields.isEmpty()) { + for (FieldInfo field : receiverType.syntheticFields) { + if (isStaticContext && !field.isStatic()) continue; + items.add(AutocompleteItem.fromField(field)); + } + } else { + JSTypeInfo jsType = getMergeRegistryType(javaClass); + if (jsType != null) { + addFieldsFromType(jsType, receiverType, items, new HashSet<>(), isStaticContext); + } + } + } return; } - boolean isStaticContext = isStaticAccess(receiverExpr, getMemberAccessResolvePosition(context)); - - // Add synthetic members (e.g., array built-ins: length, clone) for (FieldInfo field : receiverType.syntheticFields) { if (!context.methodsOnly) { if (isStaticContext && !field.isStatic()) continue; @@ -77,16 +114,28 @@ public class JSAutocompleteProvider extends JavaAutocompleteProvider { items.add(AutocompleteItem.fromMethod(method, context.methodsOnly)); } - // For pure JS types, check JSTypeRegistry JSTypeInfo jsTypeInfo = receiverType.getJSTypeInfo(); if (jsTypeInfo != null) { - // Pass both: jsTypeInfo (current type in hierarchy) and receiverType (context for type params) addMethodsFromType(jsTypeInfo, receiverType, items, new HashSet<>(), context.methodsOnly, isStaticContext); if (!context.methodsOnly && ConfigScript.ShowImplementationFieldsInAutocomplete) { addFieldsFromType(jsTypeInfo, receiverType, items, new HashSet<>(), isStaticContext); } + if (!isStaticContext) { + JSTypeInfo objJSType = registry.getType("Object"); + if (objJSType != null) { + for (JSMethodInfo m : objJSType.getMethods().values()) { + if (!m.isStatic()) { + items.add(AutocompleteItem.fromJSMethod(m, receiverType, 1, context.methodsOnly)); + } + } + } + } } } + + private JSTypeInfo getMergeRegistryType(Class javaClass) { + return document.getTypeResolver().findJSTypeForJavaClass(javaClass); + } /** * Add autocomplete suggestions for a synthetic type's methods and fields. 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 d23e878af..d188bd7fa 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 @@ -571,19 +571,19 @@ public class TypeInfo { for (MethodInfo m : syntheticMethods) if (m.getName().equals(methodName)) return true; // Check JS type first if (jsTypeInfo != null) { - return jsTypeInfo.hasMethod(methodName); + if (jsTypeInfo.hasMethod(methodName)) return true; + } else if (javaClass != null) { + try { + for (java.lang.reflect.Method m : javaClass.getMethods()) { + if (m.getName().equals(methodName)) return true; + } + } catch (Exception e) { + // Security or linkage error + } } - if (javaClass == null) return false; - try { - for (java.lang.reflect.Method m : javaClass.getMethods()) { - if (m.getName().equals(methodName)) { - return true; - } - } - } catch (Exception e) { - // Security or linkage error - } + TypeInfo obj = TypeInfo.object(); + if (obj != this) return obj.hasMethod(methodName); return false; } @@ -596,23 +596,19 @@ public class TypeInfo { if (jsTypeInfo != null) { List overloads = jsTypeInfo.getMethodOverloads(methodName); for (JSMethodInfo m : overloads) { - if (m.getParameterCount() == paramCount) { - return true; - } + if (m.getParameterCount() == paramCount) return true; } - return false; - } - - if (javaClass == null) return false; - try { - for (java.lang.reflect.Method m : javaClass.getMethods()) { - if (m.getName().equals(methodName) && m.getParameterCount() == paramCount) { - return true; + } else if (javaClass != null) { + try { + for (java.lang.reflect.Method m : javaClass.getMethods()) { + if (m.getName().equals(methodName) && m.getParameterCount() == paramCount) return true; } + } catch (Exception e) { + // Security or linkage error } - } catch (Exception e) { - // Security or linkage error } + TypeInfo obj = TypeInfo.object(); + if (obj != this) return obj.hasMethod(methodName); return false; } @@ -756,26 +752,20 @@ public class TypeInfo { */ public MethodInfo getMethodInfo(String methodName) { for (MethodInfo m : syntheticMethods) if (m.getName().equals(methodName)) return m; - // Check JS type first if (jsTypeInfo != null) { JSMethodInfo jsMethod = jsTypeInfo.getMethod(methodName); - if (jsMethod != null) { - return MethodInfo.fromJSMethod(jsMethod, this); - } - return null; - } - - if (javaClass == null) return null; - try { - for (java.lang.reflect.Method m : javaClass.getMethods()) { - if (m.getName().equals(methodName)) { - // Create a synthetic MethodInfo from reflection - return MethodInfo.fromReflection(m, this); + if (jsMethod != null) return MethodInfo.fromJSMethod(jsMethod, this); + } else if (javaClass != null) { + try { + for (java.lang.reflect.Method m : javaClass.getMethods()) { + if (m.getName().equals(methodName)) return MethodInfo.fromReflection(m, this); } + } catch (Exception e) { + // Security or linkage error } - } catch (Exception e) { - // Security or linkage error } + TypeInfo obj = TypeInfo.object(); + if (obj != this) return obj.getMethodInfo(methodName); return null; } @@ -785,6 +775,9 @@ public class TypeInfo { */ public java.util.List getAllMethodOverloads(String methodName) { java.util.List overloads = new java.util.ArrayList<>(); + + for (MethodInfo m : syntheticMethods) if (m.getName().equals(methodName)) overloads.add(m); + if (!overloads.isEmpty()) return overloads; // Check JS type first if (jsTypeInfo != null) { @@ -792,19 +785,21 @@ public class TypeInfo { // Type parameter resolution now happens inside fromJSMethod using this TypeInfo overloads.add(MethodInfo.fromJSMethod(jsMethod, this)); } - return overloads; - } - - if (javaClass == null) return overloads; - try { - for (java.lang.reflect.Method m : javaClass.getMethods()) { - if (m.getName().equals(methodName)) { - overloads.add(MethodInfo.fromReflection(m, this)); + if (!overloads.isEmpty()) return overloads; + } else if (javaClass != null) { + try { + for (java.lang.reflect.Method m : javaClass.getMethods()) { + if (m.getName().equals(methodName)) { + overloads.add(MethodInfo.fromReflection(m, this)); + } } + } catch (Exception e) { + // Security or linkage error } - } catch (Exception e) { - // Security or linkage error + if (!overloads.isEmpty()) return overloads; } + TypeInfo obj = TypeInfo.object(); + if (obj != this) return obj.getAllMethodOverloads(methodName); return overloads; } diff --git a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java index c340984b9..26b6887f9 100644 --- a/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java +++ b/src/main/java/noppes/npcs/client/gui/util/script/interpreter/type/TypeResolver.java @@ -466,6 +466,15 @@ public class TypeResolver { } } + public JSTypeInfo findJSTypeForJavaClass(Class javaClass) { + if (javaClass == null) return null; + String simpleName = javaClass.getSimpleName(); + if (getJSJavaMergeBase(simpleName) != null) { + return getJSTypeRegistry().getType(simpleName); + } + return getJSTypeRegistry().getTypeByJavaFqn(javaClass.getName()); + } + /** * For certain core types (e.g., String, Object), we want to merge JS type info from .d.ts with the Java TypeInfo to create an enriched type that has both Java and JS members. */