mirror of
https://github.com/compiler-explorer/compiler-explorer.git
synced 2025-12-27 09:23:52 -05:00
Add support for C#, F# and Visual Basic (#3168)
This commit is contained in:
5
.github/labeler.yml
vendored
5
.github/labeler.yml
vendored
@@ -42,6 +42,11 @@ lang-d:
|
||||
- lib/compilers/dmd.js
|
||||
- lib/compilers/ldc.js
|
||||
- etc/config/d.*.properties
|
||||
lang-dotnet:
|
||||
- lib/compilers/dotnet.js
|
||||
- etc/config/csharp.*.properties
|
||||
- etc/config/fsharp.*.properties
|
||||
- etc/config/vb.*.properties
|
||||
lang-fortran:
|
||||
- lib/compilers/fortran.js
|
||||
- etc/config/fortran.*.properties
|
||||
|
||||
@@ -100,11 +100,14 @@ From oldest to newest contributor, we would like to thank:
|
||||
- [Dan Okken](https://github.com/okkenator)
|
||||
- [Shivam Gupta](https://github.com/xgupta)
|
||||
- [Tamir Bahar](https://github.com/tmr232)
|
||||
- [Jason Meisel](https://github.com/jasonmeisel)
|
||||
- [Nabeel Omer](https://github.com/nabeelomer)
|
||||
- [Daniel Below](https://github.com/DanielBelow)
|
||||
- [Oleksandr Muliar](https://github.com/msdinit)
|
||||
- [Quinton Miller](https://github.com/HertzDevil)
|
||||
- [Kevin Adler](https://github.com/kadler)
|
||||
- [Björn Gustavsson](https://github.com/bjorng)
|
||||
- [Steven He](https://github.com/hez2010)
|
||||
- [Gregory Anders](https://github.com/gpanders)
|
||||
- [Marcus Geelnard](https://github.com/mbitsnbites)
|
||||
- [Haneef Mubarak](https://github.com/haneefmubarak)
|
||||
|
||||
25
etc/config/csharp.defaults.properties
Normal file
25
etc/config/csharp.defaults.properties
Normal file
@@ -0,0 +1,25 @@
|
||||
compilers=&csharp
|
||||
supportsBinary=false
|
||||
needsMulti=false
|
||||
compilerType=csharp
|
||||
defaultCompiler=dotnet6csharp:dotnettrunkcsharp
|
||||
|
||||
group.csharp.compilers=dotnet6csharp:dotnettrunkcsharp
|
||||
|
||||
compiler.dotnet6csharp.exe=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnet6csharp.name=.NET 6.0.101
|
||||
compiler.dotnet6csharp.clrDir=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnet6csharp.runtimeId=linux-x64
|
||||
compiler.dotnet6csharp.targetFramework=net6.0
|
||||
compiler.dotnet6csharp.buildConfig=Release
|
||||
compiler.dotnet6csharp.additionalSources=
|
||||
compiler.dotnet6csharp.langVersion=latest
|
||||
|
||||
compiler.dotnettrunkcsharp.exe=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnettrunkcsharp.name=.NET Trunk
|
||||
compiler.dotnettrunkcsharp.clrDir=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnettrunkcsharp.runtimeId=linux-x64
|
||||
compiler.dotnettrunkcsharp.targetFramework=net6.0
|
||||
compiler.dotnettrunkcsharp.buildConfig=Release
|
||||
compiler.dotnettrunkcsharp.additionalSources=
|
||||
compiler.dotnettrunkcsharp.langVersion=preview
|
||||
25
etc/config/fsharp.defaults.properties
Normal file
25
etc/config/fsharp.defaults.properties
Normal file
@@ -0,0 +1,25 @@
|
||||
compilers=&fsharp
|
||||
supportsBinary=false
|
||||
needsMulti=false
|
||||
compilerType=fsharp
|
||||
defaultCompiler=dotnet6fsharp
|
||||
|
||||
group.fsharp.compilers=dotnet6fsharp:dotnettrunkfsharp
|
||||
|
||||
compiler.dotnet6fsharp.exe=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnet6fsharp.name=.NET 6.0.101
|
||||
compiler.dotnet6fsharp.clrDir=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnet6fsharp.runtimeId=linux-x64
|
||||
compiler.dotnet6fsharp.targetFramework=net6.0
|
||||
compiler.dotnet6fsharp.buildConfig=Release
|
||||
compiler.dotnet6fsharp.additionalSources=
|
||||
compiler.dotnet6fsharp.langVersion=latest
|
||||
|
||||
compiler.dotnettrunkfsharp.exe=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnettrunkfsharp.name=.NET Trunk
|
||||
compiler.dotnettrunkfsharp.clrDir=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnettrunkfsharp.runtimeId=linux-x64
|
||||
compiler.dotnettrunkfsharp.targetFramework=net6.0
|
||||
compiler.dotnettrunkfsharp.buildConfig=Release
|
||||
compiler.dotnettrunkfsharp.additionalSources=
|
||||
compiler.dotnettrunkfsharp.langVersion=latest
|
||||
25
etc/config/vb.defaults.properties
Normal file
25
etc/config/vb.defaults.properties
Normal file
@@ -0,0 +1,25 @@
|
||||
compilers=&vb
|
||||
supportsBinary=false
|
||||
needsMulti=false
|
||||
compilerType=vb
|
||||
defaultCompiler=dotnet6vb
|
||||
|
||||
group.vb.compilers=dotnet6vb:dotnettrunkvb
|
||||
|
||||
compiler.dotnet6vb.exe=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnet6vb.name=.NET 6.0.101
|
||||
compiler.dotnet6vb.clrDir=/opt/compiler-explorer/dotnet-v6.0.1/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnet6vb.runtimeId=linux-x64
|
||||
compiler.dotnet6vb.targetFramework=net6.0
|
||||
compiler.dotnet6vb.buildConfig=Release
|
||||
compiler.dotnet6vb.additionalSources=
|
||||
compiler.dotnet6vb.langVersion=latest
|
||||
|
||||
compiler.dotnettrunkvb.exe=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked/.dotnet/dotnet
|
||||
compiler.dotnettrunkvb.name=.NET Trunk
|
||||
compiler.dotnettrunkvb.clrDir=/opt/compiler-explorer/dotnet-trunk/bin/coreclr/Linux.x64.Checked
|
||||
compiler.dotnettrunkvb.runtimeId=linux-x64
|
||||
compiler.dotnettrunkvb.targetFramework=net6.0
|
||||
compiler.dotnettrunkvb.buildConfig=Release
|
||||
compiler.dotnettrunkvb.additionalSources=
|
||||
compiler.dotnettrunkvb.langVersion=latest
|
||||
4
examples/csharp/default.cs
Normal file
4
examples/csharp/default.cs
Normal file
@@ -0,0 +1,4 @@
|
||||
class Program
|
||||
{
|
||||
static int Square(int num) => num * num;
|
||||
}
|
||||
3
examples/fsharp/default.fs
Normal file
3
examples/fsharp/default.fs
Normal file
@@ -0,0 +1,3 @@
|
||||
module Program
|
||||
|
||||
let square num = num * num
|
||||
5
examples/vb/default.vb
Normal file
5
examples/vb/default.vb
Normal file
@@ -0,0 +1,5 @@
|
||||
Module Program
|
||||
Function Square(num As Integer) As Integer
|
||||
Return num * num
|
||||
End Function
|
||||
End Module
|
||||
@@ -36,11 +36,13 @@ export { CprocCompiler } from './cproc';
|
||||
export { CrystalCompiler } from './crystal';
|
||||
export { DefaultCompiler } from './default';
|
||||
export { DMDCompiler } from './dmd';
|
||||
export { CSharpCompiler } from './dotnet';
|
||||
export { EllccCompiler } from './ellcc';
|
||||
export { ErlangCompiler } from './erlang';
|
||||
export { EWARMCompiler } from './ewarm';
|
||||
export { EWAVRCompiler } from './ewavr';
|
||||
export { FakeCompiler } from './fake-for-test';
|
||||
export { FSharpCompiler } from './dotnet';
|
||||
export { FortranCompiler } from './fortran';
|
||||
export { GCCCompiler } from './gcc';
|
||||
export { GCCRSCompiler } from './gccrs';
|
||||
@@ -71,12 +73,13 @@ export { SdccCompiler } from './sdcc';
|
||||
export { SPIRVCompiler } from './spirv';
|
||||
export { SwiftCompiler } from './swift';
|
||||
export { TenDRACompiler } from './tendra';
|
||||
export { TinyCCompiler } from './tinyc';
|
||||
export { VBCompiler } from './dotnet';
|
||||
export { Win32Compiler } from './win32';
|
||||
export { Win32VcCompiler } from './win32-vc';
|
||||
export { Win32Vc6Compiler } from './win32-vc6';
|
||||
export { WineVcCompiler } from './wine-vc';
|
||||
export { WslVcCompiler } from './wsl-vc';
|
||||
export { ZigCompiler } from './zig';
|
||||
export { TinyCCompiler } from './tinyc';
|
||||
export { ZigCC } from './zigcc';
|
||||
export { ZigCXX } from './zigcxx';
|
||||
|
||||
204
lib/compilers/dotnet.js
Normal file
204
lib/compilers/dotnet.js
Normal file
@@ -0,0 +1,204 @@
|
||||
// Copyright (c) 2021, Compiler Explorer Authors
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
|
||||
import { BaseCompiler } from '../base-compiler';
|
||||
|
||||
class DotNetCompiler extends BaseCompiler {
|
||||
|
||||
get rID() { return this.compilerProps(`compiler.${this.compiler.id}.runtimeId`); }
|
||||
get targetFramework() { return this.compilerProps(`compiler.${this.compiler.id}.targetFramework`); }
|
||||
get buildConfig() { return this.compilerProps(`compiler.${this.compiler.id}.buildConfig`); }
|
||||
|
||||
get compilerOptions() {
|
||||
return ['publish', '-c', this.buildConfig, '--self-contained', '--runtime', this.rID, '-v', 'q', '--nologo'];
|
||||
}
|
||||
|
||||
get configurableOptions() {
|
||||
return ['--targetos', '--targetarch', '--instruction-set', '--singlemethodtypename', '--singlemethodname',
|
||||
'--singlemethodindex', '--singlemethodgenericarg', '--codegenopt', '--codegen-options'];
|
||||
}
|
||||
|
||||
get configurableSwitches() {
|
||||
return ['-O', '--optimize', '--Od', '--optimize-disabled', '--Os', '--optimize-space', '--Ot',
|
||||
'--optimize-time'];
|
||||
}
|
||||
|
||||
async runCompiler(compiler, options, inputFileName, execOptions) {
|
||||
if (!execOptions) {
|
||||
execOptions = this.getDefaultExecOptions();
|
||||
}
|
||||
|
||||
const programDir = path.dirname(inputFileName);
|
||||
const sourceFile = path.basename(inputFileName);
|
||||
const clrBuildDir = this.compilerProps(`compiler.${this.compiler.id}.clrDir`);
|
||||
|
||||
const projectFilePath = path.join(programDir, `CompilerExplorer${this.lang.extensions[0]}proj`);
|
||||
const crossgen2Path = path.join(clrBuildDir, 'crossgen2', 'crossgen2.dll');
|
||||
|
||||
const programPublishPath = path.join(
|
||||
programDir,
|
||||
'bin',
|
||||
this.buildConfig,
|
||||
this.targetFramework,
|
||||
this.rID,
|
||||
'publish',
|
||||
);
|
||||
|
||||
const programDllPath = path.join(programPublishPath, 'CompilerExplorer.dll');
|
||||
const additionalSources = this.compilerProps(`compiler.${this.compiler.id}.additionalSources`);
|
||||
const langVersion = this.compilerProps(`compiler.${this.compiler.id}.langVersion`);
|
||||
const projectFileContent =
|
||||
`<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>${this.targetFramework}</TargetFramework>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<Nullable>enable</Nullable>
|
||||
<AssemblyName>CompilerExplorer</AssemblyName>
|
||||
<LangVersion>${langVersion}</LangVersion>
|
||||
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
|
||||
<EnablePreviewFeatures>${langVersion === 'preview' ? 'true' : 'false'}</EnablePreviewFeatures>
|
||||
<RestoreAdditionalProjectSources>
|
||||
https://api.nuget.org/v3/index.json;${additionalSources ? additionalSources : ''}
|
||||
</RestoreAdditionalProjectSources>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="${sourceFile}" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
`;
|
||||
|
||||
execOptions.customCwd = programDir;
|
||||
await fs.writeFile(projectFilePath, projectFileContent);
|
||||
|
||||
let crossgen2Options = [];
|
||||
const configurableOptions = this.configurableOptions;
|
||||
|
||||
for (const configurableOption of configurableOptions) {
|
||||
const optionIndex = options.indexOf(configurableOption);
|
||||
if (optionIndex === -1 || optionIndex === options.length - 1) {
|
||||
continue;
|
||||
}
|
||||
crossgen2Options = crossgen2Options.concat([options[optionIndex], options[optionIndex + 1]]);
|
||||
}
|
||||
|
||||
const configurableSwitches = this.configurableSwitches;
|
||||
for (const configurableSwitch of configurableSwitches) {
|
||||
const switchIndex = options.indexOf(configurableSwitch);
|
||||
if (switchIndex === -1) {
|
||||
continue;
|
||||
}
|
||||
crossgen2Options.push(options[switchIndex]);
|
||||
}
|
||||
|
||||
const compilerResult = await super.runCompiler(compiler, this.compilerOptions, inputFileName, execOptions);
|
||||
|
||||
if (compilerResult.code !== 0) {
|
||||
return compilerResult;
|
||||
}
|
||||
|
||||
const crossgen2Result = await this.runCrossgen2(
|
||||
compiler,
|
||||
execOptions,
|
||||
crossgen2Path,
|
||||
programPublishPath,
|
||||
programDllPath,
|
||||
crossgen2Options,
|
||||
this.getOutputFilename(programDir, ''),
|
||||
);
|
||||
|
||||
if (crossgen2Result.code !== 0) {
|
||||
return crossgen2Result;
|
||||
}
|
||||
|
||||
return compilerResult;
|
||||
}
|
||||
|
||||
optionsForFilter() {
|
||||
return this.compilerOptions;
|
||||
}
|
||||
|
||||
getOutputFilename(dirPath) {
|
||||
return path.join(dirPath, `output.s`);
|
||||
}
|
||||
|
||||
cleanAsm(stdout) {
|
||||
let cleanedAsm = '';
|
||||
|
||||
for (const line of stdout) {
|
||||
if (line.text.startsWith('; Assembly listing for method')) {
|
||||
// ; Assembly listing for method ConsoleApplication.Program:Main(System.String[])
|
||||
// ^ This character is the 31st character in this string.
|
||||
// `substring` removes the first 30 characters from it and uses the rest as a label.
|
||||
cleanedAsm = cleanedAsm.concat(line.text.substring(30) + ':\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.text.startsWith('Emitting R2R PE file')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (line.text.startsWith(';') && !line.text.startsWith('; Emitting')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cleanedAsm = cleanedAsm.concat(line.text + '\n');
|
||||
}
|
||||
|
||||
return cleanedAsm;
|
||||
}
|
||||
|
||||
async runCrossgen2(compiler, execOptions, crossgen2Path, publishPath, dllPath, options, outputPath) {
|
||||
const crossgen2Options = [
|
||||
crossgen2Path, '-r', path.join(publishPath, '*'), dllPath, '-o', 'CompilerExplorer.r2r.dll',
|
||||
'--codegenopt', 'NgenDisasm=*', '--codegenopt', 'JitDiffableDasm=1', '--parallelism', '1',
|
||||
].concat(options);
|
||||
|
||||
const result = await this.exec(compiler, crossgen2Options, execOptions);
|
||||
result.inputFilename = dllPath;
|
||||
const transformedInput = result.filenameTransform(dllPath);
|
||||
this.parseCompilationOutput(result, transformedInput);
|
||||
|
||||
await fs.writeFile(
|
||||
outputPath,
|
||||
this.cleanAsm(result.stdout),
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export class CSharpCompiler extends DotNetCompiler {
|
||||
static get key() { return 'csharp'; }
|
||||
}
|
||||
|
||||
export class FSharpCompiler extends DotNetCompiler {
|
||||
static get key() { return 'fsharp'; }
|
||||
}
|
||||
|
||||
export class VBCompiler extends DotNetCompiler {
|
||||
static get key() { return 'vb'; }
|
||||
}
|
||||
@@ -258,6 +258,24 @@ export const languages = {
|
||||
extensions: ['.txt'],
|
||||
alias: [],
|
||||
},
|
||||
csharp: {
|
||||
name: 'C#',
|
||||
monaco: 'csharp',
|
||||
extensions: ['.cs'],
|
||||
alias: [],
|
||||
},
|
||||
fsharp: {
|
||||
name: 'F#',
|
||||
monaco: 'fsharp',
|
||||
extensions: ['.fs'],
|
||||
alias: [],
|
||||
},
|
||||
vb: {
|
||||
name: 'Visual Basic',
|
||||
monaco: 'vb',
|
||||
extensions: ['.vb'],
|
||||
alias: [],
|
||||
},
|
||||
};
|
||||
|
||||
_.each(languages, (lang, key) => {
|
||||
|
||||
@@ -47,7 +47,8 @@ const staticPath = path.join(distPath, 'static');
|
||||
const webjackJsHack = '.v5.';
|
||||
const plugins = [
|
||||
new MonacoEditorWebpackPlugin({
|
||||
languages: ['cpp', 'go', 'pascal', 'python', 'rust', 'swift', 'java', 'kotlin', 'scala', 'ruby'],
|
||||
languages: ['cpp', 'go', 'pascal', 'python', 'rust', 'swift', 'java',
|
||||
'kotlin', 'scala', 'ruby', 'csharp', 'fsharp', 'vb'],
|
||||
filename: isDev ? '[name].worker.js' : `[name]${webjackJsHack}worker.[contenthash].js`,
|
||||
}),
|
||||
new CopyWebpackPlugin({
|
||||
|
||||
Reference in New Issue
Block a user