Skip to content

Commit

Permalink
Relax length precondition of To_Context
Browse files Browse the repository at this point in the history
Ref. #1054
  • Loading branch information
treiher committed Jun 1, 2022
1 parent 74d1384 commit aa1cb6d
Show file tree
Hide file tree
Showing 16 changed files with 124 additions and 76 deletions.
4 changes: 3 additions & 1 deletion rflx/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1138,7 +1138,9 @@ def substituted(
func = substitution(mapping or {}, func)
expr = func(-self if self.negative else self)
if isinstance(expr, Attribute):
expr = expr.__class__(expr.prefix.substituted(func))
prefix = expr.prefix.substituted(func)
if not isinstance(prefix, Attribute):
expr = expr.__class__(prefix)
return -expr if self.negative else expr

def simplified(self) -> Expr:
Expand Down
92 changes: 56 additions & 36 deletions rflx/generator/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -2924,7 +2924,7 @@ def create_structure(message: Message, prefix: str) -> UnitPart:
unit += _create_structure_type(message, prefix)
unit += _create_valid_structure_function(message, prefix)
unit += _create_to_structure_procedure(message)
unit += _create_to_context_procedure(message)
unit += _create_to_context_procedure(message, prefix)
return unit


Expand Down Expand Up @@ -2997,27 +2997,7 @@ def _create_valid_structure_function(message: Message, prefix: str) -> UnitPart:
# ISSUE: Componolit/RecordFlux#276
mapping={expr.ValidChecksum(f): expr.TRUE for f in message.checksums},
)
.substituted(
lambda x: expr.Size(expr.Variable("Struct" * x.prefix.identifier))
if isinstance(x, expr.Size)
and isinstance(x.prefix, expr.Variable)
and Field(x.prefix.identifier) in message.fields
else x
)
.substituted(
lambda x: expr.Call(
"To_U64",
[expr.Variable("Struct" * x.identifier)],
)
if isinstance(x, expr.Variable)
and Field(x.identifier) in message.fields
and isinstance(message.types[Field(x.identifier)], Scalar)
else expr.Variable("Struct" * x.identifier)
if isinstance(x, expr.Variable)
and Field(x.identifier) in message.fields
and isinstance(message.types[Field(x.identifier)], Opaque)
else x
)
.substituted(_struct_substitution(message))
.substituted(common.substitution(message, prefix))
.simplified()
.ada_expr()
Expand Down Expand Up @@ -3152,7 +3132,40 @@ def _create_to_structure_procedure(message: Message) -> UnitPart:
)


def _create_to_context_procedure(message: Message) -> UnitPart:
def _struct_substitution(
message: Message,
) -> ty.Callable[[expr.Expr], expr.Expr]:
def func(expression: expr.Expr) -> expr.Expr:
if (
isinstance(expression, expr.Size)
and isinstance(expression.prefix, expr.Variable)
and Field(expression.prefix.identifier) in message.fields
):
return expr.Size(expr.Variable("Struct" * expression.prefix.identifier))

if isinstance(expression, expr.Variable) and Field(expression.identifier) in message.fields:
field_type = message.types[Field(expression.identifier)]

if isinstance(field_type, Enumeration):
return expr.Call(
"To_U64",
[expr.Variable("Struct" * expression.identifier)],
)

if isinstance(field_type, Scalar):
return expr.Call(
const.TYPES_U64,
[expr.Variable("Struct" * expression.identifier)],
)

return expr.Variable("Struct" * expression.identifier)

return expression

return func


def _create_to_context_procedure(message: Message, prefix: str) -> UnitPart:
assert len(message.paths(FINAL)) == 1

body: ty.List[Statement] = [CallStatement("Reset", [Variable("Ctx")])]
Expand Down Expand Up @@ -3224,8 +3237,7 @@ def _create_to_context_procedure(message: Message) -> UnitPart:
"To_Context",
[Parameter(["Struct"], "Structure"), InOutParameter(["Ctx"], "Context")],
)
message_size = message.max_size()
assert isinstance(message_size, expr.Number)
message_size = message.size()

return UnitPart(
[
Expand All @@ -3238,18 +3250,26 @@ def _create_to_context_procedure(message: Message) -> UnitPart:
Not(Constrained("Ctx")),
Call("Has_Buffer", [Variable("Ctx")]),
GreaterEqual(
Add(
Call(
const.TYPES * "To_Last_Bit_Index",
[Variable("Ctx.Buffer_Last")],
),
-Call(
const.TYPES * "To_First_Bit_Index",
[Variable("Ctx.Buffer_First")],
),
Number(1),
Call(
const.TYPES_U64,
[
Add(
Call(
const.TYPES * "To_Last_Bit_Index",
[Variable("Ctx.Buffer_Last")],
),
-Call(
const.TYPES * "To_First_Bit_Index",
[Variable("Ctx.Buffer_First")],
),
Number(1),
)
],
),
message_size.ada_expr(),
message_size.substituted(_struct_substitution(message))
.substituted(common.substitution(message, prefix))
.simplified()
.ada_expr(),
),
)
),
Expand Down
22 changes: 18 additions & 4 deletions rflx/model/message.py
Original file line number Diff line number Diff line change
Expand Up @@ -936,6 +936,15 @@ def is_definite(self) -> bool:
)

def size(self, field_values: Mapping[Field, expr.Expr] = None) -> expr.Expr:
"""
Determine the size of the message based on the given field values.
If the message is not definite, a value for each field on one message path is required.
The reason for this is that only for messages without optional fields the size can be
represented by a single mathematical expression, if no field values are given.
An exception is raised if no size can be determined.
"""
field_values = field_values if field_values else {}

def remove_variable_prefix(expression: expr.Expr) -> expr.Expr:
Expand Down Expand Up @@ -979,10 +988,15 @@ def remove_variable_prefix(expression: expr.Expr) -> expr.Expr:
failures = []

for path in self.paths(FINAL):
if not self.has_fixed_size and fields != {
*self.parameters,
*(l.target for l in path if l.target != FINAL),
}:
if (
not self.is_definite
and not self.has_fixed_size
and fields
!= {
*self.parameters,
*(l.target for l in path if l.target != FINAL),
}
):
continue
message_size = expr.Add(
*[
Expand Down
20 changes: 20 additions & 0 deletions tests/data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,26 @@
},
)

DEFINITE_MESSAGE = Message(
"Definite::Message",
[
Link(INITIAL, Field("Length")),
Link(
Field("Length"),
Field("Data"),
size=Mul(Variable("Length"), Number(8)),
),
Link(
Field("Data"),
FINAL,
),
],
{
Field("Length"): UNIVERSAL_LENGTH,
Field("Data"): OPAQUE,
},
)

SESSION = Session(
identifier="P::S",
initial="A",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 64,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 64,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 64,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 64,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 96,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 96,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -611,7 +611,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 2056,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= RFLX_Types.U64 (Struct.Length) * 8 + 16,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
2 changes: 1 addition & 1 deletion tests/spark/generated/rflx-enumeration-message.ads
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 8,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 8,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
2 changes: 1 addition & 1 deletion tests/spark/generated/rflx-expression-message.ads
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 16,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 16,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
2 changes: 1 addition & 1 deletion tests/spark/generated/rflx-fixed_size-simple_message.ads
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 32,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= 32,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
2 changes: 1 addition & 1 deletion tests/spark/generated/rflx-sequence-inner_message.ads
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 2048,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= RFLX_Types.U64 (Struct.Length) * 8 + 8,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
2 changes: 1 addition & 1 deletion tests/spark/generated/rflx-udp-datagram.ads
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,7 @@ is
Valid_Structure (Struct)
and then not Ctx'Constrained
and then Has_Buffer (Ctx)
and then RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1 >= 524280,
and then RFLX_Types.U64 (RFLX_Types.To_Last_Bit_Index (Ctx.Buffer_Last) - RFLX_Types.To_First_Bit_Index (Ctx.Buffer_First) + 1) >= (RFLX_Types.U64 (Struct.Length) - 8) * 8 + 64,
Post =>
Has_Buffer (Ctx)
and Structural_Valid_Message (Ctx)
Expand Down
26 changes: 13 additions & 13 deletions tests/unit/generator_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,18 @@
from tests.data import models
from tests.utils import assert_compilable_code, assert_equal, assert_equal_code

MODELS = [
models.DERIVATION_MODEL,
models.ENUMERATION_MODEL,
models.ETHERNET_MODEL,
models.EXPRESSION_MODEL,
models.NULL_MESSAGE_IN_TLV_MESSAGE_MODEL,
models.NULL_MODEL,
models.SEQUENCE_MODEL,
models.TLV_MODEL,
Model(models.FIXED_SIZE_SIMPLE_MESSAGE.dependencies),
]


def units(generator: Generator) -> Mapping[str, str]:
result = {}
Expand Down Expand Up @@ -118,19 +130,7 @@ def test_generate_missing_template_files(monkeypatch: MonkeyPatch, tmp_path: Pat
Generator().generate(Model(), Integration(), tmp_path)


@pytest.mark.parametrize(
"model",
[
models.NULL_MODEL,
models.TLV_MODEL,
models.NULL_MESSAGE_IN_TLV_MESSAGE_MODEL,
models.ETHERNET_MODEL,
models.ENUMERATION_MODEL,
models.SEQUENCE_MODEL,
models.EXPRESSION_MODEL,
models.DERIVATION_MODEL,
],
)
@pytest.mark.parametrize("model", MODELS)
def test_equality(model: Model, tmp_path: Path) -> None:
assert_equal_code(model, Integration(), GENERATED_DIR, tmp_path, accept_extra_files=True)

Expand Down
4 changes: 4 additions & 0 deletions tests/unit/model/message_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
)
from rflx.model.message import ByteOrder
from tests.data.models import (
DEFINITE_MESSAGE,
ENUMERATION,
ETHERNET_FRAME,
FIXED_SIZE_MESSAGE,
Expand Down Expand Up @@ -2717,6 +2718,7 @@ def test_has_implicit_size() -> None:
def test_is_definite() -> None:
assert NULL_MESSAGE.is_definite
assert FIXED_SIZE_SIMPLE_MESSAGE.is_definite
assert DEFINITE_MESSAGE.is_definite
assert not FIXED_SIZE_MESSAGE.is_definite
assert not TLV_MESSAGE.is_definite
assert not ETHERNET_FRAME.is_definite
Expand All @@ -2726,6 +2728,8 @@ def test_is_definite() -> None:
def test_size() -> None:
assert NULL_MESSAGE.size() == Number(0)
assert FIXED_SIZE_MESSAGE.size() == Number(200)
assert DEFINITE_MESSAGE.size() == Add(Mul(Variable("Length"), Number(8)), Number(16))
assert DEFINITE_MESSAGE.size({Field("Length"): Number(8)}) == Number(80)
assert TLV_MESSAGE.size({Field("Tag"): Variable("TLV::Msg_Error")}) == Number(8)
assert TLV_MESSAGE.size(
{
Expand Down
14 changes: 1 addition & 13 deletions tools/generate_spark_test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,17 @@
import sys
from pathlib import Path

import tests.data.models
from rflx.generator import Generator
from rflx.integration import Integration
from rflx.model import Model
from rflx.specification import Parser
from tests.const import SPEC_DIR
from tests.unit.generator_test import MODELS

logging.basicConfig(level=logging.INFO, format="%(message)s")
logging.disable(logging.NOTSET)

OUTPUT_DIRECTORY = Path("tests/spark/generated")

MODELS = [
tests.data.models.EXPRESSION_MODEL,
tests.data.models.ENUMERATION_MODEL,
tests.data.models.SEQUENCE_MODEL,
tests.data.models.DERIVATION_MODEL,
tests.data.models.NULL_MODEL,
tests.data.models.TLV_MODEL,
tests.data.models.NULL_MESSAGE_IN_TLV_MESSAGE_MODEL,
Model(tests.data.models.FIXED_SIZE_SIMPLE_MESSAGE.dependencies),
]

SPECIFICATION_FILES = [
SPEC_DIR / "ethernet.rflx",
SPEC_DIR / "in_ethernet.rflx",
Expand Down

0 comments on commit aa1cb6d

Please sign in to comment.