mirror of
https://github.com/rizinorg/jsdec.git
synced 2026-11-09 17:49:57 -10:00
523 lines
14 KiB
JavaScript
523 lines
14 KiB
JavaScript
// SPDX-FileCopyrightText: 2017-2023 Giovanni Dante Grazioli <[email protected]>
|
|
// SPDX-FileCopyrightText: 2018-2019 elicn
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
import Cpp from '../db/cpp.js';
|
|
import CCalls from '../db/c_calls.js';
|
|
import Extra from './extra.js';
|
|
import Variable from './variable.js';
|
|
import Condition from './condition.js';
|
|
import Long from '../long.js';
|
|
|
|
/**
|
|
* Wraps a string with parenthesis.
|
|
* @param {string} s A string to wrap
|
|
* @returns {string} `s` wrapped by parenthesis
|
|
*/
|
|
var parenthesize = function(s) {
|
|
return ['(', s, ')'].join('');
|
|
};
|
|
|
|
/**
|
|
* Wraps a string with parenthesis only if it is complex.
|
|
* @param {string} s A string to wrap
|
|
* @returns {string} `s` wrapped by parenthesis if `s` is a complex string, and `s` otherwise
|
|
*/
|
|
var autoParen = function(s) {
|
|
return (s.indexOf(' ') > (-1) ? parenthesize(s) : s);
|
|
};
|
|
|
|
var autoString = function(v) {
|
|
v = v || "";
|
|
return Extra.is.string(v) ? Global().printer.auto(v) : v.toString();
|
|
};
|
|
|
|
var _generic_asm = function(asm) {
|
|
this.asm = asm;
|
|
|
|
this.toString = function() {
|
|
return Global().printer.theme.callname('__asm') + ' (' + Global().printer.theme.text('"' + this.asm + '"') + ')';
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Unary expression
|
|
* @constructor
|
|
* @inner
|
|
*/
|
|
var _uexpr = function(operator, operand) {
|
|
this.operator = operator;
|
|
|
|
this.operands = [
|
|
autoString(operand)
|
|
];
|
|
|
|
/** @returns {!string} */
|
|
this.toString = function() {
|
|
return [
|
|
this.operator,
|
|
this.operands[0]
|
|
].join('');
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Unary expression with postfix notation
|
|
* @constructor
|
|
* @inner
|
|
*/
|
|
var _uexpr_pf = function(operator, operand) {
|
|
_uexpr.call(this, operator, [operand]);
|
|
|
|
/** @returns {!string} */
|
|
this.toString = function() {
|
|
return [
|
|
this.operands[0],
|
|
this.operator,
|
|
].join('');
|
|
};
|
|
};
|
|
|
|
_uexpr_pf.prototype = Object.create(_uexpr.prototype);
|
|
|
|
/**
|
|
* Binary expression
|
|
* @constructor
|
|
* @inner
|
|
*/
|
|
var _bexpr = function(operator, lhand, rhand) {
|
|
this.operator = operator;
|
|
|
|
this.operands = [
|
|
autoString(lhand),
|
|
autoString(rhand)
|
|
];
|
|
|
|
/** @returns {!string} */
|
|
this.toString = function() {
|
|
return [
|
|
this.operands[0],
|
|
this.operator,
|
|
this.operands[1]
|
|
].join(' ');
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Ternary expression
|
|
* @constructor
|
|
* @inner
|
|
*/
|
|
var _texpr = function(operator1, operator2, operand1, operand2, operand3) {
|
|
this.operators = [operator1, operator2];
|
|
|
|
this.operands = [
|
|
autoParen(autoString(operand1)),
|
|
autoParen(autoString(operand2)),
|
|
autoParen(autoString(operand3))
|
|
];
|
|
|
|
/** @returns {!string} */
|
|
this.toString = function() {
|
|
return [
|
|
this.operands[0],
|
|
this.operators[0],
|
|
this.operands[1],
|
|
this.operators[1],
|
|
this.operands[2]
|
|
].join(' ');
|
|
};
|
|
};
|
|
|
|
var _assign = function(lhand, rhand) {
|
|
if (lhand == rhand) {
|
|
return '';
|
|
}
|
|
|
|
if ((rhand instanceof _bexpr) && (autoString(lhand) == rhand.operands[0])) {
|
|
return new _bexpr(rhand.operator + '=', lhand, rhand.operands[1]);
|
|
}
|
|
|
|
if (rhand instanceof Variable.functionPointer) {
|
|
rhand = parenthesize(rhand);
|
|
}
|
|
|
|
return new _bexpr('=', lhand, rhand);
|
|
};
|
|
|
|
var _cast = function(source, type) {
|
|
return new _uexpr(parenthesize(Global().printer.theme.types(type)) + ' ', source);
|
|
};
|
|
|
|
var _generic_call = function(function_name, args) {
|
|
this.function_name = Extra.is.string(function_name) ? Cpp(Extra.replace.call(function_name)) : function_name;
|
|
this.callargs = args || [];
|
|
|
|
this.toString = function() {
|
|
var fname = this.function_name;
|
|
|
|
if (Extra.is.string(fname)) {
|
|
fname = Global().printer.theme.callname(fname);
|
|
}
|
|
|
|
return [fname, parenthesize(this.callargs.join(', '))].join(' ');
|
|
};
|
|
};
|
|
|
|
var _generic_method_call = function(object_this, function_name, args, method_sep, prefix, postfix) {
|
|
this.object_this = Extra.is.string(object_this) ? Cpp(object_this) : object_this;
|
|
this.function_name = Extra.is.string(function_name) ? Cpp(function_name) : function_name;
|
|
this.callargs = args || [];
|
|
this.method_sep = method_sep;
|
|
this.prefix = prefix || "";
|
|
this.postfix = postfix || "";
|
|
|
|
this.toString = function() {
|
|
var fname = this.function_name;
|
|
|
|
if (Extra.is.string(fname)) {
|
|
fname = Global().printer.theme.callname(fname);
|
|
}
|
|
|
|
return this.prefix + this.object_this + this.method_sep + [fname, parenthesize(this.callargs.join(', '))].join(' ') + this.postfix;
|
|
};
|
|
};
|
|
|
|
var _generic_rotate = function(destination, source_a, rotation, bits, is_left) {
|
|
this.call = 'rotate_' + (is_left ? 'left' : 'right') + bits;
|
|
this.destination = destination;
|
|
this.source_a = source_a;
|
|
this.rotation = Extra.is.number(rotation) ? ("0x" + rotation.toString(16)) : rotation;
|
|
|
|
this.toString = function() {
|
|
var args = [autoString(this.source_a), autoString(this.rotation)];
|
|
|
|
return [autoString(this.destination), '=',
|
|
Global().printer.theme.callname(this.call),
|
|
parenthesize(args.join(', '))
|
|
].join(' ');
|
|
};
|
|
};
|
|
|
|
var _generic_return = function(value) {
|
|
this.value = value;
|
|
|
|
this.toString = function(options) {
|
|
var value = '';
|
|
|
|
if (this.value) {
|
|
value = ' ' + autoString(this.value);
|
|
}
|
|
|
|
return Global().printer.theme.flow('return') + value;
|
|
};
|
|
};
|
|
|
|
var _generic_goto = function(label_or_address) {
|
|
this.value = label_or_address;
|
|
|
|
this.toString = function(options) {
|
|
return [Global().printer.theme.flow('goto'), autoString(this.value)].join(' ');
|
|
};
|
|
};
|
|
|
|
var _generic_flow = function(name) {
|
|
this.name = name;
|
|
|
|
this.toString = function(options) {
|
|
return Global().printer.theme.flow(this.name);
|
|
};
|
|
};
|
|
|
|
var _assign_object_field = function(destination, object, field, method_sep, is_from) {
|
|
this.destination = destination;
|
|
this.object = object;
|
|
this.field = field;
|
|
this.method_sep = method_sep;
|
|
this.is_from = is_from;
|
|
|
|
this.toString = function(options) {
|
|
if (this.is_from) {
|
|
return [
|
|
this.object + this.method_sep + this.field,
|
|
'=',
|
|
this.destination
|
|
].join(' ');
|
|
}
|
|
return [
|
|
this.destination,
|
|
'=',
|
|
this.object + this.method_sep + this.field
|
|
].join(' ');
|
|
};
|
|
};
|
|
|
|
var _assign_array_at = function(destination, source, index, is_from) {
|
|
this.destination = destination;
|
|
this.source = source;
|
|
this.index = index;
|
|
this.is_from = is_from;
|
|
|
|
this.toString = function(options) {
|
|
if (this.is_from) {
|
|
return [
|
|
this.destination,
|
|
'=',
|
|
this.source,
|
|
'[' + this.index + ']'
|
|
].join(' ');
|
|
}
|
|
return [
|
|
this.destination,
|
|
'[' + this.index + ']',
|
|
'=',
|
|
this.source
|
|
].join(' ');
|
|
};
|
|
};
|
|
|
|
var _generic_throw = function(value) {
|
|
this.value = value;
|
|
|
|
this.toString = function(options) {
|
|
var value = '';
|
|
|
|
if (this.value) {
|
|
value = ' ' + autoString(this.value);
|
|
}
|
|
|
|
return Global().printer.theme.flow('throw') + value;
|
|
};
|
|
};
|
|
|
|
var _generic_objc_call = function(object_this, function_name, function_arguments) {
|
|
this.object_this = object_this;
|
|
this.function_name = function_name;
|
|
this.callargs = function_arguments || [];
|
|
|
|
this.toString = function() {
|
|
var fname = this.object_this;
|
|
if (Extra.is.string(object_this)) {
|
|
fname = Global().printer.theme.callname(object_this);
|
|
}
|
|
|
|
return "[" + [fname, this.function_name].concat(this.callargs).join(' ') + "]";
|
|
};
|
|
};
|
|
|
|
export default {
|
|
/* COMMON */
|
|
assign: _assign,
|
|
cast: function(destination, source, type) {
|
|
return _assign(destination, new _cast(source, type));
|
|
},
|
|
nop: function(asm) {
|
|
return null;
|
|
},
|
|
/* JUMPS */
|
|
goto: function(label_or_address) {
|
|
return new _generic_goto(label_or_address);
|
|
},
|
|
call: function(function_name, function_arguments) {
|
|
return new _generic_call(function_name, function_arguments);
|
|
},
|
|
return: function(value) {
|
|
return new _generic_return(value);
|
|
},
|
|
break: function(value) {
|
|
return new _generic_flow('break');
|
|
},
|
|
continue: function(value) {
|
|
return new _generic_flow('continue');
|
|
},
|
|
/* BRANCHES */
|
|
conditional_assign: function(destination, source_a, source_b, cond, src_true, src_false) {
|
|
var condition = new Condition.convert(source_a, source_b, cond, false);
|
|
|
|
return _assign(destination, new _texpr('?', ':', condition.toString(), src_true, src_false));
|
|
},
|
|
conditional_math: function(destination, source_a, source_b, cond, math_operand_a, math_operand_b, src_false, operation) {
|
|
var condition = new Condition.convert(source_a, source_b, cond, false);
|
|
var src_true = new _bexpr(operation, math_operand_a, math_operand_b);
|
|
|
|
return _assign(destination, new _texpr('?', ':', condition.toString(), src_true, src_false));
|
|
},
|
|
/* MATH */
|
|
increase: function(destination, source) {
|
|
if (source == '1') {
|
|
return new _uexpr_pf('++', destination);
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('+', destination, source));
|
|
},
|
|
decrease: function(destination, source) {
|
|
if (source == '1') {
|
|
return new _uexpr_pf('--', destination);
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('-', destination, source));
|
|
},
|
|
add: function(destination, source_a, source_b) {
|
|
if ((destination == source_a) && (source_b == '1')) {
|
|
return new _uexpr_pf('++', destination);
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('+', source_a, source_b));
|
|
},
|
|
and: function(destination, source_a, source_b) {
|
|
if (source_b == '0') {
|
|
return _assign(destination, '0');
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('&', source_a, source_b));
|
|
},
|
|
divide: function(destination, source_a, source_b) {
|
|
return _assign(destination, new _bexpr('/', source_a, source_b));
|
|
},
|
|
module: function(destination, source_a, source_b) {
|
|
return _assign(destination, new _bexpr('%', source_a, source_b));
|
|
},
|
|
multiply: function(destination, source_a, source_b) {
|
|
return _assign(destination, new _bexpr('*', source_a, source_b));
|
|
},
|
|
negate: function(destination, source) {
|
|
return _assign(destination, new _uexpr('-', source));
|
|
},
|
|
not: function(destination, source) {
|
|
return _assign(destination, new _uexpr('~', source));
|
|
},
|
|
extend: function(destination, source, bits) {
|
|
return _assign(destination, new _uexpr('(int' + bits + '_t) ', source));
|
|
},
|
|
subtract: function(destination, source_a, source_b) {
|
|
if ((destination == source_a) && (source_b == '0')) {
|
|
return null;
|
|
} else if (destination == source_a && source_b == '1') {
|
|
return new _uexpr_pf('--', destination);
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('-', source_a, source_b));
|
|
},
|
|
or: function(destination, source_a, source_b) {
|
|
if (source_b == '0') {
|
|
return _assign(destination, source_a);
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('|', source_a, source_b));
|
|
},
|
|
xor: function(destination, source_a, source_b) {
|
|
if (source_a == source_b) {
|
|
return _assign(destination, '0');
|
|
}
|
|
|
|
return _assign(destination, new _bexpr('^', source_a, source_b));
|
|
},
|
|
shift_left: function(destination, source_a, source_b) {
|
|
return _assign(destination, new _bexpr('<<', source_a, source_b));
|
|
},
|
|
shift_right: function(destination, source_a, source_b) {
|
|
return _assign(destination, new _bexpr('>>', source_a, source_b));
|
|
},
|
|
rotate_left: function(destination, source_a, source_b, bits) {
|
|
Global().context.addDependency(new CCalls.rotate_left.fcn(bits));
|
|
|
|
return new _generic_rotate(destination, source_a, source_b, bits, true);
|
|
},
|
|
rotate_right: function(destination, source_a, source_b, bits) {
|
|
Global().context.addDependency(new CCalls.rotate_right.fcn(bits));
|
|
|
|
return new _generic_rotate(destination, source_a, source_b, bits, false);
|
|
},
|
|
swap_endian: function(value, returns, bits) {
|
|
Global().context.addDependency(new CCalls.swap_endian.fcn(bits));
|
|
|
|
return _assign(returns, new _generic_call('SWAP' + bits, [value]));
|
|
},
|
|
swap: function(source_a, source_b, bits) {
|
|
var t = Variable.uniqueName('tmp');
|
|
var ops = [
|
|
_assign(Extra.to.type(bits, false) + ' ' + t, source_a),
|
|
_assign(source_a, source_b),
|
|
_assign(source_b, t),
|
|
];
|
|
return new function(composed) {
|
|
this.composed = composed;
|
|
}(ops);
|
|
},
|
|
bit_mask: function(destination, source_a, source_b) {
|
|
Global().context.addDependency(new CCalls.bit_mask.fcn());
|
|
|
|
return _assign(destination, new _generic_call('BIT_MASK', [source_a, source_b]));
|
|
},
|
|
/* MEMORY */
|
|
read_memory: function(pointer, register, bits, is_signed) {
|
|
var value = (Extra.is.string(register) || Extra.is.number(register)) ? Variable.local(register.toString(), Extra.to.type(bits, is_signed)) : register;
|
|
var ptr = (Extra.is.number(pointer) || Long.isLong(pointer)) ? Variable.pointer('0x' + pointer.toString(16), Extra.to.type(bits, is_signed)) : pointer;
|
|
if (Extra.is.string(ptr)) {
|
|
ptr = Variable.pointer(pointer, Extra.to.type(bits, is_signed));
|
|
}
|
|
return _assign(value, ptr);
|
|
},
|
|
write_memory: function(pointer, register, bits, is_signed) {
|
|
var value = (Extra.is.string(register) || Extra.is.number(register)) ? Variable.local(register.toString(), Extra.to.type(bits, is_signed)) : register;
|
|
var ptr = (Extra.is.number(pointer) || Long.isLong(pointer)) ? Variable.pointer('0x' + pointer.toString(16), Extra.to.type(bits, is_signed)) : pointer;
|
|
if (Extra.is.string(ptr)) {
|
|
ptr = Variable.pointer(pointer, Extra.to.type(bits, is_signed));
|
|
}
|
|
return _assign(ptr, value);
|
|
},
|
|
/* SPECIAL */
|
|
composed: function(instructions) {
|
|
return new function(composed) {
|
|
this.composed = composed;
|
|
}(instructions);
|
|
},
|
|
macro: function(macro, macro_rule) {
|
|
Global().context.addMacro(macro_rule);
|
|
|
|
return new function(macro) {
|
|
this.macro = macro;
|
|
|
|
this.toString = function() {
|
|
return Global().printer.theme.macro(this.macro);
|
|
};
|
|
}(macro);
|
|
},
|
|
special: function(data) {
|
|
return new function(data) {
|
|
this.data = data;
|
|
|
|
this.toString = function() {
|
|
return Global().printer.auto(this.data);
|
|
};
|
|
}(data);
|
|
},
|
|
/* Object based langs */
|
|
method_call: function(object_this, method_separator, function_name, function_arguments, prefix, postfix) {
|
|
return new _generic_method_call(object_this, function_name, function_arguments, method_separator, prefix, postfix);
|
|
},
|
|
objc_call: function(object_this, function_name, function_arguments) {
|
|
return new _generic_objc_call(object_this, function_name, function_arguments);
|
|
},
|
|
/* Object based langs */
|
|
assign_to_object_field: function(destination, object, method_separator, field) {
|
|
return new _assign_object_field(destination, object, field, method_separator, false);
|
|
},
|
|
assign_from_object_field: function(destination, object, method_separator, field) {
|
|
return new _assign_object_field(destination, object, field, method_separator, true);
|
|
},
|
|
assign_to_array: function(destination, source, index) {
|
|
return new _assign_array_at(destination, source, index, false);
|
|
},
|
|
assign_from_array: function(destination, source, index) {
|
|
return new _assign_array_at(destination, source, index, true);
|
|
},
|
|
throw: function(value) {
|
|
return new _generic_throw(value);
|
|
},
|
|
/* UNKNOWN */
|
|
unknown: function(asm) {
|
|
return new _generic_asm(asm);
|
|
}
|
|
};
|