Index: mojom/mojom_parser/mojom/computed_data.go |
diff --git a/mojom/mojom_parser/mojom/computed_data.go b/mojom/mojom_parser/mojom/computed_data.go |
index 40befafb21c37206ab6e182c811ffb4bcc080905..3edc7f030d20350c2f5be8af9d6a4c716d1c0915 100644 |
--- a/mojom/mojom_parser/mojom/computed_data.go |
+++ b/mojom/mojom_parser/mojom/computed_data.go |
@@ -5,11 +5,16 @@ |
package mojom |
import ( |
+ "container/list" |
"fmt" |
+ "mojom/mojom_parser/utils" |
) |
// ComputeFinalData() should be invoked after Resolve() has completed |
-// successfully. It computes the field packing and version data that will |
+// successfully. It performs the final computations needed in order to |
+// populate all of the fields in the intermediate representation that is |
+// the output of the frontend of the compiler and is consumed by the backend. |
+// An example is the field packing and version data for struct fields that will |
// be used by the code generators. |
func (d *MojomDescriptor) ComputeFinalData() error { |
for _, userDefinedType := range d.TypesByKey { |
@@ -20,35 +25,188 @@ func (d *MojomDescriptor) ComputeFinalData() error { |
return nil |
} |
-func (e *MojomEnum) ComputeFinalData() error { |
- return e.ComputeEnumValueIntegers() |
-} |
+//////////////////////// Structs //////////////////////// |
func (e *MojomStruct) ComputeFinalData() error { |
- if err := e.ComputeVersionInfo(); err != nil { |
+ // Note(rudominer) computeFieldOffsets must be invoked before computeVersionInfo. |
+ if err := e.computeFieldOffsets(); err != nil { |
return err |
} |
- if err := e.ComputeFieldOffsets(); err != nil { |
+ if err := e.computeVersionInfo(); err != nil { |
return err |
} |
return nil |
} |
+// computePadding returns the least non-negative integer |pad| such that |
+// |offset| + |pad| is a multiple of |alignment|. |
+func computePadding(offset, alignment uint32) uint32 { |
+ return (alignment - (offset % alignment)) % alignment |
+} |
+ |
+// computeFieldOffsets is invoked by ComputeFinalData after the |
azani
2016/03/21 20:13:04
Can you document the algorithm in english?
My und
rudominer
2016/03/21 22:35:12
Done.
|
+// parsing, resolution and type validation phases. It computes the |offset| |
+// and |bit| fields of each struct field. This function must be invoked |
+// before computeVersionInfo() because computeVersionInfo() consumes the |
+// |offset| and |size| fields that are computed by this method. |
+func (s *MojomStruct) computeFieldOffsets() error { |
+ fieldsInPackingOrder := list.New() |
+LoopOverFields: |
+ for i, field := range s.fieldsInOrdinalOrder { |
+ if i == 0 { |
+ // Field zero always goes first in packing order. |
+ field.offset = 0 |
+ field.bit = 0 |
+ fieldsInPackingOrder.PushBack(field) |
+ continue |
+ } |
+ |
+ // Search through |fieldsInPackingOrder| for two consecutive fields, |
+ // |before| and |after|, with enough space between them that we may |
+ // insert |field| between |before| and |after|. |
+ before := fieldsInPackingOrder.Front() |
+ if before.Next() != nil { |
+ for after := before.Next(); after != nil; after = after.Next() { |
+ // Tentatively assume that field will fit after |before| |
+ computeTentativeFieldOffset(field, before.Value.(*StructField)) |
+ // Check if it actually fits before |after|. |
+ if field.Offset()+field.FieldType.SerializationSize() <= after.Value.(*StructField).Offset() { |
+ // It fits: insert it. |
+ fieldsInPackingOrder.InsertAfter(field, before) |
+ // We are finished processing this |field|. |
+ continue LoopOverFields |
+ } |
+ // It didn't fit: continue to search for a spot to fit it. |
+ before = after |
+ } |
+ } |
+ // If we are here then we did not find a hole and so |field| should be inserted |
+ // at the end. |
+ computeTentativeFieldOffset(field, before.Value.(*StructField)) |
+ fieldsInPackingOrder.PushBack(field) |
+ } |
+ return nil |
+} |
+ |
+// computeTentativeFieldOffset computes and sets the |offset| and |bit| fields |
+// of |field|, assuming it will fit right after |previousField| in packing |
+// order. The setting is only tentative because it may turn out it does not fit |
+// before the field that is currently following |previousField| in packing order, |
+// in which case |field| needs to be moved forward to a different location. |
+// The |offset| and |bit| fields of |previousField| must already be set. |
+func computeTentativeFieldOffset(field, previousField *StructField) { |
+ if previousField.offset < 0 { |
+ panic("previousField.offset must be set first.") |
+ } |
+ if isBoolField(field) && isBoolField(previousField) && previousField.bit < 7 { |
+ field.offset = previousField.offset |
+ field.bit = previousField.bit + 1 |
+ return |
+ } |
+ offset := previousField.Offset() + previousField.FieldType.SerializationSize() |
+ alignment := field.FieldType.SerializationAlignment() |
+ field.offset = int64(offset + computePadding(offset, alignment)) |
+ field.bit = 0 |
+} |
+ |
+func isBoolField(field *StructField) bool { |
+ simpleType, ok := field.FieldType.(SimpleType) |
+ if ok { |
+ return simpleType == SimpleTypeBool |
+ } |
+ return false |
+} |
+ |
+// computeVersionInfo is invoked by ComputeFinalData() after the |
+// computeFieldOffsets(). 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 { |
azani
2016/03/21 20:13:04
Can you check here that the offsets have been comp
rudominer
2016/03/21 22:35:12
field offsets are initialized to -1 so it is easy
azani
2016/03/21 23:05:39
Maybe document that at the top of this function th
rudominer
2016/03/21 23:39:24
Done.
|
+ 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 |
+ } |
+ // Taking a max here is necessary since the ordinal order is not the same as packing order. |
+ payloadSizeSoFar = utils.MaximumUint32(payloadSizeSoFar, computePayloadSizeSoFar(field)) |
+ } |
+ s.versionInfo = append(s.versionInfo, StructVersion{ |
+ VersionNumber: previousMinVersion, |
+ NumFields: uint32(len(s.fieldsInOrdinalOrder)), |
+ NumBytes: payloadSizeSoFar, |
+ }) |
+ |
+ return nil |
+} |
+ |
+const structHeaderSize = uint32(8) |
+ |
+func computePayloadSizeSoFar(field *StructField) uint32 { |
+ fieldEndOffset := field.Offset() + field.FieldType.SerializationSize() |
+ return structHeaderSize + fieldEndOffset + computePadding(fieldEndOffset, uint32(8)) |
+} |
+ |
+//////////////////////// Interfaces //////////////////////// |
+ |
func (i *MojomInterface) ComputeFinalData() error { |
for _, method := range i.MethodsByOrdinal { |
if method.Parameters != nil { |
- if err := (*method.Parameters).ComputeVersionInfo(); err != nil { |
+ // Note(rudominer) computeFieldOffsets must be invoked before computeVersionInfo. |
+ if err := (*method.Parameters).computeFieldOffsets(); err != nil { |
return err |
} |
- if err := (*method.Parameters).ComputeFieldOffsets(); err != nil { |
+ if err := (*method.Parameters).computeVersionInfo(); err != nil { |
return err |
} |
} |
if method.ResponseParameters != nil { |
- if err := (*method.ResponseParameters).ComputeVersionInfo(); err != nil { |
+ // Note(rudominer) computeFieldOffsets must be invoked before computeVersionInfo. |
+ if err := (*method.ResponseParameters).computeFieldOffsets(); err != nil { |
return err |
} |
- if err := (*method.ResponseParameters).ComputeFieldOffsets(); err != nil { |
+ if err := (*method.ResponseParameters).computeVersionInfo(); err != nil { |
return err |
} |
} |
@@ -56,10 +214,17 @@ func (i *MojomInterface) ComputeFinalData() error { |
return nil |
} |
+//////////////////////// Unions //////////////////////// |
+ |
func (u *MojomUnion) ComputeFinalData() error { |
return nil |
} |
+//////////////////////// Enums //////////////////////// |
+func (e *MojomEnum) ComputeFinalData() error { |
+ return e.ComputeEnumValueIntegers() |
+} |
+ |
// ComputeEnumValueIntegers() computes the |ComputedIntValue| field of all |
// the values in |enum|. |
func (enum *MojomEnum) ComputeEnumValueIntegers() error { |