fix support for resolving type variables in method declarations

This commit is contained in:
Dave Holoway
2020-06-15 16:07:23 +01:00
parent 549473d765
commit 2f3349c359
4 changed files with 53 additions and 34 deletions

View File

@@ -554,19 +554,19 @@ function typeBody(type, tokens, owner, imports, typemap) {
/** /**
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceAnnotation[]} annotations * @param {SourceAnnotation[]} annotations
* @param {TypeVariable[]} type_variables * @param {TypeVariable[]} type_vars
* @param {SourceType} type * @param {SourceType} type
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function fmc(modifiers, annotations, type_variables, type, tokens, imports, typemap) { function fmc(modifiers, annotations, type_vars, type, tokens, imports, typemap) {
let decl_type_ident = typeIdent(tokens, type, imports, typemap); let decl_type_ident = typeIdent(tokens, type, imports, typemap, { no_array_qualifiers: false, type_vars });
if (decl_type_ident.resolved.rawTypeSignature === type.rawTypeSignature) { if (decl_type_ident.resolved.rawTypeSignature === type.rawTypeSignature) {
if (tokens.current.value === '(') { if (tokens.current.value === '(') {
// constructor // constructor
const { parameters, throws, body } = methodDeclaration(type, tokens, imports, typemap); const { parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap);
const ctr = new SourceConstructor(type, modifiers, parameters, throws, body); const ctr = new SourceConstructor(type, type_vars, modifiers, parameters, throws, body);
type.constructors.push(ctr); type.constructors.push(ctr);
return; return;
} }
@@ -577,15 +577,15 @@ function fmc(modifiers, annotations, type_variables, type, tokens, imports, type
addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`)) addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`))
} }
if (tokens.current.value === '(') { if (tokens.current.value === '(') {
const { postnamearrdims, parameters, throws, body } = methodDeclaration(type, tokens, imports, typemap); const { postnamearrdims, parameters, throws, body } = methodDeclaration(type_vars, type, tokens, imports, typemap);
if (postnamearrdims > 0) { if (postnamearrdims > 0) {
decl_type_ident.resolved = new ArrayType(decl_type_ident.resolved, postnamearrdims); decl_type_ident.resolved = new ArrayType(decl_type_ident.resolved, postnamearrdims);
} }
const method = new SourceMethod(type, modifiers, annotations, decl_type_ident, name, parameters, throws, body); const method = new SourceMethod(type, type_vars, modifiers, annotations, decl_type_ident, name, parameters, throws, body);
type.methods.push(method); type.methods.push(method);
} else { } else {
if (name) { if (name) {
if (type_variables.length) { if (type_vars.length) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Fields cannot declare type variables`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Fields cannot declare type variables`));
} }
const locals = var_ident_list(modifiers, decl_type_ident, name, tokens, new MethodDeclarations(), type, imports, typemap); const locals = var_ident_list(modifiers, decl_type_ident, name, tokens, new MethodDeclarations(), type, imports, typemap);
@@ -641,7 +641,7 @@ function annotation(tokens, scope, imports, typemap) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Type identifier expected`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Type identifier expected`));
return; return;
} }
let annotation_type = typeIdent(tokens, scope, imports, typemap, false); let annotation_type = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers: true, type_vars:[]});
if (tokens.isValue('(')) { if (tokens.isValue('(')) {
if (!tokens.isValue(')')) { if (!tokens.isValue(')')) {
expressionList(tokens, new MethodDeclarations(), scope, imports, typemap); expressionList(tokens, new MethodDeclarations(), scope, imports, typemap);
@@ -729,17 +729,18 @@ function typeVariableList(owner, tokens, scope, imports, typemap) {
/** /**
* @param {TypeVariable[]} type_vars
* @param {SourceType} owner * @param {SourceType} owner
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function methodDeclaration(owner, tokens, imports, typemap) { function methodDeclaration(type_vars, owner, tokens, imports, typemap) {
tokens.expectValue('('); tokens.expectValue('(');
let parameters = [], throws = [], postnamearrdims = 0, body = null; let parameters = [], throws = [], postnamearrdims = 0, body = null;
if (!tokens.isValue(')')) { if (!tokens.isValue(')')) {
for(;;) { for(;;) {
const p = parameterDeclaration(owner, tokens, imports, typemap); const p = parameterDeclaration(type_vars, owner, tokens, imports, typemap);
parameters.push(p); parameters.push(p);
if (tokens.isValue(',')) { if (tokens.isValue(',')) {
continue; continue;
@@ -767,19 +768,20 @@ function methodDeclaration(owner, tokens, imports, typemap) {
} }
/** /**
* @param {TypeVariable[]} type_vars
* @param {SourceType} owner * @param {SourceType} owner
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function parameterDeclaration(owner, tokens, imports, typemap) { function parameterDeclaration(type_vars, owner, tokens, imports, typemap) {
const modifiers = []; const modifiers = [];
while (tokens.current.kind === 'modifier') { while (tokens.current.kind === 'modifier') {
modifiers.push(tokens.current); modifiers.push(tokens.current);
tokens.inc(); tokens.inc();
} }
checkLocalModifiers(tokens, modifiers); checkLocalModifiers(tokens, modifiers);
let type_ident = typeIdent(tokens, owner, imports, typemap); let type_ident = typeIdent(tokens, owner, imports, typemap, { no_array_qualifiers: false, type_vars });
const varargs = tokens.isValue('...'); const varargs = tokens.isValue('...');
let name_token = tokens.current; let name_token = tokens.current;
if (!tokens.isKind('ident')) { if (!tokens.isKind('ident')) {
@@ -2353,7 +2355,7 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
function newTerm(tokens, mdecls, scope, imports, typemap) { function newTerm(tokens, mdecls, scope, imports, typemap) {
tokens.expectValue('new'); tokens.expectValue('new');
const type_start_token = tokens.idx; const type_start_token = tokens.idx;
const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, false); const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]});
if (ctr_type instanceof AnyType) { if (ctr_type instanceof AnyType) {
const toks = tokens.tokens.slice(type_start_token, tokens.idx); const toks = tokens.tokens.slice(type_start_token, tokens.idx);
addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`)); addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`));
@@ -2843,7 +2845,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
if (type) { if (type) {
matches.types = [type]; matches.types = [type];
} else { } else {
const { types, package_name } = resolveTypeOrPackage(ident, scope, imports, typemap); const { types, package_name } = resolveTypeOrPackage(ident, [], scope, imports, typemap);
matches.types = types; matches.types = types;
matches.package_name = package_name; matches.package_name = package_name;
} }

View File

@@ -1,4 +1,4 @@
const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter } = require('java-mti'); const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable } = require('java-mti');
const { Token } = require('./tokenizer'); const { Token } = require('./tokenizer');
/** /**
@@ -127,14 +127,16 @@ class SourceField extends Field {
class SourceConstructor extends Constructor { class SourceConstructor extends Constructor {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {TypeVariable[]} type_vars
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceParameter[]} parameters * @param {SourceParameter[]} parameters
* @param {JavaType[]} throws * @param {JavaType[]} throws
* @param {Token[]} body * @param {Token[]} body
*/ */
constructor(owner, modifiers, parameters, throws, body) { constructor(owner, type_vars, modifiers, parameters, throws, body) {
super(owner, modifiers.map(m => m.value), ''); super(owner, modifiers.map(m => m.value), '');
this.owner = owner; this.owner = owner;
this.typeVars = type_vars;
this.modifierTokens = modifiers; this.modifierTokens = modifiers;
this.sourceParameters = parameters; this.sourceParameters = parameters;
this.throws = throws; this.throws = throws;
@@ -162,11 +164,16 @@ class SourceConstructor extends Constructor {
get returnType() { get returnType() {
return this.owner; return this.owner;
} }
get typeVariables() {
return this.typeVars;
}
} }
class SourceMethod extends Method { class SourceMethod extends Method {
/** /**
* @param {SourceType} owner * @param {SourceType} owner
* @param {TypeVariable[]} type_vars
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {SourceAnnotation[]} annotations * @param {SourceAnnotation[]} annotations
* @param {SourceTypeIdent} method_type_ident * @param {SourceTypeIdent} method_type_ident
@@ -175,10 +182,11 @@ class SourceMethod extends Method {
* @param {JavaType[]} throws * @param {JavaType[]} throws
* @param {Token[]} body * @param {Token[]} body
*/ */
constructor(owner, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) { constructor(owner, type_vars, modifiers, annotations, method_type_ident, name_token, parameters, throws, body) {
super(owner, name_token ? name_token.value : '', modifiers.map(m => m.value), ''); super(owner, name_token ? name_token.value : '', modifiers.map(m => m.value), '');
this.annotations = annotations; this.annotations = annotations;
this.owner = owner; this.owner = owner;
this.typeVars = type_vars;
this.modifierTokens = modifiers; this.modifierTokens = modifiers;
this.returnTypeIdent = method_type_ident; this.returnTypeIdent = method_type_ident;
this.nameToken = name_token; this.nameToken = name_token;
@@ -202,12 +210,13 @@ class SourceMethod extends Method {
return this.sourceParameters; return this.sourceParameters;
} }
/**
* @returns {JavaType}
*/
get returnType() { get returnType() {
return this.returnTypeIdent.resolved; return this.returnTypeIdent.resolved;
} }
get typeVariables() {
return this.typeVars;
}
} }
class SourceInitialiser extends MethodBase { class SourceInitialiser extends MethodBase {

View File

@@ -1,7 +1,7 @@
/** /**
* @typedef {Map<string,JavaType>} TypeMap * @typedef {Map<string,JavaType>} TypeMap
*/ */
const { JavaType, PrimitiveType, ArrayType, CEIType, MethodBase } = require('java-mti'); const { JavaType, PrimitiveType, ArrayType, CEIType, MethodBase, TypeVariable } = require('java-mti');
const { ResolvedImport } = require('./import-resolver'); const { ResolvedImport } = require('./import-resolver');
const ResolvedType = require('./parsetypes/resolved-type'); const ResolvedType = require('./parsetypes/resolved-type');
@@ -260,15 +260,21 @@ function resolveTypeIdents(types, fully_qualified_scope, resolved_imports, typem
/** /**
* *
* @param {string} ident * @param {string} ident
* @param {TypeVariable[]} type_variables
* @param {CEIType|MethodBase} scope * @param {CEIType|MethodBase} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function resolveTypeOrPackage(ident, scope, imports, typemap) { function resolveTypeOrPackage(ident, type_variables, scope, imports, typemap) {
const types = []; const types = [];
let package_name = ''; let package_name = '';
if (scope instanceof MethodBase) { const tv = type_variables.find(tv => tv.name === ident);
if (tv) {
types.push(tv.type);
}
if (!types[0] && scope instanceof MethodBase) {
// is it a type variable in the current scope // is it a type variable in the current scope
const tv = scope.typeVariables.find(tv => tv.name === ident); const tv = scope.typeVariables.find(tv => tv.name === ident);
if (tv) { if (tv) {
@@ -276,7 +282,7 @@ function resolveTypeOrPackage(ident, scope, imports, typemap) {
} }
} }
if (scope) { if (!types[0] && scope) {
// is it an enclosed type of the currently scoped type or any outer type // is it an enclosed type of the currently scoped type or any outer type
const scoped_type = scope instanceof CEIType ? scope : scope.owner; const scoped_type = scope instanceof CEIType ? scope : scope.owner;
const scopes = scoped_type.shortSignature.split('$'); const scopes = scoped_type.shortSignature.split('$');
@@ -293,7 +299,7 @@ function resolveTypeOrPackage(ident, scope, imports, typemap) {
} }
} }
if (scope instanceof CEIType) { if (!types[0] && scope instanceof CEIType) {
// is it a type variable of the currently scoped type // is it a type variable of the currently scoped type
const tv = scope.typeVariables.find(tv => tv.name === ident); const tv = scope.typeVariables.find(tv => tv.name === ident);
if (tv) { if (tv) {

View File

@@ -1,4 +1,4 @@
const { ArrayType, CEIType, JavaType, PrimitiveType, MethodBase, WildcardType } = require('java-mti'); const { ArrayType, CEIType, JavaType, PrimitiveType, MethodBase, WildcardType, TypeVariable } = require('java-mti');
const { SourceTypeIdent, SourceMethod, SourceConstructor, SourceInitialiser } = require('./source-type'); const { SourceTypeIdent, SourceMethod, SourceConstructor, SourceInitialiser } = require('./source-type');
const ResolvedImport = require('./parsetypes/resolved-import'); const ResolvedImport = require('./parsetypes/resolved-import');
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
@@ -32,11 +32,11 @@ function typeIdentList(tokens, scope, imports, typemap) {
* @param {CEIType|MethodBase} scope * @param {CEIType|MethodBase} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @param {boolean} allow_array_qualifiers * @param {{no_array_qualifiers:boolean, type_vars:TypeVariable[]}} [opts]
*/ */
function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) { function typeIdent(tokens, scope, imports, typemap, opts) {
tokens.mark(); tokens.mark();
const type = singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers); const type = singleTypeIdent(tokens, scope, imports, typemap, opts);
return new SourceTypeIdent(tokens.markEnd(), type); return new SourceTypeIdent(tokens.markEnd(), type);
} }
@@ -45,15 +45,15 @@ function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = tru
* @param {CEIType|MethodBase} scope * @param {CEIType|MethodBase} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @param {boolean} allow_array_qualifiers * @param {{no_array_qualifiers:boolean, type_vars: TypeVariable[]}} [opts]
*/ */
function singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) { function singleTypeIdent(tokens, scope, imports, typemap, opts) {
/** @type {JavaType[]} */ /** @type {JavaType[]} */
let types = [], package_name = ''; let types = [], package_name = '';
tokens.mark(); tokens.mark();
switch(tokens.current.kind) { switch(tokens.current.kind) {
case 'ident': case 'ident':
({ types, package_name } = resolveTypeOrPackage(tokens.current.value, scope, imports, typemap)); ({ types, package_name } = resolveTypeOrPackage(tokens.current.value, opts ? opts.type_vars : [], scope, imports, typemap));
break; break;
case 'primitive-type': case 'primitive-type':
types.push(PrimitiveType.fromName(tokens.current.value)); types.push(PrimitiveType.fromName(tokens.current.value));
@@ -84,7 +84,9 @@ function singleTypeIdent(tokens, scope, imports, typemap, allow_array_qualifiers
types.push(anytype); types.push(anytype);
} }
if (allow_array_qualifiers && tokens.isValue('[')) { // allow array qualifiers unless specifically disabled
const allow_array_qualifiers = !opts || !opts.no_array_qualifiers;
if ( allow_array_qualifiers && tokens.isValue('[')) {
let arrdims = 0; let arrdims = 0;
for(;;) { for(;;) {
arrdims++; arrdims++;