<commit_before>package isvcs

var ZookeeperContainer ISvc

const zk_dockerfile = `
FROM ubuntu
MAINTAINER Zenoss <dev@zenoss.com>

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y

RUN apt-get install -y -q openjdk-7-jre-headless wget maven
RUN wget -q -O /opt/zookeeper-3.4.5.tar.gz http://apache.mirrors.pair.com/zookeeper/zookeeper-3.4.5/zookeeper-3.4.5.tar.gz
RUN tar -xzf /opt/zookeeper-3.4.5.tar.gz -C /opt
RUN cp /opt/zookeeper-3.4.5/conf/zoo_sample.cfg /opt/zookeeper-3.4.5/conf/zoo.cfg

ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64

RUN mkdir /opt/exhibitor && wget -q -O /opt/exhibitor/pom.xml https://raw.github.com/Netflix/exhibitor/master/exhibitor-standalone/src/main/resources/buildscripts/standalone/maven/pom.xml
RUN cd /opt/exhibitor && mvn assembly:single

RUN sed -i 's|start-foreground)|start-foreground)\n    /usr/bin/java -jar /opt/exhibitor/target/exhibitor-*-jar-with-dependencies.jar -c file --port 12181 \&|g' /opt/zookeeper-3.4.5/bin/zkServer.sh

EXPOSE 2181 12181

ENTRYPOINT ["/opt/zookeeper-3.4.5/bin/zkServer.sh"]
CMD ["start-foreground"]
`

func init() {
	ZookeeperContainer = ISvc{
		Name:       "zookeeper",
		Dockerfile: zk_dockerfile,
		Tag:        "zenoss/cp_zk",
		Ports:      []int{2181, 12181},
	}
}
<commit_msg>Use a different zookeeper source; iterim solution, need to host this ourselves<commit_after>package isvcs

var ZookeeperContainer ISvc

const zk_dockerfile = `
FROM ubuntu
MAINTAINER Zenoss <dev@zenoss.com>

RUN echo "deb http://archive.ubuntu.com/ubuntu precise main universe" > /etc/apt/sources.list
RUN apt-get update
RUN apt-get upgrade -y

RUN apt-get install -y -q openjdk-7-jre-headless wget maven
RUN wget -q -O /opt/zookeeper-3.4.5.tar.gz http://apache.cs.utah.edu/zookeeper/zookeeper-3.4.5/zookeeper-3.4.5.tar.gz
RUN tar -xzf /opt/zookeeper-3.4.5.tar.gz -C /opt
RUN cp /opt/zookeeper-3.4.5/conf/zoo_sample.cfg /opt/zookeeper-3.4.5/conf/zoo.cfg

ENV JAVA_HOME /usr/lib/jvm/java-7-openjdk-amd64

RUN mkdir /opt/exhibitor && wget -q -O /opt/exhibitor/pom.xml https://raw.github.com/Netflix/exhibitor/master/exhibitor-standalone/src/main/resources/buildscripts/standalone/maven/pom.xml
RUN cd /opt/exhibitor && mvn assembly:single

RUN sed -i 's|start-foreground)|start-foreground)\n    /usr/bin/java -jar /opt/exhibitor/target/exhibitor-*-jar-with-dependencies.jar -c file --port 12181 \&|g' /opt/zookeeper-3.4.5/bin/zkServer.sh

EXPOSE 2181 12181

ENTRYPOINT ["/opt/zookeeper-3.4.5/bin/zkServer.sh"]
CMD ["start-foreground"]
`

func init() {
	ZookeeperContainer = ISvc{
		Name:       "zookeeper",
		Dockerfile: zk_dockerfile,
		Tag:        "zenoss/cp_zk",
		Ports:      []int{2181, 12181},
	}
}
<|endoftext|>


<commit_before>package list

type IteratorAdapter interface {
	AtLastElement() bool
	AtFirstElement() bool
	MoveForward()
	MoveBack()
	RemoveElement(relPos int)
	InsertElement() int
	Pos() int
}

type Iterator struct {
	adapter IteratorAdapter

	valid bool

	moveOnNext bool
	moveOnPrev bool

	canGoForward bool
	canGoBackward bool
}

func NewIterator(adapter IteratorAdapter) IteratorInterface {
	return &Iterator{adapter: adapter, canGoForward: true, canGoBackward: true}
}

func NewEmptyIterator(adapter IteratorAdapter) IteratorInterface {
	return &Iterator{adapter: adapter, canGoForward: false, canGoBackward: false}
}

func (i *Iterator) Next() bool {
	if i.canGoForward {
		if i.moveOnNext {
			if !i.adapter.AtLastElement() {
				i.adapter.MoveForward()
				i.canGoBackward = true
				i.valid = true
				return true
			} else {
				i.canGoForward = false
				i.moveOnPrev = false
				i.valid = false
				return false
			}
		} else {
			i.canGoBackward = true
			i.valid = true
			i.moveOnNext = true
			return true
		}
	} else {
		return false
	}
}

func (i *Iterator) Prev() bool {
	if i.canGoBackward {
		if i.moveOnPrev {
			if !i.adapter.AtFirstElement() {
				i.adapter.MoveBack()
				i.canGoForward = true
				i.valid = true
				return true
			} else {
				i.canGoBackward = false
				i.moveOnNext = false
				i.valid = false
				return false
			}
		} else {
			i.canGoForward = true
			i.valid = true
			i.moveOnPrev = true
			return true
		}
	} else {
		return false
	}
}

func (i *Iterator) Insert() (index int) {
	if i.valid {
		return i.adapter.InsertElement()
	} else {
		return 0
	}
}

const (
	prev int = -1
	cur int = 0
	next int = +1
)

func (i *Iterator) Remove() {
	if !i.valid {
		return
	}

	i.valid = false
	if i.adapter.AtLastElement() {
		if i.adapter.AtFirstElement() {
			i.canGoForward = false
			i.canGoBackward = false
			i.adapter.RemoveElement(cur)
		} else {
			i.canGoForward = false
			i.canGoBackward = true
			i.moveOnPrev = false
			i.adapter.MoveBack()
			i.adapter.RemoveElement(next)
		}
	} else  {
		i.moveOnPrev = true
		i.moveOnNext = false
		i.canGoForward = true
		i.canGoBackward = true
		i.adapter.MoveForward()
		i.adapter.RemoveElement(prev)
	}
}

func (i *Iterator) Pos() int {
	return i.adapter.Pos()
}<commit_msg>simplify iterator<commit_after>package list

type IteratorAdapter interface {
	AtLastElement() bool
	AtFirstElement() bool
	MoveForward()
	MoveBack()
	RemoveElement(relPos int)
	InsertElement() int
	Pos() int
}

type Iterator struct {
	adapter IteratorAdapter

	valid bool

	moveOnNext bool
	moveOnPrev bool

	empty bool
}

func NewIterator(adapter IteratorAdapter) IteratorInterface {
	return &Iterator{adapter: adapter}
}

func NewEmptyIterator(adapter IteratorAdapter) IteratorInterface {
	return &Iterator{adapter: adapter, empty: true}
}

func (i *Iterator) Next() bool {
	if !i.empty {
		if i.moveOnNext {
			if !i.adapter.AtLastElement() {
				i.adapter.MoveForward()
				i.valid = true
				return true
			} else {
				i.moveOnPrev = false
				i.valid = false
				return false
			}
		} else {
			i.valid = true
			i.moveOnNext = true
			return true
		}
	} else {
		return false
	}
}

func (i *Iterator) Prev() bool {
	if !i.empty {
		if i.moveOnPrev {
			if !i.adapter.AtFirstElement() {
				i.adapter.MoveBack()
				i.valid = true
				return true
			} else {
				i.moveOnNext = false
				i.valid = false
				return false
			}
		} else {
			i.valid = true
			i.moveOnPrev = true
			return true
		}
	} else {
		return false
	}
}

func (i *Iterator) Insert() (index int) {
	if i.valid {
		return i.adapter.InsertElement()
	} else {
		return 0
	}
}

const (
	prev int = -1
	cur int = 0
	next int = +1
)

func (i *Iterator) Remove() {
	if !i.valid {
		return
	}

	i.valid = false
	if i.adapter.AtLastElement() {
		if i.adapter.AtFirstElement() {
			i.empty = true
			i.adapter.RemoveElement(cur)
		} else {
			i.moveOnNext = true
			i.moveOnPrev = false
			i.adapter.MoveBack()
			i.adapter.RemoveElement(next)
		}
	} else  {
		i.moveOnPrev = true
		i.moveOnNext = false
		i.adapter.MoveForward()
		i.adapter.RemoveElement(prev)
	}
}

func (i *Iterator) Pos() int {
	return i.adapter.Pos()
}<|endoftext|>


<commit_before>package jibi

import (
	"fmt"
	"sync"
)

// A list of all the special memory addresses.
const (
	AddrRom    Word = 0x0000
	AddrVRam   Word = 0x8000
	AddrERam   Word = 0xA000
	AddrRam    Word = 0xC000
	AddrOam    Word = 0xFE00
	AddrOamEnd Word = 0xFEA0

	AddrP1  Word = 0xFF00
	AddrDIV Word = 0xFF04
	AddrTMA Word = 0xFF06
	AddrIF  Word = 0xFF0F

	AddrGpuRegs    Word = 0xFF40
	AddrLCDC       Word = 0xFF40
	AddrSTAT       Word = 0xFF41
	AddrSCY        Word = 0xFF42
	AddrSCX        Word = 0xFF43
	AddrLY         Word = 0xFF44
	AddrLYC        Word = 0xFF45
	AddrDMA        Word = 0xFF46
	AddrBGP        Word = 0xFF47
	AddrOBP0       Word = 0xFF48
	AddrOBP1       Word = 0xFF49
	AddrWY         Word = 0xFF4A
	AddrWX         Word = 0xFF4B
	AddrGpuRegsEnd Word = 0xFF4C

	AddrZero Word = 0xFF80
	AddrIE   Word = 0xFFFF
)

// An Mmu is the memory management unit. Its purpose is to dispatch read and
// write requeststo the appropriate module (cpu, gpu, etc) based on the memory
// address. The Mmu is controlled by the cpu.
type Mmu struct {
	// memory blocks
	rom     []Byte
	vram    []Byte
	ram     []Byte
	oam     []Byte
	gpuregs []Byte
	zero    []Byte
	ie      Byte

	// memory mapped io
	ioIF *mmio
	ioP1 *mmio

	// memory locks
	locks []*sync.Mutex

	// internal state
	kp *Keypad
}

// NewMmu creates a new Mmu with an optional bios that replaces 0x0000-0x00FF.
func NewMmu(cart *Cartridge) *Mmu {
	var rom []Byte
	if cart != nil {
		rom = cart.Rom
	}
	locks := make([]*sync.Mutex, abLast+1)
	for i := uint16(1); i <= uint16(abLast); i = i << 1 {
		locks[i] = new(sync.Mutex)
	}
	mmu := &Mmu{
		rom:     rom,
		vram:    make([]Byte, 0x2000),
		ram:     make([]Byte, 0x2000),
		oam:     make([]Byte, 0xA0),
		ioIF:    newMmio(AddrIF),
		ioP1:    newMmio(AddrP1),
		gpuregs: make([]Byte, 12),
		zero:    make([]Byte, 0x100),
		locks:   locks,
	}
	return mmu
}

type addressBlock uint16
type AddressKeys uint16

const (
	abNil addressBlock = iota
	abRom addressBlock = 1 << iota
	abVRam
	abERam
	abRam
	abOam
	abP1
	abIF
	abGpuRegs
	abZero
	abIE
	abLast = abIE
)

func (a addressBlock) String() string {
	switch a {
	case abNil:
		return "abNil"
	case abRom:
		return "abRom"
	case abVRam:
		return "abVRam"
	case abERam:
		return "abERam"
	case abRam:
		return "abRam"
	case abOam:
		return "abOam"
	case abIF:
		return "abIF"
	case abGpuRegs:
		return "abGpuRegs"
	case abZero:
		return "abZero"
	case abIE:
		return "abIE"
	}
	return "abUNKNOWN"
}

func (m *Mmu) SetKeypad(kp *Keypad) {
	m.kp = kp
}

func (m *Mmu) selectAddressBlock(addr Worder, rw string) (addressBlock, Word) {
	a := addr.Word()
	if a < AddrVRam {
		return abRom, 0
	} else if AddrVRam <= a && a < AddrERam {
		return abVRam, AddrVRam
	} else if AddrERam <= a && a < AddrRam {
		return abERam, AddrERam
	} else if AddrRam <= a && a < AddrOam {
		return abRam, AddrRam
	} else if AddrOam <= a && a < AddrOamEnd {
		return abOam, AddrOam
	} else if AddrP1 == a {
		return abP1, AddrP1
	} else if AddrIF == a {
		return abIF, AddrIF
	} else if AddrGpuRegs <= a && a < AddrGpuRegsEnd {
		return abGpuRegs, AddrGpuRegs
	} else if AddrZero <= a && a < AddrIE {
		return abZero, AddrZero
	} else if AddrIE == a {
		return abIE, AddrIE
	}

	u, v := m.getAddressInfo(addr)
	if !v {
		if rw == "" {
			rw = "access"
		}
		panic(fmt.Sprintf("unhandled memory %s: 0x%04X - %s", rw, a, u))
	}
	return abNil, 0
}

// LockAddr gets a lock for an address if not already in the provided
// AddressKeys and appends it and returns this new key set.
func (m *Mmu) LockAddr(addr Worder, ak AddressKeys) AddressKeys {
	blk, _ := m.selectAddressBlock(addr, "lock")
	if addressBlock(ak)&blk == blk {
		// already have the key
		return ak
	}
	m.locks[blk].Lock()
	return ak | AddressKeys(blk)
}

func (m *Mmu) UnlockAddr(addr Worder, ak AddressKeys) AddressKeys {
	blk, _ := m.selectAddressBlock(addr, "unlock")
	if addressBlock(ak)&blk != blk {
		// don't have the key
		return ak
	}
	m.locks[blk].Unlock()
	return ak & AddressKeys(blk^0xFFFF)
}

func (m *Mmu) ReadByteAt(addr Worder, ak AddressKeys) Byte {
	blk, start := m.selectAddressBlock(addr, "read")
	owner := addressBlock(ak)&blk == blk
	if blk == abRom {
		if owner {
			return m.rom[addr.Word()-start]
		}
	}
	if blk == abVRam {
		if owner {
			return m.vram[addr.Word()-start]
		}
	} else if blk == abRam {
		if owner {
			return m.ram[(addr.Word()-start)&0x1FFF]
		}
	} else if blk == abOam {
		if owner {
			return m.oam[addr.Word()-start]
		}
	} else if blk == abP1 {
		return m.ioP1.readByte(owner)
	} else if blk == abIF {
		return m.ioIF.readByte(owner)
	} else if blk == abGpuRegs {
		if owner {
			return m.gpuregs[addr.Word()-start]
		}
	} else if blk == abZero {
		if owner {
			return m.zero[addr.Word()-start]
		}
	} else if blk == abIE {
		if owner {
			return m.ie
		}
	}
	if !owner {
		panic(fmt.Sprintf("unauthorized read: 0x%04X", addr.Word()))
	}
	if u, v := m.getAddressInfo(addr); !v {
		panic(fmt.Sprintf("unhandled memory read: 0x%04X - %s", addr.Word(), u))
	}
	return 0
}

func (m *Mmu) WriteByteAt(addr Worder, b Byter, ak AddressKeys) {
	blk, start := m.selectAddressBlock(addr, "write")
	owner := addressBlock(ak)&blk == blk
	if blk == abRom {
		return
	} else if blk == abVRam {
		if owner {
			m.vram[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abRam {
		if owner {
			m.ram[(addr.Word()-start)&0x1FFF] = b.Byte()
			return
		}
	} else if blk == abOam {
		if owner {
			m.oam[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abP1 {
		m.ioP1.writeByte(b, owner)
		if !owner {
			m.kp.RunCommand(CmdKeyCheck, nil)
		}
		return
	} else if blk == abIF {
		m.ioIF.writeByte(b, owner)
		return
	} else if blk == abGpuRegs {
		if owner {
			m.gpuregs[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abZero {
		if owner {
			m.zero[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abIE {
		if owner {
			m.ie = b.Byte()
			return
		}
	}
	if !owner {
		panic(fmt.Sprintf("unauthorized write: 0x%04X 0x%02X", addr.Word(), b.Byte()))
	}
	if u, v := m.getAddressInfo(addr); !v {
		panic(fmt.Sprintf("unhandled memory write: 0x%04X - %s", addr.Word(), u))
	}
}

func (m *Mmu) ReadIoByte(addr Worder, ak AddressKeys) (Byte, bool) {
	blk, _ := m.selectAddressBlock(addr, "write")
	owner := addressBlock(ak)&blk == blk
	if blk == abP1 {
		return m.ioP1.readIoByte(owner)
	} else if blk == abIF {
		return m.ioIF.readIoByte(owner)
	}
	panic(fmt.Sprintf("unhandled queued write: 0x%04X", addr.Word()))
}

// incomplete, used for debugging
func (m *Mmu) getAddressInfo(addr Worder) (string, bool) {
	a := addr.Word()
	if 0x9C00 <= a && a <= 0x9FFF {
		return "Background Map Data 2", false
	} else if 0xFEA0 <= a && a <= 0xFEFF {
		return "unusable memory", true
	} else if a == 0xFF00 {
		return "Register for reading joy pad info and determining system type. (R/W)", false
	} else if a == 0xFF01 {
		return "Serial transfer data (R/W)", true
	} else if a == 0xFF02 {
		return "SIO control (R/W)", true
	} else if a == 0xFF03 {
		return "no clue", true
	} else if a == 0xFF04 {
		return "DIV", true // TODO: priority
	} else if a == 0xFF05 {
		return "TIMA", true // TODO: priority
	} else if a == 0xFF06 {
		return "TMA", true // TODO: priority
	} else if a == 0xFF07 {
		return "TAC", true // TODO: priority
	} else if 0xFF08 <= a && a <= 0xFF0E {
		return "no clue", true
	} else if a == 0xFF10 {
		return "Sound Mode 1 register, Sweep register (R/W)", true
	} else if a == 0xFF11 {
		return "Sound Mode 1 register, Sound length/Wave pattern duty (R/W)", true
	} else if a == 0xFF12 {
		return "Sound Mode 1 register, Envelope (R/W)", true
	} else if a == 0xFF13 {
		return "Sound Mode 1 register, Frequency lo (W)", true
	} else if a == 0xFF14 {
		return "Sound Mode 1 register, Frequency hi (R/W)", true
	} else if a == 0xFF17 {
		return "Sound Mode 2 register, envelope (R/W)", true
	} else if a == 0xFF19 {
		return "Sound Mode 2 register, frequency", true
	} else if a == 0xFF1A {
		return "Sound Mode 3 register, Sound on/off (R/W)", true
	} else if a == 0xFF20 {
		return "Sound Mode 4 register, sound length (R/W)", true
	} else if a == 0xFF21 {
		return "Sound Mode 4 register, envelope (R/W)", true
	} else if a == 0xFF23 {
		return "Sound Mode 4 register, counter/consecutive; inital (R/W)", true
	} else if a == 0xFF24 {
		return "Channel control / ON-OFF / Volume (R/W)", true
	} else if a == 0xFF25 {
		return "Selection of Sound output terminal (R/W)", true
	} else if a == 0xFF26 {
		return "Sound on/off (R/W)", true
	} else if a == 0xFF47 {
		return "BGP", false
	} else if 0xFF4D <= a && a <= 0xFF7F {
		return "GBC", true
	} else if a == 0xFFFF {
		return "IE", false
	}
	return "unknown", false
}

// memory mapped io
type mmio struct {
	addr Word

	// accessed by owner
	value Byte

	// accessed through lock
	read   Byte
	write  Byte
	queued bool
	lock   *sync.Mutex
}

func newMmio(addr Worder) *mmio {
	m := &mmio{addr: addr.Word(),
		lock: new(sync.Mutex)}
	return m
}

func (m *mmio) readByte(owner bool) Byte {
	if owner {
		return m.value
	}
	m.lock.Lock()
	defer m.lock.Unlock()
	return m.read
}

func (m *mmio) writeByte(b Byter, owner bool) {
	if owner {
		m.lock.Lock()
		defer m.lock.Unlock()
		m.value = b.Byte()
		m.read = m.value
		if !m.queued {
			m.write = m.value
		}
	} else {
		m.lock.Lock()
		defer m.lock.Unlock()
		if m.queued {
			//panic(fmt.Sprintf("overwritten io write: 0x%04X", m.addr))
		}
		m.queued = true
		m.write = b.Byte()
	}
}

func (m *mmio) readIoByte(owner bool) (Byte, bool) {
	if owner {
		m.lock.Lock()
		defer m.lock.Unlock()
		q := m.queued
		m.queued = false
		return m.write, q
	}
	panic(fmt.Sprintf("unhandled io read: 0x%04X", m.addr))
}
<commit_msg>don't handle sound sample ram<commit_after>package jibi

import (
	"fmt"
	"sync"
)

// A list of all the special memory addresses.
const (
	AddrRom    Word = 0x0000
	AddrVRam   Word = 0x8000
	AddrERam   Word = 0xA000
	AddrRam    Word = 0xC000
	AddrOam    Word = 0xFE00
	AddrOamEnd Word = 0xFEA0

	AddrP1  Word = 0xFF00
	AddrDIV Word = 0xFF04
	AddrTMA Word = 0xFF06
	AddrIF  Word = 0xFF0F

	AddrGpuRegs    Word = 0xFF40
	AddrLCDC       Word = 0xFF40
	AddrSTAT       Word = 0xFF41
	AddrSCY        Word = 0xFF42
	AddrSCX        Word = 0xFF43
	AddrLY         Word = 0xFF44
	AddrLYC        Word = 0xFF45
	AddrDMA        Word = 0xFF46
	AddrBGP        Word = 0xFF47
	AddrOBP0       Word = 0xFF48
	AddrOBP1       Word = 0xFF49
	AddrWY         Word = 0xFF4A
	AddrWX         Word = 0xFF4B
	AddrGpuRegsEnd Word = 0xFF4C

	AddrZero Word = 0xFF80
	AddrIE   Word = 0xFFFF
)

// An Mmu is the memory management unit. Its purpose is to dispatch read and
// write requeststo the appropriate module (cpu, gpu, etc) based on the memory
// address. The Mmu is controlled by the cpu.
type Mmu struct {
	// memory blocks
	rom     []Byte
	vram    []Byte
	ram     []Byte
	oam     []Byte
	gpuregs []Byte
	zero    []Byte
	ie      Byte

	// memory mapped io
	ioIF *mmio
	ioP1 *mmio

	// memory locks
	locks []*sync.Mutex

	// internal state
	kp *Keypad
}

// NewMmu creates a new Mmu with an optional bios that replaces 0x0000-0x00FF.
func NewMmu(cart *Cartridge) *Mmu {
	var rom []Byte
	if cart != nil {
		rom = cart.Rom
	}
	locks := make([]*sync.Mutex, abLast+1)
	for i := uint16(1); i <= uint16(abLast); i = i << 1 {
		locks[i] = new(sync.Mutex)
	}
	mmu := &Mmu{
		rom:     rom,
		vram:    make([]Byte, 0x2000),
		ram:     make([]Byte, 0x2000),
		oam:     make([]Byte, 0xA0),
		ioIF:    newMmio(AddrIF),
		ioP1:    newMmio(AddrP1),
		gpuregs: make([]Byte, 12),
		zero:    make([]Byte, 0x100),
		locks:   locks,
	}
	return mmu
}

type addressBlock uint16
type AddressKeys uint16

const (
	abNil addressBlock = iota
	abRom addressBlock = 1 << iota
	abVRam
	abERam
	abRam
	abOam
	abP1
	abIF
	abGpuRegs
	abZero
	abIE
	abLast = abIE
)

func (a addressBlock) String() string {
	switch a {
	case abNil:
		return "abNil"
	case abRom:
		return "abRom"
	case abVRam:
		return "abVRam"
	case abERam:
		return "abERam"
	case abRam:
		return "abRam"
	case abOam:
		return "abOam"
	case abIF:
		return "abIF"
	case abGpuRegs:
		return "abGpuRegs"
	case abZero:
		return "abZero"
	case abIE:
		return "abIE"
	}
	return "abUNKNOWN"
}

func (m *Mmu) SetKeypad(kp *Keypad) {
	m.kp = kp
}

func (m *Mmu) selectAddressBlock(addr Worder, rw string) (addressBlock, Word) {
	a := addr.Word()
	if a < AddrVRam {
		return abRom, 0
	} else if AddrVRam <= a && a < AddrERam {
		return abVRam, AddrVRam
	} else if AddrERam <= a && a < AddrRam {
		return abERam, AddrERam
	} else if AddrRam <= a && a < AddrOam {
		return abRam, AddrRam
	} else if AddrOam <= a && a < AddrOamEnd {
		return abOam, AddrOam
	} else if AddrP1 == a {
		return abP1, AddrP1
	} else if AddrIF == a {
		return abIF, AddrIF
	} else if AddrGpuRegs <= a && a < AddrGpuRegsEnd {
		return abGpuRegs, AddrGpuRegs
	} else if AddrZero <= a && a < AddrIE {
		return abZero, AddrZero
	} else if AddrIE == a {
		return abIE, AddrIE
	}

	u, v := m.getAddressInfo(addr)
	if !v {
		if rw == "" {
			rw = "access"
		}
		panic(fmt.Sprintf("unhandled memory %s: 0x%04X - %s", rw, a, u))
	}
	return abNil, 0
}

// LockAddr gets a lock for an address if not already in the provided
// AddressKeys and appends it and returns this new key set.
func (m *Mmu) LockAddr(addr Worder, ak AddressKeys) AddressKeys {
	blk, _ := m.selectAddressBlock(addr, "lock")
	if addressBlock(ak)&blk == blk {
		// already have the key
		return ak
	}
	m.locks[blk].Lock()
	return ak | AddressKeys(blk)
}

func (m *Mmu) UnlockAddr(addr Worder, ak AddressKeys) AddressKeys {
	blk, _ := m.selectAddressBlock(addr, "unlock")
	if addressBlock(ak)&blk != blk {
		// don't have the key
		return ak
	}
	m.locks[blk].Unlock()
	return ak & AddressKeys(blk^0xFFFF)
}

func (m *Mmu) ReadByteAt(addr Worder, ak AddressKeys) Byte {
	blk, start := m.selectAddressBlock(addr, "read")
	owner := addressBlock(ak)&blk == blk
	if blk == abRom {
		if owner {
			return m.rom[addr.Word()-start]
		}
	}
	if blk == abVRam {
		if owner {
			return m.vram[addr.Word()-start]
		}
	} else if blk == abRam {
		if owner {
			return m.ram[(addr.Word()-start)&0x1FFF]
		}
	} else if blk == abOam {
		if owner {
			return m.oam[addr.Word()-start]
		}
	} else if blk == abP1 {
		return m.ioP1.readByte(owner)
	} else if blk == abIF {
		return m.ioIF.readByte(owner)
	} else if blk == abGpuRegs {
		if owner {
			return m.gpuregs[addr.Word()-start]
		}
	} else if blk == abZero {
		if owner {
			return m.zero[addr.Word()-start]
		}
	} else if blk == abIE {
		if owner {
			return m.ie
		}
	}
	if !owner {
		panic(fmt.Sprintf("unauthorized read: 0x%04X", addr.Word()))
	}
	if u, v := m.getAddressInfo(addr); !v {
		panic(fmt.Sprintf("unhandled memory read: 0x%04X - %s", addr.Word(), u))
	}
	return 0
}

func (m *Mmu) WriteByteAt(addr Worder, b Byter, ak AddressKeys) {
	blk, start := m.selectAddressBlock(addr, "write")
	owner := addressBlock(ak)&blk == blk
	if blk == abRom {
		return
	} else if blk == abVRam {
		if owner {
			m.vram[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abRam {
		if owner {
			m.ram[(addr.Word()-start)&0x1FFF] = b.Byte()
			return
		}
	} else if blk == abOam {
		if owner {
			m.oam[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abP1 {
		m.ioP1.writeByte(b, owner)
		if !owner {
			m.kp.RunCommand(CmdKeyCheck, nil)
		}
		return
	} else if blk == abIF {
		m.ioIF.writeByte(b, owner)
		return
	} else if blk == abGpuRegs {
		if owner {
			m.gpuregs[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abZero {
		if owner {
			m.zero[addr.Word()-start] = b.Byte()
			return
		}
	} else if blk == abIE {
		if owner {
			m.ie = b.Byte()
			return
		}
	}
	if !owner {
		panic(fmt.Sprintf("unauthorized write: 0x%04X 0x%02X", addr.Word(), b.Byte()))
	}
	if u, v := m.getAddressInfo(addr); !v {
		panic(fmt.Sprintf("unhandled memory write: 0x%04X - %s", addr.Word(), u))
	}
}

func (m *Mmu) ReadIoByte(addr Worder, ak AddressKeys) (Byte, bool) {
	blk, _ := m.selectAddressBlock(addr, "write")
	owner := addressBlock(ak)&blk == blk
	if blk == abP1 {
		return m.ioP1.readIoByte(owner)
	} else if blk == abIF {
		return m.ioIF.readIoByte(owner)
	}
	panic(fmt.Sprintf("unhandled queued write: 0x%04X", addr.Word()))
}

// incomplete, used for debugging
func (m *Mmu) getAddressInfo(addr Worder) (string, bool) {
	a := addr.Word()
	if 0x9C00 <= a && a <= 0x9FFF {
		return "Background Map Data 2", false
	} else if 0xFEA0 <= a && a <= 0xFEFF {
		return "unusable memory", true
	} else if a == 0xFF00 {
		return "Register for reading joy pad info and determining system type. (R/W)", false
	} else if a == 0xFF01 {
		return "Serial transfer data (R/W)", true
	} else if a == 0xFF02 {
		return "SIO control (R/W)", true
	} else if a == 0xFF03 {
		return "no clue", true
	} else if a == 0xFF04 {
		return "DIV", true // TODO: priority
	} else if a == 0xFF05 {
		return "TIMA", true // TODO: priority
	} else if a == 0xFF06 {
		return "TMA", true // TODO: priority
	} else if a == 0xFF07 {
		return "TAC", true // TODO: priority
	} else if 0xFF08 <= a && a <= 0xFF0E {
		return "no clue", true
	} else if a == 0xFF10 {
		return "Sound Mode 1 register, Sweep register (R/W)", true
	} else if a == 0xFF11 {
		return "Sound Mode 1 register, Sound length/Wave pattern duty (R/W)", true
	} else if a == 0xFF12 {
		return "Sound Mode 1 register, Envelope (R/W)", true
	} else if a == 0xFF13 {
		return "Sound Mode 1 register, Frequency lo (W)", true
	} else if a == 0xFF14 {
		return "Sound Mode 1 register, Frequency hi (R/W)", true
	} else if a == 0xFF17 {
		return "Sound Mode 2 register, envelope (R/W)", true
	} else if a == 0xFF19 {
		return "Sound Mode 2 register, frequency", true
	} else if a == 0xFF1A {
		return "Sound Mode 3 register, Sound on/off (R/W)", true
	} else if a == 0xFF20 {
		return "Sound Mode 4 register, sound length (R/W)", true
	} else if a == 0xFF21 {
		return "Sound Mode 4 register, envelope (R/W)", true
	} else if a == 0xFF23 {
		return "Sound Mode 4 register, counter/consecutive; inital (R/W)", true
	} else if a == 0xFF24 {
		return "Channel control / ON-OFF / Volume (R/W)", true
	} else if a == 0xFF25 {
		return "Selection of Sound output terminal (R/W)", true
	} else if a == 0xFF26 {
		return "Sound on/off (R/W)", true
	} else if 0xFF30 <= a && a <= 0xFF3F {
		return "Sound Sample RAM", true
	} else if a == 0xFF47 {
		return "BGP", false
	} else if 0xFF4D <= a && a <= 0xFF7F {
		return "GBC", true
	} else if a == 0xFFFF {
		return "IE", false
	}
	return "unknown", false
}

// memory mapped io
type mmio struct {
	addr Word

	// accessed by owner
	value Byte

	// accessed through lock
	read   Byte
	write  Byte
	queued bool
	lock   *sync.Mutex
}

func newMmio(addr Worder) *mmio {
	m := &mmio{addr: addr.Word(),
		lock: new(sync.Mutex)}
	return m
}

func (m *mmio) readByte(owner bool) Byte {
	if owner {
		return m.value
	}
	m.lock.Lock()
	defer m.lock.Unlock()
	return m.read
}

func (m *mmio) writeByte(b Byter, owner bool) {
	if owner {
		m.lock.Lock()
		defer m.lock.Unlock()
		m.value = b.Byte()
		m.read = m.value
		if !m.queued {
			m.write = m.value
		}
	} else {
		m.lock.Lock()
		defer m.lock.Unlock()
		if m.queued {
			//panic(fmt.Sprintf("overwritten io write: 0x%04X", m.addr))
		}
		m.queued = true
		m.write = b.Byte()
	}
}

func (m *mmio) readIoByte(owner bool) (Byte, bool) {
	if owner {
		m.lock.Lock()
		defer m.lock.Unlock()
		q := m.queued
		m.queued = false
		return m.write, q
	}
	panic(fmt.Sprintf("unhandled io read: 0x%04X", m.addr))
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"io/ioutil"
	"path"

	"github.com/goby-lang/goby/bytecode"
	"github.com/goby-lang/goby/parser"
)

var (
	objectClass *RClass
	classClass  *RClass
)

func initTopLevelClasses() {
	globalMethods := newEnvironment()
	classMethods := newEnvironment()

	for _, m := range builtinGlobalMethods {
		globalMethods.set(m.Name, m)
	}

	for _, m := range builtinClassClassMethods {
		classMethods.set(m.Name, m)
	}

	classClass = &RClass{
		BaseClass: &BaseClass{
			Name:         "Class",
			Methods:      globalMethods,
			ClassMethods: classMethods,
			constants:    make(map[string]*Pointer),
		},
	}

	objectClass = &RClass{
		BaseClass: &BaseClass{
			Name:         "Object",
			Class:        classClass,
			Methods:      globalMethods,
			ClassMethods: newEnvironment(),
			constants:    make(map[string]*Pointer),
		},
	}
}

// initializeClass initializes and returns a class instance with given class name
func initializeClass(name string, isModule bool) *RClass {
	class := &RClass{
		BaseClass: &BaseClass{
			Name:             name,
			Methods:          newEnvironment(),
			ClassMethods:     newEnvironment(),
			Class:            classClass,
			pseudoSuperClass: objectClass,
			superClass:       objectClass,
			constants:        make(map[string]*Pointer),
			isModule:         isModule,
		},
	}

	return class
}

// Class is an interface that implements a class's basic functions.
//
// - lookupClassMethod: search for current class's class method with given name.
// - lookupInstanceMethod: search for current class's instance method with given name.
// - ReturnName returns class's name
type Class interface {
	lookupClassMethod(string) Object
	lookupInstanceMethod(string) Object
	lookupConstant(string, bool) *Pointer
	ReturnName() string
	returnSuperClass() Class
	BaseObject
}

// RClass represents normal (not built in) class object
type RClass struct {
	*BaseClass
}

// BaseClass is a embedded struct that contains all the essential fields for a class
type BaseClass struct {
	// Name is the class's name
	Name string
	// Methods contains its instances' methods
	Methods *environment
	// ClassMethods contains this class's methods
	ClassMethods *environment
	// pseudoSuperClass points to the class it inherits
	pseudoSuperClass *RClass
	// This is the class where we should looking for a method.
	// It can be normal class, singleton class or a module.
	superClass *RClass
	// Class points to this class's class, which should be ClassClass
	Class *RClass
	// Singleton is a flag marks if this class a singleton class
	Singleton bool
	isModule  bool
	constants map[string]*Pointer
	scope     Class
}

// objectType returns class object's type
func (c *BaseClass) objectType() objectType {
	return classObj
}

// Inspect returns the basic inspected result (which is class name) of current class
// TODO: Singleton class's inspect() should also mark if it's a singleton class explicitly.
func (c *BaseClass) Inspect() string {
	if c.isModule {
		return "<Module:" + c.Name + ">"
	}
	return "<Class:" + c.Name + ">"
}

func (c *BaseClass) lookupClassMethod(methodName string) Object {
	method, ok := c.ClassMethods.get(methodName)

	if !ok {
		if c.superClass != nil {
			return c.superClass.lookupClassMethod(methodName)
		}
		if c.Class != nil {
			return c.Class.lookupClassMethod(methodName)
		}
		return nil
	}

	return method
}

func (c *BaseClass) lookupInstanceMethod(methodName string) Object {
	method, ok := c.Methods.get(methodName)

	if !ok {
		if c.superClass != nil {
			return c.superClass.lookupInstanceMethod(methodName)
		}

		if c.Class != nil {
			return c.Class.lookupInstanceMethod(methodName)
		}

		return nil
	}

	return method
}

func (c *BaseClass) lookupConstant(constName string, findInScope bool) *Pointer {
	constant, ok := c.constants[constName]

	if !ok {
		if findInScope && c.scope != nil {
			return c.scope.lookupConstant(constName, true)
		}

		if c.superClass != nil {
			return c.superClass.lookupConstant(constName, false)
		}

		return nil
	}

	return constant
}

// setSingletonMethod will sets method to class's singleton class
// However, if the class doesn't have a singleton class, it will create one for it first.
func (c *BaseClass) setSingletonMethod(name string, method *Method) {
	if c.pseudoSuperClass.Singleton {
		c.pseudoSuperClass.ClassMethods.set(name, method)
	}

	class := initializeClass(c.Name+"singleton", false)
	class.Singleton = true
	class.ClassMethods.set(name, method)
	class.superClass = c.superClass
	class.Class = classClass
	c.superClass = class
}

func (c *BaseClass) returnClass() Class {
	return c.Class
}

// ReturnName returns the name of the class
func (c *BaseClass) ReturnName() string {
	return c.Name
}

func (c *BaseClass) returnSuperClass() Class {
	return c.pseudoSuperClass
}

func (c *BaseClass) getConstants() map[string]*Pointer {
	return c.constants
}

func (c *RClass) initializeInstance() *RObject {
	instance := &RObject{Class: c, InstanceVariables: newEnvironment()}

	return instance
}

func (c *RClass) setAttrWriter(args []Object) {
	for _, attr := range args {
		attrName := attr.(*StringObject).Value
		m := &BuiltInMethod{
			Name: attrName + "=",
			Fn: func(receiver Object) builtinMethodBody {
				return func(vm *VM, args []Object, blockFrame *callFrame) Object {
					v := receiver.(*RObject).InstanceVariables.set("@"+attrName, args[0])
					return v
				}
			},
		}

		c.Methods.set(attrName+"=", m)
	}
}

func (c *RClass) setAttrReader(args []Object) {
	for _, attr := range args {
		attrName := attr.(*StringObject).Value
		m := &BuiltInMethod{
			Name: attrName,
			Fn: func(receiver Object) builtinMethodBody {
				return func(vm *VM, args []Object, blockFrame *callFrame) Object {
					v, _ := receiver.(*RObject).InstanceVariables.get("@" + attrName)
					return v
				}
			},
		}

		c.Methods.set(attrName, m)
	}
}

var builtinGlobalMethods = []*BuiltInMethod{
	{
		Name: "require",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				libName := args[0].(*StringObject).Value
				initFunc, ok := standardLibraries[libName]

				if !ok {
					msg := "Can't require \"" + libName + "\""
					vm.returnError(msg)
				}

				initFunc(vm)

				return TRUE
			}
		},
	},
	{
		Name: "require_relative",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				filepath := args[0].(*StringObject).Value
				filepath = path.Join(vm.fileDir, filepath)

				file, err := ioutil.ReadFile(filepath + ".gb")

				if err != nil {
					vm.returnError(err.Error())
				}

				vm.execRequiredFile(filepath, file)

				return TRUE
			}
		},
	},
	{
		Name: "puts",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				for _, arg := range args {
					fmt.Println(arg.Inspect())
				}

				return NULL
			}
		},
	},
	{
		Name: "class",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				switch r := receiver.(type) {
				case BaseObject:
					return r.returnClass()
				case Class:
					return r.returnClass()
				default:
					return &Error{Message: "Can't call class on %T" + string(r.objectType())}
				}
			}
		},
	},
	{
		Name: "!",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				return FALSE
			}
		},
	},
}

// BuiltinClassMethods is a collection of class methods used by Class
var builtinClassClassMethods = []*BuiltInMethod{
	{
		Name: "attr_reader",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)
				r.setAttrReader(args)

				return r
			}
		},
	},
	{
		Name: "attr_writer",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)
				r.setAttrWriter(args)

				return r
			}
		},
	},
	{
		Name: "attr_accessor",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)

				attrReader := r.lookupClassMethod("attr_reader")
				attrWriter := r.lookupClassMethod("attr_writer")
				readFn := attrReader.(*BuiltInMethod).Fn(receiver)
				writeFn := attrWriter.(*BuiltInMethod).Fn(receiver)

				readFn(vm, args, blockFrame)
				writeFn(vm, args, blockFrame)

				return r
			}
		},
	},
	{
		Name: "include",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				module := args[0].(*RClass)
				class := receiver.(*RClass)
				module.superClass = class.superClass
				class.superClass = module

				return class
			}
		},
	},
	{
		Name: "new",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				class := receiver.(*RClass)
				instance := class.initializeInstance()
				initMethod := class.lookupInstanceMethod("initialize")

				if initMethod != nil {
					instance.InitializeMethod = initMethod.(*Method)
				}

				return instance
			}
		},
	},
	{
		Name: "name",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				name := receiver.(Class).ReturnName()
				nameString := initializeString(name)
				return nameString
			}
		},
	},
	{
		Name: "superclass",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				return receiver.(Class).returnSuperClass()
			}
		},
	},
}

func (vm *VM) execRequiredFile(filepath string, file []byte) {
	program := parser.BuildAST(file)
	g := bytecode.NewGenerator(program)
	bytecodes := g.GenerateByteCode(program)

	oldMethodTable := isTable{}
	oldClassTable := isTable{}

	// Copy current file's instruction sets.
	for name, is := range vm.isTables[bytecode.LabelDef] {
		oldMethodTable[name] = is
	}

	for name, is := range vm.isTables[bytecode.LabelDefClass] {
		oldClassTable[name] = is
	}

	// This creates new execution environments for required file, including new instruction set table.
	// So we need to copy old instruction sets and restore them later, otherwise current program's instruction set would be overwrite.
	vm.ExecBytecodes(bytecodes, filepath)

	// Restore instruction sets.
	vm.isTables[bytecode.LabelDef] = oldMethodTable
	vm.isTables[bytecode.LabelDefClass] = oldClassTable
}
<commit_msg>Simplify Class.attr_accessor's implementation.<commit_after>package vm

import (
	"fmt"
	"io/ioutil"
	"path"

	"github.com/goby-lang/goby/bytecode"
	"github.com/goby-lang/goby/parser"
)

var (
	objectClass *RClass
	classClass  *RClass
)

func initTopLevelClasses() {
	globalMethods := newEnvironment()
	classMethods := newEnvironment()

	for _, m := range builtinGlobalMethods {
		globalMethods.set(m.Name, m)
	}

	for _, m := range builtinClassClassMethods {
		classMethods.set(m.Name, m)
	}

	classClass = &RClass{
		BaseClass: &BaseClass{
			Name:         "Class",
			Methods:      globalMethods,
			ClassMethods: classMethods,
			constants:    make(map[string]*Pointer),
		},
	}

	objectClass = &RClass{
		BaseClass: &BaseClass{
			Name:         "Object",
			Class:        classClass,
			Methods:      globalMethods,
			ClassMethods: newEnvironment(),
			constants:    make(map[string]*Pointer),
		},
	}
}

// initializeClass initializes and returns a class instance with given class name
func initializeClass(name string, isModule bool) *RClass {
	class := &RClass{
		BaseClass: &BaseClass{
			Name:             name,
			Methods:          newEnvironment(),
			ClassMethods:     newEnvironment(),
			Class:            classClass,
			pseudoSuperClass: objectClass,
			superClass:       objectClass,
			constants:        make(map[string]*Pointer),
			isModule:         isModule,
		},
	}

	return class
}

// Class is an interface that implements a class's basic functions.
//
// - lookupClassMethod: search for current class's class method with given name.
// - lookupInstanceMethod: search for current class's instance method with given name.
// - ReturnName returns class's name
type Class interface {
	lookupClassMethod(string) Object
	lookupInstanceMethod(string) Object
	lookupConstant(string, bool) *Pointer
	ReturnName() string
	returnSuperClass() Class
	BaseObject
}

// RClass represents normal (not built in) class object
type RClass struct {
	*BaseClass
}

// BaseClass is a embedded struct that contains all the essential fields for a class
type BaseClass struct {
	// Name is the class's name
	Name string
	// Methods contains its instances' methods
	Methods *environment
	// ClassMethods contains this class's methods
	ClassMethods *environment
	// pseudoSuperClass points to the class it inherits
	pseudoSuperClass *RClass
	// This is the class where we should looking for a method.
	// It can be normal class, singleton class or a module.
	superClass *RClass
	// Class points to this class's class, which should be ClassClass
	Class *RClass
	// Singleton is a flag marks if this class a singleton class
	Singleton bool
	isModule  bool
	constants map[string]*Pointer
	scope     Class
}

// objectType returns class object's type
func (c *BaseClass) objectType() objectType {
	return classObj
}

// Inspect returns the basic inspected result (which is class name) of current class
// TODO: Singleton class's inspect() should also mark if it's a singleton class explicitly.
func (c *BaseClass) Inspect() string {
	if c.isModule {
		return "<Module:" + c.Name + ">"
	}
	return "<Class:" + c.Name + ">"
}

func (c *BaseClass) lookupClassMethod(methodName string) Object {
	method, ok := c.ClassMethods.get(methodName)

	if !ok {
		if c.superClass != nil {
			return c.superClass.lookupClassMethod(methodName)
		}
		if c.Class != nil {
			return c.Class.lookupClassMethod(methodName)
		}
		return nil
	}

	return method
}

func (c *BaseClass) lookupInstanceMethod(methodName string) Object {
	method, ok := c.Methods.get(methodName)

	if !ok {
		if c.superClass != nil {
			return c.superClass.lookupInstanceMethod(methodName)
		}

		if c.Class != nil {
			return c.Class.lookupInstanceMethod(methodName)
		}

		return nil
	}

	return method
}

func (c *BaseClass) lookupConstant(constName string, findInScope bool) *Pointer {
	constant, ok := c.constants[constName]

	if !ok {
		if findInScope && c.scope != nil {
			return c.scope.lookupConstant(constName, true)
		}

		if c.superClass != nil {
			return c.superClass.lookupConstant(constName, false)
		}

		return nil
	}

	return constant
}

// setSingletonMethod will sets method to class's singleton class
// However, if the class doesn't have a singleton class, it will create one for it first.
func (c *BaseClass) setSingletonMethod(name string, method *Method) {
	if c.pseudoSuperClass.Singleton {
		c.pseudoSuperClass.ClassMethods.set(name, method)
	}

	class := initializeClass(c.Name+"singleton", false)
	class.Singleton = true
	class.ClassMethods.set(name, method)
	class.superClass = c.superClass
	class.Class = classClass
	c.superClass = class
}

func (c *BaseClass) returnClass() Class {
	return c.Class
}

// ReturnName returns the name of the class
func (c *BaseClass) ReturnName() string {
	return c.Name
}

func (c *BaseClass) returnSuperClass() Class {
	return c.pseudoSuperClass
}

func (c *BaseClass) getConstants() map[string]*Pointer {
	return c.constants
}

func (c *RClass) initializeInstance() *RObject {
	instance := &RObject{Class: c, InstanceVariables: newEnvironment()}

	return instance
}

func (c *RClass) setAttrWriter(args []Object) {
	for _, attr := range args {
		attrName := attr.(*StringObject).Value
		m := &BuiltInMethod{
			Name: attrName + "=",
			Fn: func(receiver Object) builtinMethodBody {
				return func(vm *VM, args []Object, blockFrame *callFrame) Object {
					v := receiver.(*RObject).InstanceVariables.set("@"+attrName, args[0])
					return v
				}
			},
		}

		c.Methods.set(attrName+"=", m)
	}
}

func (c *RClass) setAttrReader(args []Object) {
	for _, attr := range args {
		attrName := attr.(*StringObject).Value
		m := &BuiltInMethod{
			Name: attrName,
			Fn: func(receiver Object) builtinMethodBody {
				return func(vm *VM, args []Object, blockFrame *callFrame) Object {
					v, _ := receiver.(*RObject).InstanceVariables.get("@" + attrName)
					return v
				}
			},
		}

		c.Methods.set(attrName, m)
	}
}

var builtinGlobalMethods = []*BuiltInMethod{
	{
		Name: "require",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				libName := args[0].(*StringObject).Value
				initFunc, ok := standardLibraries[libName]

				if !ok {
					msg := "Can't require \"" + libName + "\""
					vm.returnError(msg)
				}

				initFunc(vm)

				return TRUE
			}
		},
	},
	{
		Name: "require_relative",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				filepath := args[0].(*StringObject).Value
				filepath = path.Join(vm.fileDir, filepath)

				file, err := ioutil.ReadFile(filepath + ".gb")

				if err != nil {
					vm.returnError(err.Error())
				}

				vm.execRequiredFile(filepath, file)

				return TRUE
			}
		},
	},
	{
		Name: "puts",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				for _, arg := range args {
					fmt.Println(arg.Inspect())
				}

				return NULL
			}
		},
	},
	{
		Name: "class",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				switch r := receiver.(type) {
				case BaseObject:
					return r.returnClass()
				case Class:
					return r.returnClass()
				default:
					return &Error{Message: "Can't call class on %T" + string(r.objectType())}
				}
			}
		},
	},
	{
		Name: "!",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				return FALSE
			}
		},
	},
}

// BuiltinClassMethods is a collection of class methods used by Class
var builtinClassClassMethods = []*BuiltInMethod{
	{
		Name: "attr_reader",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)
				r.setAttrReader(args)

				return r
			}
		},
	},
	{
		Name: "attr_writer",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)
				r.setAttrWriter(args)

				return r
			}
		},
	},
	{
		Name: "attr_accessor",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				r := receiver.(*RClass)

				r.setAttrWriter(args)
				r.setAttrReader(args)

				return r
			}
		},
	},
	{
		Name: "include",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {
				module := args[0].(*RClass)
				class := receiver.(*RClass)
				module.superClass = class.superClass
				class.superClass = module

				return class
			}
		},
	},
	{
		Name: "new",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				class := receiver.(*RClass)
				instance := class.initializeInstance()
				initMethod := class.lookupInstanceMethod("initialize")

				if initMethod != nil {
					instance.InitializeMethod = initMethod.(*Method)
				}

				return instance
			}
		},
	},
	{
		Name: "name",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				name := receiver.(Class).ReturnName()
				nameString := initializeString(name)
				return nameString
			}
		},
	},
	{
		Name: "superclass",
		Fn: func(receiver Object) builtinMethodBody {
			return func(vm *VM, args []Object, blockFrame *callFrame) Object {

				return receiver.(Class).returnSuperClass()
			}
		},
	},
}

func (vm *VM) execRequiredFile(filepath string, file []byte) {
	program := parser.BuildAST(file)
	g := bytecode.NewGenerator(program)
	bytecodes := g.GenerateByteCode(program)

	oldMethodTable := isTable{}
	oldClassTable := isTable{}

	// Copy current file's instruction sets.
	for name, is := range vm.isTables[bytecode.LabelDef] {
		oldMethodTable[name] = is
	}

	for name, is := range vm.isTables[bytecode.LabelDefClass] {
		oldClassTable[name] = is
	}

	// This creates new execution environments for required file, including new instruction set table.
	// So we need to copy old instruction sets and restore them later, otherwise current program's instruction set would be overwrite.
	vm.ExecBytecodes(bytecodes, filepath)

	// Restore instruction sets.
	vm.isTables[bytecode.LabelDef] = oldMethodTable
	vm.isTables[bytecode.LabelDefClass] = oldClassTable
}
<|endoftext|>


<commit_before>package engine

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/clearsign"
)

type PGPVerifyArg struct {
	Source       io.Reader
	Signature    []byte
	SignedBy     string
	TrackOptions keybase1.TrackOptions
}

// PGPVerify is an engine.
type PGPVerify struct {
	arg        *PGPVerifyArg
	peek       *libkb.Peeker
	signStatus *libkb.SignatureStatus
	owner      *libkb.User
	libkb.Contextified
}

// NewPGPVerify creates a PGPVerify engine.
func NewPGPVerify(arg *PGPVerifyArg, g *libkb.GlobalContext) *PGPVerify {
	return &PGPVerify{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PGPVerify) Name() string {
	return "PGPVerify"
}

// GetPrereqs returns the engine prereqs.
func (e *PGPVerify) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *PGPVerify) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PGPVerify) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&PGPDecrypt{},
		&ScanKeys{},
		&Identify{},
	}
}

// Run starts the engine.
func (e *PGPVerify) Run(ctx *Context) error {
	e.peek = libkb.NewPeeker(e.arg.Source)
	if libkb.IsClearsign(e.peek) {
		return e.runClearsign(ctx)
	}
	if len(e.arg.Signature) == 0 {
		return e.runAttached(ctx)
	}
	return e.runDetached(ctx)
}

func (e *PGPVerify) SignatureStatus() *libkb.SignatureStatus {
	return e.signStatus
}

func (e *PGPVerify) Owner() *libkb.User {
	return e.owner
}

// runAttached verifies an attached signature
func (e *PGPVerify) runAttached(ctx *Context) error {
	arg := &PGPDecryptArg{
		Source:       e.peek,
		Sink:         libkb.NopWriteCloser{W: ioutil.Discard},
		AssertSigned: true,
		TrackOptions: e.arg.TrackOptions,
	}
	eng := NewPGPDecrypt(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	e.signStatus = eng.SignatureStatus()
	e.owner = eng.Owner()

	if err := e.checkSignedBy(ctx); err != nil {
		return err
	}

	return nil
}

// runDetached verifies a detached signature
func (e *PGPVerify) runDetached(ctx *Context) error {
	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	checkfn := openpgp.CheckDetachedSignature
	if libkb.IsArmored(e.arg.Signature) {
		checkfn = openpgp.CheckArmoredDetachedSignature
	}
	signer, err := checkfn(sk, e.peek, bytes.NewReader(e.arg.Signature))
	if err != nil {
		return err
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

// runClearsign verifies a clearsign signature
func (e *PGPVerify) runClearsign(ctx *Context) error {
	// clearsign decode only works with the whole data slice, not a reader
	// so have to read it all here:
	msg, err := ioutil.ReadAll(e.peek)
	if err != nil {
		return err
	}
	b, _ := clearsign.Decode(msg)
	if b == nil {
		return errors.New("Unable to decode clearsigned message")
	}

	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	signer, err := openpgp.CheckDetachedSignature(sk, bytes.NewReader(b.Bytes), b.ArmoredSignature.Body)
	if err != nil {
		return fmt.Errorf("Check sig error: %s", err)
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

func (e *PGPVerify) checkSignedBy(ctx *Context) error {
	if len(e.arg.SignedBy) == 0 {
		// no assertion necessary
		return nil
	}
	if !e.signStatus.Verified || e.signStatus.Entity == nil || e.owner == nil {
		// signature not valid, so no need to assert
		return nil
	}

	// have: a valid signature, the signature's owner, and a user assertion to
	// match against
	e.G().Log.Debug("checking signed by assertion: %q", e.arg.SignedBy)

	// load the user in SignedBy
	arg := NewIdentifyArg(e.arg.SignedBy, false, false)
	eng := NewIdentify(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	signByUser := eng.User()
	if signByUser == nil {
		// this shouldn't happen (engine should return an error in this state)
		// but just in case:
		return libkb.ErrNilUser
	}

	// check if it is equal to signature owner
	if !e.owner.Equal(signByUser) {
		return libkb.BadSigError{
			E: fmt.Sprintf("Signer %q did not match signed by assertion %q", e.owner.GetName(), e.arg.SignedBy),
		}
	}

	return nil
}

func (e *PGPVerify) outputSuccess(ctx *Context, signer *openpgp.Entity, owner *libkb.User) {
	fingerprint := libkb.PGPFingerprint(signer.PrimaryKey.Fingerprint)
	ctx.LogUI.Notice("Signature verified.  Signed by %s.  PGP Fingerprint: %s", owner.GetName(), fingerprint)
}
<commit_msg>nits -- better spacing<commit_after>package engine

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"

	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/protocol/go"
	"golang.org/x/crypto/openpgp"
	"golang.org/x/crypto/openpgp/clearsign"
)

type PGPVerifyArg struct {
	Source       io.Reader
	Signature    []byte
	SignedBy     string
	TrackOptions keybase1.TrackOptions
}

// PGPVerify is an engine.
type PGPVerify struct {
	arg        *PGPVerifyArg
	peek       *libkb.Peeker
	signStatus *libkb.SignatureStatus
	owner      *libkb.User
	libkb.Contextified
}

// NewPGPVerify creates a PGPVerify engine.
func NewPGPVerify(arg *PGPVerifyArg, g *libkb.GlobalContext) *PGPVerify {
	return &PGPVerify{
		arg:          arg,
		Contextified: libkb.NewContextified(g),
	}
}

// Name is the unique engine name.
func (e *PGPVerify) Name() string {
	return "PGPVerify"
}

// GetPrereqs returns the engine prereqs.
func (e *PGPVerify) Prereqs() Prereqs {
	return Prereqs{}
}

// RequiredUIs returns the required UIs.
func (e *PGPVerify) RequiredUIs() []libkb.UIKind {
	return []libkb.UIKind{}
}

// SubConsumers returns the other UI consumers for this engine.
func (e *PGPVerify) SubConsumers() []libkb.UIConsumer {
	return []libkb.UIConsumer{
		&PGPDecrypt{},
		&ScanKeys{},
		&Identify{},
	}
}

// Run starts the engine.
func (e *PGPVerify) Run(ctx *Context) error {
	e.peek = libkb.NewPeeker(e.arg.Source)
	if libkb.IsClearsign(e.peek) {
		return e.runClearsign(ctx)
	}
	if len(e.arg.Signature) == 0 {
		return e.runAttached(ctx)
	}
	return e.runDetached(ctx)
}

func (e *PGPVerify) SignatureStatus() *libkb.SignatureStatus {
	return e.signStatus
}

func (e *PGPVerify) Owner() *libkb.User {
	return e.owner
}

// runAttached verifies an attached signature
func (e *PGPVerify) runAttached(ctx *Context) error {
	arg := &PGPDecryptArg{
		Source:       e.peek,
		Sink:         libkb.NopWriteCloser{W: ioutil.Discard},
		AssertSigned: true,
		TrackOptions: e.arg.TrackOptions,
	}
	eng := NewPGPDecrypt(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	e.signStatus = eng.SignatureStatus()
	e.owner = eng.Owner()

	if err := e.checkSignedBy(ctx); err != nil {
		return err
	}

	return nil
}

// runDetached verifies a detached signature
func (e *PGPVerify) runDetached(ctx *Context) error {
	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	checkfn := openpgp.CheckDetachedSignature
	if libkb.IsArmored(e.arg.Signature) {
		checkfn = openpgp.CheckArmoredDetachedSignature
	}
	signer, err := checkfn(sk, e.peek, bytes.NewReader(e.arg.Signature))
	if err != nil {
		return err
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

// runClearsign verifies a clearsign signature
func (e *PGPVerify) runClearsign(ctx *Context) error {
	// clearsign decode only works with the whole data slice, not a reader
	// so have to read it all here:
	msg, err := ioutil.ReadAll(e.peek)
	if err != nil {
		return err
	}
	b, _ := clearsign.Decode(msg)
	if b == nil {
		return errors.New("Unable to decode clearsigned message")
	}

	sk, err := NewScanKeys(ctx.SecretUI, ctx.IdentifyUI, &e.arg.TrackOptions, e.G())
	if err != nil {
		return err
	}
	signer, err := openpgp.CheckDetachedSignature(sk, bytes.NewReader(b.Bytes), b.ArmoredSignature.Body)
	if err != nil {
		return fmt.Errorf("Check sig error: %s", err)
	}

	e.owner = sk.Owner()
	e.signStatus = &libkb.SignatureStatus{IsSigned: true}

	if signer != nil {
		e.signStatus.Verified = true
		e.signStatus.Entity = signer
		if err := e.checkSignedBy(ctx); err != nil {
			return err
		}
		e.outputSuccess(ctx, signer, sk.Owner())
	}

	return nil
}

func (e *PGPVerify) checkSignedBy(ctx *Context) error {
	if len(e.arg.SignedBy) == 0 {
		// no assertion necessary
		return nil
	}
	if !e.signStatus.Verified || e.signStatus.Entity == nil || e.owner == nil {
		// signature not valid, so no need to assert
		return nil
	}

	// have: a valid signature, the signature's owner, and a user assertion to
	// match against
	e.G().Log.Debug("checking signed by assertion: %q", e.arg.SignedBy)

	// load the user in SignedBy
	arg := NewIdentifyArg(e.arg.SignedBy, false, false)
	eng := NewIdentify(arg, e.G())
	if err := RunEngine(eng, ctx); err != nil {
		return err
	}
	signByUser := eng.User()
	if signByUser == nil {
		// this shouldn't happen (engine should return an error in this state)
		// but just in case:
		return libkb.ErrNilUser
	}

	// check if it is equal to signature owner
	if !e.owner.Equal(signByUser) {
		return libkb.BadSigError{
			E: fmt.Sprintf("Signer %q did not match signed by assertion %q", e.owner.GetName(), e.arg.SignedBy),
		}
	}

	return nil
}

func (e *PGPVerify) outputSuccess(ctx *Context, signer *openpgp.Entity, owner *libkb.User) {
	fingerprint := libkb.PGPFingerprint(signer.PrimaryKey.Fingerprint)
	ctx.LogUI.Notice("Signature verified. Signed by %s. PGP Fingerprint: %s", owner.GetName(), fingerprint)
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 Outbrain Inc.

   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

       http://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 inst

import (
	"fmt"

	"github.com/github/orchestrator/go/config"
	"github.com/openark/golib/log"
	test "github.com/openark/golib/tests"
	"testing"
)

var masterKey = InstanceKey{Hostname: "host1", Port: 3306}

func init() {
	config.Config.HostnameResolveMethod = "none"
	config.Config.KVClusterMasterPrefix = "test/master/"
	config.MarkConfigurationLoaded()
	log.SetLevel(log.ERROR)
}

func TestGetClusterMasterKVKey(t *testing.T) {
	kvKey := GetClusterMasterKVKey("foo")
	test.S(t).ExpectEquals(kvKey, "test/master/foo")
}

func TestGetClusterMasterKVPair(t *testing.T) {
	{
		kvPair := getClusterMasterKVPair("myalias", &masterKey)
		test.S(t).ExpectNotNil(kvPair)
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.StringCode())
	}
	{
		kvPair := getClusterMasterKVPair("", &masterKey)
		test.S(t).ExpectTrue(kvPair == nil)
	}
	{
		kvPair := getClusterMasterKVPair("myalias", nil)
		test.S(t).ExpectTrue(kvPair == nil)
	}
}

func TestGetClusterMasterKVPairs(t *testing.T) {
	kvPairs := GetClusterMasterKVPairs("myalias", &masterKey)
	test.S(t).ExpectTrue(len(kvPairs) >= 2)

	{
		kvPair := kvPairs[0]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.StringCode())
	}
	{
		kvPair := kvPairs[1]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias/hostname")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.Hostname)
	}
	{
		kvPair := kvPairs[2]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias/port")
		test.S(t).ExpectEquals(kvPair.Value, fmt.Sprintf("%d", masterKey.Port))
	}
}
<commit_msg>Added test for empty<commit_after>/*
   Copyright 2014 Outbrain Inc.

   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

       http://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 inst

import (
	"fmt"

	"github.com/github/orchestrator/go/config"
	"github.com/openark/golib/log"
	test "github.com/openark/golib/tests"
	"testing"
)

var masterKey = InstanceKey{Hostname: "host1", Port: 3306}

func init() {
	config.Config.HostnameResolveMethod = "none"
	config.Config.KVClusterMasterPrefix = "test/master/"
	config.MarkConfigurationLoaded()
	log.SetLevel(log.ERROR)
}

func TestGetClusterMasterKVKey(t *testing.T) {
	kvKey := GetClusterMasterKVKey("foo")
	test.S(t).ExpectEquals(kvKey, "test/master/foo")
}

func TestGetClusterMasterKVPair(t *testing.T) {
	{
		kvPair := getClusterMasterKVPair("myalias", &masterKey)
		test.S(t).ExpectNotNil(kvPair)
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.StringCode())
	}
	{
		kvPair := getClusterMasterKVPair("", &masterKey)
		test.S(t).ExpectTrue(kvPair == nil)
	}
	{
		kvPair := getClusterMasterKVPair("myalias", nil)
		test.S(t).ExpectTrue(kvPair == nil)
	}
}

func TestGetClusterMasterKVPairs(t *testing.T) {
	kvPairs := GetClusterMasterKVPairs("myalias", &masterKey)
	test.S(t).ExpectTrue(len(kvPairs) >= 2)

	{
		kvPair := kvPairs[0]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.StringCode())
	}
	{
		kvPair := kvPairs[1]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias/hostname")
		test.S(t).ExpectEquals(kvPair.Value, masterKey.Hostname)
	}
	{
		kvPair := kvPairs[2]
		test.S(t).ExpectEquals(kvPair.Key, "test/master/myalias/port")
		test.S(t).ExpectEquals(kvPair.Value, fmt.Sprintf("%d", masterKey.Port))
	}
}

func TestGetClusterMasterKVPairs2(t *testing.T) {
	kvPairs := GetClusterMasterKVPairs("", &masterKey)
	test.S(t).ExpectEquals(len(kvPairs), 0)
}
<|endoftext|>


<commit_before>package models

import (
	"fmt"
	"os"
)

type stackFrame struct {
	PC, SP uint64
}

type Stacktrace struct {
	Stack []stackFrame
}

func (s *Stacktrace) Len() int {
	return len(s.Stack)
}

func (s *Stacktrace) Print(u Usercorn) {
	pc, _ := u.RegRead(u.Arch().PC)
	sp, _ := u.RegRead(u.Arch().SP)
	stack := append(s.Stack, stackFrame{pc, sp})
	for i := len(stack) - 1; i >= 0; i-- {
		frame := stack[i]
		if i == len(stack)-1 && frame.PC == pc {
			continue
		}
		sym, _ := u.Symbolicate(frame.PC, true)
		fmt.Fprintf(os.Stderr, "  0x%x %s\n", frame.PC, sym)
	}
}

func (s *Stacktrace) Push(pc, sp uint64) {
	s.Stack = append(s.Stack, stackFrame{pc, sp})
}

func (s *Stacktrace) Empty() bool {
	return s.Len() == 0
}

func (s *Stacktrace) Peek() stackFrame {
	if s.Empty() {
		return stackFrame{}
	}
	return s.Stack[s.Len()-1]
}

func (s *Stacktrace) Pop() stackFrame {
	if s.Empty() {
		return stackFrame{}
	}
	ret := s.Peek()
	s.Stack = s.Stack[:s.Len()-1]
	return ret
}

func (s *Stacktrace) Update(pc, sp uint64) {
	if s.Empty() || sp < s.Peek().SP {
		s.Push(pc, sp)
	} else {
		for !s.Empty() && sp > s.Peek().SP {
			s.Pop()
		}
	}
}
<commit_msg>fix stacktrace call sites<commit_after>package models

import (
	"fmt"
	"os"
)

type stackFrame struct {
	PC, SP uint64
}

type Stacktrace struct {
	Stack []stackFrame
}

func (s *Stacktrace) Len() int {
	return len(s.Stack)
}

func (s *Stacktrace) Print(u Usercorn) {
	pc, _ := u.RegRead(u.Arch().PC)
	sp, _ := u.RegRead(u.Arch().SP)
	stack := append(s.Stack, stackFrame{pc, sp})
	for i := len(stack) - 1; i >= 0; i-- {
		frame := stack[i]
		if i == len(stack)-1 && frame.PC == pc {
			continue
		}
		sym, _ := u.Symbolicate(frame.PC, true)
		fmt.Fprintf(os.Stderr, "  0x%x %s\n", frame.PC, sym)
	}
}

func (s *Stacktrace) Push(pc, sp uint64) {
	s.Stack = append(s.Stack, stackFrame{pc, sp})
}

func (s *Stacktrace) Empty() bool {
	return s.Len() == 0
}

func (s *Stacktrace) Peek() stackFrame {
	if s.Empty() {
		return stackFrame{}
	}
	return s.Stack[s.Len()-1]
}

func (s *Stacktrace) Pop() stackFrame {
	if s.Empty() {
		return stackFrame{}
	}
	ret := s.Peek()
	s.Stack = s.Stack[:s.Len()-1]
	return ret
}

func (s *Stacktrace) Update(pc, sp uint64) {
	if s.Empty() || sp < s.Peek().SP {
		s.Push(pc, sp)
	} else {
		if sp == s.Peek().SP {
			s.Stack[s.Len()-1].PC = pc
		} else {
			for !s.Empty() && sp > s.Peek().SP {
				s.Pop()
			}
		}
	}
}
<|endoftext|>


<commit_before>package rankings

import (
	"errors"
	"fmt"
	"math"
	"sort"

	"github.com/ChrisHines/GoSkills/skills"
	"github.com/ChrisHines/GoSkills/skills/trueskill"
	"github.com/danmane/abalone/go/game"
)

type Rating struct {
	Mean   float64
	Stddev float64
}

type Ranking struct {
	PlayerID int64
	Rating   Rating
	Rank     int
}

type Rankings []Ranking

func (r Rankings) Len() int {
	return len(r)
}

func (r Rankings) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r Rankings) Less(i, j int) bool {
	if r[i].Rating.Mean == r[j].Rating.Mean {
		// if mean is the same, the better player (i.e. one with the lower rank) will be the one we are more confident about
		return r[i].Rating.Stddev < r[j].Rating.Stddev
	}
	// otherwise, the better player (i.e. one with lower rank) is the one with the higher mean
	return r[i].Rating.Mean > r[j].Rating.Mean
}

func DefaultRatings(players []int64) map[int64]Rating {
	out := make(map[int64]Rating)
	for _, p := range players {
		out[p] = Rating{Mean: gameInfo.InitialMean, Stddev: gameInfo.InitialStddev}
	}
	return out
}

type Result struct {
	WhiteID int64
	BlackID int64
	Outcome game.Outcome
}

func outcomeToRanks(o game.Outcome) ([]int, error) {
	var result []int
	switch o {
	case game.WhiteWins:
		result = []int{1, 2}
	case game.BlackWins:
		result = []int{2, 1}
	case game.Tie:
		result = []int{1, 1}
	default:
		return nil, fmt.Errorf("got null outcome or other invalid outcome: %s", o)
	}
	return result, nil
}

func rating2srating(r Rating) skills.Rating {
	return skills.NewRating(r.Mean, r.Stddev)
}

func srating2rating(s skills.Rating) Rating {
	return Rating{Mean: s.Mean(), Stddev: s.Stddev()}
}
func (r Rating) String() string {
	return fmt.Sprintf("{μ:%.6g σ:%.6g}", r.Mean, r.Stddev)
}

func RateGames(players []int64, games []Result) (Rankings, error) {

	// TODO(dm): this function is messy because the API we are depending on
	// (GoSkills) is pretty weird. Could be cleaned up by just moving the
	// calculation logic into our own impl

	if err := checkGameParticipantsArePlayers(players, games); err != nil {
		return nil, err
	}

	ratings := DefaultRatings(players)
	for _, r := range games {
		whiteTeam := skills.NewTeam()
		whiteTeam.AddPlayer(r.WhiteID, rating2srating(ratings[r.WhiteID]))
		blackTeam := skills.NewTeam()
		blackTeam.AddPlayer(r.BlackID, rating2srating(ratings[r.BlackID]))

		var twoPlayerCalc trueskill.TwoPlayerCalc
		ranks, err := outcomeToRanks(r.Outcome)
		if err != nil {
			return nil, err
		}
		newSkills := twoPlayerCalc.CalcNewRatings(gameInfo, []skills.Team{whiteTeam, blackTeam}, ranks...)
		ratings[r.WhiteID] = srating2rating(newSkills[r.WhiteID])
		ratings[r.BlackID] = srating2rating(newSkills[r.BlackID])
	}

	numPlayers := len(players)
	ranks := make(Rankings, numPlayers)
	for id, rating := range ratings {
		ranks[id] = Ranking{PlayerID: id, Rating: rating, Rank: -1}
	}
	sort.Sort(ranks)
	var prevRating Rating
	for i, _ := range ranks {
		if prevRating == ranks[i].Rating {
			ranks[i].Rank = ranks[i-1].Rank
		} else {
			ranks[i].Rank = i + 1
		}
		prevRating = ranks[i].Rating
	}
	return ranks, nil
}

// ProposeGame returns the IDs of two players who should play the next game.
func (rankings Rankings) ProposeGame() (int64, int64, error) {
	if len(rankings) == 0 {
		return 0, 0, errors.New("cannot propose games when there are no rankings")
	}
	maxUncertainty := math.Inf(-1)
	var id1 int64 = -1
	var idx1 int64 = -1
	for i, rank := range rankings {
		if rank.Rating.Stddev > maxUncertainty {
			maxUncertainty = rank.Rating.Stddev
			id1 = rank.PlayerID
			idx1 = int64(i)
		}
	}
	var id2 int64
	if idx1 == 0 {
		id2 = rankings[1].PlayerID
	} else {
		id2 = rankings[idx1-1].PlayerID
	}
	return id1, id2, nil
}

func checkGameParticipantsArePlayers(players []int64, games []Result) error {
	playerset := make(map[int64]struct{})
	for _, p := range players {
		playerset[p] = struct{}{}
	}
	for _, g := range games {
		ps := []int64{g.WhiteID, g.BlackID}
		for _, p := range ps {
			if _, ok := playerset[p]; !ok {
				return fmt.Errorf("player %d participated in game, but isn't in list of players", p)
			}
		}
	}
	return nil
}

var gameInfo = skills.DefaultGameInfo
<commit_msg>fix(rankings) rm array index op<commit_after>package rankings

import (
	"errors"
	"fmt"
	"math"
	"sort"

	"github.com/ChrisHines/GoSkills/skills"
	"github.com/ChrisHines/GoSkills/skills/trueskill"
	"github.com/danmane/abalone/go/game"
)

type Rating struct {
	Mean   float64
	Stddev float64
}

type Ranking struct {
	PlayerID int64
	Rating   Rating
	Rank     int
}

type Rankings []Ranking

func (r Rankings) Len() int {
	return len(r)
}

func (r Rankings) Swap(i, j int) {
	r[i], r[j] = r[j], r[i]
}

func (r Rankings) Less(i, j int) bool {
	if r[i].Rating.Mean == r[j].Rating.Mean {
		// if mean is the same, the better player (i.e. one with the lower rank) will be the one we are more confident about
		return r[i].Rating.Stddev < r[j].Rating.Stddev
	}
	// otherwise, the better player (i.e. one with lower rank) is the one with the higher mean
	return r[i].Rating.Mean > r[j].Rating.Mean
}

func DefaultRatings(players []int64) map[int64]Rating {
	out := make(map[int64]Rating)
	for _, p := range players {
		out[p] = Rating{Mean: gameInfo.InitialMean, Stddev: gameInfo.InitialStddev}
	}
	return out
}

type Result struct {
	WhiteID int64
	BlackID int64
	Outcome game.Outcome
}

func outcomeToRanks(o game.Outcome) ([]int, error) {
	var result []int
	switch o {
	case game.WhiteWins:
		result = []int{1, 2}
	case game.BlackWins:
		result = []int{2, 1}
	case game.Tie:
		result = []int{1, 1}
	default:
		return nil, fmt.Errorf("got null outcome or other invalid outcome: %s", o)
	}
	return result, nil
}

func rating2srating(r Rating) skills.Rating {
	return skills.NewRating(r.Mean, r.Stddev)
}

func srating2rating(s skills.Rating) Rating {
	return Rating{Mean: s.Mean(), Stddev: s.Stddev()}
}
func (r Rating) String() string {
	return fmt.Sprintf("{μ:%.6g σ:%.6g}", r.Mean, r.Stddev)
}

func RateGames(players []int64, games []Result) (Rankings, error) {

	// TODO(dm): this function is messy because the API we are depending on
	// (GoSkills) is pretty weird. Could be cleaned up by just moving the
	// calculation logic into our own impl

	if err := checkGameParticipantsArePlayers(players, games); err != nil {
		return nil, err
	}

	ratings := DefaultRatings(players)
	for _, r := range games {
		whiteTeam := skills.NewTeam()
		whiteTeam.AddPlayer(r.WhiteID, rating2srating(ratings[r.WhiteID]))
		blackTeam := skills.NewTeam()
		blackTeam.AddPlayer(r.BlackID, rating2srating(ratings[r.BlackID]))

		var twoPlayerCalc trueskill.TwoPlayerCalc
		ranks, err := outcomeToRanks(r.Outcome)
		if err != nil {
			return nil, err
		}
		newSkills := twoPlayerCalc.CalcNewRatings(gameInfo, []skills.Team{whiteTeam, blackTeam}, ranks...)
		ratings[r.WhiteID] = srating2rating(newSkills[r.WhiteID])
		ratings[r.BlackID] = srating2rating(newSkills[r.BlackID])
	}

	ranks := make(Rankings, 0)
	for id, rating := range ratings {
		ranks = append(ranks, Ranking{PlayerID: id, Rating: rating, Rank: -1})
	}
	sort.Sort(ranks)
	var prevRating Rating
	for i, _ := range ranks {
		if prevRating == ranks[i].Rating {
			ranks[i].Rank = ranks[i-1].Rank
		} else {
			ranks[i].Rank = i + 1
		}
		prevRating = ranks[i].Rating
	}
	return ranks, nil
}

// ProposeGame returns the IDs of two players who should play the next game.
func (rankings Rankings) ProposeGame() (int64, int64, error) {
	if len(rankings) == 0 {
		return 0, 0, errors.New("cannot propose games when there are no rankings")
	}
	maxUncertainty := math.Inf(-1)
	var id1 int64 = -1
	var idx1 int64 = -1
	for i, rank := range rankings {
		if rank.Rating.Stddev > maxUncertainty {
			maxUncertainty = rank.Rating.Stddev
			id1 = rank.PlayerID
			idx1 = int64(i)
		}
	}
	var id2 int64
	if idx1 == 0 {
		id2 = rankings[1].PlayerID
	} else {
		id2 = rankings[idx1-1].PlayerID
	}
	return id1, id2, nil
}

func checkGameParticipantsArePlayers(players []int64, games []Result) error {
	playerset := make(map[int64]struct{})
	for _, p := range players {
		playerset[p] = struct{}{}
	}
	for _, g := range games {
		ps := []int64{g.WhiteID, g.BlackID}
		for _, p := range ps {
			if _, ok := playerset[p]; !ok {
				return fmt.Errorf("player %d participated in game, but isn't in list of players", p)
			}
		}
	}
	return nil
}

var gameInfo = skills.DefaultGameInfo
<|endoftext|>


<commit_before>package storage

import (
	"fmt"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// Storage stores TeamData's on memory and disk. Threadsafe. For the standard loader,
// which does a complete but slower load. Compare to FTLStorage which stores the
// Fast-loaded team state.
type Storage struct {
	*storageGeneric
}

// Increment to invalidate the disk cache.
const diskStorageVersion = 10
const memCacheLRUSize = 200

type DiskStorageItem struct {
	Version int                `codec:"V"`
	State   *keybase1.TeamData `codec:"S"`
}

var _ teamDataGeneric = (*keybase1.TeamData)(nil)
var _ diskItemGeneric = (*DiskStorageItem)(nil)

func (d *DiskStorageItem) version() int {
	return d.Version
}
func (d *DiskStorageItem) value() teamDataGeneric {
	return d.State
}
func (d *DiskStorageItem) setVersion(i int) {
	d.Version = i
}
func (d *DiskStorageItem) setValue(v teamDataGeneric) error {
	typed, ok := v.(*keybase1.TeamData)
	if !ok {
		return fmt.Errorf("teams.Storage#Put: Bad object for setValue; got type %T", v)
	}
	d.State = typed
	return nil
}

func NewStorage(g *libkb.GlobalContext) *Storage {
	s := newStorageGeneric(g, memCacheLRUSize, diskStorageVersion, libkb.DBSlowTeamsAlias, libkb.EncryptionReasonTeamsLocalStorage, "slow", func() diskItemGeneric { return &DiskStorageItem{} })
	return &Storage{s}
}

func (s *Storage) Put(mctx libkb.MetaContext, state *keybase1.TeamData) {
	s.storageGeneric.put(mctx, state)
}

// Can return nil.
func (s *Storage) Get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) (data *keybase1.TeamData, frozen bool, tombstoned bool) {
	vp := s.storageGeneric.get(mctx, teamID, public)
	if vp == nil {
		return nil, false, false
	}
	ret, ok := vp.(*keybase1.TeamData)
	if !ok {
		mctx.Debug("teams.Storage#Get cast error: %T is wrong type", vp)
	}
	if ret != nil && diskStorageVersion == 10 && ret.Subversion == 0 {
		migrateInvites(mctx, ret.ID(), ret.Chain.ActiveInvites)
		migrateInvites(mctx, ret.ID(), ret.Chain.ObsoleteInvites)
		ret.Subversion = 1
	}
	if ret.Frozen {
		mctx.Debug("returning frozen team data")
	}
	if ret.Tombstoned {
		mctx.Debug("returning tombstoned team data")
	}
	return ret, ret.Frozen, ret.Tombstoned
}

// migrateInvites converts old 'category unknown' invites into social invites for paramproofs.
func migrateInvites(mctx libkb.MetaContext, teamID keybase1.TeamID, invites map[keybase1.TeamInviteID]keybase1.TeamInvite) {
	for key, invite := range invites {
		category, err := invite.Type.C()
		if err != nil {
			continue
		}
		if category != keybase1.TeamInviteCategory_UNKNOWN {
			continue
		}
		categoryStr := invite.Type.Unknown()
		if mctx.G().GetProofServices().GetServiceType(mctx.Ctx(), categoryStr) == nil {
			continue
		}
		mctx.Debug("migrateInvites repairing teamID:%v inviteID:%v cat:%v", teamID, invite.Id, categoryStr)
		invite.Type = keybase1.NewTeamInviteTypeWithSbs(keybase1.TeamInviteSocialNetwork(categoryStr))
		invites[key] = invite
	}
}
<commit_msg>teams: backfill MaxPerTeamKeyGeneration; upgrade to 10.2 storage<commit_after>package storage

import (
	"fmt"

	"github.com/keybase/client/go/libkb"
	"github.com/keybase/client/go/protocol/keybase1"
)

// Storage stores TeamData's on memory and disk. Threadsafe. For the standard loader,
// which does a complete but slower load. Compare to FTLStorage which stores the
// Fast-loaded team state.
type Storage struct {
	*storageGeneric
}

// Increment to invalidate the disk cache.
const diskStorageVersion = 10
const memCacheLRUSize = 200

type DiskStorageItem struct {
	Version int                `codec:"V"`
	State   *keybase1.TeamData `codec:"S"`
}

var _ teamDataGeneric = (*keybase1.TeamData)(nil)
var _ diskItemGeneric = (*DiskStorageItem)(nil)

func (d *DiskStorageItem) version() int {
	return d.Version
}
func (d *DiskStorageItem) value() teamDataGeneric {
	return d.State
}
func (d *DiskStorageItem) setVersion(i int) {
	d.Version = i
}
func (d *DiskStorageItem) setValue(v teamDataGeneric) error {
	typed, ok := v.(*keybase1.TeamData)
	if !ok {
		return fmt.Errorf("teams.Storage#Put: Bad object for setValue; got type %T", v)
	}
	d.State = typed
	return nil
}

func NewStorage(g *libkb.GlobalContext) *Storage {
	s := newStorageGeneric(g, memCacheLRUSize, diskStorageVersion, libkb.DBSlowTeamsAlias, libkb.EncryptionReasonTeamsLocalStorage, "slow", func() diskItemGeneric { return &DiskStorageItem{} })
	return &Storage{s}
}

func (s *Storage) Put(mctx libkb.MetaContext, state *keybase1.TeamData) {
	s.storageGeneric.put(mctx, state)
}

// Can return nil.
func (s *Storage) Get(mctx libkb.MetaContext, teamID keybase1.TeamID, public bool) (data *keybase1.TeamData, frozen bool, tombstoned bool) {
	vp := s.storageGeneric.get(mctx, teamID, public)
	if vp == nil {
		return nil, false, false
	}
	ret, ok := vp.(*keybase1.TeamData)
	if !ok {
		mctx.Debug("teams.Storage#Get cast error: %T is wrong type", vp)
	}

	if ret != nil && diskStorageVersion == 10 && ret.Subversion == 0 {
		migrateInvites(mctx, ret.ID(), ret.Chain.ActiveInvites)
		migrateInvites(mctx, ret.ID(), ret.Chain.ObsoleteInvites)
		ret.Subversion = 1
	}

	if ret != nil && diskStorageVersion == 10 && ret.Subversion == 1 {
		// 2019-06: We added MaxPerTeamKeyGeneration and can hot-patch existing structures
		// pretty easily, so do that rather than blasting away the cache.
		for k := range ret.Chain.PerTeamKeys {
			if k > ret.Chain.MaxPerTeamKeyGeneration {
				ret.Chain.MaxPerTeamKeyGeneration = k
			}
		}
		mctx.Debug("Upgraded MaxPerTeamKeyGeneration to %d in version 10.2", ret.Chain.MaxPerTeamKeyGeneration)
		ret.Subversion = 2
	}

	if ret.Frozen {
		mctx.Debug("returning frozen team data")
	}
	if ret.Tombstoned {
		mctx.Debug("returning tombstoned team data")
	}
	return ret, ret.Frozen, ret.Tombstoned
}

// migrateInvites converts old 'category unknown' invites into social invites for paramproofs.
func migrateInvites(mctx libkb.MetaContext, teamID keybase1.TeamID, invites map[keybase1.TeamInviteID]keybase1.TeamInvite) {
	for key, invite := range invites {
		category, err := invite.Type.C()
		if err != nil {
			continue
		}
		if category != keybase1.TeamInviteCategory_UNKNOWN {
			continue
		}
		categoryStr := invite.Type.Unknown()
		if mctx.G().GetProofServices().GetServiceType(mctx.Ctx(), categoryStr) == nil {
			continue
		}
		mctx.Debug("migrateInvites repairing teamID:%v inviteID:%v cat:%v", teamID, invite.Id, categoryStr)
		invite.Type = keybase1.NewTeamInviteTypeWithSbs(keybase1.TeamInviteSocialNetwork(categoryStr))
		invites[key] = invite
	}
}
<|endoftext|>


<commit_before>package di

import (
	"fmt"
	"reflect"
)

type State uint8

const (
	Stateless State = iota
	Singleton
)

type (
	targetStructField struct {
		Object     *BindObject
		FieldIndex []int
	}

	StructInjector struct {
		initRef        reflect.Value
		initRefAsSlice []reflect.Value // useful when the struct is passed on a func as input args via reflection.
		elemType       reflect.Type
		//
		fields []*targetStructField
		// is true when contains bindable fields and it's a valid target struct,
		// it maybe 0 but struct may contain unexported fields or exported but no bindable (Stateless)
		// see `setState`.
		HasFields bool
		CanInject bool // if any bindable fields when the state is NOT singleton.
		State     State
	}
)

func (s *StructInjector) countBindType(typ BindType) (n int) {
	for _, f := range s.fields {
		if f.Object.BindType == typ {
			n++
		}
	}
	return
}

func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *StructInjector {
	s := &StructInjector{
		initRef:        v,
		initRefAsSlice: []reflect.Value{v},
		elemType:       IndirectType(v.Type()),
	}

	fields := lookupFields(s.elemType, true, nil)
	for _, f := range fields {
		if hijack != nil {
			if b, ok := hijack(f.Type); ok && b != nil {
				s.fields = append(s.fields, &targetStructField{
					FieldIndex: f.Index,
					Object:     b,
				})

				continue
			}
		}

		for _, val := range values {
			// the binded values to the struct's fields.
			b, err := MakeBindObject(val, goodFunc)

			if err != nil {
				return s // if error stop here.
			}

			if b.IsAssignable(f.Type) {
				// fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String())
				s.fields = append(s.fields, &targetStructField{
					FieldIndex: f.Index,
					Object:     &b,
				})
				break
			}
		}
	}

	s.HasFields = len(s.fields) > 0
	// set the overall state of this injector.
	s.setState()
	s.fillStruct()

	return s
}

// set the state, once.
func (s *StructInjector) setState() {
	// note for zero length of struct's fields:
	// if struct doesn't contain any field
	// so both of the below variables will be 0,
	// so it's a singleton.
	// At the other hand the `s.HasFields` maybe false
	// but the struct may contain UNEXPORTED fields or non-bindable fields (request-scoped on both cases)
	// so a new controller/struct at the caller side should be initialized on each request,
	// we should not depend on the `HasFields` for singleton or no, this is the reason I
	// added the `.State` now.

	staticBindingsFieldsLength := s.countBindType(Static)
	structFieldsLength := NumFields(s.elemType, false)

	// println("staticBindingsFieldsLength: ", staticBindingsFieldsLength)
	// println("structFieldsLength: ", structFieldsLength)

	// if the number of static values binded is equal to the
	// total struct's fields(including unexported fields this time) then set as singleton.
	if staticBindingsFieldsLength == structFieldsLength {
		s.State = Singleton
		return
	}

	s.CanInject = s.State == Stateless && s.HasFields
	// the default is `Stateless`, which means that a new instance should be created
	//  on each inject action by the caller.
}

// fill the static bindings values once.
func (s *StructInjector) fillStruct() {
	if !s.HasFields {
		return
	}
	// if field is Static then set it to the value that passed by the caller,
	// so will have the static bindings already and we can just use that value instead
	// of creating new instance.
	destElem := IndirectValue(s.initRef)
	for _, f := range s.fields {
		// if field is Static then set it to the value that passed by the caller,
		// so will have the static bindings already and we can just use that value instead
		// of creating new instance.
		if s.State == Singleton && f.Object.BindType == Static {
			destElem.FieldByIndex(f.FieldIndex).Set(f.Object.Value)
		}
	}
}

func (s *StructInjector) String() (trace string) {
	for i, f := range s.fields {
		elemField := s.elemType.FieldByIndex(f.FieldIndex)
		trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n",
			i+1, bindTypeString(f.Object.BindType), f.Object.Type.String(),
			elemField.Name, elemField.Type.String())
	}

	return
}

func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
	if dest == nil {
		return
	}

	v := IndirectValue(ValueOf(dest))
	s.InjectElem(v, ctx...)
}

func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
	for _, f := range s.fields {
		f.Object.Assign(ctx, func(v reflect.Value) {
			destElem.FieldByIndex(f.FieldIndex).Set(v)
		})
	}
}

func (s *StructInjector) New() reflect.Value {
	if s.State == Singleton {
		return s.initRef
	}
	return reflect.New(s.elemType)
}

func (s *StructInjector) NewAsSlice() []reflect.Value {
	if s.State == Singleton {
		return s.initRefAsSlice
	}
	return []reflect.Value{reflect.New(s.elemType)}
}
<commit_msg>controller's struct: if unexported field(s) is  set-ed manually (binder cannot do that) and no other dynamic dependency(depends on context) then tag the controller as singleton, use a single controller instance to serve all clients (the controller's methods are per-request of course but its(controller's) fields will be 'global'- users should be careful because parallel access of those fields are  up to them by using mutex or atomic or values that are safe for concurrent access.<commit_after>package di

import (
	"fmt"
	"reflect"
)

type State uint8

const (
	Stateless State = iota
	Singleton
)

type (
	targetStructField struct {
		Object     *BindObject
		FieldIndex []int
	}

	StructInjector struct {
		initRef        reflect.Value
		initRefAsSlice []reflect.Value // useful when the struct is passed on a func as input args via reflection.
		elemType       reflect.Type
		//
		fields []*targetStructField
		// is true when contains bindable fields and it's a valid target struct,
		// it maybe 0 but struct may contain unexported fields or exported but no bindable (Stateless)
		// see `setState`.
		HasFields bool
		CanInject bool // if any bindable fields when the state is NOT singleton.
		State     State
	}
)

func (s *StructInjector) countBindType(typ BindType) (n int) {
	for _, f := range s.fields {
		if f.Object.BindType == typ {
			n++
		}
	}
	return
}

func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *StructInjector {
	s := &StructInjector{
		initRef:        v,
		initRefAsSlice: []reflect.Value{v},
		elemType:       IndirectType(v.Type()),
	}

	fields := lookupFields(s.elemType, true, nil)
	for _, f := range fields {
		if hijack != nil {
			if b, ok := hijack(f.Type); ok && b != nil {
				s.fields = append(s.fields, &targetStructField{
					FieldIndex: f.Index,
					Object:     b,
				})

				continue
			}
		}

		for _, val := range values {
			// the binded values to the struct's fields.
			b, err := MakeBindObject(val, goodFunc)

			if err != nil {
				return s // if error stop here.
			}

			if b.IsAssignable(f.Type) {
				// fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String())
				s.fields = append(s.fields, &targetStructField{
					FieldIndex: f.Index,
					Object:     &b,
				})
				break
			}
		}
	}

	s.HasFields = len(s.fields) > 0
	// set the overall state of this injector.
	s.fillStruct()
	s.setState()

	return s
}

// set the state, once.
// Here the "initRef" have already the static bindings and the manually-filled fields.
func (s *StructInjector) setState() {
	// note for zero length of struct's fields:
	// if struct doesn't contain any field
	// so both of the below variables will be 0,
	// so it's a singleton.
	// At the other hand the `s.HasFields` maybe false
	// but the struct may contain UNEXPORTED fields or non-bindable fields (request-scoped on both cases)
	// so a new controller/struct at the caller side should be initialized on each request,
	// we should not depend on the `HasFields` for singleton or no, this is the reason I
	// added the `.State` now.

	staticBindingsFieldsLength := s.countBindType(Static)
	allStructFieldsLength := NumFields(s.elemType, false)
	// check if unexported(and exported) fields are set-ed manually or via binding (at this time we have all fields set-ed inside the "initRef")
	// i.e &Controller{unexportedField: "my value"}
	// or dependencies values = "my value" and Controller struct {Field string}
	// if so then set the temp staticBindingsFieldsLength to that number, so for example:
	// if static binding length is 0
	// but an unexported field is set-ed then act that as singleton.
	if allStructFieldsLength > staticBindingsFieldsLength {
		structFieldsUnexportedNonZero := LookupNonZeroFieldsValues(s.initRef, false)
		staticBindingsFieldsLength = len(structFieldsUnexportedNonZero)
	}

	// println("staticBindingsFieldsLength: ", staticBindingsFieldsLength)
	// println("allStructFieldsLength: ", allStructFieldsLength)

	// if the number of static values binded is equal to the
	// total struct's fields(including unexported fields this time) then set as singleton.
	if staticBindingsFieldsLength == allStructFieldsLength {
		s.State = Singleton
		// the default is `Stateless`, which means that a new instance should be created
		//  on each inject action by the caller.
		return
	}

	s.CanInject = s.State == Stateless && s.HasFields
}

// fill the static bindings values once.
func (s *StructInjector) fillStruct() {
	if !s.HasFields {
		return
	}
	// if field is Static then set it to the value that passed by the caller,
	// so will have the static bindings already and we can just use that value instead
	// of creating new instance.
	destElem := IndirectValue(s.initRef)
	for _, f := range s.fields {
		// if field is Static then set it to the value that passed by the caller,
		// so will have the static bindings already and we can just use that value instead
		// of creating new instance.
		if s.State == Singleton && f.Object.BindType == Static {
			destElem.FieldByIndex(f.FieldIndex).Set(f.Object.Value)
		}
	}
}

func (s *StructInjector) String() (trace string) {
	for i, f := range s.fields {
		elemField := s.elemType.FieldByIndex(f.FieldIndex)
		trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n",
			i+1, bindTypeString(f.Object.BindType), f.Object.Type.String(),
			elemField.Name, elemField.Type.String())
	}

	return
}

func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
	if dest == nil {
		return
	}

	v := IndirectValue(ValueOf(dest))
	s.InjectElem(v, ctx...)
}

func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
	for _, f := range s.fields {
		f.Object.Assign(ctx, func(v reflect.Value) {
			destElem.FieldByIndex(f.FieldIndex).Set(v)
		})
	}
}

func (s *StructInjector) New() reflect.Value {
	if s.State == Singleton {
		return s.initRef
	}
	return reflect.New(s.elemType)
}

func (s *StructInjector) NewAsSlice() []reflect.Value {
	if s.State == Singleton {
		return s.initRefAsSlice
	}
	return []reflect.Value{reflect.New(s.elemType)}
}
<|endoftext|>