e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
793 lines
29 KiB
Plaintext
793 lines
29 KiB
Plaintext
//-----------------------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
namespace System.Activities.Runtime
|
|
{
|
|
using System;
|
|
using System.Activities.DynamicUpdate;
|
|
using System.Collections.ObjectModel;
|
|
using System.Runtime;
|
|
using System.Runtime.Serialization;
|
|
using System.Collections.Generic;
|
|
|
|
[DataContract]
|
|
internal sealed class LocationEnvironment : ActivityInstanceMap.IActivityReferenceWithEnvironment
|
|
{
|
|
static DummyLocation dummyLocation = new DummyLocation();
|
|
|
|
bool isDisposed;
|
|
bool hasHandles;
|
|
ActivityExecutor executor;
|
|
|
|
// These two fields should be null unless we're in between calls to Update() and OnDeserialized().
|
|
// Therefore they should never need to serialize.
|
|
IList<Location> locationsToUnregister;
|
|
IList<LocationReference> locationsToRegister;
|
|
|
|
Location[] locations;
|
|
bool hasMappableLocations;
|
|
LocationEnvironment parent;
|
|
|
|
Location singleLocation;
|
|
|
|
// This list keeps track of handles that are created and initialized.
|
|
List<Handle> handles;
|
|
|
|
// We store refCount - 1 because it is more likely to
|
|
// be zero and skipped by serialization
|
|
int referenceCountMinusOne;
|
|
|
|
bool hasOwnerCompleted;
|
|
|
|
// this ctor overload is to be exclusively used by DU
|
|
// for creating a LocationEnvironment for "noSymbols" ActivityInstance
|
|
internal LocationEnvironment(LocationEnvironment parent, int capacity)
|
|
: this(null, null, parent, capacity)
|
|
{
|
|
}
|
|
|
|
internal LocationEnvironment(ActivityExecutor executor, Activity definition)
|
|
{
|
|
this.executor = executor;
|
|
this.Definition = definition;
|
|
}
|
|
|
|
internal LocationEnvironment(ActivityExecutor executor, Activity definition, LocationEnvironment parent, int capacity)
|
|
: this(executor, definition)
|
|
{
|
|
this.parent = parent;
|
|
|
|
Fx.Assert(capacity > 0, "must have a positive capacity if using this overload");
|
|
if (capacity > 1)
|
|
{
|
|
this.locations = new Location[capacity];
|
|
}
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "locations")]
|
|
internal Location[] SerializedLocations
|
|
{
|
|
get { return this.locations; }
|
|
set { this.locations = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "hasMappableLocations")]
|
|
internal bool SerializedHasMappableLocations
|
|
{
|
|
get { return this.hasMappableLocations; }
|
|
set { this.hasMappableLocations = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "parent")]
|
|
internal LocationEnvironment SerializedParent
|
|
{
|
|
get { return this.parent; }
|
|
set { this.parent = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "singleLocation")]
|
|
internal Location SerializedSingleLocation
|
|
{
|
|
get { return this.singleLocation; }
|
|
set { this.singleLocation = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "handles")]
|
|
internal List<Handle> SerializedHandles
|
|
{
|
|
get { return this.handles; }
|
|
set { this.handles = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "referenceCountMinusOne")]
|
|
internal int SerializedReferenceCountMinusOne
|
|
{
|
|
get { return this.referenceCountMinusOne; }
|
|
set { this.referenceCountMinusOne = value; }
|
|
}
|
|
|
|
[DataMember(EmitDefaultValue = false, Name = "hasOwnerCompleted")]
|
|
internal bool SerializedHasOwnerCompleted
|
|
{
|
|
get { return this.hasOwnerCompleted; }
|
|
set { this.hasOwnerCompleted = value; }
|
|
}
|
|
|
|
internal Activity Definition
|
|
{
|
|
get;
|
|
private set;
|
|
}
|
|
|
|
internal LocationEnvironment Parent
|
|
{
|
|
get
|
|
{
|
|
return this.parent;
|
|
}
|
|
set
|
|
{
|
|
this.parent = value;
|
|
}
|
|
}
|
|
|
|
internal bool HasHandles
|
|
{
|
|
get
|
|
{
|
|
return this.hasHandles;
|
|
}
|
|
}
|
|
|
|
MappableObjectManager MappableObjectManager
|
|
{
|
|
get
|
|
{
|
|
return this.executor.MappableObjectManager;
|
|
}
|
|
}
|
|
|
|
internal bool ShouldDispose
|
|
{
|
|
get
|
|
{
|
|
return this.referenceCountMinusOne == -1;
|
|
}
|
|
}
|
|
|
|
internal bool HasOwnerCompleted
|
|
{
|
|
get
|
|
{
|
|
return this.hasOwnerCompleted;
|
|
}
|
|
}
|
|
|
|
Activity ActivityInstanceMap.IActivityReference.Activity
|
|
{
|
|
get
|
|
{
|
|
return this.Definition;
|
|
}
|
|
}
|
|
|
|
internal List<Handle> Handles
|
|
{
|
|
get { return this.handles; }
|
|
}
|
|
|
|
void ActivityInstanceMap.IActivityReference.Load(Activity activity, ActivityInstanceMap instanceMap)
|
|
{
|
|
this.Definition = activity;
|
|
}
|
|
|
|
void ActivityInstanceMap.IActivityReferenceWithEnvironment.UpdateEnvironment(EnvironmentUpdateMap map, Activity activity)
|
|
{
|
|
// LocationEnvironment.Update() is invoked through this path when this is a seondary root's environment(and in its parent chain) whose owner has already completed.
|
|
this.Update(map, activity);
|
|
}
|
|
|
|
// Note that the owner should never call this as the first
|
|
// AddReference is assumed
|
|
internal void AddReference()
|
|
{
|
|
this.referenceCountMinusOne++;
|
|
}
|
|
|
|
internal void RemoveReference(bool isOwner)
|
|
{
|
|
if (isOwner)
|
|
{
|
|
this.hasOwnerCompleted = true;
|
|
}
|
|
|
|
Fx.Assert(this.referenceCountMinusOne >= 0, "We must at least have 1 reference (0 for refCountMinusOne)");
|
|
this.referenceCountMinusOne--;
|
|
}
|
|
|
|
internal void OnDeserialized(ActivityExecutor executor, ActivityInstance handleScope)
|
|
{
|
|
this.executor = executor;
|
|
|
|
// The instance map Load might have already set the definition to the correct one.
|
|
// If not then we assume the definition is the same as the handle scope.
|
|
if (this.Definition == null)
|
|
{
|
|
this.Definition = handleScope.Activity;
|
|
}
|
|
|
|
ReinitializeHandles(handleScope);
|
|
RegisterUpdatedLocations(handleScope);
|
|
}
|
|
|
|
internal void ReinitializeHandles(ActivityInstance handleScope)
|
|
{
|
|
// Need to reinitialize the handles in the list.
|
|
if (this.handles != null)
|
|
{
|
|
int count = this.handles.Count;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
this.handles[i].Reinitialize(handleScope);
|
|
this.hasHandles = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void Dispose()
|
|
{
|
|
Fx.Assert(this.ShouldDispose, "We shouldn't be calling Dispose when we have existing references.");
|
|
Fx.Assert(!this.hasHandles, "We should have already uninitialized the handles and set our hasHandles variable to false.");
|
|
Fx.Assert(!this.isDisposed, "We should not already be disposed.");
|
|
|
|
this.isDisposed = true;
|
|
|
|
CleanupMappedLocations();
|
|
}
|
|
|
|
internal void AddHandle(Handle handleToAdd)
|
|
{
|
|
if (this.handles == null)
|
|
{
|
|
this.handles = new List<Handle>();
|
|
}
|
|
this.handles.Add(handleToAdd);
|
|
this.hasHandles = true;
|
|
}
|
|
|
|
void CleanupMappedLocations()
|
|
{
|
|
if (this.hasMappableLocations)
|
|
{
|
|
if (this.singleLocation != null)
|
|
{
|
|
Fx.Assert(this.singleLocation.CanBeMapped, "Can only have mappable locations for a singleton if its mappable.");
|
|
UnregisterLocation(this.singleLocation);
|
|
}
|
|
else if (this.locations != null)
|
|
{
|
|
for (int i = 0; i < this.locations.Length; i++)
|
|
{
|
|
Location location = this.locations[i];
|
|
|
|
if (location.CanBeMapped)
|
|
{
|
|
UnregisterLocation(location);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void UninitializeHandles(ActivityInstance scope)
|
|
{
|
|
if (this.hasHandles)
|
|
{
|
|
HandleInitializationContext context = null;
|
|
|
|
try
|
|
{
|
|
UninitializeHandles(scope, this.Definition.RuntimeVariables, ref context);
|
|
UninitializeHandles(scope, this.Definition.ImplementationVariables, ref context);
|
|
|
|
this.hasHandles = false;
|
|
}
|
|
finally
|
|
{
|
|
if (context != null)
|
|
{
|
|
context.Dispose();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void UninitializeHandles(ActivityInstance scope, IList<Variable> variables, ref HandleInitializationContext context)
|
|
{
|
|
for (int i = 0; i < variables.Count; i++)
|
|
{
|
|
Variable variable = variables[i];
|
|
Fx.Assert(variable.Owner == this.Definition, "We should only be targeting the vairables at this scope.");
|
|
|
|
if (variable.IsHandle)
|
|
{
|
|
Location location = GetSpecificLocation(variable.Id);
|
|
|
|
if (location != null)
|
|
{
|
|
Handle handle = (Handle)location.Value;
|
|
|
|
if (handle != null)
|
|
{
|
|
if (context == null)
|
|
{
|
|
context = new HandleInitializationContext(this.executor, scope);
|
|
}
|
|
|
|
handle.Uninitialize(context);
|
|
}
|
|
|
|
location.Value = null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void DeclareHandle(LocationReference locationReference, Location location, ActivityInstance activityInstance)
|
|
{
|
|
this.hasHandles = true;
|
|
|
|
Declare(locationReference, location, activityInstance);
|
|
}
|
|
|
|
internal void DeclareTemporaryLocation<T>(LocationReference locationReference, ActivityInstance activityInstance, bool bufferGetsOnCollapse)
|
|
where T : Location
|
|
{
|
|
Location locationToDeclare = new Location<T>();
|
|
locationToDeclare.SetTemporaryResolutionData(this, bufferGetsOnCollapse);
|
|
|
|
this.Declare(locationReference, locationToDeclare, activityInstance);
|
|
}
|
|
|
|
internal void Declare(LocationReference locationReference, Location location, ActivityInstance activityInstance)
|
|
{
|
|
Fx.Assert((locationReference.Id == 0 && this.locations == null) || (locationReference.Id >= 0 && this.locations != null && locationReference.Id < this.locations.Length), "The environment should have been created with the appropriate capacity.");
|
|
Fx.Assert(location != null, "");
|
|
|
|
RegisterLocation(location, locationReference, activityInstance);
|
|
|
|
if (this.locations == null)
|
|
{
|
|
Fx.Assert(this.singleLocation == null, "We should not have had a single location if we are trying to declare one.");
|
|
Fx.Assert(locationReference.Id == 0, "We should think the id is zero if we are setting the single location.");
|
|
|
|
this.singleLocation = location;
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(this.locations[locationReference.Id] == null || this.locations[locationReference.Id] is DummyLocation, "We should not have had a location at the spot we are replacing.");
|
|
|
|
this.locations[locationReference.Id] = location;
|
|
}
|
|
}
|
|
|
|
internal Location<T> GetSpecificLocation<T>(int id)
|
|
{
|
|
return GetSpecificLocation(id) as Location<T>;
|
|
}
|
|
|
|
internal Location GetSpecificLocation(int id)
|
|
{
|
|
Fx.Assert(id >= 0 && ((this.locations == null && id == 0) || (this.locations != null && id < this.locations.Length)), "Id needs to be within bounds.");
|
|
|
|
if (this.locations == null)
|
|
{
|
|
return this.singleLocation;
|
|
}
|
|
else
|
|
{
|
|
return this.locations[id];
|
|
}
|
|
}
|
|
|
|
// called for asynchronous argument resolution to collapse Location<Location<T>> to Location<T> in the environment
|
|
internal void CollapseTemporaryResolutionLocations()
|
|
{
|
|
if (this.locations == null)
|
|
{
|
|
if (this.singleLocation != null &&
|
|
object.ReferenceEquals(this.singleLocation.TemporaryResolutionEnvironment, this))
|
|
{
|
|
CollapseTemporaryResolutionLocation(ref this.singleLocation);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < this.locations.Length; i++)
|
|
{
|
|
Location referenceLocation = this.locations[i];
|
|
|
|
if (referenceLocation != null &&
|
|
object.ReferenceEquals(referenceLocation.TemporaryResolutionEnvironment, this))
|
|
{
|
|
CollapseTemporaryResolutionLocation(ref this.locations[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Called after an argument is added in Dynamic Update, when we need to collapse
|
|
// just one location rather than the whole environment
|
|
internal void CollapseTemporaryResolutionLocation(Location location)
|
|
{
|
|
// This assert doesn't necessarily imply that the location is still part of this environment;
|
|
// it might have been removed in a subsequent update. If so, this method is a no-op.
|
|
Fx.Assert(location.TemporaryResolutionEnvironment == this, "Trying to collapse from the wrong environment");
|
|
|
|
if (this.singleLocation == location)
|
|
{
|
|
CollapseTemporaryResolutionLocation(ref this.singleLocation);
|
|
}
|
|
else if (this.locations != null)
|
|
{
|
|
for (int i = 0; i < this.locations.Length; i++)
|
|
{
|
|
if (this.locations[i] == location)
|
|
{
|
|
CollapseTemporaryResolutionLocation(ref this.locations[i]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Gets the location at this scope. The caller verifies that ref.owner == this.definition.
|
|
internal bool TryGetLocation(int id, out Location value)
|
|
{
|
|
ThrowIfDisposed();
|
|
|
|
value = null;
|
|
|
|
if (this.locations == null)
|
|
{
|
|
if (id == 0)
|
|
{
|
|
value = this.singleLocation;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (this.locations.Length > id)
|
|
{
|
|
value = this.locations[id];
|
|
}
|
|
}
|
|
|
|
return value != null;
|
|
}
|
|
|
|
internal bool TryGetLocation(int id, Activity environmentOwner, out Location value)
|
|
{
|
|
ThrowIfDisposed();
|
|
|
|
LocationEnvironment targetEnvironment = this;
|
|
|
|
while (targetEnvironment != null && targetEnvironment.Definition != environmentOwner)
|
|
{
|
|
targetEnvironment = targetEnvironment.Parent;
|
|
}
|
|
|
|
if (targetEnvironment == null)
|
|
{
|
|
value = null;
|
|
return false;
|
|
}
|
|
|
|
value = null;
|
|
|
|
if (id == 0 && targetEnvironment.locations == null)
|
|
{
|
|
value = targetEnvironment.singleLocation;
|
|
}
|
|
else if (targetEnvironment.locations != null && targetEnvironment.locations.Length > id)
|
|
{
|
|
value = targetEnvironment.locations[id];
|
|
}
|
|
|
|
return value != null;
|
|
}
|
|
|
|
void RegisterLocation(Location location, LocationReference locationReference, ActivityInstance activityInstance)
|
|
{
|
|
if (location.CanBeMapped)
|
|
{
|
|
this.hasMappableLocations = true;
|
|
this.MappableObjectManager.Register(location, this.Definition, locationReference, activityInstance);
|
|
}
|
|
}
|
|
|
|
void UnregisterLocation(Location location)
|
|
{
|
|
this.MappableObjectManager.Unregister(location);
|
|
}
|
|
|
|
void ThrowIfDisposed()
|
|
{
|
|
if (isDisposed)
|
|
{
|
|
throw FxTrace.Exception.AsError(
|
|
new ObjectDisposedException(this.GetType().FullName, SR.EnvironmentDisposed));
|
|
}
|
|
}
|
|
|
|
internal void Update(EnvironmentUpdateMap map, Activity activity)
|
|
{
|
|
// arguments public variables private variables RuntimeDelegateArguments
|
|
// Locations array: AAAAAAAAAA VVVVVVVVVVVVVVVVVVVVVV PPPPPPPPPPPPPPPPPPP DDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
|
|
|
|
int actualRuntimeDelegateArgumentCount = activity.HandlerOf == null ? 0 : activity.HandlerOf.RuntimeDelegateArguments.Count;
|
|
|
|
if (map.NewArgumentCount != activity.RuntimeArguments.Count ||
|
|
map.NewVariableCount != activity.RuntimeVariables.Count ||
|
|
map.NewPrivateVariableCount != activity.ImplementationVariables.Count ||
|
|
map.RuntimeDelegateArgumentCount != actualRuntimeDelegateArgumentCount)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap(
|
|
SR.WrongEnvironmentCount(activity, map.NewArgumentCount, map.NewVariableCount, map.NewPrivateVariableCount, map.RuntimeDelegateArgumentCount,
|
|
activity.RuntimeArguments.Count, activity.RuntimeVariables.Count, activity.ImplementationVariables.Count, actualRuntimeDelegateArgumentCount))));
|
|
}
|
|
|
|
int expectedLocationCount = map.OldArgumentCount + map.OldVariableCount + map.OldPrivateVariableCount + map.RuntimeDelegateArgumentCount;
|
|
|
|
int actualLocationCount;
|
|
if (this.locations == null)
|
|
{
|
|
if (this.singleLocation == null)
|
|
{
|
|
// we can hit this condition when the root activity instance has zero symbol.
|
|
actualLocationCount = 0;
|
|
}
|
|
else
|
|
{
|
|
actualLocationCount = 1;
|
|
|
|
// temporarily normalize to locations array for the sake of environment update processing
|
|
this.locations = new Location[] { this.singleLocation };
|
|
this.singleLocation = null;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(this.singleLocation == null, "locations and singleLocations cannot be non-null at the same time.");
|
|
actualLocationCount = this.locations.Length;
|
|
}
|
|
|
|
if (expectedLocationCount != actualLocationCount)
|
|
{
|
|
throw FxTrace.Exception.AsError(new InstanceUpdateException(SR.InvalidUpdateMap(
|
|
SR.Wron----ginalEnvironmentCount(activity, map.OldArgumentCount, map.OldVariableCount, map.OldPrivateVariableCount, map.RuntimeDelegateArgumentCount,
|
|
expectedLocationCount, actualLocationCount))));
|
|
}
|
|
|
|
Location[] newLocations = null;
|
|
|
|
// If newTotalLocations == 0, update will leave us with an empty LocationEnvironment,
|
|
// which is something the runtime would normally never create. This is harmless, but it
|
|
// is a loosening of normal invariants.
|
|
int newTotalLocations = map.NewArgumentCount + map.NewVariableCount + map.NewPrivateVariableCount + map.RuntimeDelegateArgumentCount;
|
|
if (newTotalLocations > 0)
|
|
{
|
|
newLocations = new Location[newTotalLocations];
|
|
}
|
|
|
|
UpdateArguments(map, newLocations);
|
|
UnregisterRemovedVariables(map);
|
|
UpdatePublicVariables(map, newLocations, activity);
|
|
UpdatePrivateVariables(map, newLocations, activity);
|
|
CopyRuntimeDelegateArguments(map, newLocations);
|
|
|
|
Location newSingleLocation = null;
|
|
if (newTotalLocations == 1)
|
|
{
|
|
newSingleLocation = newLocations[0];
|
|
newLocations = null;
|
|
}
|
|
|
|
this.singleLocation = newSingleLocation;
|
|
this.locations = newLocations;
|
|
}
|
|
|
|
void UpdateArguments(EnvironmentUpdateMap map, Location[] newLocations)
|
|
{
|
|
if (map.HasArgumentEntries)
|
|
{
|
|
for (int i = 0; i < map.ArgumentEntries.Count; i++)
|
|
{
|
|
EnvironmentUpdateMapEntry entry = map.ArgumentEntries[i];
|
|
|
|
Fx.Assert(entry.NewOffset >= 0 && entry.NewOffset < map.NewArgumentCount, "Argument offset is out of range");
|
|
|
|
if (entry.IsAddition)
|
|
{
|
|
// Location allocation will be performed later during ResolveDynamicallyAddedArguments().
|
|
// for now, simply assign a dummy location so we know not to copy over the old value.
|
|
newLocations[entry.NewOffset] = dummyLocation;
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(this.locations != null && this.singleLocation == null, "Caller should have copied singleLocation into locations array");
|
|
|
|
// rearrangement of existing arguments
|
|
// this entry here doesn't describe argument removal
|
|
newLocations[entry.NewOffset] = this.locations[entry.OldOffset];
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy over unchanged Locations, and null out DummyLocations
|
|
for (int i = 0; i < map.NewArgumentCount; i++)
|
|
{
|
|
if (newLocations[i] == null)
|
|
{
|
|
Fx.Assert(this.locations != null && this.locations.Length > i, "locations must be non-null and index i must be within the range of locations.");
|
|
newLocations[i] = this.locations[i];
|
|
}
|
|
else if (newLocations[i] == dummyLocation)
|
|
{
|
|
newLocations[i] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
void UpdatePublicVariables(EnvironmentUpdateMap map, Location[] newLocations, Activity activity)
|
|
{
|
|
UpdateVariables(
|
|
map.NewArgumentCount,
|
|
map.OldArgumentCount,
|
|
map.NewVariableCount,
|
|
map.OldVariableCount,
|
|
map.VariableEntries,
|
|
activity.RuntimeVariables,
|
|
newLocations);
|
|
}
|
|
|
|
void UpdatePrivateVariables(EnvironmentUpdateMap map, Location[] newLocations, Activity activity)
|
|
{
|
|
UpdateVariables(
|
|
map.NewArgumentCount + map.NewVariableCount,
|
|
map.OldArgumentCount + map.OldVariableCount,
|
|
map.NewPrivateVariableCount,
|
|
map.OldPrivateVariableCount,
|
|
map.PrivateVariableEntries,
|
|
activity.ImplementationVariables,
|
|
newLocations);
|
|
}
|
|
|
|
void UpdateVariables(int newVariablesOffset, int oldVariablesOffset, int newVariableCount, int oldVariableCount, IList<EnvironmentUpdateMapEntry> variableEntries, IList<Variable> variables, Location[] newLocations)
|
|
{
|
|
if (variableEntries != null)
|
|
{
|
|
for (int i = 0; i < variableEntries.Count; i++)
|
|
{
|
|
EnvironmentUpdateMapEntry entry = variableEntries[i];
|
|
|
|
Fx.Assert(entry.NewOffset >= 0 && entry.NewOffset < newVariableCount, "Variable offset is out of range");
|
|
Fx.Assert(!entry.IsNewHandle, "This should have been caught in ActivityInstanceMap.UpdateRawInstance");
|
|
|
|
if (entry.IsAddition)
|
|
{
|
|
Variable newVariable = variables[entry.NewOffset];
|
|
Location location = newVariable.CreateLocation();
|
|
newLocations[newVariablesOffset + entry.NewOffset] = location;
|
|
if (location.CanBeMapped)
|
|
{
|
|
ActivityUtilities.Add(ref this.locationsToRegister, newVariable);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(this.locations != null && this.singleLocation == null, "Caller should have copied singleLocation into locations array");
|
|
|
|
// rearrangement of existing variable
|
|
// this entry here doesn't describe variable removal
|
|
newLocations[newVariablesOffset + entry.NewOffset] = this.locations[oldVariablesOffset + entry.OldOffset];
|
|
}
|
|
}
|
|
}
|
|
|
|
// copy over unchanged variable Locations
|
|
for (int i = 0; i < newVariableCount; i++)
|
|
{
|
|
if (newLocations[newVariablesOffset + i] == null)
|
|
{
|
|
Fx.Assert(i < oldVariableCount, "New variable should have a location");
|
|
Fx.Assert(this.locations != null && this.locations.Length > oldVariablesOffset + i, "locations must be non-null and index i + oldVariableOffset must be within the range of locations.");
|
|
|
|
newLocations[newVariablesOffset + i] = this.locations[oldVariablesOffset + i];
|
|
}
|
|
}
|
|
}
|
|
|
|
void CopyRuntimeDelegateArguments(EnvironmentUpdateMap map, Location[] newLocations)
|
|
{
|
|
for (int i = 1; i <= map.RuntimeDelegateArgumentCount; i++)
|
|
{
|
|
newLocations[newLocations.Length - i] = this.locations[this.locations.Length - i];
|
|
}
|
|
}
|
|
|
|
void CollapseTemporaryResolutionLocation(ref Location location)
|
|
{
|
|
if (location.Value == null)
|
|
{
|
|
location = (Location)location.CreateDefaultValue();
|
|
}
|
|
else
|
|
{
|
|
location = ((Location)location.Value).CreateReference(location.BufferGetsOnCollapse);
|
|
}
|
|
}
|
|
|
|
void RegisterUpdatedLocations(ActivityInstance activityInstance)
|
|
{
|
|
if (this.locationsToRegister != null)
|
|
{
|
|
foreach (LocationReference locationReference in this.locationsToRegister)
|
|
{
|
|
RegisterLocation(GetSpecificLocation(locationReference.Id), locationReference, activityInstance);
|
|
}
|
|
this.locationsToRegister = null;
|
|
}
|
|
|
|
if (this.locationsToUnregister != null)
|
|
{
|
|
foreach (Location location in this.locationsToUnregister)
|
|
{
|
|
UnregisterLocation(location);
|
|
}
|
|
this.locationsToUnregister = null;
|
|
}
|
|
}
|
|
|
|
void UnregisterRemovedVariables(EnvironmentUpdateMap map)
|
|
{
|
|
bool hasMappableLocationsRemaining = false;
|
|
int offset = map.OldArgumentCount;
|
|
|
|
FindVariablesToUnregister(false, map, map.OldVariableCount, offset, ref hasMappableLocationsRemaining);
|
|
|
|
offset = map.OldArgumentCount + map.OldVariableCount;
|
|
|
|
FindVariablesToUnregister(true, map, map.OldPrivateVariableCount, offset, ref hasMappableLocationsRemaining);
|
|
|
|
this.hasMappableLocations = hasMappableLocationsRemaining;
|
|
}
|
|
|
|
delegate int? GetNewVariableIndex(int oldIndex);
|
|
private void FindVariablesToUnregister(bool forImplementation, EnvironmentUpdateMap map, int oldVariableCount, int offset, ref bool hasMappableLocationsRemaining)
|
|
{
|
|
for (int i = 0; i < oldVariableCount; i++)
|
|
{
|
|
Location location = this.locations[i + offset];
|
|
if (location.CanBeMapped)
|
|
{
|
|
if ((forImplementation && map.GetNewPrivateVariableIndex(i).HasValue) || (!forImplementation && map.GetNewVariableIndex(i).HasValue))
|
|
{
|
|
hasMappableLocationsRemaining = true;
|
|
}
|
|
else
|
|
{
|
|
ActivityUtilities.Add(ref this.locationsToUnregister, location);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private class DummyLocation : Location<object>
|
|
{
|
|
// this is a dummy location
|
|
// temporarary place holder for a dynamically added LocationReference
|
|
}
|
|
}
|
|
}
|