mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-12 20:07:32 +00:00
A bunch of JS parser fixes and improvements
This commit is contained in:
1 parent
50bcbb5872
commit
61a363fdba
6 files changed
+420
-173
No files matched your search
@@ -1,5 +1,8 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter.field;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSFieldInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.js_parser.JSTypeRegistry;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.method.MethodCallInfo;
|
||||
@@ -142,6 +145,75 @@ public final class FieldInfo {
|
||||
|
||||
return new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, null, -1, -1, field.getModifiers(), field);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a FieldInfo from a JSFieldInfo (parsed from .d.ts files).
|
||||
* Used when resolving field access on JavaScript types.
|
||||
*
|
||||
* @param jsField The JavaScript field info from the type registry
|
||||
* @param containingType The TypeInfo that owns this field
|
||||
* @return A FieldInfo representing the JavaScript field
|
||||
*/
|
||||
public static FieldInfo fromJSField(JSFieldInfo jsField, TypeInfo containingType) {
|
||||
String name = jsField.getName();
|
||||
|
||||
// Resolve the type from the JS type registry
|
||||
TypeInfo type = resolveJSType(jsField.getType());
|
||||
|
||||
// JS fields are public by default, readonly maps to final
|
||||
int modifiers = Modifier.PUBLIC;
|
||||
if (jsField.isReadonly()) {
|
||||
modifiers |= Modifier.FINAL;
|
||||
}
|
||||
|
||||
// Use documentation if available
|
||||
String documentation = jsField.getDocumentation();
|
||||
|
||||
return new FieldInfo(name, Scope.GLOBAL, type, -1, true, null, documentation, -1, -1, modifiers, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a JavaScript type name to a TypeInfo.
|
||||
* Handles primitives, mapped types, and custom types from the registry.
|
||||
*/
|
||||
private static TypeInfo resolveJSType(String jsTypeName) {
|
||||
if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle JS primitives
|
||||
switch (jsTypeName) {
|
||||
case "string":
|
||||
return TypeInfo.fromClass(String.class);
|
||||
case "number":
|
||||
return TypeInfo.fromClass(double.class);
|
||||
case "boolean":
|
||||
return TypeInfo.fromClass(boolean.class);
|
||||
case "any":
|
||||
return TypeInfo.fromClass(Object.class);
|
||||
case "void":
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle array types
|
||||
if (jsTypeName.endsWith("[]")) {
|
||||
String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
|
||||
TypeInfo elementTypeInfo = resolveJSType(elementType);
|
||||
return TypeInfo.arrayOf(elementTypeInfo);
|
||||
}
|
||||
|
||||
// Try to resolve from the JS type registry
|
||||
JSTypeRegistry registry = JSTypeRegistry.getInstance();
|
||||
if (registry != null) {
|
||||
JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
|
||||
if (jsTypeInfo != null) {
|
||||
return TypeInfo.fromJSTypeInfo(jsTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: unresolved type
|
||||
return TypeInfo.unresolved(jsTypeName, jsTypeName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a FieldInfo for an enum constant.
|
||||
|
||||
-117
@@ -1,117 +0,0 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter.js_parser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Container classes for hover information in JS scripts.
|
||||
*/
|
||||
public class JSHoverInfo {
|
||||
|
||||
/**
|
||||
* Hover info for a variable.
|
||||
*/
|
||||
public static class VariableInfo {
|
||||
public final String name;
|
||||
public final String typeName;
|
||||
public final JSTypeInfo resolvedType;
|
||||
|
||||
public VariableInfo(String name, String typeName, JSTypeInfo resolvedType) {
|
||||
this.name = name;
|
||||
this.typeName = typeName;
|
||||
this.resolvedType = resolvedType;
|
||||
}
|
||||
|
||||
public String buildHoverHtml() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<b>").append(name).append("</b>: <i>").append(typeName).append("</i>");
|
||||
|
||||
if (resolvedType != null && resolvedType.getDocumentation() != null) {
|
||||
sb.append("<br><br>").append(resolvedType.getDocumentation());
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hover info for a method.
|
||||
*/
|
||||
public static class MethodInfo {
|
||||
public final JSMethodInfo method;
|
||||
public final JSTypeInfo containingType;
|
||||
|
||||
public MethodInfo(JSMethodInfo method, JSTypeInfo containingType) {
|
||||
this.method = method;
|
||||
this.containingType = containingType;
|
||||
}
|
||||
|
||||
public String buildHoverHtml() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Show containing type
|
||||
if (containingType != null) {
|
||||
sb.append("<i>").append(containingType.getFullName()).append("</i><br>");
|
||||
}
|
||||
|
||||
// Show method signature
|
||||
sb.append(method.buildHoverInfo());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hover info for a field.
|
||||
*/
|
||||
public static class FieldInfo {
|
||||
public final JSFieldInfo field;
|
||||
public final JSTypeInfo containingType;
|
||||
|
||||
public FieldInfo(JSFieldInfo field, JSTypeInfo containingType) {
|
||||
this.field = field;
|
||||
this.containingType = containingType;
|
||||
}
|
||||
|
||||
public String buildHoverHtml() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// Show containing type
|
||||
if (containingType != null) {
|
||||
sb.append("<i>").append(containingType.getFullName()).append("</i><br>");
|
||||
}
|
||||
|
||||
// Show field info
|
||||
sb.append(field.buildHoverInfo());
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hover info for a hook function.
|
||||
*/
|
||||
public static class FunctionInfo {
|
||||
public final String name;
|
||||
public final List<JSTypeRegistry.HookSignature> signatures;
|
||||
|
||||
public FunctionInfo(String name, List<JSTypeRegistry.HookSignature> signatures) {
|
||||
this.name = name;
|
||||
this.signatures = signatures;
|
||||
}
|
||||
|
||||
public String buildHoverHtml() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<b>Hook: ").append(name).append("</b>");
|
||||
|
||||
if (!signatures.isEmpty()) {
|
||||
sb.append("<br><br>Overloads:");
|
||||
for (JSTypeRegistry.HookSignature sig : signatures) {
|
||||
sb.append("<br>• function ").append(name).append("(")
|
||||
.append(sig.paramName).append(": <i>").append(sig.paramType).append("</i>)");
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+127
-51
@@ -2,7 +2,10 @@ package noppes.npcs.client.gui.util.script.interpreter.js_parser;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.ScriptDocument;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.ScriptLine;
|
||||
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.token.TokenType;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
import scala.annotation.meta.field;
|
||||
|
||||
import java.util.*;
|
||||
@@ -127,32 +130,48 @@ public class JSScriptAnalyzer {
|
||||
|
||||
// Check if this is a known hook
|
||||
if (registry.isHook(funcName)) {
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL,
|
||||
new JSHoverInfo.FunctionInfo(funcName, registry.getHookSignatures(funcName))));
|
||||
|
||||
// Infer parameter types from hook
|
||||
// Create a unified MethodInfo for the hook
|
||||
List<JSTypeRegistry.HookSignature> sigs = registry.getHookSignatures(funcName);
|
||||
if (!sigs.isEmpty() && !params.isEmpty()) {
|
||||
// Parse parameter names from function
|
||||
String[] paramNames = params.split(",");
|
||||
if (paramNames.length > 0) {
|
||||
String paramName = paramNames[0].trim();
|
||||
// Use first hook signature's type
|
||||
String paramType = sigs.get(0).paramType;
|
||||
|
||||
// Store in function params and variable types
|
||||
Map<String, String> funcParamMap = new HashMap<>();
|
||||
funcParamMap.put(paramName, paramType);
|
||||
functionParams.put(funcName, funcParamMap);
|
||||
variableTypes.put(paramName, paramType);
|
||||
|
||||
// Mark the parameter
|
||||
int paramStart = m.start(2) + params.indexOf(paramName);
|
||||
int paramEnd = paramStart + paramName.length();
|
||||
JSTypeInfo typeInfo = registry.getType(paramType);
|
||||
marks.add(new ScriptLine.Mark(paramStart, paramEnd, TokenType.PARAMETER,
|
||||
new JSHoverInfo.VariableInfo(paramName, paramType, typeInfo)));
|
||||
if (!sigs.isEmpty()) {
|
||||
JSTypeRegistry.HookSignature sig = sigs.get(0);
|
||||
|
||||
// Create unified MethodInfo for hover info
|
||||
TypeInfo paramTypeInfo = resolveJSType(sig.paramType);
|
||||
List<FieldInfo> methodParams = new ArrayList<>();
|
||||
methodParams.add(FieldInfo.reflectionParam(sig.paramName, paramTypeInfo));
|
||||
|
||||
MethodInfo hookMethod = MethodInfo.declaration(
|
||||
funcName, null, TypeInfo.fromPrimitive("void"), methodParams,
|
||||
nameStart, nameStart, nameStart, -1, -1, 0, sig.doc
|
||||
);
|
||||
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, hookMethod));
|
||||
|
||||
// Infer parameter types from hook
|
||||
if (!params.isEmpty()) {
|
||||
// Parse parameter names from function
|
||||
String[] paramNames = params.split(",");
|
||||
if (paramNames.length > 0) {
|
||||
String paramName = paramNames[0].trim();
|
||||
String paramType = sig.paramType;
|
||||
|
||||
// Store in function params and variable types
|
||||
Map<String, String> funcParamMap = new HashMap<>();
|
||||
funcParamMap.put(paramName, paramType);
|
||||
functionParams.put(funcName, funcParamMap);
|
||||
variableTypes.put(paramName, paramType);
|
||||
|
||||
// Mark the parameter with unified FieldInfo
|
||||
int paramStart = m.start(2) + params.indexOf(paramName);
|
||||
int paramEnd = paramStart + paramName.length();
|
||||
FieldInfo paramFieldInfo = FieldInfo.parameter(
|
||||
paramName, paramTypeInfo, paramStart, hookMethod
|
||||
);
|
||||
marks.add(new ScriptLine.Mark(paramStart, paramEnd, TokenType.PARAMETER, paramFieldInfo));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, funcName));
|
||||
}
|
||||
} else {
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_DECL, funcName));
|
||||
@@ -181,12 +200,12 @@ public class JSScriptAnalyzer {
|
||||
variableTypes.put(varName, inferredType);
|
||||
}
|
||||
|
||||
// Mark variable declaration
|
||||
// Mark variable declaration with unified FieldInfo
|
||||
int varStart = m.start(1);
|
||||
int varEnd = m.end(1);
|
||||
JSTypeInfo typeInfo = inferredType != null ? registry.getType(inferredType) : null;
|
||||
marks.add(new ScriptLine.Mark(varStart, varEnd, TokenType.LOCAL_FIELD,
|
||||
new JSHoverInfo.VariableInfo(varName, inferredType != null ? inferredType : "any", typeInfo)));
|
||||
TypeInfo typeInfo = resolveJSType(inferredType != null ? inferredType : "any");
|
||||
FieldInfo varFieldInfo = FieldInfo.localField(varName, typeInfo, varStart, null);
|
||||
marks.add(new ScriptLine.Mark(varStart, varEnd, TokenType.LOCAL_FIELD, varFieldInfo));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,9 +289,9 @@ public class JSScriptAnalyzer {
|
||||
}
|
||||
|
||||
// Check if it's a field
|
||||
JSFieldInfo field = typeInfo.getField(member);
|
||||
if (field != null) {
|
||||
currentType = field.getType();
|
||||
JSFieldInfo f = typeInfo.getField(member);
|
||||
if (f != null) {
|
||||
currentType = f.getType();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -301,11 +320,11 @@ public class JSScriptAnalyzer {
|
||||
|
||||
int pos = m.start();
|
||||
|
||||
// Mark receiver
|
||||
// Mark receiver with unified FieldInfo
|
||||
if (currentType != null) {
|
||||
JSTypeInfo typeInfo = registry.getType(currentType);
|
||||
marks.add(new ScriptLine.Mark(pos, pos + receiverName.length(), TokenType.LOCAL_FIELD,
|
||||
new JSHoverInfo.VariableInfo(receiverName, currentType, typeInfo)));
|
||||
TypeInfo unifiedType = resolveJSType(currentType);
|
||||
FieldInfo receiverField = FieldInfo.localField(receiverName, unifiedType, pos, null);
|
||||
marks.add(new ScriptLine.Mark(pos, pos + receiverName.length(), TokenType.LOCAL_FIELD, receiverField));
|
||||
}
|
||||
|
||||
pos += receiverName.length() + 1; // +1 for the dot
|
||||
@@ -317,25 +336,29 @@ public class JSScriptAnalyzer {
|
||||
int memberEnd = pos + member.length();
|
||||
|
||||
if (currentType != null) {
|
||||
JSTypeInfo typeInfo = registry.getType(currentType);
|
||||
if (typeInfo != null) {
|
||||
JSTypeInfo jsTypeInfo = registry.getType(currentType);
|
||||
if (jsTypeInfo != null) {
|
||||
TypeInfo unifiedContainingType = TypeInfo.fromJSTypeInfo(jsTypeInfo);
|
||||
|
||||
// Check method first
|
||||
JSMethodInfo method = typeInfo.getMethod(member);
|
||||
if (method != null) {
|
||||
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.METHOD_CALL,
|
||||
new JSHoverInfo.MethodInfo(method, typeInfo)));
|
||||
currentType = method.getReturnType();
|
||||
JSMethodInfo jsMethod = jsTypeInfo.getMethod(member);
|
||||
if (jsMethod != null) {
|
||||
// Convert to unified MethodInfo
|
||||
MethodInfo unifiedMethod = MethodInfo.fromJSMethod(jsMethod, unifiedContainingType);
|
||||
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.METHOD_CALL, unifiedMethod));
|
||||
currentType = jsMethod.getReturnType();
|
||||
} else {
|
||||
// Check field
|
||||
JSFieldInfo field = typeInfo.getField(member);
|
||||
if (field != null) {
|
||||
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.GLOBAL_FIELD,
|
||||
new JSHoverInfo.FieldInfo(field, typeInfo)));
|
||||
currentType = field.getType();
|
||||
JSFieldInfo jsField = jsTypeInfo.getField(member);
|
||||
if (jsField != null) {
|
||||
// Convert to unified FieldInfo
|
||||
FieldInfo unifiedField = FieldInfo.fromJSField(jsField, unifiedContainingType);
|
||||
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.GLOBAL_FIELD, unifiedField));
|
||||
currentType = jsField.getType();
|
||||
} else {
|
||||
// Unknown member - mark as undefined
|
||||
marks.add(new ScriptLine.Mark(memberStart, memberEnd, TokenType.UNDEFINED_VAR,
|
||||
"Unknown member '" + member + "' on type " + typeInfo.getFullName()));
|
||||
"Unknown member '" + member + "' on type " + jsTypeInfo.getFullName()));
|
||||
currentType = null;
|
||||
}
|
||||
}
|
||||
@@ -364,9 +387,20 @@ public class JSScriptAnalyzer {
|
||||
int nameEnd = m.end(1);
|
||||
|
||||
if (registry.isHook(callExpr)) {
|
||||
// It's a known hook being called
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL,
|
||||
new JSHoverInfo.FunctionInfo(callExpr, registry.getHookSignatures(callExpr))));
|
||||
// It's a known hook being called - create unified MethodInfo
|
||||
List<JSTypeRegistry.HookSignature> sigs = registry.getHookSignatures(callExpr);
|
||||
if (!sigs.isEmpty()) {
|
||||
JSTypeRegistry.HookSignature sig = sigs.get(0);
|
||||
TypeInfo paramTypeInfo = resolveJSType(sig.paramType);
|
||||
List<FieldInfo> methodParams = new ArrayList<>();
|
||||
methodParams.add(FieldInfo.reflectionParam(sig.paramName, paramTypeInfo));
|
||||
|
||||
MethodInfo hookMethod = MethodInfo.declaration(
|
||||
callExpr, null, TypeInfo.fromPrimitive("void"), methodParams,
|
||||
nameStart, nameStart, nameStart, -1, -1, 0, sig.doc
|
||||
);
|
||||
marks.add(new ScriptLine.Mark(nameStart, nameEnd, TokenType.METHOD_CALL, hookMethod));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Chained calls are handled in markMemberAccesses
|
||||
@@ -396,4 +430,46 @@ public class JSScriptAnalyzer {
|
||||
public Map<String, String> getVariableTypes() {
|
||||
return new HashMap<>(variableTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a JavaScript type name to a unified TypeInfo.
|
||||
* Handles primitives, mapped types, and custom types from the registry.
|
||||
*/
|
||||
private TypeInfo resolveJSType(String jsTypeName) {
|
||||
if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle JS primitives
|
||||
switch (jsTypeName) {
|
||||
case "string":
|
||||
return TypeInfo.fromClass(String.class);
|
||||
case "number":
|
||||
return TypeInfo.fromClass(double.class);
|
||||
case "boolean":
|
||||
return TypeInfo.fromClass(boolean.class);
|
||||
case "any":
|
||||
return TypeInfo.fromClass(Object.class);
|
||||
case "void":
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle array types
|
||||
if (jsTypeName.endsWith("[]")) {
|
||||
String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
|
||||
TypeInfo elementTypeInfo = resolveJSType(elementType);
|
||||
return TypeInfo.arrayOf(elementTypeInfo);
|
||||
}
|
||||
|
||||
// Try to resolve from the JS type registry
|
||||
if (registry != null) {
|
||||
JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
|
||||
if (jsTypeInfo != null) {
|
||||
return TypeInfo.fromJSTypeInfo(jsTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: unresolved type
|
||||
return TypeInfo.unresolved(jsTypeName, jsTypeName);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -94,7 +94,7 @@ public class JSTypeRegistry {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
parser.parseDefinitionFile(sb.toString(), fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// File might not exist, that's ok
|
||||
|
||||
@@ -2,6 +2,9 @@ package noppes.npcs.client.gui.util.script.interpreter.method;
|
||||
|
||||
import noppes.npcs.client.gui.util.script.interpreter.*;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.field.FieldInfo;
|
||||
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.JSTypeRegistry;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.token.TokenType;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeChecker;
|
||||
import noppes.npcs.client.gui.util.script.interpreter.type.TypeInfo;
|
||||
@@ -160,6 +163,84 @@ public final class MethodInfo {
|
||||
return new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, true, modifiers, null, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MethodInfo from a JSMethodInfo (parsed from .d.ts files).
|
||||
* Used when resolving method calls on JavaScript types.
|
||||
*
|
||||
* @param jsMethod The JavaScript method info from the type registry
|
||||
* @param containingType The TypeInfo that owns this method
|
||||
* @return A MethodInfo representing the JavaScript method
|
||||
*/
|
||||
public static MethodInfo fromJSMethod(JSMethodInfo jsMethod, TypeInfo containingType) {
|
||||
String name = jsMethod.getName();
|
||||
|
||||
// Resolve the return type from the JS type registry
|
||||
TypeInfo returnType = resolveJSType(jsMethod.getReturnType());
|
||||
|
||||
// Convert JS parameters to FieldInfo
|
||||
List<FieldInfo> params = new ArrayList<>();
|
||||
List<JSMethodInfo.JSParameterInfo> jsParams = jsMethod.getParameters();
|
||||
|
||||
for (JSMethodInfo.JSParameterInfo param : jsParams) {
|
||||
String paramName = param.getName();
|
||||
String paramTypeName = param.getType();
|
||||
TypeInfo paramType = resolveJSType(paramTypeName);
|
||||
params.add(FieldInfo.reflectionParam(paramName, paramType));
|
||||
}
|
||||
|
||||
// JS methods are always public (no access modifiers in .d.ts)
|
||||
int modifiers = Modifier.PUBLIC;
|
||||
|
||||
// Use the documentation from the method if available
|
||||
String documentation = jsMethod.getDocumentation();
|
||||
|
||||
return new MethodInfo(name, returnType, containingType, params, -1, -1, -1, -1, -1, true, false, modifiers, documentation, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves a JavaScript type name to a TypeInfo.
|
||||
* Handles primitives, mapped types, and custom types from the registry.
|
||||
*/
|
||||
private static TypeInfo resolveJSType(String jsTypeName) {
|
||||
if (jsTypeName == null || jsTypeName.isEmpty() || "void".equals(jsTypeName)) {
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle JS primitives
|
||||
switch (jsTypeName) {
|
||||
case "string":
|
||||
return TypeInfo.fromClass(String.class);
|
||||
case "number":
|
||||
return TypeInfo.fromClass(double.class);
|
||||
case "boolean":
|
||||
return TypeInfo.fromClass(boolean.class);
|
||||
case "any":
|
||||
return TypeInfo.fromClass(Object.class);
|
||||
case "void":
|
||||
return TypeInfo.fromPrimitive("void");
|
||||
}
|
||||
|
||||
// Handle array types
|
||||
if (jsTypeName.endsWith("[]")) {
|
||||
String elementType = jsTypeName.substring(0, jsTypeName.length() - 2);
|
||||
TypeInfo elementTypeInfo = resolveJSType(elementType);
|
||||
// For arrays, we create an array type representation
|
||||
return TypeInfo.arrayOf(elementTypeInfo);
|
||||
}
|
||||
|
||||
// Try to resolve from the JS type registry
|
||||
JSTypeRegistry registry = JSTypeRegistry.getInstance();
|
||||
if (registry != null) {
|
||||
JSTypeInfo jsTypeInfo = registry.getType(jsTypeName);
|
||||
if (jsTypeInfo != null) {
|
||||
return TypeInfo.fromJSTypeInfo(jsTypeInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: unresolved type
|
||||
return TypeInfo.unresolved(jsTypeName, jsTypeName);
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getName() { return name; }
|
||||
public TypeInfo getReturnType() { return returnType; }
|
||||
|
||||
@@ -2,6 +2,9 @@ package noppes.npcs.client.gui.util.script.interpreter.type;
|
||||
|
||||
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.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.token.TokenType;
|
||||
|
||||
@@ -11,7 +14,14 @@ import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents resolved type information for a class/interface/enum.
|
||||
* Base class for type metadata. Extended by ScriptTypeInfo for script-defined types.
|
||||
*
|
||||
* This is the unified type system that supports:
|
||||
* - Java types (via Class<?> reflection)
|
||||
* - Script-defined types (via ScriptTypeInfo subclass)
|
||||
* - JavaScript/TypeScript types (via JSTypeInfo bridge)
|
||||
*
|
||||
* For JavaScript types, the jsTypeInfo field holds the parsed .d.ts data,
|
||||
* and methods like hasMethod/getMethodInfo delegate to it.
|
||||
*/
|
||||
public class TypeInfo {
|
||||
|
||||
@@ -21,17 +31,31 @@ public class TypeInfo {
|
||||
ENUM,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
/**
|
||||
* Singleton constant for the void type.
|
||||
*/
|
||||
public static final TypeInfo VOID = fromPrimitive("void");
|
||||
|
||||
private final String simpleName; // e.g., "List", "ColorType"
|
||||
private final String fullName; // e.g., "java.util.List", "kamkeel...IOverlay$ColorType"
|
||||
private final String packageName; // e.g., "java.util", "kamkeel.npcdbc.api.client.overlay"
|
||||
private final Kind kind; // CLASS, INTERFACE, ENUM
|
||||
private final Class<?> javaClass; // The actual resolved Java class (null if unresolved)
|
||||
private final Class<?> javaClass; // The actual resolved Java class (null if unresolved or JS type)
|
||||
private final boolean resolved; // Whether this type was successfully resolved
|
||||
private final TypeInfo enclosingType; // For inner classes, the outer type (null if top-level)
|
||||
|
||||
// JavaScript/TypeScript type info (for types from .d.ts files)
|
||||
private final JSTypeInfo jsTypeInfo; // The JS type info (null if Java type)
|
||||
|
||||
private TypeInfo(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass, boolean resolved, TypeInfo enclosingType) {
|
||||
this(simpleName, fullName, packageName, kind, javaClass, resolved, enclosingType, null);
|
||||
}
|
||||
|
||||
private TypeInfo(String simpleName, String fullName, String packageName,
|
||||
Kind kind, Class<?> javaClass, boolean resolved, TypeInfo enclosingType,
|
||||
JSTypeInfo jsTypeInfo) {
|
||||
this.simpleName = simpleName;
|
||||
this.fullName = fullName;
|
||||
this.packageName = packageName;
|
||||
@@ -39,6 +63,7 @@ public class TypeInfo {
|
||||
this.javaClass = javaClass;
|
||||
this.resolved = resolved;
|
||||
this.enclosingType = enclosingType;
|
||||
this.jsTypeInfo = jsTypeInfo;
|
||||
}
|
||||
|
||||
// Protected constructor for subclasses (like ScriptTypeInfo)
|
||||
@@ -52,6 +77,7 @@ public class TypeInfo {
|
||||
this.javaClass = javaClass;
|
||||
this.resolved = resolved;
|
||||
this.enclosingType = enclosingType;
|
||||
this.jsTypeInfo = null;
|
||||
}
|
||||
|
||||
// Factory methods
|
||||
@@ -122,6 +148,67 @@ public class TypeInfo {
|
||||
}
|
||||
return new TypeInfo(typeName, typeName, "", Kind.CLASS, primitiveClass, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TypeInfo from a JavaScript/TypeScript type.
|
||||
* This bridges JS types parsed from .d.ts files into the unified type system.
|
||||
*
|
||||
* @param jsType The parsed JS type info
|
||||
* @return A TypeInfo wrapping the JS type
|
||||
*/
|
||||
public static TypeInfo fromJSTypeInfo(JSTypeInfo jsType) {
|
||||
if (jsType == null) return null;
|
||||
|
||||
String simpleName = jsType.getSimpleName();
|
||||
String fullName = jsType.getFullName();
|
||||
String namespace = jsType.getNamespace();
|
||||
|
||||
// JS interfaces are always Kind.INTERFACE
|
||||
return new TypeInfo(simpleName, fullName, namespace != null ? namespace : "",
|
||||
Kind.INTERFACE, null, true, null, jsType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array type wrapping the given element type.
|
||||
*
|
||||
* @param elementType The type of elements in the array
|
||||
* @return A TypeInfo representing the array type
|
||||
*/
|
||||
public static TypeInfo arrayOf(TypeInfo elementType) {
|
||||
if (elementType == null) {
|
||||
return fromClass(Object[].class);
|
||||
}
|
||||
|
||||
String simpleName = elementType.getSimpleName() + "[]";
|
||||
String fullName = elementType.getFullName() + "[]";
|
||||
String pkg = elementType.getPackageName();
|
||||
|
||||
// Try to get the actual array class if we have a Java class
|
||||
Class<?> arrayClass = null;
|
||||
if (elementType.getJavaClass() != null) {
|
||||
try {
|
||||
arrayClass = java.lang.reflect.Array.newInstance(elementType.getJavaClass(), 0).getClass();
|
||||
} catch (Exception e) {
|
||||
// Fallback to Object array if we can't create the specific array type
|
||||
}
|
||||
}
|
||||
|
||||
return new TypeInfo(simpleName, fullName, pkg, Kind.CLASS, arrayClass, true, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is a JavaScript type (backed by JSTypeInfo).
|
||||
*/
|
||||
public boolean isJSType() {
|
||||
return jsTypeInfo != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying JSTypeInfo if this is a JS type.
|
||||
*/
|
||||
public JSTypeInfo getJSTypeInfo() {
|
||||
return jsTypeInfo;
|
||||
}
|
||||
|
||||
// Getters
|
||||
public String getSimpleName() { return simpleName; }
|
||||
@@ -160,6 +247,11 @@ public class TypeInfo {
|
||||
* Check if this type has a method with the given name.
|
||||
*/
|
||||
public boolean hasMethod(String methodName) {
|
||||
// Check JS type first
|
||||
if (jsTypeInfo != null) {
|
||||
return jsTypeInfo.hasMethod(methodName);
|
||||
}
|
||||
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
for (java.lang.reflect.Method m : javaClass.getMethods()) {
|
||||
@@ -177,6 +269,17 @@ public class TypeInfo {
|
||||
* Check if this type has a method with the given name and parameter count.
|
||||
*/
|
||||
public boolean hasMethod(String methodName, int paramCount) {
|
||||
// Check JS type first
|
||||
if (jsTypeInfo != null) {
|
||||
List<JSMethodInfo> overloads = jsTypeInfo.getMethodOverloads(methodName);
|
||||
for (JSMethodInfo m : overloads) {
|
||||
if (m.getParameterCount() == paramCount) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
for (java.lang.reflect.Method m : javaClass.getMethods()) {
|
||||
@@ -274,6 +377,11 @@ public class TypeInfo {
|
||||
* Check if this type has a field with the given name.
|
||||
*/
|
||||
public boolean hasField(String fieldName) {
|
||||
// Check JS type first
|
||||
if (jsTypeInfo != null) {
|
||||
return jsTypeInfo.hasField(fieldName);
|
||||
}
|
||||
|
||||
if (javaClass == null) return false;
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
@@ -320,9 +428,18 @@ public class TypeInfo {
|
||||
|
||||
/**
|
||||
* Get MethodInfo for a method by name. Returns null if not found.
|
||||
* Creates a synthetic MethodInfo based on reflection data.
|
||||
* Creates a synthetic MethodInfo based on reflection data or JS type data.
|
||||
*/
|
||||
public MethodInfo getMethodInfo(String methodName) {
|
||||
// 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()) {
|
||||
@@ -343,6 +460,15 @@ public class TypeInfo {
|
||||
*/
|
||||
public java.util.List<MethodInfo> getAllMethodOverloads(String methodName) {
|
||||
java.util.List<MethodInfo> overloads = new java.util.ArrayList<>();
|
||||
|
||||
// Check JS type first
|
||||
if (jsTypeInfo != null) {
|
||||
for (JSMethodInfo jsMethod : jsTypeInfo.getMethodOverloads(methodName)) {
|
||||
overloads.add(MethodInfo.fromJSMethod(jsMethod, this));
|
||||
}
|
||||
return overloads;
|
||||
}
|
||||
|
||||
if (javaClass == null) return overloads;
|
||||
try {
|
||||
for (java.lang.reflect.Method m : javaClass.getMethods()) {
|
||||
@@ -507,9 +633,18 @@ public class TypeInfo {
|
||||
|
||||
/**
|
||||
* Get FieldInfo for a field by name. Returns null if not found.
|
||||
* Creates a synthetic FieldInfo based on reflection data.
|
||||
* Creates a synthetic FieldInfo based on reflection data or JS type data.
|
||||
*/
|
||||
public FieldInfo getFieldInfo(String fieldName) {
|
||||
// Check JS type first
|
||||
if (jsTypeInfo != null) {
|
||||
JSFieldInfo jsField = jsTypeInfo.getField(fieldName);
|
||||
if (jsField != null) {
|
||||
return FieldInfo.fromJSField(jsField, this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (javaClass == null) return null;
|
||||
try {
|
||||
for (java.lang.reflect.Field f : javaClass.getFields()) {
|
||||
|
||||
Reference in new issue
Block a user