mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-22 17:39:19 +00:00
Improve evaluation support
Allow arithmetic, bitwise, logical and relational operators and primitive casts.
This commit is contained in:
@@ -114,6 +114,7 @@
|
||||
"dependencies": {
|
||||
"vscode-debugprotocol": "^1.15.0",
|
||||
"vscode-debugadapter": "^1.15.0",
|
||||
"long":"^3.2.0",
|
||||
"ws":"^1.1.1",
|
||||
"xmldom": "^0.1.27",
|
||||
"xpath": "^0.0.23"
|
||||
|
||||
347
src/debugMain.js
347
src/debugMain.js
@@ -14,6 +14,7 @@ const DebugProtocol = { //require('vscode-debugprotocol');
|
||||
const crypto = require('crypto');
|
||||
const dom = require('xmldom').DOMParser;
|
||||
const fs = require('fs');
|
||||
const Long = require('long');
|
||||
const path = require('path');
|
||||
const xpath = require('xpath');
|
||||
// our stuff
|
||||
@@ -26,22 +27,26 @@ const ws_proxy = require('./wsproxy').proxy.Server(6037, 5037);
|
||||
|
||||
// some commonly used Java types in debugger-compatible format
|
||||
const JTYPES = {
|
||||
byte: {name:'int',signature:'B'},
|
||||
short: {name:'short',signature:'S'},
|
||||
int: {name:'int',signature:'I'},
|
||||
long: {name:'long',signature:'J'},
|
||||
float: {name:'float',signature:'F'},
|
||||
double: {name:'double',signature:'D'},
|
||||
char: {name:'char',signature:'C'},
|
||||
boolean: {name:'boolean',signature:'Z'},
|
||||
null: {name:'null',signature:'Lnull;'}, // null has no type really, but we need something for literals
|
||||
String: {name:'String',signature:'Ljava/lang/String;'},
|
||||
Object: {name:'Object',signature:'Ljava/lang/Object;'},
|
||||
byte: {typename:'byte',signature:'B'},
|
||||
short: {typename:'short',signature:'S'},
|
||||
int: {typename:'int',signature:'I'},
|
||||
long: {typename:'long',signature:'J'},
|
||||
float: {typename:'float',signature:'F'},
|
||||
double: {typename:'double',signature:'D'},
|
||||
char: {typename:'char',signature:'C'},
|
||||
boolean: {typename:'boolean',signature:'Z'},
|
||||
null: {typename:'null',signature:'Lnull;'}, // null has no type really, but we need something for literals
|
||||
String: {typename:'String',signature:'Ljava/lang/String;'},
|
||||
Object: {typename:'Object',signature:'Ljava/lang/Object;'},
|
||||
isArray(t) { return t.signature[0]==='[' },
|
||||
isObject(t) { return t.signature[0]==='L' },
|
||||
isReference(t) { return /^[L[]/.test(t.signature) },
|
||||
isPrimitive(t) { return !JTYPES.isReference(t.signature) },
|
||||
isInteger(t) { return /^[BIJS]$/.test(t.signature) },
|
||||
isInteger(t) { return /^[BCIJS]$/.test(t.signature) },
|
||||
isNumber(t) { return /^[BCIJSFD]$/.test(t.signature) },
|
||||
isString(t) { return t.signature === this.String.signature },
|
||||
isChar(t) { return t.signature === this.char.signature },
|
||||
isBoolean(t) { return t.signature === this.boolean.signature },
|
||||
fromPrimSig(sig) { return JTYPES['byte,short,int,long,float,double,char,boolean'.split(',')['BSIJFDCZ'.indexOf(sig)]] },
|
||||
}
|
||||
|
||||
@@ -785,12 +790,12 @@ class AndroidDebugSession extends DebugSession {
|
||||
objvalue = v.type.typename;
|
||||
break;
|
||||
case v.type.signature === 'C':
|
||||
const cmap = {'\b':'b','\f':'f','\r':'r','\n':'n','\t':'t','\v':'v','\'':'\'','\\':'\\'}, cc = v.value.charCodeAt(0);
|
||||
if (cmap[v.value]) {
|
||||
objvalue = `'\\${cmap[v.value]}'`;
|
||||
} else if (cc < 32) {
|
||||
objvalue = cc ? `'\\u${('000'+cc.toString(16)).slice(-4)}'` : "'\\0'";
|
||||
} else objvalue = `'${v.value}'`;
|
||||
const cmap = {'\b':'b','\f':'f','\r':'r','\n':'n','\t':'t','\v':'v','\'':'\'','\\':'\\'};
|
||||
if (cmap[v.char]) {
|
||||
objvalue = `'\\${cmap[v.char]}'`;
|
||||
} else if (v.value < 32) {
|
||||
objvalue = v.value ? `'\\u${('000'+v.value.toString(16)).slice(-4)}'` : "'\\0'";
|
||||
} else objvalue = `'${v.char}'`;
|
||||
break;
|
||||
case v.type.signature === 'J':
|
||||
// because JS cannot handle 64bit ints, we need a bit of extra work
|
||||
@@ -1333,14 +1338,42 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
const parse_expression = function(e) {
|
||||
var root_term = e.expr.match(/^(?:(true(?![\w$]))|(false(?![\w$]))|(null(?![\w$]))|([a-zA-Z_$][a-zA-Z0-9_$]*)|(\d+(?:\.\d+)?)|('[^\\']')|('\\[bfrntv0]')|('\\u[0-9a-fA-F]{4}')|("[^"]*"))/);
|
||||
const parse_expression_term = function(e) {
|
||||
if (e.expr[0] === '(') {
|
||||
e.expr = e.expr.slice(1).trim();
|
||||
var subexpr = {expr:e.expr};
|
||||
var res = parse_expression(subexpr);
|
||||
if (res) {
|
||||
if (subexpr.expr[0] !== ')') return null;
|
||||
e.expr = subexpr.expr.slice(1).trim();
|
||||
if (/^(int|long|byte|short|double|float|char|boolean)$/.test(res.root_term) && !res.members.length && !res.array_or_fncall.call && !res.array_or_fncall.arr.length) {
|
||||
// primitive typecast
|
||||
var castexpr = parse_expression_term(e);
|
||||
if (castexpr) castexpr.typecast = res.root_term;
|
||||
res = castexpr;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
var unop = e.expr.match(/^(?:(!\s?)+|(~\s?)+|(?:([+-]\s?)+(?![\d.])))/);
|
||||
if (unop) {
|
||||
var op = unop[0].replace(/\s/g,'');
|
||||
e.expr = e.expr.slice(unop[0].length).trim();
|
||||
var res = parse_expression_term(e);
|
||||
if (res) {
|
||||
for (var i=op.length-1; i >= 0; --i)
|
||||
res = { operator:op[i], rhs:res };
|
||||
}
|
||||
return res;
|
||||
}
|
||||
var root_term = e.expr.match(/^(?:(true(?![\w$]))|(false(?![\w$]))|(null(?![\w$]))|([a-zA-Z_$][a-zA-Z0-9_$]*)|([+-]?0x[0-9a-fA-F]+[lL]?)|([+-]?0[0-7]+[lL]?)|([+-]?\d+\.\d+(?:[eE][+-]?\d+)?[fFdD]?)|([+-]?\d+[lL]?)|('[^\\']')|('\\[bfrntv0]')|('\\u[0-9a-fA-F]{4}')|("[^"]*"))/);
|
||||
if (!root_term) return null;
|
||||
var res = {
|
||||
root_term: root_term[0],
|
||||
root_term_type: ['boolean','boolean','null','ident','number','char','echar','uchar','string'][[1,2,3,4,5,6,7,8,9].find(x => root_term[x])-1],
|
||||
root_term_type: ['boolean','boolean','null','ident','hexint','octint','decfloat','decint','char','echar','uchar','string'][[1,2,3,4,5,6,7,8,9,10,11,12].find(x => root_term[x])-1],
|
||||
array_or_fncall: null,
|
||||
members:[],
|
||||
typecast:''
|
||||
}
|
||||
e.expr = e.expr.slice(res.root_term.length).trim();
|
||||
if ((res.array_or_fncall = parse_array_or_fncall(e)) === null) return null;
|
||||
@@ -1357,16 +1390,232 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
return res;
|
||||
}
|
||||
const prec = {
|
||||
'*':1,'%':1,'/':1,
|
||||
'+':2,'-':2,
|
||||
'<<':3,'>>':3,'>>>':3,
|
||||
'<':4,'>':4,'<=':4,'>=':4,'instanceof':4,
|
||||
'==':5,'!=':5,
|
||||
'&':6,'^':7,'|':8,'&&':9,'||':10,'?':11,
|
||||
}
|
||||
const parse_expression = function(e) {
|
||||
var res = parse_expression_term(e);
|
||||
|
||||
if (!e.currprec) e.currprec = [12];
|
||||
for (;;) {
|
||||
var binary_operator = e.expr.match(/^([/%*&|^+-]=|<<=|>>>?=|[><!=]=|=|<<|>>>?|[><]|&&|\|\||[/%*&|^]|\+(?=[^+]|[+][\w\d.])|\-(?=[^-]|[-][\w\d.])|instanceof\b|\?)/ );
|
||||
if (!binary_operator) break;
|
||||
var precdiff = (prec[binary_operator[0]] || 12) - e.currprec[0];
|
||||
if (precdiff > 0) {
|
||||
// bigger number -> lower precendence -> end of (sub)expression
|
||||
break;
|
||||
}
|
||||
if (precdiff === 0 && binary_operator[0]!=='?') {
|
||||
// equal precedence, ltr evaluation
|
||||
break;
|
||||
}
|
||||
// higher or equal precendence
|
||||
e.currprec.unshift(e.currprec[0]+precdiff);
|
||||
e.expr = e.expr.slice(binary_operator[0].length).trim();
|
||||
// current or higher precendence
|
||||
if (binary_operator[0]==='?') {
|
||||
res = { condition:res, operator:binary_operator[0], ternary_true:null, ternary_false:null };
|
||||
res.ternary_true = parse_expression(o);
|
||||
symbol(e,':');
|
||||
res.ternary_false = parse_expression(o);
|
||||
} else {
|
||||
res = { lhs:res, operator:binary_operator[0], rhs:parse_expression(e) };
|
||||
}
|
||||
e.currprec.shift();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
const hex_long = long => ('000000000000000' + long.toUnsigned().toString(16)).slice(-16);
|
||||
const evaluate_number = (n) => {
|
||||
const numtype = /\./.test(n) ? JTYPES.double : JTYPES.int;
|
||||
const iszero = /^0+(\.0*)?$/.test(n);
|
||||
return { vtype:'literal',name:'',hasnullvalue:iszero,type:numtype,value:n,valid:true };
|
||||
n += '';
|
||||
var numtype,m = n.match(/^([+-]?)0([bBxX0-7])(.+)/), base=10;
|
||||
if (m) {
|
||||
switch(m[2]) {
|
||||
case 'b': base = 2; n = m[1]+m[3]; break;
|
||||
case 'x': base = 16; n = m[1]+m[3]; break;
|
||||
default: base = 8; break;
|
||||
}
|
||||
}
|
||||
if (base !== 16 && /[fFdD]$/.test(n)) {
|
||||
numtype = /[fF]$/.test(n) ? 'float' : 'double';
|
||||
n = n.slice(0,-1);
|
||||
} else if (/[lL]$/.test(n)) {
|
||||
numtype = 'long'
|
||||
n = n.slice(0,-1);
|
||||
} else {
|
||||
numtype = /\./.test(n) ? 'double' : 'int';
|
||||
}
|
||||
if (numtype === 'long') n = hex_long(Long.fromString(n, false, base));
|
||||
else if (/^[fd]/.test(numtype)) n = (base === 10) ? parseFloat(n) : parseInt(n, base);
|
||||
else n = parseInt(n, base)|0;
|
||||
|
||||
const iszero = /^[+-]?0+(\.0*)?$/.test(n);
|
||||
return { vtype:'literal',name:'',hasnullvalue:iszero,type:JTYPES[numtype],value:n,valid:true };
|
||||
}
|
||||
const evaluate_char = (char) => {
|
||||
return { vtype:'literal',name:'',char:char,hasnullvalue:false,type:JTYPES.char,value:char.charCodeAt(0),valid:true };
|
||||
}
|
||||
const numberify = (local) => {
|
||||
//if (local.type.signature==='C') return local.char.charCodeAt(0);
|
||||
if (/^[FD]$/.test(local.type.signature))
|
||||
return parseFloat(local.value);
|
||||
if (local.type.signature==='J')
|
||||
return parseInt(local.value,16);
|
||||
return parseInt(local.value,10);
|
||||
}
|
||||
const stringify = (local) => {
|
||||
var s;
|
||||
if (JTYPES.isString(local.type)) s = local.string;
|
||||
else if (JTYPES.isChar(local.type)) s= local.char;
|
||||
else if (JTYPES.isPrimitive(local.type)) s = ''+local.value;
|
||||
else if (local.hasnullvalue) s= '(null)';
|
||||
if (typeof s === 'string')
|
||||
return $.Deferred().resolveWith(this,[s]);
|
||||
return this.dbgr.invokeToString(local.value, local.info.frame.threadid, local.type.signature)
|
||||
.then(s => s.string);
|
||||
}
|
||||
const evaluate_expression = (expr) => {
|
||||
var q = $.Deferred(), local;
|
||||
if (expr.operator) {
|
||||
const invalid_operator = (unary) => reject_evaluation(`Invalid ${unary?'type':'types'} for operator '${expr.operator}'`),
|
||||
divide_by_zero = () => reject_evaluation('ArithmeticException: divide by zero');
|
||||
var lhs_local;
|
||||
return !expr.lhs
|
||||
? // unary operator
|
||||
evaluate_expression(expr.rhs)
|
||||
.then(rhs_local => {
|
||||
if (expr.operator === '!' && JTYPES.isBoolean(rhs_local.type)) {
|
||||
rhs_local.value = !rhs_local.value;
|
||||
return rhs_local;
|
||||
}
|
||||
else if (expr.operator === '~' && JTYPES.isInteger(rhs_local.type)) {
|
||||
switch (rhs_local.type.typename) {
|
||||
case 'long': rhs_local.value = rhs_local.value.replace(/./g,c => (15 - parseInt(c,16)).toString(16)); break;
|
||||
default: rhs_local = evaluate_number(''+~rhs_local.value); break;
|
||||
}
|
||||
return rhs_local;
|
||||
}
|
||||
else if (/[+-]/.test(expr.operator) && JTYPES.isInteger(rhs_local.type)) {
|
||||
if (expr.operator === '+') return rhs_local;
|
||||
switch (rhs_local.type.typename) {
|
||||
case 'long': rhs_local.value = hex_long(Long.fromString(rhs_local.value,false,16).neg()); break;
|
||||
default: rhs_local = evaluate_number(''+(-rhs_local.value)); break;
|
||||
}
|
||||
return rhs_local;
|
||||
}
|
||||
return invalid_operator('unary');
|
||||
})
|
||||
: // binary operator
|
||||
evaluate_expression(expr.lhs)
|
||||
.then(x => (lhs_local = x) && evaluate_expression(expr.rhs))
|
||||
.then(rhs_local => {
|
||||
if ((lhs_local.type.signature==='J' && JTYPES.isInteger(rhs_local.type))
|
||||
|| (rhs_local.type.signature==='J' && JTYPES.isInteger(lhs_local.type))) {
|
||||
// one operand is a long, the other is an integer -> the result is a long
|
||||
var a,b, lbase, rbase;
|
||||
lbase = lhs_local.type.signature==='J' ? 16 : 10;
|
||||
rbase = rhs_local.type.signature==='J' ? 16 : 10;
|
||||
a = Long.fromString(''+lhs_local.value, false, lbase);
|
||||
b = Long.fromString(''+rhs_local.value, false, rbase);
|
||||
switch(expr.operator) {
|
||||
case '+': a = a.add(b); break;
|
||||
case '-': a = a.subtract(b); break;
|
||||
case '*': a = a.multiply(b); break;
|
||||
case '/': if (!b.isZero()) { a = a.divide(b); break } return divide_by_zero();
|
||||
case '%': if (!b.isZero()) { a = a.mod(b); break; } return divide_by_zero();
|
||||
case '<<': a = a.shl(b); break;
|
||||
case '>>': a = a.shr(b); break;
|
||||
case '>>>': a = a.shru(b); break;
|
||||
case '&': a = a.and(b); break;
|
||||
case '|': a = a.or(b); break;
|
||||
case '^': a = a.xor(b); break;
|
||||
case '==': a = a.eq(b); break;
|
||||
case '!=': a = !a.eq(b); break;
|
||||
case '<': a = a.lt(b); break;
|
||||
case '<=': a = a.lte(b); break;
|
||||
case '>': a = a.gt(b); break;
|
||||
case '>=': a = a.gte(b); break;
|
||||
default: return invalid_operator();
|
||||
}
|
||||
if (typeof a === 'boolean')
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:a,valid:true };
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.long,value:hex_long(a),valid:true };
|
||||
}
|
||||
else if (JTYPES.isInteger(lhs_local.type) && JTYPES.isInteger(rhs_local.type)) {
|
||||
// both are (non-long) integer types
|
||||
var a = numberify(lhs_local), b= numberify(rhs_local);
|
||||
switch(expr.operator) {
|
||||
case '+': a += b; break;
|
||||
case '-': a -= b; break;
|
||||
case '*': a *= b; break;
|
||||
case '/': if (b) { a = Math.trunc(a/b); break } return divide_by_zero();
|
||||
case '%': if (b) { a %= b; break; } return divide_by_zero();
|
||||
case '<<': a<<=b; break;
|
||||
case '>>': a>>=b; break;
|
||||
case '>>>': a>>>=b; break;
|
||||
case '&': a&=b; break;
|
||||
case '|': a|=b; break;
|
||||
case '^': a^=b; break;
|
||||
case '==': a = a===b; break;
|
||||
case '!=': a = a!==b; break;
|
||||
case '<': a = a<b; break;
|
||||
case '<=': a = a<=b; break;
|
||||
case '>': a = a>b; break;
|
||||
case '>=': a = a>=b; break;
|
||||
default: return invalid_operator();
|
||||
}
|
||||
if (typeof a === 'boolean')
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:a,valid:true };
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.int,value:''+a,valid:true };
|
||||
}
|
||||
else if (JTYPES.isNumber(lhs_local.type) && JTYPES.isNumber(rhs_local.type)) {
|
||||
var a = numberify(lhs_local), b= numberify(rhs_local);
|
||||
switch(expr.operator) {
|
||||
case '+': a += b; break;
|
||||
case '-': a -= b; break;
|
||||
case '*': a *= b; break;
|
||||
case '/': a /= b; break;
|
||||
case '==': a = a===b; break;
|
||||
case '!=': a = a!==b; break;
|
||||
case '<': a = a<b; break;
|
||||
case '<=': a = a<=b; break;
|
||||
case '>': a = a>b; break;
|
||||
case '>=': a = a>=b; break;
|
||||
default: return invalid_operator();
|
||||
}
|
||||
if (typeof a === 'boolean')
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:a,valid:true };
|
||||
// one of them must be a float or double
|
||||
var result_type = 'float double'.split(' ')[Math.max("FD".indexOf(lhs_local.type.signature), "FD".indexOf(rhs_local.type.signature))];
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES[result_type],value:''+a,valid:true };
|
||||
}
|
||||
else if (lhs_local.type.signature==='Z' && rhs_local.type.signature==='Z') {
|
||||
// boolean operands
|
||||
var a = lhs_local.value, b = rhs_local.value;
|
||||
switch(expr.operator) {
|
||||
case '&': case '&&': a = a && b; break;
|
||||
case '|': case '||': a = a || b; break;
|
||||
case '^': a = !!(a ^ b); break;
|
||||
case '==': a = a===b; break;
|
||||
case '!=': a = a!==b; break;
|
||||
default: return invalid_operator();
|
||||
}
|
||||
return { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:a,valid:true };
|
||||
}
|
||||
else if (expr.operator === '+' && JTYPES.isString(lhs_local.type)) {
|
||||
return stringify(rhs_local).then(rhs_str => this.createJavaString(lhs_local.string + rhs_str,{israw:true}));
|
||||
}
|
||||
return invalid_operator();
|
||||
});
|
||||
}
|
||||
switch(expr.root_term_type) {
|
||||
case 'boolean':
|
||||
local = { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:expr.root_term,valid:true };
|
||||
local = { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.boolean,value:expr.root_term!=='false',valid:true };
|
||||
break;
|
||||
case 'null':
|
||||
const nullvalue = '0000000000000000'; // null reference value
|
||||
@@ -1375,13 +1624,16 @@ class AndroidDebugSession extends DebugSession {
|
||||
case 'ident':
|
||||
local = locals && locals.find(l => l.name === expr.root_term);
|
||||
break;
|
||||
case 'number':
|
||||
case 'hexint':
|
||||
case 'octint':
|
||||
case 'decint':
|
||||
case 'decfloat':
|
||||
local = evaluate_number(expr.root_term);
|
||||
break;
|
||||
case 'char':
|
||||
case 'echar':
|
||||
case 'uchar':
|
||||
local = { vtype:'literal',name:'',hasnullvalue:false,type:JTYPES.char,value:decode_char(expr.root_term.slice(1,-1)),valid:true };
|
||||
local = evaluate_char(decode_char(expr.root_term.slice(1,-1)))
|
||||
break;
|
||||
case 'string':
|
||||
// we must get the runtime to create string instances
|
||||
@@ -1397,6 +1649,9 @@ class AndroidDebugSession extends DebugSession {
|
||||
q = expr.members.reduce((q,m) => {
|
||||
return q.then(function(m,local) { return evaluate_member.call(this,m,local) }.bind(this,m));
|
||||
}, q);
|
||||
if (expr.typecast) {
|
||||
q = q.then(function(type,local) { return evaluate_cast.call(this,type,local) }.bind(this,expr.typecast) )
|
||||
}
|
||||
// if it's a string literal, we are already waiting for the runtime to create the string
|
||||
// - otherwise, start the evalaution...
|
||||
if (expr.root_term_type !== 'string')
|
||||
@@ -1404,13 +1659,13 @@ class AndroidDebugSession extends DebugSession {
|
||||
return q;
|
||||
}
|
||||
const evaluate_array_element = (index_expr, arr_local) => {
|
||||
if (arr_local.type.signature[0] !== '[') return reject_evaluation('TypeError: value is not an array');
|
||||
if (arr_local.type.signature[0] !== '[') return reject_evaluation(`TypeError: cannot apply array index to non-array type '${arr_local.type.typename}'`);
|
||||
if (arr_local.hasnullvalue) return reject_evaluation('NullPointerException');
|
||||
return evaluate_expression(index_expr)
|
||||
.then(function(arr_local, idx_local) {
|
||||
if (!JTYPES.isInteger(idx_local.type)) return reject_evaluation('TypeError: array index is not an integer value');
|
||||
var idx = parseInt(idx_local.value,10);
|
||||
if (idx < 0 || idx >= arr_local.arraylen) return reject_evaluation('BoundsError: array index out of bounds');
|
||||
var idx = numberify(idx_local);
|
||||
if (idx < 0 || idx >= arr_local.arraylen) return reject_evaluation(`BoundsError: array index (${idx}) out of bounds. Array length = ${arr_local.arraylen}`);
|
||||
return this.dbgr.getarrayvalues(arr_local, idx, 1)
|
||||
}.bind(this,arr_local))
|
||||
.then(els => els[0])
|
||||
@@ -1448,6 +1703,38 @@ class AndroidDebugSession extends DebugSession {
|
||||
}
|
||||
});
|
||||
}
|
||||
const evaluate_cast = (type,local) => {
|
||||
if (type === local.type.typename) return local;
|
||||
const incompatible_cast = () => reject_evaluation(`Incompatible cast from ${local.type.typename} to ${type}`);
|
||||
// boolean cannot be converted from anything else
|
||||
if (type === 'boolean' || local.type.typename === 'boolean') return incompatible_cast();
|
||||
if (local.type.typename === 'long') {
|
||||
// long to something else
|
||||
var value = Long.fromString(local.value, true, 16);
|
||||
switch(true) {
|
||||
case (type === 'byte'): local = evaluate_number((parseInt(value.toString(16).slice(-2),16) << 24) >> 24); break;
|
||||
case (type === 'short'): local = evaluate_number((parseInt(value.toString(16).slice(-4),16) << 16) >> 16); break;
|
||||
case (type === 'int'): local = evaluate_number((parseInt(value.toString(16).slice(-8),16) | 0)); break;
|
||||
case (type === 'char'): local = evaluate_char(String.fromCharCode(parseInt(value.toString(16).slice(-4),16))); break;
|
||||
case (type === 'float'): local = evaluate_number(value.toSigned().toNumber()+'F'); break;
|
||||
case (type === 'double'): local = evaluate_number(value.toSigned().toNumber() + 'D'); break;
|
||||
default: return incompatible_cast();
|
||||
}
|
||||
} else {
|
||||
switch(true) {
|
||||
case (type === 'byte'): local = evaluate_number((local.value << 24) >> 24); break;
|
||||
case (type === 'short'): local = evaluate_number((local.value << 16) >> 16); break;
|
||||
case (type === 'int'): local = evaluate_number((local.value | 0)); break;
|
||||
case (type === 'long'): local = evaluate_number(local.value+'L');break;
|
||||
case (type === 'char'): local = evaluate_char(String.fromCharCode(local.value|0)); break;
|
||||
case (type === 'float'): break;
|
||||
case (type === 'double'): break;
|
||||
default: return incompatible_cast();
|
||||
}
|
||||
}
|
||||
local.type = JTYPES[type];
|
||||
return local;
|
||||
}
|
||||
|
||||
var e = { expr:expression.trim() };
|
||||
var parsed_expression = parse_expression(e);
|
||||
|
||||
Reference in New Issue
Block a user