Summary
PropertyImpl.getPropertyValueFromDeclarativeModel selects the declarative-model property lookup with an instanceof chain over Subcomponent, FeatureGroup, Feature, and PortConnection, casting to the *Impl class in each branch to reach a method that is not declared on any interface. This hand-rolls virtual dispatch in the caller, and it does so in PropertyImpl, which owns none of the types being dispatched over.
Three consequences:
- The dispatch is fragile in the direction that matters. The chain is correct today only because its branch order happens to place
FeatureGroup before Feature. Adding a declarative type, or adding an override on a subtype, requires editing PropertyImpl rather than the type that changed β and forgetting to do so fails silently by falling into the else branch.
- The
*Impl casts bypass the interface layer. getPropertyValue(Property, PropertyAcc, Classifier, boolean) is declared only on SubcomponentImpl and FeatureImpl, and getPropertyValue(Property, PropertyAcc) only on PortConnectionImpl. Nothing in the org.osate.aadl2 interfaces describes this operation, so the caller must know the implementation classes by name.
- The
all parameter is dead. PropertyImpl is the only caller of these overloads in the repository, and it passes false at every site. Every if (!all) { return; } inside SubcomponentImpl.getPropertyValue, FeatureImpl.getPropertyValue, and FeatureImpl.getPropertyValueHelper is therefore unconditionally taken, and the all argument threaded into the downstream getPropertyValueInternal(prop, pas, true, all) calls is always false.
Reproduction
No observable failure; this is a structural issue. The claims above are established by search:
getPropertyValue(this, pas, cl, false) and getPropertyValue(this, pas) appear only in PropertyImpl.getPropertyValueFromDeclarativeModel. There is no other caller of the four-argument overloads, and false is the only value ever passed for all.
- Every
NamedElement implementer in the repository routes through NamedElementImpl, including the instance metamodel by way of InstanceObjectImpl extends NamedElementImpl. NamedElementImpl is the only class that implements NamedElement without inheriting an implementation.
Expected behavior
A behavior-preserving refactor that moves the dispatch onto the types being dispatched over. Proposed shape:
Declare on the NamedElement interface:
void getPropertyValueForInstance(Property property, PropertyAcc pas, Classifier instantiatedClassifier);
This is additive and precedented β NamedElement already hand-declares getPropertyValueInternal alongside its generated members β and because every implementer inherits from NamedElementImpl, supplying the base body there cannot break any implementer.
NamedElementImpl β base body delegating to getPropertyValueInternal(property, pas, true), which is what the current else branch does.
SubcomponentImpl, FeatureImpl, PortConnectionImpl β overrides carrying the existing bodies.
PropertyImpl.getPropertyValueFromDeclarativeModel β collapses to a guard clause plus a single call, with no casts.
Declare the new method without the all parameter. That captures the simplification as new API rather than as a signature change to existing API, and reduces the question of what to do with the old overloads to a single decision:
- Preferred: delete the now-unreferenced
getPropertyValue overloads on SubcomponentImpl and FeatureImpl, FeatureImpl.getPropertyValueHelper, and PortConnectionImpl.getPropertyValue, along with the dead if (!all) branches inside them.
- Conservative: retain them as
@Deprecated delegates that ignore all, preserving binary compatibility and leaving the dead branches in place.
org.osate.aadl2.impl is exported from the bundle with no x-internal or x-friends, so the preferred option is a binary-incompatible change against the 2.18.0 API baseline. The tycho-p2-extras-plugin baseline comparison is currently commented out, so nothing in the build will flag the choice β it needs a deliberate decision rather than a green build.
Since there is no behavior change, this carries no regression test of its own and rests on the existing property-lookup suite; the branch should record a green baseline for that suite before and after.
Relevant code
core/org.osate.aadl2/src/org/osate/aadl2/impl/PropertyImpl.java, getPropertyValueFromDeclarativeModel β the instanceof chain
core/org.osate.aadl2/src/org/osate/aadl2/NamedElement.java β where the new method would be declared, next to the existing hand-declared getPropertyValueInternal
core/org.osate.aadl2/src/org/osate/aadl2/impl/NamedElementImpl.java, getPropertyValueInternal
core/org.osate.aadl2/src/org/osate/aadl2/impl/SubcomponentImpl.java, getPropertyValue
core/org.osate.aadl2/src/org/osate/aadl2/impl/FeatureImpl.java, getPropertyValue, getPropertyValueHelper
core/org.osate.aadl2/src/org/osate/aadl2/impl/PortConnectionImpl.java, getPropertyValue
core/org.osate.aadl2/src/org/osate/aadl2/instance/impl/InstanceObjectImpl.java β confirms implementers inherit from NamedElementImpl
Dependencies
Depends on #3107, which removes the redundant FeatureGroup branch. Doing that first means one fewer branch to collapse and one fewer override to consider here.
Related
Summary
PropertyImpl.getPropertyValueFromDeclarativeModelselects the declarative-model property lookup with aninstanceofchain overSubcomponent,FeatureGroup,Feature, andPortConnection, casting to the*Implclass in each branch to reach a method that is not declared on any interface. This hand-rolls virtual dispatch in the caller, and it does so inPropertyImpl, which owns none of the types being dispatched over.Three consequences:
FeatureGroupbeforeFeature. Adding a declarative type, or adding an override on a subtype, requires editingPropertyImplrather than the type that changed β and forgetting to do so fails silently by falling into theelsebranch.*Implcasts bypass the interface layer.getPropertyValue(Property, PropertyAcc, Classifier, boolean)is declared only onSubcomponentImplandFeatureImpl, andgetPropertyValue(Property, PropertyAcc)only onPortConnectionImpl. Nothing in theorg.osate.aadl2interfaces describes this operation, so the caller must know the implementation classes by name.allparameter is dead.PropertyImplis the only caller of these overloads in the repository, and it passesfalseat every site. Everyif (!all) { return; }insideSubcomponentImpl.getPropertyValue,FeatureImpl.getPropertyValue, andFeatureImpl.getPropertyValueHelperis therefore unconditionally taken, and theallargument threaded into the downstreamgetPropertyValueInternal(prop, pas, true, all)calls is alwaysfalse.Reproduction
No observable failure; this is a structural issue. The claims above are established by search:
getPropertyValue(this, pas, cl, false)andgetPropertyValue(this, pas)appear only inPropertyImpl.getPropertyValueFromDeclarativeModel. There is no other caller of the four-argument overloads, andfalseis the only value ever passed forall.NamedElementimplementer in the repository routes throughNamedElementImpl, including the instance metamodel by way ofInstanceObjectImpl extends NamedElementImpl.NamedElementImplis the only class that implementsNamedElementwithout inheriting an implementation.Expected behavior
A behavior-preserving refactor that moves the dispatch onto the types being dispatched over. Proposed shape:
Declare on the
NamedElementinterface:This is additive and precedented β
NamedElementalready hand-declaresgetPropertyValueInternalalongside its generated members β and because every implementer inherits fromNamedElementImpl, supplying the base body there cannot break any implementer.NamedElementImplβ base body delegating togetPropertyValueInternal(property, pas, true), which is what the currentelsebranch does.SubcomponentImpl,FeatureImpl,PortConnectionImplβ overrides carrying the existing bodies.PropertyImpl.getPropertyValueFromDeclarativeModelβ collapses to a guard clause plus a single call, with no casts.Declare the new method without the
allparameter. That captures the simplification as new API rather than as a signature change to existing API, and reduces the question of what to do with the old overloads to a single decision:getPropertyValueoverloads onSubcomponentImplandFeatureImpl,FeatureImpl.getPropertyValueHelper, andPortConnectionImpl.getPropertyValue, along with the deadif (!all)branches inside them.@Deprecateddelegates that ignoreall, preserving binary compatibility and leaving the dead branches in place.org.osate.aadl2.implis exported from the bundle with nox-internalorx-friends, so the preferred option is a binary-incompatible change against the 2.18.0 API baseline. Thetycho-p2-extras-pluginbaseline comparison is currently commented out, so nothing in the build will flag the choice β it needs a deliberate decision rather than a green build.Since there is no behavior change, this carries no regression test of its own and rests on the existing property-lookup suite; the branch should record a green baseline for that suite before and after.
Relevant code
core/org.osate.aadl2/src/org/osate/aadl2/impl/PropertyImpl.java,getPropertyValueFromDeclarativeModelβ theinstanceofchaincore/org.osate.aadl2/src/org/osate/aadl2/NamedElement.javaβ where the new method would be declared, next to the existing hand-declaredgetPropertyValueInternalcore/org.osate.aadl2/src/org/osate/aadl2/impl/NamedElementImpl.java,getPropertyValueInternalcore/org.osate.aadl2/src/org/osate/aadl2/impl/SubcomponentImpl.java,getPropertyValuecore/org.osate.aadl2/src/org/osate/aadl2/impl/FeatureImpl.java,getPropertyValue,getPropertyValueHelpercore/org.osate.aadl2/src/org/osate/aadl2/impl/PortConnectionImpl.java,getPropertyValuecore/org.osate.aadl2/src/org/osate/aadl2/instance/impl/InstanceObjectImpl.javaβ confirms implementers inherit fromNamedElementImplDependencies
Depends on #3107, which removes the redundant
FeatureGroupbranch. Doing that first means one fewer branch to collapse and one fewer override to consider here.Related