github.com/google/grumpy@v0.0.0-20171122020858-3ec87959189c/runtime/tuple.go (about) 1 // Copyright 2016 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package grumpy 16 17 import ( 18 "fmt" 19 "reflect" 20 ) 21 22 // Tuple represents Python 'tuple' objects. 23 // 24 // Tuples are thread safe by virtue of being immutable. 25 type Tuple struct { 26 Object 27 elems []*Object 28 } 29 30 // NewTuple returns a tuple containing the given elements. 31 func NewTuple(elems ...*Object) *Tuple { 32 if len(elems) == 0 { 33 return emptyTuple 34 } 35 return &Tuple{Object: Object{typ: TupleType}, elems: elems} 36 } 37 38 // Below are direct allocation versions of small Tuples. Rather than performing 39 // two allocations, one for the tuple object and one for the slice holding the 40 // elements, we allocate both objects at the same time in one block of memory. 41 // This both decreases the number of allocations overall as well as increases 42 // memory locality for tuple data. Both of which *should* improve time to 43 // allocate as well as read performance. The methods below are used by the 44 // compiler to create fixed size tuples when the size is known ahead of time. 45 // 46 // The number of specializations below were chosen first to cover all the fixed 47 // size tuple allocations in the runtime (currently 5), then filled out to 48 // cover the whole memory size class (see golang/src/runtime/sizeclasses.go for 49 // the table). On a 64bit system, a tuple of length 6 occupies 96 bytes - 48 50 // bytes for the tuple object and 6*8 (48) bytes of pointers. 51 // 52 // If methods are added or removed, then the constant MAX_DIRECT_TUPLE in 53 // compiler/util.py needs to be updated as well. 54 55 // NewTuple0 returns the empty tuple. This is mostly provided for the 56 // convenience of the compiler. 57 func NewTuple0() *Tuple { return emptyTuple } 58 59 // NewTuple1 returns a tuple of length 1 containing just elem0. 60 func NewTuple1(elem0 *Object) *Tuple { 61 t := struct { 62 tuple Tuple 63 elems [1]*Object 64 }{ 65 tuple: Tuple{Object: Object{typ: TupleType}}, 66 elems: [1]*Object{elem0}, 67 } 68 t.tuple.elems = t.elems[:] 69 return &t.tuple 70 } 71 72 // NewTuple2 returns a tuple of length 2 containing just elem0 and elem1. 73 func NewTuple2(elem0, elem1 *Object) *Tuple { 74 t := struct { 75 tuple Tuple 76 elems [2]*Object 77 }{ 78 tuple: Tuple{Object: Object{typ: TupleType}}, 79 elems: [2]*Object{elem0, elem1}, 80 } 81 t.tuple.elems = t.elems[:] 82 return &t.tuple 83 } 84 85 // NewTuple3 returns a tuple of length 3 containing elem0 to elem2. 86 func NewTuple3(elem0, elem1, elem2 *Object) *Tuple { 87 t := struct { 88 tuple Tuple 89 elems [3]*Object 90 }{ 91 tuple: Tuple{Object: Object{typ: TupleType}}, 92 elems: [3]*Object{elem0, elem1, elem2}, 93 } 94 t.tuple.elems = t.elems[:] 95 return &t.tuple 96 } 97 98 // NewTuple4 returns a tuple of length 4 containing elem0 to elem3. 99 func NewTuple4(elem0, elem1, elem2, elem3 *Object) *Tuple { 100 t := struct { 101 tuple Tuple 102 elems [4]*Object 103 }{ 104 tuple: Tuple{Object: Object{typ: TupleType}}, 105 elems: [4]*Object{elem0, elem1, elem2, elem3}, 106 } 107 t.tuple.elems = t.elems[:] 108 return &t.tuple 109 } 110 111 // NewTuple5 returns a tuple of length 5 containing elem0 to elem4. 112 func NewTuple5(elem0, elem1, elem2, elem3, elem4 *Object) *Tuple { 113 t := struct { 114 tuple Tuple 115 elems [5]*Object 116 }{ 117 tuple: Tuple{Object: Object{typ: TupleType}}, 118 elems: [5]*Object{elem0, elem1, elem2, elem3, elem4}, 119 } 120 t.tuple.elems = t.elems[:] 121 return &t.tuple 122 } 123 124 // NewTuple6 returns a tuple of length 6 containing elem0 to elem5. 125 func NewTuple6(elem0, elem1, elem2, elem3, elem4, elem5 *Object) *Tuple { 126 t := struct { 127 tuple Tuple 128 elems [6]*Object 129 }{ 130 tuple: Tuple{Object: Object{typ: TupleType}}, 131 elems: [6]*Object{elem0, elem1, elem2, elem3, elem4, elem5}, 132 } 133 t.tuple.elems = t.elems[:] 134 return &t.tuple 135 } 136 137 func toTupleUnsafe(o *Object) *Tuple { 138 return (*Tuple)(o.toPointer()) 139 } 140 141 // GetItem returns the i'th element of t. Bounds are unchecked and therefore 142 // this method will panic unless 0 <= i < t.Len(). 143 func (t *Tuple) GetItem(i int) *Object { 144 return t.elems[i] 145 } 146 147 // Len returns the number of elements in t. 148 func (t *Tuple) Len() int { 149 return len(t.elems) 150 } 151 152 // ToObject upcasts t to an Object. 153 func (t *Tuple) ToObject() *Object { 154 return &t.Object 155 } 156 157 // TupleType is the object representing the Python 'tuple' type. 158 var TupleType = newBasisType("tuple", reflect.TypeOf(Tuple{}), toTupleUnsafe, ObjectType) 159 160 var emptyTuple = &Tuple{Object: Object{typ: TupleType}} 161 162 func tupleAdd(f *Frame, v, w *Object) (*Object, *BaseException) { 163 if !w.isInstance(TupleType) { 164 return NotImplemented, nil 165 } 166 elems, raised := seqAdd(f, toTupleUnsafe(v).elems, toTupleUnsafe(w).elems) 167 if raised != nil { 168 return nil, raised 169 } 170 return NewTuple(elems...).ToObject(), nil 171 } 172 173 func tupleContains(f *Frame, t, v *Object) (*Object, *BaseException) { 174 return seqContains(f, t, v) 175 } 176 177 func tupleCount(f *Frame, args Args, kwargs KWArgs) (*Object, *BaseException) { 178 if raised := checkMethodArgs(f, "count", args, TupleType, ObjectType); raised != nil { 179 return nil, raised 180 } 181 return seqCount(f, args[0], args[1]) 182 } 183 184 func tupleEq(f *Frame, v, w *Object) (*Object, *BaseException) { 185 return tupleCompare(f, toTupleUnsafe(v), w, Eq) 186 } 187 188 func tupleGE(f *Frame, v, w *Object) (*Object, *BaseException) { 189 return tupleCompare(f, toTupleUnsafe(v), w, GE) 190 } 191 192 func tupleGetItem(f *Frame, o, key *Object) (*Object, *BaseException) { 193 t := toTupleUnsafe(o) 194 item, elems, raised := seqGetItem(f, t.elems, key) 195 if raised != nil { 196 return nil, raised 197 } 198 if item != nil { 199 return item, nil 200 } 201 return NewTuple(elems...).ToObject(), nil 202 } 203 204 func tupleGetNewArgs(f *Frame, args Args, _ KWArgs) (*Object, *BaseException) { 205 if raised := checkMethodArgs(f, "__getnewargs__", args, TupleType); raised != nil { 206 return nil, raised 207 } 208 return NewTuple1(args[0]).ToObject(), nil 209 } 210 211 func tupleGT(f *Frame, v, w *Object) (*Object, *BaseException) { 212 return tupleCompare(f, toTupleUnsafe(v), w, GT) 213 } 214 215 func tupleIter(f *Frame, o *Object) (*Object, *BaseException) { 216 return newSliceIterator(reflect.ValueOf(toTupleUnsafe(o).elems)), nil 217 } 218 219 func tupleLE(f *Frame, v, w *Object) (*Object, *BaseException) { 220 return tupleCompare(f, toTupleUnsafe(v), w, LE) 221 } 222 223 func tupleLen(f *Frame, o *Object) (*Object, *BaseException) { 224 return NewInt(len(toTupleUnsafe(o).elems)).ToObject(), nil 225 } 226 227 func tupleLT(f *Frame, v, w *Object) (*Object, *BaseException) { 228 return tupleCompare(f, toTupleUnsafe(v), w, LT) 229 } 230 231 func tupleMul(f *Frame, v, w *Object) (*Object, *BaseException) { 232 if !w.isInstance(IntType) { 233 return NotImplemented, nil 234 } 235 elems, raised := seqMul(f, toTupleUnsafe(v).elems, toIntUnsafe(w).Value()) 236 if raised != nil { 237 return nil, raised 238 } 239 return NewTuple(elems...).ToObject(), nil 240 } 241 242 func tupleNE(f *Frame, v, w *Object) (*Object, *BaseException) { 243 return tupleCompare(f, toTupleUnsafe(v), w, NE) 244 } 245 246 func tupleNew(f *Frame, t *Type, args Args, _ KWArgs) (*Object, *BaseException) { 247 if t == TupleType && len(args) == 1 && args[0].typ == TupleType { 248 // Tuples are immutable so just return the tuple provided. 249 return args[0], nil 250 } 251 elems, raised := seqNew(f, args) 252 if raised != nil { 253 return nil, raised 254 } 255 tup := toTupleUnsafe(newObject(t)) 256 tup.elems = elems 257 return tup.ToObject(), nil 258 } 259 260 func tupleRepr(f *Frame, o *Object) (*Object, *BaseException) { 261 t := toTupleUnsafe(o) 262 if f.reprEnter(t.ToObject()) { 263 return NewStr("(...)").ToObject(), nil 264 } 265 s, raised := seqRepr(f, t.elems) 266 f.reprLeave(t.ToObject()) 267 if raised != nil { 268 return nil, raised 269 } 270 if len(t.elems) == 1 { 271 s = fmt.Sprintf("(%s,)", s) 272 } else { 273 s = fmt.Sprintf("(%s)", s) 274 } 275 return NewStr(s).ToObject(), nil 276 } 277 278 func tupleRMul(f *Frame, v, w *Object) (*Object, *BaseException) { 279 if !w.isInstance(IntType) { 280 return NotImplemented, nil 281 } 282 elems, raised := seqMul(f, toTupleUnsafe(v).elems, toIntUnsafe(w).Value()) 283 if raised != nil { 284 return nil, raised 285 } 286 return NewTuple(elems...).ToObject(), nil 287 } 288 289 func initTupleType(dict map[string]*Object) { 290 dict["count"] = newBuiltinFunction("count", tupleCount).ToObject() 291 dict["__getnewargs__"] = newBuiltinFunction("__getnewargs__", tupleGetNewArgs).ToObject() 292 TupleType.slots.Add = &binaryOpSlot{tupleAdd} 293 TupleType.slots.Contains = &binaryOpSlot{tupleContains} 294 TupleType.slots.Eq = &binaryOpSlot{tupleEq} 295 TupleType.slots.GE = &binaryOpSlot{tupleGE} 296 TupleType.slots.GetItem = &binaryOpSlot{tupleGetItem} 297 TupleType.slots.GT = &binaryOpSlot{tupleGT} 298 TupleType.slots.Iter = &unaryOpSlot{tupleIter} 299 TupleType.slots.LE = &binaryOpSlot{tupleLE} 300 TupleType.slots.Len = &unaryOpSlot{tupleLen} 301 TupleType.slots.LT = &binaryOpSlot{tupleLT} 302 TupleType.slots.Mul = &binaryOpSlot{tupleMul} 303 TupleType.slots.NE = &binaryOpSlot{tupleNE} 304 TupleType.slots.New = &newSlot{tupleNew} 305 TupleType.slots.Repr = &unaryOpSlot{tupleRepr} 306 TupleType.slots.RMul = &binaryOpSlot{tupleRMul} 307 } 308 309 func tupleCompare(f *Frame, v *Tuple, w *Object, cmp binaryOpFunc) (*Object, *BaseException) { 310 if !w.isInstance(TupleType) { 311 return NotImplemented, nil 312 } 313 return seqCompare(f, v.elems, toTupleUnsafe(w).elems, cmp) 314 }