Skip to content

Classes referenced only from class variables are partly removed during deployment #1319

@rko281

Description

@rko281

Classes not referenced from code are removed during Lagoon image stripping. However where such an unreferenced class is actually referenced from a class variable in another non-stripped class the "unreferenced" class still exists in the deployed image as an unbound class.

This can cause issues where the image stripper is configured with "Remove redundant methods" (the default). Since the unbound class is no longer traceable from the system roots its methods are not included when assembling the set of sent messages. Thus methods referenced solely from such classes are stripped despite possibly being sent.

Example:
In the Database Connection package, subclasses of DBUnstructuredFieldBuffer are only referenced from DBColAttr class>>initialize. This reference is stripped along with other class initialization methods leaving the subclasses unreferenced from code and thus removed. However the subclasses are all referenced from the DBColAttr class variable BufferClasses. This reference means the subclasses are present and usable in a deployed image.

DBNumericFieldBuffer>>dbUnmarshal: is one of only two senders in a standard image of on:from:to: which is solely implemented by PositionableStream class. The other reference is from WriteStream class>>with:from:to which is unsent and so (correctly) stripped during deployment. However since DBNumericFieldBuffer is considered unreferenced and so partly removed as described above, its send of on:from:to: is not found. Consequently PositionableStream class>>on:from:to: is considered unsent and removed from the image. This subsequently causes an error when attempting to marshal decimals from the database.

To Reproduce:

  1. Install the package Database Test App. This prompts for an ODBC DSN, creates a temporary table containing a DECIMAL column, and attempts to INSERT and SELECT from that table.

  2. Deploy the application. The executable manifest should show that DBNumericFieldBuffer has been stripped but also exists as an unbound class:

     	<RemoveClass>DBNumericFieldBuffer</RemoveClass>
     	...
     	<Class name="DBNumericFieldBuffer" count="0" memoryUsage="0" unbound="true">
     		<Methods count="5">
     			<Method>dbConvert:for:</Method>
     			<Method>dbInterchangeType</Method>
     			<Method>dbMarshal:into:</Method>
     			<Method>dbUnmarshal:</Method>
     			<Method>initializeForColumn:</Method>
     		</Methods>
     		<ClassMethods count="2">
     			<Method>dbTransferOctetLengthForColumn:</Method>
     			<Method>valueClass</Method>
     		</ClassMethods>
     	</Class>
    

The manifest will also show that PositionableStream class>>on:from:to: has been removed:

	<StripMethods reason="unsent">
		...
		<Method methodClass="PositionableStream class">on:from:to:</Method>
  1. Run the application, selecting a writeable database. The runtime error ReadStream class does not understand #on:from:to: will be logged.

Possible fixes:
Such cases are probably rare enough to be handled ad hoc, for example by adding the associated class initialization methods to the must not strip category - doing this with DBColAttr class>>initialize allows the example app to be deployed and run successfully. An explicit warning of unbound classes following image deployment could help identify such cases.

Checking for references from class variables to otherwise-unreferenced classes (and thus preventing their removal) would resolve the issue more directly. However this suggests other types of references (global, class instance variable, pool dictionaries) should also be checked which may have an adverse effect on deployment performance.

Dolphin 7.2.2
Windows 11 on Mac Silicon under VMWare

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions