mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
add support for this() and super() constructor calls
This commit is contained in:
@@ -1,13 +1,17 @@
|
||||
/**
|
||||
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
|
||||
* @typedef {import('../tokenizer').Token} Token
|
||||
*/
|
||||
const { Expression } = require("./Expression");
|
||||
const { AnyType, AnyMethod, MethodType } = require('../anys');
|
||||
const { ArrayType, JavaType, Method, ReifiedMethod } = require('java-mti');
|
||||
const { ArrayType, JavaType, Method,PrimitiveType, ReifiedConstructor, ReifiedMethod, Constructor } = require('java-mti');
|
||||
const { NumberLiteral } = require('./literals/Number');
|
||||
const { InstanceLiteral } = require('./literals/Instance')
|
||||
const { isTypeAssignable } = require('../expression-resolver');
|
||||
const ParseProblem = require('../parsetypes/parse-problem');
|
||||
const { ValidateInfo } = require('../body-types');
|
||||
const { SourceConstructor } = require('../source-types');
|
||||
|
||||
class MethodCallExpression extends Expression {
|
||||
/**
|
||||
@@ -29,10 +33,40 @@ class MethodCallExpression extends Expression {
|
||||
return AnyType.Instance;
|
||||
}
|
||||
if (!(type instanceof MethodType)) {
|
||||
// check if this is an aleternate or super constructor call: this() / super()
|
||||
const instance = this.instance.variables[0];
|
||||
if (!(instance instanceof InstanceLiteral) || !(type instanceof JavaType)) {
|
||||
ri.problems.push(ParseProblem.Error(this.instance.tokens, `Expression is not a named method'`));
|
||||
return AnyType.Instance;
|
||||
}
|
||||
const resolved_args = this.args.map(arg => arg.resolveExpression(ri));
|
||||
let is_ctr = false;
|
||||
if (ri instanceof ValidateInfo) {
|
||||
is_ctr = ri.method instanceof SourceConstructor;
|
||||
}
|
||||
if (is_ctr) {
|
||||
resolveConstructorCall(ri, type.constructors, this.args, () => this.instance.tokens);
|
||||
} else {
|
||||
ri.problems.push(ParseProblem.Error(this.instance.tokens, `'this'/'super' constructor calls can only be used as the first statement of a constructor`));
|
||||
}
|
||||
return PrimitiveType.map.V;
|
||||
}
|
||||
|
||||
return resolveMethodCall(ri, type.methods, this.args, () => this.instance.tokens);
|
||||
}
|
||||
|
||||
tokens() {
|
||||
return this.instance.tokens;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ResolveInfo} ri
|
||||
* @param {Method[]} methods
|
||||
* @param {ResolvedIdent[]} args
|
||||
* @param {() => Token[]} tokens
|
||||
*/
|
||||
function resolveMethodCall(ri, methods, args, tokens) {
|
||||
const resolved_args = args.map(arg => arg.resolveExpression(ri));
|
||||
|
||||
// all the arguments must be typed expressions or number literals
|
||||
/** @type {(JavaType|NumberLiteral)[]} */
|
||||
@@ -42,14 +76,14 @@ class MethodCallExpression extends Expression {
|
||||
arg_types.push(a);
|
||||
return;
|
||||
}
|
||||
ri.problems.push(ParseProblem.Error(this.args[idx].tokens, `Expression expected`))
|
||||
ri.problems.push(ParseProblem.Error(args[idx].tokens, `Expression expected`))
|
||||
// use AnyType for this argument
|
||||
arg_types.push(AnyType.Instance);
|
||||
});
|
||||
|
||||
// reify any methods with type-variables
|
||||
const arg_java_types = arg_types.map(a => a instanceof NumberLiteral ? a.type : a);
|
||||
const methods = type.methods.map(m => {
|
||||
const reified_methods = methods.map(m => {
|
||||
if (m.typeVariables.length) {
|
||||
m = ReifiedMethod.build(m, arg_java_types);
|
||||
}
|
||||
@@ -57,7 +91,7 @@ class MethodCallExpression extends Expression {
|
||||
});
|
||||
|
||||
// work out which methods are compatible with the call arguments
|
||||
const compatible_methods = methods.filter(m => isCallCompatible(m, arg_types));
|
||||
const compatible_methods = reified_methods.filter(m => isCallCompatible(m, arg_types));
|
||||
const return_types = new Set(compatible_methods.map(m => m.returnType));
|
||||
|
||||
if (!compatible_methods[0]) {
|
||||
@@ -65,9 +99,9 @@ class MethodCallExpression extends Expression {
|
||||
if (arg_java_types.find(t => t instanceof AnyType)) {
|
||||
return AnyType.Instance;
|
||||
}
|
||||
const methodlist = methods.map(m => m.label).join('\n- ');
|
||||
const methodlist = reified_methods.map(m => m.label).join('\n- ');
|
||||
const callargtypes = arg_java_types.map(t => t.fullyDottedTypeName).join(' , ');
|
||||
ri.problems.push(ParseProblem.Error(this.instance.tokens,
|
||||
ri.problems.push(ParseProblem.Error(tokens(),
|
||||
`No compatible method found. Tried to match argument types:\n- ( ${callargtypes} ) with:\n- ${methodlist}`
|
||||
));
|
||||
return AnyType.Instance;
|
||||
@@ -89,23 +123,89 @@ class MethodCallExpression extends Expression {
|
||||
if (compatible_methods.length > 1) {
|
||||
const methodlist = compatible_methods.map(m => m.label).join('\n- ');
|
||||
const callargtypes = arg_java_types.map(t => t.fullyDottedTypeName).join(' , ');
|
||||
ri.problems.push(ParseProblem.Error(this.instance.tokens,
|
||||
ri.problems.push(ParseProblem.Error(tokens(),
|
||||
`Ambiguous method call. Matched argument types:\n- ( ${callargtypes} ) with:\n- ${methodlist}`
|
||||
));
|
||||
return return_types.size > 1 ? AnyType.Instance : compatible_methods[0].returnType;
|
||||
}
|
||||
|
||||
return compatible_methods[0].returnType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {ResolveInfo} ri
|
||||
* @param {Constructor[]} constructors
|
||||
* @param {ResolvedIdent[]} args
|
||||
* @param {() => Token[]} tokens
|
||||
*/
|
||||
function resolveConstructorCall(ri, constructors, args, tokens) {
|
||||
const resolved_args = args.map(arg => arg.resolveExpression(ri));
|
||||
|
||||
// all the arguments must be typed expressions or number literals
|
||||
/** @type {(JavaType|NumberLiteral)[]} */
|
||||
const arg_types = [];
|
||||
resolved_args.forEach((a, idx) => {
|
||||
if (a instanceof JavaType || a instanceof NumberLiteral) {
|
||||
arg_types.push(a);
|
||||
return;
|
||||
}
|
||||
ri.problems.push(ParseProblem.Error(args[idx].tokens, `Expression expected`))
|
||||
// use AnyType for this argument
|
||||
arg_types.push(AnyType.Instance);
|
||||
});
|
||||
|
||||
// reify any methods with type-variables
|
||||
const arg_java_types = arg_types.map(a => a instanceof NumberLiteral ? a.type : a);
|
||||
const reifed_ctrs = constructors.map(c => {
|
||||
if (c.typeVariables.length) {
|
||||
c = ReifiedConstructor.build(c, arg_java_types);
|
||||
}
|
||||
return c;
|
||||
});
|
||||
|
||||
// work out which methods are compatible with the call arguments
|
||||
const compatible_ctrs = reifed_ctrs.filter(m => isCallCompatible(m, arg_types));
|
||||
|
||||
if (!compatible_ctrs[0]) {
|
||||
// if any of the arguments is AnyType, just ignore the call
|
||||
if (arg_java_types.find(t => t instanceof AnyType)) {
|
||||
return;
|
||||
}
|
||||
const ctrlist = reifed_ctrs.map(m => m.label).join('\n- ');
|
||||
const callargtypes = arg_java_types.map(t => t.fullyDottedTypeName).join(' , ');
|
||||
ri.problems.push(ParseProblem.Error(tokens(),
|
||||
`No compatible constructor found. Tried to match argument types:\n- ( ${callargtypes} ) with:\n- ${ctrlist}`
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
tokens() {
|
||||
return this.instance.tokens;
|
||||
if (compatible_ctrs.length > 1) {
|
||||
// if any of the arguments is AnyType, return the known return-type or AnyType
|
||||
if (arg_java_types.find(t => t instanceof AnyType)) {
|
||||
return;
|
||||
}
|
||||
// see if we have an exact match
|
||||
const callsig = `(${arg_java_types.map(t => t.typeSignature).join('')})`;
|
||||
const exact_match = compatible_ctrs.find(m => m.methodSignature.startsWith(callsig));
|
||||
if (exact_match) {
|
||||
compatible_ctrs.splice(0, compatible_ctrs.length, exact_match);
|
||||
}
|
||||
}
|
||||
|
||||
if (compatible_ctrs.length > 1) {
|
||||
const ctrlist = compatible_ctrs.map(m => m.label).join('\n- ');
|
||||
const callargtypes = arg_java_types.map(t => t.fullyDottedTypeName).join(' , ');
|
||||
ri.problems.push(ParseProblem.Error(tokens(),
|
||||
`Ambiguous constructor call. Matched argument types:\n- ( ${callargtypes} ) with:\n- ${ctrlist}`
|
||||
));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Method} m
|
||||
* @param {Method|Constructor} m
|
||||
* @param {(JavaType | NumberLiteral)[]} arg_types
|
||||
*/
|
||||
function isCallCompatible(m, arg_types) {
|
||||
|
||||
Reference in New Issue
Block a user