From fac2d6a51f8af12daf0583c6180d2f89226e7861 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Sun, 13 Oct 2019 18:19:28 -0700 Subject: [PATCH] Invoke extension scripts when labels they care about change We were failing to update properly when a label changed if the label was one that a plugin cared about. The problem is that a label add/remove operation skips the code analysis, and a label edit skips everything but the display update. Plugins only run during the code analysis pass, so changes weren't being reflected in the display list until something caused it to refresh. The solution is to ask the plugin if the label being changed is one that it cares about. This allows the plugin to use the same wildcard-match logic that it uses elsewhere. For efficiency, and to reduce clutter in plugins that don't care about symbols, a new interface class has been created to handle the "here are the symbols" call and the "do you care about this label" call. The program in Examples/Scripts has been updated to show a very simple single-call plugin and a slightly more complex multi-call plugin. --- PluginCommon/Interfaces.cs | 51 ++++++++++-- PluginCommon/PluginManager.cs | 24 +++++- SourceGen/DisasmProject.cs | 10 +++ SourceGen/Examples/Scripts/InlineL1String.cs | 74 +++++++++++++++++ SourceGen/Examples/Scripts/InlineNullTerm.cs | 69 ---------------- .../Examples/Scripts/InlineNullTermString.cs | 77 ++++++++++++++++++ SourceGen/Examples/Scripts/Sample | Bin 13 -> 41 bytes SourceGen/Examples/Scripts/Sample.S | 16 +++- SourceGen/Examples/Scripts/Sample.dis65 | 14 ++-- SourceGen/RuntimeData/Apple/GSOS.cs | 14 +++- SourceGen/RuntimeData/Apple/IIgs-Toolbox.cs | 15 ++-- SourceGen/RuntimeData/Apple/ProDOS8.cs | 12 ++- SourceGen/RuntimeData/Apple/SOS.cs | 13 ++- SourceGen/RuntimeData/Help/advanced.html | 7 -- SourceGen/SGTestData/2011-hinting.cs | 3 +- .../SGTestData/2022-extension-scripts-a.cs | 22 +++-- .../SGTestData/2022-extension-scripts-b.cs | 3 +- SourceGen/Sandbox/ScriptManager.cs | 31 ++++++- 18 files changed, 337 insertions(+), 118 deletions(-) create mode 100644 SourceGen/Examples/Scripts/InlineL1String.cs delete mode 100644 SourceGen/Examples/Scripts/InlineNullTerm.cs create mode 100644 SourceGen/Examples/Scripts/InlineNullTermString.cs diff --git a/PluginCommon/Interfaces.cs b/PluginCommon/Interfaces.cs index ff2b347..05dde90 100644 --- a/PluginCommon/Interfaces.cs +++ b/PluginCommon/Interfaces.cs @@ -29,7 +29,7 @@ namespace PluginCommon { string Identifier { get; } /// - /// Prepares the plugin for action. Called at the start of the code analysis pass. + /// Prepares the plugin for action. Called at the start of every code analysis pass. /// /// In the current implementation, the file data will be the same every time, /// because it doesn't change after the project is opened. However, this could @@ -38,11 +38,44 @@ namespace PluginCommon { /// Reference to application interface. /// 65xx code and data. /// Mapping between offsets and addresses. - /// Symbols available to plugins, in no particular order. All - /// platform, project, and user labels are included; auto-generated symbols and - /// local variables are not. - void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms); + void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans); + } + + /// + /// Extension scripts that want to receive the list of symbols must implement this interface. + /// + public interface IPlugin_SymbolList { + /// + /// Receives a list of the currently defined platform, project, and user symbols. + /// The list does not include auto-generated labels or local variables. + /// + /// This is called immediately after Prepare(), before any other interfaces are + /// invoked, at the start of every code analysis pass. + /// + /// Symbols available to plugins, in no particular order. + void UpdateSymbolList(List plSyms); + + /// + /// Handles a notification that a user symbol has been added, edited, or removed. If the + /// label is of interest to the plugin, e.g. it changes how the plugin formats code or + /// data, the app needs to know. + /// + /// + /// The application does a full re-analysis when project properties change, but not + /// when labels are edited. The CheckJsr/Jsl/Brk methods are only called during code + /// analysis, so if their behavior changes based on the presence or absence of a user + /// label then we need to tell the application that a full re-analysis is needed. + /// + /// Plugins that don't care about user symbols, e.g. that just use tagged platform + /// symbols, can simply return false. (Changes to user labels that overlap with + /// project/platform symbols are detected by the app.) + /// + /// The label before the change, or empty if this is a + /// newly-added label. + /// The label after the change, or empty if the label was + /// removed. + /// True if the label change could affect the plugin's actions. + bool IsLabelSignificant(string beforeLabel, string afterLabel); } /// @@ -128,6 +161,9 @@ namespace PluginCommon { /// /// Data format type. /// + /// + /// Essentially a clone of FormatDescriptor.Type. + /// public enum DataType { Unknown = 0, NumericLE, @@ -145,6 +181,9 @@ namespace PluginCommon { /// /// Data format sub-type. /// + /// + /// Essentially a clone of FormatDescriptor.SubType. + /// public enum DataSubType { // No sub-type specified. None = 0, diff --git a/PluginCommon/PluginManager.cs b/PluginCommon/PluginManager.cs index bbee8df..dba75db 100644 --- a/PluginCommon/PluginManager.cs +++ b/PluginCommon/PluginManager.cs @@ -154,15 +154,37 @@ namespace PluginCommon { /// Invokes the Prepare() method on all active plugins. /// /// Reference to host object providing app services. + /// Serialized AddressMap entries. + /// SymbolTable contents, converted to PlSymbol. public void PreparePlugins(IApplication appRef, List addrEntries, List plSyms) { AddressMap addrMap = new AddressMap(addrEntries); AddressTranslate addrTrans = new AddressTranslate(addrMap); foreach (KeyValuePair kvp in mActivePlugins) { - kvp.Value.Prepare(appRef, mFileData, addrTrans, plSyms); + IPlugin ipl = kvp.Value; + ipl.Prepare(appRef, mFileData, addrTrans); + if (ipl is IPlugin_SymbolList) { + ((IPlugin_SymbolList)ipl).UpdateSymbolList(plSyms); + } } } + /// + /// Returns true if any of the plugins report that the before or after label is + /// significant. + /// + public bool IsLabelSignificant(string labelBefore, string labelAfter) { + foreach (KeyValuePair kvp in mActivePlugins) { + IPlugin ipl = kvp.Value; + if (ipl is IPlugin_SymbolList && + ((IPlugin_SymbolList)ipl).IsLabelSignificant(labelBefore, + labelAfter)) { + return true; + } + } + return false; + } + #if false /// /// DEBUG ONLY: establish a fast lease timeout. Normally the lease diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 7b5b639..eda1c95 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -1970,6 +1970,16 @@ namespace SourceGen { // to do the refactor here, though we can skip Anattribs work. Debug.Assert(oldValue == null || newValue == null); } + + // For add/edit/remove, we need to see if what we do will impact + // the behavior of a plugin. We don't need to do this on + // project/platform symbol changes because project property changes + // always update code and data. + if (mScriptManager.IsLabelSignificant((Symbol)oldValue, + (Symbol)newValue)) { + Debug.WriteLine("Plugin claims symbol is significant"); + needReanalysis |= UndoableChange.ReanalysisScope.CodeAndData; + } } break; case UndoableChange.ChangeType.SetOperandFormat: { diff --git a/SourceGen/Examples/Scripts/InlineL1String.cs b/SourceGen/Examples/Scripts/InlineL1String.cs new file mode 100644 index 0000000..5ff0212 --- /dev/null +++ b/SourceGen/Examples/Scripts/InlineL1String.cs @@ -0,0 +1,74 @@ +// Copyright 2019 faddenSoft. All Rights Reserved. +// See the LICENSE.txt file for distribution terms (Apache 2.0). + +using System; +using System.Collections.Generic; + +using PluginCommon; + +namespace ExtensionScriptSample { + /// + /// Sample class for handling a JSR followed by a string prefixed with a 1-byte length. + /// + public class InlineL1String: MarshalByRefObject, IPlugin, IPlugin_SymbolList, + IPlugin_InlineJsr { + private IApplication mAppRef; + private byte[] mFileData; + + // Only one call. + private const string CALL_LABEL = "PrintInlineL1String"; + private int mInlineL1StringAddr; // jsr + + public string Identifier { + get { + return "Inline L1 ASCII string handler"; + } + } + + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { + mAppRef = appRef; + mFileData = fileData; + + mAppRef.DebugLog("InlineL1String(id=" + + AppDomain.CurrentDomain.Id + "): prepare()"); + } + + public void UpdateSymbolList(List plSyms) { + // reset this every time, in case they remove the symbol + mInlineL1StringAddr = -1; + + foreach (PlSymbol sym in plSyms) { + if (sym.Label == CALL_LABEL) { + mInlineL1StringAddr = sym.Value; + break; + } + } + mAppRef.DebugLog(CALL_LABEL + " @ $" + mInlineL1StringAddr.ToString("x6")); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return beforeLabel == CALL_LABEL || afterLabel == CALL_LABEL; + } + + public void CheckJsr(int offset, out bool noContinue) { + noContinue = false; + int target = Util.GetWord(mFileData, offset + 1, 2, false); + if (target != mInlineL1StringAddr) { + return; + } + if (offset + 3 >= mFileData.Length) { + return; // length byte is off end + } + int len = mFileData[3]; // first byte past JSR + if (offset + 4 + len > mFileData.Length) { + mAppRef.DebugLog("L1 string ran off end of file at +" + + (offset + 4).ToString("x6")); + return; + } + + // Assuming ASCII. This can be hard-coded, use auto-detection, or look + // up a value in a project constant. + mAppRef.SetInlineDataFormat(offset + 3, len + 1, + DataType.StringL8, DataSubType.Ascii, null); + } + } +} diff --git a/SourceGen/Examples/Scripts/InlineNullTerm.cs b/SourceGen/Examples/Scripts/InlineNullTerm.cs deleted file mode 100644 index 30431ba..0000000 --- a/SourceGen/Examples/Scripts/InlineNullTerm.cs +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2019 faddenSoft. All Rights Reserved. -// See the LICENSE.txt file for distribution terms (Apache 2.0). - -using System; -using System.Collections.Generic; - -using PluginCommon; - -namespace ExtensionScriptSample { - /// - /// Sample class for handling a JSR followed by an inline null-terminated string. - /// - public class InlineNullStringHandler : MarshalByRefObject, IPlugin, IPlugin_InlineJsr { - private IApplication mAppRef; - private byte[] mFileData; - - private int mInlineNullStringAddr; // jsr - - public string Identifier { - get { - return "Inline null-terminated ASCII string handler"; - } - } - - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { - mAppRef = appRef; - mFileData = fileData; - - mAppRef.DebugLog("InlineNullStringHandler(id=" + - AppDomain.CurrentDomain.Id + "): prepare()"); - - // reset this every time, in case they remove the label - mInlineNullStringAddr = -1; - foreach (PlSymbol sym in plSyms) { - if (sym.Label == "PrintInlineZString") { - mInlineNullStringAddr = sym.Value; - break; - } - } - mAppRef.DebugLog("PrintInlineZString @ $" + mInlineNullStringAddr.ToString("x6")); - } - - public void CheckJsr(int offset, out bool noContinue) { - noContinue = false; - int target = Util.GetWord(mFileData, offset + 1, 2, false); - if (target == mInlineNullStringAddr) { - // search for the terminating null byte - int nullOff = offset + 3; - while (nullOff < mFileData.Length) { - if (mFileData[nullOff] == 0) { - break; - } - nullOff++; - } - if (nullOff == mFileData.Length) { - mAppRef.DebugLog("Unable to find end of null-terminated string at +" + - (offset+3).ToString("x6")); - return; - } - - // Assuming ASCII. This can be hard-coded, use auto-detection, or look - // up a value in a project constant. - mAppRef.SetInlineDataFormat(offset + 3, nullOff - (offset + 3) + 1, - DataType.StringNullTerm, DataSubType.Ascii, null); - } - } - } -} diff --git a/SourceGen/Examples/Scripts/InlineNullTermString.cs b/SourceGen/Examples/Scripts/InlineNullTermString.cs new file mode 100644 index 0000000..e5453ee --- /dev/null +++ b/SourceGen/Examples/Scripts/InlineNullTermString.cs @@ -0,0 +1,77 @@ +// Copyright 2019 faddenSoft. All Rights Reserved. +// See the LICENSE.txt file for distribution terms (Apache 2.0). + +using System; +using System.Collections.Generic; + +using PluginCommon; + +namespace ExtensionScriptSample { + /// + /// Sample class for handling a JSR followed by an inline null-terminated string. Any + /// label that starts with "PrintLineNullString" is matched. + /// + public class InlineNullTermString : MarshalByRefObject, IPlugin, IPlugin_SymbolList, + IPlugin_InlineJsr { + private IApplication mAppRef; + private byte[] mFileData; + + private const string LABEL_PREFIX = "PrintInlineNullString"; + private Dictionary mNullStringAddrs = new Dictionary(); + + public string Identifier { + get { + return "Inline null-terminated ASCII string handler"; + } + } + + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { + mAppRef = appRef; + mFileData = fileData; + + mAppRef.DebugLog("InlineNullStringHandler(id=" + + AppDomain.CurrentDomain.Id + "): prepare()"); + } + + public void UpdateSymbolList(List plSyms) { + mNullStringAddrs.Clear(); + + foreach (PlSymbol sym in plSyms) { + if (sym.Label.StartsWith(LABEL_PREFIX)) { + mNullStringAddrs.Add(sym.Value, sym); + } + } + mAppRef.DebugLog(LABEL_PREFIX + " matched " + mNullStringAddrs.Count + " labels"); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return beforeLabel.StartsWith(LABEL_PREFIX) || afterLabel.StartsWith(LABEL_PREFIX); + } + + public void CheckJsr(int offset, out bool noContinue) { + noContinue = false; + int target = Util.GetWord(mFileData, offset + 1, 2, false); + if (!mNullStringAddrs.ContainsKey(target)) { + return; + } + + // search for the terminating null byte + int nullOff = offset + 3; + while (nullOff < mFileData.Length) { + if (mFileData[nullOff] == 0) { + break; + } + nullOff++; + } + if (nullOff == mFileData.Length) { + mAppRef.DebugLog("Unable to find end of null-terminated string at +" + + (offset+3).ToString("x6")); + return; + } + + // Assuming ASCII. This can be hard-coded, use auto-detection, or look + // up a value in a project constant. + mAppRef.SetInlineDataFormat(offset + 3, nullOff - (offset + 3) + 1, + DataType.StringNullTerm, DataSubType.Ascii, null); + } + } +} diff --git a/SourceGen/Examples/Scripts/Sample b/SourceGen/Examples/Scripts/Sample index 4f43070bad8012717e520994b1f158bff085575d..c8968a968d68c85b7d654d985cf3a1eca9e24be2 100644 GIT binary patch literal 41 qcmY#T6X5j7FIULP&r7#gP!|YEEiO^W&r4-c(15T@%JUf#fB*pLtqbS? literal 13 UcmY%35eP{wF3HSGXGll@02kf^+W-In diff --git a/SourceGen/Examples/Scripts/Sample.S b/SourceGen/Examples/Scripts/Sample.S index 9ddddb2..8c2f85a 100644 --- a/SourceGen/Examples/Scripts/Sample.S +++ b/SourceGen/Examples/Scripts/Sample.S @@ -1,8 +1,18 @@ org $1000 - jsr PrintInlineZString - asc 'Testing',00 + jsr PrintInlineL1String + str 'How long?' + + jsr PrintInlineZString1 + asc 'Test one',00 + + jsr PrintInlineZString2 + asc 'Test two',00 rts -PrintInlineZString +PrintInlineL1String + rts +PrintInlineZString1 + rts +PrintInlineZString2 rts diff --git a/SourceGen/Examples/Scripts/Sample.dis65 b/SourceGen/Examples/Scripts/Sample.dis65 index e63fc3e..0f7e6d2 100644 --- a/SourceGen/Examples/Scripts/Sample.dis65 +++ b/SourceGen/Examples/Scripts/Sample.dis65 @@ -1,9 +1,9 @@ ### 6502bench SourceGen dis65 v1.0 ### { -"_ContentVersion":2,"FileDataLength":13,"FileDataCrc32":205674068,"ProjectProps":{ -"CpuName":"65816","IncludeUndocumentedInstr":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{ +"_ContentVersion":2,"FileDataLength":41,"FileDataCrc32":-1863359703,"ProjectProps":{ +"CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{ "AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true}, -"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":["PROJ:InlineNullTerm.cs"],"ProjectSyms":{ +"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":["PROJ:InlineL1String.cs","PROJ:InlineNullTermString.cs"],"ProjectSyms":{ }}, "AddressMap":[{ "Offset":0,"Addr":4096}],"TypeHints":[{ @@ -16,8 +16,12 @@ "Notes":{ }, "UserLabels":{ -"12":{ -"Label":"PrintInlineZString","Value":4108,"Source":"User","Type":"LocalOrGlobalAddr"}}, +"38":{ +"Label":"PrintInlineL1String","Value":4134,"Source":"User","Type":"LocalOrGlobalAddr"}, +"39":{ +"Label":"PrintInlineNullString1","Value":4135,"Source":"User","Type":"LocalOrGlobalAddr"}, +"40":{ +"Label":"PrintInlineNullString2","Value":4136,"Source":"User","Type":"LocalOrGlobalAddr"}}, "OperandFormats":{ }, "LvTables":{ diff --git a/SourceGen/RuntimeData/Apple/GSOS.cs b/SourceGen/RuntimeData/Apple/GSOS.cs index 57ffa15..ce3a96d 100644 --- a/SourceGen/RuntimeData/Apple/GSOS.cs +++ b/SourceGen/RuntimeData/Apple/GSOS.cs @@ -34,7 +34,7 @@ using PluginCommon; */ namespace RuntimeData.Apple { - public class GSOS : MarshalByRefObject, IPlugin, IPlugin_InlineJsl { + public class GSOS : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsl { private const string GSOS_FUNC_TAG = "AppleIIgs-GSOS-Functions"; // tag used in .sym65 file private bool VERBOSE = false; @@ -48,15 +48,21 @@ namespace RuntimeData.Apple { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAppRef.DebugLog("GSOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); //System.Diagnostics.Debugger.Break(); - mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, GSOS_FUNC_TAG, appRef); + } + + public void UpdateSymbolList(List plSyms) { + // Extract the list of function name constants from the platform symbol file. + mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, GSOS_FUNC_TAG, mAppRef); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return false; } public void CheckJsl(int offset, out bool noContinue) { diff --git a/SourceGen/RuntimeData/Apple/IIgs-Toolbox.cs b/SourceGen/RuntimeData/Apple/IIgs-Toolbox.cs index 4556ca9..744b04c 100644 --- a/SourceGen/RuntimeData/Apple/IIgs-Toolbox.cs +++ b/SourceGen/RuntimeData/Apple/IIgs-Toolbox.cs @@ -27,8 +27,8 @@ using PluginCommon; */ namespace RuntimeData.Apple { - public class IIgsToolbox : MarshalByRefObject, IPlugin, IPlugin_InlineJsl { - private const string TOOLBOX_FUNC_TAG = "AppleIIgs-Toolbox-Functions"; // tag used in .sym65 file + public class IIgsToolbox : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsl { + private const string TOOLBOX_FUNC_TAG = "AppleIIgs-Toolbox-Functions"; // tag used in .sym65 file private bool VERBOSE = false; private IApplication mAppRef; @@ -41,14 +41,19 @@ namespace RuntimeData.Apple { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAppRef.DebugLog("IIgsToolbox(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); + } - mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, TOOLBOX_FUNC_TAG, appRef); + public void UpdateSymbolList(List plSyms) { + // Extract the list of function name constants from the platform symbol file. + mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, TOOLBOX_FUNC_TAG, mAppRef); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return false; } public void CheckJsl(int offset, out bool noContinue) { diff --git a/SourceGen/RuntimeData/Apple/ProDOS8.cs b/SourceGen/RuntimeData/Apple/ProDOS8.cs index 806f0e0..696dac5 100644 --- a/SourceGen/RuntimeData/Apple/ProDOS8.cs +++ b/SourceGen/RuntimeData/Apple/ProDOS8.cs @@ -29,7 +29,7 @@ parm_block */ namespace RuntimeData.Apple { - public class ProDOS8 : MarshalByRefObject, IPlugin, IPlugin_InlineJsr { + public class ProDOS8 : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsr { private const string P8_MLI_TAG = "ProDOS8-MLI-Functions"; // tag used in .sym65 file private bool VERBOSE = false; @@ -161,8 +161,7 @@ namespace RuntimeData.Apple { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAddrTrans = addrTrans; @@ -170,8 +169,13 @@ namespace RuntimeData.Apple { mAppRef.DebugLog("ProDOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); //System.Diagnostics.Debugger.Break(); + } + public void UpdateSymbolList(List plSyms) { // Extract the list of function name constants from the platform symbol file. - mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, P8_MLI_TAG, appRef); + mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, P8_MLI_TAG, mAppRef); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return false; } public void CheckJsr(int offset, out bool noContinue) { diff --git a/SourceGen/RuntimeData/Apple/SOS.cs b/SourceGen/RuntimeData/Apple/SOS.cs index 187892a..93eb325 100644 --- a/SourceGen/RuntimeData/Apple/SOS.cs +++ b/SourceGen/RuntimeData/Apple/SOS.cs @@ -29,7 +29,7 @@ parm_block */ namespace RuntimeData.Apple { - public class SOS : MarshalByRefObject, IPlugin, IPlugin_InlineBrk { + public class SOS : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineBrk { private const string SOS_MLI_TAG = "SOS-MLI-Functions"; // tag used in .sym65 file private bool VERBOSE = true; @@ -43,15 +43,20 @@ namespace RuntimeData.Apple { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAppRef.DebugLog("SOS(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); //System.Diagnostics.Debugger.Break(); + } - mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, SOS_MLI_TAG, appRef); + public void UpdateSymbolList(List plSyms) { + // Extract the list of function name constants from the platform symbol file. + mFunctionList = PlSymbol.GeneratePlatformValueList(plSyms, SOS_MLI_TAG, mAppRef); + } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return false; } public void CheckBrk(int offset, bool twoByteBrk, out bool noContinue) { diff --git a/SourceGen/RuntimeData/Help/advanced.html b/SourceGen/RuntimeData/Help/advanced.html index 6315e47..e1f9c3a 100644 --- a/SourceGen/RuntimeData/Help/advanced.html +++ b/SourceGen/RuntimeData/Help/advanced.html @@ -115,13 +115,6 @@ invoked is not defined.

Known Issues and Limitations

-

Sometimes a manual refresh (F5) is required. Plugins are executed during -the code analysis pass, but SourceGen doesn't perform code analysis on -every change. If you add or change a label that a plugin is looking -for, you won't see the effects until you do something that causes the -code analysis pass to be run. Hitting F5 does this. (There's no easy -way around this -- either we have to re-run code analysis on every label -change, or we need a way to know which labels a plugin is interested in.)

When a project is opened, any errors encountered by the script compiler are reported to the user. If the project is already open, and a script is added to the project through the Project Properties editor, compiler diff --git a/SourceGen/SGTestData/2011-hinting.cs b/SourceGen/SGTestData/2011-hinting.cs index 34837c8..8889c05 100644 --- a/SourceGen/SGTestData/2011-hinting.cs +++ b/SourceGen/SGTestData/2011-hinting.cs @@ -17,8 +17,7 @@ namespace RuntimeData.Test2011 { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; diff --git a/SourceGen/SGTestData/2022-extension-scripts-a.cs b/SourceGen/SGTestData/2022-extension-scripts-a.cs index 9ab8639..fbeefc5 100644 --- a/SourceGen/SGTestData/2022-extension-scripts-a.cs +++ b/SourceGen/SGTestData/2022-extension-scripts-a.cs @@ -7,7 +7,8 @@ using System.Collections.Generic; using PluginCommon; namespace RuntimeData.Test2022 { - public class Test2022A : MarshalByRefObject, IPlugin, IPlugin_InlineJsr, IPlugin_InlineJsl { + public class Test2022A : MarshalByRefObject, IPlugin, IPlugin_SymbolList, + IPlugin_InlineJsr, IPlugin_InlineJsl { private IApplication mAppRef; private byte[] mFileData; @@ -24,13 +25,13 @@ namespace RuntimeData.Test2022 { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAppRef.DebugLog("Test2022-A(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); - + } + public void UpdateSymbolList(List plSyms) { foreach (PlSymbol sym in plSyms) { switch (sym.Label) { case "PrintInline8String": @@ -55,6 +56,11 @@ namespace RuntimeData.Test2022 { } } + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + const string PREFIX = "PrintInline"; // all interesting labels start with this + return (beforeLabel.StartsWith(PREFIX) || afterLabel.StartsWith(PREFIX)); + } + public void CheckJsr(int offset, out bool noContinue) { noContinue = false; int target = Util.GetWord(mFileData, offset + 1, 2, false); @@ -99,6 +105,9 @@ namespace RuntimeData.Test2022 { if (target == mInlineL1StringAddr) { // 0 1 2 3 4 5 // 22 00 10 01 01 66 + if (offset + 4 >= mFileData.Length) { + return; // length byte is off end + } int len = mFileData[offset + 4]; // 1-byte len in first byte past 4-byte JSL if (offset + 5 + len > mFileData.Length) { // ran off the end @@ -109,6 +118,9 @@ namespace RuntimeData.Test2022 { mAppRef.SetInlineDataFormat(offset + 4, len + 1, DataType.StringL8, DataSubType.Ascii, null); } else if (target == mInlineL2StringAddr) { + if (offset + 5 >= mFileData.Length) { + return; // length word is off end + } int len = Util.GetWord(mFileData, offset + 4, 2, false); if (offset + 6 + len > mFileData.Length) { // ran off the end @@ -118,7 +130,7 @@ namespace RuntimeData.Test2022 { } mAppRef.SetInlineDataFormat(offset + 4, len + 2, DataType.StringL16, DataSubType.Ascii, null); - } else { + } else if (target == mInlineDciStringAddr) { // look for the first byte whose high bit doesn't match the first byte's bit // 0 1 2 3 4 5 // 22 00 30 01 66 c1 diff --git a/SourceGen/SGTestData/2022-extension-scripts-b.cs b/SourceGen/SGTestData/2022-extension-scripts-b.cs index 1bef8d8..deabeab 100644 --- a/SourceGen/SGTestData/2022-extension-scripts-b.cs +++ b/SourceGen/SGTestData/2022-extension-scripts-b.cs @@ -18,8 +18,7 @@ namespace RuntimeData.Test2022 { } } - public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans, - List plSyms) { + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate addrTrans) { mAppRef = appRef; mFileData = fileData; mAddrTrans = addrTrans; diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index a3819a4..cc4b848 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -161,7 +161,11 @@ namespace SourceGen.Sandbox { if (DomainMgr == null) { AddressTranslate addrTrans = new AddressTranslate(mProject.AddrMap); foreach (KeyValuePair kvp in mActivePlugins) { - kvp.Value.Prepare(appRef, mProject.FileData, addrTrans, plSyms); + IPlugin ipl = kvp.Value; + ipl.Prepare(appRef, mProject.FileData, addrTrans); + if (ipl is IPlugin_SymbolList) { + ((IPlugin_SymbolList)ipl).UpdateSymbolList(plSyms); + } } } else { List addrEnts = mProject.AddrMap.GetEntryList(); @@ -169,6 +173,28 @@ namespace SourceGen.Sandbox { } } + ///

+ /// Returns true if any of the plugins report that the before or after label is + /// significant. + /// + public bool IsLabelSignificant(Symbol before, Symbol after) { + string labelBefore = (before == null) ? string.Empty : before.Label; + string labelAfter = (after == null) ? string.Empty : after.Label; + if (DomainMgr == null) { + foreach (KeyValuePair kvp in mActivePlugins) { + IPlugin ipl = kvp.Value; + if (ipl is IPlugin_SymbolList && + ((IPlugin_SymbolList)ipl).IsLabelSignificant(labelBefore, + labelAfter)) { + return true; + } + } + return false; + } else { + return DomainMgr.PluginMgr.IsLabelSignificant(labelBefore, labelAfter); + } + } + /// /// Gathers a list of platform symbols from the project's symbol table. /// @@ -261,6 +287,9 @@ namespace SourceGen.Sandbox { // The plugin is actually a MarshalByRefObject, so we can't use reflection // to gather the list of interfaces. // TODO(maybe): add a call that does the query on the remote site + if (plugin is PluginCommon.IPlugin_SymbolList) { + sb.Append(" SymbolList"); + } if (plugin is PluginCommon.IPlugin_InlineJsr) { sb.Append(" InlineJsr"); }