mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 05:27:37 +00:00
unified TypeInfo API access to it uses exposed TypeInfo members instead of inner raw Class reflection calls
This commit is contained in:
1 parent
57e6934fe5
commit
53446e06c6
4 files changed
+475
-308
No files matched your search
+89
-87
@@ -1,15 +1,16 @@
|
||||
package noppes.npcs.client.gui.util.script.autocomplete;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.*;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.field.EnumConstantInfo;
|
||||
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.type.synthetic.*;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.ScriptTypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeChecker;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeResolver;
|
||||
import noppes.npcs.config.ConfigScript;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
@@ -37,6 +38,15 @@ public class JSAutocompleteProvider extends JavaAutocompleteProvider {
|
||||
/**
|
||||
* Add suggestions for member access (after dot).
|
||||
* Uses ScriptDocument's unified type resolution system.
|
||||
*
|
||||
* Fixes applied vs original implementation:
|
||||
* - Single getMemberAccessResolvePosition() call (was duplicated)
|
||||
* - Single getMergeRegistryType() call (was duplicated)
|
||||
* - Shared addedMethods/addedFields sets across ALL sources for cross-source deduplication
|
||||
* - Unified nested type lookup via getAllNestedTypes() instead of raw reflection
|
||||
* - Single Object method injection path (registry-based via fromJSMethod)
|
||||
* - Name-based Object.class guard instead of fragile Class identity comparison
|
||||
* - Logged SecurityException instead of silently swallowed
|
||||
*/
|
||||
protected void addMemberSuggestions(Context context, List<AutocompleteItem> items) {
|
||||
String receiverExpr = context.receiverExpression;
|
||||
@@ -44,113 +54,105 @@ public class JSAutocompleteProvider extends JavaAutocompleteProvider {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve position once — used for both type resolution and static context detection
|
||||
int resolvePos = getMemberAccessResolvePosition(context);
|
||||
|
||||
// Use ScriptDocument's resolveExpressionType - handles both Java and JS
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, resolvePos);
|
||||
if (receiverType == null || !receiverType.isResolved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for synthetic types first (e.g., Nashorn Java object, custom types)
|
||||
SyntheticType syntheticType =
|
||||
document.getTypeResolver().getSyntheticType(receiverType.getSimpleName());
|
||||
// --- JS-SPECIFIC: synthetic type early return ---
|
||||
SyntheticType syntheticType = document.getTypeResolver().getSyntheticType(receiverType.getSimpleName());
|
||||
if (syntheticType != null) {
|
||||
addSyntheticTypeSuggestions(syntheticType, items, context.methodsOnly);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, resolvePos);
|
||||
|
||||
// Resolve merge-registry type once — used for both method and field fallback
|
||||
Class<?> javaClass = receiverType.getJavaClass();
|
||||
if (javaClass != null) {
|
||||
Set<String> 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));
|
||||
}
|
||||
JSTypeInfo mergeRegistryType = (javaClass != null
|
||||
&& receiverType.getSyntheticMethods().isEmpty()
|
||||
&& receiverType.getSyntheticFields().isEmpty())
|
||||
? getMergeRegistryType(javaClass) : null;
|
||||
|
||||
// Shared deduplication sets — prevent the same method/field appearing from multiple sources
|
||||
Set<String> addedMethods = new HashSet<>();
|
||||
Set<String> addedFields = new HashSet<>();
|
||||
|
||||
// ---- SHARED: unified method enumeration (TypeInfo.getAllMethods covers reflection + synthetics) ----
|
||||
for (MethodInfo method : receiverType.getAllMethods()) {
|
||||
if (isStaticContext && !Modifier.isStatic(method.getModifiers()))
|
||||
continue;
|
||||
String sig = method.getName() + "(" + method.getParameterCount() + ")";
|
||||
if (addedMethods.add(sig))
|
||||
items.add(AutocompleteItem.fromMethod(method, context.methodsOnly));
|
||||
}
|
||||
|
||||
// --- JS-SPECIFIC: merge registry type fallback for methods ---
|
||||
if (mergeRegistryType != null) {
|
||||
addMethodsFromType(mergeRegistryType, receiverType, items, addedMethods, context.methodsOnly,
|
||||
isStaticContext);
|
||||
}
|
||||
|
||||
if (!context.methodsOnly) {
|
||||
// ---- SHARED: unified field enumeration with deduplication ----
|
||||
for (FieldInfo field : receiverType.getAllFields()) {
|
||||
if (isStaticContext && !Modifier.isStatic(field.getModifiers()))
|
||||
continue;
|
||||
if (addedFields.add(field.getName()))
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
}
|
||||
List<MethodInfo> effectiveSyntheticMethods = receiverType.getSyntheticMethods();
|
||||
if (!effectiveSyntheticMethods.isEmpty()) {
|
||||
for (MethodInfo method : effectiveSyntheticMethods) {
|
||||
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);
|
||||
}
|
||||
|
||||
// --- JS-SPECIFIC: merge registry type fallback for fields ---
|
||||
if (mergeRegistryType != null) {
|
||||
addFieldsFromType(mergeRegistryType, receiverType, items, addedFields, 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.getSyntheticFields().isEmpty()) {
|
||||
for (FieldInfo field : receiverType.getSyntheticFields()) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---- SHARED: nested types (inner classes) in static context via getAllNestedTypes() ----
|
||||
if (isStaticContext && !context.methodsOnly) {
|
||||
// ScriptTypeInfo.getAllNestedTypes() returns script inner classes directly;
|
||||
// TypeInfo.getAllNestedTypes() uses reflection. No duplicate logic needed.
|
||||
for (TypeInfo nestedType : receiverType.getAllNestedTypes()) {
|
||||
items.add(AutocompleteItem.fromType(nestedType));
|
||||
}
|
||||
if (!isStaticContext && javaClass != Object.class) {
|
||||
JSTypeInfo objJSType = registry.getType("Object");
|
||||
if (objJSType != null) {
|
||||
for (JSMethodInfo m : objJSType.getMethods().values()) {
|
||||
if (!m.isStatic()) {
|
||||
}
|
||||
|
||||
// Enum constants — polymorphic via TypeInfo.getEnumConstants()
|
||||
if (!context.methodsOnly) {
|
||||
for (EnumConstantInfo enumConstant : receiverType.getEnumConstants().values()) {
|
||||
items.add(AutocompleteItem.fromField(enumConstant.getFieldInfo()));
|
||||
}
|
||||
}
|
||||
|
||||
// --- JS-SPECIFIC: Object methods from JS registry (single unified path) ---
|
||||
// Add Object.class JS methods for instance access, unless receiver IS Object itself.
|
||||
// Uses name comparison instead of Class identity for robustness across classloaders.
|
||||
if (!isStaticContext && (javaClass == null || !"java.lang.Object".equals(javaClass.getName()))) {
|
||||
JSTypeInfo objJSType = registry.getType("Object");
|
||||
if (objJSType != null) {
|
||||
for (JSMethodInfo m : objJSType.getMethods().values()) {
|
||||
if (!m.isStatic()) {
|
||||
String sig = m.getName() + "(" + m.getParameterCount() + ")";
|
||||
if (addedMethods.add(sig)) {
|
||||
items.add(AutocompleteItem.fromJSMethod(m, receiverType, 1, context.methodsOnly));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Set<String> syntheticMethodNames = new HashSet<>();
|
||||
for (MethodInfo method : receiverType.getSyntheticMethods()) {
|
||||
syntheticMethodNames.add(method.getName());
|
||||
if (isStaticContext && !method.isStatic()) continue;
|
||||
items.add(AutocompleteItem.fromMethod(method, context.methodsOnly));
|
||||
}
|
||||
for (FieldInfo field : receiverType.getSyntheticFields()) {
|
||||
if (!context.methodsOnly) {
|
||||
if (isStaticContext && !field.isStatic()) continue;
|
||||
if (syntheticMethodNames.contains(field.getName())) continue;
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
}
|
||||
}
|
||||
|
||||
JSTypeInfo jsTypeInfo = receiverType.getJSTypeInfo();
|
||||
if (jsTypeInfo != null) {
|
||||
addMethodsFromType(jsTypeInfo, receiverType, items, new HashSet<>(), context.methodsOnly, isStaticContext);
|
||||
if (!context.methodsOnly && ConfigScript.ShowImplementationFieldsInAutocomplete) {
|
||||
addFieldsFromType(jsTypeInfo, receiverType, items, new HashSet<>(), isStaticContext);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isStaticContext) {
|
||||
Set<String> addedObjMethods = new HashSet<>();
|
||||
for (Method m : Object.class.getMethods()) {
|
||||
if (!Modifier.isPublic(m.getModifiers()) || Modifier.isStatic(m.getModifiers())) continue;
|
||||
String sig = m.getName() + "(" + m.getParameterCount() + ")";
|
||||
if (addedObjMethods.add(sig)) {
|
||||
items.add(AutocompleteItem.fromMethod(MethodInfo.fromReflection(m, receiverType), context.methodsOnly));
|
||||
}
|
||||
}
|
||||
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));
|
||||
}
|
||||
// --- JS-SPECIFIC: pure JS type fallback (javaClass == null, not ScriptTypeInfo) ---
|
||||
if (javaClass == null && !(receiverType instanceof ScriptTypeInfo)) {
|
||||
JSTypeInfo jsTypeInfo = receiverType.getJSTypeInfo();
|
||||
if (jsTypeInfo != null) {
|
||||
addMethodsFromType(jsTypeInfo, receiverType, items, addedMethods, context.methodsOnly, isStaticContext);
|
||||
if (!context.methodsOnly && ConfigScript.ShowImplementationFieldsInAutocomplete) {
|
||||
addFieldsFromType(jsTypeInfo, receiverType, items, addedFields, isStaticContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-140
@@ -10,8 +10,6 @@ import noppes.npcs.client.gui.util.script.interpreter.type.TypeChecker;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeResolver;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
|
||||
@@ -100,92 +98,52 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
if (receiverExpr == null || receiverExpr.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Resolve position once — used for both type resolution and static context detection
|
||||
int resolvePos = getMemberAccessResolvePosition(context);
|
||||
|
||||
// Resolve the type of the receiver expression
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
TypeInfo receiverType = document.resolveExpressionType(receiverExpr, resolvePos);
|
||||
|
||||
if (receiverType == null || !receiverType.isResolved()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if this is a static context (accessing a class type)
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, getMemberAccessResolvePosition(context));
|
||||
boolean isStaticContext = isStaticAccess(receiverExpr, resolvePos);
|
||||
|
||||
Class<?> clazz = receiverType.getJavaClass();
|
||||
if (clazz == null) {
|
||||
// Try ScriptTypeInfo
|
||||
if (receiverType instanceof ScriptTypeInfo) {
|
||||
addScriptTypeMembers((ScriptTypeInfo) receiverType, items, isStaticContext, context.methodsOnly);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Add methods
|
||||
// Shared deduplication sets across all member sources
|
||||
Set<String> addedMethods = new HashSet<>();
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
// Filter by static context
|
||||
if (isStaticContext && !Modifier.isStatic(method.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String sig = method.getName() + "(" + method.getParameterCount() + ")";
|
||||
if (!addedMethods.contains(sig)) {
|
||||
addedMethods.add(sig);
|
||||
MethodInfo methodInfo = MethodInfo.fromReflection(method, receiverType);
|
||||
items.add(AutocompleteItem.fromMethod(methodInfo, context.methodsOnly));
|
||||
}
|
||||
}
|
||||
}
|
||||
Set<String> addedFields = new HashSet<>();
|
||||
|
||||
for (MethodInfo method : receiverType.getSyntheticMethods()) {
|
||||
if (isStaticContext && !Modifier.isStatic(method.getModifiers()))
|
||||
continue;
|
||||
|
||||
items.add(AutocompleteItem.fromMethod(method, context.methodsOnly));
|
||||
// Unified method enumeration — works for Java types, ScriptTypeInfo, and intersection bounds
|
||||
for (MethodInfo method : receiverType.getAllMethods()) {
|
||||
if (isStaticContext && !Modifier.isStatic(method.getModifiers())) continue;
|
||||
String sig = method.getName() + "(" + method.getParameterCount() + ")";
|
||||
if (addedMethods.add(sig))
|
||||
items.add(AutocompleteItem.fromMethod(method, context.methodsOnly));
|
||||
}
|
||||
|
||||
// Add fields (skip if methodsOnly is true - but the filtering will be done in getSuggestions)
|
||||
if (!context.methodsOnly) {
|
||||
for (Field field : clazz.getFields()) {
|
||||
if (Modifier.isPublic(field.getModifiers())) {
|
||||
// Filter by static context
|
||||
if (isStaticContext && !Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FieldInfo fieldInfo = FieldInfo.fromReflection(field, receiverType);
|
||||
items.add(AutocompleteItem.fromField(fieldInfo));
|
||||
}
|
||||
}
|
||||
|
||||
for (FieldInfo field : receiverType.getSyntheticFields()) {
|
||||
if (isStaticContext && !Modifier.isStatic(field.getModifiers()))
|
||||
continue;
|
||||
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
// Unified field enumeration with deduplication
|
||||
for (FieldInfo field : receiverType.getAllFields()) {
|
||||
if (isStaticContext && !Modifier.isStatic(field.getModifiers())) continue;
|
||||
if (addedFields.add(field.getName()))
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
}
|
||||
}
|
||||
|
||||
// Add nested types (inner classes/interfaces) in static context
|
||||
// Nested types (inner classes) in static context via getAllNestedTypes()
|
||||
if (isStaticContext && !context.methodsOnly) {
|
||||
try {
|
||||
for (Class<?> nested : clazz.getDeclaredClasses()) {
|
||||
if (!Modifier.isPublic(nested.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
// Convert $ notation to dot notation for resolution
|
||||
String nestedFullName = nested.getName().replace('$', '.');
|
||||
TypeInfo nestedType = TypeResolver.getInstance().resolveFullName(nestedFullName);
|
||||
if (nestedType != null && nestedType.isResolved()) {
|
||||
items.add(AutocompleteItem.fromType(nestedType));
|
||||
}
|
||||
}
|
||||
} catch (SecurityException ignored) {
|
||||
// Some runtimes may restrict getDeclaredClasses()
|
||||
for (TypeInfo nestedType : receiverType.getAllNestedTypes()) {
|
||||
items.add(AutocompleteItem.fromType(nestedType));
|
||||
}
|
||||
}
|
||||
|
||||
// Enum constants — polymorphic via TypeInfo.getEnumConstants()
|
||||
if (!context.methodsOnly) {
|
||||
for (EnumConstantInfo enumConstant : receiverType.getEnumConstants().values()) {
|
||||
items.add(AutocompleteItem.fromField(enumConstant.getFieldInfo()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -218,76 +176,6 @@ public class JavaAutocompleteProvider implements AutocompleteProvider {
|
||||
return Math.max(0, Math.min(context.prefixStart, context.text.length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add members from a script-defined type.
|
||||
*/
|
||||
protected void addScriptTypeMembers(ScriptTypeInfo scriptType, List<AutocompleteItem> items, boolean isStaticContext) {
|
||||
addScriptTypeMembers(scriptType, items, isStaticContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add members from a script-defined type.
|
||||
* @param scriptType The script type to get members from
|
||||
* @param items The list to add items to
|
||||
* @param isStaticContext Whether we're in a static context
|
||||
* @param forMethodReference If true, only add methods with no parentheses in insert text
|
||||
*/
|
||||
protected void addScriptTypeMembers(ScriptTypeInfo scriptType, List<AutocompleteItem> items, boolean isStaticContext, boolean forMethodReference) {
|
||||
// Add methods (getMethods returns Map<String, List<MethodInfo>>)
|
||||
for (List<MethodInfo> overloads : scriptType.getMethods().values()) {
|
||||
for (MethodInfo method : overloads) {
|
||||
// Filter by static context
|
||||
if (isStaticContext && !method.isStatic()) {
|
||||
continue;
|
||||
}
|
||||
items.add(AutocompleteItem.fromMethod(method, forMethodReference));
|
||||
}
|
||||
}
|
||||
|
||||
// Add fields (skip if methodsOnly/forMethodReference is true)
|
||||
if (!forMethodReference) {
|
||||
// Add fields (getFields returns Map<String, FieldInfo>)
|
||||
for (FieldInfo field : scriptType.getFields().values()) {
|
||||
// Filter by static context
|
||||
if (isStaticContext && !field.isStatic()) {
|
||||
continue;
|
||||
}
|
||||
items.add(AutocompleteItem.fromField(field));
|
||||
}
|
||||
|
||||
// Add enum constants (getEnumConstants returns Map<String, EnumConstantInfo>)
|
||||
for (EnumConstantInfo enumConstant : scriptType.getEnumConstants().values()) {
|
||||
items.add(AutocompleteItem.fromField(enumConstant.getFieldInfo()));
|
||||
}
|
||||
|
||||
// Add nested types (inner classes/interfaces) in static context
|
||||
if (isStaticContext) {
|
||||
for (ScriptTypeInfo inner : scriptType.getInnerClasses()) {
|
||||
items.add(AutocompleteItem.fromType(inner));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add parent class members
|
||||
if (scriptType.hasSuperClass()) {
|
||||
TypeInfo superType = scriptType.getSuperClass();
|
||||
if (superType != null && superType.getJavaClass() != null) {
|
||||
Class<?> superClazz = superType.getJavaClass();
|
||||
Set<String> addedMethods = new HashSet<>();
|
||||
for (Method method : superClazz.getMethods()) {
|
||||
if (Modifier.isPublic(method.getModifiers())) {
|
||||
String sig = method.getName() + "(" + method.getParameterCount() + ")";
|
||||
if (!addedMethods.contains(sig)) {
|
||||
addedMethods.add(sig);
|
||||
MethodInfo methodInfo = MethodInfo.fromReflection(method, superType);
|
||||
items.add(AutocompleteItem.fromMethod(methodInfo, forMethodReference));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add suggestions based on current scope (not after a dot).
|
||||
*/
|
||||
|
||||
+57
-11
@@ -10,6 +10,7 @@ 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.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -176,8 +177,6 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
|
||||
Set<ScriptTypeInfo> active = PARAMETERIZING.get();
|
||||
if (!active.add(this)) {
|
||||
// Re-entrant call on the same raw type — fall back to shallow parameterization
|
||||
// (no method/field substitution) to break the infinite recursion.
|
||||
return super.parameterize(typeArgs);
|
||||
}
|
||||
|
||||
@@ -310,7 +309,11 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
|
||||
if (enumConstants.containsKey(fieldName))
|
||||
return true;
|
||||
|
||||
|
||||
for (FieldInfo f : getSyntheticFields())
|
||||
if (f.getName().equals(fieldName))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -325,6 +328,10 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
return enumConst.getFieldInfo();
|
||||
}
|
||||
|
||||
for (FieldInfo f : getSyntheticFields())
|
||||
if (f.getName().equals(fieldName))
|
||||
return f;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -361,6 +368,7 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
/**
|
||||
* Get all enum constants.
|
||||
*/
|
||||
@Override
|
||||
public Map<String, EnumConstantInfo> getEnumConstants() {
|
||||
return new HashMap<>(enumConstants);
|
||||
}
|
||||
@@ -380,32 +388,51 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
|
||||
@Override
|
||||
public boolean hasMethod(String methodName) {
|
||||
return methods.containsKey(methodName);
|
||||
if (methods.containsKey(methodName))
|
||||
return true;
|
||||
for (MethodInfo m : getSyntheticMethods())
|
||||
if (m.getName().equals(methodName))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasMethod(String methodName, int paramCount) {
|
||||
List<MethodInfo> overloads = methods.get(methodName);
|
||||
if (overloads == null) return false;
|
||||
for (MethodInfo m : overloads) {
|
||||
if (m.getParameterCount() == paramCount) {
|
||||
return true;
|
||||
if (overloads != null) {
|
||||
for (MethodInfo m : overloads) {
|
||||
if (m.getParameterCount() == paramCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (MethodInfo m : getSyntheticMethods())
|
||||
if (m.getName().equals(methodName) && m.getParameterCount() == paramCount)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodInfo getMethodInfo(String methodName) {
|
||||
List<MethodInfo> overloads = methods.get(methodName);
|
||||
return (overloads != null && !overloads.isEmpty()) ? overloads.get(0) : null;
|
||||
if (overloads != null && !overloads.isEmpty())
|
||||
return overloads.get(0);
|
||||
for (MethodInfo m : getSyntheticMethods())
|
||||
if (m.getName().equals(methodName))
|
||||
return m;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all method overloads with the given name.
|
||||
*/
|
||||
@Override
|
||||
public List<MethodInfo> getAllMethodOverloads(String methodName) {
|
||||
return methods.getOrDefault(methodName, new ArrayList<>());
|
||||
List<MethodInfo> result = new ArrayList<>(methods.getOrDefault(methodName, Collections.emptyList()));
|
||||
for (MethodInfo m : getSyntheticMethods())
|
||||
if (m.getName().equals(methodName))
|
||||
result.add(m);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -432,7 +459,26 @@ public class ScriptTypeInfo extends TypeInfo {
|
||||
allMethods.addAll(overloads);
|
||||
}
|
||||
return allMethods;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MethodInfo> getAllMethods() {
|
||||
List<MethodInfo> result = getAllMethodsFlat();
|
||||
result.addAll(getSyntheticMethods());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FieldInfo> getAllFields() {
|
||||
List<FieldInfo> result = new ArrayList<>(getFields().values());
|
||||
result.addAll(getSyntheticFields());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TypeInfo> getAllNestedTypes() {
|
||||
return new ArrayList<>(innerClasses);
|
||||
}
|
||||
|
||||
// ==================== CONSTRUCTOR MANAGEMENT ====================
|
||||
|
||||
|
||||
@@ -771,6 +771,149 @@ public class TypeInfo {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all public instance members of this type for autocomplete/resolution.
|
||||
* Override in subclasses to extend or replace the default Java reflection path.
|
||||
*/
|
||||
public List<MethodInfo> getAllMethods() {
|
||||
if (javaClass != null) {
|
||||
// Java reflection path: enumerate public methods + synthetics
|
||||
List<MethodInfo> result = new ArrayList<>();
|
||||
try {
|
||||
for (java.lang.reflect.Method m : javaClass.getMethods()) {
|
||||
try {
|
||||
result.add(MethodInfo.fromReflection(m, this));
|
||||
} catch (Exception e) {
|
||||
// Skip individual methods that fail (e.g., linkage errors on obscure return types)
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
result.addAll(getSyntheticMethods());
|
||||
return result;
|
||||
}
|
||||
|
||||
// javaClass == null: check rawType, jsTypeInfo, synthetics in priority order
|
||||
if (rawType != null && rawType != this) {
|
||||
List<MethodInfo> rawMethods = rawType.getAllMethods();
|
||||
if (!rawMethods.isEmpty() && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) {
|
||||
List<MethodInfo> substituted = new ArrayList<>(rawMethods.size());
|
||||
for (MethodInfo m : rawMethods) {
|
||||
substituted.add(m.substituteTypeParams(bindings));
|
||||
}
|
||||
return substituted;
|
||||
}
|
||||
}
|
||||
return rawMethods;
|
||||
}
|
||||
|
||||
// No javaClass, no rawType — collect from jsTypeInfo and synthetics
|
||||
List<MethodInfo> result = new ArrayList<>();
|
||||
if (jsTypeInfo != null) {
|
||||
for (JSMethodInfo m : jsTypeInfo.getMethods().values()) {
|
||||
result.add(MethodInfo.fromJSMethod(m, this));
|
||||
}
|
||||
}
|
||||
result.addAll(getSyntheticMethods());
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<FieldInfo> getAllFields() {
|
||||
if (javaClass != null) {
|
||||
// Java reflection path: enumerate public fields + synthetics
|
||||
List<FieldInfo> result = new ArrayList<>();
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
try {
|
||||
result.add(FieldInfo.fromReflection(f, this));
|
||||
} catch (Exception e) {
|
||||
// Skip individual fields that fail rather than losing ALL fields.
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
result.addAll(getSyntheticFields());
|
||||
return result;
|
||||
}
|
||||
|
||||
// javaClass == null: check rawType, jsTypeInfo, synthetics in priority order
|
||||
if (rawType != null && rawType != this) {
|
||||
List<FieldInfo> rawFields = rawType.getAllFields();
|
||||
if (!rawFields.isEmpty() && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) {
|
||||
List<FieldInfo> substituted = new ArrayList<>(rawFields.size());
|
||||
for (FieldInfo f : rawFields) {
|
||||
substituted.add(f.substituteTypeParams(bindings));
|
||||
}
|
||||
return substituted;
|
||||
}
|
||||
}
|
||||
return rawFields;
|
||||
}
|
||||
|
||||
// No javaClass, no rawType — collect from jsTypeInfo and synthetics
|
||||
List<FieldInfo> result = new ArrayList<>();
|
||||
if (jsTypeInfo != null) {
|
||||
for (JSFieldInfo f : jsTypeInfo.getFields().values()) {
|
||||
result.add(FieldInfo.fromJSField(f, this));
|
||||
}
|
||||
}
|
||||
result.addAll(getSyntheticFields());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all public nested types (inner classes/interfaces/enums) of this type.
|
||||
* Uses reflection to discover nested types from the Java class, filters to public only,
|
||||
* converts internal '$' separators to '.' for name resolution, and resolves each via TypeResolver.
|
||||
*
|
||||
* Override in subclasses (e.g., ScriptTypeInfo) to return script-defined inner classes.
|
||||
*
|
||||
* @return List of resolved nested TypeInfo instances (may be empty, never null)
|
||||
*/
|
||||
public List<TypeInfo> getAllNestedTypes() {
|
||||
if (javaClass != null) {
|
||||
List<TypeInfo> result = new ArrayList<>();
|
||||
try {
|
||||
for (Class<?> nested : javaClass.getDeclaredClasses()) {
|
||||
if (!Modifier.isPublic(nested.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
// Convert JVM internal '$' separator to '.' for TypeResolver lookup
|
||||
String nestedFullName = nested.getName().replace('$', '.');
|
||||
TypeInfo nestedType = TypeResolver.getInstance().resolveFullName(nestedFullName);
|
||||
if (nestedType != null && nestedType.isResolved()) {
|
||||
result.add(nestedType);
|
||||
}
|
||||
}
|
||||
} catch (SecurityException ignored) {
|
||||
// Some runtimes may restrict getDeclaredClasses()
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
if (rawType != null && rawType != this) {
|
||||
return rawType.getAllNestedTypes();
|
||||
}
|
||||
|
||||
// No javaClass, no rawType — check jsTypeInfo for inner types
|
||||
if (jsTypeInfo != null) {
|
||||
Map<String, JSTypeInfo> innerTypes = jsTypeInfo.getInnerTypes();
|
||||
if (!innerTypes.isEmpty()) {
|
||||
List<TypeInfo> result = new ArrayList<>(innerTypes.size());
|
||||
for (JSTypeInfo inner : innerTypes.values()) {
|
||||
TypeInfo ti = TypeInfo.fromJSTypeInfo(inner);
|
||||
if (ti != null) {
|
||||
result.add(ti);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this type has a method with the given name.
|
||||
*/
|
||||
@@ -789,6 +932,8 @@ public class TypeInfo {
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} else if (rawType != null && rawType != this) {
|
||||
return rawType.hasMethod(methodName);
|
||||
}
|
||||
|
||||
TypeInfo obj = TypeInfo.object();
|
||||
@@ -817,6 +962,8 @@ public class TypeInfo {
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} else if (rawType != null && rawType != this) {
|
||||
return rawType.hasMethod(methodName, paramCount);
|
||||
}
|
||||
TypeInfo obj = TypeInfo.object();
|
||||
if (obj != this) return obj.hasMethod(methodName);
|
||||
@@ -828,13 +975,15 @@ public class TypeInfo {
|
||||
* Override in ScriptTypeInfo for script-defined types.
|
||||
*/
|
||||
public boolean hasConstructors() {
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
return javaClass.getConstructors().length > 0;
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return false;
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
return javaClass.getConstructors().length > 0;
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.hasConstructors();
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -843,16 +992,18 @@ public class TypeInfo {
|
||||
*/
|
||||
public List<MethodInfo> getConstructors() {
|
||||
List<MethodInfo> result = new ArrayList<>();
|
||||
if (javaClass == null) return result;
|
||||
|
||||
try {
|
||||
java.lang.reflect.Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (java.lang.reflect.Constructor<?> ctor : constructors) {
|
||||
result.add(MethodInfo.fromReflectionConstructor(ctor, this));
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
java.lang.reflect.Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (java.lang.reflect.Constructor<?> ctor : constructors) {
|
||||
result.add(MethodInfo.fromReflectionConstructor(ctor, this));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return result;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.getConstructors();
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -861,45 +1012,49 @@ public class TypeInfo {
|
||||
* Override in ScriptTypeInfo for script-defined types.
|
||||
*/
|
||||
public MethodInfo findConstructor(int argCount) {
|
||||
if (javaClass == null) return null;
|
||||
|
||||
try {
|
||||
Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (Constructor<?> ctor : constructors) {
|
||||
if (ctor.getParameterCount() == argCount) {
|
||||
return MethodInfo.fromReflectionConstructor(ctor, this);
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (Constructor<?> ctor : constructors) {
|
||||
if (ctor.getParameterCount() == argCount) {
|
||||
return MethodInfo.fromReflectionConstructor(ctor, this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return null;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.findConstructor(argCount);
|
||||
return null;
|
||||
}
|
||||
|
||||
public MethodInfo findConstructor(TypeInfo[] argTypes) {
|
||||
if (javaClass == null) return null;
|
||||
|
||||
try {
|
||||
Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (Constructor<?> ctor : constructors) {
|
||||
if (ctor.getParameterCount() == argTypes.length) {
|
||||
Class<?>[] paramTypes = ctor.getParameterTypes();
|
||||
boolean match = true;
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
TypeInfo paramTypeInfo = TypeInfo.fromClass(paramTypes[i]);
|
||||
if (!TypeChecker.isTypeCompatible(paramTypeInfo, argTypes[i])) {
|
||||
match = false;
|
||||
break;
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
Constructor<?>[] constructors = javaClass.getConstructors();
|
||||
for (Constructor<?> ctor : constructors) {
|
||||
if (ctor.getParameterCount() == argTypes.length) {
|
||||
Class<?>[] paramTypes = ctor.getParameterTypes();
|
||||
boolean match = true;
|
||||
for (int i = 0; i < argTypes.length; i++) {
|
||||
TypeInfo paramTypeInfo = TypeInfo.fromClass(paramTypes[i]);
|
||||
if (!TypeChecker.isTypeCompatible(paramTypeInfo, argTypes[i])) {
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return MethodInfo.fromReflectionConstructor(ctor, this);
|
||||
}
|
||||
}
|
||||
if (match) {
|
||||
return MethodInfo.fromReflectionConstructor(ctor, this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return null;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.findConstructor(argTypes);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -913,30 +1068,63 @@ public class TypeInfo {
|
||||
return jsTypeInfo.hasField(fieldName);
|
||||
}
|
||||
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
return true;
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return false;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.hasField(fieldName);
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean hasEnumConstant(String constantName) {
|
||||
if (javaClass == null || !javaClass.isEnum()) return false;
|
||||
/**
|
||||
* Returns all enum constants for this type as a name-to-EnumConstantInfo map.
|
||||
* For Java enum types, builds the map via reflection. For script-defined enums,
|
||||
* ScriptTypeInfo overrides this to return its stored constants directly.
|
||||
*
|
||||
* @return Map of constant name to EnumConstantInfo (empty if not an enum or no constants)
|
||||
*/
|
||||
public Map<String, EnumConstantInfo> getEnumConstants() {
|
||||
if (javaClass == null || !javaClass.isEnum()) {
|
||||
if (rawType != null && rawType != this) {
|
||||
return rawType.getEnumConstants();
|
||||
}
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, EnumConstantInfo> result = new LinkedHashMap<>();
|
||||
try {
|
||||
Object[] constants = javaClass.getEnumConstants();
|
||||
for (Object constant : constants) {
|
||||
if (constant.toString().equals(constantName))
|
||||
return true;
|
||||
String name = constant.toString();
|
||||
result.put(name, EnumConstantInfo.fromReflection(name, this, null));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
// Security or linkage error — return whatever we have so far
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean hasEnumConstant(String constantName) {
|
||||
if (javaClass != null && javaClass.isEnum()) {
|
||||
try {
|
||||
Object[] constants = javaClass.getEnumConstants();
|
||||
for (Object constant : constants) {
|
||||
if (constant.toString().equals(constantName))
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.hasEnumConstant(constantName);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -944,16 +1132,19 @@ public class TypeInfo {
|
||||
* Get an enum constant by name.
|
||||
*/
|
||||
public EnumConstantInfo getEnumConstant(String constantName) {
|
||||
if (javaClass == null || !javaClass.isEnum()) return null;
|
||||
try {
|
||||
Object[] constants = javaClass.getEnumConstants();
|
||||
for (Object constant : constants) {
|
||||
if (constant.toString().equals(constantName))
|
||||
return EnumConstantInfo.fromReflection(constantName, this, null);
|
||||
if (javaClass != null && javaClass.isEnum()) {
|
||||
try {
|
||||
Object[] constants = javaClass.getEnumConstants();
|
||||
for (Object constant : constants) {
|
||||
if (constant.toString().equals(constantName))
|
||||
return EnumConstantInfo.fromReflection(constantName, this, null);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return null;
|
||||
}
|
||||
if (rawType != null && rawType != this) return rawType.getEnumConstant(constantName);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -976,6 +1167,13 @@ public class TypeInfo {
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} else if (rawType != null && rawType != this) {
|
||||
MethodInfo rawMethod = rawType.getMethodInfo(methodName);
|
||||
if (rawMethod != null && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) return rawMethod.substituteTypeParams(bindings);
|
||||
}
|
||||
return rawMethod;
|
||||
}
|
||||
TypeInfo obj = TypeInfo.object();
|
||||
if (obj != this) return obj.getMethodInfo(methodName);
|
||||
@@ -1012,6 +1210,18 @@ public class TypeInfo {
|
||||
// Security or linkage error
|
||||
}
|
||||
if (!overloads.isEmpty()) return overloads;
|
||||
} else if (rawType != null && rawType != this) {
|
||||
java.util.List<MethodInfo> rawOverloads = rawType.getAllMethodOverloads(methodName);
|
||||
if (!rawOverloads.isEmpty() && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) {
|
||||
for (MethodInfo m : rawOverloads) {
|
||||
overloads.add(m.substituteTypeParams(bindings));
|
||||
}
|
||||
return overloads;
|
||||
}
|
||||
}
|
||||
return rawOverloads;
|
||||
}
|
||||
TypeInfo obj = TypeInfo.object();
|
||||
if (obj != this) return obj.getAllMethodOverloads(methodName);
|
||||
@@ -1078,16 +1288,25 @@ public class TypeInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (javaClass == null) return null;
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
// Create a synthetic FieldInfo from reflection
|
||||
return FieldInfo.fromReflection(f, this);
|
||||
if (javaClass != null) {
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
if (f.getName().equals(fieldName)) {
|
||||
return FieldInfo.fromReflection(f, this);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Security or linkage error
|
||||
return null;
|
||||
}
|
||||
if (rawType != null && rawType != this) {
|
||||
FieldInfo rawField = rawType.getFieldInfo(fieldName);
|
||||
if (rawField != null && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) return rawField.substituteTypeParams(bindings);
|
||||
}
|
||||
return rawField;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1206,7 +1425,19 @@ public class TypeInfo {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Not a functional interface
|
||||
// Not a functional interface — try rawType for parameterized script types
|
||||
if (rawType != null && rawType != this) {
|
||||
MethodInfo rawSAM = rawType.getSingleAbstractMethod();
|
||||
if (rawSAM != null && isParameterized()) {
|
||||
Map<String, TypeInfo> bindings = TypeSubstitutor.createBindingsFromReceiver(this);
|
||||
if (!bindings.isEmpty()) {
|
||||
cachedSAM = rawSAM.substituteTypeParams(bindings);
|
||||
return cachedSAM;
|
||||
}
|
||||
}
|
||||
cachedSAM = rawSAM;
|
||||
return cachedSAM;
|
||||
}
|
||||
cachedSAM = null;
|
||||
return null;
|
||||
|
||||
|
||||
Reference in new issue
Block a user