Object replacement is a general mechanism that allows an object to be substituted by another during a swapping operation (loading or dumping). In the case of dumping, the object that is being dumped can specify a replacement for itself. Say object A specifies that B replaces it when dumping happens. Then, A itself will never be dumped. B, instead, takes its place.
A GlobalDescriptor can be used to replace a global variable, where it stores the name of the global in its instance variable
nameOfGlobal. After defining this class, all that needs to be done is to define a replacement object (a
GlobalDescriptor) for global Smalltalk. The example below shows a possible replacement object.
Once the new class is implemented, GlobalDescriptor, and the replacement object properly defined, identity-based replacement can be done. The example below shows how dumping can be performed so that all references to Smalltalk will be replaced by references to its replacement object, an instance of
GlobalDescriptor.
Replacing something by nil is, conceptually, the same as unlinking, a feature that was present in previous releases and now is obsolete. This can be accomplished by modifying the code in the example above by replacing the
replaceObject:with: statement with the following:
This code replaces the Point 203@485 by a corresponding
Association, 203 >485, and
Point 1@2 by a corresponding
Association, 1 >2.
Class-based replacement using ObjectDumper instance method
replaceInstancesOf:using: has to be configured for every instance of
ObjectDumper. However, there is an alternative form of defining class based replacement, which will be applied to all instances of an
ObjectDumper used in the image. It makes use of protocols that form the Swapper framework (explained in
The Swapper framework). For instance, suppose you want instances of
CompiledMethod to be replaced by an intermediate representation, which just carries the class name and selector of the compiled method. And you want this form of replacement to apply to any
ObjectDumper used in the image. Simply define
CompiledMethod instance method
dumpingReplacementForReplacer: as in the following example:
CompiledMethodDescriptor is a class whose instances hold the values for the class name and the selector of the compiled method being represented. For example:
You will also need to define CompiledMethodDescriptor instance methods
className:,
className,
methodSelector:, and
methodSelector and class method
forClassNamed:selector:.
The parameter aClassBasedReplacer can be ignored now for the sake of simplicity (it is explained in detail in
The Swapper framework). The method defined in "Example: Redefining methods to implement class-based replacement" on page
*** will cause all instances of
CompiledMethod to be replaced (upon unload) by the intermediate representation,
CompiledMethodDescriptor.
The reason why just implementing dumpingReplacementForReplacer: is not enough is because there are internal optimizations in
ObjectDumper which avoid computing dumping replacements for instances of classes which do not define any replacement at all. Therefore, implementing
definesClassBasedReplacement as a class method allows
ObjectDumper to know whether the optimization is valid for instances of the class or not.
In the case of replaceInstVarIndex:of:using:, the first parameter is the instance variable index, an
Integer. This is the same value you would use to index instance variables using methods
instVarAt: and
instVarAt:put:. The second parameter is the class whose instances will have their instance variable replaced (for example,
Point,
Rectangle, and so on). The third parameter is a two parameter block. This block will be evaluated using the original object and the index of the instance variable to be replaced as parameters. The result of the block is the replacement object for that particular instance variable of the object.
This code will replace instance variable 1 of Point 203@485 by its
y value, updating it so that the
Point is
485@485 when dumped.
Point 1@2 will also be updated, and
2@2 will be dumped. Note that this does not affect the original instance of
Point in the image. Only the dumped copy of the object is changed.
For these reasons, a second API is provided. By using ObjectDumper instance method
replaceInstVarNamed:of:using: your code will work correctly even in the situations listed above. The following example shows how the same behavior of the above example can be achieved, but using instance variable names instead.
Note that in the case of replaceInstVarNamed:of:using:, the first parameter is the instance variable name, a
String. The second parameter is the class whose instances will have the instance variable replaced (for example,
Point,
Rectangle, and so on). The third parameter is a two parameter block. This block will be evaluated using the original object and the name of the instance variable to be replaced as parameters. The result of the block is the replacement object for that particular instance variable.
Replacing an object by nil is, conceptually, the same as unlinking. For example, you can modify the
replaceInstVarNamed:of:using: in the previous example with the following:
Since the need to replace by nil is a common case, the API also accepts
nil instead of the block. For example, modify the same statement as follows:
Instance-variable-based replacement using ObjectDumper instance methods
replaceInstVarIndex:of:using: and
replaceInstVarNamed:of:using: has to be configured for every instance of
ObjectDumper used. However, there is an alternative form of defining instance variable based replacement, which will be applied to all instances of an
ObjectDumper used in the image. It makes use of protocols that form the Swapper framework (explained in
The Swapper framework).
For example, suppose you want the first instance variable of Point to be replaced by the point's
y value. And you want this form of replacement to apply to any
ObjectDumper used in the image. You need to define
Point instance method
dumpingReplacementForInstVar:forReplacer: to perform the replacement and
Point class method
definesInstVarReplacement as shown in the next example:
Unlike the dumping replacements presented in Dumping replacement there is no API in
ObjectLoader to override a loading replacement for just one instance of loader. For instance,
ObjectLoader new replaceInstancesOf: Point using: [:point | point printString] is not permitted. That is, the
replaceInstancesOf:using: method is only defined by
ObjectDumper and is not defined by
ObjectLoader. A loading replacement for a class will be effective for all instances of
ObjectLoader used.
For example, suppose you want to replace all instances of Point in the dumped output by their corresponding print string when you load. To do this, you define
Point instance method
loadingReplacementForReplacer: and
Point class method
definesLoadingReplacement as in the example below:
In the above example, parameter aLoadingReplacer was ignored. Advanced users can query the replacer and its corresponding
ObjectLoader, to decide what the replacement must be (see
The Swapper framework). An example is the implementation of
Class instance method
loadingReplacementForReplacer:.
In Dumping replacement we saw how to specify different kinds of dumping replacements for an object. For many cases, only a dumping replacement or only a loading replacement is enough. For other cases, a combination of the two is desired.
In Object-identity-based replacement, we saw how to specify a dumping replacement for an object based on its identity. However, what if we want to specify a loading replacement for the replacement dumped? For instance, if now we want to load the object dumped in "Example: Identity-based replacement" on page
***, so that the reference to Smalltalk is remapped back in the image where the object is loaded, we must define
GlobalDescriptor instance method
loadingReplacementForReplacer: as in the next example. Remember to define
GlobalDescriptor class method
definesLoadingReplacement and have it return
true as in "Example: Loading replacement" on page
***.
The method above will replace the GlobalDescriptor. The original reference to Smalltalk in the image where the object was dumped will be remapped to the local global Smalltalk in the image where the objects are loaded.
In "Example: Redefining methods to implement class-based replacement" on page *** all instances of
CompiledMethod were replaced by instances of
CompiledMethodDescriptor. To have these intermediate representations of
CompiledMethods rebound again to their corresponding
CompiledMethods in the image where they are loaded, you must define the replacement method
CompiledMethodDescriptor instance method
loadingReplacementForReplacer:. The next example shows how to do this. Remember to define
CompiledMethodDescriptor class method
definesLoadingReplacement and have it return
true.
The combined use of a dumping and loading replacement for CompiledMethod presented here swaps only the minimum information necessary, just enough to rebind the object to an equivalent object in the image where it will be loaded. If you also need to transfer the byte codes of the
CompiledMethod, you should have no class based dumping replacement and a loading replacement that will fix the bytecodes upon loading, so that the instance is consistent and can be run in the image where loaded.
Except immediates like SmallInteger,
true,
false,
nil, and
Character. There are also some loading optimizations, turned on by default, which will make an
ObjectLoader compute only replacements for objects that were dumped marked as needing loading replacement. See
Limitations for details on how to deal with these optimizations.
Copyright 2005, 2018 Instantiations, Inc. All rights reserved.