mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
initial support for wildcard type arguments
This commit is contained in:
@@ -4,7 +4,7 @@
|
|||||||
*
|
*
|
||||||
* Each token also contains detailed state information used for completion suggestions.
|
* Each token also contains detailed state information used for completion suggestions.
|
||||||
*/
|
*/
|
||||||
const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, Field, Method, Parameter, Constructor, signatureToType } = require('java-mti');
|
const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, WildcardType, TypeVariable, Field, Method, Parameter, Constructor, signatureToType } = require('java-mti');
|
||||||
const { SourceMethod, SourceConstructor } = require('./source-type');
|
const { SourceMethod, SourceConstructor } = require('./source-type');
|
||||||
const ResolvedImport = require('./parsetypes/resolved-import');
|
const ResolvedImport = require('./parsetypes/resolved-import');
|
||||||
const ParseProblem = require('./parsetypes/parse-problem');
|
const ParseProblem = require('./parsetypes/parse-problem');
|
||||||
@@ -1203,11 +1203,41 @@ function isTypeAssignable(dest_type, value_type) {
|
|||||||
// generic types are also assignable to their raw counterparts
|
// generic types are also assignable to their raw counterparts
|
||||||
const valid_raw_types = valid_types.map(t => t.getRawType());
|
const valid_raw_types = valid_types.map(t => t.getRawType());
|
||||||
is_assignable = valid_raw_types.includes(dest_type);
|
is_assignable = valid_raw_types.includes(dest_type);
|
||||||
|
if (!is_assignable) {
|
||||||
|
// generic types are also assignable to compatible wildcard type bounds
|
||||||
|
if (dest_type.rawTypeSignature === value_type.rawTypeSignature) {
|
||||||
|
is_assignable = dest_type.typevars.every((dest_tv, idx) => isTypeArgumentCompatible(dest_tv, value_type.typevars[idx].type));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return is_assignable;
|
return is_assignable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {TypeVariable} dest_typevar
|
||||||
|
* @param {JavaType} value_typevar_type
|
||||||
|
*/
|
||||||
|
function isTypeArgumentCompatible(dest_typevar, value_typevar_type) {
|
||||||
|
if (dest_typevar.type instanceof WildcardType) {
|
||||||
|
if (!dest_typevar.type.bound) {
|
||||||
|
// unbounded wildcard types are compatible with everything
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (dest_typevar.type.bound.type === value_typevar_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (dest_typevar.type.bound.kind) {
|
||||||
|
case 'extends':
|
||||||
|
return isTypeAssignable(dest_typevar.type.bound.type, value_typevar_type);
|
||||||
|
case 'super':;
|
||||||
|
return isTypeAssignable(value_typevar_type, dest_typevar.type.bound.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return dest_typevar.type === value_typevar_type;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} ident
|
* @param {string} ident
|
||||||
* @param {ResolvedIdent} lhs
|
* @param {ResolvedIdent} lhs
|
||||||
@@ -1221,6 +1251,119 @@ function resolveEquality(tokens, ident, lhs, op, rhs) {
|
|||||||
return new ResolvedIdent(ident, [Value.build(ident, lhs, rhs, PrimitiveType.map.Z)]);
|
return new ResolvedIdent(ident, [Value.build(ident, lhs, rhs, PrimitiveType.map.Z)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {JavaType} lhs_type
|
||||||
|
* @param {JavaType} rhs_type
|
||||||
|
*/
|
||||||
|
function isTypeComparable(lhs_type, rhs_type) {
|
||||||
|
let is_comparable;
|
||||||
|
if (lhs_type.typeSignature === rhs_type.typeSignature) {
|
||||||
|
is_comparable = true;
|
||||||
|
} else if (lhs_type instanceof AnyType || rhs_type instanceof AnyType) {
|
||||||
|
is_comparable = true;
|
||||||
|
} else if (lhs_type instanceof PrimitiveType) {
|
||||||
|
const valid_rhs_type = {
|
||||||
|
Z: /^Z$/,
|
||||||
|
V: /^$/,
|
||||||
|
}[lhs_type.typeSignature] || /^[BSIJFDC]$/;
|
||||||
|
is_comparable = valid_rhs_type.test(rhs_type.typeSignature);
|
||||||
|
} else if (lhs_type instanceof NullType || rhs_type instanceof NullType) {
|
||||||
|
is_comparable = !(rhs_type instanceof PrimitiveType);
|
||||||
|
} else if (lhs_type instanceof ArrayType) {
|
||||||
|
const base_type = lhs_type.base;
|
||||||
|
const valid_array_types = base_type instanceof CEIType ? getTypeInheritanceList(base_type) : [base_type];
|
||||||
|
is_comparable = rhs_type.typeSignature === 'Ljava/lang/Object;'
|
||||||
|
|| (rhs_type instanceof ArrayType
|
||||||
|
&& rhs_type.arrdims === rhs_type.arrdims
|
||||||
|
&& valid_array_types.includes(rhs_type));
|
||||||
|
} else if (lhs_type instanceof CEIType && rhs_type instanceof CEIType) {
|
||||||
|
const lhs_types = getTypeInheritanceList(lhs_type);
|
||||||
|
const rhs_types = getTypeInheritanceList(rhs_type);
|
||||||
|
is_comparable = lhs_types.includes(rhs_type) || rhs_types.includes(lhs_type);
|
||||||
|
if (!is_comparable) {
|
||||||
|
if (lhs_type.rawTypeSignature === rhs_type.rawTypeSignature) {
|
||||||
|
is_comparable = lhs_type.typevars.every((tv, idx) => isTypeArgumentComparable(tv, rhs_type.typevars[idx]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is_comparable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {TypeVariable} a
|
||||||
|
* @param {TypeVariable} b
|
||||||
|
*/
|
||||||
|
function isTypeArgumentComparable(a, b) {
|
||||||
|
let a_type = a.type, b_type = b.type;
|
||||||
|
if (a_type === b_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (a_type instanceof WildcardType) {
|
||||||
|
if (!a_type.bound)
|
||||||
|
return true; // unbounded wildcard types are comparable with everything
|
||||||
|
if (a_type.bound.type.typeKind === 'interface')
|
||||||
|
return true; // interface bounds are comparable with everything
|
||||||
|
}
|
||||||
|
if (b_type instanceof WildcardType) {
|
||||||
|
if (!b_type.bound)
|
||||||
|
return true; // unbounded wildcard types are comparable with everything
|
||||||
|
if (b_type.bound.type.typeKind === 'interface')
|
||||||
|
return true; // interface bounds are comparable with everything
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {JavaType} type
|
||||||
|
* @param {JavaType} list_type
|
||||||
|
*/
|
||||||
|
function extendsFrom(type, list_type) {
|
||||||
|
if (!(list_type instanceof CEIType)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return list_type === type || getTypeInheritanceList(list_type).includes(type);
|
||||||
|
}
|
||||||
|
// each type argument can have 3 possible states
|
||||||
|
// - a extends, a super, a (exact)
|
||||||
|
// - b extends, b super, b (exact)
|
||||||
|
// we need to cover all combinations of a and b...
|
||||||
|
if (a_type instanceof WildcardType && a_type.bound.kind === 'extends') {
|
||||||
|
if (b_type instanceof WildcardType && b_type.bound.kind === 'extends') {
|
||||||
|
// both are extends - one must extend from the other
|
||||||
|
return extendsFrom(a_type.bound.type, b_type.bound.type) || extendsFrom(b_type.bound.type, a_type.bound.type);
|
||||||
|
}
|
||||||
|
else if (b_type instanceof WildcardType && b_type.bound.kind === 'super') {
|
||||||
|
// a extends, b super - b must extend from a
|
||||||
|
return extendsFrom(a_type.bound.type, b_type.bound.type);
|
||||||
|
} else {
|
||||||
|
// b is an exact type - b must extend from a
|
||||||
|
return extendsFrom(a_type.bound.type, b_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (a_type instanceof WildcardType && a_type.bound.kind === 'super') {
|
||||||
|
if (b_type instanceof WildcardType && b_type.bound.kind === 'super') {
|
||||||
|
// both are super - one must extend from the other
|
||||||
|
return extendsFrom(a_type.bound.type, b_type.bound.type) || extendsFrom(b_type.bound.type, a_type.bound.type);
|
||||||
|
}
|
||||||
|
else if (b_type instanceof WildcardType && b_type.bound.kind === 'extends') {
|
||||||
|
// a super, b extends - a must extend from b
|
||||||
|
return extendsFrom(b_type.bound.type, a_type.bound.type);
|
||||||
|
} else {
|
||||||
|
// b is an exact type - a must extend from b
|
||||||
|
return extendsFrom(b_type, a_type.bound.type);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// a is an exact type
|
||||||
|
if (b_type instanceof WildcardType && b_type.bound.kind === 'extends') {
|
||||||
|
// a exact, b extends - a must extend from b
|
||||||
|
return extendsFrom(b_type.bound.type, a_type);
|
||||||
|
}
|
||||||
|
else if (b_type instanceof WildcardType && b_type.bound.kind === 'super') {
|
||||||
|
// a exact, b super - b must extend from a
|
||||||
|
return extendsFrom(a_type, b_type.bound.type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TokenList} tokens
|
* @param {TokenList} tokens
|
||||||
* @param {Local|Parameter|Field|ArrayElement|Value} lhs
|
* @param {Local|Parameter|Field|ArrayElement|Value} lhs
|
||||||
@@ -1228,31 +1371,7 @@ function resolveEquality(tokens, ident, lhs, op, rhs) {
|
|||||||
* @param {Local|Parameter|Field|ArrayElement|Value} rhs
|
* @param {Local|Parameter|Field|ArrayElement|Value} rhs
|
||||||
*/
|
*/
|
||||||
function checkEqualityComparison(tokens, lhs, op, rhs) {
|
function checkEqualityComparison(tokens, lhs, op, rhs) {
|
||||||
let is_comparable;
|
const is_comparable = isTypeComparable(lhs.type, rhs.type);
|
||||||
if (lhs.type.typeSignature === rhs.type.typeSignature) {
|
|
||||||
is_comparable = true;
|
|
||||||
} else if (lhs.type instanceof AnyType || rhs.type instanceof AnyType) {
|
|
||||||
is_comparable = true;
|
|
||||||
} else if (lhs.type instanceof PrimitiveType) {
|
|
||||||
const valid_rhs_type = {
|
|
||||||
Z: /^Z$/,
|
|
||||||
V: /^$/,
|
|
||||||
}[lhs.type.typeSignature] || /^[BSIJFDC]$/;
|
|
||||||
is_comparable = valid_rhs_type.test(rhs.type.typeSignature);
|
|
||||||
} else if (lhs.type instanceof NullType || rhs.type instanceof NullType) {
|
|
||||||
is_comparable = !(rhs.type instanceof PrimitiveType);
|
|
||||||
} else if (lhs.type instanceof ArrayType) {
|
|
||||||
const base_type = lhs.type.base;
|
|
||||||
const valid_array_types = base_type instanceof CEIType ? getTypeInheritanceList(base_type) : [base_type];
|
|
||||||
is_comparable = rhs.type.typeSignature === 'Ljava/lang/Object;'
|
|
||||||
|| (rhs.type instanceof ArrayType
|
|
||||||
&& rhs.type.arrdims === rhs.type.arrdims
|
|
||||||
&& valid_array_types.includes(rhs.type));
|
|
||||||
} else if (lhs.type instanceof CEIType && rhs.type instanceof CEIType) {
|
|
||||||
const lhs_types = getTypeInheritanceList(lhs.type);
|
|
||||||
const rhs_types = getTypeInheritanceList(rhs.type);
|
|
||||||
is_comparable = lhs_types.includes(rhs.type) || rhs_types.includes(lhs.type);
|
|
||||||
}
|
|
||||||
if (!is_comparable) {
|
if (!is_comparable) {
|
||||||
addproblem(tokens, ParseProblem.Error(op, `Incomparable types: '${lhs.type.fullyDottedTypeName}' and '${rhs.type.fullyDottedTypeName}'`));
|
addproblem(tokens, ParseProblem.Error(op, `Incomparable types: '${lhs.type.fullyDottedTypeName}' and '${rhs.type.fullyDottedTypeName}'`));
|
||||||
}
|
}
|
||||||
@@ -1649,7 +1768,10 @@ function typeIdentList(tokens, method, imports, typemap) {
|
|||||||
*/
|
*/
|
||||||
function typeIdent(tokens, method, imports, typemap) {
|
function typeIdent(tokens, method, imports, typemap) {
|
||||||
if (tokens.current.kind !== 'ident') {
|
if (tokens.current.kind !== 'ident') {
|
||||||
return new UnresolvedType();
|
if (tokens.current.value === '?') {
|
||||||
|
return wildcardTypeArgument(tokens, method, imports, typemap);
|
||||||
|
}
|
||||||
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
const { types, package_name } = resolveTypeOrPackage(tokens.current.value, method._owner, imports, typemap);
|
const { types, package_name } = resolveTypeOrPackage(tokens.current.value, method._owner, imports, typemap);
|
||||||
let matches = new ResolvedIdent(tokens.current.value, [], [], types, package_name);
|
let matches = new ResolvedIdent(tokens.current.value, [], [], types, package_name);
|
||||||
@@ -1669,6 +1791,29 @@ function typeIdent(tokens, method, imports, typemap) {
|
|||||||
return matches.types[0] || new UnresolvedType(matches.source);
|
return matches.types[0] || new UnresolvedType(matches.source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {TokenList} tokens
|
||||||
|
* @param {SourceMC} method
|
||||||
|
* @param {ResolvedImport[]} imports
|
||||||
|
* @param {Map<string,JavaType>} typemap
|
||||||
|
*/
|
||||||
|
function wildcardTypeArgument(tokens, method, imports, typemap) {
|
||||||
|
tokens.expectValue('?');
|
||||||
|
let bound = null;
|
||||||
|
switch (tokens.current.value) {
|
||||||
|
case 'extends':
|
||||||
|
case 'super':
|
||||||
|
const kind = tokens.current.value;
|
||||||
|
tokens.inc();
|
||||||
|
bound = {
|
||||||
|
kind,
|
||||||
|
type: typeIdent(tokens, method, imports, typemap),
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new WildcardType(bound);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {TokenList} tokens
|
* @param {TokenList} tokens
|
||||||
* @param {Local[]} locals
|
* @param {Local[]} locals
|
||||||
|
|||||||
Reference in New Issue
Block a user