diff --git a/bbq/bytecode_printer.go b/bbq/bytecode_printer.go index 2a547f0ab..6cc2a63a1 100644 --- a/bbq/bytecode_printer.go +++ b/bbq/bytecode_printer.go @@ -37,6 +37,8 @@ type BytecodePrinter struct { func (p *BytecodePrinter) PrintProgram(program *Program) string { p.printImports(program.Imports) p.printConstantPool(program.Constants) + p.printTypePool(program.Types) + for _, function := range program.Functions { p.printFunction(function) p.stringBuilder.WriteRune('\n') @@ -73,20 +75,10 @@ func (p *BytecodePrinter) printCode(codes []byte) { p.stringBuilder.WriteString(" " + fmt.Sprint(operand)) case opcode.New: - var kind int + var kind, typeIndex int kind, i = p.getIntOperand(codes, i) - - var location common.Location - location, i = p.getLocation(codes, i) - - var typeName string - typeName, i = p.getStringOperand(codes, i) - - if location != nil { - typeName = string(location.TypeID(nil, typeName)) - } - - p.stringBuilder.WriteString(" " + fmt.Sprint(kind) + " " + typeName) + typeIndex, i = p.getIntOperand(codes, i) + p.stringBuilder.WriteString(" " + fmt.Sprint(kind) + " " + fmt.Sprint(typeIndex)) case opcode.Cast: var typeIndex int @@ -190,6 +182,26 @@ func (p *BytecodePrinter) printConstantPool(constants []*Constant) { p.stringBuilder.WriteRune('\n') } +func (p *BytecodePrinter) printTypePool(types [][]byte) { + p.stringBuilder.WriteString("-- Type Pool --\n") + + for index, typeBytes := range types { + dec := interpreter.CBORDecMode.NewByteStreamDecoder(typeBytes) + typeDecoder := interpreter.NewTypeDecoder(dec, nil) + staticType, err := typeDecoder.DecodeStaticType() + if err != nil { + panic(err) + } + + p.stringBuilder.WriteString(fmt.Sprint(index)) + p.stringBuilder.WriteString(" | ") + p.stringBuilder.WriteString(string(staticType.ID())) + p.stringBuilder.WriteRune('\n') + } + + p.stringBuilder.WriteRune('\n') +} + func (p *BytecodePrinter) getLocation(codes []byte, i int) (location common.Location, endIndex int) { locationLen, i := p.getIntOperand(codes, i) locationBytes := codes[i+1 : i+1+locationLen] diff --git a/bbq/compiler/compiler.go b/bbq/compiler/compiler.go index e9bc6581b..6d55df9eb 100644 --- a/bbq/compiler/compiler.go +++ b/bbq/compiler/compiler.go @@ -53,7 +53,7 @@ type Compiler struct { staticTypes [][]byte // Cache alike for staticTypes and constants in the pool. - typesInPool map[sema.Type]uint16 + typesInPool map[sema.TypeID]uint16 constantsInPool map[constantsCacheKey]*constant // TODO: initialize @@ -104,7 +104,7 @@ func NewCompiler( Config: &Config{}, globals: make(map[string]*global), importedGlobals: indexedNativeFunctions, - typesInPool: make(map[sema.Type]uint16), + typesInPool: make(map[sema.TypeID]uint16), constantsInPool: make(map[constantsCacheKey]*constant), compositeTypeStack: &Stack[*sema.CompositeType]{ elements: make([]*sema.CompositeType, 0), @@ -1087,34 +1087,18 @@ func (c *Compiler) compileInitializer(declaration *ast.SpecialFunctionDeclaratio // i.e: `self = New()` enclosingCompositeType := c.compositeTypeStack.top() - locationBytes, err := commons.LocationToBytes(enclosingCompositeType.Location) - if err != nil { - panic(err) - } - - byteSize := 2 + // two bytes for composite kind - 2 + // 2 bytes for location size - len(locationBytes) + // location - 2 + // 2 bytes for type name size - len(enclosingCompositeTypeName) // type name - - args := make([]byte, 0, byteSize) // Write composite kind kindFirst, kindSecond := encodeUint16(uint16(enclosingCompositeType.Kind)) - args = append(args, kindFirst, kindSecond) - - // Write location - locationSizeFirst, locationSizeSecond := encodeUint16(uint16(len(locationBytes))) - args = append(args, locationSizeFirst, locationSizeSecond) - args = append(args, locationBytes...) - // Write composite name - typeNameSizeFirst, typeNameSizeSecond := encodeUint16(uint16(len(enclosingCompositeTypeName))) - args = append(args, typeNameSizeFirst, typeNameSizeSecond) - args = append(args, enclosingCompositeTypeName...) + index := c.getOrAddType(enclosingCompositeType) + typeFirst, typeSecond := encodeUint16(index) - c.emit(opcode.New, args...) + c.emit( + opcode.New, + kindFirst, kindSecond, + typeFirst, typeSecond, + ) if enclosingType.Kind == common.CompositeKindContract { // During contract init, update the global variable with the newly initialized contract value. @@ -1290,7 +1274,7 @@ func (c *Compiler) emitCheckType(targetType sema.Type) { func (c *Compiler) getOrAddType(targetType sema.Type) uint16 { // Optimization: Re-use types in the pool. - index, ok := c.typesInPool[targetType] + index, ok := c.typesInPool[targetType.ID()] if !ok { staticType := interpreter.ConvertSemaToStaticType(c.memoryGauge, targetType) bytes, err := interpreter.StaticTypeToBytes(staticType) @@ -1298,7 +1282,7 @@ func (c *Compiler) getOrAddType(targetType sema.Type) uint16 { panic(err) } index = c.addType(bytes) - c.typesInPool[targetType] = index + c.typesInPool[targetType.ID()] = index } return index } diff --git a/bbq/vm/test/vm_bench_test.go b/bbq/vm/test/vm_bench_test.go index 97adb3b1f..0cdcdf6b6 100644 --- a/bbq/vm/test/vm_bench_test.go +++ b/bbq/vm/test/vm_bench_test.go @@ -169,10 +169,12 @@ func BenchmarkNewStructRaw(b *testing.B) { for i := 0; i < b.N; i++ { for j := 0; j < 1; j++ { structValue := vm.NewCompositeValue( - nil, - "Foo", common.CompositeKindStructure, - common.Address{}, + interpreter.NewCompositeStaticTypeComputeTypeID( + nil, + common.NewAddressLocation(nil, common.ZeroAddress, "Foo"), + "Foo", + ), storage.BasicSlabStorage, ) structValue.SetMember(vmConfig, "id", fieldValue) diff --git a/bbq/vm/test/vm_test.go b/bbq/vm/test/vm_test.go index 41601f527..45adbee04 100644 --- a/bbq/vm/test/vm_test.go +++ b/bbq/vm/test/vm_test.go @@ -269,8 +269,9 @@ func TestNewStruct(t *testing.T) { require.IsType(t, &vm.CompositeValue{}, result) structValue := result.(*vm.CompositeValue) + compositeType := structValue.CompositeType - require.Equal(t, "Foo", structValue.QualifiedIdentifier) + require.Equal(t, "Foo", compositeType.QualifiedIdentifier) require.Equal( t, vm.IntValue{SmallInt: 12}, diff --git a/bbq/vm/value_composite.go b/bbq/vm/value_composite.go index a7e4ae88f..eb9ae3180 100644 --- a/bbq/vm/value_composite.go +++ b/bbq/vm/value_composite.go @@ -29,12 +29,9 @@ import ( ) type CompositeValue struct { - dictionary *atree.OrderedMap - Location common.Location - QualifiedIdentifier string - typeID common.TypeID - staticType StaticType - Kind common.CompositeKind + dictionary *atree.OrderedMap + CompositeType *interpreter.CompositeStaticType + Kind common.CompositeKind } var _ Value = &CompositeValue{} @@ -42,21 +39,25 @@ var _ MemberAccessibleValue = &CompositeValue{} var _ ReferenceTrackedResourceKindedValue = &CompositeValue{} func NewCompositeValue( - location common.Location, - qualifiedIdentifier string, kind common.CompositeKind, - address common.Address, + staticType *interpreter.CompositeStaticType, storage atree.SlabStorage, ) *CompositeValue { + // Newly created values are always on stack. + // Need to 'Transfer' if needed to be stored in an account. + address := common.ZeroAddress + dictionary, err := atree.NewMap( storage, + atree.Address(address), + atree.NewDefaultDigesterBuilder(), interpreter.NewCompositeTypeInfo( nil, - location, - qualifiedIdentifier, + staticType.Location, + staticType.QualifiedIdentifier, kind, ), ) @@ -66,41 +67,28 @@ func NewCompositeValue( } return &CompositeValue{ - QualifiedIdentifier: qualifiedIdentifier, - Location: location, - dictionary: dictionary, - Kind: kind, + CompositeType: staticType, + dictionary: dictionary, + Kind: kind, } } func newCompositeValueFromOrderedMap( dict *atree.OrderedMap, - location common.Location, - qualifiedIdentifier string, + staticType *interpreter.CompositeStaticType, kind common.CompositeKind, ) *CompositeValue { return &CompositeValue{ - dictionary: dict, - Location: location, - QualifiedIdentifier: qualifiedIdentifier, - Kind: kind, + dictionary: dict, + CompositeType: staticType, + Kind: kind, } } func (*CompositeValue) isValue() {} -func (v *CompositeValue) StaticType(memoryGauge common.MemoryGauge) StaticType { - if v.staticType == nil { - // NOTE: Instead of using NewCompositeStaticType, which always generates the type ID, - // use the TypeID accessor, which may return an already computed type ID - v.staticType = interpreter.NewCompositeStaticType( - memoryGauge, - v.Location, - v.QualifiedIdentifier, - v.TypeID(), - ) - } - return v.staticType +func (v *CompositeValue) StaticType(common.MemoryGauge) StaticType { + return v.CompositeType } func (v *CompositeValue) GetMember(config *Config, name string) Value { @@ -164,17 +152,7 @@ func (v *CompositeValue) SlabID() atree.SlabID { } func (v *CompositeValue) TypeID() common.TypeID { - if v.typeID == "" { - location := v.Location - qualifiedIdentifier := v.QualifiedIdentifier - if location == nil { - return common.TypeID(qualifiedIdentifier) - } - - // TODO: TypeID metering - v.typeID = location.TypeID(nil, qualifiedIdentifier) - } - return v.typeID + return v.CompositeType.TypeID } func (v *CompositeValue) IsResourceKinded() bool { @@ -311,20 +289,11 @@ func (v *CompositeValue) Transfer( } if res == nil { - typeInfo := interpreter.NewCompositeTypeInfo( - config.MemoryGauge, - v.Location, - v.QualifiedIdentifier, + res = newCompositeValueFromOrderedMap( + dictionary, + v.CompositeType, v.Kind, ) - res = &CompositeValue{ - dictionary: dictionary, - Location: typeInfo.Location, - QualifiedIdentifier: typeInfo.QualifiedIdentifier, - Kind: typeInfo.Kind, - typeID: v.typeID, - staticType: v.staticType, - } //res.InjectedFields = v.InjectedFields //res.ComputedFields = v.ComputedFields diff --git a/bbq/vm/value_conversions.go b/bbq/vm/value_conversions.go index 9351533dc..69123b727 100644 --- a/bbq/vm/value_conversions.go +++ b/bbq/vm/value_conversions.go @@ -40,8 +40,7 @@ func InterpreterValueToVMValue(storage interpreter.Storage, value interpreter.Va case *interpreter.CompositeValue: return newCompositeValueFromOrderedMap( value.AtreeMap(), - value.Location, - value.QualifiedIdentifier, + value.StaticType(nil).(*interpreter.CompositeStaticType), value.Kind, ) //case interpreter.LinkValue: @@ -112,11 +111,12 @@ func VMValueToInterpreterValue(config *Config, value Value) interpreter.Value { case StringValue: return interpreter.NewUnmeteredStringValue(string(value.Str)) case *CompositeValue: + compositeType := value.CompositeType return interpreter.NewCompositeValueFromAtreeMap( nil, interpreter.CompositeTypeInfo{ - Location: value.Location, - QualifiedIdentifier: value.QualifiedIdentifier, + Location: compositeType.Location, + QualifiedIdentifier: compositeType.QualifiedIdentifier, Kind: value.Kind, }, value.dictionary, diff --git a/bbq/vm/vm.go b/bbq/vm/vm.go index fbe9ff46b..6666b6c8f 100644 --- a/bbq/vm/vm.go +++ b/bbq/vm/vm.go @@ -429,9 +429,10 @@ func opInvokeDynamic(vm *VM) { } compositeValue := receiver.(*CompositeValue) + compositeType := compositeValue.CompositeType - qualifiedFuncName := commons.TypeQualifiedName(compositeValue.QualifiedIdentifier, funcName) - var functionValue = vm.lookupFunction(compositeValue.Location, qualifiedFuncName) + qualifiedFuncName := commons.TypeQualifiedName(compositeType.QualifiedIdentifier, funcName) + var functionValue = vm.lookupFunction(compositeType.Location, qualifiedFuncName) parameterCount := int(functionValue.Function.ParameterCount) arguments := vm.stack[stackHeight-parameterCount:] @@ -455,18 +456,14 @@ func opNew(vm *VM) { compositeKind := common.CompositeKind(kind) // decode location - locationLen := callframe.getUint16() - locationBytes := callframe.function.Code[callframe.ip : callframe.ip+locationLen] - callframe.ip = callframe.ip + locationLen - location := decodeLocation(locationBytes) + staticType := vm.loadType() - typeName := callframe.getString() + // TODO: Support inclusive-range type + compositeStaticType := staticType.(*interpreter.CompositeStaticType) value := NewCompositeValue( - location, - typeName, compositeKind, - common.Address{}, + compositeStaticType, vm.config.Storage, ) vm.push(value)