Skip to content

Commit

Permalink
Added explicit pop placeholders to ASM dialect.
Browse files Browse the repository at this point in the history
  • Loading branch information
ricmoo committed Feb 6, 2020
1 parent 89615c5 commit a6b696d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 43 deletions.
11 changes: 9 additions & 2 deletions packages/asm/grammar.jison
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,10 @@
(0x([0-9a-fA-F][0-9a-fA-F])*) return "HEX"
([1-9][0-9]*|0) return "DECIMAL"
//(0b[01]*) return "BINARY"

// Pop Placeholders
"$$" return "DOLLAR_DOLLAR"
([$][1-9][0-9]*) return "DOLLAR_INDEX"

// Special
<<EOF>> return "EOF"
Expand Down Expand Up @@ -99,7 +102,9 @@ opcode
| DECIMAL
{ $$ = { type: "decimal", value: $1, loc: getLoc(yy, @1) }; }
| DOLLAR_DOLLAR
{ $$ = { type: "pop", loc: getLoc(yy, @1) }; }
{ $$ = { type: "pop", index: 0, loc: getLoc(yy, @1) }; }
| DOLLAR_INDEX
{ $$ = { type: "pop", index: parseInt(($1).substring(1)), loc: getLoc(yy, @1) }; }
| SCRIPT_EVAL javascript
{ $$ = { type: "eval", script: $2, loc: getLoc(yy, @1, @2) }; }
;
Expand All @@ -122,7 +127,9 @@ hex
{ {
const value = parseInt($1);
if (value >= 256) { throw new Error("decimal data values must be single bytes"); }
$$ = { type: "hex", verbatim: true, value: ("0x" + (value).toString(16)), loc: getLoc(yy, @1) };
let hex = (value).toString(16);
while (hex.length < 2) { hex = "0" + hex; }
$$ = { type: "hex", verbatim: true, value: ("0x" + hex), loc: getLoc(yy, @1) };
} }
| SCRIPT_EVAL javascript
{ $$ = { type: "eval", verbatim: true, script: $2, loc: getLoc(yy, @1, @2) }; }
Expand Down
53 changes: 47 additions & 6 deletions packages/asm/src.ts/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,19 @@ export class LiteralNode extends ValueNode {
}

export class PopNode extends ValueNode {
readonly index: number;

constructor(guard: any, location: Location, index: number) {
super(guard, location, { index });
}

get placeholder(): string {
if (this.index === 0) { return "$$"; }
return "$" + String(this.index);
}

static from(options: any): PopNode {
return new PopNode(Guard, options.loc, { });
return new PopNode(Guard, options.loc, options.index);
}
}

Expand Down Expand Up @@ -291,9 +302,10 @@ export class LinkNode extends ValueNode {

const opcodes = [ ];

// Have to jump backwards
if (here > value) {
// Find a literal which can include its own length in the delta
// Jump backwards

// Find a literal with length the encodes its own length in the delta
let literal = "0x";
for (let w = 1; w <= 5; w++) {
if (w > 4) { throw new Error("jump too large!"); }
Expand All @@ -314,6 +326,8 @@ export class LinkNode extends ValueNode {
//opcodes.push(Opcode.from("SWAP1"));
//opcodes.push(Opcode.from("SUB"));
} else {
// Jump forwards; this is easy to calculate since we can
// do PC firat.
opcodes.push(Opcode.from("PC"));
opcodes.push(pushLiteral(value - here));
opcodes.push(Opcode.from("ADD"));
Expand Down Expand Up @@ -897,17 +911,44 @@ class SemanticChecker extends Assembler {
});

// Allow any number of stack adjacent $$
let foundZero = null;
let lastIndex = 0;

while (ordered.length && ordered[0] instanceof PopNode) {
ordered.shift();
const popNode = (<PopNode>(ordered.shift()));
const index = popNode.index;
if (index === 0) {
foundZero = popNode;
} else if (index !== lastIndex + 1) {
errors.push({
message: `out-of-order stack placeholder ${ popNode.placeholder }; expected $$${ lastIndex + 1 }`,
severity: SemanticErrorSeverity.error,
node: popNode
});
while (ordered.length && ordered[0] instanceof PopNode) {
ordered.shift();
}
break;
} else {
lastIndex = index;
}
}

if (foundZero && lastIndex > 0) {
errors.push({
message: "cannot mix $$ and $1 stack placeholder",
severity: SemanticErrorSeverity.error,
node: foundZero
});
}

// If there are still any buried, we have a problem
const pops = ordered.filter((n) => (n instanceof PopNode));
if (pops.length) {
errors.push({
message: `$$ must be stack adjacent`,
message: `stack placeholder ${ (<PopNode>(pops[0])).placeholder } must be stack adjacent`,
severity: SemanticErrorSeverity.error,
node: node
node: pops[0]
});
}
}
Expand Down
76 changes: 41 additions & 35 deletions packages/asm/src.ts/opcodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@

import { ethers } from "ethers";

export enum OpcodeMemoryAccess {
write = "write",
read = "read",
full = "full"
};

export class Opcode {
readonly value: number;
readonly mnemonic: string
Expand Down Expand Up @@ -47,13 +53,20 @@ export class Opcode {
}

// Returns true if this operation writes to memory contents (or if readOrWrite, reads memory)
isMemory(readOrWrite?: boolean): boolean {
throw new Error("@TODO: return true if modifies memory");
// Unknown opcodes return null
isMemoryAccess(readOrWrite?: boolean): OpcodeMemoryAccess {
switch ((_Opcodes[this.mnemonic.toLowerCase()] || { memory: null }).memory) {
case "read": return OpcodeMemoryAccess.read;
case "write": return OpcodeMemoryAccess.write;
case "full": return OpcodeMemoryAccess.full;
}
return null;
}

// Returns true if this opcode does not affect state
// Unknown opcodes return false
isStatic(): boolean {
throw new Error("@TODO: return true if certain non-state-changing");
return !(_Opcodes[this.mnemonic.toLowerCase()] || { nonStatic: true }).nonStatic;
}

static from(valueOrMnemonic: number | string) {
Expand All @@ -69,6 +82,8 @@ type _Opcode = {
delta: number;
alpha: number;
doc?: string;
nonStatic?: boolean;
memory?: "read" | "write" | "full";
};

const _Opcodes: { [ name: string ]: _Opcode } = {
Expand Down Expand Up @@ -103,7 +118,7 @@ const _Opcodes: { [ name: string ]: _Opcode } = {
sar: { value: 0x1d, delta: 2, alpha: 1, doc: "v = sar(shiftBits, value)" },

// SHA3
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)" },
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)", memory: "read" },

// Environmental Information
address: { value: 0x30, delta: 0, alpha: 1, doc: "myAddr = address" },
Expand All @@ -113,14 +128,14 @@ const _Opcodes: { [ name: string ]: _Opcode } = {
callvalue: { value: 0x34, delta: 0, alpha: 1, doc: "msgValue = callvalue" },
calldataload: { value: 0x35, delta: 1, alpha: 1, doc: "calldataWordValue = calldataload(byteOffet)" },
calldatasize: { value: 0x36, delta: 0, alpha: 1, doc: "calldataLength = calldatasize" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)" },
calldatacopy: { value: 0x37, delta: 3, alpha: 0, doc: "calldatacopy(dstMemoryIndex, dataIndex, length)", memory: "write" },
codesize: { value: 0x38, delta: 0, alpha: 1, doc: "myCodeLength = codesize" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)" },
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)", memory: "write" },
gasprice: { value: 0x3a, delta: 0, alpha: 1, doc: "txGasPrice = gasprice" },
extcodesize: { value: 0x3b, delta: 1, alpha: 1, doc: "otherCodeLength = extcodesize(address)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)" },
extcodecopy: { value: 0x3c, delta: 4, alpha: 0, doc: "extcodecopy(address, dstMemoryIndex, extcodeIndex, length)", memory: "write" },
returndatasize: { value: 0x3d, delta: 0, alpha: 1, doc: "v = returndatasize" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)" },
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)", memory: "write" },
extcodehash: { value: 0x3f, delta: 1, alpha: 1, doc: "hash = extcodehash(address)" },

// Block Information
Expand All @@ -133,11 +148,11 @@ const _Opcodes: { [ name: string ]: _Opcode } = {

// Stack, Memory, Storage and Flow Operations
pop: { value: 0x50, delta: 1, alpha: 0, doc: "stackTopValue = pop" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])" },
mload: { value: 0x51, delta: 1, alpha: 1, doc: "memoryWordValue = mload(memoryByteIndex)", memory: "read" },
mstore: { value: 0x52, delta: 2, alpha: 0, doc: "mstore(memoryByteIndex, valueOut)", memory: "write" },
mstore8: { value: 0x53, delta: 2, alpha: 0, doc: "mstore8(memoryByteIndex, valueOut [ & 0xff ])", memory: "write" },
sload: { value: 0x54, delta: 1, alpha: 1, doc: "storageWordValue = sload(storageWordIndex)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)" },
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)", nonStatic: true },
jump: { value: 0x56, delta: 1, alpha: 0, doc: "jump(target)" },
jumpi: { value: 0x57, delta: 2, alpha: 0, doc: "jumpi(target, notZero)" },
pc: { value: 0x58, delta: 0, alpha: 1, doc: "programCounter = pc" },
Expand Down Expand Up @@ -216,23 +231,23 @@ const _Opcodes: { [ name: string ]: _Opcode } = {
swap16: { value: 0x9f, delta: 0, alpha: 0 },

// Loggin Operations
log0: { value: 0xa0, delta: 2, alpha: 0 },
log1: { value: 0xa1, delta: 3, alpha: 0 },
log2: { value: 0xa2, delta: 4, alpha: 0 },
log3: { value: 0xa3, delta: 5, alpha: 0 },
log4: { value: 0xa4, delta: 6, alpha: 0 },
log0: { value: 0xa0, delta: 2, alpha: 0, nonStatic: true, memory: "read" },
log1: { value: 0xa1, delta: 3, alpha: 0, nonStatic: true, memory: "read" },
log2: { value: 0xa2, delta: 4, alpha: 0, nonStatic: true, memory: "read" },
log3: { value: 0xa3, delta: 5, alpha: 0, nonStatic: true, memory: "read" },
log4: { value: 0xa4, delta: 6, alpha: 0, nonStatic: true, memory: "read" },

// System Operations
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)" },
create: { value: 0xf0, delta: 3, alpha: 1, doc: "address = create(value, index, length)", nonStatic: true, memory: "read" },
call: { value: 0xf1, delta: 7, alpha: 1, doc: "v = call(gasLimit, address, value, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
callcode: { value: 0xf2, delta: 7, alpha: 1, doc: "v = callcode(@TODO)", nonStatic: true, memory: "full" },
"return": { value: 0xf3, delta: 2, alpha: 0, doc: "return(index, length)", memory: "read" },
delegatecall: { value: 0xf4, delta: 6, alpha: 1, doc: "v = delegatecall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", nonStatic: true, memory: "full" },
create2: { value: 0xf5, delta: 4, alpha: 1, doc: "address = create2(value, index, length, salt)", nonStatic: true, memory: "read" },
staticcall: { value: 0xfa, delta: 6, alpha: 1, doc: "v = staticcall(gasLimit, address, inputIndex, inputLength, outputIndex, outputLength)", memory: "full" },
revert: { value: 0xfd, delta: 2, alpha: 0, doc: "revert(returnDataOffset, returnDataLength)", memory: "read" },
invalid: { value: 0xfe, delta: 0, alpha: 0, doc: "invalid" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)" },
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)", nonStatic: true },
};

const OpcodeMap: { [ mnemonic: string ]: Opcode } = { };
Expand All @@ -256,12 +271,3 @@ Object.keys(_Opcodes).forEach((mnemonic) => {
Opcodes[value] = opcode;
});
Object.freeze(Opcodes);

/*
function repeat(char: string, length: number): string {
let result = char;
while (result.length < length) { result += result; }
return result.substring(0, length);
}
*/

0 comments on commit a6b696d

Please sign in to comment.