Skip to content

Commit

Permalink
feat: add ethereum
Browse files Browse the repository at this point in the history
  • Loading branch information
streamich committed Jan 3, 2018
1 parent 7925215 commit 70bc6e2
Show file tree
Hide file tree
Showing 17 changed files with 313 additions and 44 deletions.
14 changes: 14 additions & 0 deletions demo/ethereum/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import {Ethereum} from "../../src/index";

const asm = Ethereum();

asm.code(_ => {
_('ADD');
_('MUL');
_('PUSH1', [1]);
_('add');
});

console.log(String(asm));
console.log(asm.compile());
console.log(String(asm));
11 changes: 8 additions & 3 deletions src/Mnemonic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,16 @@ export class Mnemonic {
return String(operand);
}

mnemonic: string = '';
operandSize: SIZE = SIZE.NONE;
opcode: number = 0x00;
mnemonic: string;
operandSize: SIZE | number = SIZE.NONE;
opcode: number;
operandTemplates: TTableOperand[][];

constructor (mnemonic: string = '', opcode: number = 0x00) {
this.mnemonic = mnemonic;
this.opcode = opcode;
}

getName (): string {
const size = this.operandSize;
if((size === SIZE.ANY) || (size === SIZE.NONE)) return this.mnemonic;
Expand Down
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import Asm from "./Asm";
import PresetX64 from "./presets/PresetX64";
import PresetEthereum from "./presets/PresetEthereum";

export const X64 = (opts?) => new Asm(PresetX64(opts));
export const Ethereum = (opts?) => new Asm(PresetEthereum(opts));
7 changes: 5 additions & 2 deletions src/instruction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,13 @@ export class Instruction extends ExpressionVolatile {
const parts = [];

parts.push(this.mnemonic.getName());

if ((parts.join(' ')).length < 8)
parts.push((new Array(7 - (parts.join(' ')).length)).join(' '));
if (this.ops.list.length)
parts.push(this.ops.toString());

if (this.ops)
if (this.ops.list.length)
parts.push(this.ops.toString());

return parts.join(' ');
}
Expand Down
9 changes: 5 additions & 4 deletions src/operand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export type TUiOperandNormalized = Operand|Register|Memory|Tnumber|Relative; //
// General operand used in our assembly "language".
export abstract class Operand {
// Size in bits.
size: SIZE = SIZE.ANY;
size: SIZE | number = SIZE.ANY;

// Convenience method to get `Register` associated with `Register` or `Memory`.
reg(): Register {
Expand Down Expand Up @@ -250,7 +250,8 @@ export class Constant extends Operand {
}

fitsSize(num: Tnumber) {
var size = this.signed ? Constant.sizeClass(num) : Constant.sizeClassUnsigned(num);
const size = this.signed ? Constant.sizeClass(num) : Constant.sizeClassUnsigned(num);

return size <= this.size;
}

Expand Down Expand Up @@ -660,9 +661,9 @@ export class Operands {
else return null;
}

getFirstOfClass (Clazz, skip = 0) {
getFirstOfClass (Klass, skip = 0) {
for (const op of this.list) {
if (op instanceof Clazz) {
if (op instanceof Klass) {
if(!skip) return op;
else skip--;
}
Expand Down
27 changes: 7 additions & 20 deletions src/plugins/data/Data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,12 @@ import {Expression, IPushable} from '../../expression';
import {SIZE, Tnumber, number64} from '../../operand';
import {UInt64} from '../../util';
import CodeBuffer from "../../CodeBuffer";
import formatOctet from "./formatOctet";
import formatOctets from "./formatOctets";

export type TOctets = number[];

export class Data extends Expression {

static formatOctet(octet) {
var neg = octet < 0 ? '-' : '';
octet = Math.abs(octet);
return octet <= 0xF ? neg + '0x0' + octet.toString(16).toUpperCase() : neg + '0x' + octet.toString(16).toUpperCase();
}

static numbersToOctets(numbers: Tnumber[], size: SIZE, littleEndian = true): TOctets {
if(size === SIZE.Q) return Data.quadsToOctets(numbers, littleEndian);

Expand Down Expand Up @@ -92,21 +87,13 @@ export class Data extends Expression {
}

toString(margin = ' ', comment = true) {
var datastr = '';
var bytes = this.bytes();
if(bytes < 200) {
datastr = this.octets.map(function(octet) {
return Data.formatOctet(octet);
}).join(', ');
} else {
datastr = `[${this.bytes()} bytes]`;
}
const datastr = formatOctets(this.octets);
const expression = margin + 'db ' + datastr;
let cmt = '';

var expression = margin + 'db ' + datastr;
var cmt = '';
if(comment) {
var spaces = (new Array(1 + Math.max(0, Expression.commentColls - expression.length))).join(' ');
cmt = `${spaces}; ${this.formatOffset()} ${bytes} bytes`;
const spaces = (new Array(1 + Math.max(0, Expression.commentColls - expression.length))).join(' ');
cmt = `${spaces}; ${this.formatOffset()} ${this.bytes()} bytes`;
}

return expression + cmt;
Expand Down
22 changes: 8 additions & 14 deletions src/plugins/data/DataVariable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {Expression, ExpressionVariable} from '../../expression';
import {TOctets} from './Data';
import {Operands, SIZE, Tnumber, isTnumber, Relative} from '../../operand';
import CodeBuffer from "../../CodeBuffer";
import formatOctet from "./formatOctet";
import formatOctets from "./formatOctets";

export class DataVariable extends ExpressionVariable {

Expand Down Expand Up @@ -53,28 +55,20 @@ export class DataVariable extends ExpressionVariable {
}

toString(margin = ' ', comment = true) {
var datastr = '';
var bytes = this.bytes();
if(bytes < 200) {
datastr = this.ops.list.map(function(op) {
return typeof op === 'number' ? Data.formatOctet(op) : op.toString();
}).join(', ');
} else {
datastr = `[${bytes} bytes]`;
}

var expression = margin + 'dbv ' + datastr;
const datastr = formatOctets(this.octets);
const expression = margin + 'dbv ' + datastr;
let cmt = '';

var cmt = '';
if(comment) {
var spaces = (new Array(1 + Math.max(0, Expression.commentColls - expression.length))).join(' ');
cmt = `${spaces}; ${this.formatOffset()} ${bytes} bytes`;
cmt = `${spaces}; ${this.formatOffset()} ${this.bytes()} bytes`;
if(this.isEvaluated) {
cmt += ' ' + this.octets.map(function(octet) {
return Data.formatOctet(octet);
return formatOctet(octet);
}).join(', ');
}
}

return expression + cmt;
}
}
Expand Down
9 changes: 9 additions & 0 deletions src/plugins/data/formatOctet.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function formatOctet(octet) {
const neg = octet < 0 ? '-' : '';

octet = Math.abs(octet);

return octet <= 0xF ? neg + '0x0' + octet.toString(16).toUpperCase() : neg + '0x' + octet.toString(16).toUpperCase();
}

export default formatOctet;
15 changes: 15 additions & 0 deletions src/plugins/data/formatOctets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import formatOctet from "./formatOctet";

function formatOctets (octets: number[] | Buffer | Uint8Array, maxLength = 200) {
if (octets.length < maxLength) {
const out = [];

for (let i = 0; i < octets.length; i++)
out.push(formatOctet(octets[i]));

return out.join(', ');
} else
return `[${this.bytes()} bytes]`;
}

export default formatOctets;
19 changes: 19 additions & 0 deletions src/plugins/ethereum/ImmediateEthereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {ConstantEthereum} from "./operand";
import {IPushable} from "../../expression";

class ImmediateEthereum {
constant: ConstantEthereum;

constructor (constant: ConstantEthereum) {
this.constant = constant;
}

write (bin: IPushable) {
const {octets} = this.constant;

for (let i = 0; i < octets.length; i++)
bin.push(octets[i]);
}
}

export default ImmediateEthereum;
12 changes: 12 additions & 0 deletions src/plugins/ethereum/MnemonicEthereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import Mnemonic from "../../Mnemonic";
import {SIZE_ETHEREUM} from "./operand";

class MnemonicEthereum extends Mnemonic {
operandSize: SIZE_ETHEREUM = SIZE_ETHEREUM.S0;

getName () {
return this.mnemonic;
}
}

export default MnemonicEthereum;
56 changes: 56 additions & 0 deletions src/plugins/ethereum/PluginEthereum.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import Plugin from "../Plugin";
import {opcodes} from './table';
import {InstructionEthereum} from "./instruction";
import MnemonicEthereum from "./MnemonicEthereum";
import {ConstantEthereum, SIZE_ETHEREUM, TOctetsEthereum} from "./operand";
import {Constant, Operand, Operands} from "../../operand";

class PluginEthereum extends Plugin {
onAsm (asm) {
asm.hooks.command.tap('PluginEthereum', (name, args) => {
name = name.toUpperCase();

const opcode = opcodes[name];

if (typeof opcode === 'number') {
if (name === 'PUSH') {
this.push.apply(this, args);
} else if ((name[0] === 'P') && (name.substr(0, 4) === 'PUSH')) {
const size = parseInt(name.substr(4));
return this.pushX(args[0], size);
} else {
return this.mnemonic(name, opcode);
}
}
});
}

mnemonic (name, opcode) {
const instruction = new InstructionEthereum(new MnemonicEthereum(name, opcode));

return this.asm.insert(instruction);
}

pushX (octets: TOctetsEthereum, X = 1) {
const name = 'PUSH' + X;
const mnemonic = new MnemonicEthereum(name, opcodes[name]);

mnemonic.operandSize = SIZE_ETHEREUM['S' + X];

const bytes = mnemonic.operandSize >> 3;
const operands = new Operands([new ConstantEthereum(octets)]);
const instruction = new InstructionEthereum(mnemonic, operands);

return this.asm.insert(instruction);
}

push (data, size = 8) {

}

operands (uiOperands) {
const operands = new Operands();
}
}

export default PluginEthereum;
56 changes: 56 additions & 0 deletions src/plugins/ethereum/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@

https://github.com/zack-bitcoin/ethereum-assembly

```
3 3 mul
60 03 PUSH1 3
60 03 PUSH1 3
02 MUL
foo jump
60 0f PUSH1 0x0F ($15)
56 JUMP
27 0 0 log1 \this code wont be run. we jumped over it.
60 1b PUSH1 0x1B ($27)
60 00 PUSH1 0
60 00 PUSH1 0
a1 LOG 1
5b JUMPDEST
61 01 a5 PUSH2 0x01A5 ($421)
60 00 PUSH1 0
60 00 PUSH1 0
jumpdest foo
420 0 0 log1 \this code runs 9 times (from 3 and 3 we multiplied above)
a1 LOG 1
1 swap1 sub \run 9 times
60 01 PUSH1 0x01
90 SWAP1
03 SUB
dup iszero iszero foo jumpi \this jumps us back to foo
80 DUP1
15 ISZERO
15 ISZERO
60 0f PUSH1 0x0F ($15)
57 JUMPI
06 0 0 log1
60 06 PUSH1 0x06
60 00 PUSH1 0x00
60 00 PUSH1 0x00
a1 LOG1
```
44 changes: 44 additions & 0 deletions src/plugins/ethereum/instruction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import {Instruction} from "../../instruction";
import {IPushable} from "../../expression";
import {Operands} from "../../operand";
import MnemonicEthereum from "./MnemonicEthereum";
import ImmediateEthereum from "./ImmediateEthereum";
import {ConstantEthereum} from "./operand";

export class InstructionEthereum extends Instruction {
mnemonic: MnemonicEthereum;
length = 1;
lengthMax = 1;

immediate: ImmediateEthereum = null;

constructor (mnemonic: MnemonicEthereum, ops: Operands = null, opts?: object) {
super(ops, opts);

this.mnemonic = mnemonic;
}

write (bin: IPushable) {
bin.push(this.mnemonic.opcode);

if (this.immediate)
this.immediate.write(bin);
}

build (): this {
super.build();


if (this.ops && this.ops.list.length) {
const constant = this.ops.getFirstOfClass(ConstantEthereum) as ConstantEthereum;

this.length += constant.octets.length;
this.lengthMax += constant.octets.length;

if (constant)
this.immediate = new ImmediateEthereum(constant);
}

return this;
}
}
Loading

0 comments on commit 70bc6e2

Please sign in to comment.