mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
implement method body and ststement validation
This commit is contained in:
@@ -8,6 +8,7 @@ const { JavaType, PrimitiveType } = require('java-mti');
|
||||
const ParseProblem = require('../parsetypes/parse-problem');
|
||||
const { AnyType, TypeIdentType } = require('../anys');
|
||||
const { NumberLiteral } = require('./literals/Number');
|
||||
const { checkTypeAssignable } = require('../expression-resolver');
|
||||
|
||||
class BinaryOpExpression extends Expression {
|
||||
/**
|
||||
@@ -27,46 +28,43 @@ class BinaryOpExpression extends Expression {
|
||||
*/
|
||||
resolveExpression(ri) {
|
||||
const operator = this.op.value;
|
||||
let lhstype = this.lhs.resolveExpression(ri);
|
||||
let rhstype = this.rhs.resolveExpression(ri);
|
||||
const lhsvalue = this.lhs.resolveExpression(ri);
|
||||
const rhsvalue = this.rhs.resolveExpression(ri);
|
||||
|
||||
if (lhstype instanceof AnyType || rhstype instanceof AnyType) {
|
||||
if (lhsvalue instanceof AnyType || rhsvalue instanceof AnyType) {
|
||||
return AnyType.Instance;
|
||||
}
|
||||
|
||||
if (lhstype instanceof NumberLiteral || rhstype instanceof NumberLiteral) {
|
||||
if (lhstype instanceof NumberLiteral && rhstype instanceof NumberLiteral) {
|
||||
if (lhsvalue instanceof NumberLiteral || rhsvalue instanceof NumberLiteral) {
|
||||
if (lhsvalue instanceof NumberLiteral && rhsvalue instanceof NumberLiteral) {
|
||||
// if they are both literals, compute the result
|
||||
if (/^[*/%+-]$/.test(operator)) {
|
||||
return NumberLiteral[operator](lhstype, rhstype);
|
||||
return NumberLiteral[operator](lhsvalue, rhsvalue);
|
||||
}
|
||||
if (/^([&|^]|<<|>>>?)$/.test(operator) && !/[FD]/.test(`${lhstype.type.typeSignature}${rhstype.type.typeSignature}`)) {
|
||||
return NumberLiteral[operator](lhstype, rhstype);
|
||||
if (/^([&|^]|<<|>>>?)$/.test(operator) && !/[FD]/.test(`${lhsvalue.type.typeSignature}${rhsvalue.type.typeSignature}`)) {
|
||||
return NumberLiteral[operator](lhsvalue, rhsvalue);
|
||||
}
|
||||
}
|
||||
if (lhstype instanceof NumberLiteral) {
|
||||
lhstype = lhstype.type;
|
||||
}
|
||||
if (rhstype instanceof NumberLiteral) {
|
||||
rhstype = rhstype.type;
|
||||
}
|
||||
}
|
||||
|
||||
const lhstype = lhsvalue instanceof JavaType ? lhsvalue : lhsvalue instanceof NumberLiteral ? lhsvalue.type : null;
|
||||
const rhstype = rhsvalue instanceof JavaType ? rhsvalue : rhsvalue instanceof NumberLiteral ? rhsvalue.type : null;
|
||||
|
||||
if (operator === 'instanceof') {
|
||||
if (!(rhstype instanceof TypeIdentType)) {
|
||||
if (!(rhsvalue instanceof TypeIdentType)) {
|
||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Type expected`));
|
||||
}
|
||||
if (!(lhstype instanceof JavaType)) {
|
||||
if (!lhstype) {
|
||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Expression expected`));
|
||||
}
|
||||
return PrimitiveType.map.Z;
|
||||
}
|
||||
|
||||
if (!(lhstype instanceof JavaType) || !(rhstype instanceof JavaType)) {
|
||||
if (!(lhstype instanceof JavaType)) {
|
||||
if (!lhstype || !rhstype) {
|
||||
if (!lhstype) {
|
||||
ri.problems.push(ParseProblem.Error(this.lhs.tokens, `Expression expected`));
|
||||
}
|
||||
if (!(rhstype instanceof JavaType)) {
|
||||
if (!rhstype) {
|
||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Expression expected`));
|
||||
}
|
||||
return AnyType.Instance;
|
||||
@@ -74,13 +72,17 @@ class BinaryOpExpression extends Expression {
|
||||
|
||||
const typekey = `${lhstype.typeSignature}#${rhstype.typeSignature}`;
|
||||
|
||||
if (operator === '+' && typekey.startsWith('Ljava/lang/String;')) {
|
||||
if (operator === '+' && /(^|#)Ljava\/lang\/String;/.test(typekey)) {
|
||||
// string appending is compatible with all types
|
||||
return lhstype;
|
||||
return ri.typemap.get('java/lang/String');
|
||||
}
|
||||
|
||||
if (/^([*/%&|^+-]?=|<<=|>>>?=)$/.test(operator)) {
|
||||
checkOperator(operator.slice(0,-1), ri, this.op, typekey, lhstype, rhstype);
|
||||
let src_type = rhsvalue;
|
||||
if (operator.length > 1) {
|
||||
src_type = checkOperator(operator.slice(0,-1), ri, this.op, typekey, lhstype, rhstype);
|
||||
}
|
||||
checkTypeAssignable(lhstype, src_type, () => this.rhs.tokens, ri.problems);
|
||||
// result of assignments are lhs
|
||||
return lhstype;
|
||||
}
|
||||
@@ -104,9 +106,14 @@ class BinaryOpExpression extends Expression {
|
||||
*/
|
||||
function checkOperator(operator, ri, operator_token, typekey, lhstype, rhstype) {
|
||||
|
||||
if (operator === '+' && /(^|#)Ljava\/lang\/String;/.test(typekey)) {
|
||||
// string appending is compatible with all types
|
||||
return ri.typemap.get('java/lang/String');
|
||||
}
|
||||
|
||||
if (/^[*/%+-]$/.test(operator)) {
|
||||
// math operators - must be numeric
|
||||
if (!/^[BSIJFD]#[BSIJFD]$/.test(typekey)) {
|
||||
if (!/^[BSIJFDC]#[BSIJFDC]$/.test(typekey)) {
|
||||
ri.problems.push(ParseProblem.Error(operator_token, `Operator '${operator_token.value}' is not valid for types '${lhstype.fullyDottedTypeName}' and '${rhstype.fullyDottedTypeName}'`));
|
||||
}
|
||||
if (/^(D|F#[^D]|J#[^FD]|I#[^JFD])/.test(typekey)) {
|
||||
@@ -120,7 +127,7 @@ function checkOperator(operator, ri, operator_token, typekey, lhstype, rhstype)
|
||||
|
||||
if (/^(<<|>>>?)$/.test(operator)) {
|
||||
// shift operators - must be integral
|
||||
if (!/^[BSIJ]#[BSIJ]$/.test(typekey)) {
|
||||
if (!/^[BSIJC]#[BSIJC]$/.test(typekey)) {
|
||||
ri.problems.push(ParseProblem.Error(operator_token, `Operator '${operator_token.value}' is not valid for types '${lhstype.fullyDottedTypeName}' and '${rhstype.fullyDottedTypeName}'`));
|
||||
}
|
||||
if (/^J/.test(typekey)) {
|
||||
@@ -131,7 +138,7 @@ function checkOperator(operator, ri, operator_token, typekey, lhstype, rhstype)
|
||||
|
||||
if (/^[&|^]$/.test(operator)) {
|
||||
// bitwise or logical operators
|
||||
if (!/^[BSIJ]#[BSIJ]$|^Z#Z$/.test(typekey)) {
|
||||
if (!/^[BSIJC]#[BSIJC]$|^Z#Z$/.test(typekey)) {
|
||||
ri.problems.push(ParseProblem.Error(operator_token, `Operator '${operator_token.value}' is not valid for types '${lhstype.fullyDottedTypeName}' and '${rhstype.fullyDottedTypeName}'`));
|
||||
}
|
||||
if (/^[JZ]/.test(typekey)) {
|
||||
|
||||
@@ -4,10 +4,11 @@
|
||||
* @typedef {import('../anys').ResolvedValue} ResolvedValue
|
||||
*/
|
||||
const { Expression } = require("./Expression");
|
||||
const { AnyType, TypeIdentType } = require('../anys');
|
||||
const { AnyType, MultiValueType, TypeIdentType } = require('../anys');
|
||||
const ParseProblem = require('../parsetypes/parse-problem');
|
||||
const { JavaType, PrimitiveType, NullType, CEIType, ArrayType } = require('java-mti');
|
||||
const { getTypeInheritanceList } = require('../expression-resolver');
|
||||
const { NumberLiteral } = require('../expressiontypes/literals/Number');
|
||||
|
||||
class CastExpression extends Expression {
|
||||
/**
|
||||
@@ -55,6 +56,14 @@ function checkCastable(cast, cast_type, expr_type, problems) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (expr_type instanceof NumberLiteral) {
|
||||
checkCastable(cast, cast_type, expr_type.type, problems);
|
||||
return;
|
||||
}
|
||||
if (expr_type instanceof MultiValueType) {
|
||||
expr_type.types.forEach(type => checkCastable(cast, cast_type, type, problems));
|
||||
return;
|
||||
}
|
||||
problems.push(ParseProblem.Error(cast.expression.tokens, `Invalid cast: expression is not a value or variable`));
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* @typedef {import('../tokenizer').Token} Token
|
||||
*/
|
||||
const { Expression } = require("./Expression");
|
||||
const { CEIType } = require('java-mti');
|
||||
const { JavaType, CEIType } = require('java-mti');
|
||||
const { AnyType, MethodType, PackageNameType, TypeIdentType } = require('../anys');
|
||||
const { getTypeInheritanceList } = require('../expression-resolver');
|
||||
const { resolveNextPackage } = require('../type-resolver');
|
||||
@@ -46,7 +46,7 @@ class MemberExpression extends Expression {
|
||||
: AnyType.Instance;
|
||||
}
|
||||
|
||||
if (!(instance instanceof CEIType)) {
|
||||
if (!(instance instanceof JavaType)) {
|
||||
ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}'`));
|
||||
return AnyType.Instance;
|
||||
}
|
||||
@@ -54,6 +54,12 @@ class MemberExpression extends Expression {
|
||||
if (field) {
|
||||
return field.type;
|
||||
}
|
||||
|
||||
if (!(instance instanceof CEIType)) {
|
||||
ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}'`));
|
||||
return AnyType.Instance;
|
||||
}
|
||||
|
||||
let methods = new Map();
|
||||
getTypeInheritanceList(instance).forEach(type => {
|
||||
type.methods.forEach(m => {
|
||||
|
||||
@@ -81,30 +81,29 @@ class NumberLiteral extends LiteralValue {
|
||||
* @param {NumberLiteral} a
|
||||
* @param {NumberLiteral} b
|
||||
* @param {(a,b) => Number} op
|
||||
* @param {boolean} [divmod]
|
||||
*/
|
||||
static math(a, b, op, divmod) {
|
||||
static math(a, b, op) {
|
||||
const ai = a.toNumber(), bi = b.toNumber();
|
||||
if (bi === 0 && divmod) {
|
||||
return null;
|
||||
}
|
||||
let val = op(ai, bi);
|
||||
const typekey = a.type.typeSignature + b.type.typeSignature;
|
||||
if (!/[FD]/.test(typekey) && divmod) {
|
||||
if (!/[FD]/.test(typekey)) {
|
||||
val = Math.trunc(val);
|
||||
}
|
||||
const type = typekey.includes('D') ? PrimitiveType.map.D
|
||||
: typekey.includes('F') ? PrimitiveType.map.F
|
||||
: typekey.includes('J') ? PrimitiveType.map.J
|
||||
: PrimitiveType.map.I;
|
||||
// note: Java allows integer division by zero at compile-time - it will
|
||||
// always cause an ArithmeticException at runtime, so the result here (inf or nan)
|
||||
// is largely meaningless
|
||||
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||
}
|
||||
|
||||
static '+'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a + b) }
|
||||
static '-'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a - b) }
|
||||
static '*'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a * b) }
|
||||
static '/'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a / b, true) }
|
||||
static '%'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a % b, true) }
|
||||
static '/'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a / b) }
|
||||
static '%'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a % b) }
|
||||
static '&'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a & b) }
|
||||
static '|'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a | b) }
|
||||
static '^'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a ^ b) }
|
||||
|
||||
Reference in New Issue
Block a user