Exemple #1
0
func copyTableToMap(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} {
	if t == nil {
		t = tmap
	}
	te, tk := t.Elem(), t.Key()
	m := reflect.MakeMap(t)

	// See copyTableToSlice.
	ptr := L.ToPointer(idx)
	if !luaIsEmpty(L, idx) {
		visited[ptr] = m.Interface()
	}

	L.PushNil()
	if idx < 0 {
		idx--
	}
	for L.Next(idx) != 0 {
		// key at -2, value at -1
		key := reflect.ValueOf(luaToGo(L, tk, -2, visited))
		val := reflect.ValueOf(luaToGo(L, te, -1, visited))
		if val.Interface() == nullv.Interface() {
			val = reflect.Zero(te)
		}
		m.SetMapIndex(key, val)
		L.Pop(1)
	}
	return m.Interface()
}
Exemple #2
0
func copyTableToStruct(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} {
	if t == nil {
		RaiseError(L, "type argument must be non-nill")
	}
	wasPtr := t.Kind() == reflect.Ptr
	if wasPtr {
		t = t.Elem()
	}
	s := reflect.New(t) // T -> *T
	ref := s.Elem()

	// See copyTableToSlice.
	ptr := L.ToPointer(idx)
	if !luaIsEmpty(L, idx) {
		if wasPtr {
			visited[ptr] = s.Interface()
		} else {
			visited[ptr] = s.Elem().Interface()
		}
	}

	// Associate Lua keys with Go fields: tags have priority over matching field
	// name.
	fields := map[string]string{}
	st := ref.Type()
	for i := 0; i < ref.NumField(); i++ {
		field := st.Field(i)
		tag := field.Tag.Get("lua")
		if tag != "" {
			fields[tag] = field.Name
			continue
		}
		fields[field.Name] = field.Name
	}

	L.PushNil()
	if idx < 0 {
		idx--
	}
	for L.Next(idx) != 0 {
		key := L.ToString(-2)
		f := ref.FieldByName(fields[key])
		if f.CanSet() && f.IsValid() {
			val := reflect.ValueOf(luaToGo(L, f.Type(), -1, visited))
			f.Set(val)
		}
		L.Pop(1)
	}
	if wasPtr {
		return s.Interface()
	}
	return s.Elem().Interface()
}
Exemple #3
0
// Also for arrays.
func copyTableToSlice(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} {
	if t == nil {
		t = tslice
	}

	ref := t
	// There is probably no point at accepting more than one level of dreference.
	if t.Kind() == reflect.Ptr {
		t = t.Elem()
	}

	n := int(L.ObjLen(idx))

	var slice reflect.Value
	if t.Kind() == reflect.Array {
		slice = reflect.New(t)
		slice = slice.Elem()
	} else {
		slice = reflect.MakeSlice(t, n, n)
	}

	// Do not add empty slices to the list of visited elements.
	// The empty Lua table is a single instance object and gets re-used across maps, slices and others.
	if n > 0 {
		ptr := L.ToPointer(idx)
		if ref.Kind() == reflect.Ptr {
			visited[ptr] = slice.Addr().Interface()
		} else {
			visited[ptr] = slice.Interface()
		}
	}

	te := t.Elem()
	for i := 1; i <= n; i++ {
		L.RawGeti(idx, i)
		val := reflect.ValueOf(luaToGo(L, te, -1, visited))
		if val.Interface() == nullv.Interface() {
			val = reflect.Zero(te)
		}
		slice.Index(i - 1).Set(val)
		L.Pop(1)
	}

	if ref.Kind() == reflect.Ptr {
		return slice.Addr().Interface()
	}
	return slice.Interface()
}
Exemple #4
0
func luaToGo(L *lua.State, t reflect.Type, idx int, visited map[uintptr]interface{}) interface{} {
	var value interface{}

	var kind reflect.Kind
	if t != nil {
		if t.Kind() == reflect.Ptr {
			kind = t.Elem().Kind()
		} else if t.Kind() == reflect.Interface {
			// Let the Lua type drive the conversion.
			t = nil
		} else {
			kind = t.Kind()
		}
	}

	switch L.Type(idx) {
	case lua.LUA_TNIL:
		if t == nil {
			return nil
		}
		switch kind {
		default:
			value = reflect.Zero(t).Interface()
		}
	case lua.LUA_TBOOLEAN:
		if t == nil {
			kind = reflect.Bool
		}
		switch kind {
		case reflect.Bool:
			ptr := new(bool)
			*ptr = L.ToBoolean(idx)
			value = *ptr
		default:
			value = reflect.Zero(t).Interface()
		}
	case lua.LUA_TSTRING:
		if t == nil {
			kind = reflect.String
		}
		switch kind {
		case reflect.String:
			tos := L.ToString(idx)
			ptr := new(string)
			*ptr = tos
			value = *ptr
		default:
			value = reflect.Zero(t).Interface()
		}
	case lua.LUA_TNUMBER:
		if t == nil {
			// Infering the type here (e.g. int if round value) would break backward
			// compatibility and drift away from Lua's design: the numeric type is
			// specified at compile time.
			kind = reflect.Float64
		}
		switch kind {
		case reflect.Float64:
			ptr := new(float64)
			*ptr = L.ToNumber(idx)
			value = *ptr
		case reflect.Float32:
			ptr := new(float32)
			*ptr = float32(L.ToNumber(idx))
			value = *ptr
		case reflect.Int:
			ptr := new(int)
			*ptr = int(L.ToNumber(idx))
			value = *ptr
		case reflect.Int8:
			ptr := new(int8)
			*ptr = int8(L.ToNumber(idx))
			value = *ptr
		case reflect.Int16:
			ptr := new(int16)
			*ptr = int16(L.ToNumber(idx))
			value = *ptr
		case reflect.Int32:
			ptr := new(int32)
			*ptr = int32(L.ToNumber(idx))
			value = *ptr
		case reflect.Int64:
			ptr := new(int64)
			*ptr = int64(L.ToNumber(idx))
			value = *ptr
		case reflect.Uint:
			ptr := new(uint)
			*ptr = uint(L.ToNumber(idx))
			value = *ptr
		case reflect.Uint8:
			ptr := new(uint8)
			*ptr = uint8(L.ToNumber(idx))
			value = *ptr
		case reflect.Uint16:
			ptr := new(uint16)
			*ptr = uint16(L.ToNumber(idx))
			value = *ptr
		case reflect.Uint32:
			ptr := new(uint32)
			*ptr = uint32(L.ToNumber(idx))
			value = *ptr
		case reflect.Uint64:
			ptr := new(uint64)
			*ptr = uint64(L.ToNumber(idx))
			value = *ptr
		default:
			value = reflect.Zero(t).Interface()
		}
	case lua.LUA_TTABLE:
		if t == nil {
			kind = reflect.Interface
		}
		fallthrough
	default:
		istable := L.IsTable(idx)
		// If we don't know the type and the Lua object is userdata,
		// then it might be a proxy for a Go object. Otherwise wrap
		// it up as a LuaObject.
		if t == nil && !istable {
			if isValueProxy(L, idx) {
				v, _ := valueOfProxy(L, idx)
				return v.Interface()
			}
			return NewLuaObject(L, idx)
		}
		switch kind {
		case reflect.Array:
			if istable {
				ptr := L.ToPointer(idx)
				if val, ok := visited[ptr]; ok {
					return val
				}
				value = copyTableToSlice(L, t, idx, visited)
			} else {
				value = mustUnwrapProxy(L, idx)
			}
		case reflect.Slice:
			// if we get a table, then copy its values to a new slice
			if istable {
				ptr := L.ToPointer(idx)
				if val, ok := visited[ptr]; ok {
					return val
				}
				value = copyTableToSlice(L, t, idx, visited)
			} else {
				value = mustUnwrapProxy(L, idx)
			}
		case reflect.Map:
			if istable {
				ptr := L.ToPointer(idx)
				if val, ok := visited[ptr]; ok {
					return val
				}
				value = copyTableToMap(L, t, idx, visited)
			} else {
				value = mustUnwrapProxy(L, idx)
			}
		case reflect.Struct:
			if istable {
				ptr := L.ToPointer(idx)
				if val, ok := visited[ptr]; ok {
					return val
				}
				value = copyTableToStruct(L, t, idx, visited)
			} else {
				value = mustUnwrapProxy(L, idx)
			}
		case reflect.Interface:
			if istable {
				ptr := L.ToPointer(idx)
				if val, ok := visited[ptr]; ok {
					return val
				}
				// We have to make an executive decision here: tables with non-zero
				// length are assumed to be slices!
				if L.ObjLen(idx) > 0 {
					value = copyTableToSlice(L, nil, idx, visited)
				} else {
					value = copyTableToMap(L, nil, idx, visited)
				}
			} else if L.IsNumber(idx) {
				value = L.ToNumber(idx)
			} else if L.IsString(idx) {
				value = L.ToString(idx)
			} else if L.IsBoolean(idx) {
				value = L.ToBoolean(idx)
			} else if L.IsNil(idx) {
				return nil
			} else {
				value = mustUnwrapProxy(L, idx)
			}
		default:
			value = mustUnwrapProxy(L, idx)
		}
	}

	return value
}