Skip to content
← Back

src/semantic/dotnet/assemblies.ghul

1
namespace Semantic.DotNet is
2
use TYPE = System.Type
3
4
use System.Reflection.PathAssemblyResolver
5
use System.Reflection.MetadataLoadContext
6
use System.Reflection.MetadataAssemblyResolver
7
use System.Reflection.Assembly
8
9
use IO.Std
10
11
use Collections.MAP
12
use Collections.SET
13
use Collections.LIST
14
use Collections.Iterable
15
16
use Ghul.Pipes
17
18
use Logging.TIMERS
19
20
// The load context comes from the assembly paths, which are not
21
// known until start.
22
@suppress("field-definite-assignment")
23
class ASSEMBLIES(
24
_timers: TIMERS,
25
_flags: Compiler.GLOBAL_BUILD_FLAGS,
26
_paths: Driver.PATH_CONFIG,
27
_ghul_symbol_table: Semantic.SYMBOL_TABLE,
28
_namespaces: NAMESPACES,
29
_type_name_map: TYPE_NAME_MAP,
30
_type_details_lookup: TYPE_DETAILS_LOOKUP
31
): TypeSource is
32
_callbacks: LIST[() -> void]
33
34
_metadata_load_context: MetadataLoadContext
35
36
_assemblies_by_name: MAP[string,Assembly]
37
_assemblies_by_ghul_namespace: MAP[string,SET[Assembly]]
38
39
// The file each assembly was loaded from, kept so
40
// `interface_nullability_bytes` can re-read the raw bytes —
41
// `Assembly.location` is empty for one loaded via
42
// `load_from_byte_array` below, so the path has to be tracked
43
// separately or it's lost the moment the byte array is.
44
_assembly_paths: MAP[string,string]
45
46
// A digest of each assembly in the set, by name, taken when it is
47
// first needed, so that naming the same assembly again can be told
48
// apart from naming a rebuilt one under the same name.
49
_digests: MAP[string,string]
50
51
// The assemblies this compile was handed by name, on the command
52
// line or added to a running compiler since, as against those it
53
// found for itself. Only one of these can be an earlier cell of
54
// a session.
55
_explicit_references: SET[string]
56
_session_cells: MAP[string,bool]
57
_has_access_attribute: bool?
58
59
// One `System.Reflection.PortableExecutable.PEReader` per
60
// assembly that has actually needed one — built, and its file
61
// read into memory, only once `interface_nullability_bytes`
62
// finds a type that implements a generic interface at all (its
63
// own cheap gate, checked before this cache is ever touched);
64
// an assembly contributing no such type is never opened here,
65
// however many of its types get imported. `null` is cached too,
66
// for an assembly whose path couldn't be read or parsed, so a
67
// failing lookup isn't retried on every call. Kept for the
68
// process's lifetime once built, so this only pays off in the
69
// common case where a nullable-argument-carrying assembly is
70
// asked about more than once.
71
//
72
// Cached as the `PEReader` itself, not the `MetadataReader` it
73
// hands out — a `MetadataReader` is a pointer into the memory
74
// block the `PEReader` owns and holds no reference back to it,
75
// so a `MetadataReader` cached alone outlives the pin on that
76
// block only until the `PEReader` is collected.
77
_interface_metadata_readers: MAP[string,System.Reflection.PortableExecutable.PEReader?]
78
79
_is_started: bool
80
_default_imports_are_needed: bool
81
82
blocked_assemblies: SET[string]
83
84
all_assemblies: Iterable[Assembly] => _assemblies_by_name.values
85
86
// A type by its full name, from whichever loaded assembly defines
87
// it, or null if none does.
88
find_type(full_name: string) -> TYPE? is
89
for assembly in _assemblies_by_name.values do
90
let type = assembly.get_type(full_name)
91
92
if type? then
93
return type
94
fi
95
od
96
97
return null
98
si
99
100
// The version of the assembly loaded under this name, in the
101
// colon-separated form a reference row records, or null if none
102
// was.
103
version_of(name: string) -> string? =>
104
if _assemblies_by_name.contains_key(name) then
105
if let version = _assemblies_by_name[name].get_name().version then
106
version.to_string().replace('.', ':')
107
else
108
null
109
fi
110
else
111
null
112
fi
113
114
// The public key token of the assembly loaded under this name,
115
// which a reference to a strong-named assembly has to record for
116
// a compiler to accept it. Null if none was loaded or it has none.
117
public_key_token_of(name: string) -> ubyte[]? =>
118
if _assemblies_by_name.contains_key(name) then
119
(let token = _assemblies_by_name[name].get_name().get_public_key_token()
120
121
if token? /\ token.count > 0 then token else null fi)
122
else
123
null
124
fi
125
126
init(..) is
127
_default_imports_are_needed = true
128
129
_callbacks = LIST()
130
131
_assemblies_by_name = MAP()
132
_assemblies_by_ghul_namespace = MAP[string,SET[Assembly]]()
133
_assembly_paths = MAP()
134
_digests = MAP()
135
_explicit_references = SET[string]()
136
_session_cells = MAP[string,bool]()
137
_interface_metadata_readers = MAP[string,System.Reflection.PortableExecutable.PEReader?]()
138
139
blocked_assemblies = SET()
140
block_all_unsupported_assemblies()
141
si
142
143
on_start(callback: () -> void) is
144
_callbacks.add(callback)
145
si
146
147
get_type(type_name: string) -> TYPE =>
148
get_type("System.Runtime", type_name)
149
150
get_type(assembly_name: string? mut, type_name: string) -> TYPE is
151
if assembly_name == null then
152
assembly_name = "System.Runtime"
153
fi
154
155
let result: TYPE? = _assemblies_by_name[assembly_name].get_type(type_name)
156
157
assert result? else "couldn't find type {type_name} in {assembly_name}"
158
159
return result
160
si
161
162
get_types(type_names: Iterable[string]) -> Collections.List[TYPE] =>
163
type_names |>
164
map(name => get_type(name)) |>
165
collect()
166
167
get_types(assembly_and_type_names: Iterable[(assembly_name: string, type_name: string)]) -> Collections.List[TYPE] =>
168
assembly_and_type_names |>
169
map(names => get_type(names.assembly_name, names.type_name)) |>
170
collect()
171
172
// See `INTERFACE_NULLABILITY` for why this exists at all: the
173
// nullability of a reference type argument in `declaring_type`'s
174
// own ancestor list (`class THING: MutableMap[string, string?]`)
175
// has no reflection-visible carrier, so `add_ancestors` needs
176
// this to recover it from the assembly's raw metadata instead.
177
// Empty when `declaring_type` implements no generic interface at
178
// all — the common case, and the check that keeps this from
179
// reading every referenced assembly's file into memory just
180
// because *some* type in it got imported — or the reader can't
181
// be built, or `declaring_type` implements nothing with a `?` in
182
// its type arguments.
183
interface_nullability_bytes(declaring_type: TYPE) -> MAP[TYPE,Collections.LIST[int]] is
184
if !_implements_generic_interface(declaring_type) then
185
return MAP[TYPE,Collections.LIST[int]]()
186
fi
187
188
let reader = _interface_metadata_reader(declaring_type)
189
190
if !reader? then
191
return MAP[TYPE,Collections.LIST[int]]()
192
fi
193
194
return INTERFACE_NULLABILITY.read_declared(reader, declaring_type, _assemblies_by_name)
195
si
196
197
// A cheap, reflection-only pre-check: does `declaring_type`
198
// implement any generic interface at all? Every case this whole
199
// mechanism exists for needs one — a non-generic interface has
200
// no type argument to be `?`. A false positive here (a generic
201
// interface with no nullable argument) still costs a metadata
202
// read; a false negative would silently drop a real `?`, so the
203
// check stays deliberately wide rather than trying to rule out
204
// value-type arguments here too.
205
_implements_generic_interface(type: TYPE) -> bool is
206
for i in type.get_interfaces() do
207
if i.is_generic_type then
208
return true
209
fi
210
od
211
212
return false
213
si
214
215
_interface_metadata_reader(type: TYPE) -> System.Reflection.Metadata.MetadataReader? is
216
let assembly_name = type.assembly.get_name().name ?? ""
217
218
let cached: System.Reflection.PortableExecutable.PEReader? mut
219
220
let pe_reader =
221
if _interface_metadata_readers.try_get_value(assembly_name, cached ref) then
222
cached
223
else
224
let loaded = _load_interface_reader(assembly_name)
225
226
_interface_metadata_readers.add(assembly_name, loaded)
227
228
loaded
229
fi
230
231
if !pe_reader? then
232
return null
233
fi
234
235
return System.Reflection.Metadata.PEReaderExtensions.get_metadata_reader(pe_reader)
236
si
237
238
// Re-reads the assembly's bytes rather than reusing what
239
// `_metadata_load_context` loaded with: those went into
240
// `load_from_byte_array` and aren't retained afterwards, and a
241
// fresh in-memory copy carries none of the open-file-handle risk
242
// that reading via a file stream would (see the comment on
243
// `load_from_byte_array` above) — the bytes are read once, into
244
// an `ImmutableArray`, and the file is never held open.
245
//
246
// Returns the `PEReader` itself, not a `MetadataReader` derived
247
// from it — see the comment on `_interface_metadata_readers`.
248
// The caller derives a fresh `MetadataReader` from the cached
249
// `PEReader` on every call, which is cheap: it just wraps the
250
// memory block the `PEReader` already read.
251
_load_interface_reader(assembly_name: string) -> System.Reflection.PortableExecutable.PEReader? is
252
let path: string mut
253
254
if !_assembly_paths.try_get_value(assembly_name, path ref) then
255
return null
256
fi
257
258
try
259
let bytes = IO.File.read_all_bytes(path)
260
let image = System.Collections.Immutable.ImmutableArray.create[ubyte](bytes)
261
262
return System.Reflection.PortableExecutable.PEReader(image)
263
catch ex: System.Exception
264
return null
265
yrt
266
si
267
268
import(assembly_names: Iterable[string]?) is
269
import(assembly_names, LIST[string]())
270
si
271
272
// `assembly_names` is the whole reference set, or empty for the
273
// one discovered from the SDK; `additional` is imported as well,
274
// whichever of the two that was.
275
import(assembly_names: Iterable[string]? mut, additional: Iterable[string]) is
276
if _is_started then
277
return
278
fi
279
280
_is_started = true
281
282
for path in additional do
283
_explicit_references.add(assembly_name_of(path))
284
od
285
286
assembly_names = REFERENCE_SET.resolve(assembly_names, additional, () => _default_imports())
287
288
let path_resolver = PathAssemblyResolver(assembly_names)
289
290
_metadata_load_context =
291
MetadataLoadContext(
292
LOADED_ASSEMBLY_RESOLVER(
293
name is
294
let assembly: Assembly mut = _
295
296
return if _assemblies_by_name.try_get_value(name, assembly ref) then assembly else null fi
297
si,
298
path_resolver),
299
null)
300
301
let to_process = LIST[(assembly: Assembly, name: string)]()
302
303
for path in assembly_names do
304
let name = assembly_name_of(path)
305
306
if _assemblies_by_name.contains_key(name) \/ blocked_assemblies.contains(name) then
307
continue
308
fi
309
310
// load_from_assembly_path memory-maps the file, holding it open and
311
// write-locked for the lifetime of the MetadataLoadContext; that blocks
312
// rebuilds of project-reference outputs from an analysis-mode session.
313
let assembly = _metadata_load_context.load_from_byte_array(IO.File.read_all_bytes(path))
314
315
to_process.add((assembly, name))
316
317
_assemblies_by_name.add(name, assembly)
318
_assembly_paths.add(name, path)
319
od
320
321
_type_name_map.start(self)
322
323
for c in _callbacks do
324
c()
325
od
326
327
for a in to_process do
328
import(a.assembly, a.name)
329
od
330
si
331
332
// Imports one more assembly into a reference set that is already
333
// loaded, for a process that outlives a single compile and is
334
// handed references as it goes. The same assembly named again is
335
// left as it is. A different assembly under a name the set already
336
// holds cannot be loaded beside it, and compiling against the one
337
// already loaded would silently use the wrong symbols, so that is an
338
// error.
339
import_additional(path: string) is
340
assert _is_started else "no reference set to add to yet"
341
342
let name = assembly_name_of(path)
343
344
if blocked_assemblies.contains(name) then
345
return
346
fi
347
348
_explicit_references.add(name)
349
350
let bytes = IO.File.read_all_bytes(path)
351
let digest = _digest(bytes)
352
353
if _assemblies_by_name.contains_key(name) then
354
if _digest_of_loaded(name) =~ digest then
355
return
356
fi
357
358
throw System.InvalidOperationException("the reference set already holds a different assembly named {name}")
359
fi
360
361
let assembly = _metadata_load_context.load_from_byte_array(bytes)
362
363
_assemblies_by_name.add(name, assembly)
364
_assembly_paths.add(name, path)
365
_digests.add(name, digest)
366
367
import(assembly, name)
368
si
369
370
// Whether `assembly` is an earlier cell of the REPL session this
371
// compile belongs to: named as one of its references, and marked
372
// as a session cell when it was compiled. A session is compiled a
373
// cell at a time but written as though it were one file, so what
374
// such an assembly declares is treated as this assembly's own:
375
// its assembly-private definitions are visible, and the one
376
// compiled here is given access to them.
377
is_session_cell(assembly: Assembly) -> bool is
378
let name = assembly.get_name().name
379
380
if !name? \/ !_explicit_references.contains(name) then
381
return false
382
fi
383
384
let known: bool mut = false
385
386
if _session_cells.try_get_value(name, known ref) then
387
return known
388
fi
389
390
let result = SESSION_CELL_MARKER.is_marked(assembly)
391
392
_session_cells.add(name, result)
393
394
return result
395
si
396
397
// Whether the assembly-private definitions of an earlier cell can
398
// be used from this compile: it is a session cell, and the runtime
399
// referenced declares the attribute that gives the access at run
400
// time. Without that attribute the cell's private names are left
401
// out, so a use of one is a compile error rather than a failure
402
// when the cell runs.
403
can_reach_privates_of(assembly: Assembly) -> bool is
404
if !is_session_cell(assembly) then
405
return false
406
fi
407
408
if !_has_access_attribute? then
409
_has_access_attribute = find_type(SESSION_CELL_ATTRIBUTES.ACCESS_ATTRIBUTE_NAME)?
410
fi
411
412
return _has_access_attribute ?? false
413
si
414
415
// The same question asked of a name rather than an assembly,
416
// for a symbol that knows which assembly it was imported from
417
// but not which Assembly object that was.
418
is_session_cell_named(name: string?) -> bool is
419
if !name? then
420
return false
421
fi
422
423
let assembly: Assembly mut = _
424
425
if !_assemblies_by_name.try_get_value(name, assembly ref) then
426
return false
427
fi
428
429
return is_session_cell(assembly)
430
si
431
432
// The names of the references that are earlier cells of the
433
// session.
434
session_cell_names: Collections.List[string] is
435
let result = LIST[string]()
436
437
for name in _explicit_references do
438
let assembly: Assembly mut = _
439
440
if _assemblies_by_name.try_get_value(name, assembly ref) /\ is_session_cell(assembly) then
441
result.add(name)
442
fi
443
od
444
445
result.sort()
446
447
return result
448
si
449
450
_digest(bytes: ubyte[]) -> string static =>
451
System.Convert.to_base64_string(System.Security.Cryptography.SHA256.hash_data(bytes))
452
453
// The digest of the assembly loaded under `name`: recorded when
454
// import_additional loaded it, and otherwise taken from the file the
455
// set loaded it from, since the reference set a process started with
456
// records no digests. Null when there is no such file to read.
457
_digest_of_loaded(name: string) -> string? is
458
let known: string mut = _
459
460
if _digests.try_get_value(name, known ref) then
461
return known
462
fi
463
464
let path: string mut = _
465
466
if !_assembly_paths.try_get_value(name, path ref) \/ !IO.File.exists(path) then
467
return null
468
fi
469
470
let digest = _digest(IO.File.read_all_bytes(path))
471
472
_digests.add(name, digest)
473
474
return digest
475
si
476
477
// The name an assembly file holds for itself, which is what anything
478
// referring to it records: the file can be called anything. Read
479
// from its metadata before it is loaded, since a load context holds
480
// one assembly of each name, so a duplicate or a blocked name has to
481
// be recognised first. Falls back to the file name for a file whose
482
// metadata cannot be read, leaving the load to report it.
483
assembly_name_of(path: string) -> string static is
484
try
485
if let name = System.Reflection.AssemblyName.get_assembly_name(path).name then
486
return name
487
fi
488
catch ex: System.Exception
489
yrt
490
491
return IO.Path.get_file_name_without_extension(path) ?? ""
492
si
493
494
// The whole reference pack, which is also what an MSBuild build
495
// passes: the SDK decides what the framework is, so a pack that
496
// gains an assembly needs no change here. Sorted so the load
497
// order does not depend on the file system.
498
_default_imports() -> Iterable[string] is
499
let sdk_path = _paths.sdk_reference_path ?? _paths.discover_sdk_reference_path()
500
501
assert sdk_path? else System.Exception("no reference assemblies supplied: pass -a <assembly> or --sdk-ref-path <directory>, or ensure the .NET SDK is installed")
502
503
_paths.sdk_reference_path = sdk_path
504
505
let result =
506
IO.Directory.get_files(sdk_path, "*.dll") |>
507
sort() |>
508
collect_list()
509
510
if !_flags.exclude_runtime_symbols then
511
result.add("{_paths.install_folder}ghul-runtime.dll")
512
fi
513
514
return result
515
si
516
517
import(assembly: Assembly, assembly_name: string?) is
518
assert assembly_name? /\ assembly_name.length > 0
519
520
let types_file = "{_paths.get_library_location(IO.Path.combine("dotnet", "refs"))}{assembly_name}.types"
521
522
let any_succeeded mut = false
523
let exported_ex: System.Exception? mut = null
524
let forwarded_ex: System.Exception? mut = null
525
526
try
527
for type in assembly.get_types() do
528
import_type(type, assembly_name)
529
530
any_succeeded = true
531
od
532
catch ex: System.Exception
533
exported_ex = exported_ex
534
yrt
535
536
try
537
for type in assembly.get_forwarded_types() do
538
import_type(type, assembly_name)
539
540
any_succeeded = true
541
od
542
catch ex: System.Exception
543
forwarded_ex = ex
544
yrt
545
546
if !any_succeeded then
547
Std.error.write_line("warning: couldn't enumerate any types in {assembly}")
548
fi
549
si
550
551
// Nested `family` and `famorassem` types stay reachable, matching the
552
// carve-out the method import makes for protected members. An enclosing
553
// type that is out of reach puts everything nested inside it out of
554
// reach too.
555
_is_externally_reachable(type: TYPE) -> bool is
556
// An earlier cell of the session is this assembly's own, all
557
// but what the compiler generated for it.
558
if can_reach_privates_of(type.assembly) then
559
return !type.name.contains('$') \/ type.is_public
560
fi
561
562
let current: TYPE? mut = type
563
564
while current? do
565
if current.is_nested then
566
if !(current.is_nested_public \/ current.is_nested_family \/ current.is_nested_fam_o_r_assem) then
567
return false
568
fi
569
elif !current.is_public then
570
return false
571
fi
572
573
current = current.declaring_type
574
od
575
576
return true
577
si
578
579
import_type(type: TYPE, assembly_name: string?) is
580
assert assembly_name? else "attempting to import type {type} with no assembly name supplied"
581
582
if !_is_externally_reachable(type) then
583
return
584
fi
585
586
try
587
let existing = _type_name_map.get_type_details(type)
588
589
if existing? then
590
existing.merge_assembly_reference(type, assembly_name)
591
592
return
593
elif !type.`namespace? \/ !type.full_name? then
594
return
595
fi
596
597
// A ghūl `union` emits each variant as a sibling class
598
// whose .NET `Namespace` is the union's full name. Left
599
// alone, the reflection loader would register that
600
// string as an actual namespace and clash with the
601
// union class itself. The compiler tags variants with
602
// `[Ghul.Internal.VARIANT_ATTRIBUTE]`; queue them under
603
// the union's full name so symbol_factory can
604
// materialize each one as a member of the union when
605
// the union's symbol is created.
606
if has_attribute(type, "Ghul.Internal.VARIANT_ATTRIBUTE") /\ type.`namespace? then
607
_type_details_lookup.register_variant(type.`namespace!, type)
608
return
609
fi
610
611
let namespace_details = _type_name_map.get_namespace_details(type)
612
let namespace_name: string mut
613
614
if namespace_details? then
615
namespace_name = namespace_details.ghul_name
616
else
617
namespace_name = type.`namespace!
618
fi
619
620
// The intrinsic declarations are made in a staging
621
// namespace - `Ghul.Internal` on assemblies built before
622
// `Ghul.Intrinsics.Staging` existed - so that assembly can
623
// declare them and still be compiled by a compiler that
624
// reflects them: the declaration and the registration it
625
// produces never occupy the same namespace. The language
626
// expects to find them in `Ghul.Intrinsics`, the namespace
627
// every compilation imports as its ambient set.
628
let is_globals_carrier =
629
GLOBALS_CARRIER.is_carrier(type.name, has_attribute(type, GLOBALS_CARRIER.attribute_name))
630
631
if
632
(namespace_name =~ "Ghul.Intrinsics.Staging" \/ namespace_name =~ "Ghul.Internal") /\
633
(is_globals_carrier \/ has_attribute(type, "Ghul.Internal.INTRINSIC_ATTRIBUTE"))
634
then
635
namespace_name = "Ghul.Intrinsics"
636
fi
637
638
// Build the ghūl-visible identifier by stripping the `\`N`
639
// generic-argument-count suffix that .NET reflection exposes
640
// for generic-type definitions (`Foo<T>` arrives as `Foo\`1`).
641
// For a nested type the suffix sits on each enclosing segment
642
// (`Dictionary\`2+ValueCollection`), so the strip is applied
643
// per `+`-separated segment before the segments are joined with
644
// `_`. Only the ghūl-visible identifier is cleaned up; IL
645
// emission and reflection load still see the .NET full_name
646
// with the suffixes.
647
let type_name mut = _strip_arity_suffix(type.name)
648
649
if type.is_nested then
650
let parts = type.full_name!.split(['.'])
651
let leaf = parts[parts.count-1]
652
653
let buffer = System.Text.StringBuilder()
654
let seen_any mut = false
655
656
for segment in leaf.split(['+']) do
657
if seen_any then
658
buffer.append('_')
659
fi
660
661
buffer.append(_strip_arity_suffix(segment))
662
seen_any = true
663
od
664
665
type_name = buffer.to_string()
666
fi
667
668
let type_details = TYPE_DETAILS(type, namespace_name, type_name, null, assembly_name)
669
670
type_details.is_globals_carrier = is_globals_carrier
671
type_details.is_compiler_generated =
672
has_attribute(type, "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
673
674
type_details.async_builder_type = _read_async_method_builder(type)
675
676
if type.is_enum then
677
type_details.has_flags_attribute = has_attribute(type, "System.FlagsAttribute")
678
fi
679
680
assert type_details.assembly_name? /\ type_details.assembly_name.length > 0 else "invalid assmbly name before add type {type_details}"
681
682
_type_details_lookup.add_type(type_details)
683
catch ex: System.Exception
684
Std.error.write_line("failed to import: {type} exception: {ex.to_string().replace('\n', ' ')}")
685
yrt
686
si
687
688
// The System.Type argument of the type.s AsyncMethodBuilderAttribute,
689
// or null when it carries none. The attribute names the builder an
690
// async function returning this type drives; its argument is an
691
// open generic type definition when the task-like is generic.
692
_read_async_method_builder(type: TYPE) -> TYPE? is
693
try
694
for attr in type.get_custom_attributes_data() do
695
if attr.attribute_type.full_name =~ "System.Runtime.CompilerServices.AsyncMethodBuilderAttribute" then
696
for arg in attr.constructor_arguments do
697
if let builder: TYPE = arg.value then
698
return builder
699
fi
700
od
701
fi
702
od
703
catch ex: System.Exception
704
// Mirrors has_attribute: an unreadable attribute set reads
705
// as no attribute.
706
yrt
707
708
return null
709
si
710
711
has_attribute(type: TYPE, attribute_name: string) -> bool is
712
try
713
for attr in type.get_custom_attributes_data() do
714
let attr_type = attr.attribute_type
715
716
if attr_type.full_name =~ attribute_name then
717
return true
718
fi
719
od
720
catch ex: System.Exception
721
// Some reflected types fail at GetCustomAttributesData()
722
// (e.g. with missing referenced assemblies). Treating
723
// the absence as an absent marker is safe — the worst
724
// case is the unmarked behaviour kicking in for that type.
725
yrt
726
727
return false
728
si
729
730
_strip_arity_suffix(name: string) -> string static is
731
let backtick = name.last_index_of('`')
732
733
if backtick <= 0 \/ backtick >= name.length - 1 then
734
return name
735
fi
736
737
for i in (backtick + 1)..name.length do
738
let c = name[i]
739
740
if c < '0' \/ c > '9' then
741
return name
742
fi
743
od
744
745
return name.substring(0, backtick)
746
si
747
748
block_all_unsupported_assemblies() is
749
blocked_assemblies.add("netstandard")
750
blocked_assemblies.add("mscorlib")
751
blocked_assemblies.add("WindowsBase")
752
blocked_assemblies.add("System.Configuration")
753
blocked_assemblies.add("System.Core")
754
blocked_assemblies.add("System.Data")
755
blocked_assemblies.add("System")
756
blocked_assemblies.add("System.Drawing")
757
blocked_assemblies.add("System.Net")
758
blocked_assemblies.add("System.Private.CoreLib")
759
blocked_assemblies.add("System.Security")
760
blocked_assemblies.add("System.ServiceModel.Web")
761
blocked_assemblies.add("System.ServiceProcess")
762
blocked_assemblies.add("System.Transactions")
763
blocked_assemblies.add("System.Configuration.ConfigurationManager")
764
blocked_assemblies.add("System.Runtime.Serialization")
765
si
766
si
767
si