mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
add type cast checking
This commit is contained in:
@@ -1488,28 +1488,28 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
||||
matches = resolveIdentifier(tokens, mdecls, scope, imports, typemap);
|
||||
break;
|
||||
case 'primitive-type':
|
||||
matches = new ResolvedIdent(tokens.current.value, [], [], [PrimitiveType.fromName(tokens.current.value)]);
|
||||
matches = new ResolvedIdent(tokens.current, [], [], [PrimitiveType.fromName(tokens.current.value)]);
|
||||
break;
|
||||
case 'string-literal':
|
||||
matches = new ResolvedIdent(tokens.current.value, [new StringLiteral(tokens.current, typemap.get('java/lang/String'))]);
|
||||
matches = new ResolvedIdent(tokens.current, [new StringLiteral(tokens.current, typemap.get('java/lang/String'))]);
|
||||
break;
|
||||
case 'char-literal':
|
||||
matches = new ResolvedIdent(tokens.current.value, [new CharacterLiteral(tokens.current)]);
|
||||
matches = new ResolvedIdent(tokens.current, [new CharacterLiteral(tokens.current)]);
|
||||
break;
|
||||
case 'boolean-literal':
|
||||
matches = new ResolvedIdent(tokens.current.value, [new BooleanLiteral(tokens.current)]);
|
||||
matches = new ResolvedIdent(tokens.current, [new BooleanLiteral(tokens.current)]);
|
||||
break;
|
||||
case 'object-literal':
|
||||
// this, super or null
|
||||
const scoped_type = scope instanceof SourceType ? scope : scope.owner;
|
||||
if (tokens.current.value === 'this' || tokens.current.value === 'super') {
|
||||
matches = new ResolvedIdent(tokens.current.value, [new InstanceLiteral(tokens.current, scoped_type)]);
|
||||
matches = new ResolvedIdent(tokens.current, [new InstanceLiteral(tokens.current, scoped_type)]);
|
||||
} else {
|
||||
matches = new ResolvedIdent(tokens.current.value, [new NullLiteral(tokens.current)]);
|
||||
matches = new ResolvedIdent(tokens.current, [new NullLiteral(tokens.current)]);
|
||||
}
|
||||
break;
|
||||
case /number-literal/.test(tokens.current.kind) && tokens.current.kind:
|
||||
matches = new ResolvedIdent(tokens.current.value, [NumberLiteral.from(tokens.current)]);
|
||||
matches = new ResolvedIdent(tokens.current, [NumberLiteral.from(tokens.current)]);
|
||||
break;
|
||||
case 'inc-operator':
|
||||
let incop = tokens.getIfKind('inc-operator');
|
||||
|
||||
@@ -9,7 +9,7 @@ const { AnyType, MethodType, TypeIdentType } = require('./anys');
|
||||
|
||||
class ResolvedIdent {
|
||||
/**
|
||||
* @param {string} ident
|
||||
* @param {string|Token} ident
|
||||
* @param {Expression[]} variables
|
||||
* @param {Method[]} methods
|
||||
* @param {JavaType[]} types
|
||||
@@ -17,13 +17,12 @@ class ResolvedIdent {
|
||||
* @param {Token[]} tokens
|
||||
*/
|
||||
constructor(ident, variables = [], methods = [], types = [], package_name = '', tokens = []) {
|
||||
this.source = ident;
|
||||
this.source = ident instanceof Token ? ident.value : ident;
|
||||
this.variables = variables;
|
||||
this.methods = methods;
|
||||
this.types = types;
|
||||
this.package_name = package_name;
|
||||
/** @type {Token[]} */
|
||||
this.tokens = tokens;
|
||||
this.tokens = ident instanceof Token ? [ident] : tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,7 +33,7 @@ class ResolvedIdent {
|
||||
if (this.variables[0]) {
|
||||
return this.variables[0].resolveExpression(ri);
|
||||
}
|
||||
if (this.methods) {
|
||||
if (this.methods[0]) {
|
||||
return new MethodType(this.methods);
|
||||
}
|
||||
if (this.types[0]) {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
/**
|
||||
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
|
||||
* @typedef {import('../anys').ResolvedType} ResolvedType
|
||||
*/
|
||||
const { Expression } = require("./Expression");
|
||||
const { AnyType, TypeIdentType } = require('../anys');
|
||||
const ParseProblem = require('../parsetypes/parse-problem');
|
||||
const { JavaType, PrimitiveType, NullType, CEIType, ArrayType } = require('java-mti');
|
||||
const { getTypeInheritanceList } = require('../expression-resolver');
|
||||
|
||||
class CastExpression extends Expression {
|
||||
/**
|
||||
@@ -22,8 +26,14 @@ class CastExpression extends Expression {
|
||||
resolveExpression(ri) {
|
||||
const cast_type = this.castType.resolveExpression(ri);
|
||||
if (cast_type instanceof TypeIdentType) {
|
||||
const expr_type = this.expression.resolveExpression(ri);
|
||||
checkCastable(this, cast_type.type, expr_type, ri.problems);
|
||||
return cast_type.type;
|
||||
}
|
||||
if (cast_type instanceof AnyType) {
|
||||
return cast_type;
|
||||
}
|
||||
ri.problems.push(ParseProblem.Error(this.castType.tokens, 'Type expected'))
|
||||
return AnyType.Instance;
|
||||
}
|
||||
|
||||
@@ -32,4 +42,87 @@ class CastExpression extends Expression {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CastExpression} cast
|
||||
* @param {JavaType} cast_type
|
||||
* @param {ResolvedType} expr_type
|
||||
* @param {ParseProblem[]} problems
|
||||
*/
|
||||
function checkCastable(cast, cast_type, expr_type, problems) {
|
||||
if (expr_type instanceof JavaType) {
|
||||
if (!isTypeCastable(expr_type, cast_type)) {
|
||||
problems.push(ParseProblem.Error(cast.expression.tokens, `Invalid cast: An expression of type '${expr_type.fullyDottedTypeName}' cannot be cast to type '${cast_type.fullyDottedTypeName}'`));
|
||||
}
|
||||
return;
|
||||
}
|
||||
problems.push(ParseProblem.Error(cast.expression.tokens, `Invalid cast: expression is not a value or variable`));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {JavaType} source_type
|
||||
* @param {JavaType} cast_type
|
||||
*/
|
||||
function isTypeCastable(source_type, cast_type) {
|
||||
if (source_type.typeSignature === 'Ljava/lang/Object;') {
|
||||
// everything is castable from Object
|
||||
return true;
|
||||
}
|
||||
if (cast_type.typeSignature === 'Ljava/lang/Object;') {
|
||||
// everything is castable to Object
|
||||
return true;
|
||||
}
|
||||
if (source_type instanceof NullType) {
|
||||
// null is castable to any non-primitive
|
||||
return !(cast_type instanceof PrimitiveType);
|
||||
}
|
||||
if (source_type instanceof CEIType && cast_type instanceof CEIType) {
|
||||
if (source_type.typeKind === 'interface') {
|
||||
// interfaces are castable to any non-final class type (derived types might implement the interface)
|
||||
if (cast_type.typeKind === 'class' && !cast_type.modifiers.includes('final')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// for other class casts, one type must be in the inheritence tree of the other
|
||||
if (getTypeInheritanceList(source_type).includes(cast_type)) {
|
||||
return true;
|
||||
}
|
||||
if (getTypeInheritanceList(cast_type).includes(source_type)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (cast_type instanceof PrimitiveType) {
|
||||
// source type must be a compatible primitive or class
|
||||
switch (cast_type.typeSignature) {
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'I':
|
||||
case 'J':
|
||||
case 'C':
|
||||
case 'F':
|
||||
case 'D':
|
||||
return /^([BSIJCFD]|Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float|Double);)$/.test(source_type.typeSignature);
|
||||
case 'Z':
|
||||
return /^([Z]|Ljava\/lang\/(Boolean);)$/.test(source_type.typeSignature);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (cast_type instanceof ArrayType) {
|
||||
// the source type must have the same array dimensionality and have a castable base type
|
||||
if (source_type instanceof ArrayType) {
|
||||
if (source_type.arrdims === cast_type.arrdims) {
|
||||
if (isTypeCastable(source_type.base, cast_type.base)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source_type instanceof AnyType || cast_type instanceof AnyType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
exports.CastExpression = CastExpression;
|
||||
|
||||
Reference in New Issue
Block a user