Skip to content

Commit 36fde16

Browse files
authored
Merge pull request ethereumjs#664 from rumkin/f/opcodes
Refactor opcodes
2 parents e229b8e + ec3539d commit 36fde16

File tree

2 files changed

+98
-34
lines changed

2 files changed

+98
-34
lines changed

packages/vm/lib/evm/interpreter.ts

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,11 @@ export interface InterpreterStep {
4242
address: Buffer
4343
memory: number[]
4444
memoryWordCount: BN
45-
opcode: Opcode
45+
opcode: {
46+
name: string
47+
fee: number
48+
isAsync: boolean
49+
}
4650
account: Account
4751
codeAddress: Buffer
4852
}
@@ -147,39 +151,22 @@ export default class Interpreter {
147151
/**
148152
* Get info for an opcode from VM's list of opcodes.
149153
*/
150-
lookupOpInfo(op: number, full: boolean = false): Opcode {
151-
const opcode = this._vm._opcodes[op]
152-
? this._vm._opcodes[op]
153-
: { name: 'INVALID', fee: 0, isAsync: false }
154-
155-
if (full) {
156-
let name = opcode.name
157-
if (name === 'LOG') {
158-
name += op - 0xa0
159-
}
160-
161-
if (name === 'PUSH') {
162-
name += op - 0x5f
163-
}
164-
165-
if (name === 'DUP') {
166-
name += op - 0x7f
167-
}
168-
169-
if (name === 'SWAP') {
170-
name += op - 0x8f
171-
}
172-
return { ...opcode, ...{ name } }
173-
}
154+
lookupOpInfo(op: number): Opcode {
155+
const opcode = this._vm._opcodes[op] ? this._vm._opcodes[op] : this._vm._opcodes[0xfe]
174156

175157
return opcode
176158
}
177159

178160
async _runStepHook(): Promise<void> {
161+
const opcode = this.lookupOpInfo(this._runState.opCode)
179162
const eventObj: InterpreterStep = {
180163
pc: this._runState.programCounter,
181164
gasLeft: this._eei.getGasLeft(),
182-
opcode: this.lookupOpInfo(this._runState.opCode, true),
165+
opcode: {
166+
name: opcode.fullName,
167+
fee: opcode.fee,
168+
isAsync: opcode.isAsync,
169+
},
183170
stack: this._runState.stack._store,
184171
depth: this._eei._env.depth,
185172
address: this._eei._env.address,

packages/vm/lib/evm/opcodes.ts

Lines changed: 85 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,41 @@
11
import Common from 'ethereumjs-common'
22

3-
export interface Opcode {
4-
name: string
5-
fee: number
6-
isAsync: boolean
3+
export class Opcode {
4+
readonly code: number
5+
readonly name: string
6+
readonly fullName: string
7+
readonly fee: number
8+
readonly isAsync: boolean
9+
10+
constructor({
11+
code,
12+
name,
13+
fullName,
14+
fee,
15+
isAsync,
16+
}: {
17+
code: number
18+
name: string
19+
fullName: string
20+
fee: number
21+
isAsync: boolean
22+
}) {
23+
this.code = code
24+
this.name = name
25+
this.fullName = fullName
26+
this.fee = fee
27+
this.isAsync = isAsync
28+
29+
// Opcode isn't subject to change, thus all futher modifications are prevented.
30+
Object.freeze(this)
31+
}
732
}
833

934
export interface OpcodeList {
1035
[code: number]: Opcode
1136
}
1237

13-
const opcodes: OpcodeList = {
38+
const opcodes: OpcodeList = createOpcodes({
1439
// 0x0 range - arithmetic ops
1540
// name, baseCost, async
1641
0x00: { name: 'STOP', fee: 0, isAsync: false },
@@ -172,17 +197,69 @@ const opcodes: OpcodeList = {
172197
// '0x70', range - other
173198
0xfe: { name: 'INVALID', fee: 0, isAsync: false },
174199
0xff: { name: 'SELFDESTRUCT', fee: 5000, isAsync: true },
175-
}
200+
})
176201

177-
const istanbulOpcodes: OpcodeList = {
202+
const istanbulOpcodes: OpcodeList = createOpcodes({
178203
0x31: { name: 'BALANCE', fee: 700, isAsync: true },
179204
0x3f: { name: 'EXTCODEHASH', fee: 700, isAsync: true },
180205
0x46: { name: 'CHAINID', fee: 2, isAsync: false },
181206
0x47: { name: 'SELFBALANCE', fee: 5, isAsync: false },
182207
0x54: { name: 'SLOAD', fee: 800, isAsync: true },
208+
})
209+
210+
/**
211+
* Convert basic opcode info dictonary into complete OpcodeList instance.
212+
*
213+
* @param opcodes {Object} Receive basic opcodes info dictionary.
214+
* @returns {OpcodeList} Complete Opcode list
215+
*/
216+
function createOpcodes(opcodes: {
217+
[key: string]: { name: string; fee: number; isAsync: boolean }
218+
}): OpcodeList {
219+
const result: OpcodeList = {}
220+
for (const [key, value] of Object.entries(opcodes)) {
221+
const code = parseInt(key, 10)
222+
result[<any>key] = new Opcode({
223+
code,
224+
fullName: getFullname(code, value.name),
225+
...value,
226+
})
227+
}
228+
return result
229+
}
230+
231+
/**
232+
* Get full opcode name from its name and code.
233+
*
234+
* @param code {number} Integer code of opcode.
235+
* @param name {string} Short name of the opcode.
236+
* @returns {string} Full opcode name
237+
*/
238+
function getFullname(code: number, name: string): string {
239+
switch (name) {
240+
case 'LOG':
241+
name += code - 0xa0
242+
break
243+
case 'PUSH':
244+
name += code - 0x5f
245+
break
246+
case 'DUP':
247+
name += code - 0x7f
248+
break
249+
case 'SWAP':
250+
name += code - 0x8f
251+
break
252+
}
253+
return name
183254
}
184255

185-
export function getOpcodesForHF(common: Common) {
256+
/**
257+
* Get suitable opcodes for the required hardfork.
258+
*
259+
* @param common {Common} Ethereumjs Common metadaata object.
260+
* @returns {OpcodeList} Opcodes dictionary object.
261+
*/
262+
export function getOpcodesForHF(common: Common): OpcodeList {
186263
if (common.gteHardfork('istanbul')) {
187264
return { ...opcodes, ...istanbulOpcodes }
188265
} else {

0 commit comments

Comments
 (0)