mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
improve support for ternary operators in assignments and method invocations
This commit is contained in:
@@ -244,7 +244,7 @@ const valid_primitive_types = {
|
|||||||
/**
|
/**
|
||||||
* Returns true if a value of value_type is assignable to a variable of dest_type
|
* Returns true if a value of value_type is assignable to a variable of dest_type
|
||||||
* @param {JavaType} dest_type
|
* @param {JavaType} dest_type
|
||||||
* @param {JavaType|NumberLiteral|LambdaType} value_type
|
* @param {JavaType|NumberLiteral|LambdaType|MultiValueType} value_type
|
||||||
*/
|
*/
|
||||||
function isTypeAssignable(dest_type, value_type) {
|
function isTypeAssignable(dest_type, value_type) {
|
||||||
|
|
||||||
@@ -256,6 +256,14 @@ function isTypeAssignable(dest_type, value_type) {
|
|||||||
return isLambdaAssignable(dest_type, value_type) === true;
|
return isLambdaAssignable(dest_type, value_type) === true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (value_type instanceof MultiValueType) {
|
||||||
|
return value_type.types.every(t => {
|
||||||
|
if (t instanceof JavaType || t instanceof NumberLiteral || t instanceof LambdaType || t instanceof MultiValueType)
|
||||||
|
return isTypeAssignable(dest_type, t);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let is_assignable = false;
|
let is_assignable = false;
|
||||||
if (dest_type.typeSignature === value_type.typeSignature) {
|
if (dest_type.typeSignature === value_type.typeSignature) {
|
||||||
// exact signature match
|
// exact signature match
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
/**
|
/**
|
||||||
* @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('../body-types').ResolvedValue} ResolvedValue
|
||||||
* @typedef {import('../tokenizer').Token} Token
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
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, MultiValueType, TypeIdentType } = require('../anys');
|
||||||
const { NumberLiteral } = require('./literals/Number');
|
const { NumberLiteral } = require('./literals/Number');
|
||||||
const { checkTypeAssignable } = require('../expression-resolver');
|
const { checkTypeAssignable } = require('../expression-resolver');
|
||||||
|
|
||||||
@@ -47,47 +48,33 @@ class BinaryOpExpression extends Expression {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 (operator === 'instanceof') {
|
||||||
if (!(rhsvalue instanceof TypeIdentType)) {
|
if (!(rhsvalue instanceof TypeIdentType)) {
|
||||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Type expected`));
|
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Type expected`));
|
||||||
}
|
}
|
||||||
if (!lhstype) {
|
if (!(lhsvalue instanceof JavaType || lhsvalue instanceof NumberLiteral)) {
|
||||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Expression expected`));
|
|
||||||
}
|
|
||||||
return PrimitiveType.map.Z;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!lhstype || !rhstype) {
|
|
||||||
if (!lhstype) {
|
|
||||||
ri.problems.push(ParseProblem.Error(this.lhs.tokens, `Expression expected`));
|
ri.problems.push(ParseProblem.Error(this.lhs.tokens, `Expression expected`));
|
||||||
}
|
}
|
||||||
if (!rhstype) {
|
return PrimitiveType.map.Z;
|
||||||
ri.problems.push(ParseProblem.Error(this.rhs.tokens, `Expression expected`));
|
|
||||||
}
|
|
||||||
return AnyType.Instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
const typekey = `${lhstype.typeSignature}#${rhstype.typeSignature}`;
|
|
||||||
|
|
||||||
if (operator === '+' && /(^|#)Ljava\/lang\/String;/.test(typekey)) {
|
|
||||||
// string appending is compatible with all types
|
|
||||||
return ri.typemap.get('java/lang/String');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/^([*/%&|^+-]?=|<<=|>>>?=)$/.test(operator)) {
|
if (/^([*/%&|^+-]?=|<<=|>>>?=)$/.test(operator)) {
|
||||||
let src_type = rhsvalue;
|
let src_type = rhsvalue;
|
||||||
if (operator.length > 1) {
|
if (operator.length > 1) {
|
||||||
src_type = checkOperator(operator.slice(0,-1), ri, this.op, typekey, lhstype, rhstype);
|
const result_types = checkOperator(operator.slice(0,-1), ri, this.op, lhsvalue, rhsvalue);
|
||||||
|
src_type = Array.isArray(result_types) ? new MultiValueType(...result_types) : result_types;
|
||||||
}
|
}
|
||||||
checkTypeAssignable(lhstype, src_type, () => this.rhs.tokens, ri.problems);
|
if (lhsvalue instanceof JavaType) {
|
||||||
// result of assignments are lhs
|
checkTypeAssignable(lhsvalue, src_type, () => this.rhs.tokens, ri.problems);
|
||||||
return lhstype;
|
// result of assignments are lhs type
|
||||||
|
return lhsvalue;
|
||||||
|
}
|
||||||
|
ri.problems.push(ParseProblem.Error(this.op, `Invalid assignment`));
|
||||||
|
return AnyType.Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
return checkOperator(operator, ri, this.op, typekey, lhstype, rhstype);
|
const result_types = checkOperator(operator, ri, this.op, lhsvalue, rhsvalue);
|
||||||
|
return Array.isArray(result_types) ? new MultiValueType(...result_types) : result_types;
|
||||||
}
|
}
|
||||||
|
|
||||||
tokens() {
|
tokens() {
|
||||||
@@ -100,11 +87,51 @@ class BinaryOpExpression extends Expression {
|
|||||||
* @param {string} operator
|
* @param {string} operator
|
||||||
* @param {ResolveInfo} ri
|
* @param {ResolveInfo} ri
|
||||||
* @param {Token} operator_token
|
* @param {Token} operator_token
|
||||||
* @param {string} typekey
|
* @param {ResolvedValue} lhstype
|
||||||
* @param {JavaType} lhstype
|
* @param {ResolvedValue} rhstype
|
||||||
* @param {JavaType} rhstype
|
* @returns {JavaType|JavaType[]}
|
||||||
*/
|
*/
|
||||||
function checkOperator(operator, ri, operator_token, typekey, lhstype, rhstype) {
|
function checkOperator(operator, ri, operator_token, lhstype, rhstype) {
|
||||||
|
|
||||||
|
if (lhstype instanceof MultiValueType) {
|
||||||
|
/** @type {JavaType[]} */
|
||||||
|
let types = [];
|
||||||
|
lhstype.types.reduce((arr, type) => {
|
||||||
|
const types = checkOperator(operator, ri, operator_token, type, rhstype);
|
||||||
|
Array.isArray(types) ? arr.splice(arr.length, 0, ...types) : arr.push(types);
|
||||||
|
return arr;
|
||||||
|
}, types);
|
||||||
|
types = [...new Set(types)];
|
||||||
|
return types.length === 1 ? types[0] : types;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rhstype instanceof MultiValueType) {
|
||||||
|
/** @type {JavaType[]} */
|
||||||
|
let types = [];
|
||||||
|
rhstype.types.reduce((arr, type) => {
|
||||||
|
const types = checkOperator(operator, ri, operator_token, lhstype, type);
|
||||||
|
Array.isArray(types) ? arr.splice(arr.length, 0, ...types) : arr.push(types);
|
||||||
|
return arr;
|
||||||
|
}, types);
|
||||||
|
types = [...new Set(types)];
|
||||||
|
return types.length === 1 ? types[0] : types;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhstype instanceof NumberLiteral) {
|
||||||
|
lhstype = lhstype.type;
|
||||||
|
}
|
||||||
|
if (rhstype instanceof NumberLiteral) {
|
||||||
|
rhstype = rhstype.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(lhstype instanceof JavaType)) {
|
||||||
|
return AnyType.Instance;
|
||||||
|
}
|
||||||
|
if (!(rhstype instanceof JavaType)) {
|
||||||
|
return AnyType.Instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typekey = `${lhstype.typeSignature}#${rhstype.typeSignature}`;
|
||||||
|
|
||||||
if (operator === '+' && /(^|#)Ljava\/lang\/String;/.test(typekey)) {
|
if (operator === '+' && /(^|#)Ljava\/lang\/String;/.test(typekey)) {
|
||||||
// string appending is compatible with all types
|
// string appending is compatible with all types
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* @typedef {import('../tokenizer').Token} Token
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
const { Expression } = require("./Expression");
|
||||||
const { AnyType, AnyMethod, LambdaType, MethodType } = require('../anys');
|
const { AnyType, AnyMethod, LambdaType, MethodType, MultiValueType } = require('../anys');
|
||||||
const { ArrayType, JavaType, Method,PrimitiveType, ReifiedConstructor, ReifiedMethod, Constructor } = require('java-mti');
|
const { ArrayType, JavaType, Method,PrimitiveType, ReifiedConstructor, ReifiedMethod, Constructor } = require('java-mti');
|
||||||
const { NumberLiteral } = require('./literals/Number');
|
const { NumberLiteral } = require('./literals/Number');
|
||||||
const { InstanceLiteral } = require('./literals/Instance')
|
const { InstanceLiteral } = require('./literals/Instance')
|
||||||
@@ -69,10 +69,10 @@ function resolveMethodCall(ri, methods, args, tokens) {
|
|||||||
const resolved_args = args.map(arg => arg.resolveExpression(ri));
|
const resolved_args = args.map(arg => arg.resolveExpression(ri));
|
||||||
|
|
||||||
// all the arguments must be typed expressions, number literals or lambdas
|
// all the arguments must be typed expressions, number literals or lambdas
|
||||||
/** @type {(JavaType|NumberLiteral|LambdaType)[]} */
|
/** @type {(JavaType|NumberLiteral|LambdaType|MultiValueType)[]} */
|
||||||
const arg_types = [];
|
const arg_types = [];
|
||||||
resolved_args.forEach((a, idx) => {
|
resolved_args.forEach((a, idx) => {
|
||||||
if (a instanceof JavaType || a instanceof NumberLiteral || a instanceof LambdaType) {
|
if (a instanceof JavaType || a instanceof NumberLiteral || a instanceof LambdaType || a instanceof MultiValueType) {
|
||||||
arg_types.push(a);
|
arg_types.push(a);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -83,9 +83,11 @@ function resolveMethodCall(ri, methods, args, tokens) {
|
|||||||
|
|
||||||
// reify any methods with type-variables
|
// reify any methods with type-variables
|
||||||
// - lambda expressions can't be used as type arguments so just pass them as void
|
// - lambda expressions can't be used as type arguments so just pass them as void
|
||||||
|
// - multi-value types will dynamically chhose the type, but it's always a reference type (so assignable to Object)
|
||||||
const arg_java_types = arg_types.map(a =>
|
const arg_java_types = arg_types.map(a =>
|
||||||
a instanceof NumberLiteral ? a.type
|
a instanceof NumberLiteral ? a.type
|
||||||
: a instanceof LambdaType ? PrimitiveType.map.V
|
: a instanceof LambdaType ? PrimitiveType.map.V
|
||||||
|
: a instanceof MultiValueType ? ri.typemap.get('java/lang/Object')
|
||||||
: a);
|
: a);
|
||||||
const reified_methods = methods.map(m => {
|
const reified_methods = methods.map(m => {
|
||||||
if (m.typeVariables.length) {
|
if (m.typeVariables.length) {
|
||||||
@@ -214,7 +216,7 @@ function resolveConstructorCall(ri, constructors, args, tokens) {
|
|||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {Method|Constructor} m
|
* @param {Method|Constructor} m
|
||||||
* @param {(JavaType | NumberLiteral | LambdaType)[]} arg_types
|
* @param {(JavaType | NumberLiteral | LambdaType | MultiValueType)[]} arg_types
|
||||||
*/
|
*/
|
||||||
function isCallCompatible(m, arg_types) {
|
function isCallCompatible(m, arg_types) {
|
||||||
if (m instanceof AnyMethod) {
|
if (m instanceof AnyMethod) {
|
||||||
|
|||||||
Reference in New Issue
Block a user