Skip to content
This repository has been archived by the owner on Dec 1, 2022. It is now read-only.

Checks all custom actions for cycles. #222

Closed
wants to merge 12 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -332,7 +332,7 @@ public void Execute()
var msiPropertiesByPackage = this.Section.Symbols.OfType<WixBundleMsiPropertySymbol>().ToLookup(r => r.PackageRef);
var payloadsByPackage = this.Payloads.Values.ToLookup(p => p.PackageRef);
var relatedPackagesByPackage = this.Section.Symbols.OfType<WixBundleRelatedPackageSymbol>().ToLookup(r => r.PackageRef);
var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.MspPackageRef);
var slipstreamMspsByPackage = this.Section.Symbols.OfType<WixBundleSlipstreamMspSymbol>().ToLookup(r => r.TargetPackageRef);
var exitCodesByPackage = this.Section.Symbols.OfType<WixBundlePackageExitCodeSymbol>().ToLookup(r => r.ChainPackageId);
var commandLinesByPackage = this.Section.Symbols.OfType<WixBundlePackageCommandLineSymbol>().ToLookup(r => r.WixBundlePackageRef);

Expand Down
118 changes: 81 additions & 37 deletions src/WixToolset.Core.WindowsInstaller/Bind/SequenceActionsCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,9 @@ public void Execute()
}
}

// A dictionary used for detecting cyclic references among action symbols.
var firstReference = new Dictionary<WixActionSymbol, WixActionSymbol>();

// Build up dependency trees of the relatively scheduled actions.
// Use ToList() to create a copy of the required action symbols so that new symbols can
// be added while enumerating.
Expand All @@ -142,7 +145,7 @@ public void Execute()
this.Messaging.Write(ErrorMessages.StandardActionRelativelyScheduledInModule(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action));
}

this.SequenceActionSymbol(actionSymbol, requiredActionSymbols);
this.SequenceActionSymbol(actionSymbol, requiredActionSymbols, firstReference);
}
else if (SectionType.Module == this.Section.Type && 0 < actionSymbol.Sequence && !WindowsInstallerStandard.IsStandardAction(actionSymbol.Action)) // check for custom actions and dialogs that have a sequence number
{
Expand Down Expand Up @@ -562,64 +565,105 @@ private IEnumerable<string> GetRequiredActionIds()
}

/// <summary>
/// Sequence an action before or after a standard action.
/// Sequence an action before or after a standard action and verify that it is not part of
/// an action reference cycle.
/// </summary>
/// <para> Use the provided dictionary to note the initial action symbol
/// that first led to each action symbol. Any action symbol encountered that has already been
/// encountered starting from a different initial action symbol inherits the loop characteristics of that
/// initial action symbol, and thus is also not part of a cycle. However, any action symbol
/// encountered that has already been encountered starting from the same initial action symbol
/// is an indication that the current action symbol is part of a cycle.
/// </para>
/// <param name="actionSymbol">The action symbol to be sequenced.</param>
/// <param name="requiredActionSymbols">Collection of actions which must be included.</param>
private void SequenceActionSymbol(WixActionSymbol actionSymbol, Dictionary<string, WixActionSymbol> requiredActionSymbols)
/// <param name="firstReference">The first encountered action symbol that led to each action symbol.</param>
private void SequenceActionSymbol(WixActionSymbol actionSymbol,
Dictionary<string, WixActionSymbol> requiredActionSymbols,
Dictionary<WixActionSymbol, WixActionSymbol> firstReference)
{
var after = false;
WixActionSymbol initialParentActionSymbol = null;
var previousActionSymbol = actionSymbol;
var initialActionSymbol = actionSymbol;
var initialAfter = this.GetAfter(actionSymbol);

if (actionSymbol.After != null)
{
after = true;
}
else if (actionSymbol.Before == null)
{
throw new InvalidOperationException("Found an action with no Sequence, Before, or After column set.");
}
var firstTime = true;

var parentActionName = (after ? actionSymbol.After : actionSymbol.Before);
var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + parentActionName;

if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
do
{
// If the missing parent action is a standard action (with a suggested sequence number), add it.
if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol))
if (!firstReference.ContainsKey(actionSymbol))
{
// Create a clone to avoid modifying the static copy of the object.
// TODO: consider this: parentActionSymbol = parentActionSymbol.Clone();
firstReference[actionSymbol] = initialActionSymbol;
}
else if (firstReference[actionSymbol] == initialActionSymbol)
{
throw new WixException(ErrorMessages.ActionCircularDependency(actionSymbol.SourceLineNumbers,
actionSymbol.SequenceTable.ToString(), actionSymbol.Action, previousActionSymbol.Action));
}

if (null == actionSymbol.Before && null == actionSymbol.After)
{
if (firstTime)
{
throw new InvalidOperationException(
"Found an action with no Sequence, Before, or After column set.");
}

requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol);
break;
}
else

var parentActionSymbol = this.GetParentActionSymbol(actionSymbol, requiredActionSymbols, firstTime);

if (firstTime)
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, "Found an action with a non-existent {0} action: {1}.", (after ? "After" : "Before"), parentActionName));
initialParentActionSymbol = parentActionSymbol ?? throw new InvalidOperationException(
"Found an action with no Sequence, Before, or After column set.");
}
}
else if (actionSymbol == parentActionSymbol || this.ContainsChildActionSymbol(actionSymbol, parentActionSymbol)) // cycle detected
{
throw new WixException(ErrorMessages.ActionCircularDependency(actionSymbol.SourceLineNumbers, actionSymbol.SequenceTable.ToString(), actionSymbol.Action, parentActionSymbol.Action));
}

previousActionSymbol = actionSymbol;
actionSymbol = parentActionSymbol;
firstTime = false;
} while (actionSymbol != null);

// Add this action to the appropriate list of dependent action symbols.
var relativeActions = this.GetRelativeActions(parentActionSymbol);
var relatedSymbols = (after ? relativeActions.NextActions : relativeActions.PreviousActions);
relatedSymbols.Add(actionSymbol);
var relativeActions = this.GetRelativeActions(initialParentActionSymbol);
var relatedSymbols = (initialAfter ? relativeActions.NextActions : relativeActions.PreviousActions);
relatedSymbols.Add(initialActionSymbol);
}

private bool ContainsChildActionSymbol(WixActionSymbol childSymbol, WixActionSymbol parentSymbol)
private WixActionSymbol GetParentActionSymbol(WixActionSymbol actionSymbol,
Dictionary<string, WixActionSymbol> requiredActionSymbols,
bool firstTime)
{
var result = false;
var after = this.GetAfter(actionSymbol);
var parentActionName = after ? actionSymbol.After : actionSymbol.Before;
var parentActionKey = actionSymbol.SequenceTable.ToString() + "/" + parentActionName;

if (this.RelativeActionsForActions.TryGetValue(childSymbol.Id.Id, out var relativeActions))
if (!requiredActionSymbols.TryGetValue(parentActionKey, out var parentActionSymbol))
{
result = relativeActions.NextActions.Any(a => a.SequenceTable == parentSymbol.SequenceTable && a.Id.Id == parentSymbol.Id.Id) ||
relativeActions.PreviousActions.Any(a => a.SequenceTable == parentSymbol.SequenceTable && a.Id.Id == parentSymbol.Id.Id);
if (WindowsInstallerStandard.TryGetStandardAction(parentActionKey, out parentActionSymbol))
{
// If the missing parent action is a standard action (with a suggested sequence number), add it.
if (firstTime)
{
// Create a clone to avoid modifying the static copy of the object.
// TODO: consider this: parentActionSymbol = parentActionSymbol.Clone();

requiredActionSymbols.Add(parentActionSymbol.Id.Id, parentActionSymbol);
}
}
else
{
throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture,
"Found an action with a non-existent {0} action: {1}.",
(after ? "After" : "Before"), parentActionName));
}
}

return result;
return parentActionSymbol;
}

private bool GetAfter(WixActionSymbol actionSymbol) => (actionSymbol.After != null);

private RelativeActions GetRelativeActions(WixActionSymbol action)
{
Expand Down
12 changes: 6 additions & 6 deletions src/WixToolset.Core.WindowsInstaller/Decompile/Decompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3950,12 +3950,12 @@ private void DecompileCustomActionTable(Table table)

if (WindowsInstallerConstants.MsidbCustomActionType64BitScript == (type & WindowsInstallerConstants.MsidbCustomActionType64BitScript))
{
xCustomAction.SetAttributeValue("Win64", "yes");
xCustomAction.SetAttributeValue("Bitness", "always64");
}
else if (WindowsInstallerConstants.MsidbCustomActionTypeVBScript == (type & WindowsInstallerConstants.MsidbCustomActionTypeVBScript) ||
WindowsInstallerConstants.MsidbCustomActionTypeJScript == (type & WindowsInstallerConstants.MsidbCustomActionTypeJScript))
{
xCustomAction.SetAttributeValue("Win64", "no");
xCustomAction.SetAttributeValue("Bitness", "always32");
}

switch (type & WindowsInstallerConstants.MsidbCustomActionTypeExecuteBits)
Expand Down Expand Up @@ -4191,11 +4191,11 @@ private void DecompileComponentTable(Table table)

if (WindowsInstallerConstants.MsidbComponentAttributes64bit == (attributes & WindowsInstallerConstants.MsidbComponentAttributes64bit))
{
xComponent.SetAttributeValue("Win64", "yes");
xComponent.SetAttributeValue("Bitness", "always64");
}
else
{
xComponent.SetAttributeValue("Win64", "no");
xComponent.SetAttributeValue("Bitness", "always32");
}

if (WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection == (attributes & WindowsInstallerConstants.MsidbComponentAttributesDisableRegistryReflection))
Expand Down Expand Up @@ -6479,12 +6479,12 @@ private void DecompileRegLocatorTable(Table table)

if (WindowsInstallerConstants.MsidbLocatorType64bit == (type & WindowsInstallerConstants.MsidbLocatorType64bit))
{
xRegistrySearch.SetAttributeValue("Win64", "yes");
xRegistrySearch.SetAttributeValue("Bitness", "always64");
type &= ~WindowsInstallerConstants.MsidbLocatorType64bit;
}
else
{
xRegistrySearch.SetAttributeValue("Win64", "no");
xRegistrySearch.SetAttributeValue("Bitness", "always32");
}

switch (type)
Expand Down
2 changes: 2 additions & 0 deletions src/WixToolset.Core/CompileContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ internal CompileContext(IWixToolsetServiceProvider serviceProvider)

public Platform Platform { get; set; }

public bool IsCurrentPlatform64Bit => this.Platform == Platform.ARM64 || this.Platform == Platform.X64;

public XDocument Source { get; set; }

public CancellationToken CancellationToken { get; set; }
Expand Down