mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
add support for parsing and validating anonymous types
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
const { CEIType, PrimitiveType, ArrayType, UnresolvedType, TypeVariable, Field, Method } = require('java-mti');
|
const { CEIType, PrimitiveType, ArrayType, UnresolvedType, TypeVariable, Field, Method } = require('java-mti');
|
||||||
const { SourceType, SourceTypeIdent, SourceField, SourceMethod, SourceConstructor, SourceInitialiser, SourceParameter, SourceAnnotation,
|
const { SourceType, SourceTypeIdent, SourceField, SourceMethod, SourceConstructor, SourceInitialiser, SourceParameter, SourceAnnotation,
|
||||||
SourceUnit, SourcePackage, SourceImport, SourceArrayType, FixedLengthArrayType } = require('./source-types');
|
SourceUnit, SourcePackage, SourceImport, SourceArrayType, FixedLengthArrayType, NamedSourceType, AnonymousSourceType } = require('./source-types');
|
||||||
const ResolvedImport = require('./parsetypes/resolved-import');
|
const ResolvedImport = require('./parsetypes/resolved-import');
|
||||||
const ParseProblem = require('./parsetypes/parse-problem');
|
const ParseProblem = require('./parsetypes/parse-problem');
|
||||||
const { tokenize, Token } = require('./tokenizer');
|
const { tokenize, Token } = require('./tokenizer');
|
||||||
@@ -86,6 +86,23 @@ function flattenBlocks(blocks, isMethod) {
|
|||||||
}, [])
|
}, [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SourceType} type
|
||||||
|
* @param {ResolvedImport[]} imports
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
|
*/
|
||||||
|
function parseTypeMethods(type, imports, typemap) {
|
||||||
|
type.initers.forEach(i => {
|
||||||
|
i.parsed = parseBody(i, imports, typemap);
|
||||||
|
})
|
||||||
|
type.constructors.forEach(c => {
|
||||||
|
c.parsed = parseBody(c, imports, typemap);
|
||||||
|
})
|
||||||
|
type.sourceMethods.forEach(m => {
|
||||||
|
m.parsed = parseBody(m, imports, typemap);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SourceMethod | SourceConstructor | SourceInitialiser} method
|
* @param {SourceMethod | SourceConstructor | SourceInitialiser} method
|
||||||
* @param {ResolvedImport[]} imports
|
* @param {ResolvedImport[]} imports
|
||||||
@@ -97,16 +114,15 @@ function parseBody(method, imports, typemap) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const tokenlist = new TokenList(flattenBlocks(body_tokens, true));
|
const tokenlist = new TokenList(flattenBlocks(body_tokens, true));
|
||||||
let block = null;
|
|
||||||
let mdecls = new MethodDeclarations();
|
let mdecls = new MethodDeclarations();
|
||||||
try {
|
try {
|
||||||
block = statementBlock(tokenlist, mdecls, method, imports, typemap);
|
method.body.block = statementBlock(tokenlist, mdecls, method, imports, typemap);
|
||||||
checkStatementBlock(block, method, typemap, tokenlist.problems);
|
checkStatementBlock(method.body.block, method, typemap, tokenlist.problems);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
addproblem(tokenlist, ParseProblem.Information(tokenlist.current, `Parse failed: ${err.message}`));
|
addproblem(tokenlist, ParseProblem.Information(tokenlist.current, `Parse failed: ${err.message}`));
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
block,
|
block: method.body.block,
|
||||||
problems: tokenlist.problems,
|
problems: tokenlist.problems,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +184,7 @@ function extractSourceTypes(tokens, typemap) {
|
|||||||
kind_token = findTokenAt(m.index),
|
kind_token = findTokenAt(m.index),
|
||||||
name_token = findTokenAt(m.index + m[0].match(/\w+$/).index),
|
name_token = findTokenAt(m.index + m[0].match(/\w+$/).index),
|
||||||
outer_type = type_stack[0] && type_stack[0].source_type,
|
outer_type = type_stack[0] && type_stack[0].source_type,
|
||||||
source_type = new SourceType(package_name, outer_type, '', [], typeKind, kind_token, name_token, typemap);
|
source_type = new NamedSourceType(package_name, outer_type, '', [], typeKind, kind_token, name_token, typemap);
|
||||||
|
|
||||||
type_stack.unshift({
|
type_stack.unshift({
|
||||||
source_type,
|
source_type,
|
||||||
@@ -690,17 +706,17 @@ function typeDeclaration(package_name, scope, docs, modifiers, typeKind, kind_to
|
|||||||
addproblem(tokens, ParseProblem.Error(tokens.current, `Type identifier expected`));
|
addproblem(tokens, ParseProblem.Error(tokens.current, `Type identifier expected`));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const type_short_sig = SourceType.getShortSignature(package_name, scope, name.value);
|
const type_short_sig = NamedSourceType.getShortSignature(package_name, scope, name.value);
|
||||||
// the source type object should already exist in the type map
|
// the source type object should already exist in the type map
|
||||||
/** @type {SourceType} */
|
/** @type {NamedSourceType} */
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
let type = typemap.get(type_short_sig);
|
let type = typemap.get(type_short_sig);
|
||||||
if (type instanceof SourceType) {
|
if (type instanceof NamedSourceType) {
|
||||||
// update the missing parts
|
// update the missing parts
|
||||||
type.setModifierTokens(modifiers);
|
type.setModifierTokens(modifiers);
|
||||||
type.docs = docs;
|
type.docs = docs;
|
||||||
} else {
|
} else {
|
||||||
type = new SourceType(package_name, scope, docs, modifiers, typeKind, kind_token, name, typemap);
|
type = new NamedSourceType(package_name, scope, docs, modifiers, typeKind, kind_token, name, typemap);
|
||||||
}
|
}
|
||||||
type.typeVariables = tokens.current.value === '<'
|
type.typeVariables = tokens.current.value === '<'
|
||||||
? typeVariableList(type, tokens, scope, imports, typemap)
|
? typeVariableList(type, tokens, scope, imports, typemap)
|
||||||
@@ -1608,8 +1624,14 @@ function newTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
newtokens = tokens.markEnd();
|
newtokens = tokens.markEnd();
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
if (tokens.current.value === '{') {
|
if (tokens.current.value === '{') {
|
||||||
// anonymous type - just skip for now
|
// anonymous type
|
||||||
type_body = skipBody(tokens);
|
tokens.consume();
|
||||||
|
type_body = new AnonymousSourceType(ctr_type, scope, typemap);
|
||||||
|
typemap.set(type_body.shortSignature, type_body);
|
||||||
|
typeBody(type_body, tokens, mdecls, imports, typemap);
|
||||||
|
tokens.expectValue('}');
|
||||||
|
// perform an immediate parse of all the methods in the anonymous class
|
||||||
|
parseTypeMethods(type_body, imports, typemap);
|
||||||
}
|
}
|
||||||
return new ResolvedIdent(match.source, [new NewObject(new_token, ctr_type, open_bracket, ctr_args, commas, type_body)], [], [], '', newtokens);
|
return new ResolvedIdent(match.source, [new NewObject(new_token, ctr_type, open_bracket, ctr_args, commas, type_body)], [], [], '', newtokens);
|
||||||
}
|
}
|
||||||
@@ -1907,6 +1929,6 @@ function findIdentifier(token, mdecls, scope, imports, typemap) {
|
|||||||
|
|
||||||
|
|
||||||
exports.addproblem = addproblem;
|
exports.addproblem = addproblem;
|
||||||
exports.parseBody = parseBody;
|
exports.parseTypeMethods = parseTypeMethods;
|
||||||
exports.parse = parse;
|
exports.parse = parse;
|
||||||
exports.flattenBlocks = flattenBlocks;
|
exports.flattenBlocks = flattenBlocks;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* @typedef {import('../tokenizer').Token} Token
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
* @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('../source-types').AnonymousSourceType} AnonymousSourceType
|
||||||
* @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent
|
* @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent
|
||||||
* @typedef {import('java-mti').JavaType} JavaType
|
* @typedef {import('java-mti').JavaType} JavaType
|
||||||
*/
|
*/
|
||||||
@@ -64,7 +65,7 @@ class NewObject extends Expression {
|
|||||||
* @param {Token} open_bracket
|
* @param {Token} open_bracket
|
||||||
* @param {ResolvedIdent[]} ctr_args
|
* @param {ResolvedIdent[]} ctr_args
|
||||||
* @param {Token[]} commas
|
* @param {Token[]} commas
|
||||||
* @param {Token[]} type_body
|
* @param {AnonymousSourceType} type_body
|
||||||
*/
|
*/
|
||||||
constructor(new_token, object_type, open_bracket, ctr_args, commas, type_body) {
|
constructor(new_token, object_type, open_bracket, ctr_args, commas, type_body) {
|
||||||
super();
|
super();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
const { CEIType, JavaType, PrimitiveType, ArrayType, TypeVariableType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable, TypeArgument } = require('java-mti');
|
const { CEIType, JavaType, PrimitiveType, ArrayType, TypeVariableType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable, TypeArgument } = require('java-mti');
|
||||||
|
const { AnyType } = require('./anys');
|
||||||
const { Token } = require('./tokenizer');
|
const { Token } = require('./tokenizer');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,24 +60,14 @@ function createImplicitEnumMethods(enum_type, typemap) {
|
|||||||
|
|
||||||
class SourceType extends CEIType {
|
class SourceType extends CEIType {
|
||||||
/**
|
/**
|
||||||
* @param {string} packageName
|
* @param {string} rawShortSignature
|
||||||
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} outer_scope
|
* @param {'class'|'interface'|'enum'|'@interface'} typeKind
|
||||||
|
* @param {string[]|number} modifiers
|
||||||
* @param {string} docs
|
* @param {string} docs
|
||||||
* @param {Token[]} modifiers
|
|
||||||
* @param {string} typeKind
|
|
||||||
* @param {Token} kind_token
|
|
||||||
* @param {Token} name_token
|
|
||||||
* @param {Map<string,CEIType>} typemap
|
* @param {Map<string,CEIType>} typemap
|
||||||
*/
|
*/
|
||||||
constructor(packageName, outer_scope, docs, modifiers, typeKind, kind_token, name_token, typemap) {
|
constructor(rawShortSignature, typeKind, modifiers, docs, typemap) {
|
||||||
// @ts-ignore
|
super(rawShortSignature, typeKind, modifiers, docs);
|
||||||
super(generateShortSignature(outer_scope || packageName, name_token.value), typeKind, modifiers.map(m => m.source), docs);
|
|
||||||
super.packageName = packageName;
|
|
||||||
this.modifierTokens = modifiers;
|
|
||||||
this.kind_token = kind_token;
|
|
||||||
this.nameToken = name_token;
|
|
||||||
this.scope = outer_scope;
|
|
||||||
this.typemap = typemap;
|
|
||||||
/**
|
/**
|
||||||
* Number of local/anonymous types declared in the scope of this type
|
* Number of local/anonymous types declared in the scope of this type
|
||||||
* The number is used when naming them.
|
* The number is used when naming them.
|
||||||
@@ -98,14 +89,7 @@ class SourceType extends CEIType {
|
|||||||
this.initers = [];
|
this.initers = [];
|
||||||
/** @type {SourceEnumValue[]} */
|
/** @type {SourceEnumValue[]} */
|
||||||
this.enumValues = [];
|
this.enumValues = [];
|
||||||
}
|
this.typemap = typemap;
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {SourceMethod[]}
|
|
||||||
*/
|
|
||||||
get sourceMethods() {
|
|
||||||
// @ts-ignore
|
|
||||||
return this.methods.filter(m => m instanceof SourceMethod);// [...this.implicitMethods, ...this.sourceMethods];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -118,6 +102,95 @@ class SourceType extends CEIType {
|
|||||||
this.enumValues.push(new SourceEnumValue(this, docs, ident, ctr_args, anonymousType));
|
this.enumValues.push(new SourceEnumValue(this, docs, ident, ctr_args, anonymousType));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {SourceMethod[]}
|
||||||
|
*/
|
||||||
|
get sourceMethods() {
|
||||||
|
// @ts-ignore
|
||||||
|
return this.methods.filter(m => m instanceof SourceMethod);// [...this.implicitMethods, ...this.sourceMethods];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnonymousSourceType extends SourceType {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} scope
|
||||||
|
*/
|
||||||
|
static genSignature(scope) {
|
||||||
|
const type = scope instanceof SourceType ? scope : scope.owner;
|
||||||
|
return `${type._rawShortSignature}$${type.localTypeCount += 1}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {SourceTypeIdent} typeident
|
||||||
|
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} outer_scope
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
|
*/
|
||||||
|
constructor(typeident, outer_scope, typemap) {
|
||||||
|
super(AnonymousSourceType.genSignature(outer_scope), 'class', [], '', typemap);
|
||||||
|
this.simpleTypeName = typeident.resolved.simpleTypeName;
|
||||||
|
this.typeIdent = typeident;
|
||||||
|
}
|
||||||
|
|
||||||
|
get dottedTypeName() {
|
||||||
|
return this.typeIdent.resolved.dottedTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fullyDottedRawName() {
|
||||||
|
return this.dottedTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get fullyDottedTypeName() {
|
||||||
|
return this.dottedTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
get label() {
|
||||||
|
return `new ${this.dottedTypeName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {JavaType[]} */
|
||||||
|
get supers() {
|
||||||
|
if (this.typeIdent.resolved instanceof AnyType || this.typeIdent.resolved.typeKind !== 'class') {
|
||||||
|
return [this.typemap.get('java/lang/Object')]
|
||||||
|
}
|
||||||
|
return [this.typeIdent.resolved];
|
||||||
|
}
|
||||||
|
|
||||||
|
get shortSignature() {
|
||||||
|
return this._rawShortSignature;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rawTypeSignature() {
|
||||||
|
return `L${this._rawShortSignature};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
get typeSignature() {
|
||||||
|
return this.rawTypeSignature;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class NamedSourceType extends SourceType {
|
||||||
|
/**
|
||||||
|
* @param {string} packageName
|
||||||
|
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} outer_scope
|
||||||
|
* @param {string} docs
|
||||||
|
* @param {Token[]} modifiers
|
||||||
|
* @param {string} typeKind
|
||||||
|
* @param {Token} kind_token
|
||||||
|
* @param {Token} name_token
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
|
*/
|
||||||
|
constructor(packageName, outer_scope, docs, modifiers, typeKind, kind_token, name_token, typemap) {
|
||||||
|
// @ts-ignore
|
||||||
|
super(generateShortSignature(outer_scope || packageName, name_token.value), typeKind, modifiers.map(m => m.source), docs, typemap);
|
||||||
|
super.packageName = packageName;
|
||||||
|
this.modifierTokens = modifiers;
|
||||||
|
this.kind_token = kind_token;
|
||||||
|
this.nameToken = name_token;
|
||||||
|
this.scope = outer_scope;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} package_name
|
* @param {string} package_name
|
||||||
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} outer_scope
|
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser} outer_scope
|
||||||
@@ -372,6 +445,8 @@ class SourceConstructor extends Constructor {
|
|||||||
tokens: body_tokens,
|
tokens: body_tokens,
|
||||||
/** @type {import('./body-types').Local[]} */
|
/** @type {import('./body-types').Local[]} */
|
||||||
locals: [],
|
locals: [],
|
||||||
|
/** @type {import('./statementtypes/Block').Block} */
|
||||||
|
block: null,
|
||||||
}
|
}
|
||||||
this.parsed = null;
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
@@ -423,6 +498,8 @@ class SourceMethod extends Method {
|
|||||||
tokens: body_tokens,
|
tokens: body_tokens,
|
||||||
/** @type {import('./body-types').Local[]} */
|
/** @type {import('./body-types').Local[]} */
|
||||||
locals: [],
|
locals: [],
|
||||||
|
/** @type {import('./statementtypes/Block').Block} */
|
||||||
|
block: null,
|
||||||
}
|
}
|
||||||
this.parsed = null;
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
@@ -467,6 +544,8 @@ class SourceInitialiser extends MethodBase {
|
|||||||
tokens: body_tokens,
|
tokens: body_tokens,
|
||||||
/** @type {import('./body-types').Local[]} */
|
/** @type {import('./body-types').Local[]} */
|
||||||
locals: [],
|
locals: [],
|
||||||
|
/** @type {import('./statementtypes/Block').Block} */
|
||||||
|
block: null,
|
||||||
}
|
}
|
||||||
this.parsed = null;
|
this.parsed = null;
|
||||||
}
|
}
|
||||||
@@ -673,3 +752,5 @@ exports.SourceImport = SourceImport;
|
|||||||
exports.SourceEnumValue = SourceEnumValue;
|
exports.SourceEnumValue = SourceEnumValue;
|
||||||
exports.SourceArrayType = SourceArrayType;
|
exports.SourceArrayType = SourceArrayType;
|
||||||
exports.FixedLengthArrayType = FixedLengthArrayType;
|
exports.FixedLengthArrayType = FixedLengthArrayType;
|
||||||
|
exports.NamedSourceType = NamedSourceType;
|
||||||
|
exports.AnonymousSourceType = AnonymousSourceType;
|
||||||
|
|||||||
@@ -16,6 +16,9 @@ const { ValidateInfo } = require('./body-types');
|
|||||||
* @param {ParseProblem[]} problems
|
* @param {ParseProblem[]} problems
|
||||||
*/
|
*/
|
||||||
function checkStatementBlock(block, method, typemap, problems) {
|
function checkStatementBlock(block, method, typemap, problems) {
|
||||||
|
if (!block) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
block.validate(new ValidateInfo(typemap, problems, method));
|
block.validate(new ValidateInfo(typemap, problems, method));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const { CEIType } = require('java-mti');
|
const { CEIType } = require('java-mti');
|
||||||
const { resolveImports } = require('../java/import-resolver');
|
const { resolveImports } = require('../java/import-resolver');
|
||||||
const { SourceUnit } = require('./source-types');
|
const { SourceUnit } = require('./source-types');
|
||||||
const { parseBody } = require('./body-parser');
|
const { parseTypeMethods } = require('./body-parser');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {SourceUnit} unit
|
* @param {SourceUnit} unit
|
||||||
@@ -12,17 +12,7 @@ function parseMethodBodies(unit, typemap) {
|
|||||||
...resolveImports(typemap, unit.packageName),
|
...resolveImports(typemap, unit.packageName),
|
||||||
...unit.imports.filter(i => i.resolved).map(i => i.resolved),
|
...unit.imports.filter(i => i.resolved).map(i => i.resolved),
|
||||||
]
|
]
|
||||||
unit.types.forEach(t => {
|
unit.types.forEach(t => parseTypeMethods(t, resolved_types, typemap));
|
||||||
t.initers.forEach(i => {
|
|
||||||
i.parsed = parseBody(i, resolved_types, typemap);
|
|
||||||
})
|
|
||||||
t.constructors.forEach(c => {
|
|
||||||
c.parsed = parseBody(c, resolved_types, typemap);
|
|
||||||
})
|
|
||||||
t.sourceMethods.forEach(m => {
|
|
||||||
m.parsed = parseBody(m, resolved_types, typemap);
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user