Geoffroy Gadacz

Jean-Paul Viricelle
email: paulviricelle@emse.fr

Christophe Pijolat

Michèle Pijolat

Voltage Oscillations in Single-Chamber Fuel Cells operating under a C 3 H 8 / O 2 mixture

Keywords: Single Chamber Fuel Cell, Propane, Oscillation, Anode. 2

Single-Chamber Fuel Cells (SCFC) are an alternative way to conventional SOFCs. Effectively, they do not require any more sealing between anodic and cathodic compartment's as the cell is fed with a mixture of hydrocarbon and oxygen. This paper deals with voltage oscillations observed with a SCFC composed of GDC (Ce 0.9 Gd 0.1 O 1.95 ) supported electrolyte, Ni-GDC anode and a BSCF (Ba 0.5 Sr 0.5 Co 0.8 Fe 0.2 O 3-δ δ δ δ ) cathode, operating in the temperature range 500-600°C and under various propane and oxygen mixtures, with a C 3 H 8 /O 2 ratio between 0.2-0.66 . Electrical and Thermo-Programmed Oxidation (TPO) measurements combined with SEM observations allowed us proving that these oscillations are due to carbon nanowires deposition/oxidation cycles. A mechanism is proposed to explain this behaviour.

Introduction

Solid Oxide Fuel Cells have been intensively studied in the two past decades [1][2][3]. One of the biggest challenges is to control the sealing procedure [4]. Alternatives Fuel Cells such as Single Chamber Fuel Cells (SCFC) have been proposed early in the 90ties by the group of Hibino [5][6] who has performed the most significant developments at this period. Since this date, SCFC have shown a growing interest and many other research groups have focussed on this kind of device as reported in recent review papers [7][8]. Their main working principle is reminder hereafter. In such devices, anode and cathode are directly exposed to an oxygen and hydrocarbon mixture. The cathode must be highly selective to the oxygen adsorption, without direct catalytic activity for hydrocarbon conversion [9]. The anode, which is generally composed of a nickel based cermet, produces hydrogen and carbon monoxide (partial oxidation of the hydrocarbon) that maintains a reducing atmosphere inside the anode [10], as presented by reaction Eq.(1).

→

Eq.(

A lot of other reactions are also known to take place at the anode, such as total oxidation of the hydrocarbon Eq. ( 2), direct oxidation of hydrogen Eq. ( 3), or direct oxidation of carbon monoxide Eq. (4).

O H y + xCO O y + x + H C 2 y x 2 2 2 4 →      
Eq.( 2)

O H O + H NiO O + Ni → 2 2 1
Eq.( 6)

xNi + H y + xCO xNiO + H C y x 2 2 →
Eq. (7) Such OCV oscillations explained by Ni/NiO redox cycle were also observed for some conventional dual-chamber SOFC [18]. In a previous paper dealing with SCFC, we have also studied the specific problem of anode possible re-oxidation depending on temperature and propane/oxygen ratio [19]. It was demonstrated that a minimum temperature, around 500-550°C depending on C 3 H 8 /O 2 , is required to avoid nickel oxidation. However, even in conditions where anode redox cycles are not supposed to occur (stability of nickel), some oscillations phenomena during SCFC evaluation were observed.

Another phenomenon explaining voltage oscillations is carbon deposition and combustion, especially in conventional SOFC fed with hydrocarbon. Hydrocarbon cracking on Ni-based anode, thus in reducing conditions, is responsible of carbon nano filaments formation [20][21].

However, in single chamber conditions, redox conditions are rather different as both anode and cathodes are exposed to the air/hydrocarbon mixture.

Hence, in this paper, we focus our investigations in order to propose a mechanism explaining such oscillating behaviour observed for anode electrical conductivity and SCFCs OCV on the basis of catalytic, electrical and electrochemical characterizations.

Experimental

Materials & Fuel cell preparation

Electrolyte supported cells were prepared using a commercial GDC powder (Ce 0,9 Gd 0.1 O 1,95 )

supplied by FuelCellMaterials™, with a specific surface area of 8 m².g -1 measured by nitrogen adsorption on Micromeritics ASAP 2000 equipment (BET method). Average particle size was checked by laser granulometry analysis (Malvern Mastersizer 2000) and showed three maxima at 1, 5 and 110 µm. The powder was sieved at 100 µm to remove the largest particles. GDC powder was pressed into pellets and sintered with the following thermal treatment: 1200°C during 6 hours, then 1350°C during 4 hours. The final pellets have a diameter of 19 mm, are 1.5 mm thick and present 94 % relative density.

NiO powder was supplied by Sigma-Aldrich™, and had a specific surface area of 5 m².g -1 .

Laser granulometry analysis revealed a very broad particle size distribution with various maxima, the most important being at 4 and 500 µm. To lower the average NiO particle sizes, a grinding procedure has been applied using a planetary ball mill (stainless steel balls) at 180 rpm for 30 min. X-ray photoemission spectroscopy analyses after treatment did not show any pollution of the surface due to grinding. Laser granulometry analysis shows that all the agglomerates have been broken at sizes smaller than 100 µm.

The anode was prepared using 60 % wt. of NiO and 40 % wt. of GDC powders. Commercial binder and solvent (supplied by ESL™) were used to prepare an ink which was mixed and homogenised with an UltraTurrax™ for 20 min. The ink was deposited using screen-printing technology (Aurel™ device) on one side of the electrolyte. Samples were treated at 130°C for 15 min to dry the ink. Five successive deposition-drying steps led to a 25 µm thickness anode after a final annealing procedure at 1200°C during 2 hours.

For fuel cells preparation, a BSCF (Ba 0.5 Sr 0.5 Co 0.8 Fe 0.2 O 3-δ ) cathode was deposited on the opposite side of the electrolyte. The powder was supplied by IRCELYON laboratory and showed a specific surface area of 2 m².g -1 . Laser granulometry analysis showed an average particle size of 10 µm. An ink was prepared using the same binders as for the anode layer.

The cathode layer was annealed at 950°C for 2 hours. Finally, a gold commercial ink (ESL™) was used in order to deposit a collector on the surface of the cathode, and annealed at 950°C for 2 hours. A photograph of the obtained cell is presented in figure 1.

Figure 2 shows the cermet anode and cathode microstructures. One can see that both layers have a homogeneous thickness, around 25 µm for the anode (Fig. 2a) and 40 µm for the cathode (Fig. 2b). The two layers are porous, which is required for gas diffusion. As the objective of the present study was to investigate oscillatory phenomena occurring at anode side, the cathode microstructure was not optimized. Figure 2b shows that the cathode may delaminate from the GDC electrolyte. For further studies, this problem was solved by using a 30/70 wt % GDC/BSCF cathode [22].

Electrical measurements

Anode conductivity measurements were performed using in situ Van der Pauw method [23].

Four probes (using gold wires) were placed at the edges of the anode layer deposited on the GDC pellet. Wires were stuck to the sample with a platinum paste. Attention was given to minimize the platinum quantity and thus the contact size, both to reduce the platinum catalytic effect and also to minimize the measurement error according to Van der Pauw criteria.

Fuel Cells were tested under two different C 3 H 8 :O 2 :N 2 atmospheres, namely (5:5:90) and (4:6:90) for comparison (vol. % composition). For simplification, these atmospheres are named 5/5 and 4/6 ratios respectively. The total gas flow was 10 l/h. The 4/6 (0.66) ratio corresponds to the stoichiometric amounts for the partial oxidation. The other one (5/5) has a richer C 3 H 8 quantity. These ratios were chosen because they correspond to the best catalytic activity towards propane conversion, determined from a previous study, concerning the anode stability under various C 3 H 8 /O 2 mixtures [19]. A third ratio, 3/7 (3:7:90) has been investigated, but it led to anodes degradation; hence, it will not be presented. Conductivity and OCV were measured using a Keithley 2600C Sourcemeter.

The SCFC test bench is composed of a quartz tube which is inserted inside a tubular furnace.

The sample holder is a long hollow tube. The extremity of this one has been cut to create a holder for the sample, as shown in figure 3. Gas and wires come from the inside of the tube.

The gas flows in parallel directions to both cathode (down-side) and anode (up-side).

Thermo-Programmed oxidation (TPO)

The objective of TPO experiments is to detect possible carbon deposition on the anode during it's running in SCFC conditions.

A catalytic test bench, consisting of a quartz tube, an alumina protected thermocouple (close to the sample) and a furnace was used to perform the TPO study. 10 wt% of nickel oxide powder was mixed with α-alumina (Alpha-Aesar TM ), reduced with hydrogen and then treated during two hours at 580°C with oxygen/propane mixture (either 5/5 or 4/6 ratios). After cooling under argon, samples were placed into a 1% O 2 containing atmosphere (balanced with argon) at 10 l.h -1 . A 2°C/min ramp temperature was applied and a mass spectrometer (Balzers, OMNISTAR) was used to monitor the outlet gas concentrations, especially by measuring the mass numbers (m/z) 44 and 28, in order to identify respectively CO 2 or CO production.

Results

Characterization of SCFC

The as-prepared fuel-cells, more specifically the anode, need to be reduced before the tests.

However, BSCF is known to be unstable under reducing environment such as H 2 atmospheres [24][25]. This is why different tests were made in a previous study [24] using thermogravimetry analysis to find the best compromise for this step. In this case, it has been determined that directly injecting a C 3 H 8 /O 2 mixture on the cermet was not sufficient to reduce nickel oxide. So we decided to use H 2 for a short time (2 min) to initiate the reduction at 580°C. After this step, the complete reduction was achieved by the C 3 H 8 /O 2 mixture (either 5/5 or 4/6 ratios). This treatment was performed in situ, so it was possible to monitor the OCV during the reduction. An example is represented in figure 4 for the 6/4 ratio. It is possible to see that nearly two minutes after the C 3 H 8 /O 2 introduction, the OCV rises suddenly up to 0.95 V and progressively stabilizes at 0.94 V. This proves that nickel is reduced and that only few minutes are necessary to stabilize the cell.

Following the initialization treatment at 580°C, OCV measurements were done for the SCFC under 4/6 and 5/5 C 3 H 8 /O 2 ratios, at different temperatures. Results can be seen in figure 5a and 5b respectively.

Concerning the 4/6 ratio, it can be seen that OCV decreases with increasing temperature.

Effectively, the value stabilizes at 0.92 V at 545°C and decreases to 0.895 V at 575°C. This unexpected OCV variation in regards of ohmic loss decrease with higher temperature is explained by local changes of the oxygen partial pressure at the electrodes. In particular, , catalytic activity of BSCF cathode for propane conversion strongly increases in this temperature range, leading to lower oxygen activity gradient between anode and cathode [24].

After temperature stabilization at 575°C, it appears that the OCV remains stable, with only small oscillations of around 2 mV of amplitude.

On the contrary, the cell tested with the 5/5 ratio has an opposite behaviour (figure 5b). OCV decreases from 0,896 V at 580°C to 0.87-0.88V at 550°C. At 570°C, the voltage starts to oscillate. As the temperature decreases, the oscillation amplitude increases. At 550°C, this oscillating amplitude is around 30 mV, and remains constant.

Isothermal tests at 565°C were reproduced in order to check previous behaviour for both C 3 H 8 /O 2 ratios (figure 6). Initial OCV value is a little higher (0.935 V) but remains in the same range than the one measured before. The cell under a 4/6 ratio mixture keeps a nearly constant OCV value with very small oscillations with an amplitude lower than 5 mV. By contrast, the OCV of the cell under the 5/5 ratio decreases to 0.9 V, and an oscillating behaviour takes place, as shown before in fig. 5b, with an amplitude close to 20 mV.

Since these observations may be the consequence of different phenomena, both fuel cells were observed by Scanning Electron Microscopy. No differences were seen on the cathode side. On the contrary, the anode microstructure is different, as shown in figure 7.

Initially, the anode microstructure is a porous homogeneous mixture of Ni and CGO grains (figure 7a). After treatment at the 4/6 ratio (figure 7b), the microstructure is similar, but some nanofibers are visible. They partially cover the anode surface. The same phenomenon is observed for the fuel cell tested with the 5/5 ratio (figure 7c), but nanofiber quantity is much more important, and they completely cover the anode surface. From the literature, it is expected that these nanofibers are carbon nanotubes resulting from propane cracking due the nickel catalytic activity [20,21,26].

To confirm these observations, TPO measurements were performed over nickel powder as explained in experimental part. It was checked that no significant CO 2 or CO evolving was detected with a nickel-free (only alumina) sample. For nickel samples, no peak were observed for signal m/z = 28, corresponding to CO production.. On the contrary, different observations can be done for signal m/z = 44 (CO 2 production). Effectively, a production of CO 2 is observed for the two samples in the range 350-600°C (figure 8). Even if this will not be discussed hereafter, the measured signal indicates that at least two different carbon species are present. Effectively, two different peaks can be observed for the samples. The maximum of the first one is at 440°C and the second one around 520°C. Integration of the total CO 2 peak observed in this temperature range leads to values of 1.37x10 -10 and 3.56x10 -10 (a.u.) for 4/6 and 5/5 ratios respectively. This result confirm that carbon deposition is much more important in the case of the 5/5 ratio. This is in good agreement with the previously observed SEM microstructures, because the carbon nanofibers are much more numerous on the anode surface for the 5/5 tested ratio.

Electrical conductivity measurement

As the carbon is a conducting material, and to prove that the oscillating behaviour previously observed is a consequence of the carbon nanowires presence, Van der Pauw conductivity measurements were done over the cermet anodes. To avoid the cathode influence, the cells were only composed of an electrolyte-supported anode, as described in experimental section.

Van der Pauw measurement method is usually used for arbitrary shape samples, and one of the conditions is to consider that the sample is dense. Since our cermet is porous, the measured conductivity is probably affected by the porosity and probably do not correspond to the real one. So, we use this method for comparison between samples, by considering for reference, reduced anode in hydrogen before exposure to propane/oxygen mixture. Hence, we are sure that the reference is not polluted by carbon deposition. So, conductivity higher than the reference one indicates a carbon deposition, and a lower one corresponds to nickel oxidation [19].

Directly after H 2 reduction on the prepared anode cermet, the conductivity was measured as a function of time. Results are shown in figure 9. The obtained value is very stable and about 700 S.cm -1 , which will constitute the reference value for a reduced cermet.

Results concerning 4/6 and 5/5 ratio with varying temperature are presented respectively in figures 10 and 11.

For 4/6 ratio (Fig. 10), after an initial stabilization time under gas (not seen in the figure), the value reaches 860 S.cm -1 which is higher than the reference one (700 S.cm -1 ). It progressively increases to approximately 900 S.cm -1 . In the range 580-530°C, irregular oscillations occur, with increasing amplitude as temperature is decreasing. Amplitudes higher than 100 S.cm -1 are visible at 530°C. At temperature lower than 530°C, the cermet starts to oxidize, as proved by the sudden decrease of the conductivity, and in agreement with our previous study [19].

For 5/5 ratio (figure 11), the initial value is much higher than in the previous case: 1100 S.cm - 1 . This one remains stable between 555°C and 535°C. Below this temperature, the electrical conductivity starts to oscillate. As observed for the 4/6 ratio, the oscillation amplitude is increasing when the temperature is decreasing. The electrical conductivity progressively drops to 850 S.cm -1 but remains higher than the reference one. At 520°C, the oscillations have very high amplitude in the range 850-1050 S.cm -1 . Furthermore, the temperature also starts to oscillate with variations of 2-3°C. Below this temperature, the electrical conductivity drops drastically, corresponding to the nickel oxidation.

To check if these oscillations are reversible, the temperature was increased from 510°C up to 535°C in the continuity of previous test. . This resulting graph (figure 12) shows that just after the temperature increase, the electrical conductivity comes back to 1000 S.cm -1 . This means that the nickel oxide is reduced again and already covered with carbon. After stabilization of the temperature, the conductivity starts again to oscillate.

Sample observation after test. (figure 13). clearly shows the carbon deposit at the surface of the cermet (black appearance). The grey appearance around the Pt paste corresponds probably to particular phenomena enhanced by the platinum (used as sticking paste) catalytic activity.

In the centre of the sample, it is possible to observe arches: such structure corresponds to "space structures" related to oscillating reaction mechanisms which will be discussed hereafter.

Discussions

Oscillations origins

All previous results are used to explain phenomena occurring in the anode. We have shown throughout this paper that some oscillations occur especially in the range 560-510°C. They are pointed out by both OCV and conductivity measurements on the anode, with same type of variations depending on temperature and C 3 H 8 /O 2 ratio. This proves that the oscillations origin come from the anode, as no cathode is present for Van der Pauw conductivity measurement. They are much more important for the 5/5 ratio. We have also shown that if the conductivity variations are high enough, the temperature starts to oscillate (Fig. 11) with an identical frequency, leading to space structures of the carbon inside the anode (Fig. 13).

In the literature, two types of oscillating behaviours have been presented for SCFC or Ni containing systems:

The first one has been attributed to the instability of nickel (redox cycles) in the presence of a high oxygen partial pressure [10, 17-18; 27-29]. Considering this hypothesis and our experiments, conductivities oscillations should be present, but with values below 700 S.cm -1 , measured for reduced anode (Fig. 9). Since the measurements shows conductivities values up to 900 S.cm -1 and sometimes 1050 S.cm -1 , nickel redox cycles cannot explain our observations.

The second one is generally observed for co-planar SCFC. In this configuration, where the anode is on the same side as the cathode, the gap between the two electrodes is a determining factor for the performances. By reducing this gap (miniaturization), it is expected to obtain better performances [13]. However, it is observed that below a critical gap, the OCV becomes unstable due to the proximity of the anode and the cathode [30]. Then, oscillating phenomena are generally explained by the high oxygen partial pressure gradient between the electrodes.

As all fuel-cells tested in the present study are in an opposite side configuration, and considering that the electrolyte is thick enough (1.5 mm, 94% relative density) to avoid gas diffusion in the electrolyte pores, this possibility cannot explain the observations made here.

The only way to explain the observed oscillating behaviour is to consider carbon deposition/oxidation cycles. Effectively, high conductivity values obtained (900-1100 S.cm -1 ) during measurements under C 3 H 8 /O 2 mixtures prove that carbon is present, and this was confirmed by SEM observations (Fig. 7).

To support this hypothesis, it is possible to consider thermal effects and temperature oscillations (Fig. 11). The sudden rise of the electrical conductivity (at experimental time 95 min, Fig. 11) is correlated to a drop of the temperature (~ 2°C) indicating an endothermic process. Effectively, all the reactions involving the propane decomposition to form carbon atoms are endothermic, as for example reaction Eq.( 8):

2 8 3 4 3 H C H C + →
Eq.( 8)

∆H° (550°C) = 125 kJ.mol -1
This reaction can explain the conductivity increase due to carbon formation and the momentary temperature decrease due to endothermic effect.

On the contrary, conductivities drops can be explained by the oxidation of carbon nanowires Eq. ( 9). TPO measurements have shown that it takes place between 350 and 600°C (Fig. 8),

2 2 CO O C → +
Eq.( 9)

∆H°(550°C) = -394 kJ.mol -1 This last reaction which is exothermic can explain both oscillating behaviour of OCV and of conductivity. Effectively, OCV value is directly dependant on the oxygen partial pressure at the anode. So, local oxygen consumption increases the OCV value. Furthermore, it is possible to see also in figure 11 that when the conductivity is decreasing during an oscillation, the temperature is rising (around 2°C). This is in agreement with the exothermic effect of Eq. ( 9).

As a conclusion, such a redox mechanism based on carbon formation and oxidation can explain both OCV and anode conductivity oscillation.

Mechanism proposition

To go further with the proposed mechanism, leading to formation of space structure of carbon, one can examine the required conditions of oscillation phenomena. Glansdorff and Prigogine [31] have studied oscillating behaviours and found out that this type of cycling reactions are generally obtained for systems which are far from thermodynamic equilibrium.

To be possible, they need common requirements such as:

• An autocatalytic effect, illustrated by reaction Eq. ( 10)

X X A 2 → + Eq. ( 10
)
• The compound involved in the autocatalytic reaction (X) must act as a reactant in another reaction Eq. ( 11)

P Y X → +
Eq. ( 11)

Moreover, for heterogeneous systems, the system can present a space periodicity named "dissipative space structure". If this space structure appears, then the diffusion of one species is involved inside the mechanism.

In our study, the autocatalytic effect is difficult to highlight because it would need further catalytic experiments.. However, propane is known to crack on nickel, forming carbon and hydrogen (Eq. 8). It is possible that deposited carbon participates to the following autocatalytic effect (Eq.12). Eq. ( 12) Then, carbon acts as "X" species involved in the autocatalytic reaction (Eq.10) and is also reactant in carbon oxidation reaction Eq.( 9). Furthermore, the mechanism is supported by explanation of carbon nanofilament formation in the literature. Carbon is deposited from a carbon source (hydrocarbon) on a metal particle (Ni) and then dissolves in the bulk of the particle; then it continues to precipitate as a fiber at the surface of this considered particle [21].

Finally, as described previously, we have observed dissipative spaces structures of carbon on the anode. So, all hypothesis and observations for explaining oscillations are consistent with cycling mechanism of carbon formation and oxidation.

Conclusions

During tests of a single chamber fuel cell under different C 3 H 8 /O 2 atmospheres, oscillations of both OCV and anode conductivity were observed. SEM observations and TPO experiments, as well as high conductivity values allowed us to conclude that carbon formation and

Figure 1 :

 1 Figure 1: Photograph of a typical single-chamber fuel cell viewed from the cathode side, with

Figure 2 :

 2 Figure 2: Anode (a) and cathode (b) microstructures observed by scanning electron microscopy on GDC support.

Figure 3 :

 3 Figure 3: Schematic representation of the sample holder and positioning of the SCFC sample

Figure 4 :

 4 Figure 4: OCV measurement during initialization phase of the SCFC under C 3 H 8 /O 2 mixture

Figure 5 :Figure 6 :

 56 Figure 5: OCV measurement as a function of the temperature, under C 3 H 8 /O 2 ratio 4/6 (a) and

Figure 7 :

 7 Figure 7: SEM of the initial anode fuel cell (a), after tests at 565°C under 4/6 (b) and 5/5 (c)

Figure 8 :

 8 Figure 8: TPO measurements on Ni powder tested with the 4/6 and 5/5 ratio; mass

Figure 9 :Figure 10 :

 910 Figure 9: Electrical measurement of the cermet under hydrogen at 580°C

Figure 11 :

 11 Figure 11: Electrical conductivity measured as a function of the time and the temperature for

Figure 12 :

 12 Figure 12: Electrical conductivity measured as a function of the time and the temperature for

Figure 13 :

 13 Figure 13: Anode layer appearance after electrical measurements (5/5 ratio). The wide array

oxidation take place on the anode in our experimental conditions. Observations of dissipative space structures of carbon deposit lead to propose an oscillating mechanism involving carbon redox cycles.


Erwann Wernli

Oscar Nierstrasz

Camille Teruel

Stéphane Ducasse

Delegation Proxies: The Power of Propagation *

Keywords: D.3.3 [Software]: Programming Languages -Constructs and Features Reflection, proxy, dynamic extent

Scoping behavioral variations to dynamic extents is useful to support non-functional requirements that otherwise result in crosscutting code. Unfortunately, such variations are difficult to achieve with traditional reflection or aspects. We show that with a modification of dynamic proxies, called delegation proxies, it becomes possible to reflectively implement variations that propagate to all objects accessed in the dynamic extent of a message send. We demonstrate our approach with examples of variations scoped to dynamic extents that help simplify code related to safety, reliability, and monitoring.

Introduction

Non-functional concerns like monitoring or reliability typically result in code duplication in the code base. The use of aspects is the de-facto solution to factor such boilerplate code in a single place. Aspects enable scoping variations in space (with a rich variety of static pointcuts), in time (with dynamic aspects), and in the control flow (with the corresponding pointcuts). Scoping a variation to the dynamic extent [START_REF] Tanter | Beyond static and dynamic scope[END_REF] of an expression is however challenging, since scoping between threads is not easily realized with aspects. Traditional reflection and meta-object protocols suffer from similar limitations. This is unfortunate since scoping variations to dynamic extents increases the expressiveness of the language in useful ways [START_REF] Tanter | Expressive scoping of dynamically-deployed aspects[END_REF][START_REF] Tanter | Beyond static and dynamic scope[END_REF]. With such variations, it is for instance possible to execute code in a read-only manner [START_REF] Arnaud | Read-only execution for dynamic languages[END_REF] (thus improving safety), or to track all state mutations to ease recovery in case of errors (thus improving reliability), or to trace and profile methods at a fine-grained level (thus improving monitoring).

We show in this paper that with minor changes to the way dynamic proxies operate, it becomes possible to reflectively implement variations that are scoped to dynamic extents. A dynamic proxy [START_REF] Eugster | Uniform proxies for java[END_REF][START_REF] Peck | Efficient proxies in smalltalk[END_REF][START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF] is a special object that mediates interactions between a client object and another target object. When the client sends a message to the proxy, the message is intercepted, reified, and passed to the proxy's handler for further processing. To scope variations to dynamic extents using proxies, we must first slightly adapt the proxy mechanism, then implement specific handlers.

Our adaptation of dynamic proxies has the following characteristics: 1) it supports delegation [START_REF] Lieberman | Using prototypical objects to implement shared behavior in object oriented systems[END_REF] by rebinding self-reference, 2) it intercepts state accesses, both for regular fields and variables captured in closures, 3) it intercepts object creations. We refer to our extension of dynamic proxies as delegation proxies.

With delegation proxies, it becomes possible to implement handlers that will wrap all objects the target object accesses. Such a handler wraps the result of state reads and object instantiations, and unwraps the arguments of state writes. A proxy can consequently encode a variation that will be consistently propagated to all objects accessed during the evaluation of a message send (i.e., its dynamic extent), without impacting objects in the heap.

Delegation proxies have several positive properties. First, delegation proxies do not lead to meta-regressions -infinite recursion that arises in reflective architectures when reflecting on code that is used to implement the reflective behavior itself. In aspectoriented programming, this arises when an advice triggers the associated pointcut. The solutions to this problem generally add an explicit model of the different levels of execution [START_REF] Denker | The meta in meta-object architectures[END_REF][START_REF] Tanter | Execution levels for aspect-oriented programming[END_REF]. With delegation proxies, this problem doesn't appear since the proxy and its target are distinct objects and the propagation is enabled only for the proxy. No variation is active when executing the code of the handler. Second, variations expressed with delegation proxies compose, similarly to aspects. For instance, tracing and profiling variations can be implemented with delegation proxies and then composed to apply both variations. Third, delegation proxies naturally support partial reflection [START_REF] Tanter | Partial behavioral reflection: Spatial and temporal selection of reification[END_REF]. Only the objects effectively accessed in the dynamic extent of an execution involving a proxy pay a performance overhead; all other objects in the system remain unaffected, including the target.

In this paper, we explore and demonstrate the flexibility of delegation proxies in Smalltalk with the following contributions:

• A model of proxies based on delegation that intercepts object instantiations and state accesses (including variables in closures) (Section 2);

• A technique to use delegation proxies to scope variations to dynamic extents (Section 2);

• Several examples of useful applications of variations scoped to dynamic extents (Section 3);

• A formalization of delegation proxies and the propagation technique (Section 4);

• An implementation of delegation proxies in Smalltalk based on code generation (Section 5).

Delegation Proxies

We now describe how delegation proxies work and exemplify them with an implementation of tracing. Let us consider the Smalltalk method Integer>>fib 1 which computes the Fibonacci value of an integer using recursion:

Integer>>fib self < 2 ifTrue: [ self ]. (self -1) fib + (self -2) fib Listing 1. Fibonacci computation
The computation of the Fibonacci value of 2 corresponds to the following sequence of message sends (first the receiver of the message, then the message with its arguments):

2 fib 2 < 2 false ifTrue: [ self ] 2 -1 1 fib 1 < 2 true ifTrue: [ self ] [ self ] value 2 -2 0 fib 0 < 2 true ifTrue: [ self ] [ self ] value 1 + 0

Listing 2. Trace of 2 fib

To automatically trace message sends, we can use delegation proxies to intercept message sends and print them. Like a dynamic proxy, a delegation proxy is a special object that acts as a surrogate for another object, called its target. The behavior of a proxy is defined by a separate object called its handler, whose methods are referred to as traps [START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF]. When an operation (message send, state access, etc.) is applied to a proxy, the proxy reifies the operation and instead invokes the corresponding trap in the handler. When an operation is intercepted, the handler can take some action and can reflectively perform the original operation on the target. Figure 1 shows the relationships between a proxy, a handler and a target.

The distinction between proxies and handlers is called stratification [START_REF] Bracha | Mirrors: design principles for meta-level facilities of object-oriented programming languages[END_REF][START_REF] Peck | Efficient proxies in smalltalk[END_REF][START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF]. Stratification avoids name conflicts between application methods and traps, i.e., between the base-level and the meta-level.

The target and the handler can be regular objects or proxies as well. By using proxies as the targets of other proxies, we obtain a chain of delegation. Each of the variations implemented by the handlers of the proxies in the chain will be triggered in order. This is a natural way to compose different variations together.

A tracing proxy can be obtained by instantiating a proxy with a tracing handler. For convenience, we add method Object>> tracing that returns a tracing proxy for any object. For instance, 2 tracing returns a tracing proxy for the number 2. 1 In Smalltalk, closures are expressed with square brackets ([ . . . ]) and booleans are objects. The method ifTrue: takes a closure as argument: if the receiver is true, the closure is evaluated by sending the message value. The up-arrow (↑ ...) denotes a return statement. Example of message interception. First, the client sends the message msg to a proxy. Then, the proxy intercepts the message and invokes the handler trap associated with message reception (handleMessage:myself:target:) with three arguments: the reified message, the proxy itself and the target.

Object>> tracing

Proxy handler: TracingHandler new target: self.

Listing 3. Creation of a tracing proxy

To trace messages, the tracing handler must define a message trap that prints the name of the reified message. Listing 4 shows the code of such a message trap:

Listing 4. A simple tracing handler

The reflective invocation with perform: takes one additional parameter myself, which specifies how self is rebound in the reflective invocation. The handler can thus either rebind self to the proxy (delegation) or rebind self to the target (forwarding). Delegation proxies thus trivially subsume traditional forwarding proxies. In the case of a reflective invocation with delegation, the method is executed with self rebound to the proxy, which allows the proxy to further intercept operations happening during this method execution.

If the target of a proxy is another proxy, proxies form chains of delegation. The identity of the proxy that received the original message send is consistently passed to the handlers.

Propagation

The tracing handler in the previous section defines a message trap. Doing so ensures that messages received by the proxy are traced, including self-sends in the method executed with delegation. However, it would fail to trace messages sent to other objects. The evaluation of 2 tracing fib would print 2 fib, 2 < 2, 2 -1, 2 -2, but all the messages sent to 1, 0, true, false and [ self ] would not be traced. To consistently apply a variation during the evaluation of a message send, all objects accessed during the evaluation must be represented with proxies. To achieve this, we can implement a handler that replaces all object references accessed by proxies. This way, the variation will propagate during the execution.

In a given method activation, a reference to an object can be obtained from:

• an argument,

• a field read,

• the return value of message sends,

• the instantiation of new objects or the resolution of literals.

The following rules suffice to make sure that all objects are represented with proxies. To distinguish between the initial proxy and the proxies created during the propagation, we call the former the root proxy. We need three rules to control how objects must be wrapped:

• Wrap the initial arguments. When the root proxy receives a message, the arguments must be wrapped with proxies. We don't need to wrap the arguments of other message sends: the following rules ensure that the arguments were already wrapped in the context of the caller.

• Wrap field reads. This way, references to fields are represented with a proxy.

• Wrap object instantiation. The return value of primitive message sends that "create" new objects must be wrapped. Such primitive messages include explicit instantiations with new and arithmetic computations with +,-,/. Similarly, the resolution of literal must be wrapped. We don't need to wrap the return value of other message sends. Indeed, if the receiver and the arguments of a message send are already wrapped, and if the results of state reads and object instantiations are also wrapped in the execution of the method triggered by this message send, this message send will necessarily return a proxy.

Additionally, we need two rules to control how objects must be unwrapped:

• Unwrap field writes. When a field is written, we unwrap the value of the assignment before performing it. This way, the proxies created during the propagation are only referred to from within the call stack and don't pollute the heap via the fields of target objects. They can be garbage collected once the propagation is over.

• Unwrap the initial return value. The root proxy unwraps the objects returned to the clients.

Applying this technique to the code in Listing 1, the subtractions self-2 and self-1 return proxies as well. Figure 2 depicts the situation. This way, tracing is consistently applied during the computation of Fibonacci numbers.

Traps

In Smalltalk, an object is instantiated by sending the message new to a class, which is an object as well. The interception of object instantiations does thus not require a specific trap and is realized indirectly. The following set of traps is thus sufficient to intercept all method invocations, state accesses, and object instantiations:

• handleMessage:myself:target:

The trap for message sends takes as parameters the reified message, the original proxy2 and the target.

• handleReadField:myself:target:

The trap for field reads takes as parameters the field name, the original proxy and the target. A proxy to 2 receives the subtraction message "-" with a proxy to 1 as argument (a). The message is forwarded to 2 to perform the actual subtraction (b) that returns 1 (c). Finally the result is wrapped (d).

• handleWriteField:value:myself:target:

The trap for field writes takes as parameters the field name, the value to write, the original proxy and the target.

• handleLiteral:myself:target:

The trap for the resolution of literals (symbols, string, numbers, class names, and closures) takes as parameters the resolved literal, the original proxy and the target.

Instantiations of objects using literals, e.g., the instantiation of a string, are intercepted with the literal trap. In Java, an additional trap would be needed to intercept constructor invocations. Similar considerations hold for access to static fields and invocation of static methods.

Similarly to perform:, reflective methods to read fields, to write fields and to resolve literals are extended with an additional parameter myself. They become instVarNamed:myself:, instVarNamed:put:myself and literal:myself:. The parameter myself is needed, because proxies may form chains of delegation. When invoked on a proxy, the reflective operations will trigger the corresponding trap. The parameter myself is passed to the traps along the chain to preserve the identity of the proxy that originally intercepted the operation.

With the support of delegation and the ability to intercept state accesses and object instantiations, it becomes possible to implement a handler that realizes the propagation technique. The code of such a propagating handler is shown in Listing 5. We assume the existence of a class Reflect to unwrap proxies.

Closures

A closure should be evaluated with the variations that are active in the current dynamic extent, and not the variations that were active when the closure was created. For instance, if the closure [ self printString ] is created when tracing is enabled, its evaluation during a regular execution should not trace the message printString. Conversely, if the closure [ self printString ] is created during a regular execution, its evaluation when tracing is enabled should trace the message printString. For this to work correctly, closures are always created in an unproxied form, and proxying is only applied on demand.

Variables captured in a closure are stored in indexed fields. Let us describe first how creation works and illustrate it with the closure [ self printString ] and tracing:

1. The closure is created by the runtime and captures variables asis. Tracing example: the closure captures self, which refers to a proxy.

2. The closure creation is intercepted by the literal trap of the creator. Tracing example: the closure is treated like other literals and thus proxied.

3. If the closure was proxied, the runtime invokes the write trap of the closure's proxy for all captured variables. Tracing example: the runtime invokes the write trap of the closure's proxy passing 0 as field index and the self proxy as value. The trap unproxies the value and reflectively invokes instVarNamed:put:myself: for field 0. This overwrites the previous value in the closure with a reference to the base object.

Evaluation of closures follows the inverse scheme:

1. If the closure is evaluated via a proxy, the runtime invokes the read trap each time a captured variable is accessed. Tracing example: the runtime invokes the read trap of the closure's proxy passing 0 as field index. The trap reflectively invokes instVarNamed: for field 0 and wraps the result with a proxy. The message printString is sent to the proxy.

Note that this scheme is quite natural if we consider that closures could be encoded with regular objects, similarly to anonymous classes in Java. In that case, captured variables are effectively stored in synthetic fields initialized in the constructor. The instanti-ation of the anonymous class would trigger write traps, and evaluation would trigger read traps.

Adding method valueWithHandler: in BlockClosure, tracing 2 fib can also be achieved with [ 2 fib ] valueWithHandler: TracingHandler new instead of 2 tracing fib. When we evaluate the closure via the proxy, the literal trap will wrap 2 before it is used. Closures provide a convenient way to activate a behavioral variation in the dynamic extent of expression. Listing 6. Convenience method to wrap and evaluate a closure

Transparency

Traps are implemented in a separate handler and not in the proxy itself. If an application defines an application-level method whose name collides with the name of a trap, the explicit invocation of this method will be trapped by the handler. It avoids conflicts between the base-level and the meta-level. The proxy can expose the exact same interface as its target.

It is impossible to deconstruct a proxy to obtain its handler or its target without using reflective capabilities. Since security is not a concern, we assume for simplicity the existence of a class Reflect that exposes the following methods globally:

• Reflect class>>isProxy: aProxy

Returns whether the argument is a proxy or not.

• Reflect class>>handlerOf: aProxy If the argument is a proxy, returns its handler. Fails otherwise.

• Reflect class>>targetOf: aProxy If the argument is a proxy, returns its target. Fails otherwise.

For increased security, these methods could to be stratified with mirrors [START_REF] Bracha | Mirrors: design principles for meta-level facilities of object-oriented programming languages[END_REF], in which case handlers would need to have access to a mirror when instantiated.

Examples

Since delegation proxies subsume dynamic proxies, they can be used to implement all classic examples of dynamic proxies like lazy values, membranes, remote stubs, etc. We omit such examples that can be found elsewhere in the literature [START_REF] Cutsem | Trustworthy proxies: Virtualizing objects with invariants[END_REF][START_REF] Eugster | Uniform proxies for java[END_REF][START_REF] Peck | Efficient proxies in smalltalk[END_REF].

We focus in this section on new examples enabled by delegation proxies. They all rely on the propagation technique presented earlier. We assume that the handlers inherit from the class PropHandler that implements the propagation technique for reuse (see Listing 5).

Object Versioning

To tolerate errors, developers implement recovery blocks that undo mutations and leave the objects in a consistent state [START_REF] Pluquet | Executing code in the past: efficient in-memory object graph versioning[END_REF]. Typically, this requires cloning objects to obtain snapshots. Delegation proxies enable the implementation of object versioning elegantly. Before any field is mutated, the handler shown below records the old value into a log using a reflective field read. The log can be used in recovery block, for instance to implement rollback. Similarly to the other examples that follow, we assume that the handler inherits from a base handler that implements the propagation technique. Listing 9. Undoing changes (m stands for mutation)

Read-only Execution

Read-only execution [START_REF] Arnaud | Read-only execution for dynamic languages[END_REF] prevents mutation of state during evaluation. Read-only execution can dynamically guarantee that the evaluation of a given piece of code is either side-effect free or raises an error.

Classical proxies could restrict the interface of a given object to the subset of read-only methods. However, they would fail to enable read-only execution of arbitrary functions, or to guarantee that methods are deeply read-only. Read-only execution can be implemented trivially using propagation and a handler that fails upon state writes.

ReadOnlyHandler>>handleWriteField: f value: v myself: p target: t ReadOnlyError signal: 'Illegal write'.

Listing 10. Read-only handler

Dynamic Scoping

In most modern programming languages, variables are lexically scoped and can't be dynamically scoped. Dynamic scoping is sometimes desirable, for instance in web frameworks to access easily the ongoing request. Developers must in this case use alternatives like thread locals. It is for instance the strategy taken by Java Server Faces in the static method getCurrentInstance() of class FacesContext 3 ).

Dynamic scoping can be realized in Smalltalk using stack manipulation [START_REF] Deutsch | Building control structures in smalltalk-80[END_REF] or by accessing the active process. Delegation proxies offer an additional approach to implement dynamic bindings by simply sharing a common (key,value) pair between handlers. If multiple dynamic bindings are defined, objects will be proxied multiple times, once per binding. When a binding value must be retrieved, a utility method locates the handler corresponding to the request key, and returns the corresponding value:

Profiling

Previous sections already illustrated delegation proxies using tracing. The exact same approach could be used to implement other interceptors like profiling or code contracts. The following handler implements profiling. It stores records of the different execution durations in an instance variable tallies for later analysis.

Semantics

We formalize delegation proxies by extending SMALLTALKLITE [START_REF] Bergel | Stateful traits and their formalization[END_REF], a lightweight calculus in the spirit of CLASSICJAVA [START_REF] Flatt | Classes and mixins[END_REF] that omits static types. This paper does not assume any prior knowledge of it. Our formalization simplifies three aspects of the semantics presented in the previous sections: it doesn't model first-class classes, literals or closures. Consequently, a literal trap does not make sense. Instead, we introduce a new trap that intercepts object instantiations.

The syntax of our extended calculus, SMALLTALKPROXY, is shown in Figure 3 Redexes and their subredexes reduce to a value, which is either an address a, nil, or a proxy. A proxy has a handler h and a target t. A proxy is itself a value. Both h and t can be proxies as well. Redexes may be evaluated within an expression context E. An expression context corresponds to an redex with a hole that can be filled with another redex. For example, E[expr] denotes an expression that contains the sub-expression expr.

Translation from the main expression to an initial redex is carried out by the o[[e]] c function (see Figure 4). This binds fields to their enclosing object context and binds self to the value o of the receiver. The initial object context for a program is nil. (i.e., there P ⊢ ǫ, S ֒→ ǫ ′ , S ′ means that we reduce an expression (redex) ǫ in the context of a (static) program P and a (dynamic) store of objects S to a new expression ǫ ′ and (possibly) updated store S ′ . The store consists of a set of mappings from addresses a ∈ dom(S) to tuples c, {f → v} representing the class c of an object and the set of its field values. The initial value of the store is S = {}.

o[[new c]] c = new o c o[[x]] c = x o[[self]] c = o o[[nil]] c = nil o[[f ]] c = f o o[[f = e]] c = f o = o[[e]] c o[[e.m(e * i )]] c = o[[e]] c .m o (o[[ei]] * c ) o[[super.m(e * i )]] c = super c .m o (o[[ei]] * c ) o[[let x = e in e ′ ]] c = let x = o[[e]] c in o[[e ′ ]] c o[[proxy e e ′ ]] c = proxy o[[e]] c o[[e ′ ]] c
ǫ = o | new o c | x | self | nil | f o | f o = ǫ | ǫ.m o (ǫ * ) | super c .m o (ǫ * ) | let x = ǫ in ǫ E = [ ] | f o = E | E.m o (ǫ * ) | o.m o (o * E ǫ * ) | super c .m o (o * E ǫ * ) | let x = E in ǫ | proxy E ǫ | proxy o E o, h, t = nil | a | proxy h t
The reductions are summarized in Figure 6. Predicate ∈ * P is used for field lookup in a class (f ∈ * P c) and method lookup ( c, m, x * , e ∈ * P c ′ , where c ′ is the class where the method was found in the hierarchy). Predicates ≤P and ≺P are used respectively for subclass and direct subclass relationships.

If the object context o of an instantiation with new o c is an object (i.e., not a proxy), the expression reduces to a fresh address a, bound in the store to an object whose class is c and whose fields are all nil(reduction [new]). If the object context of the instantiation is a proxy, the newTrap is invoked on the handler instead (reduction [new-proxy]). The trap takes the result of the instantiation new t c as parameter; it can take further action or return it as-is.

The object context o of field reads and field writes can be an object or a proxy. A local field read in the context of an object address reduces to the value of the field (reduction [get]). A local field read in the context of a proxy invokes the trap readTrap on the handler h (reduction [get-proxy]). A local field write in the context of an object simply updates the corresponding binding of the field in the store (reduction [set]). A local field read in the context of a proxy invokes the trap writeTrap on the handler h (reduction [setproxy]).

Messages can be sent to an object or to a proxy. When we send a message to an object, the corresponding method body e is lookedup, starting from the class c of the receiver a. The method body is then evaluated in the context of the receiver, binding self to the address a. Formal parameters to the method are substituted by the actual arguments. We also pass in the actual class in which the method is found, so that super sends have the right context to start their method lookup (reduction [message]). When a message is sent to a proxy, the trap messageTrap is invoked on the handler. The object context s that decorates the message corresponds to the sender of the message. The trap takes as parameters the message and its arguments, and the initial receiver of the message proxy h t.

Super-sends are similar to regular message sends, except that the method lookup must start in the superclass of the class of the method in which the super send was declared. In the case of super-send, the object context s corresponds to the sender of the message as well as the receiver. The object context is used to rebind self (reduction [super]). When we reduce the super-send, we must take care to pass on the class c ′′ of the method in which the super reference was found, since that method may make further supersends.

Finally, let in expressions simply represent local variable bindings (reduction [let]). Errors occur if an expression gets ÒstuckÓ and does not reduce to an a or to nil. This may occur if a nonexistent variable, field or method is referenced (for example, when sending any message to nil, or applying traps on a handler h that isn't suitable). For the purpose of this paper we are not concerned with errors, so we do not introduce any special rules to generate an error value in these cases.

Identity Proxy

As was discussed in subsection 2.4, the system requires the ability to reflectively apply operations on base objects and proxies to be useful. For simplicity, we extend the language with three additional non-stratified reflective primitives: send, read, and write. The semantics of these primitives is given in Figure 7.

All three primitives take a last argument my (shortcut for "myself") representing the object context that will be rebound. When applied to a proxy, the operations invoke the corresponding trap in a straightforward manner, passing my as-is. When read or write is applied to an object address, the argument my is ignored. When send is applied to an object address, my defines how self will be rebound during the reflective invocation.

With these primitives, we can trivially define the identity handler, idHandler. idHandler is an instance of a handler class that defines the following methods:

newTrap(t, my) = t readTrap(t, f, my) = t.read(f, my) writeTrap(t, f, o, my) = t.write(f, o, my) messageTrap(t, m, o * , my) = t.send(m, o * , my)
We can show that sending a message to an identity proxy will delegate the message to the target, and rebind self to the proxy.

Let us consider an object s that sends the message m(o) to a proxy p = proxy idHandler t. Object t is an instance of class c which defines method m(x) with body e = self n(x).

p.m s (o) idHandler.messageTrap(t, m, o, p) [send-proxy] t.send(m, o, p) [send] p[[e[o/x]]] c [reflect-message] p[[self]] c .n p (o)
[translation] p.n p (o)

[translation]

Propagating Identity Proxy

Following the technique of propagation presented in subsection 2.1, we propose a propagating identity handler, propHandler. This handler defines the behavior of the root proxy and uses another handler propHandler * to create the other proxies during propagation. This technique requires the ability to unwrap a proxy. The expression unproxy is added to the language as defined in Figure 7. We also assume the existence of the traditional sequencing (;) operation. We can formally express the intuitive explanation of subsection 2.1 about soundness of the propagation.

P ⊢ E[new r c], S ֒→ E[a], S[a → c, {f → nil | ∀f, f ∈ * P c} ] [
P ⊢ E[f a = o], S ֒→ E[o], S[a → c, F [f → o] ] [set] where S(a) = c, F P ⊢ E[f proxy h t = o],
Let us assume that all values in the expression E[e] are proxies (using the propHandler * ). The reduction rules that can match are [new-proxy], [get-proxy], [set-proxy], [super], [let], and [messageproxy]. According to the definition of the propHandler * traps, rule [new-proxy] will preserve the invariant that all values are proxies. Rule [get-proxy] does so as well. Rule [set-proxy] preserves the assumption since it returns the value written, which we know is a proxy. Rules [super] and [let] do as well since they only bind variables with existing values, which we know are proxies. Similarly, rule [message-proxy] will bind self with the expected proxy (see previous section). It also binds the variables with the passed arguments, which are known to be proxies. Since all values remain proxies, the evaluation is consistent.

The initial redex is evaluated with nil as object context: nil[[e]] Object . If the proxy p = proxy propHandler nil is used instead of nil, the assumption is initially true, and will not be broken during evaluation.

Implementation

We have implemented a prototype of delegation proxies in Smalltalk that relies on code generation. For each existing method in a base class, a hidden method with an additional parameter myself and a transformed body is generated. Instead of self, myself is used in the generated method body (this is similar to Python's explicit self argument). Following the same approach as Uniform Proxies for Java [START_REF] Eugster | Uniform proxies for java[END_REF], proxy classes are auto-generated. Let us consider the class Suitcase:

Listing 16. Sample generated method in proxy class of Suitcase

Classes

Smalltalk has first-class classes whose behaviors are defined in meta-classes. The class and meta-class hierarchies are parallel. Classes can be proxied like any object. Consequently, meta-classes are rewritten and extended with synthetic methods similarly to classes. However, the generated proxy classes do not inherit from Class, but Proxy, as is shown in Figure 8.

Closures

Closures are regular objects that are adapted upon creation and evaluation according to subsection 2.3. When a closure defined in an original uninstrumented method is proxied, the code of the closure is transformed lazily.

Weaving

Sending a message to a proxy entails reification of the message, invocation of the handler's trap, and then reflective invocation of the message on the target. In addition, the handler might take additional actions that entail costs. The handler and the proxy can be woven into specialized classes for less levels of indirection. For instance, a SuitcaseProxy with a Tracing handler can be woven into a

Listing 17. Sample woven method

We have implemented a simple weaver that works for basic cases. We plan to mature it in the future and leverage techniques for partial evaluation [START_REF] Futamura | Partial evaluation of computation process: An approach to a compiler-compiler[END_REF] developed for aspect compilers [START_REF] Masuhara | A compilation and optimization model for aspect-oriented programs[END_REF].

Performance

Delegation proxies have no impact on performance if not used: the transformation adds new code but does not alter the existing one. When used, we need to distinguish between the performance of delegation proxies themselves and the overhead of the propagation technique.

Used sparingly, delegation proxies do not entail performance issues. The situation is similar to traditional forwarding proxies. Used extensively with our propagation technique, the cost of delegation proxies is prohibitive unless weaving is used. With weaving, benchmarks of Fibonacci4 reveal a performance degradation of below one order of magnitude (8x slower). We believe it is an encouraging result given that delegation proxies enable unanticipated behavioral reflection, which is known to be costly.

With our propagation technique, a given object might be wrapped multiple times, producing multiple equivalent proxies. In the Fibonacci examples, 1, 2 and [^self] are literals that are intercepted and wrapped thousands of times. This increases the number of objects created and puts pressure on the garbage collector. Future work could address this issue, possibly with caching.

Discussion

Scoping Scoping variations to dynamic extents was the motivation for delegation proxies. However, the propagation technique that we have presented covers only one particular form of scoping that can be realized with delegation proxies. Since the propagation is implemented reflectively, it can be customized in many ways.

If a variation is active in all threads, the propagation can for instance be adapted to proxy only instances of application classes and skip kernel classes (string, dictionaries, arrays, etc.). The application and the kernel form two layers. Any application object referenced by a kernel object has necessarily been provided by the application. If application objects are wrapped, this guarantees that kernel objects hold only references to proxies of application objects. Therefore, if a kernel object sends a message to an application object, the propagation will start again. Kernel objects will not unwrap proxies of application objects upon state writes, and the heap might contain references to proxies (the variation must thus be active in all threads). Omitting kernel objects from the propagation could be desirable to improve performance.

It is also be possible to adapt the propagation so that the root proxies doesn't unwrap the objects they returns. Variations that apply security concerns such as access control usually have this requirement [START_REF] Arnaud | Read-only execution for dynamic languages[END_REF].

Closure wrapping rules can also be customized in different ways. It is possible to control which closures should "escape" the dynamic extent as proxy or not. If a method parallelizes work internally using multiple threads, the propagation could for instance be customized to propagate to those threads as well. Or if the system uses callbacks, the callbacks could restore the variation that was active when they were created.

Delegation proxies provide flexible building blocks to implement various forms of scopes, possibly blurring the line between static and dynamic scoping, similarly to Tanter's scoping strategies [START_REF] Tanter | Beyond static and dynamic scope[END_REF].

Static Typing There is no major obstacle to port our implementation to a statically-typed language. Delegation proxies preserve the interface of their target, like traditional forwarding proxies. For type compatibility, the generated proxy must inherit from the original class. Reflective operations can fail with run-time type errors. Forwarding and delegation proxies suffer from the same lack of type safety from this perspective.

If closures cannot be adapted at run time with the same flexibility as in Smalltalk, the implementation might require a global rewrite of the sources to adapt the code of the closures at compiletime.

Delegation proxies require that reflective operations have an additional parameter that specifies how to rebind self. Naturally, this parameter must be of a valid type: in practice it will be either the target of the invocation or a proxy of the target. Both implement the same interface.

Related Work

Method Dispatch MOPs, AOP and proxies are various approaches that enable the interception and customization of method dispatch. MOPs reify the execution into meta-objects that can be customized [START_REF] Kiczales | The Art of the Metaobject Protocol[END_REF]. AOP adopts another perspective on the problem and enables the definition of join points where additional logic is woven [START_REF] Kiczales | Aspect-oriented programming[END_REF]. MOP and AOP share similarities with method combination of CLOS [START_REF] Demichiel | The Common Lisp object system: An overview[END_REF].

Many languages provide support for dynamic proxies. When a message is sent to a dynamic proxy, the message is intercepted and reified. Dynamic proxies have found many usefully applications that can be categorized as "interceptors" or "virtual objects" [START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF]. An important question for proxies is whether to support them natively at the language level or via lower-level abstractions.

Most dynamic languages support proxies via traps that are invoked when a message cannot be delivered [START_REF] Peck | Efficient proxies in smalltalk[END_REF]. However, modern proxy mechanisms stratify the base and meta levels with a handler [START_REF] Eugster | Uniform proxies for java[END_REF][START_REF] Peck | Efficient proxies in smalltalk[END_REF][START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF], including Java that uses code generation to enable proxies for interfaces. The mechanism was extended to enable proxies of classes as well [START_REF] Eugster | Uniform proxies for java[END_REF].

AOP and MOP inherently suffer from meta-regression issues, unless the meta-levels are explicitly modeled [START_REF] Chiba | Avoiding confusion in metacircularity: The meta-helix[END_REF][START_REF] Denker | The meta in meta-object architectures[END_REF][START_REF] Tanter | Execution levels for aspect-oriented programming[END_REF]. In contrast to AOP and MOPs, delegation proxies do not suffer from meta-regression issues since the adapted object and the base object are distinct. For instance, the tracing handler in Listing 4 does not lead to a meta-regression since it sends the message asString to the target, which is distinct from the proxy (in parameter myself). System code can in this way be adapted. Also, delegation proxies naturally enable partial reflection [START_REF] Tanter | Partial behavioral reflection: Spatial and temporal selection of reification[END_REF] since objects are selectively proxied.

Recent works on proxies in dynamic languages have studied orthogonal issues related to stratification [START_REF] Cutsem | On the design of the ECMAScript reflection api[END_REF][START_REF] Van Cutsem | Proxies: design principles for robust object-oriented intercession apis[END_REF], preservation of abstractions and invariants [START_REF] Cutsem | Trustworthy proxies: Virtualizing objects with invariants[END_REF][START_REF] Strickland | Chaperones and impersonators: Run-time support for reasonable interposition[END_REF], and traps for values [START_REF] Austin | Virtual values for language extension[END_REF]. Only Javascript direct proxies support delegation [START_REF] Cutsem | Trustworthy proxies: Virtualizing objects with invariants[END_REF]. However, Javascript proxies do not enable the interception of object instantiations; the variables captured in a closure will not be unproxied upon capture and proxied upon evaluation.

In addition to full-fledged MOPs and AOP, reflective language like Smalltalk provide various ways to intercept message sends [START_REF] Ducasse | Evaluating message passing control techniques in Smalltalk[END_REF]. Java and .NET support custom method dispatch via JSR 292 [START_REF]Jsr 292: Supporting dynamically typed languages on the java platform[END_REF] and the Dynamic Language Runtime [START_REF]Microsoft .net dynamic language runtime[END_REF].

Composing Behavior Inheritance leads to an explosion in the number of classes when multiple variations (decorations) of a given set of classes must be designed. Static traits [START_REF] Schärli | Traits: Composable units of behavior[END_REF] or mixins enable the definition of units of reuse that can be composed into classes, but they do not solve the issue of class explosion.

One solution to this problem is the use of decorators that refine a specific set of known methods, e.g., the method paint of a window. Static and dynamic approaches have been proposed to decoration. Unlike decorators, proxies find their use when the refinement applies to unknown methods, e.g., to trace all invocations. Büchi and Weck proposed a mechanism [START_REF] Büchi | Generic wrappers[END_REF] to statically parameterize classes with a decorator (called wrapper in their terminology). Bettini et al. [START_REF] Bettini | Featherweight wrap java[END_REF] proposed a similar construct but composition happens at creation time. Ressia et al. proposed talents [START_REF] Ressia | Talents: an environment for dynamically composing units of reuse[END_REF] which enable adaptations of the behavior of individual objects by composing trait-like units of behavior dynamically. Other works enable dynamic replacement of behavior in a trait-like fashion [START_REF] Bettini | On flexible dynamic trait replacement for java-like languages[END_REF].

The code snippet below illustrates how to achieve the decoration of a Window with a Border and shows the conceptual differences between these approaches. The two first approaches can work with forwarding or delegation (but no implementations with delegation are available). The third approach replaces the behavior or the object so the distinction does not apply. Listing 18. Differences between approaches to decoration Several languages that combine class-based inheritance and object inheritance (i.e., delegation) have been proposed [START_REF] Kniesel | Type-safe delegation for run-time component adaptation[END_REF][START_REF] Viega | Automated delegation is a viable alternative to multiple inheritance in class based languages[END_REF]. Delegation enables the behavior of an object to be composed dynamically from other objects with partial behaviors. Essentially, delegation achieves trait-like dynamic composition of behavior.

Ostermann proposed delegation layers [START_REF] Ostermann | Dynamically composable collaborations with delegation layers[END_REF], which extend the notion of delegation from objects to collaborations of nested objects, e.g., a graph with edges and nodes. An outer object wrapped with a delegation layer will affects its nested objects as well. Similary to decorators, the mechanism refines specific sets of methods of the objects in the collaboration.

Dynamic Scoping

The dynamic extent of an expression corresponds to all operations that happen during the evaluation of the expression by a given thread of execution. Control-flow pointcuts are thus not sufficient to scope to dynamic extents, since they lack control over the thread scope. Control-flow pointcuts are popular and supported by mainstream AOP implementations, e.g., As-pectJ's flow and cfbelow. Aware of the limitations of control-flow pointcuts, some AOP implementations provide specific constructs to scope to dynamic extents, e.g., CaesarJ's deploy [START_REF] Aracic | An overview of CaesarJ[END_REF]. Implemented naively, control-flow pointcuts are expensive since they entail a traversal of the stack at run time, but they can be implemented efficiently using partial evaluation [START_REF] Masuhara | A compilation and optimization model for aspect-oriented programs[END_REF].

In context-oriented programming (COP) [START_REF] Hirschfeld | Context-oriented programming[END_REF][START_REF] Löwis | Context-oriented programming: Beyond layers[END_REF], variations can be encapsulated into layers that are dynamically activated in the dynamic extent of an expression. Unlike delegation proxies that support homogenous variations, COP supports best heterogenous variations [START_REF] Apel | Aspectual feature modules[END_REF]. COP can be seen as a form of multi-dimensional dispatch, where the context is an additional dimension.

Other mechanisms to vary the behavior of objects in a contextual manner are roles [START_REF] Kristensen | Object-oriented modeling with roles[END_REF], perspectives [START_REF] Smith | A simple and unifying approach to subjective objects[END_REF], and subjects [START_REF] Harrison | Subject-oriented programming (a critique of pure objects)[END_REF]. Delegation proxies can realize dynamic scoping via reference flow, by proxying and unproxying objects accesses during the execution. Delegation proxies can provide a foundation to design contextual variations.

Similarly to our approach, the handle model proposed by Arnaud et al. [START_REF] Arnaud | Towards First Class References as a Security Infrastructure in Dynamically-Typed Languages[END_REF][START_REF] Arnaud | Read-only execution for dynamic languages[END_REF] enables the adaptation of references with behavioral variations that propagate. The propagation belongs to the semantics of the handles, whereas in our approach, the propagation is encoded reflectively. Propagation unfolds from a principled use of delegation. Our approach is more flexible since it decouples the notion of propagation from the notion of proxy.

Conclusions

We can draw the following conclusions about the applicability of delegation proxies:

• Expressiveness. Delegation proxies subsume forwarding proxies and enable variations to be propagated to dynamic extents. This suits well non-functional concerns like monitoring (tracing, profiling), safety (read-only references), or reliability (rollback with object versioning). Since the propagation is written reflectively, it can be customized to achieve other forms of scopes.

• Metaness. Delegation proxies naturally compose, support partial behavioral reflection, and avoid meta-regressions. We can for instance trace and profile an execution by using tracing proxies and profiling proxies that form chains of delegation (composition). Objects are wrapped selectively. Adapting objects during an execution will not affect other objects in the system (partial reflection). Proxies and targets represent the same object at two different levels but have distinct identities (no meta-regression).

• Encoding. Delegation proxies can be implemented with code generation. In our Smalltalk implementation, only new code needs to be added; existing code remains unchanged. Delegation proxies have thus no overhead if not used. Delegation proxies do not entail performance issues when used sporadically (same situation as with forwarding proxies). The overhead of our propagation technique is of factor 8 when handlers are woven into dedicated proxies. Excluding system classes, if viable, can improve performance further. For optimal performance, the language should provide native support of delegation proxies.

In the future, we plan to further mature our implementation, notably the weaver, and explore native support at the VM level.

  Figure1. Example of message interception. First, the client sends the message msg to a proxy. Then, the proxy intercepts the message and invokes the handler trap associated with message reception (handleMessage:myself:target:) with three arguments: the reified message, the proxy itself and the target.

Figure 2 .

 2 Figure2. Illustration of propagation during the subtraction 2 -1. A proxy to 2 receives the subtraction message "-" with a proxy to 1 as argument (a). The message is forwarded to 2 to perform the actual subtraction (b) that returns 1 (c). Finally the result is wrapped (d).

  BlockClosure>> valueWithHandler: aHandler (Proxy handler: aHandler target: self) value.

3

  http://www.webcitation.org/6FOF4DFab

  ProfilingHandler>>initialize tallies := OrderedCollection new ProfilingHandler>>handleMessage: m myself: p target: t | start | start := Time now. [ super handleMessage: m myself: p target: t ] ensure: [ | duration | duration := Time now -start. tallies add: {t. m. duration} ] Listing 12. A simple profiling handler

Figure 3 .

 3 Figure 3. Syntax of SMALLTALKPROXY

Figure 4 .

 4 Figure 4. Translating expressions to redexes

Figure 5 .

 5 Figure 5. Redex syntax

Figure 8 .

 8 Figure 8. Inheritance of classes, meta-classes, and auto-generated proxy classes.

  Window w = new Window<Border>(); // Buchi and Weck Window w = new BorderWrap( new Window() ); // Bettini Window w = new WindowEmptyPaint(); // Ressia w.acquire( new BorderedPaint() );

  Listing 8. Enabling recordingThe log can then be used to reflectively undo changes if needed.

	BlockClosure>>recordInLog: aLog
	self valueWithHandler: (RecordingHandler log: aLog)
	aLog reverseDo: [ :m |
	m first instVarNamed: m second put: m third
	]
	RecordingHandler>>handleWriteField: f value: v
	myself: p target: t
	| oldValue |
	oldValue := t instVarNamed: f.

log add: { t. f. oldValue }. super handleWriteField: f value: v myself: p target: t Listing 7. Recording handler A convenience method can be added to enable recording with [. . .] recordInLog: aLog.

  Listing 11. Inspection of a chain of proxies During the evaluation of a block, a dynamic variable can be bound with [. . .] valueWith: #currentRequest value: aRequest and accessed pervasively with ScopeUtils valueOf: #currentRequest

	ScopeUtils>>valueOf: aKey for: aProxy
	| h p |
	p := aProxy.
	[ Reflect isProxy: p ] whileTrue: [
	h := Reflect handlerOf: p.
	( h bindingKey == aKey ) ifTrue: [
	h bindingValue.
	].
	p := Reflect targetOf: p.
	].
	nil. "Not found"

for: self.

  ]]] c ′′ ], S [super] where c ≺P c ′ and c ′ , m, x * , e ∈ * P c ′′ and c ′ ≤P c ′′ , my) = unproxy(t.send(m, (proxy propHandler * o 1 , . . . , proxy propHandler * o n ), my))

		S ֒→ E[h.writeTrap(t, f, o, proxy h t)], S	[set-proxy]
	P ⊢	E[a.m s (o [message] where S[a] = c, F and c, m, x * , e ∈ * P c ′
	P ⊢	E[(proxy h t).m s (o * )], S ֒→ E[h.messageTrap(t, m, o * , proxy h t)], S	[message-proxy]
	P ⊢ E[super c .m s (o P ⊢ E[let x = o in ǫ], S ֒→ E[ǫ[o/x]], S	[let]
		Figure 6. Reductions for SMALLTALKPROXY
	P ⊢ E[a.send(m, o P ⊢ E[unproxy(proxy h t)], S ֒→ E[t], S	[unproxy]
		Figure 7. Reflective facilities added to SMALLTALKPROXY
	The handler propHandler is defined as follows:
	newTrap(t, my)	= proxy propHandler * t
	readTrap(t, f, my)	= proxy propHandler * my))
	writeTrap(t, f, o, my)	= t.write(f, unproxy o, my); o
	messageTrap(t, m, o The handler propHandler * is defined as follows:
	newTrap(t, my)	= proxy propHandler * t
	readTrap(t, f, my)	= proxy propHandler * (t.read(f, my))
	writeTrap(t, f, o, my)	= t.write(f, unproxy o, my); o
	messageTrap(t, m, o

* )], S ֒→ E[a[[e[o * /x * ]]] c ′ ], S * )], S ֒→ E[s[[e[o * /x * * , my)], S ֒→ E[my[[e[o * /x * ]]] c ′ ], S [reflect-message] where S[a] = c, F and c, m, x * , e ∈ * P c ′ P ⊢ E[(proxy h t).send(m, o * , my)], S ֒→ E[h.messageTrap(t, m, o * , my)], S [reflect-message-proxy] P ⊢ E[a.read(f, my)], S ֒→ E[o], S [reflect-get] where S(a) = c, F and F (f ) = o P ⊢ E[(

proxy h t).read(f, my)], S ֒→ E[h.readTrap(t, f, my)], S [reflect-get-proxy] P ⊢ E[a.write(f, o, my)], S ֒→ E[o], S[a → c, F [f → o] ] [reflect-set] where S(a) = c, F P ⊢ E[(proxy h t).write(f, o, my)], S ֒→ E[h.writeTrap(t, f, o, my)], S [reflect-set-proxy] * * , my) = t.send(m, o * , my)

In case of chain proxies, the original proxy is not necessarily the one that intercepted the operation but the root of the chain

CogVM 6.0, Mac OS X, 2.3 GHz Intel Core

Acknowledgments

We thank Jorge Ressia, Mircea Lungu, Niko Schwarz and Jan Kurš for support and feedback about ideas in the paper. We gratefully acknowledge the financial support of the Swiss National Science Foundation for the project "Agile Software Assessment" (SNSF project Np. 200020-144126/1, Jan 1, 2013 -Dec. 30, 2015) and of the French General Directorate for Armament (DGA).

Object>>subclass: #Suitcase instanceVariableNames: 'content' Suitcase>>printString 'Content: ' concat: content.

Listing 13. Original code of class Suitcase

Applying our transformation, the class Suitcase is augmented with synthetic methods to read and write the field content and to resolve literals. In Smalltalk, fields are encapsulated and can be accessed only by their respective object. The sender of a state access is always myself, and can thus be omitted from the traps. For each existing method in class Suitcase, a hidden method with a transformed body and one additional parameter myself is generated.

Suitcase>>printStringMyself: slf ( slf literal: 'Content: ' myself: slf ) concat: (self readContentMyself: slf).

Listing 15. A transformed version of method printString

A proxy class for Suitcase is then generated. It inherits from a class Proxy, which defines the handler field common to all proxies. The generated class implements the same methods as the Suitcase class, i.e., printString, printStringMyself:, readContentMyself:, and writeContent:myself:. The methods invoke respectively message, read and write traps on the handler.