diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index 14c2274..8062789 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -82,9 +82,10 @@ namespace CommonUtil { /// /// /// MUST match Asm65.Address.NON_ADDR. We can't use the constant directly here because - /// the classes are in different packages that aren't dependent upon each other. + /// the classes are in different packages that aren't dependent upon each other. We + /// have to make this public because PluginCommon.AddressTranslate needs it as well. /// - private const int NON_ADDR = -1025; + public const int NON_ADDR = -1025; #region Structural diff --git a/PluginCommon/AddressTranslate.cs b/PluginCommon/AddressTranslate.cs index a7c510d..bfec621 100644 --- a/PluginCommon/AddressTranslate.cs +++ b/PluginCommon/AddressTranslate.cs @@ -15,6 +15,7 @@ */ using System; using System.Runtime.Serialization; + using CommonUtil; namespace PluginCommon { @@ -37,6 +38,8 @@ namespace PluginCommon { /// access to data that is split into multiple regions. /// public class AddressTranslate { + public const int NON_ADDR = AddressMap.NON_ADDR; + private AddressMap mAddrMap; public AddressTranslate(AddressMap addrMap) { diff --git a/PluginCommon/PluginManager.cs b/PluginCommon/PluginManager.cs index 39b3826..c8eb010 100644 --- a/PluginCommon/PluginManager.cs +++ b/PluginCommon/PluginManager.cs @@ -190,7 +190,11 @@ namespace PluginCommon { IPlugin ipl = kvp.Value; ipl.Prepare(appRef, mFileData, addrTrans); if (ipl is IPlugin_SymbolList) { - ((IPlugin_SymbolList)ipl).UpdateSymbolList(plSyms); + try { + ((IPlugin_SymbolList)ipl).UpdateSymbolList(plSyms); + } catch (Exception ex) { + throw new Exception("Failed in UpdateSymbolList(" + kvp.Key + ")", ex); + } } } } diff --git a/SourceGen/RuntimeData/Common/StdInline.cs b/SourceGen/RuntimeData/Common/StdInline.cs new file mode 100644 index 0000000..bc39fcb --- /dev/null +++ b/SourceGen/RuntimeData/Common/StdInline.cs @@ -0,0 +1,231 @@ +/* + * Copyright 2021 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.Generic; +using PluginCommon; + +namespace RuntimeData.Common { + /// + /// Performs inline data formatting for various common situations: + /// + /// InAZ_* - inline ASCII null-terminated string + /// InA1_* - inline ASCII length-delimited string + /// InPZ_* - inline PETSCII null-terminated string + /// InP1_* - inline PETSCII length-delimited string + /// InW_* - inline 16-bit word + /// InWA_* - inline 16-bit address + /// InNR_* - non-returning call + /// + /// Put a label with the appropriate prefix on the address of the subroutine, and all + /// calls to it will be formatted automatically. For example, JSRs to the label + /// "InAZ_PrintString" will be expected to be followed by null-terminated ASCII string data. + /// + /// ASCII functions work for standard and high ASCII, auto-detecting the encoding based on + /// the first character. + /// + public class StdInline : MarshalByRefObject, IPlugin, IPlugin_SymbolList, IPlugin_InlineJsr { + private IApplication mAppRef; + private byte[] mFileData; + + private class NameMap { + public string Prefix { get; private set; } + public InlineKind Kind { get; private set; } + public NameMap(string prefix, InlineKind kind) { + Prefix = prefix; + Kind = kind; + } + }; + private enum InlineKind { Unknown = 0, InAZ, InA1, InPZ, InP1, InW, InWA, InNR }; + private static NameMap[] sMap = { + new NameMap("InNR_", InlineKind.InNR), + new NameMap("InAZ_", InlineKind.InAZ), + new NameMap("InA1_", InlineKind.InA1), + new NameMap("InPZ_", InlineKind.InPZ), + new NameMap("InP1_", InlineKind.InP1), + new NameMap("InW_", InlineKind.InW), + new NameMap("InWA_", InlineKind.InWA), + }; + + // Map of addresses (not offsets) in project to inline data handled by code there. + private Dictionary mInlineLabels = new Dictionary(); + + // IPlugin + public string Identifier { + get { return "Standard inline data formatter"; } + } + + // IPlugin + public void Prepare(IApplication appRef, byte[] fileData, AddressTranslate unused) { + mAppRef = appRef; + mFileData = fileData; + + mAppRef.DebugLog("StdInline(id=" + AppDomain.CurrentDomain.Id + "): prepare()"); + } + + // IPlugin + public void Unprepare() { + mAppRef = null; + mFileData = null; + } + + // IPlugin_SymbolList + public void UpdateSymbolList(List plSyms) { + mInlineLabels.Clear(); + + // Find matching symbols. Save the symbol's value (its address) and the type. + // We want an exact match on L1STR_NAME, and prefix matches on the other two. + foreach (PlSymbol sym in plSyms) { + // We might want to ignore user labels in non-addressable regions, which all + // show up with NON_ADDR as their address. In practice it doesn't matter. + foreach (NameMap map in sMap) { + if (sym.Label.StartsWith(map.Prefix)) { + // Multiple offsets could have the same address. Map the first. + if (!mInlineLabels.ContainsKey(sym.Value)) { + mInlineLabels.Add(sym.Value, map.Kind); + } else { + mAppRef.DebugLog("Ignoring duplicate address " + + sym.Value.ToString("x4")); + } + break; + } + } + } + mAppRef.DebugLog("Found matches for " + mInlineLabels.Count + " labels"); + } + + // IPlugin_SymbolList + public bool IsLabelSignificant(string beforeLabel, string afterLabel) { + return DoesLabelMatch(beforeLabel) || DoesLabelMatch(afterLabel); + } + private static bool DoesLabelMatch(string label) { + foreach (NameMap map in sMap) { + if (label.StartsWith(map.Prefix)) { + return true; + } + } + return false; + } + + // IPlugin_InlineJsr + public void CheckJsr(int offset, int operand, out bool noContinue) { + noContinue = false; + + InlineKind kind; + if (!mInlineLabels.TryGetValue(operand, out kind)) { + // JSR destination address not recognized. + return; + } + + offset += 3; // move past JSR + + switch (kind) { + case InlineKind.InAZ: + // Null-terminated ASCII string. + FormatNullTermString(offset, false); + break; + case InlineKind.InA1: + // Length-delimited ASCII string + FormatL1String(offset, false); + break; + case InlineKind.InPZ: + // Null-terminated PETSCII string. + FormatNullTermString(offset, true); + break; + case InlineKind.InP1: + // Length-delimited PETSCII string + FormatL1String(offset, true); + break; + case InlineKind.InW: + case InlineKind.InWA: + // 16-bit value. Start by confirming next two bytes are inside the file bounds. + if (!Util.IsInBounds(mFileData, offset, 2)) { + return; + } + + if (kind == InlineKind.InW) { + // Format 16-bit value as default (hex). + mAppRef.SetInlineDataFormat(offset, 2, + DataType.NumericLE, DataSubType.None, null); + } else { + // Format 16-bit value as an address. + mAppRef.SetInlineDataFormat(offset, 2, + DataType.NumericLE, DataSubType.Address, null); + } + break; + case InlineKind.InNR: + // Non-returning call. + noContinue = true; + break; + } + } + + private void FormatNullTermString(int offset, bool isPetscii) { + if (offset < 0 || offset >= mFileData.Length) { + return; // first byte is not inside file + } + + // search for the terminating null byte + int nullOff = offset; + while (nullOff < mFileData.Length) { + if (mFileData[nullOff] == 0x00) { + break; + } + nullOff++; + } + if (nullOff == mFileData.Length) { + mAppRef.DebugLog("Unable to find end of null-terminated string at +" + + offset.ToString("x6")); + return; + } + + DataSubType stype; + if (isPetscii) { + stype = DataSubType.C64Petscii; + } else if (mFileData[offset] >= 0x80) { + stype = DataSubType.HighAscii; + } else { + stype = DataSubType.Ascii; + } + + mAppRef.SetInlineDataFormat(offset, nullOff - offset + 1, + DataType.StringNullTerm, stype, null); + } + + private void FormatL1String(int offset, bool isPetscii) { + if (offset < 0 || offset >= mFileData.Length) { + return; // length byte is not inside file + } + int len = mFileData[offset]; + if (offset + 1 + len > mFileData.Length) { + mAppRef.DebugLog("L1 string ran off end of file at +" + offset.ToString("x6")); + return; + } + + DataSubType stype; + if (isPetscii) { + stype = DataSubType.C64Petscii; + } else if (len > 0 && mFileData[offset + 1] >= 0x80) { + stype = DataSubType.HighAscii; + } else { + stype = DataSubType.Ascii; + } + + mAppRef.SetInlineDataFormat(offset, len + 1, + DataType.StringL8, stype, null); + } + } +} diff --git a/SourceGen/SGTestData/20270-std-inline b/SourceGen/SGTestData/20270-std-inline new file mode 100644 index 0000000..a151d1c Binary files /dev/null and b/SourceGen/SGTestData/20270-std-inline differ diff --git a/SourceGen/SGTestData/20270-std-inline.dis65 b/SourceGen/SGTestData/20270-std-inline.dis65 new file mode 100644 index 0000000..df1d859 --- /dev/null +++ b/SourceGen/SGTestData/20270-std-inline.dis65 @@ -0,0 +1,113 @@ +### 6502bench SourceGen dis65 v1.0 ### +{ +"_ContentVersion":5, +"FileDataLength":210, +"FileDataCrc32":-1608872177, +"ProjectProps":{ +"CpuName":"6502", +"IncludeUndocumentedInstr":false, +"TwoByteBrk":false, +"EntryFlags":32702671, +"AutoLabelStyle":"Simple", +"AnalysisParams":{ +"AnalyzeUncategorizedData":true, +"DefaultTextScanMode":"LowHighAscii", +"MinCharsForString":4, +"SeekNearbyTargets":true, +"UseRelocData":false, +"SmartPlpHandling":false, +"SmartPlbHandling":true}, + +"PlatformSymbolFileIdentifiers":[], +"ExtensionScriptFileIdentifiers":["RT:Common/StdInline.cs"], +"ProjectSyms":{ +}}, + +"AddressMap":[{ +"Offset":0, +"Addr":4096, +"Length":-1024, +"PreLabel":"", +"IsRelative":false}], +"TypeHints":[{ +"Low":0, +"High":0, +"Hint":"Code"}], +"StatusFlagOverrides":{ +}, + +"Comments":{ +}, + +"LongComments":{ +}, + +"Notes":{ +}, + +"UserLabels":{ +"3":{ +"Label":"InAZ_test", +"Value":4099, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"4":{ +"Label":"InA1_test", +"Value":4100, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"5":{ +"Label":"InPZ_test", +"Value":4101, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"6":{ +"Label":"InP1_test", +"Value":4102, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"7":{ +"Label":"InW_test", +"Value":4103, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"8":{ +"Label":"InWA_test", +"Value":4104, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}, + +"9":{ +"Label":"InNR_test", +"Value":4105, +"Source":"User", +"Type":"GlobalAddr", +"LabelAnno":"None"}}, + +"OperandFormats":{ +}, + +"LvTables":{ +}, + +"Visualizations":[], +"VisualizationAnimations":[], +"VisualizationSets":{ +}, + +"RelocList":{ +}, + +"DbrValues":{ +}} diff --git a/SourceGen/SGTestData/Expected/20270-std-inline_64tass.S b/SourceGen/SGTestData/Expected/20270-std-inline_64tass.S new file mode 100644 index 0000000..565b853 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20270-std-inline_64tass.S @@ -0,0 +1,65 @@ + .cpu "6502" + .enc "sg_hiascii" + .cdef $20,$7e,$a0 + .enc "sg_ascii" + .cdef $20,$7e,$20 +* = $1000 + jmp L1040 + +InAZ_test rts + +InA1_test rts + +InPZ_test rts + +InP1_test rts + +InW_test rts + +InWA_test rts + +InNR_test rts + + .fill 54,$00 + +L1040 nop + jsr InAZ_test + .null "Test AZ_ low" + jsr InAZ_test + .enc "sg_hiascii" + .null "Test AZ_ high" + jsr InA1_test + .enc "sg_ascii" + .ptext "Test A1_ low" + jsr InA1_test + .enc "sg_hiascii" + .ptext "Test A1_ high" + jsr InPZ_test + .enc "none" + .null "Test PZ",$5f + jsr InP1_test + .ptext "Test P1",$5f + jsr InW_test + .word $1234 + jsr InWA_test + .word L1040 + jsr _L10AD + jsr InNR_test + + .byte $ea + .byte $00 + +_L10AD nop + jsr _L10B6 + jsr _L10C3 + nop + rts + +_L10B6 jsr InA1_test + .byte $ff + .enc "sg_ascii" + .text "too long" + .byte $ea + +_L10C3 jsr InAZ_test + .text "does not end" diff --git a/SourceGen/SGTestData/Expected/20270-std-inline_acme.S b/SourceGen/SGTestData/Expected/20270-std-inline_acme.S new file mode 100644 index 0000000..164742e --- /dev/null +++ b/SourceGen/SGTestData/Expected/20270-std-inline_acme.S @@ -0,0 +1,56 @@ + !cpu 6502 +* = $1000 + jmp L1040 + +InAZ_test rts + +InA1_test rts + +InPZ_test rts + +InP1_test rts + +InW_test rts + +InWA_test rts + +InNR_test rts + + !fill 54,$00 + +L1040 nop + jsr InAZ_test + !text "Test AZ_ low",$00 + jsr InAZ_test + !hex d4e5f3f4a0c1dadfa0e8e9e7e800 + jsr InA1_test + !text $0c,"Test A1_ low" + jsr InA1_test + !hex 0dd4e5f3f4a0c1b1dfa0e8e9e7e8 + jsr InPZ_test + !pet "Test PZ",$5f,$00 + jsr InP1_test + !pet $08,"Test P1",$5f + jsr InW_test + !word $1234 + jsr InWA_test + !word L1040 + jsr @L10AD + jsr InNR_test + + !byte $ea + !byte $00 + +@L10AD nop + jsr @L10B6 + jsr @L10C3 + nop + rts + +@L10B6 jsr InA1_test + !byte $ff + !text "too long" + !byte $ea + +@L10C3 jsr InAZ_test + !text "does not end" diff --git a/SourceGen/SGTestData/Expected/20270-std-inline_cc65.S b/SourceGen/SGTestData/Expected/20270-std-inline_cc65.S new file mode 100644 index 0000000..d0859fb --- /dev/null +++ b/SourceGen/SGTestData/Expected/20270-std-inline_cc65.S @@ -0,0 +1,56 @@ + .setcpu "6502" + .org $1000 + jmp L1040 + +InAZ_test: rts + +InA1_test: rts + +InPZ_test: rts + +InP1_test: rts + +InW_test: rts + +InWA_test: rts + +InNR_test: rts + + .res 54,$00 + +L1040: nop + jsr InAZ_test + .asciiz "Test AZ_ low" + jsr InAZ_test + .byte $d4,$e5,$f3,$f4,$a0,$c1,$da,$df,$a0,$e8,$e9,$e7,$e8,$00 + jsr InA1_test + .byte $0c,"Test A1_ low" + jsr InA1_test + .byte $0d,$d4,$e5,$f3,$f4,$a0,$c1,$b1,$df,$a0,$e8,$e9,$e7,$e8 + jsr InPZ_test + .byte $d4,$45,$53,$54,$20,$d0,$da,$5f,$00 + jsr InP1_test + .byte $08,$d4,$45,$53,$54,$20,$d0,$31,$5f + jsr InW_test + .word $1234 + jsr InWA_test + .word L1040 + jsr @L10AD + jsr InNR_test + + .byte $ea + .byte $00 + +@L10AD: nop + jsr @L10B6 + jsr @L10C3 + nop + rts + +@L10B6: jsr InA1_test + .byte $ff + .byte "too long" + .byte $ea + +@L10C3: jsr InAZ_test + .byte "does not end" diff --git a/SourceGen/SGTestData/Expected/20270-std-inline_cc65.cfg b/SourceGen/SGTestData/Expected/20270-std-inline_cc65.cfg new file mode 100644 index 0000000..44719e4 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20270-std-inline_cc65.cfg @@ -0,0 +1,9 @@ +# 6502bench SourceGen generated linker script for 20270-std-inline +MEMORY { + MAIN: file=%O, start=%S, size=65536; +} +SEGMENTS { + CODE: load=MAIN, type=rw; +} +FEATURES {} +SYMBOLS {} diff --git a/SourceGen/SGTestData/Expected/20270-std-inline_merlin32.S b/SourceGen/SGTestData/Expected/20270-std-inline_merlin32.S new file mode 100644 index 0000000..b8bd7b0 --- /dev/null +++ b/SourceGen/SGTestData/Expected/20270-std-inline_merlin32.S @@ -0,0 +1,55 @@ + org $1000 + jmp L1040 + +InAZ_test rts + +InA1_test rts + +InPZ_test rts + +InP1_test rts + +InW_test rts + +InWA_test rts + +InNR_test rts + + ds 54 + +L1040 nop + jsr InAZ_test + asc 'Test AZ_ low',00 + jsr InAZ_test + asc "Test AZ_ high",00 + jsr InA1_test + str 'Test A1_ low' + jsr InA1_test + str "Test A1_ high" + jsr InPZ_test + hex d445535420d0da5f00 + jsr InP1_test + hex 08d445535420d0315f + jsr InW_test + dw $1234 + jsr InWA_test + dw L1040 + jsr :L10AD + jsr InNR_test + + dfb $ea + dfb $00 + +:L10AD nop + jsr :L10B6 + jsr :L10C3 + nop + rts + +:L10B6 jsr InA1_test + dfb $ff + asc 'too long' + dfb $ea + +:L10C3 jsr InAZ_test + asc 'does not end' diff --git a/SourceGen/SGTestData/Source/20270-std-inline.S b/SourceGen/SGTestData/Source/20270-std-inline.S new file mode 100644 index 0000000..de44f5b --- /dev/null +++ b/SourceGen/SGTestData/Source/20270-std-inline.S @@ -0,0 +1,72 @@ +; Copyright 2021 faddenSoft. All Rights Reserved. +; See the LICENSE.txt file for distribution terms (Apache 2.0). +; +; Test standard inline script. +; +; Assembler: ACME +; % tass64 --ascii --case-sensitive --nostart 20260-nested-regions.S + + !cpu 6502 +* = $1000 + jmp calls + +; EDIT: put appropriate labels on these +f_AZ rts +f_A1 rts +f_PZ rts +f_P1 rts +f_W rts +f_WA rts +f_NR rts + + !align 63,0,0 +calls nop + jsr f_AZ + !text "Test AZ_ low",$00 + + jsr f_AZ + !xor $80 { + !text "Test AZ_ high" + } + !byte $00 + + jsr f_A1 + !text 12,"Test A1_ low" + + jsr f_A1 + !byte 13 + !xor $80 { + !text "Test A1_ high" + } + + jsr f_PZ + !pet "Test PZ_",$00 + jsr f_P1 + !pet 8,"Test P1_" + + jsr f_W + !word $1234 + + jsr f_WA + !word calls + + jsr cont + jsr f_NR + nop ;check: not formatted as instruction + brk +cont nop + +; end-of-file error cases + jsr end_err1 + jsr end_err2 + + nop + rts + +end_err1 + jsr f_A1 + !text 255,"too long" + nop +end_err2 + jsr f_AZ + !text "does not end" ;must be last diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index daddb1a..bd4bb6b 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -325,7 +325,7 @@ namespace SourceGen.Sandbox { } /// - /// Gathers a list of platform symbols from the project's symbol table. + /// Gathers a list of symbols from the project's symbol table. /// private List GeneratePlSymbolList() { List plSymbols = new List(); diff --git a/docs/sgmanual/advanced.html b/docs/sgmanual/advanced.html index 76b6da6..aeadf36 100644 --- a/docs/sgmanual/advanced.html +++ b/docs/sgmanual/advanced.html @@ -157,10 +157,6 @@ import the changes.

the full .NET Standard 2.0 APIs. They're compiled at run time by SourceGen and executed in a sandbox with security restrictions.

-

SourceGen defines an interface that plugins must implement, and an -interface that plugins can use to interact with SourceGen. See -Interfaces.cs in the PluginCommon directory.

-

The current interfaces can be used to generate visualizations, to identify inline data that follows JSR, JSL, or BRK instructions, and to format operands. The latter can be used to format code and data, e.g. @@ -169,23 +165,62 @@ replacing immediate load operands with symbolic constants.

Scripts may be loaded from the RuntimeData directory, or from the directory where the project file lives. Attempts to load them from other locations will fail.

-

A project may load multiple scripts. The order in which they are +

A project may load multiple scripts. The order in which functions are invoked is not defined.

-

Known Issues and Limitations

+

Built-In Scripts

-

Scripts are currently limited to C# version 5, because the compiler -built into .NET only handles that. C# 6 and later require installing an -additional package ("Roslyn"), so SourceGen does not support this.

+

A number of scripts are distributed with SourceGen, and may be used +freely by projects. Most are tailored for a specific platform, e.g. +Apple II ProDOS calls or Atari 2600 graphics.

+

The StdInline.cs script in the RuntimeData/Common +directory has some general-purpose inline data formatting functions. +To use them, add the script to the project, then add an appropriate label +to the subroutine that handles the inline data. For example, suppose the +code looks like this:

+
+$1000  START        JSR     L1234
+$1003               .STR    "hello, world!"
+$1010               .DD1    $00
+$1011               .DD1    $a9
+$1012               .DD1    $55
+[...]
+$1234  L1234        PLA
+[...]
+
+

The code won't analyze correctly because it will try to follow the +code into the string data. If you include the script, and set the label +at L1234 to InAZ_PrintString, the code will +then format correctly:

+
+$1000  START        JSR     InAZ_PrintString
+$1003               .ZSTR   "hello, world!"
+$1011               LDA     #$55
+[...]
+$1234  InAZ_PrintString PLA
+[...]
+
-

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 -messages are silently discarded. (This also applies if you undo/redo across -the property edit.) Use File > Reload External Files to see the -compiler messages.

+

The label prefixes currently defined by the script are:

+
    +
  • InAZ_ : inline ASCII null-terminated string
  • +
  • InA1_ : inline ASCII length-delimited string
  • +
  • InPZ_ : inline PETSCII null-terminated string
  • +
  • InP1_ : inline PETSCII length-delimited string
  • +
  • InW_ : inline 16-bit word
  • +
  • InWA_ : inline 16-bit address
  • +
  • InNR_ : non-returning call (i.e. the JSR acts like + a JMP)
  • +
+

-

Development

+

Anything more complicated will require a custom script.

+ +

Script Development

+ +

SourceGen defines an interface that plugins must implement, and an +interface that plugins can use to interact with SourceGen. See +Interfaces.cs in the PluginCommon directory.

The easiest way to develop extension scripts is inside the 6502bench solution in Visual Studio. This way you have the interfaces available @@ -205,9 +240,22 @@ will allow regeneration of the PDB files.

Some commonly useful functions are defined in the PluginCommon.Util class, which is available to plugins. These call into the CommonUtil library, which is shared with SourceGen. -While plugins could use CommonUtil directly, they should avoid doing so. The -APIs there are not guaranteed to be stable, so plugins that rely on them -may break in a subsequent release of SourceGen.

+While plugins could technically use CommonUtil directly, they should avoid +doing so. The APIs there are not guaranteed to be stable, so plugins +that rely on them may break in a subsequent release of SourceGen.

+ +

Known Issues and Limitations

+ +

Scripts are currently limited to C# version 5, because the compiler +built into .NET only handles that. C# 6 and later require installing an +additional package ("Roslyn"), so SourceGen does not support this.

+ +

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 +messages are silently discarded. (This also applies if you undo/redo across +the property edit.) Use File > Reload External Files to see the +compiler messages.

PluginDllCache Directory

diff --git a/docs/sgmanual/index.html b/docs/sgmanual/index.html index 139c156..d0c2d25 100644 --- a/docs/sgmanual/index.html +++ b/docs/sgmanual/index.html @@ -174,7 +174,11 @@ using the Help > Help menu item or by hitting
  • Advanced Topics