<commit_before>// Copyright 2016 AlexStocks(https://github.com/AlexStocks).
// All rights reserved.  Use of m source code is
// governed by a BSD-style license.
//
// 2016-09-11 19:30
// Package gxdriver provides a MySQL driver for Go's database/sql package
// code example: https://github.com/alexstocks/go-practice/blob/master/mysql/stmt.go
package gxdriver

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

import (
	"github.com/AlexStocks/goext/log"
	"github.com/AlexStocks/goext/strings"
	mysql "github.com/go-sql-driver/mysql"
)

const (
	GettyMySQLDriver = "getty_mysql_driver"
)

func init() {
	sql.Register(GettyMySQLDriver, &MySQLDriver{})
	dbConnMap = make(map[string]*sql.DB)
}

///////////////////////////////////////
// Mysql Driver
///////////////////////////////////////

// refs: https://zhuanlan.zhihu.com/p/24768377
// author: https://github.com/idada
// 重写driver，方便重用prepare statement.
// !: statement用到的connection和transaction begin的connection不是同一个connection
type mysqlStmt struct {
	driver.Stmt
	conn  *mySQLConn
	query string
	ref   int32
}

// 程序退出的时候调用之
func (stmt *mysqlStmt) Close() error {
	gxlog.CInfo("Close()")
	stmt.conn.stmtMutex.Lock()
	defer stmt.conn.stmtMutex.Unlock()

	if atomic.AddInt32(&stmt.ref, -1) == 0 {
		gxlog.CInfo("really close")
		delete(stmt.conn.stmtCache, stmt.query)
		return stmt.Stmt.Close()
	}
	return nil
}

type MySQLDriver struct {
}

func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
	gxlog.CInfo("GettyMSDriver:Open(%s)", dsn)
	var driver mysql.MySQLDriver
	conn, err := driver.Open(dsn)
	if err != nil {
		return nil, err
	}

	return &mySQLConn{Conn: conn, stmtCache: make(map[string]*mysqlStmt)}, nil
}

type mySQLConn struct {
	driver.Conn
	stmtMutex sync.RWMutex
	// 缓存prepared stmt链接，在程序启动的时候全都创建好
	stmtCache map[string]*mysqlStmt
}

// 这个函数应该在程序启动的时候被调用以创建全局prepared stmt句柄，在程序退出的时候调用(stmt *mysqlStmt) Close()
func (m *mySQLConn) Prepare(query string) (driver.Stmt, error) {
	gxlog.CInfo("GettyMSDriver:Prepare(%s)", query)
	m.stmtMutex.RLock()
	if stmt, exists := m.stmtCache[query]; exists {
		// must update reference counter in lock scope
		atomic.AddInt32(&stmt.ref, 1)
		m.stmtMutex.RUnlock()
		return stmt, nil
	}
	m.stmtMutex.RUnlock()

	m.stmtMutex.Lock()
	defer m.stmtMutex.Unlock()

	// double check
	if stmt, exists := m.stmtCache[query]; exists {
		atomic.AddInt32(&stmt.ref, 1)
		return stmt, nil
	}

	stmt, err := m.Conn.Prepare(query)
	if err != nil {
		return nil, err
	}

	stmt2 := &mysqlStmt{stmt, m, query, 1}
	m.stmtCache[query] = stmt2
	return stmt2, nil
}

func (m *mySQLConn) Begin() (driver.Tx, error) {
	gxlog.CInfo("GettyMSDriver:Begin")
	tx, err := m.Conn.Begin()
	if err != nil {
		return nil, err
	}
	return &mysqlTx{m, tx}, nil
}

type mysqlTx struct {
	*mySQLConn
	tx driver.Tx
}

func (tx *mysqlTx) Commit() (err error) {
	gxlog.CInfo("GettyMSDriver:Commit")
	return tx.tx.Commit()
}

func (tx *mysqlTx) Rollback() (err error) {
	return tx.tx.Rollback()
}

///////////////////////////////////////
// Mysql Instance
///////////////////////////////////////

var (
	dbConnMapLock sync.Mutex
	dbConnMap     map[string]*sql.DB
)

/*
feature list:
1 读写分离
2 长连接有效性检测和自动重连
3 mysql prepared statement 重用
! Note: 在执行sql操作之前请执行CheckSQl & CheckAcitve
*/
type MySQL struct {
	*sql.DB             //数据库操作
	tx          *sql.Tx //带事务的数据库操作
	txIndex     int     //事务记数器，只有当txIndex=0才会触发Begin动作，只有当txIndex=1才会触发Commit动作
	role        string  //操作类型，分Master或Slave
	schema      string  //数据库连接schema
	active      int64   //上次建立连接的时间点，需要一种机制来检测客户端与mysql服务端连接的有效性
	waitTimeout int64   //mysql服务器空闲等待时长
	sync.Once
}

//创建一个默认的mysql操作实例
func Open(schema string) *MySQL {
	return newMySQLInstance(schema, "Master")
}

//创建一个默认的mysql查询实例
func OpenQuery(schema string) *MySQL {
	return newMySQLInstance(schema, "Slave")
}

func (m *MySQL) Close() {
	m.Do(func() {
		if m.tx != nil {
			m.tx.Commit()
		}
		m.tx = nil
		m.DB.Close()
		var key string = m.schema + m.role
		dbConnMapLock.Lock()
		db, ok := dbConnMap[key]
		if ok && db == m.DB {
			delete(dbConnMap, key)
		}
		dbConnMapLock.Unlock()
		m.DB = nil
	})
}

//@param string schema
//@param string role [Master, Slave]
func newMySQLInstance(schema string, role string) *MySQL {
	if !gxstrings.Contains([]string{"Master", "Slave"}, role) {
		panic("function common.NewSqlInstance's second argument must be 'Master' or 'Slave'.")
	}

	var key string = schema + role
	dbConnMapLock.Lock()
	conn, ok := dbConnMap[key]
	dbConnMapLock.Unlock()
	if !ok {
		//建立一个新连接到mysql
		connect(schema, role)
		dbConnMapLock.Lock()
		conn = dbConnMap[key]
		dbConnMapLock.Unlock()
	}
	return &MySQL{
		DB:     conn,
		schema: schema,
		role:   role,
		active: time.Now().Unix(),
	}
}

//建立数据库连接
//@param string schema 连接DB方案
//@param string role 连接类型，是分Master和Slave类型
func connect(schema string, role string) {
	var key = schema + role
	//开始连接DB
	conn, err := sql.Open(GettyMySQLDriver, schema)
	if err != nil {
		panic(err)
	}

	//将DB连接放入一个全局变量中
	dbConnMapLock.Lock()
	dbConnMap[key] = conn
	dbConnMapLock.Unlock()
}

//根据db.Query的查询结果，组装成一个关联key的数据集，数据类型[]map[string]string
func (m *MySQL) FetchRows(rows *sql.Rows) ([]map[string]string, error) {
	result := make([]map[string]string, 0)
	columns, err := rows.Columns()
	if err != nil {
		//an error occurred
		return nil, err
	}

	rawBytes := make([]sql.RawBytes, len(columns))

	//rows.Scan wants '[]interface{}' as an argument, so we must copy
	//the references into such a slice
	scanArgs := make([]interface{}, len(columns))

	for i := range rawBytes {
		scanArgs[i] = &rawBytes[i]
	}

	for rows.Next() {
		err := rows.Scan(scanArgs...)
		if err != nil {
			return nil, err
		}
		var val string
		item := make(map[string]string)
		for i, col := range rawBytes {
			if col == nil {
				val = ""
			} else {
				val = string(col)
			}
			item[columns[i]] = val
		}
		result = append(result, item)
	}
	return result, nil
}

//检查MySQL实例的连接是否还在活跃时间范围内
//! note: 这个函数的实际意义在于更新active时间，实际的sql.DB下面有一个连接池，
// 完全没必要因为超时就更换conn对象。
func (m *MySQL) CheckActive() {
	var now int64 = time.Now().Unix()
	if m.tx != nil {
		//如果存在事务会话，则不再进行连接检查
		m.active = now
		return
	}

	//从MySQL的wait_timeout变量中定位waitTimeout
	if m.waitTimeout == 0 {
		rows, err := m.Query("SHOW VARIABLES LIKE 'wait_timeout'")
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		result, err := m.FetchRows(rows)
		if err != nil {
			panic(err)
		}
		if result != nil && len(result) != 0 {
			timeout, err := strconv.Atoi(result[0]["Value"])
			if err != nil {
				panic(err)
			}
			m.waitTimeout = int64(timeout)
		}
	}

	if now-m.active > m.waitTimeout-2 {
		//此时认为数据库连接已经超时了，重新进行一次连接
		connect(m.schema, m.role)
		var key string = m.schema + m.role
		dbConnMapLock.Lock()
		m.DB = dbConnMap[key]
		dbConnMapLock.Unlock()
	}

	//设置当前时间为最新活跃点
	m.active = now
}

//保证修改、写入类的操作不在slave上执行
func (m *MySQL) CheckSQL(sql string) error {
	if m.role == "Slave" {
		sql = strings.TrimSpace(sql)
		exp := regexp.MustCompile(`^(?i:insert|update|delete|alter|truncate|drop)`)
		if exp.MatchString(sql) {
			return fmt.Errorf("insert|update|delete|alter|truncate|drop operation is not allowed on slave.")
		}
	}

	return nil
}

//开始一个事务，开始一个事务和提交、回滚事务必须一一对应
func (m *MySQL) Begin() error {
	if m.txIndex == 0 {
		var err error
		m.tx, err = m.DB.Begin()
		if err != nil {
			return err
		}
	}
	m.txIndex++

	return nil
}

//提交一个事务
func (m *MySQL) Commit() error {
	var err error
	if m.txIndex == 1 {
		err = m.tx.Commit()
		m.tx = nil
	}
	m.txIndex--

	return err
}

//事务回滚
func (m *MySQL) Rollback() error {
	err := m.tx.Rollback()
	m.txIndex = 0
	m.tx = nil

	return err
}
<commit_msg>remark gxlog<commit_after>// Copyright 2016 AlexStocks(https://github.com/AlexStocks).
// All rights reserved.  Use of m source code is
// governed by a BSD-style license.
//
// 2016-09-11 19:30
// Package gxdriver provides a MySQL driver for Go's database/sql package
// code example: https://github.com/alexstocks/go-practice/blob/master/mysql/stmt.go
package gxdriver

import (
	"database/sql"
	"database/sql/driver"
	"fmt"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"sync/atomic"
	"time"
)

import (
	// "github.com/AlexStocks/goext/log"
	"github.com/AlexStocks/goext/strings"
	"github.com/go-sql-driver/mysql"
)

const (
	GettyMySQLDriver = "getty_mysql_driver"
)

func init() {
	sql.Register(GettyMySQLDriver, &MySQLDriver{})
	dbConnMap = make(map[string]*sql.DB)
}

///////////////////////////////////////
// Mysql Driver
///////////////////////////////////////

// refs: https://zhuanlan.zhihu.com/p/24768377
// author: https://github.com/idada
// 重写driver，方便重用prepare statement.
// !: statement用到的connection和transaction begin的connection不是同一个connection
type mysqlStmt struct {
	driver.Stmt
	conn  *mySQLConn
	query string
	ref   int32
}

// 程序退出的时候调用之
func (stmt *mysqlStmt) Close() error {
	// gxlog.CInfo("Close()")
	stmt.conn.stmtMutex.Lock()
	defer stmt.conn.stmtMutex.Unlock()

	if atomic.AddInt32(&stmt.ref, -1) == 0 {
		// gxlog.CInfo("really close")
		delete(stmt.conn.stmtCache, stmt.query)
		return stmt.Stmt.Close()
	}
	return nil
}

type MySQLDriver struct {
}

func (d MySQLDriver) Open(dsn string) (driver.Conn, error) {
	// gxlog.CInfo("GettyMSDriver:Open(%s)", dsn)
	var driver mysql.MySQLDriver
	conn, err := driver.Open(dsn)
	if err != nil {
		return nil, err
	}

	return &mySQLConn{Conn: conn, stmtCache: make(map[string]*mysqlStmt)}, nil
}

type mySQLConn struct {
	driver.Conn
	stmtMutex sync.RWMutex
	// 缓存prepared stmt链接，在程序启动的时候全都创建好
	stmtCache map[string]*mysqlStmt
}

// 这个函数应该在程序启动的时候被调用以创建全局prepared stmt句柄，在程序退出的时候调用(stmt *mysqlStmt) Close()
func (m *mySQLConn) Prepare(query string) (driver.Stmt, error) {
	// gxlog.CInfo("GettyMSDriver:Prepare(%s)", query)
	m.stmtMutex.RLock()
	if stmt, exists := m.stmtCache[query]; exists {
		// must update reference counter in lock scope
		atomic.AddInt32(&stmt.ref, 1)
		m.stmtMutex.RUnlock()
		return stmt, nil
	}
	m.stmtMutex.RUnlock()

	m.stmtMutex.Lock()
	defer m.stmtMutex.Unlock()

	// double check
	if stmt, exists := m.stmtCache[query]; exists {
		atomic.AddInt32(&stmt.ref, 1)
		return stmt, nil
	}

	stmt, err := m.Conn.Prepare(query)
	if err != nil {
		return nil, err
	}

	stmt2 := &mysqlStmt{stmt, m, query, 1}
	m.stmtCache[query] = stmt2
	return stmt2, nil
}

func (m *mySQLConn) Begin() (driver.Tx, error) {
	// gxlog.CInfo("GettyMSDriver:Begin")
	tx, err := m.Conn.Begin()
	if err != nil {
		return nil, err
	}
	return &mysqlTx{m, tx}, nil
}

type mysqlTx struct {
	*mySQLConn
	tx driver.Tx
}

func (tx *mysqlTx) Commit() (err error) {
	// gxlog.CInfo("GettyMSDriver:Commit")
	return tx.tx.Commit()
}

func (tx *mysqlTx) Rollback() (err error) {
	return tx.tx.Rollback()
}

///////////////////////////////////////
// Mysql Instance
///////////////////////////////////////

var (
	dbConnMapLock sync.Mutex
	dbConnMap     map[string]*sql.DB
)

/*
feature list:
1 读写分离
2 长连接有效性检测和自动重连
3 mysql prepared statement 重用
! Note: 在执行sql操作之前请执行CheckSQl & CheckAcitve
*/
type MySQL struct {
	*sql.DB             //数据库操作
	tx          *sql.Tx //带事务的数据库操作
	txIndex     int     //事务记数器，只有当txIndex=0才会触发Begin动作，只有当txIndex=1才会触发Commit动作
	role        string  //操作类型，分Master或Slave
	schema      string  //数据库连接schema
	active      int64   //上次建立连接的时间点，需要一种机制来检测客户端与mysql服务端连接的有效性
	waitTimeout int64   //mysql服务器空闲等待时长
	sync.Once
}

//创建一个默认的mysql操作实例
func Open(schema string) *MySQL {
	return newMySQLInstance(schema, "Master")
}

//创建一个默认的mysql查询实例
func OpenQuery(schema string) *MySQL {
	return newMySQLInstance(schema, "Slave")
}

func (m *MySQL) Close() {
	m.Do(func() {
		if m.tx != nil {
			m.tx.Commit()
		}
		m.tx = nil
		m.DB.Close()
		var key string = m.schema + m.role
		dbConnMapLock.Lock()
		db, ok := dbConnMap[key]
		if ok && db == m.DB {
			delete(dbConnMap, key)
		}
		dbConnMapLock.Unlock()
		m.DB = nil
	})
}

//@param string schema
//@param string role [Master, Slave]
func newMySQLInstance(schema string, role string) *MySQL {
	if !gxstrings.Contains([]string{"Master", "Slave"}, role) {
		panic("function common.NewSqlInstance's second argument must be 'Master' or 'Slave'.")
	}

	var key string = schema + role
	dbConnMapLock.Lock()
	conn, ok := dbConnMap[key]
	dbConnMapLock.Unlock()
	if !ok {
		//建立一个新连接到mysql
		connect(schema, role)
		dbConnMapLock.Lock()
		conn = dbConnMap[key]
		dbConnMapLock.Unlock()
	}
	return &MySQL{
		DB:     conn,
		schema: schema,
		role:   role,
		active: time.Now().Unix(),
	}
}

//建立数据库连接
//@param string schema 连接DB方案
//@param string role 连接类型，是分Master和Slave类型
func connect(schema string, role string) {
	var key = schema + role
	//开始连接DB
	conn, err := sql.Open(GettyMySQLDriver, schema)
	if err != nil {
		panic(err)
	}

	//将DB连接放入一个全局变量中
	dbConnMapLock.Lock()
	dbConnMap[key] = conn
	dbConnMapLock.Unlock()
}

//根据db.Query的查询结果，组装成一个关联key的数据集，数据类型[]map[string]string
func (m *MySQL) FetchRows(rows *sql.Rows) ([]map[string]string, error) {
	result := make([]map[string]string, 0)
	columns, err := rows.Columns()
	if err != nil {
		//an error occurred
		return nil, err
	}

	rawBytes := make([]sql.RawBytes, len(columns))

	//rows.Scan wants '[]interface{}' as an argument, so we must copy
	//the references into such a slice
	scanArgs := make([]interface{}, len(columns))

	for i := range rawBytes {
		scanArgs[i] = &rawBytes[i]
	}

	for rows.Next() {
		err := rows.Scan(scanArgs...)
		if err != nil {
			return nil, err
		}
		var val string
		item := make(map[string]string)
		for i, col := range rawBytes {
			if col == nil {
				val = ""
			} else {
				val = string(col)
			}
			item[columns[i]] = val
		}
		result = append(result, item)
	}
	return result, nil
}

//检查MySQL实例的连接是否还在活跃时间范围内
//! note: 这个函数的实际意义在于更新active时间，实际的sql.DB下面有一个连接池，
// 完全没必要因为超时就更换conn对象。
func (m *MySQL) CheckActive() {
	var now int64 = time.Now().Unix()
	if m.tx != nil {
		//如果存在事务会话，则不再进行连接检查
		m.active = now
		return
	}

	//从MySQL的wait_timeout变量中定位waitTimeout
	if m.waitTimeout == 0 {
		rows, err := m.Query("SHOW VARIABLES LIKE 'wait_timeout'")
		if err != nil {
			panic(err)
		}
		defer rows.Close()

		result, err := m.FetchRows(rows)
		if err != nil {
			panic(err)
		}
		if result != nil && len(result) != 0 {
			timeout, err := strconv.Atoi(result[0]["Value"])
			if err != nil {
				panic(err)
			}
			m.waitTimeout = int64(timeout)
		}
	}

	if now-m.active > m.waitTimeout-2 {
		//此时认为数据库连接已经超时了，重新进行一次连接
		connect(m.schema, m.role)
		var key string = m.schema + m.role
		dbConnMapLock.Lock()
		m.DB = dbConnMap[key]
		dbConnMapLock.Unlock()
	}

	//设置当前时间为最新活跃点
	m.active = now
}

//保证修改、写入类的操作不在slave上执行
func (m *MySQL) CheckSQL(sql string) error {
	if m.role == "Slave" {
		sql = strings.TrimSpace(sql)
		exp := regexp.MustCompile(`^(?i:insert|update|delete|alter|truncate|drop)`)
		if exp.MatchString(sql) {
			return fmt.Errorf("insert|update|delete|alter|truncate|drop operation is not allowed on slave.")
		}
	}

	return nil
}

//开始一个事务，开始一个事务和提交、回滚事务必须一一对应
func (m *MySQL) Begin() error {
	if m.txIndex == 0 {
		var err error
		m.tx, err = m.DB.Begin()
		if err != nil {
			return err
		}
	}
	m.txIndex++

	return nil
}

//提交一个事务
func (m *MySQL) Commit() error {
	var err error
	if m.txIndex == 1 {
		err = m.tx.Commit()
		m.tx = nil
	}
	m.txIndex--

	return err
}

//事务回滚
func (m *MySQL) Rollback() error {
	err := m.tx.Rollback()
	m.txIndex = 0
	m.tx = nil

	return err
}
<|endoftext|>


<commit_before>package template

import (
	"io"
	"reflect"
	"strconv"
)

// If v is a string, put it in single quotes.
// Otherwise return the string normally.
func quoteString(v Value) string {
	if s, ok := v.(stringValue); ok {
		return "'" + string(s) + "'"
	}
	return v.String()
}

// TODO: should there be a Float method on Value?

// A Value represents a generic value of any type.
type Value interface {
	Node

	// Bool coerces the Value to a boolean. Values that are true are:
	//	- A bool that is true
	//	- A non-zero integer
	//	- A non-empty string
	//	- A slice, array, map, or channel with non-zero length
	//	- Any struct
	//	- A non-nil pointer to any of the above
	// All other values evaluate to false.
	Bool() bool

	// Int coerces the Value to a signed integer. The return parameter follows these
	// rules:
	//	- A bool that is true evaluates to 1; false evaluates to 0
	//	- An integer evaluates to itself, with unsigned types possibly overflowing
	//	- A string is converted to a signed integer if possible
	//	- A non-nil pointer to one of the above types uses
	// All other values evaluate to 0.
	Int() int64

	// String coerces the Value to a string. The return parameter follows these rules:
	//	- A bool returns "true" if it is true and "false" otherwise
	//	- An integer is converted to its string representation
	//	- A string evaluates to itself
	//	- A non-nil pointer evaluates to whatever its element would evaluate to
	//	according to these rules
	// All other values evaluate to the empty string "".
	String() string

	// Uint coerces the Value to an unsigned integer. It uses the same rules as
	// Int except that negative signed integers will underflow to positive integers.
	Uint() uint64

	// Reflect returns the Value's reflected value.
	Reflect() reflect.Value
}

type nilValue byte

func (n nilValue) Bool() bool             { return false }
func (n nilValue) Int() int64             { return 0 }
func (n nilValue) String() string         { return "" }
func (n nilValue) Uint() uint64           { return 0 }
func (n nilValue) Reflect() reflect.Value { return reflect.NewValue(nil) }
func (n nilValue) Render(wr io.Writer, c *Context)  {}

type boolValue bool

func (b boolValue) Bool() bool  { return bool(b) }
func (b boolValue) Int() int64 {
	if b {
		return 1
	}
	return 0
}
func (b boolValue) String() string {
	if b {
		return "true"
	}
	return "false"
}
func (b boolValue) Uint() uint64 {
	if b {
		return 1
	}
	return 0
}
func (b boolValue) Reflect() reflect.Value { return reflect.NewValue(b) }
func (b boolValue) Render(wr io.Writer, c *Context)  { wr.Write([]byte(b.String())) }

type stringValue string

func (str stringValue) Bool() bool  { return str != "" }

func (str stringValue) Int() int64 {
	if i, err := strconv.Atoi64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) String() string { return string(str) }

func (str stringValue) Uint() uint64 {
	if i, err := strconv.Atoui64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) Reflect() reflect.Value { return reflect.NewValue(str) }

func (str stringValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(string(str))) }

type intValue int64

func (i intValue) Bool() bool             { return i != 0 }
func (i intValue) Int() int64             { return int64(i) }
func (i intValue) String() string         { return strconv.Itoa64(int64(i)) }
func (i intValue) Uint() uint64           { return uint64(i) }
func (i intValue) Reflect() reflect.Value { return reflect.NewValue(i) }

func (i intValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(i.String()))
}

type floatValue float64

func (f floatValue) Bool() bool             { return f != 0 }
func (f floatValue) Int() int64             { return int64(f) }
func (f floatValue) String() string         { return strconv.Ftoa64(float64(f), 'g', -1) }
func (f floatValue) Uint() uint64           { return uint64(f) }
func (f floatValue) Reflect() reflect.Value { return reflect.NewValue(f) }

func (f floatValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(f.String()))
}

/*
TODO: uncomment this when issue 1716 is fixed
type complexValue complex128

func (c complexValue) Bool() bool { return c != 0 }
func (c complexValue) Int() bool { return 0 }
// TODO: implement
func (c complexValue) String() bool { return "" }
func (c complexValue) Uint() bool { return 0 }
func (c complexValue) Reflect() reflect.Value { return reflect.NewValue(c) }

func (c complexValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(c.String(c)))
}
*/

// reflectValue implements the common Value methods for reflected types.
type reflectValue reflect.Value

func (v reflectValue) Bool() bool             { return reflect.Value(v).Len() != 0 }
func (v reflectValue) Int() int64             { return 0 }
func (v reflectValue) Uint() uint64           { return 0 }
func (v reflectValue) Reflect() reflect.Value { return reflect.Value(v) }

// arrayValue represents a slice or array value
type arrayValue struct {
	reflectValue
}

func (a arrayValue) String() string {
	v := reflect.Value(a.reflectValue)
	str := "["
	for i := 0; i < v.Len(); i++ {
		if i > 0 {
			str += ", "
		}
		str1 := quoteString(refToVal(v.Index(i)))
		str += str1
	}
	str += "]"
	return str
}
func (a arrayValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(a.String())) }

type mapValue struct {
	reflectValue
}

func (m mapValue) String() string {
	v := reflect.Value(m.reflectValue)
	keys := v.MapKeys()
	str := "{"
	for i, key := range keys {
		if i > 0 {
			str += ", "
		}
		v1 := refToVal(key)
		str1 := quoteString(v1)
		str += str1
		str += ": "
		v1 = refToVal(v.MapIndex(key))
		str1 = quoteString(v1)
		str += str1
	}
	str += "}"
	return str
}
func (m mapValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(m.String())) }

type chanValue struct {
	reflectValue
}

func (ch chanValue) String() string {
	// TODO: implement
	return ""
}
func (ch chanValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(ch.String())) }

type structValue struct {
	reflectValue
}

func (st structValue) Bool() bool { return true }
func (st structValue) String() string {
	// TODO: implement
	return ""
}
func (st structValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(st.String())) }

type pointerValue struct {
	reflectValue
}

func (p pointerValue) value() Value { return refToVal(reflect.Value(p.reflectValue).Elem()) }
// TODO: correct
func (p pointerValue) Bool() bool { return !reflect.Value(p.reflectValue).IsNil() }
func (p pointerValue) String() string {
	if reflect.Value(p.reflectValue).IsNil() {
		return "<nil>"
	}
	return p.value().String()
}
func (p pointerValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(p.String())) }

// A Variable is an index into a Context's stack.
// Variables must be obtained through the Parser before runtime.
type Variable int

func (v Variable) Eval(c *Context) Value {
	if val := c.stack[v]; val != nil {
		return val
	}
	return nilValue(0)
}

func (v Variable) Set(val Value, c *Context) { c.stack[v] = val }

func getVal(ref reflect.Value, specs []string) Value {
	for _, s := range specs {
		ref = lookup(ref, s)
		if ref.Kind() == reflect.Invalid {
			return nilValue(0)
		}
	}
	return refToVal(ref)
}

func refToVal(ref reflect.Value) Value {
	switch ref.Kind() {
	case reflect.Bool:
		return boolValue(ref.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intValue(ref.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		return intValue(ref.Uint())
	case reflect.Float32, reflect.Float64:
		return floatValue(ref.Float())
	// TODO: uncomment this when issue 1716 is fixed
	//case reflect.Complex64, reflect.Complex128:
	//return complexValue(ref.Complex())
	case reflect.Array, reflect.Slice:
		return arrayValue{reflectValue(ref)}
	case reflect.Chan:
		return chanValue{reflectValue(ref)}
	case reflect.Map:
		return mapValue{reflectValue(ref)}
	case reflect.Ptr:
		return pointerValue{reflectValue(ref)}
	case reflect.String:
		return stringValue(ref.String())
	case reflect.Struct:
		return structValue{reflectValue(ref)}
	}
	return nilValue(0)
}

func listElem(v reflect.Value, s string) reflect.Value {
	if idx, err := strconv.Atoi(s); err == nil {
		return v.Index(idx)
	}
	return reflect.Value{}
}

func lookup(v reflect.Value, s string) reflect.Value {
	var ret reflect.Value
	switch v.Kind() {
	case reflect.Array, reflect.Slice:
		ret = listElem(v, s)
	case reflect.Map:
		keyt := v.Type().Key()
		switch keyt.Kind() {
		case reflect.String:
			ret = v.MapIndex(reflect.NewValue(s))
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if idx, err := strconv.Atoi64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetInt(idx)
				ret = v.MapIndex(idxVal)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			if idx, err := strconv.Atoui64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetUint(idx)
				ret = v.MapIndex(idxVal)
			}
		}
	case reflect.Ptr:
		v := v.Elem()
		switch v.Kind() {
		case reflect.Array, reflect.Slice:
			ret = listElem(v, s)
		case reflect.Struct:
			ret = v.FieldByName(s)
		}
	case reflect.Struct:
		ret = v.FieldByName(s)
	}
	// TODO: Find a way to look up methods by name
	return ret
}
<commit_msg>Remove some unnecessary code<commit_after>package template

import (
	"io"
	"reflect"
	"strconv"
)

// If v is a string, put it in single quotes.
// Otherwise return the string normally.
func quoteString(v Value) string {
	if s, ok := v.(stringValue); ok {
		return "'" + string(s) + "'"
	}
	return v.String()
}

// TODO: should there be a Float method on Value?

// A Value represents a generic value of any type.
type Value interface {
	Node

	// Bool coerces the Value to a boolean. Values that are true are:
	//	- A bool that is true
	//	- A non-zero integer
	//	- A non-empty string
	//	- A slice, array, map, or channel with non-zero length
	//	- Any struct
	//	- A non-nil pointer to any of the above
	// All other values evaluate to false.
	Bool() bool

	// Int coerces the Value to a signed integer. The return parameter follows these
	// rules:
	//	- A bool that is true evaluates to 1; false evaluates to 0
	//	- An integer evaluates to itself, with unsigned types possibly overflowing
	//	- A string is converted to a signed integer if possible
	//	- A non-nil pointer to one of the above types uses
	// All other values evaluate to 0.
	Int() int64

	// String coerces the Value to a string. The return parameter follows these rules:
	//	- A bool returns "true" if it is true and "false" otherwise
	//	- An integer is converted to its string representation
	//	- A string evaluates to itself
	//	- A non-nil pointer evaluates to whatever its element would evaluate to
	//	according to these rules
	// All other values evaluate to the empty string "".
	String() string

	// Uint coerces the Value to an unsigned integer. It uses the same rules as
	// Int except that negative signed integers will underflow to positive integers.
	Uint() uint64

	// Reflect returns the Value's reflected value.
	Reflect() reflect.Value
}

type nilValue byte

func (n nilValue) Bool() bool             { return false }
func (n nilValue) Int() int64             { return 0 }
func (n nilValue) String() string         { return "" }
func (n nilValue) Uint() uint64           { return 0 }
func (n nilValue) Reflect() reflect.Value { return reflect.NewValue(nil) }
func (n nilValue) Render(wr io.Writer, c *Context)  {}

type boolValue bool

func (b boolValue) Bool() bool  { return bool(b) }
func (b boolValue) Int() int64 {
	if b {
		return 1
	}
	return 0
}
func (b boolValue) String() string {
	if b {
		return "true"
	}
	return "false"
}
func (b boolValue) Uint() uint64 {
	if b {
		return 1
	}
	return 0
}
func (b boolValue) Reflect() reflect.Value { return reflect.NewValue(b) }
func (b boolValue) Render(wr io.Writer, c *Context)  { wr.Write([]byte(b.String())) }

type stringValue string

func (str stringValue) Bool() bool  { return str != "" }

func (str stringValue) Int() int64 {
	if i, err := strconv.Atoi64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) String() string { return string(str) }

func (str stringValue) Uint() uint64 {
	if i, err := strconv.Atoui64(string(str)); err == nil {
		return i
	}
	return 0
}

func (str stringValue) Reflect() reflect.Value { return reflect.NewValue(str) }

func (str stringValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(string(str))) }

type intValue int64

func (i intValue) Bool() bool             { return i != 0 }
func (i intValue) Int() int64             { return int64(i) }
func (i intValue) String() string         { return strconv.Itoa64(int64(i)) }
func (i intValue) Uint() uint64           { return uint64(i) }
func (i intValue) Reflect() reflect.Value { return reflect.NewValue(i) }

func (i intValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(i.String()))
}

type floatValue float64

func (f floatValue) Bool() bool             { return f != 0 }
func (f floatValue) Int() int64             { return int64(f) }
func (f floatValue) String() string         { return strconv.Ftoa64(float64(f), 'g', -1) }
func (f floatValue) Uint() uint64           { return uint64(f) }
func (f floatValue) Reflect() reflect.Value { return reflect.NewValue(f) }

func (f floatValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(f.String()))
}

/*
TODO: uncomment this when issue 1716 is fixed
type complexValue complex128

func (c complexValue) Bool() bool { return c != 0 }
func (c complexValue) Int() bool { return 0 }
// TODO: implement
func (c complexValue) String() bool { return "" }
func (c complexValue) Uint() bool { return 0 }
func (c complexValue) Reflect() reflect.Value { return reflect.NewValue(c) }

func (c complexValue) Render(wr io.Writer, c *Context) {
	wr.Write([]byte(c.String(c)))
}
*/

// reflectValue implements the common Value methods for reflected types.
type reflectValue reflect.Value

func (v reflectValue) Bool() bool             { return reflect.Value(v).Len() != 0 }
func (v reflectValue) Int() int64             { return 0 }
func (v reflectValue) Uint() uint64           { return 0 }
func (v reflectValue) Reflect() reflect.Value { return reflect.Value(v) }

// arrayValue represents a slice or array value
type arrayValue struct {
	reflectValue
}

func (a arrayValue) String() string {
	v := reflect.Value(a.reflectValue)
	str := "["
	for i := 0; i < v.Len(); i++ {
		if i > 0 {
			str += ", "
		}
		str1 := quoteString(refToVal(v.Index(i)))
		str += str1
	}
	str += "]"
	return str
}
func (a arrayValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(a.String())) }

type mapValue struct {
	reflectValue
}

func (m mapValue) String() string {
	v := reflect.Value(m.reflectValue)
	keys := v.MapKeys()
	str := "{"
	for i, key := range keys {
		if i > 0 {
			str += ", "
		}
		v1 := refToVal(key)
		str1 := quoteString(v1)
		str += str1
		str += ": "
		v1 = refToVal(v.MapIndex(key))
		str1 = quoteString(v1)
		str += str1
	}
	str += "}"
	return str
}
func (m mapValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(m.String())) }

type chanValue struct {
	reflectValue
}

func (ch chanValue) String() string {
	// TODO: implement
	return ""
}
func (ch chanValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(ch.String())) }

type structValue struct {
	reflectValue
}

func (st structValue) Bool() bool { return true }
func (st structValue) String() string {
	// TODO: implement
	return ""
}
func (st structValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(st.String())) }

type pointerValue struct {
	reflectValue
}

func (p pointerValue) value() Value { return refToVal(reflect.Value(p.reflectValue).Elem()) }
// TODO: correct
func (p pointerValue) Bool() bool { return !reflect.Value(p.reflectValue).IsNil() }
func (p pointerValue) String() string {
	if reflect.Value(p.reflectValue).IsNil() {
		return "<nil>"
	}
	return p.value().String()
}
func (p pointerValue) Render(wr io.Writer, c *Context) { wr.Write([]byte(p.String())) }

// A Variable is an index into a Context's stack.
// Variables must be obtained through the Parser before runtime.
type Variable int

func (v Variable) Eval(c *Context) Value {
	if val := c.stack[v]; val != nil {
		return val
	}
	return nilValue(0)
}

func (v Variable) Set(val Value, c *Context) { c.stack[v] = val }

func getVal(ref reflect.Value, specs []string) Value {
	for _, s := range specs {
		ref = lookup(ref, s)
		if ref.Kind() == reflect.Invalid {
			return nilValue(0)
		}
	}
	return refToVal(ref)
}

func refToVal(ref reflect.Value) Value {
	switch ref.Kind() {
	case reflect.Bool:
		return boolValue(ref.Bool())
	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
		return intValue(ref.Int())
	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,
		reflect.Uint64, reflect.Uintptr:
		return intValue(ref.Uint())
	case reflect.Float32, reflect.Float64:
		return floatValue(ref.Float())
	// TODO: uncomment this when issue 1716 is fixed
	//case reflect.Complex64, reflect.Complex128:
	//return complexValue(ref.Complex())
	case reflect.Array, reflect.Slice:
		return arrayValue{reflectValue(ref)}
	case reflect.Chan:
		return chanValue{reflectValue(ref)}
	case reflect.Map:
		return mapValue{reflectValue(ref)}
	case reflect.Ptr:
		return pointerValue{reflectValue(ref)}
	case reflect.String:
		return stringValue(ref.String())
	case reflect.Struct:
		return structValue{reflectValue(ref)}
	}
	return nilValue(0)
}

func lookup(v reflect.Value, s string) reflect.Value {
	var ret reflect.Value
	v = reflect.Indirect(v)
	switch v.Kind() {
	case reflect.Array, reflect.Slice:
		if idx, err := strconv.Atoi(s); err == nil {
			ret = v.Index(idx)
		}
	case reflect.Map:
		keyt := v.Type().Key()
		switch keyt.Kind() {
		case reflect.String:
			ret = v.MapIndex(reflect.NewValue(s))
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			if idx, err := strconv.Atoi64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetInt(idx)
				ret = v.MapIndex(idxVal)
			}
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
			if idx, err := strconv.Atoui64(s); err == nil {
				idxVal := reflect.New(keyt).Elem()
				idxVal.SetUint(idx)
				ret = v.MapIndex(idxVal)
			}
		}
	case reflect.Struct:
		ret = v.FieldByName(s)
	}
	// TODO: Find a way to look up methods by name
	return ret
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 golang

import (
	"archive/tar"
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	cutil "github.com/hyperledger/fabric/core/container/util"
	"github.com/hyperledger/fabric/core/util"
	pb "github.com/hyperledger/fabric/protos"
)

var logger = logging.MustGetLogger("golang/hash")

//hashFilesInDir computes h=hash(h,file bytes) for each file in a directory
//Directory entries are traversed recursively. In the end a single
//hash value is returned for the entire directory structure
func hashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) {
	subdir := filepath.Join(rootDir, dir)
	logger.Debug("hashFiles %s", subdir)
	//ReadDir returns sorted list of files in dir
	fis, err := ioutil.ReadDir(subdir)
	if err != nil {
		return hash, fmt.Errorf("ReadDir failed %s\n", err)
	}
	for _, fi := range fis {
		name := filepath.Join(dir, fi.Name())
		if fi.IsDir() {
			var err error
			hash, err = hashFilesInDir(rootDir, name, hash, tw)
			if err != nil {
				return hash, err
			}
			continue
		}
		fqp := filepath.Join(rootDir, name)
		buf, err := ioutil.ReadFile(fqp)
		if err != nil {
			fmt.Printf("Error reading %s\n", err)
			return hash, err
		}

		newSlice := make([]byte, len(hash)+len(buf))
		copy(newSlice[len(buf):], hash[:])
		//hash = md5.Sum(newSlice)
		hash = util.ComputeCryptoHash(newSlice)

		if tw != nil {
			is := bytes.NewReader(buf)
			if err = cutil.WriteStreamToPackage(is, fqp, filepath.Join("src", name), tw); err != nil {
				return hash, fmt.Errorf("Error adding file to tar %s", err)
			}
		}
	}
	return hash, nil
}

func isCodeExist(tmppath string) error {
	file, err := os.Open(tmppath)
	if err != nil {
		return fmt.Errorf("Download failed %s", err)
	}

	fi, err := file.Stat()
	if err != nil {
		return fmt.Errorf("Could not stat file %s", err)
	}

	if !fi.IsDir() {
		return fmt.Errorf("File %s is not dir\n", file.Name())
	}

	return nil
}

func getCodeFromHTTP(path string) (codegopath string, err error) {
	codegopath = ""
	err = nil
	logger.Debug("getCodeFromHTTP %s", path)

	origgopath := os.Getenv("GOPATH")
	if origgopath == "" {
		err = fmt.Errorf("GOPATH not defined")
		return
	}
	// Only take the first element of GOPATH
	gopath := filepath.SplitList(origgopath)[0]

	// Define a new gopath in which to download the code
	newgopath := filepath.Join(gopath, "_usercode_")

	//ignore errors.. _usercode_ might exist. TempDir will catch any other errors
	os.Mkdir(newgopath, 0755)

	if codegopath, err = ioutil.TempDir(newgopath, ""); err != nil {
		err = fmt.Errorf("could not create tmp dir under %s(%s)", newgopath, err)
		return
	}

	//go paths can have multiple dirs. We create a GOPATH with two source tree's as follows
	//
	//    <temporary empty folder to download chaincode source> : <local go path with OBC source>
	//
	//This approach has several goodness:
	// . Go will pick the first path to download user code (which we will delete after processing)
	// . GO will not download OBC as it is in the second path. GO will use the local OBC for generating chaincode image
	//     . network savings
	//     . more secure
	//     . as we are not downloading OBC, private, password-protected OBC repo's become non-issue

	os.Setenv("GOPATH", codegopath+":"+origgopath)
	// Get a copy of that new env for the go get command
	env := os.Environ()
	// and reset GOPATH to its original value
	os.Setenv("GOPATH", origgopath)

	// Use a 'go get' command to pull the chaincode from the given repo
	logger.Debug("go get %s", path)
	cmd := exec.Command("go", "get", path)
	cmd.Env = env
	var out bytes.Buffer
	cmd.Stdout = &out
	var errBuf bytes.Buffer
	cmd.Stderr = &errBuf //capture Stderr and print it on error
	err = cmd.Start()

	// Create a go routine that will wait for the command to finish
	done := make(chan error, 1)
	go func() {
		done <- cmd.Wait()
	}()

	select {
	case <-time.After(time.Duration(viper.GetInt("chaincode.deploytimeout")) * time.Millisecond):
		// If pulling repos takes too long, we should give up
		// (This can happen if a repo is private and the git clone asks for credentials)
		if err = cmd.Process.Kill(); err != nil {
			err = fmt.Errorf("failed to kill: %s", err)
		} else {
			err = errors.New("Getting chaincode took too long")
		}
	case err = <-done:
		// If we're here, the 'go get' command must have finished
		if err != nil {
			err = fmt.Errorf("'go get' failed with error\n\"%s\"\n", err, string(errBuf.Bytes()))
		}
	}
	return
}

func getCodeFromFS(path string) (codegopath string, err error) {
	logger.Debug("getCodeFromFS %s", path)
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		err = fmt.Errorf("GOPATH not defined")
		return
	}
	// Only take the first element of GOPATH
	gopath = filepath.SplitList(gopath)[0]

	return
}

//generateHashcode gets hashcode of the code under path. If path is a HTTP(s) url
//it downloads the code first to compute the hash.
//NOTE: for dev mode, user builds and runs chaincode manually. The name provided
//by the user is equivalent to the path. This method will treat the name
//as codebytes and compute the hash from it. ie, user cannot run the chaincode
//with the same (name, ctor, args)
func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) {
	if spec == nil {
		return "", fmt.Errorf("Cannot generate hashcode from nil spec")
	}

	chaincodeID := spec.ChaincodeID
	if chaincodeID == nil || chaincodeID.Path == "" {
		return "", fmt.Errorf("Cannot generate hashcode from empty chaincode path")
	}

	ctor := spec.CtorMsg
	if ctor == nil || ctor.Function == "" {
		return "", fmt.Errorf("Cannot generate hashcode from empty ctor")
	}

	//code root will point to the directory where the code exists
	//in the case of http it will be a temporary dir that
	//will have to be deleted
	var codegopath string

	var ishttp bool
	defer func() {
		if ishttp && codegopath != "" {
			os.RemoveAll(codegopath)
		}
	}()

	path := chaincodeID.Path

	var err error
	var actualcodepath string
	if strings.HasPrefix(path, "http://") {
		ishttp = true
		actualcodepath = path[7:]
		codegopath, err = getCodeFromHTTP(actualcodepath)
	} else if strings.HasPrefix(path, "https://") {
		ishttp = true
		actualcodepath = path[8:]
		codegopath, err = getCodeFromHTTP(actualcodepath)
	} else {
		actualcodepath = path
		codegopath, err = getCodeFromFS(path)
	}

	if err != nil {
		return "", fmt.Errorf("Error getting code %s", err)
	}

	tmppath := filepath.Join(codegopath, "src", actualcodepath)
	if err = isCodeExist(tmppath); err != nil {
		return "", fmt.Errorf("code does not exist %s", err)
	}

	hash := util.GenerateHashFromSignature(actualcodepath, ctor.Function, ctor.Args)

	hash, err = hashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw)
	if err != nil {
		return "", fmt.Errorf("Could not get hashcode for %s - %s\n", path, err)
	}

	return hex.EncodeToString(hash[:]), nil
}
<commit_msg>Fix getCodeFromFS<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 golang

import (
	"archive/tar"
	"bytes"
	"encoding/hex"
	"errors"
	"fmt"
	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"

	cutil "github.com/hyperledger/fabric/core/container/util"
	"github.com/hyperledger/fabric/core/util"
	pb "github.com/hyperledger/fabric/protos"
)

var logger = logging.MustGetLogger("golang/hash")

//hashFilesInDir computes h=hash(h,file bytes) for each file in a directory
//Directory entries are traversed recursively. In the end a single
//hash value is returned for the entire directory structure
func hashFilesInDir(rootDir string, dir string, hash []byte, tw *tar.Writer) ([]byte, error) {
	subdir := filepath.Join(rootDir, dir)
	logger.Debug("hashFiles %s", subdir)
	//ReadDir returns sorted list of files in dir
	fis, err := ioutil.ReadDir(subdir)
	if err != nil {
		return hash, fmt.Errorf("ReadDir failed %s\n", err)
	}
	for _, fi := range fis {
		name := filepath.Join(dir, fi.Name())
		if fi.IsDir() {
			var err error
			hash, err = hashFilesInDir(rootDir, name, hash, tw)
			if err != nil {
				return hash, err
			}
			continue
		}
		fqp := filepath.Join(rootDir, name)
		buf, err := ioutil.ReadFile(fqp)
		if err != nil {
			fmt.Printf("Error reading %s\n", err)
			return hash, err
		}

		newSlice := make([]byte, len(hash)+len(buf))
		copy(newSlice[len(buf):], hash[:])
		//hash = md5.Sum(newSlice)
		hash = util.ComputeCryptoHash(newSlice)

		if tw != nil {
			is := bytes.NewReader(buf)
			if err = cutil.WriteStreamToPackage(is, fqp, filepath.Join("src", name), tw); err != nil {
				return hash, fmt.Errorf("Error adding file to tar %s", err)
			}
		}
	}
	return hash, nil
}

func isCodeExist(tmppath string) error {
	file, err := os.Open(tmppath)
	if err != nil {
		return fmt.Errorf("Download failed %s", err)
	}

	fi, err := file.Stat()
	if err != nil {
		return fmt.Errorf("Could not stat file %s", err)
	}

	if !fi.IsDir() {
		return fmt.Errorf("File %s is not dir\n", file.Name())
	}

	return nil
}

func getCodeFromHTTP(path string) (codegopath string, err error) {
	codegopath = ""
	err = nil
	logger.Debug("getCodeFromHTTP %s", path)

	origgopath := os.Getenv("GOPATH")
	if origgopath == "" {
		err = fmt.Errorf("GOPATH not defined")
		return
	}
	// Only take the first element of GOPATH
	gopath := filepath.SplitList(origgopath)[0]

	// Define a new gopath in which to download the code
	newgopath := filepath.Join(gopath, "_usercode_")

	//ignore errors.. _usercode_ might exist. TempDir will catch any other errors
	os.Mkdir(newgopath, 0755)

	if codegopath, err = ioutil.TempDir(newgopath, ""); err != nil {
		err = fmt.Errorf("could not create tmp dir under %s(%s)", newgopath, err)
		return
	}

	//go paths can have multiple dirs. We create a GOPATH with two source tree's as follows
	//
	//    <temporary empty folder to download chaincode source> : <local go path with OBC source>
	//
	//This approach has several goodness:
	// . Go will pick the first path to download user code (which we will delete after processing)
	// . GO will not download OBC as it is in the second path. GO will use the local OBC for generating chaincode image
	//     . network savings
	//     . more secure
	//     . as we are not downloading OBC, private, password-protected OBC repo's become non-issue

	os.Setenv("GOPATH", codegopath+":"+origgopath)
	// Get a copy of that new env for the go get command
	env := os.Environ()
	// and reset GOPATH to its original value
	os.Setenv("GOPATH", origgopath)

	// Use a 'go get' command to pull the chaincode from the given repo
	logger.Debug("go get %s", path)
	cmd := exec.Command("go", "get", path)
	cmd.Env = env
	var out bytes.Buffer
	cmd.Stdout = &out
	var errBuf bytes.Buffer
	cmd.Stderr = &errBuf //capture Stderr and print it on error
	err = cmd.Start()

	// Create a go routine that will wait for the command to finish
	done := make(chan error, 1)
	go func() {
		done <- cmd.Wait()
	}()

	select {
	case <-time.After(time.Duration(viper.GetInt("chaincode.deploytimeout")) * time.Millisecond):
		// If pulling repos takes too long, we should give up
		// (This can happen if a repo is private and the git clone asks for credentials)
		if err = cmd.Process.Kill(); err != nil {
			err = fmt.Errorf("failed to kill: %s", err)
		} else {
			err = errors.New("Getting chaincode took too long")
		}
	case err = <-done:
		// If we're here, the 'go get' command must have finished
		if err != nil {
			err = fmt.Errorf("'go get' failed with error\n\"%s\"\n", err, string(errBuf.Bytes()))
		}
	}
	return
}

func getCodeFromFS(path string) (codegopath string, err error) {
	logger.Debug("getCodeFromFS %s", path)
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		err = fmt.Errorf("GOPATH not defined")
		return
	}
	// Only take the first element of GOPATH
	codegopath = filepath.SplitList(gopath)[0]

	return
}

//generateHashcode gets hashcode of the code under path. If path is a HTTP(s) url
//it downloads the code first to compute the hash.
//NOTE: for dev mode, user builds and runs chaincode manually. The name provided
//by the user is equivalent to the path. This method will treat the name
//as codebytes and compute the hash from it. ie, user cannot run the chaincode
//with the same (name, ctor, args)
func generateHashcode(spec *pb.ChaincodeSpec, tw *tar.Writer) (string, error) {
	if spec == nil {
		return "", fmt.Errorf("Cannot generate hashcode from nil spec")
	}

	chaincodeID := spec.ChaincodeID
	if chaincodeID == nil || chaincodeID.Path == "" {
		return "", fmt.Errorf("Cannot generate hashcode from empty chaincode path")
	}

	ctor := spec.CtorMsg
	if ctor == nil || ctor.Function == "" {
		return "", fmt.Errorf("Cannot generate hashcode from empty ctor")
	}

	//code root will point to the directory where the code exists
	//in the case of http it will be a temporary dir that
	//will have to be deleted
	var codegopath string

	var ishttp bool
	defer func() {
		if ishttp && codegopath != "" {
			os.RemoveAll(codegopath)
		}
	}()

	path := chaincodeID.Path

	var err error
	var actualcodepath string
	if strings.HasPrefix(path, "http://") {
		ishttp = true
		actualcodepath = path[7:]
		codegopath, err = getCodeFromHTTP(actualcodepath)
	} else if strings.HasPrefix(path, "https://") {
		ishttp = true
		actualcodepath = path[8:]
		codegopath, err = getCodeFromHTTP(actualcodepath)
	} else {
		actualcodepath = path
		codegopath, err = getCodeFromFS(path)
	}

	if err != nil {
		return "", fmt.Errorf("Error getting code %s", err)
	}

	tmppath := filepath.Join(codegopath, "src", actualcodepath)
	if err = isCodeExist(tmppath); err != nil {
		return "", fmt.Errorf("code does not exist %s", err)
	}

	hash := util.GenerateHashFromSignature(actualcodepath, ctor.Function, ctor.Args)

	hash, err = hashFilesInDir(filepath.Join(codegopath, "src"), actualcodepath, hash, tw)
	if err != nil {
		return "", fmt.Errorf("Could not get hashcode for %s - %s\n", path, err)
	}

	return hex.EncodeToString(hash[:]), nil
}
<|endoftext|>


<commit_before>package vm

import "testing"

func TestNewBool(t *testing.T) {
	for _, b := range []bool{true, false} {
		if bool(NewBool(b).Eval().(Bool)) != b {
			t.Fail()
		}
	}
}

func TestAnd(t *testing.T) {
	and := func(ts ...*Thunk) bool {
		return bool(And(ts...).Eval().(Bool))
	}

	if !and(True, True) {
		t.Fail()
	}

	for _, ts := range [][]*Thunk{{False, False}, {True, False}, {False, True}} {
		if and(ts...) {
			t.Fail()
		}
	}
}

func TestOr(t *testing.T) {
	or := func(ts ...*Thunk) bool {
		return bool(Or(ts...).Eval().(Bool))
	}

	for _, ts := range [][]*Thunk{{True, True}, {True, False}, {False, True}} {
		if !or(ts...) {
			t.Fail()
		}
	}

	if or(False, False) {
		t.Fail()
	}
}
<commit_msg>Test Not<commit_after>package vm

import "testing"

func TestNewBool(t *testing.T) {
	for _, b := range []bool{true, false} {
		if bool(NewBool(b).Eval().(Bool)) != b {
			t.Fail()
		}
	}
}

func TestAnd(t *testing.T) {
	and := func(ts ...*Thunk) bool {
		return bool(And(ts...).Eval().(Bool))
	}

	if !and(True, True) {
		t.Fail()
	}

	for _, ts := range [][]*Thunk{{False, False}, {True, False}, {False, True}} {
		if and(ts...) {
			t.Fail()
		}
	}
}

func TestOr(t *testing.T) {
	or := func(ts ...*Thunk) bool {
		return bool(Or(ts...).Eval().(Bool))
	}

	for _, ts := range [][]*Thunk{{True, True}, {True, False}, {False, True}} {
		if !or(ts...) {
			t.Fail()
		}
	}

	if or(False, False) {
		t.Fail()
	}
}

func TestNot(t *testing.T) {
	for _, b := range []bool{true, false} {
		if bool(Not(NewBool(b)).Eval().(Bool)) == b {
			t.Fail()
		}
	}
}
<|endoftext|>


<commit_before>package scanner

import (
	"bufio"
	"bytes"
	"io"
	"strconv"
	"unicode"
)

// A Scanner tokenizes runes from an io.Reader.
type Scanner struct {
	r               io.RuneReader
	rbuf            []rune
	line, col, pcol int

	tok         Token
	tline, tcol int
	err         error

	tbuf  bytes.Buffer
	quote rune
}

// New returns a new Scanner that reads from r.
func New(r io.Reader) *Scanner {
	var rr io.RuneReader
	switch r := r.(type) {
	case io.RuneReader:
		rr = r
	default:
		rr = bufio.NewReader(r)
	}

	return &Scanner{
		r:    rr,
		line: 1,
	}
}

// Scan reads the next token from the underlying io.Reader. If a token
// was successfully read, it returns true. It is designed to be used
// in a loop, similarly to bufio.Scanner's API.
func (s *Scanner) Scan() bool {
	if s.err != nil {
		return false
	}

	s.tbuf.Reset()

	state := s.whitespace
	for (state != nil) && (s.err == nil) {
		r, err := s.read()
		if err != nil {
			s.err = err

			if err == io.EOF {
				r = '\n'
			}
		}

		state = state(r)

		if (s.err == io.EOF) && (state != nil) {
			return false
		}
	}

	return true
}

// Tok returns the latest token scanned. If there was an error or a
// token hasn't been scanned yet, its return value is undefined.
func (s *Scanner) Tok() Token {
	return s.tok
}

// Err returns the error that stopped the scanner, if any.
func (s *Scanner) Err() error {
	if s.err == io.EOF {
		return nil
	}

	return s.err
}

func (s *Scanner) read() (r rune, err error) {
	defer func() {
		s.col++

		if r == '\n' {
			s.line++
			s.pcol = s.col
			s.col = 0
		}
	}()

	if len(s.rbuf) > 0 {
		r = s.rbuf[len(s.rbuf)-1]
		s.rbuf = s.rbuf[:len(s.rbuf)-1]
		return
	}

	r, _, err = s.r.ReadRune()
	return
}

func (s *Scanner) unread(r rune) {
	s.rbuf = append(s.rbuf, r)

	s.col--
	if r == '\n' {
		s.line--
		s.col = s.pcol
	}
}

func (s *Scanner) setTok(t TokenType, v interface{}) {
	s.tok = Token{
		Line: s.tline,
		Col:  s.tcol,
		Type: t,
		Val:  v,
	}
}

type stateFunc func(rune) stateFunc

func (s *Scanner) whitespace(r rune) stateFunc {
	if r == '#' {
		return s.comment
	}

	if unicode.IsSpace(r) {
		return s.whitespace
	}

	if r == '-' {
		s.tline, s.tcol = s.line, s.col
		s.unread(r)
		return s.negative
	}

	if unicode.IsDigit(r) {
		s.tline, s.tcol = s.line, s.col
		s.unread(r)
		return s.number
	}

	if isQuote(r) {
		s.tline, s.tcol = s.line, s.col
		s.quote = r
		return s.string
	}

	s.unread(r)
	s.tline, s.tcol = s.line, s.col
	return s.id
}

func (s *Scanner) comment(r rune) stateFunc {
	if r == '\n' {
		return s.whitespace
	}

	return s.comment
}

func (s *Scanner) negative(r rune) stateFunc {
	if r == '-' {
		s.tbuf.WriteRune(r)
		return s.negative
	}

	if unicode.IsDigit(r) {
		s.tbuf.WriteRune(r)
		return s.number
	}

	s.unread(r)
	return s.id
}

func (s *Scanner) number(r rune) stateFunc {
	if unicode.IsDigit(r) || (r == '.') {
		s.tbuf.WriteRune(r)
		return s.number
	}

	val, _ := strconv.ParseFloat(s.tbuf.String(), 64)
	s.setTok(Number, val)

	s.unread(r)
	return nil
}

func (s *Scanner) string(r rune) stateFunc {
	if r == '\\' {
		return s.escape
	}

	if r != s.quote {
		s.tbuf.WriteRune(r)
		return s.string
	}

	s.setTok(String, s.tbuf.String())

	return nil
}

func (s *Scanner) escape(r rune) stateFunc {
	switch r {
	case 'n':
		s.tbuf.WriteRune('\n')
	case 't':
		s.tbuf.WriteRune('\t')
	case '\n':
	default:
		s.tbuf.WriteRune(r)
	}

	return s.string
}

func (s *Scanner) id(r rune) stateFunc {
	if !unicode.IsSpace(r) {
		s.tbuf.WriteRune(r)

		// TODO: Find a way to do this without allocating and copying.
		val := s.tbuf.String()
		if k := symbolicSuffix(val); k != "" {
			// BUG: This only works so long as the set of keywords doesn't
			// contain any which contain other keywords as prefixes.
			if len(val) == len(k) {
				t := Keyword
				s.setTok(t, val)
				return nil
			}

			for i := len(k) - 1; i >= 0; i-- {
				s.unread(rune(k[i]))
			}

			t, val := ID, val[:len(val)-len(k)]
			if isKeyword(val) {
				t = Keyword
			}
			s.setTok(t, val)
			return nil
		}

		return s.id
	}

	s.setTok(ID, s.tbuf.String())
	s.unread(r)
	return nil
}
<commit_msg>scanner: Add method for getting current position.<commit_after>package scanner

import (
	"bufio"
	"bytes"
	"io"
	"strconv"
	"unicode"
)

// A Scanner tokenizes runes from an io.Reader.
type Scanner struct {
	r               io.RuneReader
	rbuf            []rune
	line, col, pcol int

	tok         Token
	tline, tcol int
	err         error

	tbuf  bytes.Buffer
	quote rune
}

// New returns a new Scanner that reads from r.
func New(r io.Reader) *Scanner {
	var rr io.RuneReader
	switch r := r.(type) {
	case io.RuneReader:
		rr = r
	default:
		rr = bufio.NewReader(r)
	}

	return &Scanner{
		r:    rr,
		line: 1,
	}
}

// Scan reads the next token from the underlying io.Reader. If a token
// was successfully read, it returns true. It is designed to be used
// in a loop, similarly to bufio.Scanner's API.
func (s *Scanner) Scan() bool {
	if s.err != nil {
		return false
	}

	s.tbuf.Reset()

	state := s.whitespace
	for (state != nil) && (s.err == nil) {
		r, err := s.read()
		if err != nil {
			s.err = err

			if err == io.EOF {
				r = '\n'
			}
		}

		state = state(r)

		if (s.err == io.EOF) && (state != nil) {
			return false
		}
	}

	return true
}

// Tok returns the latest token scanned. If there was an error or a
// token hasn't been scanned yet, its return value is undefined.
func (s *Scanner) Tok() Token {
	return s.tok
}

// Err returns the error that stopped the scanner, if any.
func (s *Scanner) Err() error {
	if s.err == io.EOF {
		return nil
	}

	return s.err
}

func (s *Scanner) Pos() (line, col int) {
	return s.line, s.col
}

func (s *Scanner) read() (r rune, err error) {
	defer func() {
		s.col++

		if r == '\n' {
			s.line++
			s.pcol = s.col
			s.col = 0
		}
	}()

	if len(s.rbuf) > 0 {
		r = s.rbuf[len(s.rbuf)-1]
		s.rbuf = s.rbuf[:len(s.rbuf)-1]
		return
	}

	r, _, err = s.r.ReadRune()
	return
}

func (s *Scanner) unread(r rune) {
	s.rbuf = append(s.rbuf, r)

	s.col--
	if r == '\n' {
		s.line--
		s.col = s.pcol
	}
}

func (s *Scanner) setTok(t TokenType, v interface{}) {
	s.tok = Token{
		Line: s.tline,
		Col:  s.tcol,
		Type: t,
		Val:  v,
	}
}

type stateFunc func(rune) stateFunc

func (s *Scanner) whitespace(r rune) stateFunc {
	if r == '#' {
		return s.comment
	}

	if unicode.IsSpace(r) {
		return s.whitespace
	}

	if r == '-' {
		s.tline, s.tcol = s.line, s.col
		s.unread(r)
		return s.negative
	}

	if unicode.IsDigit(r) {
		s.tline, s.tcol = s.line, s.col
		s.unread(r)
		return s.number
	}

	if isQuote(r) {
		s.tline, s.tcol = s.line, s.col
		s.quote = r
		return s.string
	}

	s.unread(r)
	s.tline, s.tcol = s.line, s.col
	return s.id
}

func (s *Scanner) comment(r rune) stateFunc {
	if r == '\n' {
		return s.whitespace
	}

	return s.comment
}

func (s *Scanner) negative(r rune) stateFunc {
	if r == '-' {
		s.tbuf.WriteRune(r)
		return s.negative
	}

	if unicode.IsDigit(r) {
		s.tbuf.WriteRune(r)
		return s.number
	}

	s.unread(r)
	return s.id
}

func (s *Scanner) number(r rune) stateFunc {
	if unicode.IsDigit(r) || (r == '.') {
		s.tbuf.WriteRune(r)
		return s.number
	}

	val, _ := strconv.ParseFloat(s.tbuf.String(), 64)
	s.setTok(Number, val)

	s.unread(r)
	return nil
}

func (s *Scanner) string(r rune) stateFunc {
	if r == '\\' {
		return s.escape
	}

	if r != s.quote {
		s.tbuf.WriteRune(r)
		return s.string
	}

	s.setTok(String, s.tbuf.String())

	return nil
}

func (s *Scanner) escape(r rune) stateFunc {
	switch r {
	case 'n':
		s.tbuf.WriteRune('\n')
	case 't':
		s.tbuf.WriteRune('\t')
	case '\n':
	default:
		s.tbuf.WriteRune(r)
	}

	return s.string
}

func (s *Scanner) id(r rune) stateFunc {
	if !unicode.IsSpace(r) {
		s.tbuf.WriteRune(r)

		// TODO: Find a way to do this without allocating and copying.
		val := s.tbuf.String()
		if k := symbolicSuffix(val); k != "" {
			// BUG: This only works so long as the set of keywords doesn't
			// contain any which contain other keywords as prefixes.
			if len(val) == len(k) {
				t := Keyword
				s.setTok(t, val)
				return nil
			}

			for i := len(k) - 1; i >= 0; i-- {
				s.unread(rune(k[i]))
			}

			t, val := ID, val[:len(val)-len(k)]
			if isKeyword(val) {
				t = Keyword
			}
			s.setTok(t, val)
			return nil
		}

		return s.id
	}

	s.setTok(ID, s.tbuf.String())
	s.unread(r)
	return nil
}
<|endoftext|>


<commit_before>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	state           errorcode.ErrorCode
	reader          *bufio.Reader
	currentPosition *Position
	positionBuffer  []*Position

	currentLine   int
	currentColumn int

	asyncDef bool
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		state:          errorcode.E_OK,
		reader:         bufio.NewReader(r),
		positionBuffer: make([]*Position, 0),
		currentLine:    1,
		currentColumn:  0,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseNumber(positions *Positions, nextChar rune) *token.Token {
	pos := scanner.nextPosition()
	switch ch := pos.Char; {
	case nextChar == '0' && (ch == 'j' || ch == 'J'):
		// Imaginary
		positions.Append(pos)
	case nextChar == '0' && (ch == 'x' || ch == 'X'):
		// Hex
		positions.Append(pos)
		pos = scanner.nextPosition()
		if !IsXDigit(pos.Char) {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for IsXDigit(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'b' || ch == 'B'):
		// Binary
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char != '0' && pos.Char != '1' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char == '0' || pos.Char == '1' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'o' || ch == 'O'):
		// Octal
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char < '0' || pos.Char >= '8' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char >= '0' && pos.Char < '8' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	default:
		decimal := nextChar == '.'
		imaginary := false
		exponent := false
		for {
			if pos.Char == '.' && decimal {
				break
			} else if pos.Char == '.' && !decimal {
				decimal = true
			} else if (pos.Char == 'j' || pos.Char == 'J') && !imaginary {
				imaginary = true
			} else if (pos.Char == 'e' || pos.Char == 'E') && !exponent {
				exponent = true
				positions.Append(pos)
				pos2 := scanner.nextPosition()
				if pos2.Char == '-' || pos2.Char == '+' {
					pos3 := scanner.nextPosition()
					if !IsDigit(pos3.Char) {
						return positions.AsToken(token.ERRORTOKEN)
					}
					scanner.unreadPosition(pos3)
					positions.Append(pos2)
				} else if !IsDigit(pos2.Char) {
					return positions.AsToken(token.ERRORTOKEN)
				} else {
					scanner.unreadPosition(pos2)
				}
				pos = scanner.nextPosition()
				continue
			} else if !IsDigit(pos.Char) {
				break
			}
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	}

	return positions.AsToken(token.NUMBER)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) NextToken() *token.Token {
	positions := NewPositions()

	pos := scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		// Check for async/await
		// literal := positions.String()
		// if literal == "async" || literal == "await" {
		// 	if scanner.asyncDef {
		// 		switch literal {
		// 		case "async":
		// 			return positions.AsToken(token.ASYNC)
		// 		case "await":
		// 			return positions.AsToken(token.AWAIT)
		// 		}
		// 	} else if literal == "async" {
		// 		nextToken := scanner.NextToken()
		// 		if nextToken.ID == token.NAME && nextToken.Literal == "def" {
		// 			scanner.asyncDef = true
		// 			return positions.AsToken(token.ASYNC)
		// 		}
		// 	}
		// }

		return positions.AsToken(token.NAME)
	case ch == '\n':
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			positions.Append(pos2)
			return scanner.parseNumber(positions, pos2.Char)
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
		return scanner.parseNumber(positions, ch)
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<commit_msg>add proper indent/dedent handling<commit_after>package scanner

import (
	"bufio"
	"io"

	"github.com/brettlangdon/gython/errorcode"
	"github.com/brettlangdon/gython/token"
)

var EOF rune = 0
var MAXINDENT int = 100

type Scanner struct {
	asyncDef            bool
	atBol               bool
	currentColumn       int
	currentLine         int
	currentPosition     *Position
	indentationAltStack []int
	indentationCurrent  int
	indentationLevel    int
	indentationPending  int
	indentationStack    []int
	positionBuffer      []*Position
	tokenBuffer         []*token.Token
	reader              *bufio.Reader
	state               errorcode.ErrorCode
	tabsize             int
	tabsizeAlt          int
}

func NewScanner(r io.Reader) *Scanner {
	return &Scanner{
		atBol:               true,
		currentColumn:       0,
		currentLine:         1,
		indentationAltStack: make([]int, MAXINDENT),
		indentationCurrent:  0,
		indentationLevel:    0,
		indentationPending:  0,
		indentationStack:    make([]int, MAXINDENT),
		positionBuffer:      make([]*Position, 0),
		tokenBuffer:         make([]*token.Token, 0),
		reader:              bufio.NewReader(r),
		state:               errorcode.E_OK,
		tabsize:             8,
	}
}

func (scanner *Scanner) nextPosition() *Position {
	if len(scanner.positionBuffer) > 0 {
		last := len(scanner.positionBuffer) - 1
		scanner.currentPosition = scanner.positionBuffer[last]
		scanner.positionBuffer = scanner.positionBuffer[0:last]
		return scanner.currentPosition
	}

	next, _, err := scanner.reader.ReadRune()
	if err != nil {
		scanner.state = errorcode.E_EOF
		next = EOF
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	if next == '\n' || next == EOF {
		scanner.currentLine++
		scanner.currentColumn = 0
	}

	pos := &Position{
		Char:   next,
		Line:   scanner.currentLine,
		Column: scanner.currentColumn,
	}
	scanner.currentColumn++
	return pos
}

func (scanner *Scanner) unreadPosition(pos *Position) {
	scanner.positionBuffer = append(scanner.positionBuffer, pos)
}

func (scanner *Scanner) parseNumber(positions *Positions, nextChar rune) *token.Token {
	pos := scanner.nextPosition()
	switch ch := pos.Char; {
	case nextChar == '0' && (ch == 'j' || ch == 'J'):
		// Imaginary
		positions.Append(pos)
	case nextChar == '0' && (ch == 'x' || ch == 'X'):
		// Hex
		positions.Append(pos)
		pos = scanner.nextPosition()
		if !IsXDigit(pos.Char) {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for IsXDigit(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'b' || ch == 'B'):
		// Binary
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char != '0' && pos.Char != '1' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char == '0' || pos.Char == '1' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	case nextChar == '0' && (ch == 'o' || ch == 'O'):
		// Octal
		positions.Append(pos)
		pos = scanner.nextPosition()
		if pos.Char < '0' || pos.Char >= '8' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		for pos.Char >= '0' && pos.Char < '8' {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	default:
		decimal := nextChar == '.'
		imaginary := false
		exponent := false
		for {
			if pos.Char == '.' && decimal {
				break
			} else if pos.Char == '.' && !decimal {
				decimal = true
			} else if (pos.Char == 'j' || pos.Char == 'J') && !imaginary {
				imaginary = true
			} else if (pos.Char == 'e' || pos.Char == 'E') && !exponent {
				exponent = true
				positions.Append(pos)
				pos2 := scanner.nextPosition()
				if pos2.Char == '-' || pos2.Char == '+' {
					pos3 := scanner.nextPosition()
					if !IsDigit(pos3.Char) {
						return positions.AsToken(token.ERRORTOKEN)
					}
					scanner.unreadPosition(pos3)
					positions.Append(pos2)
				} else if !IsDigit(pos2.Char) {
					return positions.AsToken(token.ERRORTOKEN)
				} else {
					scanner.unreadPosition(pos2)
				}
				pos = scanner.nextPosition()
				continue
			} else if !IsDigit(pos.Char) {
				break
			}
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)
	}

	return positions.AsToken(token.NUMBER)
}

func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Token {
	// Determine quote size, 1 or 3 (e.g. 'string',  '''string''')
	quoteSize := 1
	endQuoteSize := 0
	pos := scanner.nextPosition()
	if pos.Char == quote {
		pos2 := scanner.nextPosition()
		if pos2.Char == quote {
			positions.Append(pos)
			positions.Append(pos2)
			quoteSize = 3
		} else {
			scanner.unreadPosition(pos2)
			endQuoteSize = 1
		}
	} else {
		scanner.unreadPosition(pos)
	}

	for {
		if endQuoteSize == quoteSize {
			break
		}
		pos = scanner.nextPosition()
		positions.Append(pos)
		if pos.Char == EOF {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if quoteSize == 1 && pos.Char == '\n' {
			return positions.AsToken(token.ERRORTOKEN)
		}
		if pos.Char == quote {
			endQuoteSize += 1
		} else {
			endQuoteSize = 0
			if pos.Char == '\\' {
				pos = scanner.nextPosition()
			}
		}
	}
	return positions.AsToken(token.STRING)
}

func (scanner *Scanner) unreadToken(tok *token.Token) {
	scanner.tokenBuffer = append(scanner.tokenBuffer, tok)
}

func (scanner *Scanner) NextToken() *token.Token {
	if len(scanner.tokenBuffer) > 0 {
		last := len(scanner.tokenBuffer) - 1
		nextToken := scanner.tokenBuffer[last]
		scanner.tokenBuffer = scanner.tokenBuffer[0:last]
		return nextToken
	}

	blankline := false
	positions := NewPositions()
	var pos *Position

	if scanner.atBol {
		// Get indentation level
		col := 0
		altcol := 0
		scanner.atBol = false
		pos = scanner.nextPosition()
		for {
			if pos.Char == ' ' {
				col++
				altcol++
			} else if pos.Char == '\t' {
				col = (col/scanner.tabsize + 1) * scanner.tabsize
				altcol = (altcol/scanner.tabsizeAlt + 1) * scanner.tabsizeAlt
			} else {
				break
			}
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		if pos.Char == '#' || pos.Char == '\n' {
			// Lines with only newline or comment, shouldn't affect indentation
			if col == 0 && pos.Char == '\n' {
				blankline = false
			} else {
				blankline = true
			}
		}
		if !blankline && scanner.indentationLevel == 0 {
			if col == scanner.indentationStack[scanner.indentationCurrent] {
				if altcol != scanner.indentationAltStack[scanner.indentationCurrent] {
					return positions.AsToken(token.ERRORTOKEN)
				}
			} else if col > scanner.indentationStack[scanner.indentationCurrent] {
				if scanner.indentationCurrent+1 >= MAXINDENT {
					return positions.AsToken(token.ERRORTOKEN)
				}
				if altcol <= scanner.indentationAltStack[scanner.indentationCurrent] {
					return positions.AsToken(token.ERRORTOKEN)
				}
				scanner.indentationPending++
				scanner.indentationCurrent++
				scanner.indentationStack[scanner.indentationCurrent] = col
				scanner.indentationAltStack[scanner.indentationCurrent] = altcol

			} else {
				for scanner.indentationCurrent > 0 && col < scanner.indentationStack[scanner.indentationCurrent] {
					scanner.indentationPending--
					scanner.indentationCurrent--
				}
				if col != scanner.indentationStack[scanner.indentationCurrent] {
					return positions.AsToken(token.ERRORTOKEN)
				}
				if altcol != scanner.indentationAltStack[scanner.indentationCurrent] {
					return positions.AsToken(token.ERRORTOKEN)
				}
			}
		}
	}

	if scanner.indentationPending != 0 {
		if scanner.indentationPending < 0 {
			scanner.indentationPending++
			pos = scanner.currentPosition
			return &token.Token{
				ID:          token.DEDENT,
				LineStart:   pos.Line,
				ColumnStart: pos.Column,
				LineEnd:     pos.Line,
				ColumnEnd:   pos.Column,
				Literal:     "",
			}
		} else {
			scanner.indentationPending--
			pos = scanner.currentPosition
			return &token.Token{
				ID:          token.INDENT,
				LineStart:   pos.Line,
				ColumnStart: pos.Column,
				LineEnd:     pos.Line,
				ColumnEnd:   pos.Column + 4,
				Literal:     "    ",
			}
		}
	}

	pos = scanner.nextPosition()
	// skip spaces
	for {
		if pos.Char != ' ' && pos.Char != '\t' {
			break
		}
		pos = scanner.nextPosition()
	}

	// skip comments
	if pos.Char == '#' {
		for {
			pos = scanner.nextPosition()
			if pos.Char == EOF || pos.Char == '\n' {
				break
			}
		}
	}

	positions.Append(pos)
	switch ch := pos.Char; {
	case ch == EOF:
		id := token.ENDMARKER
		if scanner.state != errorcode.E_EOF {
			id = token.ERRORTOKEN
		}
		return positions.AsToken(id)
	case IsIdentifierStart(ch):
		// Parse Identifier
		saw_b, saw_r, saw_u := false, false, false
		for {
			if !(saw_b || saw_u) && (ch == 'b' || ch == 'B') {
				saw_b = true
			} else if !(saw_b || saw_u || saw_r) && (ch == 'u' || ch == 'U') {
				saw_u = true
			} else if !(saw_r || saw_u) && (ch == 'r' || ch == 'R') {
				saw_r = true
			} else {
				break
			}
			pos = scanner.nextPosition()
			if IsQuote(pos.Char) {
				positions.Append(pos)
				return scanner.parseQuoted(positions, pos.Char)
			}
		}
		pos = scanner.nextPosition()
		for IsIdentifierChar(pos.Char) {
			positions.Append(pos)
			pos = scanner.nextPosition()
		}
		scanner.unreadPosition(pos)

		// Check for async/await
		// literal := positions.String()
		// if literal == "async" || literal == "await" {
		// 	if scanner.asyncDef {
		// 		switch literal {
		// 		case "async":
		// 			return positions.AsToken(token.ASYNC)
		// 		case "await":
		// 			return positions.AsToken(token.AWAIT)
		// 		}
		// 	} else if literal == "async" {
		// 		nextToken := scanner.NextToken()
		// 		if nextToken.ID == token.NAME && nextToken.Literal == "def" {
		// 			scanner.asyncDef = true
		// 			return positions.AsToken(token.ASYNC)
		// 		}
		// 	}
		// }

		return positions.AsToken(token.NAME)
	case ch == '\n':
		scanner.atBol = true
		return positions.AsToken(token.NEWLINE)
	case ch == '.':
		pos2 := scanner.nextPosition()
		if IsDigit(pos2.Char) {
			positions.Append(pos2)
			return scanner.parseNumber(positions, pos2.Char)
		} else if pos2.Char == '.' {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			if pos3.Char == '.' {
				positions.Append(pos3)
				return positions.AsToken(token.ELLIPSIS)
			}
			scanner.unreadPosition(pos3)
		}
		scanner.unreadPosition(pos2)

		return positions.AsToken(token.DOT)
	case IsDigit(ch):
		// Parse Number
		return scanner.parseNumber(positions, ch)
	case IsQuote(ch):
		// Parse String
		return scanner.parseQuoted(positions, ch)
	case ch == '\\':
		// Parse Continuation
	default:
		// Two and Three character operators
		pos2 := scanner.nextPosition()
		op2Id := GetTwoCharTokenID(pos.Char, pos2.Char)
		if op2Id != token.OP {
			positions.Append(pos2)
			pos3 := scanner.nextPosition()
			op3Id := GetThreeCharTokenID(pos.Char, pos2.Char, pos3.Char)
			if op3Id != token.OP {
				positions.Append(pos3)
				return positions.AsToken(op3Id)
			}
			scanner.unreadPosition(pos3)
			return positions.AsToken(op2Id)
		}
		scanner.unreadPosition(pos2)
	}
	switch pos.Char {
	case '(', '[', '{':
		// Increment indentation level
		// scanner.indentationLevel++
		break
	case ')', ']', '}':
		// Decrement indentation level
		// scanner.indentationLevel--
		break
	}

	opId := GetOneCharTokenID(pos.Char)
	return positions.AsToken(opId)
}
<|endoftext|>


<commit_before>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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 go_kafka_client

import (
	avro "github.com/stealthly/go-avro"
)

type SchemaRegistryClient interface {
	Register(subject string, schema avro.Schema) int32
	GetByID(id int32) avro.Schema
	GetLatestSchemaMetadata(subject string) SchemaMetadata
	GetVersion(subject string, schema avro.Schema)
}

type SchemaMetadata struct {
	Id int32
	Version int32
	Schema string
}
<commit_msg>updated schemametadata to be pointer in schemaregistry client interface<commit_after>/* Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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 go_kafka_client

import (
	avro "github.com/stealthly/go-avro"
)

type SchemaRegistryClient interface {
	Register(subject string, schema avro.Schema) int32
	GetByID(id int32) avro.Schema
	GetLatestSchemaMetadata(subject string) *SchemaMetadata
	GetVersion(subject string, schema avro.Schema)
}

type SchemaMetadata struct {
	Id int32
	Version int32
	Schema string
}
<|endoftext|>


<commit_before>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// Version stores current Goby version
const Version = "0.1.2"

// These are the enums for marking parser's mode, which decides whether it should pop unused values.
const (
	NormalMode int = iota
	REPLMode
	TestMode
)

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

type errorMessage = string

var standardLibraries = map[string]func(*VM){
	"file":              initFileClass,
	"net/http":          initHTTPClass,
	"net/simple_server": initSimpleServerClass,
	"uri":               initURIClass,
	"db":                initDBClass,
	"plugin":            initPluginClass,
	"json":              initJSONClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  *thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string
	// projectRoot is goby root's absolute path, which is $GOROOT/src/github.com/goby-lang/goby
	projectRoot string

	stackTraceCount int

	channelObjectMap *objectMap

	sync.Mutex

	mode int
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread = vm.newThread()

	vm.initConstants()
	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		vm.projectRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s/e", Version)

		_, err := os.Stat(vm.projectRoot)

		if err != nil {
			path, _ := filepath.Abs("$GOPATH/src/github.com/goby-lang/goby/e")
			_, err = os.Stat(path)

			if err != nil {
				e = fmt.Errorf("You haven't set $GOBY_ROOT properly")
				return nil, e
			}

			vm.projectRoot = path
		}
	} else {
		vm.projectRoot = gobyRoot
	}

	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	return
}

func (vm *VM) newThread() *thread {
	s := &stack{RWMutex: new(sync.RWMutex)}
	cfs := &callFrameStack{callFrames: []*callFrame{}}
	t := &thread{stack: s, callFrameStack: cfs, sp: 0, cfp: 0}
	s.thread = t
	cfs.thread = t
	t.vm = vm
	return t
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	p := newInstructionTranslator(fn)
	p.vm = vm
	p.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range p.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[p.filename] = p.blockTable
	vm.SetClassISIndexTable(p.filename)
	vm.SetMethodISIndexTable(p.filename)

	cf := newCallFrame(p.program)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)
	vm.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.toString()), false)
	singletonClass.Methods.set("include", vm.topLevelClass(classClass).lookupMethod("include"))
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	cClass := initClassClass()
	vm.objectClass = initObjectClass(cClass)
	vm.topLevelClass(objectClass).setClassConstant(cClass)

	builtInClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
	}

	vm.initErrorClasses()

	for _, c := range builtInClasses {
		vm.objectClass.setClassConstant(c)
	}

	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.initStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.initArrayObject(args)}

	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.initStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.initHashObject(envs)}
}

func (vm *VM) topLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == objectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

// Start evaluation from top most call frame
func (vm *VM) startFromTopFrame() {
	vm.mainThread.startFromTopFrame()
}

func (vm *VM) currentFilePath() string {
	return string(vm.mainThread.callFrameStack.top().instructionSet.filename)
}

func (vm *VM) getBlock(name string, filename filename) *instructionSet {
	// The "name" here is actually an index of block
	// for example <Block:1>'s name is "1"
	is, ok := vm.blockTables[filename][name]

	if !ok {
		panic(fmt.Sprintf("Can't find block %s", name))
	}

	return is
}

func (vm *VM) getMethodIS(name string, filename filename) (*instructionSet, bool) {
	iss, ok := vm.isTables[bytecode.MethodDef][name]

	if !ok {
		return nil, false
	}

	is := iss[vm.methodISIndexTables[filename].Data[name]]

	vm.methodISIndexTables[filename].Data[name]++

	return is, ok
}

func (vm *VM) getClassIS(name string, filename filename) *instructionSet {
	iss, ok := vm.isTables[bytecode.ClassDef][name]

	if !ok {
		panic(fmt.Sprintf("Can't find class %s's instructions", name))
	}

	is := iss[vm.classISIndexTables[filename].Data[name]]

	vm.classISIndexTables[filename].Data[name]++

	return is
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		c = vm.initializeClass(name, isModule)
		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf *callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstant(constName, true)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstant(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == objectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}

func (vm *VM) execGobyLib(libName string) {
	libPath := filepath.Join(vm.projectRoot, "lib", libName)
	file, err := ioutil.ReadFile(libPath)

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

	vm.execRequiredFile(libPath, file)
}

func (vm *VM) execRequiredFile(filepath string, file []byte) {
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		fmt.Println(err.Error())
		return
	}

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

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

	for name, is := range vm.isTables[bytecode.ClassDef] {
		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.ExecInstructions(instructionSets, filepath)

	// Restore instruction sets.
	vm.isTables[bytecode.MethodDef] = oldMethodTable
	vm.isTables[bytecode.ClassDef] = oldClassTable
}

func newError(format string, args ...interface{}) *Error {
	return &Error{Message: fmt.Sprintf(format, args...)}
}
<commit_msg>Fix GOBY_ROOT issue.<commit_after>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/parser"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
)

// Version stores current Goby version
const Version = "0.1.2"

// These are the enums for marking parser's mode, which decides whether it should pop unused values.
const (
	NormalMode int = iota
	REPLMode
	TestMode
)

type isIndexTable struct {
	Data map[string]int
}

func newISIndexTable() *isIndexTable {
	return &isIndexTable{Data: make(map[string]int)}
}

type isTable map[string][]*instructionSet

type filename = string

type errorMessage = string

var standardLibraries = map[string]func(*VM){
	"file":              initFileClass,
	"net/http":          initHTTPClass,
	"net/simple_server": initSimpleServerClass,
	"uri":               initURIClass,
	"db":                initDBClass,
	"plugin":            initPluginClass,
	"json":              initJSONClass,
}

// VM represents a stack based virtual machine.
type VM struct {
	mainObj     *RObject
	mainThread  *thread
	objectClass *RClass
	// a map holds different types of instruction set tables
	isTables map[setType]isTable
	// method instruction set table
	methodISIndexTables map[filename]*isIndexTable
	// class instruction set table
	classISIndexTables map[filename]*isIndexTable
	// block instruction set table
	blockTables map[filename]map[string]*instructionSet
	// fileDir indicates executed file's directory
	fileDir string
	// args are command line arguments
	args []string
	// projectRoot is goby root's absolute path, which is $GOROOT/src/github.com/goby-lang/goby
	projectRoot string

	stackTraceCount int

	channelObjectMap *objectMap

	sync.Mutex

	mode int
}

// New initializes a vm to initialize state and returns it.
func New(fileDir string, args []string) (vm *VM, e error) {
	vm = &VM{args: args}
	vm.mainThread = vm.newThread()

	vm.initConstants()
	vm.methodISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.classISIndexTables = map[filename]*isIndexTable{
		fileDir: newISIndexTable(),
	}
	vm.blockTables = make(map[filename]map[string]*instructionSet)
	vm.isTables = map[setType]isTable{
		bytecode.MethodDef: make(isTable),
		bytecode.ClassDef:  make(isTable),
	}
	vm.fileDir = fileDir

	gobyRoot := os.Getenv("GOBY_ROOT")

	if len(gobyRoot) == 0 {
		vm.projectRoot = fmt.Sprintf("/usr/local/Cellar/goby/%s", Version)

		_, err := os.Stat(vm.projectRoot)

		if err != nil {
			path, _ := filepath.Abs("$GOPATH/src/github.com/goby-lang/goby")
			_, err = os.Stat(path)

			if err != nil {
				e = fmt.Errorf("You haven't set $GOBY_ROOT properly")
				return nil, e
			}

			vm.projectRoot = path
		}
	} else {
		vm.projectRoot = gobyRoot
	}

	vm.mainObj = vm.initMainObj()
	vm.channelObjectMap = &objectMap{store: &sync.Map{}}

	return
}

func (vm *VM) newThread() *thread {
	s := &stack{RWMutex: new(sync.RWMutex)}
	cfs := &callFrameStack{callFrames: []*callFrame{}}
	t := &thread{stack: s, callFrameStack: cfs, sp: 0, cfp: 0}
	s.thread = t
	cfs.thread = t
	t.vm = vm
	return t
}

// ExecInstructions accepts a sequence of bytecodes and use vm to evaluate them.
func (vm *VM) ExecInstructions(sets []*bytecode.InstructionSet, fn string) {
	p := newInstructionTranslator(fn)
	p.vm = vm
	p.transferInstructionSets(sets)

	// Keep instruction set table updated after parsed new files.
	// TODO: Find more efficient way to do this.
	for setType, table := range p.setTable {
		for name, is := range table {
			vm.isTables[setType][name] = is
		}
	}

	vm.blockTables[p.filename] = p.blockTable
	vm.SetClassISIndexTable(p.filename)
	vm.SetMethodISIndexTable(p.filename)

	cf := newCallFrame(p.program)
	cf.self = vm.mainObj
	vm.mainThread.callFrameStack.push(cf)
	vm.startFromTopFrame()
}

// SetClassISIndexTable adds new instruction set's index table to vm.classISIndexTables
func (vm *VM) SetClassISIndexTable(fn filename) {
	vm.classISIndexTables[fn] = newISIndexTable()
}

// SetMethodISIndexTable adds new instruction set's index table to vm.methodISIndexTables
func (vm *VM) SetMethodISIndexTable(fn filename) {
	vm.methodISIndexTables[fn] = newISIndexTable()
}

func (vm *VM) initMainObj() *RObject {
	obj := vm.objectClass.initializeInstance()
	singletonClass := vm.initializeClass(fmt.Sprintf("#<Class:%s>", obj.toString()), false)
	singletonClass.Methods.set("include", vm.topLevelClass(classClass).lookupMethod("include"))
	obj.singletonClass = singletonClass

	return obj
}

func (vm *VM) initConstants() {
	cClass := initClassClass()
	vm.objectClass = initObjectClass(cClass)
	vm.topLevelClass(objectClass).setClassConstant(cClass)

	builtInClasses := []*RClass{
		vm.initIntegerClass(),
		vm.initStringClass(),
		vm.initBoolClass(),
		vm.initNullClass(),
		vm.initArrayClass(),
		vm.initHashClass(),
		vm.initRangeClass(),
		vm.initMethodClass(),
		vm.initChannelClass(),
		vm.initGoClass(),
	}

	vm.initErrorClasses()

	for _, c := range builtInClasses {
		vm.objectClass.setClassConstant(c)
	}

	args := []Object{}

	for _, arg := range vm.args {
		args = append(args, vm.initStringObject(arg))
	}

	vm.objectClass.constants["ARGV"] = &Pointer{Target: vm.initArrayObject(args)}

	envs := map[string]Object{}

	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		envs[pair[0]] = vm.initStringObject(pair[1])
	}

	vm.objectClass.constants["ENV"] = &Pointer{Target: vm.initHashObject(envs)}
}

func (vm *VM) topLevelClass(cn string) *RClass {
	objClass := vm.objectClass

	if cn == objectClass {
		return objClass
	}

	return objClass.constants[cn].Target.(*RClass)
}

// Start evaluation from top most call frame
func (vm *VM) startFromTopFrame() {
	vm.mainThread.startFromTopFrame()
}

func (vm *VM) currentFilePath() string {
	return string(vm.mainThread.callFrameStack.top().instructionSet.filename)
}

func (vm *VM) getBlock(name string, filename filename) *instructionSet {
	// The "name" here is actually an index of block
	// for example <Block:1>'s name is "1"
	is, ok := vm.blockTables[filename][name]

	if !ok {
		panic(fmt.Sprintf("Can't find block %s", name))
	}

	return is
}

func (vm *VM) getMethodIS(name string, filename filename) (*instructionSet, bool) {
	iss, ok := vm.isTables[bytecode.MethodDef][name]

	if !ok {
		return nil, false
	}

	is := iss[vm.methodISIndexTables[filename].Data[name]]

	vm.methodISIndexTables[filename].Data[name]++

	return is, ok
}

func (vm *VM) getClassIS(name string, filename filename) *instructionSet {
	iss, ok := vm.isTables[bytecode.ClassDef][name]

	if !ok {
		panic(fmt.Sprintf("Can't find class %s's instructions", name))
	}

	is := iss[vm.classISIndexTables[filename].Data[name]]

	vm.classISIndexTables[filename].Data[name]++

	return is
}

// loadConstant makes sure we don't create a class twice.
func (vm *VM) loadConstant(name string, isModule bool) *RClass {
	var c *RClass
	var ptr *Pointer

	ptr = vm.objectClass.constants[name]

	if ptr == nil {
		c = vm.initializeClass(name, isModule)
		vm.objectClass.setClassConstant(c)
	} else {
		c = ptr.Target.(*RClass)
	}

	return c
}

func (vm *VM) lookupConstant(cf *callFrame, constName string) (constant *Pointer) {
	var namespace *RClass
	var hasNamespace bool

	top := vm.mainThread.stack.top()

	if top == nil {
		hasNamespace = false
	} else {
		namespace, hasNamespace = top.Target.(*RClass)
	}

	if hasNamespace {
		constant = namespace.lookupConstant(constName, true)

		if constant != nil {
			return
		}
	}

	constant = cf.lookupConstant(constName)

	if constant == nil {
		constant = vm.objectClass.constants[constName]
	}

	if constName == objectClass {
		constant = &Pointer{Target: vm.objectClass}
	}

	return
}

func (vm *VM) execGobyLib(libName string) {
	libPath := filepath.Join(vm.projectRoot, "lib", libName)
	file, err := ioutil.ReadFile(libPath)

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

	vm.execRequiredFile(libPath, file)
}

func (vm *VM) execRequiredFile(filepath string, file []byte) {
	instructionSets, err := compiler.CompileToInstructions(string(file), parser.NormalMode)

	if err != nil {
		fmt.Println(err.Error())
		return
	}

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

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

	for name, is := range vm.isTables[bytecode.ClassDef] {
		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.ExecInstructions(instructionSets, filepath)

	// Restore instruction sets.
	vm.isTables[bytecode.MethodDef] = oldMethodTable
	vm.isTables[bytecode.ClassDef] = oldClassTable
}

func newError(format string, args ...interface{}) *Error {
	return &Error{Message: fmt.Sprintf(format, args...)}
}
<|endoftext|>