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