Index: mojom/mojom_parser/mojom/user_defined_types.go |
diff --git a/mojom/mojom_parser/mojom/user_defined_types.go b/mojom/mojom_parser/mojom/user_defined_types.go |
index 0da6dfcc657bc21e7020a6740065f6be306c3e2a..4daf9796e730780f6c179dd898ccfe0a9a25cb1e 100644 |
--- a/mojom/mojom_parser/mojom/user_defined_types.go |
+++ b/mojom/mojom_parser/mojom/user_defined_types.go |
@@ -80,6 +80,13 @@ type UserDefinedType interface { |
// reachable from this UserDefinedType. This method must only be invoked |
// after type resolution has succeeded. |
FindReachableTypes() []string |
+ |
+ // ComputeFinalData() is invoked on each user-defined type in a MojomDescriptor |
+ // after the resolution and type validation phases have completed successfully. |
+ // The method computes information that is useful for the code generators in the |
+ // backend. Examples include struct field packing data and MinVersion values. |
+ // See computed_data.go. |
+ ComputeFinalData() error |
} |
// This struct is embedded in each of MojomStruct, MojomInterface |
@@ -334,8 +341,13 @@ type MojomStruct struct { |
fieldsByName map[string]*StructField |
FieldsInLexicalOrder []*StructField |
+ |
+ // This is computed after the parsing phase. |
fieldsInOrdinalOrder []*StructField |
+ // This is computed in ComputeVersionInfo which is invoked by ComputeFinalData(). |
+ versionInfo []StructVersion |
+ |
// Used to form an error message in case of a duplicate field name. |
userFacingName string |
} |
@@ -424,6 +436,13 @@ func (s *MojomStruct) FieldsInOrdinalOrder() []*StructField { |
return s.fieldsInOrdinalOrder |
} |
+func (s *MojomStruct) VersionInfo() []StructVersion { |
+ if s.versionInfo == nil { |
+ panic("The method ComputeVersionInfo() must be invoked first.") |
+ } |
+ return s.versionInfo |
+} |
+ |
func (*MojomStruct) Kind() UserDefinedTypeKind { |
return UserDefinedTypeKindStruct |
} |
@@ -499,6 +518,145 @@ func (s *MojomStruct) ComputeFieldOrdinals() error { |
return nil |
} |
+var ErrMinVersionIllformed = errors.New("MinVersion attribute value illformed") |
+var ErrMinVersionOutOfOrder = errors.New("MinVersion attribute value out of order") |
+var ErrMinVersionNotNullable = errors.New("Non-Zero MinVersion attribute value on non-nullable field") |
+ |
+type StructFieldMinVersionError struct { |
+ // The field whose MinVersion is being set. |
+ field *StructField |
+ |
+ // The MinValue of the previous field. Only used for ErrMinVersionOutOfOrder |
+ previousValue uint32 |
+ |
+ // The LiteralValue of the attribute assignment. |
+ // NOTE: We use the following convention: literalValue.token == nil indicates that |
+ // there was no MinVersion attribute on the given field. This can only happen with |
+ // ErrMinVersionOutOfOrder |
+ literalValue LiteralValue |
+ |
+ // The type of error (ErrMinVersionIllfromed, ErrMinVersionOutOfOrder, ErrMinVersionNotNullable) |
+ err error |
+} |
+ |
+// StructFieldMinVersionError implements error. |
+func (e *StructFieldMinVersionError) Error() string { |
+ var message string |
+ var token lexer.Token |
+ switch e.err { |
+ case ErrMinVersionIllformed: |
+ message = fmt.Sprintf("Invalid MinVersion attribute for field %s: %s. "+ |
+ "The value must be a non-negative 32-bit integer value.", |
+ e.field.SimpleName(), e.literalValue) |
+ token = *e.literalValue.token |
+ case ErrMinVersionOutOfOrder: |
+ if e.literalValue.token == nil { |
+ message = fmt.Sprintf("Invalid missing MinVersion for field %s. "+ |
+ "The MinVersion must be non-decreasing as a function of the ordinal. "+ |
+ "This field must have a MinVersion attribute with a value at least %d.", |
+ e.field.SimpleName(), e.previousValue) |
+ token = e.field.NameToken() |
+ } else { |
+ message = fmt.Sprintf("Invalid MinVersion attribute for field %s: %s. "+ |
+ "The MinVersion must be non-decreasing as a function of the ordinal. "+ |
+ "This field's MinVersion must be at least %d.", |
+ e.field.SimpleName(), e.literalValue.token.Text, e.previousValue) |
+ token = *e.literalValue.token |
+ } |
+ case ErrMinVersionNotNullable: |
+ message = fmt.Sprintf("Invalid type for field %s: %s. "+ |
+ "Non-nullable fields are only allowed in version 0 of of a struct. "+ |
+ "This field's MinVersion is %s.", |
+ e.field.SimpleName(), e.field.FieldType.TypeName(), e.literalValue.token.Text) |
+ switch fieldType := e.field.FieldType.(type) { |
+ case *UserTypeRef: |
+ token = fieldType.token |
+ default: |
+ // It would be nice for the green carets in the snippit in the error message to point at |
+ // the type name, but other than for user type refs we don't store that token so |
+ // instead we use the field's name. |
+ token = e.field.NameToken() |
+ } |
+ } |
+ return UserErrorMessage(e.field.OwningFile(), token, message) |
+} |
+ |
+// ComputeVersionInfo is invoked by ComputeFinalData() after the |
+// parsing, resolution and type validation phases. It examines the |MinVersion| |
+// attributes of all of the fields of the struct, validates the values, and |
+// sets up the versionInfo array. |
+func (s *MojomStruct) ComputeVersionInfo() error { |
+ s.versionInfo = make([]StructVersion, 0) |
+ previousMinVersion := uint32(0) |
+ payloadSizeSoFar := uint32(0) |
+ for i, field := range s.fieldsInOrdinalOrder { |
+ value, literalValue, found, ok := field.minVersionAttribute() |
+ if found == false { |
+ if previousMinVersion != 0 { |
+ return &StructFieldMinVersionError{ |
+ field: field, |
+ previousValue: previousMinVersion, |
+ literalValue: MakeStringLiteralValue("", nil), |
+ err: ErrMinVersionOutOfOrder, |
+ } |
+ } |
+ } else { |
+ if !ok { |
+ return &StructFieldMinVersionError{ |
+ field: field, |
+ literalValue: literalValue, |
+ err: ErrMinVersionIllformed, |
+ } |
+ } |
+ if value < previousMinVersion { |
+ return &StructFieldMinVersionError{ |
+ field: field, |
+ previousValue: previousMinVersion, |
+ literalValue: literalValue, |
+ err: ErrMinVersionOutOfOrder, |
+ } |
+ } |
+ } |
+ if value != 0 && !field.FieldType.Nullable() { |
+ return &StructFieldMinVersionError{ |
+ field: field, |
+ literalValue: literalValue, |
+ err: ErrMinVersionNotNullable, |
+ } |
+ } |
+ field.minVersion = int64(value) |
+ if value > previousMinVersion { |
+ s.versionInfo = append(s.versionInfo, StructVersion{ |
+ VersionNumber: previousMinVersion, |
+ NumFields: uint32(i), |
+ NumBytes: payloadSizeSoFar, |
+ }) |
+ previousMinVersion = value |
+ } |
+ // TODO(rudominer) Set payloadSizeSoFar to the payload size if the |
+ // current |field| were the last field. |
+ } |
+ s.versionInfo = append(s.versionInfo, StructVersion{ |
+ VersionNumber: previousMinVersion, |
+ NumFields: uint32(len(s.fieldsInOrdinalOrder)), |
+ NumBytes: payloadSizeSoFar, |
+ }) |
+ |
+ return nil |
+} |
+ |
+// ComputeFieldOffsets is invoked by ComputeFinalData after the |
+// parsing, resolution and type validation phases. It computes the |offset| |
+// and |bit| fields of each struct field. |
+func (s *MojomStruct) ComputeFieldOffsets() error { |
+ // TODO(rudominer) Implement MojomStruct.ComputeFieldOffsets |
+ for _, field := range s.FieldsInLexicalOrder { |
+ field.offset = 0 |
+ field.bit = 0 |
+ } |
+ return nil |
+} |
+ |
func (m MojomStruct) String() string { |
s := fmt.Sprintf("\n---------struct--------------\n") |
s += fmt.Sprintf("%s\n", m.UserDefinedTypeBase) |
@@ -547,14 +705,27 @@ type StructField struct { |
FieldType TypeRef |
DefaultValue ValueRef |
- // TODO(rudominer) Implement struct field offset computation. |
- Offset int32 |
+ |
+ // Computed Data. The values are computed and set in |
+ // See mojom_types.mojom for the meanings. |
+ |
+ // A valid offset is a uint32. We use -1 to indicate unset. |
+ offset int64 |
+ |
+ // A valid |bit| value is a uint8. We use -1 to indicate unset. |
+ bit int16 |
+ |
+ // A valid min version is a unt32. We use -1 to indicate unset. |
+ minVersion int64 |
} |
func NewStructField(declData DeclarationData, fieldType TypeRef, defaultValue ValueRef) *StructField { |
field := StructField{FieldType: fieldType, DefaultValue: defaultValue} |
declData.declaredObject = &field |
field.DeclarationData = declData |
+ field.offset = -1 |
+ field.bit = -1 |
+ field.minVersion = -1 |
return &field |
} |
@@ -600,6 +771,52 @@ func (f *StructField) KindString() string { |
return "field" |
} |
+const minVersionAttributeName = "MinVersion" |
+ |
+// minVersionAttribute() Attempts to return a uint32 corresponding to the "MinVersion" attribute of this field. |
+// If found = false then no such attribute could be found and the rest of the return values should be ignored. |
+// If found = true then |literalValue| is the LiteralValue of the found attribute. In this case: |
+// If ok = false then |literalValue| does not contain a uint32 and |value| should be ignored. |
+// If ok = true then |literalValue| contains the uint32 value in |value|. |
+func (f *StructField) minVersionAttribute() (value uint32, literalValue LiteralValue, found, ok bool) { |
+ if f.attributes == nil { |
+ return 0, LiteralValue{}, false, false |
+ } |
+ for _, attribute := range f.attributes.List { |
+ if attribute.Key == minVersionAttributeName { |
+ value, ok := uint32Value(attribute.Value) |
+ return value, attribute.Value, true, ok |
+ } |
+ } |
+ return 0, LiteralValue{}, false, false |
+} |
+ |
+// MinVersion() returns the computed value of MinVersion for this field. This method should only be invoked |
+// after the method ComputeVersionInfo() has been invoked on the containing MojomStruct and returned a nil error. |
+// This method is different than the minVersionAttribute() method in that it does not just return a value |
+// for fields that have the "MinVersion" attribute explicitly specified. Rather it returns a computed value for |
+// every field after the "MinVersion" attributes for every field have been checked and validated. |
+func (f *StructField) MinVersion() uint32 { |
+ if f.minVersion < 0 { |
+ panic("The method ComputeVersionInfo() must first be invoked for the containing struct.") |
+ } |
+ return uint32(f.minVersion) |
+} |
+ |
+func (f *StructField) Offset() uint32 { |
+ if f.offset < 0 { |
+ panic("The method ComputeFieldOffsets() must first be invoked for the containing struct.") |
+ } |
+ return uint32(f.offset) |
+} |
+ |
+func (f *StructField) Bit() uint8 { |
+ if f.bit < 0 { |
+ panic("The method ComputeFieldOffsets() must first be invoked for the containing struct.") |
+ } |
+ return uint8(f.bit) |
+} |
+ |
func (f StructField) String() string { |
attributeString := "" |
if f.attributes != nil { |
@@ -616,6 +833,13 @@ func (f StructField) String() string { |
return fmt.Sprintf("%s%s %s%s%s", attributeString, f.FieldType, f.simpleName, ordinalString, defaultValueString) |
} |
+// See StructVersion in mojom_types.mojom for a description of the fields. |
+type StructVersion struct { |
+ VersionNumber uint32 |
+ NumFields uint32 |
+ NumBytes uint32 |
+} |
+ |
///////////////////////////////////////////////////////////// |
// Interfaces and Methods |
///////////////////////////////////////////////////////////// |