Added initial codedrop for the asm package.
This commit is contained in:
parent
bee5944567
commit
0296594aba
|
@ -0,0 +1,182 @@
|
|||
ASM Utilities
|
||||
=============
|
||||
|
||||
**Experimental** Do not use this package in production (yet)
|
||||
|
||||
-----
|
||||
|
||||
A semi-advanced EVM assembler.
|
||||
|
||||
**Features**
|
||||
|
||||
- Nested code scoping allows relative jumps
|
||||
- Execute JavaScript meta-programming inline
|
||||
- Self-padding data blocks
|
||||
- TODO: optional Position-Independant-Code
|
||||
- MIT licensed.
|
||||
|
||||
Command-Line Interface
|
||||
======================
|
||||
|
||||
@TODO: Add this to the CLI package.
|
||||
|
||||
```
|
||||
/home/ethers> ethers-asm [ --disassemble ] [ FILENAME ]
|
||||
```
|
||||
|
||||
Syntax
|
||||
======
|
||||
|
||||
Comments
|
||||
--------
|
||||
|
||||
Any text that occurs after a semi-colon (i.e. `;`) is treated as a comment.
|
||||
|
||||
```
|
||||
; This is a comment. If a comments spans multiple
|
||||
; lines, it needs multiple semi-colons.
|
||||
|
||||
@foobar: ; Here is another comment
|
||||
```
|
||||
|
||||
Opcodes
|
||||
-------
|
||||
|
||||
Each OPCODE may be specified using either the **functional notations** or
|
||||
the **stack notation**.
|
||||
|
||||
### Functional Notation
|
||||
|
||||
This is the recommended syntax for opcodes as the assembler will perform
|
||||
the additional step of verifying the correct number of operands are passed
|
||||
in for the giver operation.
|
||||
|
||||
```
|
||||
blockhash(sub(number, 1))
|
||||
```
|
||||
|
||||
### Stack Notation
|
||||
|
||||
This method is often useful when adapting other existing disassembled
|
||||
bytecode.
|
||||
|
||||
```
|
||||
1
|
||||
number
|
||||
sub
|
||||
blockhash
|
||||
```
|
||||
|
||||
Labels
|
||||
------
|
||||
|
||||
Labels are used for control flow, by providing an achor that can be used
|
||||
by `JUMP` and `JUMPI`.
|
||||
|
||||
A label is relative to its **scope** and cannot be references outside of
|
||||
its exact scope and automatically injects a `JUMPDEST` opcode.
|
||||
|
||||
```
|
||||
@top:
|
||||
jump($top)
|
||||
```
|
||||
|
||||
Data Blocks
|
||||
-----------
|
||||
|
||||
Sometimes verbatim data is desired, for example, embedding strings or
|
||||
look-up tables.
|
||||
|
||||
This can be any number of **hexstrings**, **decimal bytes** or **evals**.
|
||||
|
||||
A **data block** is automatically padded to ensure that any data that is
|
||||
coincidentally a PUSH opcode does not impact code or data outside the
|
||||
data block.
|
||||
|
||||
A **data** exposes two variables: the offset (in the current scope) `$foo`
|
||||
and `#foo`, the length of the data. The offset may only be accessed from an
|
||||
ancestor scope while the length may be accessed from any scope.
|
||||
|
||||
```
|
||||
codecopy(0x20, $foobar, #foobar) ; Copy the data to memory address 32
|
||||
|
||||
@foobar [
|
||||
0x1234 ; This is exactly 2 bytes (i.e. 4 nibbles)
|
||||
42 65 73 ; These are decmial values (3 bytes)
|
||||
]
|
||||
```
|
||||
|
||||
Scopes
|
||||
------
|
||||
|
||||
A scope is a new frame of reference, which offsets will be based on. This
|
||||
makes embedding code within code easier, since the jump destinations and
|
||||
**data blocks** can be accessed relatively.
|
||||
|
||||
The top scope is named `_`.
|
||||
|
||||
```
|
||||
|
||||
// This runs the deployment
|
||||
sstore(0, ${{ toUtf8Bytes("Hello World") }})
|
||||
codecopy(0, $deployment, #deployment)
|
||||
return (0, #deployment)
|
||||
|
||||
@contract {
|
||||
@label:
|
||||
jump($label)
|
||||
}
|
||||
```
|
||||
|
||||
Evaluation and Execution
|
||||
------------------------
|
||||
|
||||
It is often useful to be able to modify a program in more advanced ways
|
||||
at code generation time. JavaScript code can be executed in a `{{! code }}``
|
||||
which does not place any output in the code, but can be used to define
|
||||
functions and variables and code can be evaluated in a ``{{= code }}``
|
||||
which will place the output of the *code* into the assembled output, following
|
||||
the same rules as **Data Blocks**.
|
||||
|
||||
```
|
||||
{{!
|
||||
function foo() { return 42; }
|
||||
}}
|
||||
|
||||
{{= foo() }}
|
||||
1
|
||||
add
|
||||
```
|
||||
|
||||
Notes
|
||||
=====
|
||||
|
||||
Because of the nature of script evaluation, it is possible to create
|
||||
programs which cannot actually be assembled. The assembler will give
|
||||
up after 512 attempts to find a stable organization of the code.
|
||||
|
||||
For example, this code contains a scope named `junk`, which is a `CALLER`
|
||||
statement followed by a data block equal to the bytecode of `junk`. Since
|
||||
this is recursive, there is never any way for this to be satisfied. This is
|
||||
similar to VHDL programs where it is possible to simulate recursion, but
|
||||
impossible to synthesize recursive hardware.
|
||||
|
||||
```
|
||||
@junk {
|
||||
caller
|
||||
@thisIsRecursive[
|
||||
{{= junk }}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Building
|
||||
========
|
||||
|
||||
If you make changes to the `grammar.jison` file, make sure to run the `npm generate`
|
||||
command to re-build the AST parser.
|
||||
|
||||
License
|
||||
=======
|
||||
|
||||
MIT License.
|
|
@ -0,0 +1,13 @@
|
|||
"use strict";
|
||||
|
||||
const fs = require("fs");
|
||||
|
||||
const jison = require("jison")
|
||||
|
||||
const grammar = fs.readFileSync("grammar.jison").toString();
|
||||
|
||||
const parser = new jison.Parser(grammar);
|
||||
|
||||
const parserSource = parser.generate({ moduleName: "parser" });
|
||||
|
||||
fs.writeFileSync("./lib/_parser.js", parserSource);
|
|
@ -0,0 +1,168 @@
|
|||
%lex
|
||||
|
||||
%x script
|
||||
|
||||
%%
|
||||
|
||||
// Inline JavaScript State (gobble up all tokens including whitespace)
|
||||
|
||||
"{{=" %{ this.begin("script"); return "SCRIPT_EVAL"; %}
|
||||
"{{!" %{ this.begin("script"); return "SCRIPT_EXEC"; %}
|
||||
<script>([^\}]|\n|\}[^}]) return "SCRIPT_TOKEN";
|
||||
<script>"}}" this.popState()
|
||||
|
||||
|
||||
// Assembler State
|
||||
|
||||
// Ignorables
|
||||
([;][^\n]*\n) // Ignore comments
|
||||
(\s+) // Ignore Whitespace
|
||||
|
||||
// Identifiers (and opcodes)
|
||||
([A-Za-z][A-Za-z0-9]*) return "ID"
|
||||
|
||||
// Lists
|
||||
"(" return "OPEN_PAREN"
|
||||
")" return "CLOSE_PAREN"
|
||||
"," return "COMMA"
|
||||
|
||||
// Labels prefixes
|
||||
([@][A-Za-z][A-Za-z0-9]*) return "AT_ID"
|
||||
([#][A-Za-z][A-Za-z0-9]*) return "HASH_ID"
|
||||
([$][A-Za-z][A-Za-z0-9]*) return "DOLLAR_ID"
|
||||
|
||||
// Scope
|
||||
"{" return "OPEN_BRACE"
|
||||
"}" return "CLOSE_BRACE"
|
||||
":" return "COLON"
|
||||
|
||||
// Data
|
||||
"[" return "OPEN_BRACKET"
|
||||
"]" return "CLOSE_BRACKET"
|
||||
|
||||
// Literals
|
||||
(0x([0-9a-fA-F][0-9a-fA-F])*) return "HEX"
|
||||
([1-9][0-9]*|0) return "DECIMAL"
|
||||
//(0b[01]*) return "BINARY"
|
||||
|
||||
// Special
|
||||
<<EOF>> return "EOF"
|
||||
return "INVALID"
|
||||
|
||||
/lex
|
||||
|
||||
%start program
|
||||
|
||||
%%
|
||||
|
||||
program
|
||||
: statement_list EOF
|
||||
{ return { type: "scope", name: "_", statements: $1, loc: getLoc(yy, null) }; }
|
||||
;
|
||||
|
||||
javascript
|
||||
: /* empty */
|
||||
{ $$ = ""; }
|
||||
| SCRIPT_TOKEN javascript
|
||||
{ $$ = $1 + $2; }
|
||||
;
|
||||
|
||||
opcode_list
|
||||
: opcode
|
||||
{ $$ = [ $1 ]; }
|
||||
| opcode COMMA opcode_list
|
||||
{ {
|
||||
const opcodes = $3.slice();
|
||||
opcodes.unshift($1);
|
||||
$$ = opcodes;
|
||||
} }
|
||||
;
|
||||
|
||||
opcode
|
||||
: ID
|
||||
{ $$ = { type: "opcode", bare: true, mnemonic: $1, operands: [ ], loc: getLoc(yy, @1) }; }
|
||||
| ID OPEN_PAREN CLOSE_PAREN
|
||||
{ $$ = { type: "opcode", mnemonic: $1, operands: [ ], loc: getLoc(yy, @1, @3) }; }
|
||||
| ID OPEN_PAREN opcode_list CLOSE_PAREN
|
||||
{ $$ = { type: "opcode", mnemonic: $1, operands: $3, loc: getLoc(yy, @1, @4) }; }
|
||||
| HASH_ID
|
||||
{ $$ = { type: "length", label: $1.substring(1), loc: getLoc(yy, @1) }; }
|
||||
| DOLLAR_ID
|
||||
{ $$ = { type: "offset", label: $1.substring(1), loc: getLoc(yy, @1) }; }
|
||||
| HEX
|
||||
{ $$ = { type: "hex", value: $1, loc: getLoc(yy, @1) }; }
|
||||
| DECIMAL
|
||||
{ $$ = { type: "decimal", value: $1, loc: getLoc(yy, @1) }; }
|
||||
| SCRIPT_EVAL javascript
|
||||
{ $$ = { type: "eval", script: $2, loc: getLoc(yy, @1, @2) }; }
|
||||
;
|
||||
|
||||
hex_list
|
||||
: /* empty */
|
||||
{ $$ = [ ]; }
|
||||
| hex hex_list
|
||||
{ {
|
||||
const hexes = $2.slice();;
|
||||
hexes.unshift($1);
|
||||
$$ = hexes;
|
||||
} }
|
||||
;
|
||||
|
||||
hex
|
||||
: HEX
|
||||
{ $$ = { type: "hex", verbatim: true, value: $1, loc: getLoc(yy, @1) }; }
|
||||
| DECIMAL
|
||||
{ {
|
||||
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) };
|
||||
} }
|
||||
| SCRIPT_EVAL javascript
|
||||
{ $$ = { type: "eval", verbatim: true, script: $2, loc: getLoc(yy, @1, @2) }; }
|
||||
;
|
||||
|
||||
statement_list
|
||||
: /* empty */
|
||||
{ $$ = [ ]; }
|
||||
| statement statement_list
|
||||
{ {
|
||||
const statements = $2.slice();
|
||||
statements.unshift($1);
|
||||
$$ = statements;
|
||||
} }
|
||||
;
|
||||
|
||||
statement
|
||||
: opcode
|
||||
| AT_ID COLON
|
||||
{ $$ = { type: "label", name: $1.substring(1), loc: getLoc(yy, @1, @2) }; }
|
||||
| AT_ID OPEN_BRACE statement_list CLOSE_BRACE
|
||||
{ $$ = { type: "scope", name: $1.substring(1), statements: $3, loc: getLoc(yy, @1, @4) }; }
|
||||
| AT_ID OPEN_BRACKET hex_list CLOSE_BRACKET
|
||||
{ $$ = { type: "data", name: $1.substring(1), data: $3, loc: getLoc(yy, @1, @4) }; }
|
||||
| SCRIPT_EXEC javascript
|
||||
{ $$ = { type: "exec", script: $2, loc: getLoc(yy, @1, @2) }; }
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
function getLoc(yy, start, end) {
|
||||
if (end == null) { end = start; }
|
||||
|
||||
let result = null;
|
||||
if (start) {
|
||||
result = {
|
||||
first_line: start.first_line,
|
||||
first_column: start.first_column,
|
||||
last_line: end.last_line,
|
||||
last_column: end.last_column
|
||||
};
|
||||
}
|
||||
|
||||
if (yy._ethersLocation) {
|
||||
return yy._ethersLocation(result);
|
||||
}
|
||||
|
||||
return Object.freeze(result);
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"author": "Richard Moore <me@ricmoo.com>",
|
||||
"dependencies": {
|
||||
"ethers": ">=5.0.0-beta.171"
|
||||
},
|
||||
"description": "ASM libraries and tools for the Ethereum EVM.",
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.7.4",
|
||||
"jison": "^0.4.18"
|
||||
},
|
||||
"ethereum": "donations.ethers.eth",
|
||||
"keywords": [
|
||||
"Ethereum",
|
||||
"asm",
|
||||
"evm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./lib/index.js",
|
||||
"module": "./lib.esm/index.js",
|
||||
"name": "@ethersproject/asm",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/ethers-io/ethers.js.git"
|
||||
},
|
||||
"scripts": {
|
||||
"generate": "node ./generate.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"tarballHash": "0x8c1c74059eaf59d10d94fcb2285fb2512a1fdf121ef5c3167e8afa16807bbbb3",
|
||||
"types": "./lib/index.d.ts",
|
||||
"version": "5.0.0-beta.148"
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
export function parse(source: string): any;
|
||||
export const parser: { yy: { [ key: string ]: any } };
|
|
@ -0,0 +1 @@
|
|||
export const version = "asm/5.0.0-beta.148";
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,40 @@
|
|||
"use strict";
|
||||
|
||||
import { assemble, CodeNode, DataNode, disassemble, EvaluationNode, ExecutionNode, formatBytecode, LabelNode, LabelledNode, LinkNode, LiteralNode, Node, OpcodeNode, parse, ScopeNode, ValueNode } from "./assembler";
|
||||
import { getOpcode, Opcode } from "./opcodes";
|
||||
|
||||
import { AssemblerOptions, AssembleVisitFunc, Bytecode, Location, Operation, VisitFunc } from "./assembler";
|
||||
|
||||
export {
|
||||
// Opcodes
|
||||
getOpcode,
|
||||
Opcode,
|
||||
|
||||
// Assembler functions
|
||||
assemble,
|
||||
disassemble,
|
||||
formatBytecode,
|
||||
parse,
|
||||
|
||||
// Assembly AST Nodes
|
||||
CodeNode,
|
||||
DataNode,
|
||||
EvaluationNode,
|
||||
ExecutionNode,
|
||||
LabelNode,
|
||||
LabelledNode,
|
||||
LinkNode,
|
||||
LiteralNode,
|
||||
Node,
|
||||
OpcodeNode,
|
||||
ScopeNode,
|
||||
ValueNode,
|
||||
|
||||
// Assembler Types
|
||||
AssemblerOptions,
|
||||
AssembleVisitFunc,
|
||||
Bytecode,
|
||||
Location,
|
||||
Operation,
|
||||
VisitFunc,
|
||||
}
|
|
@ -0,0 +1,258 @@
|
|||
"use strict";
|
||||
|
||||
// Yellow Paper
|
||||
// See: https://ethereum.github.io/yellowpaper/paper.pdf
|
||||
// SHL / SHR / SAR:
|
||||
// See: https://eips.ethereum.org/EIPS/eip-145
|
||||
// CREATE2:
|
||||
// See: https://eips.ethereum.org/EIPS/eip-1014
|
||||
// EXTCODEHASH
|
||||
// See: https://eips.ethereum.org/EIPS/eip-1052
|
||||
|
||||
|
||||
import { ethers } from "ethers";
|
||||
|
||||
export class Opcode {
|
||||
readonly value: number;
|
||||
readonly mnemonic: string
|
||||
|
||||
readonly delta: number;
|
||||
readonly alpha: number;
|
||||
|
||||
readonly doc: string;
|
||||
|
||||
constructor(mnemonic: string, value: number, delta: number, alpha: number, doc?: string) {
|
||||
ethers.utils.defineReadOnly(this, "mnemonic", mnemonic);
|
||||
ethers.utils.defineReadOnly(this, "value", value);
|
||||
ethers.utils.defineReadOnly(this, "delta", delta);
|
||||
ethers.utils.defineReadOnly(this, "alpha", alpha);
|
||||
ethers.utils.defineReadOnly(this, "doc", doc || null);
|
||||
}
|
||||
|
||||
// Returns if this opcode is a jump
|
||||
isJump(): boolean {
|
||||
return (this.mnemonic === "JUMP" || this.mnemonic === "JUMPI");
|
||||
}
|
||||
|
||||
isValidJumpDest(): boolean {
|
||||
return (this.mnemonic === "JUMPDEST");
|
||||
}
|
||||
|
||||
// Returns the number of of bytes this performs if a PUSH; 0 otherwise
|
||||
isPush(): number {
|
||||
if (this.mnemonic.substring(0, 4) === "PUSH") {
|
||||
return this.value - 0x60 + 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Returns true if this opcode does not affect state
|
||||
isStatic(): boolean {
|
||||
throw new Error("@TODO: return true if certain non-state-changing");
|
||||
}
|
||||
}
|
||||
|
||||
type _Opcode = {
|
||||
value: number;
|
||||
delta: number;
|
||||
alpha: number;
|
||||
doc?: string;
|
||||
};
|
||||
|
||||
const _Opcodes: { [ name: string ]: _Opcode } = {
|
||||
// Stop and Arithmetic Operations
|
||||
stop: { value: 0x00, delta: 0, alpha: 0, doc: "stop" },
|
||||
add: { value: 0x01, delta: 2, alpha: 1, doc: "v = add(a, b)" },
|
||||
mul: { value: 0x02, delta: 2, alpha: 1, doc: "v = mul(a, b)" },
|
||||
sub: { value: 0x03, delta: 2, alpha: 1, doc: "v = sub(a, b)" },
|
||||
div: { value: 0x04, delta: 2, alpha: 1, doc: "v = div(top, bottom)" },
|
||||
sdiv: { value: 0x05, delta: 2, alpha: 1, doc: "v = sdiv(top, bottom)" },
|
||||
mod: { value: 0x06, delta: 2, alpha: 1, doc: "v = mod(a, modulo)" },
|
||||
smod: { value: 0x07, delta: 2, alpha: 1, doc: "v = smod(a, modulo)" },
|
||||
addmod: { value: 0x08, delta: 3, alpha: 1, doc: "v = addmod(a, b, modulo)" },
|
||||
mulmod: { value: 0x09, delta: 3, alpha: 1, doc: "v = mul(a, b, modulo)" },
|
||||
exp: { value: 0x0a, delta: 2, alpha: 1, doc: "v = exp(base, exponent)" },
|
||||
signextend: { value: 0x0b, delta: 2, alpha: 1, doc: "v = signextend(value, byteWidth)" },
|
||||
|
||||
// Comparison & Bitwise Logic Operations
|
||||
lt: { value: 0x10, delta: 2, alpha: 1, doc: "v = lt(a, b)" },
|
||||
gt: { value: 0x11, delta: 2, alpha: 1, doc: "v = gt(a, b)" },
|
||||
slt: { value: 0x12, delta: 2, alpha: 1, doc: "v = slt(a, b)" },
|
||||
sgt: { value: 0x13, delta: 2, alpha: 1, doc: "v = sgt(a, b)" },
|
||||
eq: { value: 0x14, delta: 2, alpha: 1, doc: "v = eq(a, b)" },
|
||||
iszero: { value: 0x15, delta: 1, alpha: 1, doc: "v = iszero(a)" },
|
||||
and: { value: 0x16, delta: 2, alpha: 1, doc: "v = and(a, b)" },
|
||||
or: { value: 0x17, delta: 2, alpha: 1, doc: "v = or(a, b)" },
|
||||
xor: { value: 0x18, delta: 2, alpha: 1, doc: "v = xor(a, b)" },
|
||||
not: { value: 0x19, delta: 1, alpha: 1, doc: "v = not(a, b)" },
|
||||
byte: { value: 0x1a, delta: 2, alpha: 1, doc: "v = byte(msbByteIndex, value)" },
|
||||
shl: { value: 0x1b, delta: 2, alpha: 1, doc: "v = shl(shiftBits, value)" },
|
||||
shr: { value: 0x1c, delta: 2, alpha: 1, doc: "v = shr(shiftBits, value)" },
|
||||
sar: { value: 0x1c, delta: 2, alpha: 1, doc: "v = sar(shiftBits, value)" },
|
||||
|
||||
// SHA3
|
||||
sha3: { value: 0x20, delta: 2, alpha: 1, doc: "v = sha3(offset, length)" },
|
||||
|
||||
// Environmental Information
|
||||
address: { value: 0x30, delta: 0, alpha: 1, doc: "myAddr = address" },
|
||||
balance: { value: 0x31, delta: 1, alpha: 1, doc: "wei = balance(address)" },
|
||||
origin: { value: 0x32, delta: 0, alpha: 1, doc: "txOrigin = origin" },
|
||||
caller: { value: 0x33, delta: 0, alpha: 1, doc: "msgSender = caller" },
|
||||
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)" },
|
||||
codesize: { value: 0x38, delta: 0, alpha: 1, doc: "myCodeLength = codesize" },
|
||||
codecopy: { value: 0x39, delta: 3, alpha: 0, doc: "codecopy(dstMemoryIndex, codeIndex, length)" },
|
||||
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)" },
|
||||
returndatasize: { value: 0x3d, delta: 0, alpha: 1, doc: "v = returndatasize" },
|
||||
returndatacopy: { value: 0x3e, delta: 3, alpha: 0, doc: "returndatacopy(dstMemoryOffset, returndataIndex, length)" },
|
||||
extcodehash: { value: 0x3f, delta: 1, alpha: 1, doc: "hash = extcodehash(address)" },
|
||||
|
||||
// Block Information
|
||||
blockhash: { value: 0x40, delta: 1, alpha: 1, doc: "hash = blockhash(blockNumber)" },
|
||||
coinbase: { value: 0x41, delta: 0, alpha: 1, doc: "miner = coinbase" },
|
||||
timestamp: { value: 0x42, delta: 0, alpha: 1, doc: "now = timestamp" },
|
||||
number: { value: 0x43, delta: 0, alpha: 1, doc: "blockNumber = number" },
|
||||
difficulty: { value: 0x44, delta: 0, alpha: 1, doc: "diff = difficulty" },
|
||||
gaslimit: { value: 0x45, delta: 0, alpha: 1, doc: "gas = gaslimit" },
|
||||
|
||||
// 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 ])" },
|
||||
sload: { value: 0x54, delta: 1, alpha: 1, doc: "storageWordValue = sload(storageWordIndex)" },
|
||||
sstore: { value: 0x55, delta: 2, alpha: 0, doc: "sstore(storageWordIndex, valueOut)" },
|
||||
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" },
|
||||
msize: { value: 0x59, delta: 0, alpha: 1, doc: "currentMemorySize = msize" },
|
||||
gas: { value: 0x5a, delta: 0, alpha: 1, doc: "remainingGas = gas" },
|
||||
jumpdest: { value: 0x5b, delta: 0, alpha: 0, doc: "jumpdest" },
|
||||
|
||||
// Push Operations
|
||||
push1: { value: 0x60, delta: 0, alpha: 1 },
|
||||
push2: { value: 0x61, delta: 0, alpha: 1 },
|
||||
push3: { value: 0x62, delta: 0, alpha: 1 },
|
||||
push4: { value: 0x63, delta: 0, alpha: 1 },
|
||||
push5: { value: 0x64, delta: 0, alpha: 1 },
|
||||
push6: { value: 0x65, delta: 0, alpha: 1 },
|
||||
push7: { value: 0x66, delta: 0, alpha: 1 },
|
||||
push8: { value: 0x67, delta: 0, alpha: 1 },
|
||||
push9: { value: 0x68, delta: 0, alpha: 1 },
|
||||
push10: { value: 0x69, delta: 0, alpha: 1 },
|
||||
push11: { value: 0x6a, delta: 0, alpha: 1 },
|
||||
push12: { value: 0x6b, delta: 0, alpha: 1 },
|
||||
push13: { value: 0x6c, delta: 0, alpha: 1 },
|
||||
push14: { value: 0x6d, delta: 0, alpha: 1 },
|
||||
push15: { value: 0x6e, delta: 0, alpha: 1 },
|
||||
push16: { value: 0x6f, delta: 0, alpha: 1 },
|
||||
push17: { value: 0x70, delta: 0, alpha: 1 },
|
||||
push18: { value: 0x71, delta: 0, alpha: 1 },
|
||||
push19: { value: 0x72, delta: 0, alpha: 1 },
|
||||
push20: { value: 0x73, delta: 0, alpha: 1 },
|
||||
push21: { value: 0x74, delta: 0, alpha: 1 },
|
||||
push22: { value: 0x75, delta: 0, alpha: 1 },
|
||||
push23: { value: 0x76, delta: 0, alpha: 1 },
|
||||
push24: { value: 0x77, delta: 0, alpha: 1 },
|
||||
push25: { value: 0x78, delta: 0, alpha: 1 },
|
||||
push26: { value: 0x79, delta: 0, alpha: 1 },
|
||||
push27: { value: 0x7a, delta: 0, alpha: 1 },
|
||||
push28: { value: 0x7b, delta: 0, alpha: 1 },
|
||||
push29: { value: 0x7c, delta: 0, alpha: 1 },
|
||||
push30: { value: 0x7d, delta: 0, alpha: 1 },
|
||||
push31: { value: 0x7e, delta: 0, alpha: 1 },
|
||||
push32: { value: 0x7f, delta: 0, alpha: 1 },
|
||||
|
||||
// Duplicate Operations
|
||||
dup1: { value: 0x80, delta: 0, alpha: 1 },
|
||||
dup2: { value: 0x81, delta: 0, alpha: 1 },
|
||||
dup3: { value: 0x82, delta: 0, alpha: 1 },
|
||||
dup4: { value: 0x83, delta: 0, alpha: 1 },
|
||||
dup5: { value: 0x84, delta: 0, alpha: 1 },
|
||||
dup6: { value: 0x85, delta: 0, alpha: 1 },
|
||||
dup7: { value: 0x86, delta: 0, alpha: 1 },
|
||||
dup8: { value: 0x87, delta: 0, alpha: 1 },
|
||||
dup9: { value: 0x88, delta: 0, alpha: 1 },
|
||||
dup10: { value: 0x89, delta: 0, alpha: 1 },
|
||||
dup11: { value: 0x8a, delta: 0, alpha: 1 },
|
||||
dup12: { value: 0x8b, delta: 0, alpha: 1 },
|
||||
dup13: { value: 0x8c, delta: 0, alpha: 1 },
|
||||
dup14: { value: 0x8d, delta: 0, alpha: 1 },
|
||||
dup15: { value: 0x8e, delta: 0, alpha: 1 },
|
||||
dup16: { value: 0x8f, delta: 0, alpha: 1 },
|
||||
|
||||
// Exchange Operations
|
||||
swap1: { value: 0x90, delta: 0, alpha: 0 },
|
||||
swap2: { value: 0x91, delta: 0, alpha: 0 },
|
||||
swap3: { value: 0x92, delta: 0, alpha: 0 },
|
||||
swap4: { value: 0x93, delta: 0, alpha: 0 },
|
||||
swap5: { value: 0x94, delta: 0, alpha: 0 },
|
||||
swap6: { value: 0x95, delta: 0, alpha: 0 },
|
||||
swap7: { value: 0x96, delta: 0, alpha: 0 },
|
||||
swap8: { value: 0x97, delta: 0, alpha: 0 },
|
||||
swap9: { value: 0x98, delta: 0, alpha: 0 },
|
||||
swap10: { value: 0x99, delta: 0, alpha: 0 },
|
||||
swap11: { value: 0x9a, delta: 0, alpha: 0 },
|
||||
swap12: { value: 0x9b, delta: 0, alpha: 0 },
|
||||
swap13: { value: 0x9c, delta: 0, alpha: 0 },
|
||||
swap14: { value: 0x9d, delta: 0, alpha: 0 },
|
||||
swap15: { value: 0x9e, delta: 0, alpha: 0 },
|
||||
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 },
|
||||
|
||||
// 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)" },
|
||||
invalid: { value: 0xfe, delta: 0, alpha: 0, doc: "invalid" },
|
||||
suicide: { value: 0xff, delta: 1, alpha: 0, doc: "suicide(targetAddress)" },
|
||||
};
|
||||
|
||||
const OpcodeMap: { [ mnemonic: string ]: Opcode } = { };
|
||||
|
||||
const Opcodes: Array<Opcode> = [ ];
|
||||
for (let i = 0; i < 256; i++) { Opcodes.push(null); }
|
||||
|
||||
Object.keys(_Opcodes).forEach((mnemonic) => {
|
||||
const info = _Opcodes[mnemonic];
|
||||
const opcode = new Opcode(mnemonic.toUpperCase(), info.value, info.delta, info.alpha, info.doc);
|
||||
|
||||
OpcodeMap[opcode.mnemonic.toLowerCase()] = opcode;
|
||||
Opcodes[opcode.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);
|
||||
}
|
||||
*/
|
||||
export function getOpcode(valueOrMnemonic: number | string) {
|
||||
if (typeof(valueOrMnemonic) === "string") {
|
||||
return OpcodeMap[valueOrMnemonic.toLowerCase()] || null;
|
||||
}
|
||||
return (Opcodes[valueOrMnemonic] || null);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"extends": "../../tsconfig.package.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "./src.ts",
|
||||
"outDir": "./lib/"
|
||||
},
|
||||
"include": [
|
||||
"./src.ts/*"
|
||||
],
|
||||
"exclude": []
|
||||
}
|
Loading…
Reference in New Issue