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').ResolvedIdent} ResolvedIdent
|
||||||
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
|
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
|
||||||
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
const { Expression } = require("./Expression");
|
||||||
const { AnyType, AnyMethod, MethodType } = require('../anys');
|
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 { NumberLiteral } = require('./literals/Number');
|
||||||
|
const { InstanceLiteral } = require('./literals/Instance')
|
||||||
const { isTypeAssignable } = require('../expression-resolver');
|
const { isTypeAssignable } = require('../expression-resolver');
|
||||||
const ParseProblem = require('../parsetypes/parse-problem');
|
const ParseProblem = require('../parsetypes/parse-problem');
|
||||||
|
const { ValidateInfo } = require('../body-types');
|
||||||
|
const { SourceConstructor } = require('../source-types');
|
||||||
|
|
||||||
class MethodCallExpression extends Expression {
|
class MethodCallExpression extends Expression {
|
||||||
/**
|
/**
|
||||||
@@ -29,73 +33,25 @@ class MethodCallExpression extends Expression {
|
|||||||
return AnyType.Instance;
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
if (!(type instanceof MethodType)) {
|
if (!(type instanceof MethodType)) {
|
||||||
ri.problems.push(ParseProblem.Error(this.instance.tokens, `Expression is not a named method'`));
|
// check if this is an aleternate or super constructor call: this() / super()
|
||||||
return AnyType.Instance;
|
const instance = this.instance.variables[0];
|
||||||
}
|
if (!(instance instanceof InstanceLiteral) || !(type instanceof JavaType)) {
|
||||||
const resolved_args = this.args.map(arg => arg.resolveExpression(ri));
|
ri.problems.push(ParseProblem.Error(this.instance.tokens, `Expression is not a named method'`));
|
||||||
|
|
||||||
// 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(this.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 => {
|
|
||||||
if (m.typeVariables.length) {
|
|
||||||
m = ReifiedMethod.build(m, arg_java_types);
|
|
||||||
}
|
|
||||||
return m;
|
|
||||||
});
|
|
||||||
|
|
||||||
// work out which methods are compatible with the call arguments
|
|
||||||
const compatible_methods = methods.filter(m => isCallCompatible(m, arg_types));
|
|
||||||
const return_types = new Set(compatible_methods.map(m => m.returnType));
|
|
||||||
|
|
||||||
if (!compatible_methods[0]) {
|
|
||||||
// if any of the arguments is AnyType, just return AnyType
|
|
||||||
if (arg_java_types.find(t => t instanceof AnyType)) {
|
|
||||||
return AnyType.Instance;
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
const methodlist = methods.map(m => m.label).join('\n- ');
|
let is_ctr = false;
|
||||||
const callargtypes = arg_java_types.map(t => t.fullyDottedTypeName).join(' , ');
|
if (ri instanceof ValidateInfo) {
|
||||||
ri.problems.push(ParseProblem.Error(this.instance.tokens,
|
is_ctr = ri.method instanceof SourceConstructor;
|
||||||
`No compatible method found. Tried to match argument types:\n- ( ${callargtypes} ) with:\n- ${methodlist}`
|
|
||||||
));
|
|
||||||
return AnyType.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compatible_methods.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 return_types.size > 1 ? AnyType.Instance : compatible_methods[0].returnType;
|
|
||||||
}
|
}
|
||||||
// see if we have an exact match
|
if (is_ctr) {
|
||||||
const callsig = `(${arg_java_types.map(t => t.typeSignature).join('')})`;
|
resolveConstructorCall(ri, type.constructors, this.args, () => this.instance.tokens);
|
||||||
const exact_match = compatible_methods.find(m => m.methodSignature.startsWith(callsig));
|
} else {
|
||||||
if (exact_match) {
|
ri.problems.push(ParseProblem.Error(this.instance.tokens, `'this'/'super' constructor calls can only be used as the first statement of a constructor`));
|
||||||
compatible_methods.splice(0, compatible_methods.length, exact_match);
|
|
||||||
}
|
}
|
||||||
|
return PrimitiveType.map.V;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compatible_methods.length > 1) {
|
return resolveMethodCall(ri, type.methods, this.args, () => this.instance.tokens);
|
||||||
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,
|
|
||||||
`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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens() {
|
tokens() {
|
||||||
@@ -103,9 +59,153 @@ class MethodCallExpression extends Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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)[]} */
|
||||||
|
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 reified_methods = methods.map(m => {
|
||||||
|
if (m.typeVariables.length) {
|
||||||
|
m = ReifiedMethod.build(m, arg_java_types);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
|
});
|
||||||
|
|
||||||
|
// work out which methods are compatible with the call arguments
|
||||||
|
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]) {
|
||||||
|
// if any of the arguments is AnyType, just return AnyType
|
||||||
|
if (arg_java_types.find(t => t instanceof AnyType)) {
|
||||||
|
return AnyType.Instance;
|
||||||
|
}
|
||||||
|
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(tokens(),
|
||||||
|
`No compatible method found. Tried to match argument types:\n- ( ${callargtypes} ) with:\n- ${methodlist}`
|
||||||
|
));
|
||||||
|
return AnyType.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (compatible_methods.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 return_types.size > 1 ? AnyType.Instance : compatible_methods[0].returnType;
|
||||||
|
}
|
||||||
|
// see if we have an exact match
|
||||||
|
const callsig = `(${arg_java_types.map(t => t.typeSignature).join('')})`;
|
||||||
|
const exact_match = compatible_methods.find(m => m.methodSignature.startsWith(callsig));
|
||||||
|
if (exact_match) {
|
||||||
|
compatible_methods.splice(0, compatible_methods.length, exact_match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
* @param {(JavaType | NumberLiteral)[]} arg_types
|
||||||
*/
|
*/
|
||||||
function isCallCompatible(m, arg_types) {
|
function isCallCompatible(m, arg_types) {
|
||||||
|
|||||||
Reference in New Issue
Block a user