Added initial codedrop for the asm package.

This commit is contained in:
Richard Moore 2020-01-30 21:26:46 -05:00
parent bee5944567
commit 0296594aba
No known key found for this signature in database
GPG Key ID: 665176BE8E9DC651
10 changed files with 1745 additions and 0 deletions

182
packages/asm/README.md Normal file
View File

@ -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.

13
packages/asm/generate.js Normal file
View File

@ -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);

168
packages/asm/grammar.jison Normal file
View File

@ -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);
}

35
packages/asm/package.json Normal file
View File

@ -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"
}

2
packages/asm/src.ts/_parser.d.ts vendored Normal file
View File

@ -0,0 +1,2 @@
export function parse(source: string): any;
export const parser: { yy: { [ key: string ]: any } };

View File

@ -0,0 +1 @@
export const version = "asm/5.0.0-beta.148";

File diff suppressed because it is too large Load Diff

View File

@ -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,
}

View File

@ -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);
}

View File

@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.package.json",
"compilerOptions": {
"rootDir": "./src.ts",
"outDir": "./lib/"
},
"include": [
"./src.ts/*"
],
"exclude": []
}