Skip to content

Commit

Permalink
Implement assemble func prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
AgwaB committed Jan 3, 2019
1 parent e35b588 commit c5b83a0
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 11 deletions.
13 changes: 11 additions & 2 deletions opcode/constant.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,15 @@ const (
//
Pop Type = 0x20

// Push the 32bits(uint32) item .
//
// Ex)
// [a]
// [b] ==> [b]
// [x] [x]
//
Push Type = 0x21

// Pop the first item in the stack.
// Load a value from memory and push it to the stack
//
Expand All @@ -130,7 +139,7 @@ const (
// [x] ==> [b]
// [y] [x]
//
Mload Type = 0x21
Mload Type = 0x22

// Pop the first two items in the stack.
// Store the value with the first item(offset) as the offset of the memory.
Expand All @@ -141,5 +150,5 @@ const (
// [y] [y]
//
// memory[offset:offset+32] = value
Mstore Type = 0x22
Mstore Type = 0x23
)
71 changes: 63 additions & 8 deletions vm/asm.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,45 @@

package vm

import (
"github.com/DE-labtory/koa/opcode"
"github.com/pkg/errors"
)

var opCodes = map[opcode.Type]opCode{
opcode.Add: add{},
opcode.Push: push{},
}

// Converts rawByteCode to assembly code.
func assemble(rawByteCode []byte) (*asm, error) {
analysis()
return &asm{}, nil
asm := newAsm()

for i := 0; i < len(rawByteCode); i++ {
op, ok := opCodes[opcode.Type(rawByteCode[i])]

if !ok {
return nil, errors.New("Invalid byteCode")
}

switch op.hex()[0] {
case uint8(opcode.Push):
body := make([]uint8, 0)
body = append(body, rawByteCode[i+1:i+5]...)

asm.code = append(asm.code, op)
asm.code = append(asm.code, Data{Body: body})
i += 4
default:
asm.code = append(asm.code, op)
}
}

return asm, nil
}

// Do some analysis step (calculating the cost of running the code)
func analysis() {

}

// Assemble Reader read assembly codes and can jump to certain assembly code
Expand All @@ -33,22 +63,47 @@ type asmReader interface {
jump(i uint64)
}

type Data struct {
Body []uint8
}

func (d Data) hex() []uint8 {
return d.Body
}

type hexer interface {
hex() []uint8
}

type asm struct {
code []hexer
cost uint64
index uint64
code []hexer
cost uint64
pc uint64
}

func newAsm() *asm {
return &asm{
code: make([]hexer, 0),
cost: 0,
pc: 0,
}
}

func (a *asm) next() hexer {
return nil
if a.pc+1 == uint64(len(a.code)) {
return nil
}

code := a.code[a.pc+1]
a.pc += 1
return code
}

func (a *asm) jump(pc uint64) {

if pc > uint64(len(a.code))-1 {
panic("Access to invalid program counter!")
}
a.pc = pc
}

func (a *asm) print() {
Expand Down
124 changes: 124 additions & 0 deletions vm/asm_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2018 De-labtory
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package vm

import (
"testing"

"bytes"

"github.com/DE-labtory/koa/opcode"
)

func TestAssemble(t *testing.T) {
rawByteCode := make([]byte, 0)
rawByteCode = append(rawByteCode, uint8(opcode.Push))
rawByteCode = append(rawByteCode, uint32To4Bytes(1)...) // 1 byte allocation
rawByteCode = append(rawByteCode, uint8(opcode.Push))
rawByteCode = append(rawByteCode, uint32To4Bytes(300)...) // 2 byte allocation
rawByteCode = append(rawByteCode, uint8(opcode.Add))

testAsmExpected := asm{
code: []hexer{
push{}, Data{Body: uint32To4Bytes(1)},
push{}, Data{Body: uint32To4Bytes(300)},
add{},
},
}

asm, err := assemble(rawByteCode)
if err != nil {
t.Error(err)
}

//opCode is a byte
//Data is 4 bytes
for i, code := range asm.code {
switch code.(type) {
case opCode:
if !bytes.Equal(testAsmExpected.code[i].(opCode).hex(), code.(opCode).hex()) {
t.Fatal("There is an error in Opcode during analysis")
}
case Data:
if !bytes.Equal(testAsmExpected.code[i].(Data).hex(), code.(Data).hex()) {
t.Fatal("There is an error in Data during analysis")
}
}

}
}

func TestAssemble_invalid(t *testing.T) {
rawByteCode := make([]byte, 0)
rawByteCode = append(rawByteCode, uint8(opcode.Push))
rawByteCode = append(rawByteCode, uint32To4Bytes(1)...)
rawByteCode = append(rawByteCode, uint8(255)) // invaild code
rawByteCode = append(rawByteCode, uint32To4Bytes(300)...)
rawByteCode = append(rawByteCode, uint8(opcode.Add))

_, err := assemble(rawByteCode)
if err == nil {
t.Error("The desired error was not found ")
}
}

func TestNext(t *testing.T) {
rawByteCode := []byte{
uint8(opcode.Push), 1,
uint8(opcode.Push), 2,
uint8(opcode.Add),
}

asm, err := assemble(rawByteCode)
if err != nil {
t.Error(err)
}

for asm.pc+1 < uint64(len(asm.code)) {
code := asm.next()
_, ok := code.(hexer)
if !ok {
t.Errorf("Error at %d", asm.pc-1)
}
}

if asm.next() != nil {
t.Error("Error at last index")
}
}

func TestJump(t *testing.T) {
rawByteCode := []byte{
uint8(opcode.Push), 1,
uint8(opcode.Push), 2,
uint8(opcode.Add),
}

asm, err := assemble(rawByteCode)
if err != nil {
t.Error(err)
}
defer func() {
if r := recover(); r == nil {
t.Fatal("Access to in valid program counter!")
}
}()
asm.jump(1)
asm.jump(2)
asm.jump(4)
asm.jump(10) // Error occur
}
19 changes: 18 additions & 1 deletion vm/vm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package vm
import (
"errors"

"encoding/binary"

"github.com/DE-labtory/koa/opcode"
)

Expand Down Expand Up @@ -57,11 +59,26 @@ type add struct{}

// TODO: implement me w/ test cases :-)
func (add) Do(stack *stack, _ asmReader) error {

return nil
}

// TODO: implement me w/ test cases :-)
func (add) hex() []uint8 {
return []uint8{uint8(opcode.Add)}
}

type push struct{}

func (push) Do(stack *stack, asm asmReader) error {
return nil
}

func (push) hex() []uint8 {
return []uint8{uint8(opcode.Push)}
}

func uint32To4Bytes(uint32 uint32) []byte {
byteSlice := make([]byte, 4)
binary.BigEndian.PutUint32(byteSlice, uint32)
return byteSlice
}

0 comments on commit c5b83a0

Please sign in to comment.