Megjegyzés
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhat bejelentkezni vagy módosítani a címtárat.
Az oldalhoz való hozzáféréshez engedély szükséges. Megpróbálhatja módosítani a címtárat.
Jegyzet
Ez a cikk egy funkcióspecifikáció. A specifikáció a funkció tervezési dokumentumaként szolgál. Tartalmazza a specifikáció javasolt módosításait, valamint a funkció tervezése és fejlesztése során szükséges információkat. Ezeket a cikkeket mindaddig közzéteszik, amíg a javasolt specifikációmódosításokat nem véglegesítik, és be nem építik a jelenlegi ECMA-specifikációba.
A szolgáltatás specifikációja és a befejezett implementáció között eltérések lehetnek. Ezeket a különbségeket a vonatkozó nyelvi tervezési értekezlet (LDM) megjegyzései rögzítik.
A funkcióspektusok C# nyelvi szabványba való bevezetésének folyamatáról a specifikációkcímű cikkben olvashat bővebben.
A bajnokkal kapcsolatos kérdés: https://github.com/dotnet/csharplang/issues/4747
Összefoglalás
A fordító az #line
irányelvek által meghatározott leképezést alkalmazza a PDB-nek kibocsátott diagnosztikai helyekre és szekvenciapontokra.
Jelenleg csak a sorszámot és a fájl elérési útját lehet leképezni, amíg a kezdő karakter a forráskódból származik. A javaslat lehetővé teszi a teljes span-leképezés megadását.
Motiváció
A C#-forráskódot (például ASP.NET Razort) létrehozó DLL-ek jelenleg nem tudnak pontos forrásleképezést létrehozni #line
irányelvek használatával. Ez bizonyos esetekben csökkentett hibakeresési élményt eredményez, mivel a PDB-nek kibocsátott szekvenciapontok nem tudnak az eredeti forráskód pontos helyére leképezést adni.
Például a következő Razor-kód
@page "/"
Time: @DateTime.Now
így generálja a kódot (egyszerűsített):
#line hidden
void Render()
{
_builder.Add("Time:");
#line 2 "page.razor"
_builder.Add(DateTime.Now);
#line hidden
}
A fenti irányelv a fordító által a _builder.Add(DateTime.Now);
utasításhoz kibocsátott szekvenciapontot a 2. sorra képezné le, de az oszlop ki lenne kapcsolva (7 helyett 16).
A Razor-forrásgenerátor valójában helytelenül hozza létre a következő kódot:
#line hidden
void Render()
{
_builder.Add("Time:");
_builder.Add(
#line 2 "page.razor"
DateTime.Now
#line hidden
);
}
A szándék az volt, hogy megőrizze a kezdő karaktert, és a diagnosztikai hely leképezéséhez működik. Ez azonban nem működik a szekvenciapontok esetében, mivel #line
irányelv csak az azt követő sorrendi pontokra vonatkozik. A _builder.Add(DateTime.Now);
utasítás közepén nincs szekvenciapont (a sorrendi pontok csak üres kiértékelési vermet tartalmazó IL-utasításokban bocsáthatók ki). A fenti kód #line 2
irányelve így nincs hatással a létrehozott PDF-fájlra, és a hibakereső nem helyez el töréspontot vagy nem áll le a Razor-oldalon található @DateTime.Now
kódrészleten.
A javaslat által kezelt problémák: https://github.com/dotnet/roslyn/issues/43432https://github.com/dotnet/roslyn/issues/46526
Részletes kialakítás
A pp_line
irányelvben használt line_indicator
szintaxisát a következőképpen módosítjuk:
Aktuális:
line_indicator
: decimal_digit+ whitespace file_name
| decimal_digit+
| 'default'
| 'hidden'
;
Javasolt:
line_indicator
: '(' decimal_digit+ ',' decimal_digit+ ')' '-' '(' decimal_digit+ ',' decimal_digit+ ')' whitespace decimal_digit+ whitespace file_name
| '(' decimal_digit+ ',' decimal_digit+ ')' '-' '(' decimal_digit+ ',' decimal_digit+ ')' whitespace file_name
| decimal_digit+ whitespace file_name
| decimal_digit+
| 'default'
| 'hidden'
;
Vagyis: a #line
irányelv 5 tizedesjegyet fogadna el (kezdővonal, kezdő karakter, záróvonal, végkaraktér, karaktereltolás), 4 decimális szám (kezdővonal, kezdő karakter, végsor, végkaraktér) vagy egyetlen (sor).
Ha karaktereltolás nincs megadva, az alapértelmezett értéke 0, ellenkező esetben az UTF-16 karakterek számát adja meg. A számnak nem negatívnak kell lennie, és a nem megfeleltetett fájlban lévő #line direktívát követő sor hosszának kisebbnek kell lennie.
(kezdővonal, kezdő karakter)-(záróvonal, záró karakter) a megfeleltetett fájlban megadott időtartamot adja meg. kezdővonal és zárósor pozitív egész számok, amelyek sorszámokat adnak meg. kezdő karakter, záró karakter pozitív egész számok, amelyek UTF-16 karakterszámokat adnak meg. kezdővonal, kezdő karakter, záróvonal, záró karakter 1-alapúak, ami azt jelenti, hogy a fájl első sora és az egyes sorok első UTF-16 karaktere 1-es számhoz van rendelve.
A megvalósítás korlátozná ezeket a számokat, hogy érvényes szekvenciapont-forrástartományt határozzanak meg:
- kezdővonal – 1 a [0, 0x20000000) tartományon belül van, és nem egyenlő a 0xfeefee.
- záróvonal – 1 a [0, 0x20000000) tartományon belül van, és nem egyenlő a 0xfeefee.
- kezdő karakter – Az 1 tartományon belül van [0, 0x10000)
- végkarakter – 1 a tartományon belül van [0, 0x10000)
- záróvonal nagyobb vagy egyenlő, mint kezdővonal.
- kezdővonal egyenlő záróvonallal akkor nagyobb, mint kezdő karakter.
Vegye figyelembe, hogy az irányelv szintaxisában megadott számok 1-alapú számok, de a PDB tényleges spanjai nulla alapúak. Ezért a fenti -1 kiigazítások.
A sorrendpontok leképezett hatóköreit és az irányelv #line
alkalmazandó diagnosztikai helyeket az alábbiak szerint számítjuk ki.
Legyen d a #line
irányelvet tartalmazó leképezetlen vonal nullaalapú száma.
Legyen az L = (kezdet: (kezdővonal - 1, kezdő karakter - 1), vége: (záróvonal - 1, záró karakter - 1)) legyen az irányelv által meghatározott nulla alapú span.
Az M függvény, amely a #line direktívát tartalmazó forrásfájl #line
irányelvének hatókörén belüli pozíciót (vonal, karakter) egy leképezett pozícióra (leképezett vonal, leképezett karakter) képezi le, a következőképpen van definiálva:
M(l, c) =
l == d + 1 => (L.start.line + l – d – 1, L.start.character + max(c – karaktereltolás, 0))
l>d + 1 => (L.start.line + l – d – 1, c)
Azokat a szintaxisszerkezeteket, amelyekkel a szekvenciapontok társítva vannak, a fordító implementációja határozza meg, és nem fedi le ezt a specifikációt. A fordító emellett minden egyes szekvenciapont esetében a leképezetlen terjedelmet is meghatározza. Ez az időtartam részben vagy teljesen lefedheti a társított szintaxisszerkezetet.
Miután a fordító meghatározta a le nem képezett terjedelmeket, a fent definiált M függvény lesz alkalmazva azok kezdő és záró pozícióira, kivéve az #line irányelv hatókörébe tartozó összes sorozatpont záró pozícióját, amelynek le nem képezett helye a d + 1 sorban van, és a karakter eltolási értéknél kisebb karakter. Az összes szekvenciapont végpozíciója L.end.
Az [5.i] példa bemutatja, hogy miért szükséges megadni az első szekvenciapont-időtartam végpontpozícióját.
A fenti definíció lehetővé teszi a leképezetlen forráskód generátorának, hogy elkerülje annak intim ismeretét, hogy a C#-nyelv pontos forrásszerkezetei milyen sorrendi pontokat hoznak létre. A
#line
direktíva hatókörében lévő műveleti pontok térképezett tartományai a megfelelő térképezetlen tartományok relatív helyzetéből származnak az első térképezetlen tartományhoz képest.
A karaktereltolás megadásával a generátor bármilyen egysoros előtagot beszúrhat az első sorba. Ez az előtag egy generált kód, amely nem szerepel a megfeleltetett fájlban. Az ilyen beszúrt előtag hatással van az első leképezetlen szekvenciapont-tartomány értékére. Ezért a következő sorozatpont-szakaszok kezdő karakterét el kell tolni az előtag hosszával (karaktereltolás). Lásd a(z) [2] példát.
Példák
Az egyértelműség kedvéért a példák a spanof('...')
és lineof('...')
pszeudo-szintaxis használatával fejezik ki a megadott kódrészlet leképezett kezdőpozícióját és sorszámát.
1. Első és későbbi szakaszok
Vegye figyelembe a következő kódot a jobb oldalon felsorolt, leképezetlen nulla alapú sorszámokkal:
#line (1,10)-(1,15) "a" // 3
A();B( // 4
);C(); // 5
D(); // 6
d = 3 L = (0, 9).. (0, 14)
Az irányelv 4 szekvenciapontra vonatkozik a következő leképezetlen és leképezett hatókörökkel: (4, 2).. (4, 5) => (0, 9).. (0, 14) (4, 6).. (5, 1) => (0, 15).. (1, 1) (5, 2).. (5, 5) => (1, 2).. (1, 5) (6, 4).. (6, 7) => (2, 4).. (2, 7)
2. Karaktereltolás
A Razor _builder.Add(
15 hosszúságú előtagot hoz létre (két kezdő szóközt is beleértve).
Borotva:
@page "/"
@F(() => 1+1,
() => 2+2
)
Generált C#:
#line hidden
void Render()
{
#line spanof('F(...)') 15 "page.razor" // 4
_builder.Add(F(() => 1+1, // 5
() => 2+2 // 6
)); // 7
#line hidden
}
);
}
d = 4 L = (1, 1)..(3, 0) karaktereltolás = 15
Ível:
-
_builder.Add(F(…));
=>F(…)
: (5, 2).. (7, 2) => (1, 1).. (3, 0) -
1+1
=>1+1
: (5, 23).. (5, 25) => (1, 9).. (1, 11) -
2+2
=>2+2
: (6, 7).. (6, 9) => (2, 7).. (2, 9)
3. Razor: Egyvonalas kiterjedés
Borotva:
@page "/"
Time: @DateTime.Now
Generált C#:
#line hidden
void Render()
{
_builder.Add("Time:");
#line spanof('DateTime.Now') 15 "page.razor"
_builder.Add(DateTime.Now);
#line hidden
);
}
4. Razor: Többsoros elrendezés
Borotva:
@page "/"
@JsonToHtml(@"
{
""key1"": "value1",
""key2"": "value2"
}")
Generált C#:
#line hidden
void Render()
{
_builder.Add("Time:");
#line spanof('JsonToHtml(@"...")') 15 "page.razor"
_builder.Add(JsonToHtml(@"
{
""key1"": "value1",
""key2"": "value2"
}"));
#line hidden
}
);
}
5. Razor: blokkszerkezetek
i. kifejezéseket tartalmazó blokk
Ebben a példában a _builder.Add(Html.Helper(() =>
utasításhoz kibocsátott IL utasításhoz társított első szekvenciapont leképezett tartományának a létrehozott fájl a.razor
Html.Helper(...)
teljes kifejezésére kell kiterjednie. Ez az [1] szabálynak a szekvenciapont végponti pozíciójára történő alkalmazásával érhető el.
@Html.Helper(() =>
{
<p>Hello World</p>
@DateTime.Now
})
#line spanof('Html.Helper(() => { ... })') 13 "a.razor"
_builder.Add(Html.Helper(() =>
#line lineof('{') "a.razor"
{
#line spanof('DateTime.Now') 13 "a.razor"
_builder.Add(DateTime.Now);
#line lineof('}') "a.razor"
}
#line hidden
)
ii. utasításokat tartalmazó blokk
Meglévő #line line file
űrlapot használ, mivel
a) A Razor nem ad hozzá előtagot, b) {
nincs jelen a létrehozott fájlban, és nem lehet rajta szekvenciapont, ezért az első le nem képezett szekvenciapont kiterjedése ismeretlen a Razor számára.
A létrehozott fájlban lévő Console
kezdő karakterének a Razor-fájlhoz kell igazodnia.
@{Console.WriteLine(1);Console.WriteLine(2);}
#line lineof('@{') "a.razor"
Console.WriteLine(1);Console.WriteLine(2);
#line hidden
iii. legfelső szintű kódot tartalmazó blokk (@code, @functions)
Meglévő #line line file
űrlapot használ, mivel
a) A Razor nem ad hozzá előtagot, b) {
nincs jelen a létrehozott fájlban, és nem lehet rajta szekvenciapont, ezért az első le nem képezett szekvenciapont kiterjedése ismeretlen a Razor számára.
A létrehozott fájlban lévő [Parameter]
kezdő karakterének a Razor-fájlhoz kell igazodnia.
@code {
[Parameter]
public int IncrementAmount { get; set; }
}
#line lineof('[') "a.razor"
[Parameter]
public int IncrementAmount { get; set; }
#line hidden
6. Razor: @for
, @foreach
, @while
, @do
, @if
, @switch
, @using
, @try
, @lock
Meglévő #line line file
űrlapot használ, mivel a) a Razor nem ad hozzá előtagot.
b) az első le nem képezett szekvenciapont kiterjedése nem ismert a Razor számára (vagy nem kell tudnia).
A generált fájlban lévő kulcsszó kezdő karakterének a Razor-fájlhoz kell igazodnia.
@for (var i = 0; i < 10; i++)
{
}
@if (condition)
{
}
else
{
}
#line lineof('for') "a.razor"
for (var i = 0; i < 10; i++)
{
}
#line lineof('if') "a.razor"
if (condition)
{
}
else
{
}
#line hidden
C# feature specifications