From f8a1e5f4491b8206b23f351721c9b10e37526c8c Mon Sep 17 00:00:00 2001 From: Konstantin Yegupov Date: Wed, 30 Jan 2019 23:22:29 +1000 Subject: [PATCH] interp: support map literals with integer keys --- interp/frame.go | 7 ++++++- interp/values.go | 32 ++++++++++++++++++++++++++++++++ testdata/map.go | 4 ++++ testdata/map.txt | 2 ++ 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/interp/frame.go b/interp/frame.go index b52dfa1f..ef0b128e 100644 --- a/interp/frame.go +++ b/interp/frame.go @@ -244,13 +244,18 @@ func (fr *frame) evalBasicBlock(bb, incoming llvm.BasicBlock, indent string) (re case callee.Name() == "runtime.hashmapStringSet": // set a string key in the map m := fr.getLocal(inst.Operand(0)).(*MapValue) + // "key" is a Go string value, which in the TinyGo calling convention is split up + // into separate pointer and length parameters. keyBuf := fr.getLocal(inst.Operand(1)) keyLen := fr.getLocal(inst.Operand(2)) valPtr := fr.getLocal(inst.Operand(3)) m.PutString(keyBuf, keyLen, valPtr) case callee.Name() == "runtime.hashmapBinarySet": // set a binary (int etc.) key in the map - // TODO: unimplemented + m := fr.getLocal(inst.Operand(0)).(*MapValue) + keyBuf := fr.getLocal(inst.Operand(1)) + valPtr := fr.getLocal(inst.Operand(2)) + m.PutBinary(keyBuf, valPtr) case callee.Name() == "runtime.stringConcat": // adding two strings together buf1Ptr := fr.getLocal(inst.Operand(0)) diff --git a/interp/values.go b/interp/values.go index 97c414c0..c4f6e302 100644 --- a/interp/values.go +++ b/interp/values.go @@ -561,6 +561,38 @@ func (v *MapValue) PutString(keyBuf, keyLen, valPtr Value) { v.Values = append(v.Values, &LocalValue{v.Eval, value}) } +// PutBinary does a map assign operation. +func (v *MapValue) PutBinary(keyPtr, valPtr Value) { + if !v.Underlying.IsNil() { + panic("map already created") + } + + var value llvm.Value + switch valPtr := valPtr.(type) { + case *PointerCastValue: + value = valPtr.Underlying.Load() + if v.ValueType.IsNil() { + v.ValueType = value.Type() + if int(v.Eval.TargetData.TypeAllocSize(v.ValueType)) != v.ValueSize { + panic("interp: map store value type has the wrong size") + } + } else { + if value.Type() != v.ValueType { + panic("interp: map store value type is inconsistent") + } + } + default: + panic("interp: todo: handle map value pointer") + } + + key := keyPtr.(*PointerCastValue).Underlying.Load() + v.KeyType = key.Type() + + // TODO: avoid duplicate keys + v.Keys = append(v.Keys, &LocalValue{v.Eval, key}) + v.Values = append(v.Values, &LocalValue{v.Eval, value}) +} + // Get FNV-1a hash of this string. // // https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function#FNV-1a_hash diff --git a/testdata/map.go b/testdata/map.go index 66699390..5b97bf5d 100644 --- a/testdata/map.go +++ b/testdata/map.go @@ -15,6 +15,7 @@ var testmap2 = map[string]int{ "eleven": 11, "twelve": 12, } +var testmapIntInt = map[int]int{1: 1, 2: 4, 3: 9} func main() { m := map[string]int{"answer": 42, "foo": 3} @@ -31,6 +32,9 @@ func main() { var nilmap map[string]int println(m == nil, m != nil, len(m)) println(nilmap == nil, nilmap != nil, len(nilmap)) + println(testmapIntInt[2]) + testmapIntInt[2] = 42 + println(testmapIntInt[2]) } func readMap(m map[string]int, key string) { diff --git a/testdata/map.txt b/testdata/map.txt index 31ea290f..a11e421c 100644 --- a/testdata/map.txt +++ b/testdata/map.txt @@ -50,3 +50,5 @@ lookup with comma-ok: eight 8 true lookup with comma-ok: nokey 0 false false true 2 true false 0 +4 +42