Add support for C#, F# and Visual Basic (#3168)

This commit is contained in:
Steve
2022-01-30 00:16:27 +09:00
committed by GitHub
parent 92ce512996
commit 0a3aadbe80
12 changed files with 323 additions and 2 deletions

5
.github/labeler.yml vendored
View File

@@ -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

View File

@@ -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)

View 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

View 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

View 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

View File

@@ -0,0 +1,4 @@
class Program
{
static int Square(int num) => num * num;
}

View File

@@ -0,0 +1,3 @@
module Program
let square num = num * num

5
examples/vb/default.vb Normal file
View File

@@ -0,0 +1,5 @@
Module Program
Function Square(num As Integer) As Integer
Return num * num
End Function
End Module

View File

@@ -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
View 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'; }
}

View File

@@ -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) => {

View File

@@ -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({