mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
improve checking of number literals
This commit is contained in:
@@ -130,7 +130,8 @@ class MultiValueType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {JavaType|MethodType|LambdaType|ArrayValueType|TypeIdentType|MultiValueType} ResolvedValue
|
* @typedef {import('./expressiontypes/literals/Number').NumberLiteral} NumberLiteral
|
||||||
|
* @typedef {JavaType|MethodType|LambdaType|ArrayValueType|TypeIdentType|MultiValueType|NumberLiteral} ResolvedValue
|
||||||
**/
|
**/
|
||||||
|
|
||||||
exports.AnyMethod = AnyMethod;
|
exports.AnyMethod = AnyMethod;
|
||||||
|
|||||||
@@ -24,16 +24,6 @@ function checkAssignment(e, assign_type, typemap, problems) {
|
|||||||
checkTypeAssignable(assign_type, value.type, () => value.name_token, problems);
|
checkTypeAssignable(assign_type, value.type, () => value.name_token, problems);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value instanceof NumberLiteral) {
|
|
||||||
if (!value.isCompatibleWith(assign_type)) {
|
|
||||||
incompatibleTypesError(assign_type, value.type, () => value.token, problems);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value instanceof LiteralValue) {
|
|
||||||
checkTypeAssignable(assign_type, value.type, () => value.token, problems);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (value instanceof Expression) {
|
if (value instanceof Expression) {
|
||||||
const expression_result_type = value.resolveExpression(new ResolveInfo(typemap, problems));
|
const expression_result_type = value.resolveExpression(new ResolveInfo(typemap, problems));
|
||||||
checkTypeAssignable(assign_type, expression_result_type, () => value.tokens(), problems);
|
checkTypeAssignable(assign_type, expression_result_type, () => value.tokens(), problems);
|
||||||
@@ -44,22 +34,28 @@ function checkAssignment(e, assign_type, typemap, problems) {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {JavaType} variable_type
|
* @param {JavaType} variable_type
|
||||||
* @param {ResolvedValue} value_type
|
* @param {ResolvedValue} value
|
||||||
* @param {() => Token|Token[]} tokens
|
* @param {() => Token|Token[]} tokens
|
||||||
* @param {ParseProblem[]} problems
|
* @param {ParseProblem[]} problems
|
||||||
*/
|
*/
|
||||||
function checkTypeAssignable(variable_type, value_type, tokens, problems) {
|
function checkTypeAssignable(variable_type, value, tokens, problems) {
|
||||||
if (value_type instanceof MultiValueType) {
|
if (value instanceof NumberLiteral) {
|
||||||
value_type.types.forEach(t => checkTypeAssignable(variable_type, t, tokens, problems));
|
if (!value.isCompatibleWith(variable_type)) {
|
||||||
|
incompatibleTypesError(variable_type, value.type, () => value.tokens(), problems);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value_type instanceof ArrayValueType) {
|
if (value instanceof MultiValueType) {
|
||||||
checkArrayLiteral(variable_type, value_type, tokens, problems);
|
value.types.forEach(t => checkTypeAssignable(variable_type, t, tokens, problems));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (value_type instanceof JavaType) {
|
if (value instanceof ArrayValueType) {
|
||||||
if (!isTypeAssignable(variable_type, value_type)) {
|
checkArrayLiteral(variable_type, value, tokens, problems);
|
||||||
incompatibleTypesError(variable_type, value_type, tokens, problems);
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof JavaType) {
|
||||||
|
if (!isTypeAssignable(variable_type, value)) {
|
||||||
|
incompatibleTypesError(variable_type, value, tokens, problems);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,6 +100,12 @@ function checkArrayLiteral(variable_type, value_type, tokens, problems) {
|
|||||||
* @param {Token[]} tokens
|
* @param {Token[]} tokens
|
||||||
*/
|
*/
|
||||||
function checkArrayElement(element_type, value_type, tokens) {
|
function checkArrayElement(element_type, value_type, tokens) {
|
||||||
|
if (value_type instanceof NumberLiteral) {
|
||||||
|
if (!value_type.isCompatibleWith(element_type)) {
|
||||||
|
incompatibleTypesError(element_type, value_type.type, () => tokens, problems);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (value_type instanceof JavaType) {
|
if (value_type instanceof JavaType) {
|
||||||
if (!isTypeAssignable(element_type, value_type)) {
|
if (!isTypeAssignable(element_type, value_type)) {
|
||||||
incompatibleTypesError(element_type, value_type, () => tokens, problems);
|
incompatibleTypesError(element_type, value_type, () => tokens, problems);
|
||||||
@@ -125,6 +127,15 @@ function checkArrayLiteral(variable_type, value_type, tokens, problems) {
|
|||||||
*/
|
*/
|
||||||
function checkArrayIndex(ri, d, kind) {
|
function checkArrayIndex(ri, d, kind) {
|
||||||
const idx = d.resolveExpression(ri);
|
const idx = d.resolveExpression(ri);
|
||||||
|
if (idx instanceof NumberLiteral) {
|
||||||
|
if (!idx.isCompatibleWith(PrimitiveType.map.I)) {
|
||||||
|
ri.problems.push(ParseProblem.Error(d.tokens, `Value '${idx.toNumber()}' is not valid as an array ${kind}`));
|
||||||
|
}
|
||||||
|
else if (idx.toNumber() < 0) {
|
||||||
|
ri.problems.push(ParseProblem.Error(d.tokens, `Negative array ${kind}: ${idx.toNumber()}`));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (idx instanceof PrimitiveType) {
|
if (idx instanceof PrimitiveType) {
|
||||||
if (!/^[BSI]$/.test(idx.typeSignature)) {
|
if (!/^[BSI]$/.test(idx.typeSignature)) {
|
||||||
ri.problems.push(ParseProblem.Error(d.tokens, `Expression of type '${idx.label}' is not valid as an array ${kind}`));
|
ri.problems.push(ParseProblem.Error(d.tokens, `Expression of type '${idx.label}' is not valid as an array ${kind}`));
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const { Expression } = require("./Expression");
|
|||||||
const { JavaType, PrimitiveType } = require('java-mti');
|
const { JavaType, PrimitiveType } = require('java-mti');
|
||||||
const ParseProblem = require('../parsetypes/parse-problem');
|
const ParseProblem = require('../parsetypes/parse-problem');
|
||||||
const { AnyType, TypeIdentType } = require('../anys');
|
const { AnyType, TypeIdentType } = require('../anys');
|
||||||
|
const { NumberLiteral } = require('./literals/Number');
|
||||||
|
|
||||||
class BinaryOpExpression extends Expression {
|
class BinaryOpExpression extends Expression {
|
||||||
/**
|
/**
|
||||||
@@ -26,13 +27,31 @@ class BinaryOpExpression extends Expression {
|
|||||||
*/
|
*/
|
||||||
resolveExpression(ri) {
|
resolveExpression(ri) {
|
||||||
const operator = this.op.value;
|
const operator = this.op.value;
|
||||||
const lhstype = this.lhs.resolveExpression(ri);
|
let lhstype = this.lhs.resolveExpression(ri);
|
||||||
const rhstype = this.rhs.resolveExpression(ri);
|
let rhstype = this.rhs.resolveExpression(ri);
|
||||||
|
|
||||||
if (lhstype instanceof AnyType || rhstype instanceof AnyType) {
|
if (lhstype instanceof AnyType || rhstype instanceof AnyType) {
|
||||||
return AnyType.Instance;
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lhstype instanceof NumberLiteral || rhstype instanceof NumberLiteral) {
|
||||||
|
if (lhstype instanceof NumberLiteral && rhstype instanceof NumberLiteral) {
|
||||||
|
// if they are both literals, compute the result
|
||||||
|
if (/^[*/%+-]$/.test(operator)) {
|
||||||
|
return NumberLiteral[operator](lhstype, rhstype);
|
||||||
|
}
|
||||||
|
if (/^([&|^]|<<|>>>?)$/.test(operator) && !/[FD]/.test(`${lhstype.type.typeSignature}${rhstype.type.typeSignature}`)) {
|
||||||
|
return NumberLiteral[operator](lhstype, rhstype);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lhstype instanceof NumberLiteral) {
|
||||||
|
lhstype = lhstype.type;
|
||||||
|
}
|
||||||
|
if (rhstype instanceof NumberLiteral) {
|
||||||
|
rhstype = rhstype.type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (operator === 'instanceof') {
|
if (operator === 'instanceof') {
|
||||||
if (!(rhstype instanceof TypeIdentType)) {
|
if (!(rhstype instanceof TypeIdentType)) {
|
||||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Type expected`));
|
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Type expected`));
|
||||||
|
|||||||
@@ -2,29 +2,31 @@
|
|||||||
* @typedef {import('java-mti').JavaType} JavaType
|
* @typedef {import('java-mti').JavaType} JavaType
|
||||||
* @typedef {import('../../body-types').ResolveInfo} ResolveInfo
|
* @typedef {import('../../body-types').ResolveInfo} ResolveInfo
|
||||||
* @typedef {import('../../tokenizer').Token} Token
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
* @typedef {import('../../anys').ResolvedValue} ResolvedValue
|
||||||
*/
|
*/
|
||||||
const { Expression } = require('../Expression');
|
const { Expression } = require('../Expression');
|
||||||
|
|
||||||
class LiteralValue extends Expression {
|
class LiteralValue extends Expression {
|
||||||
/**
|
/**
|
||||||
* @param {Token} token
|
* @param {Token|Token[]} tokens
|
||||||
* @param {JavaType} known_type
|
* @param {JavaType} known_type
|
||||||
*/
|
*/
|
||||||
constructor(token, known_type) {
|
constructor(tokens, known_type) {
|
||||||
super();
|
super();
|
||||||
this.token = token;
|
this._tokens = tokens;
|
||||||
this.type = known_type;
|
this.type = known_type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ResolveInfo} ri
|
* @param {ResolveInfo} ri
|
||||||
|
* @returns {ResolvedValue}
|
||||||
*/
|
*/
|
||||||
resolveExpression(ri) {
|
resolveExpression(ri) {
|
||||||
return this.type;
|
return this.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens() {
|
tokens() {
|
||||||
return this.token;
|
return this._tokens;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import('../../tokenizer').Token} Token
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
* @typedef {import('java-mti').JavaType} JavaType
|
* @typedef {import('java-mti').JavaType} JavaType
|
||||||
|
* @typedef {import('../../body-types').ResolveInfo} ResolveInfo
|
||||||
*/
|
*/
|
||||||
const { LiteralValue } = require('./LiteralValue');
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
const { PrimitiveType } = require('java-mti');
|
const { PrimitiveType } = require('java-mti');
|
||||||
@@ -13,15 +14,43 @@ const { PrimitiveType } = require('java-mti');
|
|||||||
*/
|
*/
|
||||||
class NumberLiteral extends LiteralValue {
|
class NumberLiteral extends LiteralValue {
|
||||||
/**
|
/**
|
||||||
* @param {Token} value
|
* @param {Token[]} tokens
|
||||||
* @param {string} kind
|
* @param {string} kind
|
||||||
* @param {PrimitiveType} default_type
|
* @param {PrimitiveType} default_type
|
||||||
|
* @param {string} [value]
|
||||||
*/
|
*/
|
||||||
constructor(value, kind, default_type) {
|
constructor(tokens, kind, default_type, value = tokens[0].value) {
|
||||||
super(value, default_type);
|
super(tokens, default_type);
|
||||||
|
this.value = value;
|
||||||
this.numberKind = kind;
|
this.numberKind = kind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {ResolveInfo} ri
|
||||||
|
*/
|
||||||
|
resolveExpression(ri) {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {NumberLiteral} a
|
||||||
|
* @param {NumberLiteral} b
|
||||||
|
* @param {string} kind
|
||||||
|
* @param {PrimitiveType} type
|
||||||
|
* @param {number} value
|
||||||
|
*/
|
||||||
|
static calc(a, b, kind, type, value) {
|
||||||
|
let atoks = a.tokens(), btoks = b.tokens();
|
||||||
|
atoks = Array.isArray(atoks) ? atoks : [atoks];
|
||||||
|
btoks = Array.isArray(btoks) ? btoks : [btoks];
|
||||||
|
return new NumberLiteral([...atoks, ...btoks], kind, type, value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {NumberLiteral} a
|
||||||
|
* @param {NumberLiteral} b
|
||||||
|
* @param {(a,b) => Number} op
|
||||||
|
*/
|
||||||
static shift(a, b, op) {
|
static shift(a, b, op) {
|
||||||
const ai = a.toInt(), bi = b.toInt();
|
const ai = a.toInt(), bi = b.toInt();
|
||||||
if (ai === null || bi === null) {
|
if (ai === null || bi === null) {
|
||||||
@@ -29,9 +58,14 @@ class NumberLiteral extends LiteralValue {
|
|||||||
}
|
}
|
||||||
const val = op(ai, bi);
|
const val = op(ai, bi);
|
||||||
const type = a.type.typeSignature === 'J' ? PrimitiveType.map.J : PrimitiveType.map.I;
|
const type = a.type.typeSignature === 'J' ? PrimitiveType.map.J : PrimitiveType.map.I;
|
||||||
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {NumberLiteral} a
|
||||||
|
* @param {NumberLiteral} b
|
||||||
|
* @param {(a,b) => Number} op
|
||||||
|
*/
|
||||||
static bitwise(a, b, op) {
|
static bitwise(a, b, op) {
|
||||||
const ai = a.toInt(), bi = b.toInt();
|
const ai = a.toInt(), bi = b.toInt();
|
||||||
if (ai === null || bi === null) {
|
if (ai === null || bi === null) {
|
||||||
@@ -39,27 +73,31 @@ class NumberLiteral extends LiteralValue {
|
|||||||
}
|
}
|
||||||
const val = op(ai, bi);
|
const val = op(ai, bi);
|
||||||
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
||||||
let type = /J/.test(typekey) ? PrimitiveType.map.J : PrimitiveType.map.I;
|
const type = /J/.test(typekey) ? PrimitiveType.map.J : PrimitiveType.map.I;
|
||||||
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @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, divmod) {
|
||||||
const ai = a.toNumber(), bi = b.toNumber();
|
const ai = a.toNumber(), bi = b.toNumber();
|
||||||
if (bi === 0 && divmod) {
|
if (bi === 0 && divmod) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let val = op(ai, bi);
|
let val = op(ai, bi);
|
||||||
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
const typekey = a.type.typeSignature + b.type.typeSignature;
|
||||||
if (!/[FD]/.test(typekey) && divmod) {
|
if (!/[FD]/.test(typekey) && divmod) {
|
||||||
val = Math.trunc(val);
|
val = Math.trunc(val);
|
||||||
}
|
}
|
||||||
let type;
|
const type = typekey.includes('D') ? PrimitiveType.map.D
|
||||||
if (/^(D|F[^D]|J[^FD])/.test(typekey)) {
|
: typekey.includes('F') ? PrimitiveType.map.F
|
||||||
type = a.type;
|
: typekey.includes('J') ? PrimitiveType.map.J
|
||||||
} else {
|
: PrimitiveType.map.I;
|
||||||
type = b.type;
|
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||||
}
|
|
||||||
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static '+'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a + b) }
|
static '+'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a + b) }
|
||||||
@@ -84,13 +122,13 @@ class NumberLiteral extends LiteralValue {
|
|||||||
case 'int-number-literal':
|
case 'int-number-literal':
|
||||||
// unlike parseInt, BigInt doesn't like invalid characters, so
|
// unlike parseInt, BigInt doesn't like invalid characters, so
|
||||||
// ensure we strip any trailing long specifier
|
// ensure we strip any trailing long specifier
|
||||||
return BigInt(this.token.value.match(/(.+?)[lL]?$/)[1]);
|
return BigInt(this.value.match(/(.+?)[lL]?$/)[1]);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
toNumber() {
|
toNumber() {
|
||||||
return parseFloat(this.token.value);
|
return parseFloat(this.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,12 +154,12 @@ class NumberLiteral extends LiteralValue {
|
|||||||
}
|
}
|
||||||
let number = 0;
|
let number = 0;
|
||||||
if (this.numberKind === 'hex-number-literal') {
|
if (this.numberKind === 'hex-number-literal') {
|
||||||
if (this.token.value !== '0x') {
|
if (this.value !== '0x') {
|
||||||
const non_leading_zero_digits = this.token.value.match(/0x0*(.+)/)[1];
|
const non_leading_zero_digits = this.value.match(/0x0*(.+)/)[1];
|
||||||
number = non_leading_zero_digits.length > 8 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 16);
|
number = non_leading_zero_digits.length > 8 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 16);
|
||||||
}
|
}
|
||||||
} else if (this.numberKind === 'int-number-literal') {
|
} else if (this.numberKind === 'int-number-literal') {
|
||||||
const non_leading_zero_digits = this.token.value.match(/0*(.+)/)[1];
|
const non_leading_zero_digits = this.value.match(/0*(.+)/)[1];
|
||||||
number = non_leading_zero_digits.length > 10 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 10);
|
number = non_leading_zero_digits.length > 10 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 10);
|
||||||
}
|
}
|
||||||
if (number >= -128 && number <= 127) {
|
if (number >= -128 && number <= 127) {
|
||||||
@@ -153,12 +191,12 @@ class NumberLiteral extends LiteralValue {
|
|||||||
switch(token.kind) {
|
switch(token.kind) {
|
||||||
case 'dec-exp-number-literal':
|
case 'dec-exp-number-literal':
|
||||||
case 'dec-number-literal':
|
case 'dec-number-literal':
|
||||||
return new NumberLiteral(token, token.kind, suffix('FfDdLl') || PrimitiveType.map.D);
|
return new NumberLiteral([token], token.kind, suffix('FfDdLl') || PrimitiveType.map.D);
|
||||||
case 'hex-number-literal':
|
case 'hex-number-literal':
|
||||||
return new NumberLiteral(token, token.kind, suffix(' Ll') || PrimitiveType.map.I);
|
return new NumberLiteral([token], token.kind, suffix(' Ll') || PrimitiveType.map.I);
|
||||||
case 'int-number-literal':
|
case 'int-number-literal':
|
||||||
default:
|
default:
|
||||||
return new NumberLiteral(token, token.kind, suffix('FfDdLl') || PrimitiveType.map.I);
|
return new NumberLiteral([token], token.kind, suffix('FfDdLl') || PrimitiveType.map.I);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user