1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-05-31 22:41:37 +00:00
6502bench/SourceGen/MessageList.cs

222 lines
7.7 KiB
C#
Raw Permalink Normal View History

/*
* Copyright 2019 faddenSoft
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using MainWindow = SourceGen.WpfGui.MainWindow;
namespace SourceGen {
/// <summary>
/// List of problems and oddities noted during analysis of the project.
/// </summary>
/// <remarks>
/// Could also make a place for load-time problems, though those tend to be resolved by
/// discarding the offending data, so not much value in continuing to report them.
/// </remarks>
public class MessageList : IEnumerable<MessageList.MessageEntry> {
/// <summary>
/// One message entry.
///
/// Instances are immutable.
/// </summary>
public class MessageEntry {
public enum SeverityLevel {
Unknown = 0,
Info,
Warning,
Error
}
public SeverityLevel Severity { get; private set; }
public int Offset { get; private set; }
public enum MessageType {
Unknown = 0,
HiddenLabel,
HiddenLocalVariableTable,
HiddenVisualization,
UnresolvedWeakRef,
ORG rework, part 6 Added support for non-addressable regions, which are useful for things like file headers stripped out by the system loader, or chunks that get loaded into non-addressable graphics RAM. Regions are specified with the "NA" address value. The code list displays the address field greyed out, starting from zero (which is kind of handy if you want to know the relative offset within the region). Putting labels in non-addressable regions doesn't make sense, but symbol resolution is complicated enough that we really only have two options: ignore the labels entirely, or allow them but warn of their presence. The problem isn't so much the label, which you could legitimately want to access from an extension script, but rather the references to them from code or data. So we keep the label and add a warning to the Messages list when we see a reference. Moved NON_ADDR constants to Address class. AddressMap now has a copy. This is awkward because Asm65 and CommonUtil don't share. Updated the asm code generators to understand NON_ADDR, and reworked the API so that Merlin and cc65 output is correct for nested regions. Address region changes are now noted in the anattribs array, which makes certain operations faster than checking the address map. It also fixes a failure to recognize mid-instruction region changes in the code analyzer. Tweaked handling of synthetic regions, which are non-addressable areas generated by the linear address map traversal to fill in any "holes". The address region editor now treats attempts to edit them as creation of a new region.
2021-10-01 01:07:21 +00:00
NonAddrLabelRef,
InvalidOffsetOrLength,
InvalidDescriptor,
BankOverrun,
}
public MessageType MsgType { get; private set; }
// Context object. Could be a label string, a format descriptor, etc.
public object Context { get; private set; }
public enum ProblemResolution {
Unknown = 0,
None,
LabelIgnored,
LocalVariableTableIgnored,
VisualizationIgnored,
FormatDescriptorIgnored,
}
public ProblemResolution Resolution { get; private set; }
public MessageEntry(SeverityLevel severity, int offset, MessageType mtype,
object context, ProblemResolution resolution) {
Debug.Assert(context != null);
Severity = severity;
Offset = offset;
MsgType = mtype;
Context = context;
Resolution = resolution;
}
public override string ToString() {
return Severity.ToString() + " +" + Offset.ToString("x6") + " " +
MsgType + "(" + Context.ToString() + "): " + Resolution;
}
}
/// <summary>
/// Maximum file offset. Used to flag offsets as invalid.
/// </summary>
//public int MaxOffset { get; set; }
/// <summary>
/// List of messages. This is not kept in sorted order, because the DataGrid used to
/// display it will do the sorting for us. Call the Sort() function to establish an
/// initial sort.
/// </summary>
private List<MessageEntry> mList;
/// <summary>
/// Constructor.
/// </summary>
public MessageList() {
mList = new List<MessageEntry>();
}
// IEnumerable
public IEnumerator<MessageEntry> GetEnumerator() {
// .Values is documented as O(1)
return mList.GetEnumerator();
}
// IEnumerable
IEnumerator IEnumerable.GetEnumerator() {
return mList.GetEnumerator();
}
public int Count {
get { return mList.Count; }
}
public void Add(MessageEntry entry) {
mList.Add(entry);
}
public void Clear() {
mList.Clear();
}
/// <summary>
/// Sorts the list, by severity then offset.
/// </summary>
public void Sort() {
mList.Sort(delegate (MessageEntry a, MessageEntry b) {
if (a.Severity != b.Severity) {
return (int)b.Severity - (int)a.Severity;
}
return a.Offset - b.Offset;
});
}
/// <summary>
/// Formats a message for display.
/// </summary>
public static MainWindow.MessageListItem FormatMessage(MessageEntry entry,
Asm65.Formatter formatter) {
string severity = entry.Severity.ToString(); // enum
string offset = formatter.FormatOffset24(entry.Offset);
string problem;
switch (entry.MsgType) {
case MessageEntry.MessageType.HiddenLabel:
problem = Res.Strings.MSG_HIDDEN_LABEL;
break;
case MessageEntry.MessageType.HiddenLocalVariableTable:
problem = Res.Strings.MSG_HIDDEN_LOCAL_VARIABLE_TABLE;
break;
case MessageEntry.MessageType.HiddenVisualization:
problem = Res.Strings.MSG_HIDDEN_VISUALIZATION;
break;
case MessageEntry.MessageType.UnresolvedWeakRef:
problem = Res.Strings.MSG_UNRESOLVED_WEAK_REF;
break;
ORG rework, part 6 Added support for non-addressable regions, which are useful for things like file headers stripped out by the system loader, or chunks that get loaded into non-addressable graphics RAM. Regions are specified with the "NA" address value. The code list displays the address field greyed out, starting from zero (which is kind of handy if you want to know the relative offset within the region). Putting labels in non-addressable regions doesn't make sense, but symbol resolution is complicated enough that we really only have two options: ignore the labels entirely, or allow them but warn of their presence. The problem isn't so much the label, which you could legitimately want to access from an extension script, but rather the references to them from code or data. So we keep the label and add a warning to the Messages list when we see a reference. Moved NON_ADDR constants to Address class. AddressMap now has a copy. This is awkward because Asm65 and CommonUtil don't share. Updated the asm code generators to understand NON_ADDR, and reworked the API so that Merlin and cc65 output is correct for nested regions. Address region changes are now noted in the anattribs array, which makes certain operations faster than checking the address map. It also fixes a failure to recognize mid-instruction region changes in the code analyzer. Tweaked handling of synthetic regions, which are non-addressable areas generated by the linear address map traversal to fill in any "holes". The address region editor now treats attempts to edit them as creation of a new region.
2021-10-01 01:07:21 +00:00
case MessageEntry.MessageType.NonAddrLabelRef:
problem = Res.Strings.MSG_NON_ADDR_LABEL_REF;
break;
case MessageEntry.MessageType.InvalidOffsetOrLength:
problem = Res.Strings.MSG_INVALID_OFFSET_OR_LENGTH;
break;
case MessageEntry.MessageType.InvalidDescriptor:
problem = Res.Strings.MSG_INVALID_DESCRIPTOR;
break;
case MessageEntry.MessageType.BankOverrun:
problem = Res.Strings.MSG_BANK_OVERRUN;
break;
default:
problem = "???";
break;
}
string context = entry.Context.ToString();
string resolution;
switch (entry.Resolution) {
case MessageEntry.ProblemResolution.None:
resolution = string.Empty;
break;
case MessageEntry.ProblemResolution.LabelIgnored:
resolution = Res.Strings.MSG_LABEL_IGNORED;
break;
case MessageEntry.ProblemResolution.LocalVariableTableIgnored:
resolution = Res.Strings.MSG_LOCAL_VARIABLE_TABLE_IGNORED;
break;
case MessageEntry.ProblemResolution.FormatDescriptorIgnored:
resolution = Res.Strings.MSG_FORMAT_DESCRIPTOR_IGNORED;
break;
case MessageEntry.ProblemResolution.VisualizationIgnored:
resolution = Res.Strings.MSG_VISUALIZATION_IGNORED;
break;
default:
resolution = "???";
break;
}
return new MainWindow.MessageListItem(severity, entry.Offset, offset, problem,
context, resolution);
}
public void DebugDump() {
Debug.WriteLine("Message list (" + mList.Count + " entries):");
foreach (MessageEntry entry in mList) {
Debug.WriteLine(entry);
}
}
}
}