diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 2accfcebf6b..28c138d93c9 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -353,6 +353,16 @@ stages: - script: .\tests\EndToEndBuildTests\EndToEndBuildTests.cmd -c Release displayName: End to end build tests + # Determinism + - job: Determinism + pool: + vmImage: windows-latest + steps: + - checkout: self + clean: true + - script: .\eng\test-determinism.cmd -configuration Debug + displayName: Determinism tests + # Up-to-date - disabled due to it being flaky #- job: UpToDate_Windows # pool: diff --git a/eng/pipelines/checkout-windows-task.yml b/eng/pipelines/checkout-windows-task.yml new file mode 100644 index 00000000000..76a97eb381e --- /dev/null +++ b/eng/pipelines/checkout-windows-task.yml @@ -0,0 +1,11 @@ +# Shallow checkout sources on Windows +steps: + - checkout: none + + - script: | + @echo on + git init + git remote add origin "$(Build.Repository.Uri)" + git fetch --progress --no-tags --depth=1 origin "$(Build.SourceVersion)" + git checkout "$(Build.SourceVersion)" + displayName: Shallow Checkout diff --git a/eng/pipelines/publish-logs.yml b/eng/pipelines/publish-logs.yml new file mode 100644 index 00000000000..79835baea3f --- /dev/null +++ b/eng/pipelines/publish-logs.yml @@ -0,0 +1,17 @@ +# Build on windows desktop +parameters: +- name: jobName + type: string + default: '' +- name: configuration + type: string + default: 'Debug' + +steps: + - task: PublishPipelineArtifact@1 + displayName: Publish Logs + inputs: + targetPath: '$(Build.SourcesDirectory)/artifacts/log/${{ parameters.configuration }}' + artifactName: '${{ parameters.jobName }} Attempt $(System.JobAttempt) Logs' + continueOnError: true + condition: not(succeeded()) diff --git a/eng/test-determinism.cmd b/eng/test-determinism.cmd new file mode 100644 index 00000000000..972f85371e6 --- /dev/null +++ b/eng/test-determinism.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -noprofile -executionPolicy RemoteSigned -file "%~dp0\test-determinism.ps1" %* diff --git a/eng/test-determinism.ps1 b/eng/test-determinism.ps1 new file mode 100644 index 00000000000..fdfb2d0ccdb --- /dev/null +++ b/eng/test-determinism.ps1 @@ -0,0 +1,370 @@ +[CmdletBinding(PositionalBinding=$false)] +param([string]$configuration = "Debug", + [string]$msbuildEngine = "vs", + [string]$altRootDrive = "q:", + [switch]$help, + [switch]$norestore, + [switch]$rebuild) + +Set-StrictMode -version 2.0 +$ErrorActionPreference = "Stop" + +function Print-Usage() { + Write-Host "Usage: test-determinism.ps1" + Write-Host " -configuration Build configuration ('Debug' or 'Release')" + Write-Host " -msbuildEngine Msbuild engine to use to run build ('dotnet', 'vs', or unspecified)." + Write-Host " -bootstrapDir Directory containing the bootstrap compiler" + Write-Host " -altRootDrive The drive we build on (via subst) for verifying pathmap implementation" +} + +if ($help) { + Print-Usage + exit 0 +} + +# List of binary names that should be skipped because they have a known issue that +# makes them non-deterministic. +$script:skipList = @() +function Run-Build([string]$rootDir, [string]$logFileName) { + + # Clean out the previous run + Write-Host "Cleaning binaries" + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + Remove-Item -Recurse (Get-BinDir $rootDir) -ErrorAction SilentlyContinue + Remove-Item -Recurse (Get-ObjDir $rootDir) -ErrorAction SilentlyContinue + $stopWatch.Stop() + Write-Host "Cleaning took $($stopWatch.Elapsed)" + + $solution = Join-Path $rootDir "VisualFSharp.sln" + + if ($logFileName -eq "") { + $logFileName = [IO.Path]::GetFileNameWithoutExtension($projectFilePath) + } + $logFileName = [IO.Path]::ChangeExtension($logFileName, ".binlog") + $logFilePath = Join-Path $LogDir $logFileName + + Stop-Processes + + Write-Host "Building $solution using $bootstrapDir" + MSBuild $toolsetBuildProj ` + /p:Projects=$solution ` + /p:Restore=true ` + /p:Build=true ` + /p:DebugDeterminism=true ` + /p:Features="debug-determinism" ` + /p:DeployExtension=false ` + /p:RepoRoot=$rootDir ` + /p:TreatWarningsAsErrors=true ` + /p:BootstrapBuildPath=$bootstrapDir ` + /p:RunAnalyzers=false ` + /p:RunAnalyzersDuringBuild=false ` + /p:RestoreUseStaticGraphEvaluation=true ` + /bl:$logFilePath + + Stop-Processes +} + +function Get-ObjDir([string]$rootDir) { + return Join-Path $rootDir "artifacts\obj" +} + +function Get-BinDir([string]$rootDir) { + return Join-Path $rootDir "artifacts\bin" +} + +# Return all of the files that need to be processed for determinism under the given +# directory. +function Get-FilesToProcess([string]$rootDir) { + $objDir = Get-ObjDir $rootDir + foreach ($item in Get-ChildItem -re -in *.dll,*.exe,*.pdb,*.sourcelink.json $objDir) { + $filePath = $item.FullName + $fileName = Split-Path -leaf $filePath + $relativeDirectory = Split-Path -parent $filePath + $relativeDirectory = $relativeDirectory.Substring($objDir.Length) + $relativeDirectory = $relativeDirectory.TrimStart("\") + + if ($skipList.Contains($fileName)) { + continue; + } + + $fileId = $filePath.Substring($objDir.Length).Replace("\", ".").TrimStart(".") + $fileHash = (Get-FileHash $filePath -algorithm MD5).Hash + + $data = @{} + $data.Hash = $fileHash + $data.Content = [IO.File]::ReadAllBytes($filePath) + $data.FileId = $fileId + $data.FileName = $fileName + $data.FilePath = $filePath + $data.RelativeDirectory = $relativeDirectory + + $keyFilePath = $filePath + ".key" + $keyFileName = Split-Path -leaf $keyFilePath + if (Test-Path $keyFilePath) { + $data.KeyFileName = $keyFileName + $data.KeyFilePath = $keyFilePath + $data.KeyFileContent = [IO.File]::ReadAllBytes($keyFilePath) + } + else { + $data.KeyFileName = "" + $data.KeyFilePath = "" + $data.KeyFileContent = $null + } + + Write-Output $data + } +} + +# This will build up the map of all of the binaries and their respective hashes. +function Record-Binaries([string]$rootDir) { + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + Write-Host "Recording file hashes" + + $map = @{ } + foreach ($fileData in Get-FilesToProcess $rootDir) { + Write-Host "`t$($fileData.FileId) = $($fileData.Hash)" + $map[$fileData.FileId] = $fileData + } + $stopWatch.Stop() + Write-Host "Recording took $($stopWatch.Elapsed)" + return $map +} + +# This is a sanity check to ensure that we're actually putting the right entries into +# the core data map. Essentially to ensure things like if we change our directory layout +# that this test fails beacuse we didn't record the binaries we intended to record. +function Test-MapContents($dataMap) { + + # Sanity check to ensure we didn't return a false positive because we failed + # to examine any binaries. + if ($dataMap.Count -lt 40) { + throw "Didn't find the expected count of binaries" + } + + # Test for some well known binaries + $list = @( + "FSharp.Core.dll", + "FSharp.Compiler.Service.dll") + + foreach ($fileName in $list) { + $found = $false + foreach ($value in $dataMap.Values) { + if ($value.FileName -eq $fileName) { + $found = $true + break; + } + } + + if (-not $found) { + throw "Did not find the expected binary $fileName" + } + } +} + +function Test-Build([string]$rootDir, $dataMap, [string]$logFileName) { + Run-Build $rootDir -logFile $logFileName + + $errorList = @() + $allGood = $true + + Write-Host "Testing the binaries" + $stopWatch = [System.Diagnostics.StopWatch]::StartNew() + foreach ($fileData in Get-FilesToProcess $rootDir) { + $fileId = $fileData.FileId + $fileName = $fileData.FileName + $filePath = $fileData.FilePath + $relativeDir = $fileData.RelativeDirectory + + if (-not $dataMap.Contains($fileId)) { + Write-Host "ERROR! Missing entry in map $fileId->$filePath" + $allGood = $false + continue + } + + $oldfileData = $datamap[$fileId] + if ($fileData.Hash -ne $oldFileData.Hash) { + Write-Host "`tERROR! $relativeDir\$fileName contents don't match" + $allGood = $false + $errorList += $fileName + + $errorCurrentDirLeft = Join-Path $errorDirLeft $relativeDir + Create-Directory $errorCurrentDirLeft + $errorCurrentDirRight = Join-Path $errorDirRight $relativeDir + Create-Directory $errorCurrentDirRight + + # Save out the original and baseline for investigation + [IO.File]::WriteAllBytes((Join-Path $errorCurrentDirLeft $fileName), $oldFileData.Content) + Copy-Item $filePath (Join-Path $errorCurrentDirRight $fileName) + + # Copy the key files if available too + $keyFileName = $oldFileData.KeyFileName + if ($keyFileName -ne "") { + [IO.File]::WriteAllBytes((Join-Path $errorCurrentDirLeft $keyFileName), $oldFileData.KeyFileContent) + Copy-Item $fileData.KeyFilePath (Join-Path $errorCurrentDirRight $keyFileName) + } + + continue + } + + Write-Host "`tVerified $relativeDir\$fileName" + } + + if (-not $allGood) { + Write-Host "Determinism failed for the following binaries:" + foreach ($name in $errorList) { + Write-Host "`t$name" + } + + Write-Host "Archiving failure information" + $zipFile = Join-Path $LogDir "determinism.zip" + Add-Type -Assembly "System.IO.Compression.FileSystem"; + [System.IO.Compression.ZipFile]::CreateFromDirectory($script:errorDir, $zipFile, "Fastest", $true); + + Write-Host "Please send $zipFile to compiler team for analysis" + exit 1 + } + + $stopWatch.Stop() + Write-Host "Testing took $($stopWatch.Elapsed)" +} + +function Run-Test() { + # Run the initial build so that we can populate the maps + Run-Build $RepoRoot -logFileName "Initial" -useBootstrap + $dataMap = Record-Binaries $RepoRoot + Test-MapContents $dataMap + + # Run a test against the source in the same directory location + Test-Build -rootDir $RepoRoot -dataMap $dataMap -logFileName "test1" + + # Run another build in a different source location and verify that path mapping + # allows the build to be identical. To do this we'll copy the entire source + # tree under the artifacts\q directory and run a build from there. + # Write-Host "Building in a different directory" + # Exec-Command "subst" "$altRootDrive $(Split-Path -parent $RepoRoot)" + # try { + # $altRootDir = Join-Path "$($altRootDrive)\" (Split-Path -leaf $RepoRoot) + # Test-Build -rootDir $altRootDir -dataMap $dataMap -logFileName "test2" + # } + # finally { + # Exec-Command "subst" "$altRootDrive /d" + # } +} + +function Test-IsAdmin { + ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator") +} + +function TryDownloadDotnetFrameworkSdk() { + # If we are not running as admin user, don't bother grabbing ndp sdk -- since we don't need sn.exe + $isAdmin = Test-IsAdmin + Write-Host "TryDownloadDotnetFrameworkSdk -- Test-IsAdmin = '$isAdmin'" + if ($isAdmin -eq $true) + { + # Get program files(x86) location + if (${env:ProgramFiles(x86)} -eq $null) { + $programFiles = $env:ProgramFiles + } + else { + $programFiles = ${env:ProgramFiles(x86)} + } + + # Get windowsSDK location for x86 + $windowsSDK_ExecutablePath_x86 = $env:WindowsSDK_ExecutablePath_x86 + $newWindowsSDK_ExecutablePath_x86 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools" + + if ($windowsSDK_ExecutablePath_x86 -eq $null) { + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + else { + $snPathX86 = Join-Path $windowsSDK_ExecutablePath_x86 "sn.exe" + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX86 = Join-Path $newWindowsSDK_ExecutablePath_x86 "sn.exe" + } + } + + $windowsSDK_ExecutablePath_x64 = $env:WindowsSDK_ExecutablePath_x64 + $newWindowsSDK_ExecutablePath_x64 = Join-Path "$programFiles" "Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64" + + if ($windowsSDK_ExecutablePath_x64 -eq $null) { + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + else { + $snPathX64 = Join-Path $windowsSDK_ExecutablePath_x64 "sn.exe" + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -ne $true) { + $windowsSDK_ExecutablePath_x86 = null + $snPathX64 = Join-Path $newWindowsSDK_ExecutablePath_x64 "sn.exe" + } + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + Write-Host "pre-dl snPathX86Exists : $snPathX86Exists - '$snPathX86'" + if ($snPathX86Exists -ne $true) { + DownloadDotnetFrameworkSdk + } + + $snPathX86Exists = Test-Path $snPathX86 -PathType Leaf + if ($snPathX86Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x86 -ne $newWindowsSDK_ExecutablePath_x86) { + $windowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + # x86 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x86=$WindowsSDK_ExecutablePath_x86" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x86","$newWindowsSDK_ExecutablePath_x86",[System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x86 = $newWindowsSDK_ExecutablePath_x86 + } + } + + # Also update environment variable for x64 + $snPathX64Exists = Test-Path $snPathX64 -PathType Leaf + if ($snPathX64Exists -eq $true) { + if ($windowsSDK_ExecutablePath_x64 -ne $newWindowsSDK_ExecutablePath_x64) { + $windowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + # x64 environment variable + Write-Host "set WindowsSDK_ExecutablePath_x64=$WindowsSDK_ExecutablePath_x64" + [System.Environment]::SetEnvironmentVariable("WindowsSDK_ExecutablePath_x64","$newWindowsSDK_ExecutablePath_x64",[System.EnvironmentVariableTarget]::Machine) + $env:WindowsSDK_ExecutablePath_x64 = $newWindowsSDK_ExecutablePath_x64 + } + } + } +} + +try { + . (Join-Path $PSScriptRoot "build-utils.ps1") + + # Create all of the logging directories + $errorDir = Join-Path $LogDir "DeterminismFailures" + $errorDirLeft = Join-Path $errorDir "Left" + $errorDirRight = Join-Path $errorDir "Right" + + Create-Directory $LogDir + Create-Directory $errorDirLeft + Create-Directory $errorDirRight + + $ci = $true + $runAnalyzers = $false + $binaryLog = $true + $officialBuildId = "" + $nodeReuse = $false + $properties = @() + $script:bootstrap = $true + $script:bootstrapConfiguration = "Proto" + + $buildTool = InitializeBuildTool + $toolsetBuildProj = InitializeToolset + TryDownloadDotnetFrameworkSdk + + $bootstrapDir = Make-BootstrapBuild + + Run-Test + exit 0 +} +catch { + Write-Host $_ + Write-Host $_.Exception + Write-Host $_.ScriptStackTrace + exit 1 +} + diff --git a/src/fsharp/CompilerConfig.fs b/src/fsharp/CompilerConfig.fs index 9b0b8731ab9..b829795b394 100644 --- a/src/fsharp/CompilerConfig.fs +++ b/src/fsharp/CompilerConfig.fs @@ -325,6 +325,14 @@ type PackageManagerLine = static member StripDependencyManagerKey (packageKey: string) (line: string): string = line.Substring(packageKey.Length + 1).Trim() +[] +type MetadataAssemblyGeneration = + | None + | ReferenceOut of outputPath: string + | ReferenceOnly + | MetadataOnly + | TestSigOfImpl + [] type TcConfigBuilder = { @@ -437,6 +445,7 @@ type TcConfigBuilder = mutable emitTailcalls: bool mutable deterministic: bool mutable concurrentBuild: bool + mutable emitMetadataAssembly: MetadataAssemblyGeneration mutable preferredUiLang: string option mutable lcid: int option mutable productNameForBannerText: string @@ -642,6 +651,7 @@ type TcConfigBuilder = emitTailcalls = true deterministic = false concurrentBuild = true + emitMetadataAssembly = MetadataAssemblyGeneration.None preferredUiLang = None lcid = None productNameForBannerText = FSharpProductName @@ -1022,6 +1032,7 @@ type TcConfig private (data: TcConfigBuilder, validate: bool) = member x.emitTailcalls = data.emitTailcalls member x.deterministic = data.deterministic member x.concurrentBuild = data.concurrentBuild + member x.emitMetadataAssembly = data.emitMetadataAssembly member x.pathMap = data.pathMap member x.langVersion = data.langVersion member x.preferredUiLang = data.preferredUiLang diff --git a/src/fsharp/CompilerConfig.fsi b/src/fsharp/CompilerConfig.fsi index 4a80fea97d1..2c00bf9f06d 100644 --- a/src/fsharp/CompilerConfig.fsi +++ b/src/fsharp/CompilerConfig.fsi @@ -143,6 +143,22 @@ type PackageManagerLine = static member SetLinesAsProcessed: string -> Map -> Map static member StripDependencyManagerKey: string -> string -> string +[] +type MetadataAssemblyGeneration = + | None + /// Includes F# signature and optimization metadata as resources in the emitting assembly. + /// Implementation assembly will still be emitted normally, but will emit the reference assembly with the specified output path. + | ReferenceOut of outputPath: string + /// Includes F# signature and optimization metadata as resources in the emitting assembly. + /// Only emits the assembly as a reference assembly. + | ReferenceOnly + /// Do not include F# optimization metadata as a resource in the emitting assembly. + /// Means we do not necessarily need to type-check implementation files if they have a backing signature file. + /// Instead, a dummy implementation file will be created. + | MetadataOnly + /// This is only for used for testing. + | TestSigOfImpl + [] type TcConfigBuilder = { mutable primaryAssembly: PrimaryAssembly @@ -249,6 +265,7 @@ type TcConfigBuilder = mutable emitTailcalls: bool mutable deterministic: bool mutable concurrentBuild: bool + mutable emitMetadataAssembly: MetadataAssemblyGeneration mutable preferredUiLang: string option mutable lcid : int option mutable productNameForBannerText: string @@ -439,6 +456,7 @@ type TcConfig = member emitTailcalls: bool member deterministic: bool member concurrentBuild: bool + member emitMetadataAssembly: MetadataAssemblyGeneration member pathMap: PathMap member preferredUiLang: string option member optsOn : bool diff --git a/src/fsharp/CompilerOptions.fs b/src/fsharp/CompilerOptions.fs index 253e22747c8..c11e3d56e02 100644 --- a/src/fsharp/CompilerOptions.fs +++ b/src/fsharp/CompilerOptions.fs @@ -406,6 +406,23 @@ let SetTailcallSwitch (tcConfigB: TcConfigBuilder) switch = let SetDeterministicSwitch (tcConfigB: TcConfigBuilder) switch = tcConfigB.deterministic <- (switch = OptionSwitch.On) +let SetReferenceAssemblyOnlySwitch (tcConfigB: TcConfigBuilder) switch = + match tcConfigB.emitMetadataAssembly with + | MetadataAssemblyGeneration.None -> + tcConfigB.emitMetadataAssembly <- if (switch = OptionSwitch.On) then MetadataAssemblyGeneration.ReferenceOnly else MetadataAssemblyGeneration.None + | _ -> + error(Error(FSComp.SR.optsInvalidRefAssembly(), rangeCmdArgs)) + +let SetReferenceAssemblyOutSwitch (tcConfigB: TcConfigBuilder) outputPath = + match tcConfigB.emitMetadataAssembly with + | MetadataAssemblyGeneration.None -> + if FileSystem.IsInvalidPathShim outputPath then + error(Error(FSComp.SR.optsInvalidRefOut(), rangeCmdArgs)) + else + tcConfigB.emitMetadataAssembly <- MetadataAssemblyGeneration.ReferenceOut outputPath + | _ -> + error(Error(FSComp.SR.optsInvalidRefAssembly(), rangeCmdArgs)) + let AddPathMapping (tcConfigB: TcConfigBuilder) (pathPair: string) = match pathPair.Split([|'='|], 2) with | [| oldPrefix; newPrefix |] -> @@ -723,6 +740,16 @@ let outputFileFlagsFsc (tcConfigB: TcConfigBuilder) = ("nocopyfsharpcore", tagNone, OptionUnit (fun () -> tcConfigB.copyFSharpCore <- CopyFSharpCoreFlag.No), None, Some (FSComp.SR.optsNoCopyFsharpCore())) + + CompilerOption + ("refonly", tagNone, + OptionSwitch (SetReferenceAssemblyOnlySwitch tcConfigB), None, + Some (FSComp.SR.optsRefOnly())) + + CompilerOption + ("refout", tagFile, + OptionString (SetReferenceAssemblyOutSwitch tcConfigB), None, + Some (FSComp.SR.optsRefOut())) ] @@ -1041,6 +1068,8 @@ let testFlag tcConfigB = | "ShowLoadedAssemblies" -> tcConfigB.showLoadedAssemblies <- true | "ContinueAfterParseFailure" -> tcConfigB.continueAfterParseFailure <- true | "ParallelOff" -> tcConfigB.concurrentBuild <- false + | "MetadataOnly" -> tcConfigB.emitMetadataAssembly <- MetadataAssemblyGeneration.MetadataOnly + | "RefOnlyTestSigOfImpl" -> tcConfigB.emitMetadataAssembly <- MetadataAssemblyGeneration.TestSigOfImpl #if DEBUG | "ShowParserStackOnParseError" -> showParserStackOnParseError <- true #endif diff --git a/src/fsharp/FSComp.txt b/src/fsharp/FSComp.txt index 9bf9937c1e7..e2dd7d474a0 100644 --- a/src/fsharp/FSComp.txt +++ b/src/fsharp/FSComp.txt @@ -872,6 +872,8 @@ optsDebug,"Specify debugging type: full, portable, embedded, pdbonly. ('%s' is t optsOptimize,"Enable optimizations (Short form: -O)" optsTailcalls,"Enable or disable tailcalls" optsDeterministic,"Produce a deterministic assembly (including module version GUID and timestamp)" +optsRefOnly,"Produce a reference assembly, instead of a full assembly, as the primary output" +optsRefOut,"Produce a reference assembly with the specified file path." optsPathMap,"Maps physical paths to source path names output by the compiler" optsCrossoptimize,"Enable or disable cross-module optimizations" optsWarnaserrorPM,"Report all warnings as errors" @@ -1158,6 +1160,8 @@ fscTooManyErrors,"Exiting - too many errors" 2026,fscDeterministicDebugRequiresPortablePdb,"Deterministic builds only support portable PDBs (--debug:portable or --debug:embedded)" 2027,fscPathMapDebugRequiresPortablePdb,"--pathmap can only be used with portable PDBs (--debug:portable or --debug:embedded)" 2028,optsInvalidPathMapFormat,"Invalid path map. Mappings must be comma separated and of the format 'path=sourcePath'" +2029,optsInvalidRefOut,"Invalid reference assembly path'" +2030,optsInvalidRefAssembly,"Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together." 3000,etIllegalCharactersInNamespaceName,"Character '%s' is not allowed in provided namespace name '%s'" 3001,etNullOrEmptyMemberName,"The provided type '%s' returned a member with a null or empty member name" 3002,etNullMember,"The provided type '%s' returned a null member" diff --git a/src/fsharp/IlxGen.fs b/src/fsharp/IlxGen.fs index f17e12e1f68..e6e12b29254 100644 --- a/src/fsharp/IlxGen.fs +++ b/src/fsharp/IlxGen.fs @@ -64,17 +64,45 @@ let iLdcDouble i = AI_ldc (DT_R8, ILConst.R8 i) let iLdcSingle i = AI_ldc (DT_R4, ILConst.R4 i) +let ilThrowNullInstrs = [|ILInstr.AI_ldnull; ILInstr.I_throw|] +let emptyDict = Dictionary() +let mkILThrowNullMethodBody name = + let ilCode = buildILCode name emptyDict ilThrowNullInstrs [] [] + mkILMethodBody(false, ILLocals.Empty, 0, ilCode, None, None) + +let mkILThrowNullStorageCtorWithParamNames (extraParams, flds, access) = + mkILCtor(access, + (flds |> List.map (fun (pnm, _, ty) -> mkILParamNamed (pnm, ty))) @ extraParams, + mkILThrowNullMethodBody ".ctor" + |> notlazy + |> MethodBody.IL) + +let mkILThrowNullStorageCtor(extraParams, flds, access) = + mkILThrowNullStorageCtorWithParamNames (extraParams, flds |> List.map (fun (nm, ty) -> (nm, nm, ty)), access) + /// Make a method that simply loads a field -let mkLdfldMethodDef (ilMethName, reprAccess, isStatic, ilTy, ilFieldName, ilPropType) = +let mkLdfldMethodDef referenceAssemblyOnly (ilMethName, reprAccess, isStatic, ilTy, ilFieldName, ilPropType) = let ilFieldSpec = mkILFieldSpecInTy(ilTy, ilFieldName, ilPropType) let ilReturn = mkILReturn ilPropType let ilMethodDef = if isStatic then - let body = mkMethodBody(true, [], 2, nonBranchingInstrsToCode [mkNormalLdsfld ilFieldSpec], None, None) - mkILNonGenericStaticMethod (ilMethName, reprAccess, [], ilReturn, body) + let methBody = + if referenceAssemblyOnly then + mkILThrowNullMethodBody ilMethName + |> notlazy + |> MethodBody.IL + else + mkMethodBody(true, [], 2, nonBranchingInstrsToCode [mkNormalLdsfld ilFieldSpec], None, None) + mkILNonGenericStaticMethod (ilMethName, reprAccess, [], ilReturn, methBody) else - let body = mkMethodBody (true, [], 2, nonBranchingInstrsToCode [ mkLdarg0; mkNormalLdfld ilFieldSpec], None, None) - mkILNonGenericInstanceMethod (ilMethName, reprAccess, [], ilReturn, body) + let methBody = + if referenceAssemblyOnly then + mkILThrowNullMethodBody ilMethName + |> notlazy + |> MethodBody.IL + else + mkMethodBody (true, [], 2, nonBranchingInstrsToCode [ mkLdarg0; mkNormalLdfld ilFieldSpec], None, None) + mkILNonGenericInstanceMethod (ilMethName, reprAccess, [], ilReturn, methBody) ilMethodDef.WithSpecialName /// Choose the constructor parameter names for fields @@ -217,6 +245,9 @@ type IlxGenOptions = /// Whenever possible, use callvirt instead of call alwaysCallVirt: bool + + /// Indicates that we are not generating method bodies. + metadataOnly: bool } /// Compilation environment for compiling a fragment of an assembly @@ -1667,14 +1698,18 @@ type AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbu customAttrs=mkILCustomAttrs [ mkCompilationMappingAttrWithSeqNum g (int SourceConstructFlags.Field) i ]) ] let ilMethods = - [ for propName, fldName, fldTy in flds -> - mkLdfldMethodDef ("get_" + propName, ILMemberAccess.Public, false, ilTy, fldName, fldTy) + [ for (propName, fldName, fldTy) in flds -> + mkLdfldMethodDef cenv.opts.metadataOnly ("get_" + propName, ILMemberAccess.Public, false, ilTy, fldName, fldTy) yield! genToStringMethod ilTy ] let ilBaseTy = (if isStruct then g.iltyp_ValueType else g.ilg.typ_Object) let ilBaseTySpec = (if isStruct then None else Some ilBaseTy.TypeSpec) - let ilCtorDef = mkILSimpleStorageCtorWithParamNames(ilBaseTySpec, ilTy, [], flds, ILMemberAccess.Public, None, None) + let ilCtorDef = + if cenv.opts.metadataOnly then + mkILThrowNullStorageCtorWithParamNames([], flds, ILMemberAccess.Public) + else + mkILSimpleStorageCtorWithParamNames(ilBaseTySpec, ilTy, [], flds, ILMemberAccess.Public, None, None) // Create a tycon that looks exactly like a record definition, to help drive the generation of equality/comparison code let m = range0 @@ -1819,15 +1854,16 @@ type AssemblyBuilder(cenv: cenv, anonTypeTable: AnonTypeGenerationTable) as mgbu explicitEntryPointInfo <- Some tref member _.AddExplicitInitToSpecificMethodDef (cond, tref, fspec, sourceOpt, imports, feefee, seqpt) = - // Authoring a .cctor with effects forces the cctor for the 'initialization' module by doing a dummy store & load of a field - // Doing both a store and load keeps FxCop happier because it thinks the field is useful - let instrs = - [ yield! (if condition "NO_ADD_FEEFEE_TO_CCTORS" then [] elif condition "ADD_SEQPT_TO_CCTORS" then seqpt else feefee) // mark start of hidden code - yield mkLdcInt32 0 - yield mkNormalStsfld fspec - yield mkNormalLdsfld fspec - yield AI_pop] - gtdefs.FindNestedTypeDefBuilder(tref).PrependInstructionsToSpecificMethodDef(cond, instrs, sourceOpt, imports) + if not cenv.opts.metadataOnly then + // Authoring a .cctor with effects forces the cctor for the 'initialization' module by doing a dummy store & load of a field + // Doing both a store and load keeps FxCop happier because it thinks the field is useful + let instrs = + [ yield! (if condition "NO_ADD_FEEFEE_TO_CCTORS" then [] elif condition "ADD_SEQPT_TO_CCTORS" then seqpt else feefee) // mark start of hidden code + yield mkLdcInt32 0 + yield mkNormalStsfld fspec + yield mkNormalLdsfld fspec + yield AI_pop] + gtdefs.FindNestedTypeDefBuilder(tref).PrependInstructionsToSpecificMethodDef(cond, instrs, sourceOpt, imports) member _.AddEventDef (tref, edef) = gtdefs.FindNestedTypeDefBuilder(tref).AddEventDef(edef) @@ -6237,12 +6273,24 @@ and GenBindingAfterDebugPoint cenv cgbuf eenv sp (TBind(vspec, rhsExpr, _)) isSt cgbuf.mgbuf.AddOrMergePropertyDef(ilTypeRefForProperty, ilPropDef, m) let getterMethod = - let body = mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkNormalLdsfld fspec ], None, eenv.imports) + let body = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody ilGetterMethRef.Name + |> notlazy + |> MethodBody.IL + else + mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkNormalLdsfld fspec ], None, eenv.imports) mkILStaticMethod([], ilGetterMethRef.Name, access, [], mkILReturn fty, body).WithSpecialName cgbuf.mgbuf.AddMethodDef(ilTypeRefForProperty, getterMethod) if mut || cenv.opts.isInteractiveItExpr then - let body = mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkNormalStsfld fspec], None, eenv.imports) let setterMethod = + let body = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody ilGetterMethRef.Name + |> notlazy + |> MethodBody.IL + else + mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkNormalStsfld fspec], None, eenv.imports) mkILStaticMethod([], ilSetterMethRef.Name, access, [mkILParamNamed("value", fty)], mkILReturn ILType.Void, body).WithSpecialName cgbuf.mgbuf.AddMethodDef(ilTypeRefForProperty, setterMethod) @@ -6630,7 +6678,7 @@ and GenMethodForBinding ctorThisValOpt, baseValOpt, methLambdaTypars, methLambdaVars, methLambdaBody, returnTy) = let g = cenv.g let m = v.Range - + // If a method has a witness-passing version of the code, then suppress // the generation of any witness in the non-witness passing version of the code let eenv = { eenv with suppressWitnesses = hasWitnessEntry && not generateWitnessArgs } @@ -6712,11 +6760,17 @@ and GenMethodForBinding mkThrow m returnTy exnExpr else body + + if cenv.opts.metadataOnly then + // The reason for using 'throw null' bodies (as opposed to no bodies) is so + // that PEVerify can run and pass (thus validating the completeness of the metadata). + let ilMethBody = mkILThrowNullMethodBody mspec.Name + false, MethodBody.IL(notlazy ilMethBody), false + else + let ilCodeLazy = lazy CodeGenMethodForExpr cenv mgbuf (SPAlways, tailCallInfo, mspec.Name, eenvForMeth, 0, bodyExpr, sequel) - let ilCodeLazy = lazy CodeGenMethodForExpr cenv mgbuf (SPAlways, tailCallInfo, mspec.Name, eenvForMeth, 0, bodyExpr, sequel) - - // This is the main code generation for most methods - false, MethodBody.IL(ilCodeLazy), false + // This is the main code generation for most methods + false, MethodBody.IL(ilCodeLazy), false match ilMethodBody with | MethodBody.IL(ilCodeLazy) -> @@ -7540,9 +7594,9 @@ and GenImplFile cenv (mgbuf: AssemblyBuilder) mainInfoOpt eenv (implFile: TypedI let topInstrs, topCode = CodeGenMethod cenv mgbuf ([], methodName, eenv, 0, - (fun cgbuf eenv -> - GenModuleExpr cenv cgbuf qname lazyInitInfo eenv mexpr - CG.EmitInstr cgbuf (pop 0) Push0 I_ret), m) + (fun cgbuf eenv -> + GenModuleExpr cenv cgbuf qname lazyInitInfo eenv mexpr + CG.EmitInstr cgbuf (pop 0) Push0 I_ret), m) // The code generation for the initialization is now complete and the IL code is in topCode. // Make a .cctor and/or main method to contain the code. This initializes all modules. @@ -7648,17 +7702,22 @@ and GenEqualsOverrideCallingIComparable cenv (tcref: TyconRef, ilThisTy, _ilThat let g = cenv.g let mspec = mkILNonGenericInstanceMethSpecInTy (g.iltyp_IComparable, "CompareTo", [g.ilg.typ_Object], g.ilg.typ_Int32) - let ilInstrs = - [ mkLdarg0 - mkLdarg 1us - if tcref.IsStructOrEnumTycon then - I_callconstraint ( Normalcall, ilThisTy, mspec, None) - else - I_callvirt ( Normalcall, mspec, None) - mkLdcInt32 0 - AI_ceq ] - - let ilMethodBody = mkMethodBody(true, [], 2, nonBranchingInstrsToCode ilInstrs, None, None) + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody "Equals" + |> notlazy + |> MethodBody.IL + else + let ilInstrs = + [ mkLdarg0 + mkLdarg 1us + if tcref.IsStructOrEnumTycon then + I_callconstraint ( Normalcall, ilThisTy, mspec, None) + else + I_callvirt ( Normalcall, mspec, None) + mkLdcInt32 0 + AI_ceq ] + mkMethodBody(true, [], 2, nonBranchingInstrsToCode ilInstrs, None, None) mkILNonGenericVirtualMethod ("Equals", ILMemberAccess.Public, @@ -7785,23 +7844,27 @@ and GenToStringMethod cenv eenv ilThisTy m = // Here's the body of the method. Call printf, then invoke the function it returns let callInstrs = EraseClosures.mkCallFunc g.ilxPubCloEnv (fun _ -> 0us) eenv.tyenv.Count Normalcall (Apps_app(ilThisTy, Apps_done g.ilg.typ_String)) - - let ilInstrs = - [ // load the hardwired format string - I_ldstr "%+A" - // make the printf format object - mkNormalNewobj newFormatMethSpec - // call sprintf - mkNormalCall sprintfMethSpec - // call the function returned by sprintf - mkLdarg0 - if ilThisTy.Boxity = ILBoxity.AsValue then - mkNormalLdobj ilThisTy - yield! callInstrs ] - - let ilMethodBody = mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) - - let mdef = mkILNonGenericVirtualMethod ("ToString", ILMemberAccess.Public, [], mkILReturn g.ilg.typ_String, ilMethodBody) + let mdef = + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody "ToString" + |> notlazy + |> MethodBody.IL + else + let ilInstrs = + [ // load the hardwired format string + I_ldstr "%+A" + // make the printf format object + mkNormalNewobj newFormatMethSpec + // call sprintf + mkNormalCall sprintfMethSpec + // call the function returned by sprintf + mkLdarg0 + if ilThisTy.Boxity = ILBoxity.AsValue then + mkNormalLdobj ilThisTy + yield! callInstrs ] + mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) + mkILNonGenericVirtualMethod ("ToString", ILMemberAccess.Public, [], mkILReturn g.ilg.typ_String, ilMethodBody) let mdef = mdef.With(customAttrs = mkILCustomAttrs [ g.CompilerGeneratedAttribute ]) yield mdef | _ -> () ] @@ -8074,7 +8137,7 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = let ilPropName = fspec.Name let ilMethName = "get_" + ilPropName let access = ComputeMemberAccess isPropHidden - yield mkLdfldMethodDef (ilMethName, access, isStatic, ilThisTy, ilFieldName, ilPropType) + yield mkLdfldMethodDef cenv.opts.metadataOnly (ilMethName, access, isStatic, ilThisTy, ilFieldName, ilPropType) // Generate property setter methods for the mutable fields for useGenuineField, ilFieldName, isFSharpMutable, isStatic, _, ilPropType, isPropHidden, fspec in fieldSummaries do @@ -8088,10 +8151,22 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = let iLAccess = ComputeMemberAccess isPropHidden let ilMethodDef = if isStatic then - let ilMethodBody = mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkNormalStsfld ilFieldSpec], None, eenv.imports) + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody ilMethName + |> notlazy + |> MethodBody.IL + else + mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkNormalStsfld ilFieldSpec], None, eenv.imports) mkILNonGenericStaticMethod (ilMethName, iLAccess, ilParams, ilReturn, ilMethodBody) else - let ilMethodBody = mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkLdarg 1us;mkNormalStfld ilFieldSpec], None, eenv.imports) + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody ilMethName + |> notlazy + |> MethodBody.IL + else + mkMethodBody(true, [], 2, nonBranchingInstrsToCode [ mkLdarg0;mkLdarg 1us;mkNormalStfld ilFieldSpec], None, eenv.imports) mkILNonGenericInstanceMethod (ilMethName, iLAccess, ilParams, ilReturn, ilMethodBody) yield ilMethodDef.WithSpecialName @@ -8116,23 +8191,28 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = // Here's the body of the method. Call printf, then invoke the function it returns let callInstrs = EraseClosures.mkCallFunc g.ilxPubCloEnv (fun _ -> 0us) eenv.tyenv.Count Normalcall (Apps_app(ilThisTy, Apps_done g.ilg.typ_String)) - - let ilInstrs = - [ // load the hardwired format string - I_ldstr "%+0.8A" - // make the printf format object - mkNormalNewobj newFormatMethSpec - // call sprintf - mkNormalCall sprintfMethSpec - // call the function returned by sprintf - mkLdarg0 - if ilThisTy.Boxity = ILBoxity.AsValue then - mkNormalLdobj ilThisTy - yield! callInstrs ] - - let ilMethodBody = mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) - - let ilMethodDef = mkILNonGenericInstanceMethod (debugDisplayMethodName, ILMemberAccess.Assembly, [], mkILReturn g.ilg.typ_Object, ilMethodBody) + let ilMethodDef = + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody debugDisplayMethodName + |> notlazy + |> MethodBody.IL + else + let ilInstrs = + [ // load the hardwired format string + I_ldstr "%+0.8A" + // make the printf format object + mkNormalNewobj newFormatMethSpec + // call sprintf + mkNormalCall sprintfMethSpec + // call the function returned by sprintf + mkLdarg0 + if ilThisTy.Boxity = ILBoxity.AsValue then + mkNormalLdobj ilThisTy + yield! callInstrs ] + + mkMethodBody (true, [], 2, nonBranchingInstrsToCode ilInstrs, None, eenv.imports) + mkILNonGenericInstanceMethod (debugDisplayMethodName, ILMemberAccess.Assembly, [], mkILReturn g.ilg.typ_Object, ilMethodBody) yield ilMethodDef.WithSpecialName |> AddNonUserCompilerGeneratedAttribs g | None, _ -> //printfn "sprintf not found" @@ -8162,14 +8242,21 @@ and GenTypeDef cenv mgbuf lazyInitInfo eenv m (tycon: Tycon) = // No type spec if the record is a value type let spec = if isStructRecord then None else Some(g.ilg.typ_Object.TypeSpec) - let ilMethodDef = mkILSimpleStorageCtorWithParamNames(spec, ilThisTy, [], ChooseParamNames fieldNamesAndTypes, reprAccess, None, eenv.imports) + let ilMethodDef = + if cenv.opts.metadataOnly then + mkILThrowNullStorageCtorWithParamNames([], ChooseParamNames fieldNamesAndTypes, reprAccess) + else + mkILSimpleStorageCtorWithParamNames(spec, ilThisTy, [], ChooseParamNames fieldNamesAndTypes, reprAccess, None, eenv.imports) yield ilMethodDef // FSharp 1.0 bug 1988: Explicitly setting the ComVisible(true) attribute on an F# type causes an F# record to be emitted in a way that enables mutation for COM interop scenarios // FSharp 3.0 feature: adding CLIMutable to a record type causes emit of default constructor, and all fields get property setters // Records that are value types do not create a default constructor with CLIMutable or ComVisible if not isStructRecord && (isCLIMutable || (TryFindFSharpBoolAttribute g g.attrib_ComVisibleAttribute tycon.Attribs = Some true)) then - yield mkILSimpleStorageCtor(Some g.ilg.typ_Object.TypeSpec, ilThisTy, [], [], reprAccess, None, eenv.imports) + if cenv.opts.metadataOnly then + yield mkILThrowNullStorageCtor([], [], reprAccess) + else + yield mkILSimpleStorageCtor(Some g.ilg.typ_Object.TypeSpec, ilThisTy, [], [], reprAccess, None, eenv.imports) if not (tycon.HasMember g "ToString" []) then yield! GenToStringMethod cenv eenv ilThisTy m @@ -8427,7 +8514,7 @@ and GenExnDef cenv mgbuf eenv m (exnc: Tycon) = let ilPropType = GenType cenv.amap m eenv.tyenv fld.FormalType let ilMethName = "get_" + fld.Name let ilFieldName = ComputeFieldName exnc fld - let ilMethodDef = mkLdfldMethodDef (ilMethName, reprAccess, false, ilThisTy, ilFieldName, ilPropType) + let ilMethodDef = mkLdfldMethodDef cenv.opts.metadataOnly (ilMethName, reprAccess, false, ilThisTy, ilFieldName, ilPropType) let ilFieldDef = mkILInstanceField(ilFieldName, ilPropType, None, ILMemberAccess.Assembly) let ilPropDef = ILPropertyDef(name = ilPropName, @@ -8443,13 +8530,19 @@ and GenExnDef cenv mgbuf eenv m (exnc: Tycon) = |> List.unzip4 let ilCtorDef = - mkILSimpleStorageCtorWithParamNames(Some g.iltyp_Exception.TypeSpec, ilThisTy, [], ChooseParamNames fieldNamesAndTypes, reprAccess, None, eenv.imports) + if cenv.opts.metadataOnly then + mkILThrowNullStorageCtorWithParamNames([], ChooseParamNames fieldNamesAndTypes, reprAccess) + else + mkILSimpleStorageCtorWithParamNames(Some g.iltyp_Exception.TypeSpec, ilThisTy, [], ChooseParamNames fieldNamesAndTypes, reprAccess, None, eenv.imports) // In compiled code, all exception types get a parameterless constructor for use with XML serialization // This does default-initialization of all fields let ilCtorDefNoArgs = if not (isNil fieldNamesAndTypes) then - [ mkILSimpleStorageCtor(Some g.iltyp_Exception.TypeSpec, ilThisTy, [], [], reprAccess, None, eenv.imports) ] + if cenv.opts.metadataOnly then + [ mkILThrowNullStorageCtor([], [], reprAccess) ] + else + [ mkILSimpleStorageCtor(Some g.iltyp_Exception.TypeSpec, ilThisTy, [], [], reprAccess, None, eenv.imports) ] else [] @@ -8458,17 +8551,23 @@ and GenExnDef cenv mgbuf eenv m (exnc: Tycon) = match g.iltyp_SerializationInfo, g.iltyp_StreamingContext with | Some serializationInfoType, Some streamingContextType -> - let ilInstrsForSerialization = - [ mkLdarg0 - mkLdarg 1us - mkLdarg 2us - mkNormalCall (mkILCtorMethSpecForTy (g.iltyp_Exception, [serializationInfoType; streamingContextType])) ] - |> nonBranchingInstrsToCode - let ilCtorDefForSerialization = + let ilMethodBody = + if cenv.opts.metadataOnly then + mkILThrowNullMethodBody "info" + |> notlazy + |> MethodBody.IL + else + let ilInstrsForSerialization = + [ mkLdarg0 + mkLdarg 1us + mkLdarg 2us + mkNormalCall (mkILCtorMethSpecForTy (g.iltyp_Exception, [serializationInfoType; streamingContextType])) ] + |> nonBranchingInstrsToCode + mkMethodBody (false, [], 8, ilInstrsForSerialization, None, eenv.imports) mkILCtor(ILMemberAccess.Family, [mkILParamNamed("info", serializationInfoType);mkILParamNamed("context", streamingContextType)], - mkMethodBody (false, [], 8, ilInstrsForSerialization, None, eenv.imports)) + ilMethodBody) [ilCtorDefForSerialization] | _ -> [] diff --git a/src/fsharp/IlxGen.fsi b/src/fsharp/IlxGen.fsi index 7fe28f1616b..790d568cdd8 100644 --- a/src/fsharp/IlxGen.fsi +++ b/src/fsharp/IlxGen.fsi @@ -52,6 +52,9 @@ type internal IlxGenOptions = /// Indicates that, whenever possible, use callvirt instead of call alwaysCallVirt: bool + + /// Indicates that we are not generating method bodies. + metadataOnly: bool } /// The results of the ILX compilation of one fragment of an assembly diff --git a/src/fsharp/OptimizeInputs.fs b/src/fsharp/OptimizeInputs.fs index 5c6225e8d34..cee77799f89 100644 --- a/src/fsharp/OptimizeInputs.fs +++ b/src/fsharp/OptimizeInputs.fs @@ -179,7 +179,11 @@ let GenerateIlxCode ilxBackend = ilxBackend isInteractive = tcConfig.isInteractive isInteractiveItExpr = isInteractiveItExpr - alwaysCallVirt = tcConfig.alwaysCallVirt } + alwaysCallVirt = tcConfig.alwaysCallVirt + metadataOnly = + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.MetadataOnly -> true + | _ -> false } ilxGenerator.GenerateCode (ilxGenOpts, optimizedImpls, topAttrs.assemblyAttrs, topAttrs.netModuleAttrs) diff --git a/src/fsharp/ParseAndCheckInputs.fs b/src/fsharp/ParseAndCheckInputs.fs index 9c633e880de..d60b575a1e3 100644 --- a/src/fsharp/ParseAndCheckInputs.fs +++ b/src/fsharp/ParseAndCheckInputs.fs @@ -34,6 +34,7 @@ open FSharp.Compiler.Text.Range open FSharp.Compiler.Xml open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps +open FSharp.Compiler.TypedTreeBasics open FSharp.Compiler.TcGlobals let CanonicalizeFilename filename = @@ -773,6 +774,141 @@ let GetInitialTcState(m, ccuName, tcConfig: TcConfig, tcGlobals, tcImports: TcIm tcsImplicitOpenDeclarations = openDecls0 } +let mkDummyParameterVal name attribs ty = + Construct.NewVal( + name, range0, None, ty, ValMutability.Immutable, false, None, taccessPublic, + ValRecursiveScopeInfo.ValNotInRecScope, None, ValBaseOrThisInfo.NormalVal, attribs, ValInline.Never, + XmlDoc.Empty, false, false, false, false, false, false, None, ParentNone) + +let rec CreateDummyModuleOrNamespaceExpr (g: TcGlobals) (mty: ModuleOrNamespaceType) = + + let dummyValAsBinding (v: Val) = + let dummyExpr = + // It does not matter what this expression is as it will never get checked or emitted. + let retDummyExpr = Expr.Op(TOp.Return, [], [], range0) + + if isFunTy g v.Type || isForallFunctionTy g v.Type then + match v.ValReprInfo with + | Some valReprInfo -> + let typars, curriedArgInfos, retTy, _retInfo = GetTopValTypeInFSharpForm g valReprInfo v.Type v.Range + + let valParams = + let defaultParamNames = + match + v.MemberInfo + |> Option.bind (fun x -> x.ImplementedSlotSigs |> List.tryExactlyOne) + with + | Some slotSig when v.IsCompilerGenerated -> + let paramNames = + slotSig.FormalParams + |> List.map (fun slotParams -> + slotParams + |> List.map (fun slotParam -> + match slotParam with + | TSlotParam(paramName=paramName) -> + paramName + |> Option.defaultValue "" + ) + |> Array.ofList + ) + + if v.IsInstanceMember then + [|""|] :: paramNames |> Array.ofList + else + paramNames |> Array.ofList + | _ -> + curriedArgInfos + |> List.map (fun x -> Array.init x.Length (fun _ -> "")) + |> Array.ofSeq + + curriedArgInfos + |> List.mapi (fun i argInfos -> + argInfos + |> List.mapi (fun j (ty, argInfo) -> + let defaultParamName = + if i >= defaultParamNames.Length || j >= defaultParamNames.[i].Length then + "" + else + defaultParamNames.[i].[j] + let name = + argInfo.Name + |> Option.map (fun x -> x.idText) + |> Option.defaultValue defaultParamName + mkDummyParameterVal name argInfo.Attribs ty + ) + ) + + if valParams.IsEmpty || (valParams.Length = 1 && valParams.Head.IsEmpty) then + // We have to create a lambda like this as `mkMemberLambdas` will throw if it is passed + // a single empty curried argument list. + if typars.IsEmpty then + Expr.Lambda(newUnique(), None, None, [], retDummyExpr, range0, retTy) + else + Expr.TyLambda(newUnique(), typars, retDummyExpr, range0, retTy) + else + mkMemberLambdas range0 typars None None valParams (retDummyExpr, retTy) + | _ -> + failwith "Expected top-level val" + else + retDummyExpr + mkBind DebugPointAtBinding.NoneAtLet v dummyExpr + + let dummyValAsModuleOrNamespaceExpr (v: Val) = + ModuleOrNamespaceExpr.TMDefLet(dummyValAsBinding v, range0) + + let dummyValAsModuleOrNamespaceExprs (vs: Val seq) = + vs + |> Seq.map dummyValAsModuleOrNamespaceExpr + + let dummyEntityAsModuleOrNamespaceBinding (ent: Entity) = + ModuleOrNamespaceBinding.Module(ent, CreateDummyModuleOrNamespaceExpr g ent.ModuleOrNamespaceType) + + let dummyEntitiesAsModuleOrNamespaceBindings (ents: Entity seq) = + ents + |> Seq.map dummyEntityAsModuleOrNamespaceBinding + + let entBindings = + mty.ModuleAndNamespaceDefinitions + |> dummyEntitiesAsModuleOrNamespaceBindings + |> List.ofSeq + + let tycons = mty.TypeAndExceptionDefinitions + + let dummyExprs = + dummyValAsModuleOrNamespaceExprs mty.AllValsAndMembers + |> List.ofSeq + + let dummyExprs = + if entBindings.IsEmpty && tycons.IsEmpty then + dummyExprs + else + ModuleOrNamespaceExpr.TMDefRec(false, [], tycons, entBindings, range0) :: dummyExprs + + ModuleOrNamespaceExpr.TMDefs dummyExprs + +let CreateDummyModuleOrNamespaceExprWithSig g (sigTy: ModuleOrNamespaceType) = + let dummyExpr = CreateDummyModuleOrNamespaceExpr g sigTy + ModuleOrNamespaceExprWithSig(sigTy, ModuleOrNamespaceExpr.TMDefs [dummyExpr], range0) + +/// Similar to 'createDummyTypedImplFile', only diffference is that there are no definitions and is not used for emitting any kind of assembly. +let CreateEmptyDummyTypedImplFile qualNameOfFile sigTy = + let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(sigTy, ModuleOrNamespaceExpr.TMDefs [], range0) + TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap.Empty) + +/// 'dummy' in this context means it acts as a placeholder so other parts of the compiler will work with it. +/// In this case, this is used to create a typed impl file based on a signature so we can emit a partial reference assembly +/// for tooling, IDEs, etc - without having to actually check an implementation file. +/// An example of this use would be for other .NET languages wanting cross-project referencing with F# as they require an assembly. +let CreateDummyTypedImplFile g qualNameOfFile sigTy = + let exprWithSig = CreateDummyModuleOrNamespaceExprWithSig g sigTy + + let anonRecdTypeInfos = + let s = freeAnonRecdTypeInfosInModuleTy sigTy + StampMap.Empty + |> s.Fold (fun x stamps -> stamps.Add(x.Stamp, x)) + + TypedImplFile.TImplFile(qualNameOfFile, [], exprWithSig, false, false, anonRecdTypeInfos) + /// Typecheck a single file (or interactive entry into F# Interactive) let TypeCheckOneInput(checkForErrors, tcConfig: TcConfig, @@ -849,17 +985,20 @@ let TypeCheckOneInput(checkForErrors, // Typecheck the implementation file let typeCheckOne = if skipImplIfSigExists && hadSig then - let dummyExpr = ModuleOrNamespaceExprWithSig.ModuleOrNamespaceExprWithSig(rootSigOpt.Value, ModuleOrNamespaceExpr.TMDefs [], range.Zero) - let dummyImplFile = TypedImplFile.TImplFile(qualNameOfFile, [], dummyExpr, false, false, StampMap []) - - (EmptyTopAttrs, dummyImplFile, Unchecked.defaultof<_>, tcImplEnv, false) + (EmptyTopAttrs, CreateEmptyDummyTypedImplFile qualNameOfFile rootSigOpt.Value, Unchecked.defaultof<_>, tcImplEnv, false) |> Cancellable.ret else TypeCheckOneImplFile (tcGlobals, tcState.tcsNiceNameGen, amap, tcState.tcsCcu, tcState.tcsImplicitOpenDeclarations, checkForErrors, conditionalDefines, tcSink, tcConfig.internalTestSpanStackReferring, tcImplEnv, rootSigOpt, file) - let! topAttrs, implFile, _implFileHiddenType, tcEnvAtEnd, createsGeneratedProvidedTypes = typeCheckOne + let! topAttrs, implFile0, _implFileHiddenType, tcEnvAtEnd, createsGeneratedProvidedTypes = typeCheckOne + + let implFileSigType = SigTypeOfImplFile implFile0 - let implFileSigType = SigTypeOfImplFile implFile + let implFile = + if tcConfig.emitMetadataAssembly = MetadataAssemblyGeneration.TestSigOfImpl then + CreateDummyTypedImplFile tcGlobals qualNameOfFile implFileSigType + else + implFile0 let rootImpls = Zset.add qualNameOfFile tcState.tcsRootImpls diff --git a/src/fsharp/ParseAndCheckInputs.fsi b/src/fsharp/ParseAndCheckInputs.fsi index dce7ab03f95..1ba695568ae 100644 --- a/src/fsharp/ParseAndCheckInputs.fsi +++ b/src/fsharp/ParseAndCheckInputs.fsi @@ -90,6 +90,12 @@ val GetInitialTcState: OpenDeclaration list -> TcState +/// 'dummy' in this context means it acts as a placeholder so other parts of the compiler will work with it. +/// In this case, this is used to create a typed impl file based on a signature so we can emit a partial reference assembly +/// for tooling, IDEs, etc - without having to actually check an implementation file. +/// An example of this use would be for other .NET languages wanting cross-project referencing with F# as they require an assembly. +val CreateDummyTypedImplFile: g: TcGlobals -> qualNameOfFile: QualifiedNameOfFile -> sigTy: ModuleOrNamespaceType -> TypedImplFile + /// Check one input, returned as an Eventually computation val TypeCheckOneInput: checkForErrors:(unit -> bool) * diff --git a/src/fsharp/StaticLinking.fs b/src/fsharp/StaticLinking.fs index c2a1e9c25ce..e8e3ce7e00c 100644 --- a/src/fsharp/StaticLinking.fs +++ b/src/fsharp/StaticLinking.fs @@ -354,6 +354,11 @@ let StaticLink (ctok, tcConfig: TcConfig, tcImports: TcImports, ilGlobals: ILGlo id else (fun ilxMainModule -> + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.None -> () + | _ -> + error(Error(FSComp.SR.optsInvalidRefAssembly(), rangeCmdArgs)) + ReportTime tcConfig "Find assembly references" let dependentILModules = FindDependentILModulesForStaticLinking (ctok, tcConfig, tcImports, ilGlobals, ilxMainModule) diff --git a/src/fsharp/TcGlobals.fs b/src/fsharp/TcGlobals.fs index fa82099c85a..a7e4e7a23f9 100755 --- a/src/fsharp/TcGlobals.fs +++ b/src/fsharp/TcGlobals.fs @@ -165,6 +165,8 @@ let tname_RuntimeFieldHandle = "System.RuntimeFieldHandle" [] let tname_CompilerGeneratedAttribute = "System.Runtime.CompilerServices.CompilerGeneratedAttribute" [] +let tname_ReferenceAssemblyAttribute = "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" +[] let tname_DebuggableAttribute = "System.Diagnostics.DebuggableAttribute" [] let tname_AsyncCallback = "System.AsyncCallback" @@ -1181,6 +1183,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val iltyp_RuntimeFieldHandle = findSysILTypeRef tname_RuntimeFieldHandle |> mkILNonGenericValueTy member val iltyp_RuntimeMethodHandle = findSysILTypeRef tname_RuntimeMethodHandle |> mkILNonGenericValueTy member val iltyp_RuntimeTypeHandle = findSysILTypeRef tname_RuntimeTypeHandle |> mkILNonGenericValueTy + member val iltyp_ReferenceAssemblyAttributeOpt = tryFindSysILTypeRef tname_ReferenceAssemblyAttribute |> Option.map mkILNonGenericBoxedTy member val attrib_AttributeUsageAttribute = findSysAttrib "System.AttributeUsageAttribute" @@ -1219,6 +1222,7 @@ type public TcGlobals(compilingFslib: bool, ilg:ILGlobals, fslibCcu: CcuThunk, d member val attrib_CallerLineNumberAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerLineNumberAttribute" member val attrib_CallerFilePathAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerFilePathAttribute" member val attrib_CallerMemberNameAttribute = findSysAttrib "System.Runtime.CompilerServices.CallerMemberNameAttribute" + member val attrib_ReferenceAssemblyAttribute = findSysAttrib "System.Runtime.CompilerServices.ReferenceAssemblyAttribute" member val attrib_SkipLocalsInitAttribute = findSysAttrib "System.Runtime.CompilerServices.SkipLocalsInitAttribute" member val attribs_Unsupported = [ tryFindSysAttrib "System.Runtime.CompilerServices.ModuleInitializerAttribute" diff --git a/src/fsharp/TypedTree.fs b/src/fsharp/TypedTree.fs index 9d770d8316d..8dc72e970ed 100644 --- a/src/fsharp/TypedTree.fs +++ b/src/fsharp/TypedTree.fs @@ -5368,6 +5368,9 @@ type FreeRecdFields = Zset /// Represents a set of 'free' union cases. Used to collect the union cases referred to from an expression. type FreeUnionCases = Zset +/// Represents a set of 'free' anonymous record types. Used to collect the anonymous records in a signature. +type FreeAnonRecdTypeInfos = Zset + /// Represents a set of 'free' type-related elements, including named types, trait solutions, union cases and /// record fields. [] @@ -5383,6 +5386,9 @@ type FreeTyvars = /// The summary of type parameters used in the expression. These may not escape the enclosing generic construct /// and we have to check various conditions associated with that. FreeTypars: FreeTypars + + /// The summary of anonymous records used in a signature. + FreeAnonRecdTypeInfos: FreeAnonRecdTypeInfos } [] diff --git a/src/fsharp/TypedTreeOps.fs b/src/fsharp/TypedTreeOps.fs index f84223fdf9c..29955bc0ce7 100644 --- a/src/fsharp/TypedTreeOps.fs +++ b/src/fsharp/TypedTreeOps.fs @@ -1974,11 +1974,22 @@ let unionFreeTypars s1 s2 = elif s2 === emptyFreeTypars then s1 else Zset.union s1 s2 +let anonRecdTypeInfoOrder = + { new System.Collections.Generic.IComparer with + member x.Compare (v1: AnonRecdTypeInfo, v2: AnonRecdTypeInfo) = compare v1.Stamp v2.Stamp } + +let emptyFreeAnonRecdTypeInfos = Zset.empty anonRecdTypeInfoOrder +let unionFreeAnonRecdTypeInfos s1 s2 = + if s1 === emptyFreeAnonRecdTypeInfos then s2 + elif s2 === emptyFreeAnonRecdTypeInfos then s1 + else Zset.union s1 s2 + let emptyFreeTyvars = { FreeTycons = emptyFreeTycons /// The summary of values used as trait solutions FreeTraitSolutions = emptyFreeLocals - FreeTypars = emptyFreeTypars} + FreeTypars = emptyFreeTypars + FreeAnonRecdTypeInfos = emptyFreeAnonRecdTypeInfos } let isEmptyFreeTyvars ftyvs = Zset.isEmpty ftyvs.FreeTypars && @@ -1989,7 +2000,8 @@ let unionFreeTyvars fvs1 fvs2 = if fvs2 === emptyFreeTyvars then fvs1 else { FreeTycons = unionFreeTycons fvs1.FreeTycons fvs2.FreeTycons FreeTraitSolutions = unionFreeLocals fvs1.FreeTraitSolutions fvs2.FreeTraitSolutions - FreeTypars = unionFreeTypars fvs1.FreeTypars fvs2.FreeTypars } + FreeTypars = unionFreeTypars fvs1.FreeTypars fvs2.FreeTypars + FreeAnonRecdTypeInfos = unionFreeAnonRecdTypeInfos fvs1.FreeAnonRecdTypeInfos fvs2.FreeAnonRecdTypeInfos } type FreeVarOptions = { canCache: bool @@ -1999,7 +2011,8 @@ type FreeVarOptions = includeLocalTyconReprs: bool includeRecdFields: bool includeUnionCases: bool - includeLocals: bool } + includeLocals: bool + includeAnonRecdTypeInfos: bool } let CollectAllNoCaching = { canCache = false @@ -2009,7 +2022,11 @@ let CollectAllNoCaching = includeRecdFields = true includeUnionCases = true includeTypars = true - includeLocals = true } + includeLocals = true + + // REVIEW: While this options dictates that we collect all the information, + // we only want to collect anonymous record information when building a dummy typed implementation file. + includeAnonRecdTypeInfos = false } let CollectTyparsNoCaching = { canCache = false @@ -2019,7 +2036,8 @@ let CollectTyparsNoCaching = includeLocalTyconReprs = false includeRecdFields = false includeUnionCases = false - includeLocals = false } + includeLocals = false + includeAnonRecdTypeInfos = false } let CollectLocalsNoCaching = { canCache = false @@ -2029,7 +2047,8 @@ let CollectLocalsNoCaching = includeLocalTyconReprs = false includeRecdFields = false includeUnionCases = false - includeLocals = true } + includeLocals = true + includeAnonRecdTypeInfos = false } let CollectTyparsAndLocalsNoCaching = { canCache = false @@ -2039,7 +2058,8 @@ let CollectTyparsAndLocalsNoCaching = includeRecdFields = false includeUnionCases = false includeTypars = true - includeLocals = true } + includeLocals = true + includeAnonRecdTypeInfos = false } let CollectAll = { canCache = false @@ -2049,7 +2069,8 @@ let CollectAll = includeRecdFields = true includeUnionCases = true includeTypars = true - includeLocals = true } + includeLocals = true + includeAnonRecdTypeInfos = false } let CollectTyparsAndLocals = // CollectAll { canCache = true // only cache for this one @@ -2059,8 +2080,22 @@ let CollectTyparsAndLocals = // CollectAll includeLocalTycons = false includeLocalTyconReprs = false includeRecdFields = false - includeUnionCases = false } + includeUnionCases = false + // REVIEW: While this options dictates that we collect all the information, + // we only want to collect anonymous record information when building a dummy typed implementation file. + includeAnonRecdTypeInfos = false } + +let CollectAnonRecdTypeInfosNoCaching = + { canCache = false + collectInTypes = true + includeTypars = true + includeLocals = true + includeLocalTycons = false + includeLocalTyconReprs = false + includeRecdFields = false + includeUnionCases = false + includeAnonRecdTypeInfos = true } let CollectTypars = CollectTyparsAndLocals @@ -2150,7 +2185,14 @@ and accFreeTyparRef opts (tp: Typar) acc = and accFreeInType opts ty acc = match stripTyparEqns ty with | TType_tuple (tupInfo, l) -> accFreeInTypes opts l (accFreeInTupInfo opts tupInfo acc) - | TType_anon (anonInfo, l) -> accFreeInTypes opts l (accFreeInTupInfo opts anonInfo.TupInfo acc) + | TType_anon (anonInfo, l) -> + let acc = + if opts.includeAnonRecdTypeInfos then + if Zset.contains anonInfo acc.FreeAnonRecdTypeInfos then acc + else { acc with FreeAnonRecdTypeInfos = Zset.add anonInfo acc.FreeAnonRecdTypeInfos } + else + acc + accFreeInTypes opts l (accFreeInTupInfo opts anonInfo.TupInfo acc) | TType_app (tc, tinst) -> let acc = accFreeTycon opts tc acc match tinst with @@ -2180,11 +2222,14 @@ let freeInVal opts v = accFreeInVal opts v emptyFreeTyvars let freeInTyparConstraints opts v = accFreeInTyparConstraints opts v emptyFreeTyvars let accFreeInTypars opts tps acc = List.foldBack (accFreeTyparRef opts) tps acc -let rec addFreeInModuleTy (mtyp: ModuleOrNamespaceType) acc = - QueueList.foldBack (typeOfVal >> accFreeInType CollectAllNoCaching) mtyp.AllValsAndMembers - (QueueList.foldBack (fun (mspec: ModuleOrNamespace) acc -> addFreeInModuleTy mspec.ModuleOrNamespaceType acc) mtyp.AllEntities acc) +let rec addFreeInModuleTy (mtyp: ModuleOrNamespaceType) opts acc = + QueueList.foldBack (typeOfVal >> accFreeInType opts) mtyp.AllValsAndMembers + (QueueList.foldBack (fun (mspec: ModuleOrNamespace) acc -> addFreeInModuleTy mspec.ModuleOrNamespaceType opts acc) mtyp.AllEntities acc) + +let freeInModuleTy mtyp = addFreeInModuleTy mtyp CollectAllNoCaching emptyFreeTyvars -let freeInModuleTy mtyp = addFreeInModuleTy mtyp emptyFreeTyvars +let freeAnonRecdTypeInfosInModuleTy mtyp = + (addFreeInModuleTy mtyp CollectAnonRecdTypeInfosNoCaching emptyFreeTyvars).FreeAnonRecdTypeInfos //-------------------------------------------------------------------------- diff --git a/src/fsharp/TypedTreeOps.fsi b/src/fsharp/TypedTreeOps.fsi index 8963d7d9222..a2aa11e204e 100755 --- a/src/fsharp/TypedTreeOps.fsi +++ b/src/fsharp/TypedTreeOps.fsi @@ -759,7 +759,9 @@ val emptyFreeLocals: FreeLocals val unionFreeLocals: FreeLocals -> FreeLocals -> FreeLocals -type FreeVarOptions +type FreeVarOptions + +val CollectAnonRecdTypeInfosNoCaching: FreeVarOptions val CollectLocalsNoCaching: FreeVarOptions @@ -798,6 +800,8 @@ val freeInTypesLeftToRightSkippingConstraints: TcGlobals -> TType list -> Typars val freeInModuleTy: ModuleOrNamespaceType -> FreeTyvars +val freeAnonRecdTypeInfosInModuleTy: ModuleOrNamespaceType -> FreeAnonRecdTypeInfos + val isDimensionless: TcGlobals -> TType -> bool //--------------------------------------------------------------------------- diff --git a/src/fsharp/absil/ilwrite.fs b/src/fsharp/absil/ilwrite.fs index 0960530c613..4d8ce140fe8 100644 --- a/src/fsharp/absil/ilwrite.fs +++ b/src/fsharp/absil/ilwrite.fs @@ -564,6 +564,12 @@ type cenv = normalizeAssemblyRefs: ILAssemblyRef -> ILAssemblyRef + /// Indicates that the writing assembly will have an assembly-level attribute, System.Runtime.CompilerServices.InternalsVisibleToAttribute. + hasInternalsVisibleToAttrib: bool + + /// Indicates that the writing assembly will be a reference assembly. Method bodies will be replaced with a `throw null` if there are any. + referenceAssemblyOnly: bool + pdbImports: Dictionary } member cenv.GetTable (tab: TableName) = cenv.tables.[tab.Index] @@ -1076,6 +1082,14 @@ let GetTypeAccessFlags access = | ILTypeDefAccess.Nested ILMemberAccess.FamilyOrAssembly -> 0x00000007 | ILTypeDefAccess.Nested ILMemberAccess.Assembly -> 0x00000005 +let canGenMethodDef cenv (md: ILMethodDef) = + // When emitting a reference assembly, do not emit methods that are private unless they are virtual/abstract or provide an explicit interface implementation. + // Internal methods can be omitted only if the assembly does not contain a System.Runtime.CompilerServices.InternalsVisibleToAttribute. + if cenv.referenceAssemblyOnly && + (match md.Access with ILMemberAccess.Private -> true | ILMemberAccess.Assembly | ILMemberAccess.FamilyAndAssembly -> not cenv.hasInternalsVisibleToAttrib | _ -> false) && + not (md.IsVirtual || md.IsAbstract || md.IsNewSlot || md.IsFinal) then false + else true + let rec GetTypeDefAsRow cenv env _enc (td: ILTypeDef) = let nselem, nelem = GetTypeNameAsElemPair cenv td.Name let flags = @@ -1117,19 +1131,20 @@ and GetKeyForMethodDef cenv tidx (md: ILMethodDef) = MethodDefKey (cenv.ilg, tidx, md.GenericParams.Length, md.Name, md.Return.Type, md.ParameterTypes, md.CallingConv.IsStatic) and GenMethodDefPass2 cenv tidx md = - let idx = - cenv.methodDefIdxsByKey.AddUniqueEntry - "method" - (fun (key: MethodDefKey) -> - dprintn "Duplicate in method table is:" - dprintn (" Type index: "+string key.TypeIdx) - dprintn (" Method name: "+key.Name) - dprintn (" Method arity (num generic params): "+string key.GenericArity) - key.Name - ) - (GetKeyForMethodDef cenv tidx md) - - cenv.methodDefIdxs.[md] <- idx + if canGenMethodDef cenv md then + let idx = + cenv.methodDefIdxsByKey.AddUniqueEntry + "method" + (fun (key: MethodDefKey) -> + dprintn "Duplicate in method table is:" + dprintn (" Type index: "+string key.TypeIdx) + dprintn (" Method name: "+key.Name) + dprintn (" Method arity (num generic params): "+string key.GenericArity) + key.Name + ) + (GetKeyForMethodDef cenv tidx md) + + cenv.methodDefIdxs.[md] <- idx and GetKeyForPropertyDef tidx (x: ILPropertyDef) = PropKey (tidx, x.Name, x.PropertyType, x.Args) @@ -2506,6 +2521,10 @@ let GetMethodDefSigAsBytes cenv env (mdef: ILMethodDef) = let GenMethodDefSigAsBlobIdx cenv env mdef = GetBytesAsBlobIdx cenv (GetMethodDefSigAsBytes cenv env mdef) +let ilMethodBodyThrowNull = + let ilCode = IL.buildILCode "" (Dictionary()) [|ILInstr.AI_ldnull; ILInstr.I_throw|] [] [] + mkILMethodBody(false, ILLocals.Empty, 0, ilCode, None, None) + let GenMethodDefAsRow cenv env midx (md: ILMethodDef) = let flags = md.Attributes @@ -2517,7 +2536,11 @@ let GenMethodDefAsRow cenv env midx (md: ILMethodDef) = let codeAddr = (match md.Body with | MethodBody.IL ilmbodyLazy -> - let ilmbody = ilmbodyLazy.Value + let ilmbody = + if cenv.referenceAssemblyOnly then + ilMethodBodyThrowNull + else + ilmbodyLazy.Value let addr = cenv.nextCodeAddr let localToken, code, seqpoints, rootScope = GenILMethodBody md.Name cenv env ilmbody @@ -2580,55 +2603,57 @@ let GenMethodImplPass3 cenv env _tgparams tidx mimpl = MethodDefOrRef (midx2Tag, midx2Row) |]) |> ignore let GenMethodDefPass3 cenv env (md: ILMethodDef) = - let midx = GetMethodDefIdx cenv md - let idx2 = AddUnsharedRow cenv TableNames.Method (GenMethodDefAsRow cenv env midx md) - if midx <> idx2 then failwith "index of method def on pass 3 does not match index on pass 2" - GenReturnPass3 cenv md.Return - md.Parameters |> List.iteri (fun n param -> GenParamPass3 cenv env (n+1) param) - md.CustomAttrs |> GenCustomAttrsPass3Or4 cenv (hca_MethodDef, midx) - md.SecurityDecls.AsList |> GenSecurityDeclsPass3 cenv (hds_MethodDef, midx) - md.GenericParams |> List.iteri (fun n gp -> GenGenericParamPass3 cenv env n (tomd_MethodDef, midx) gp) - match md.Body with - | MethodBody.PInvoke attrLazy -> - let attr = attrLazy.Value - let flags = - begin match attr.CallingConv with - | PInvokeCallingConvention.None -> 0x0000 - | PInvokeCallingConvention.Cdecl -> 0x0200 - | PInvokeCallingConvention.Stdcall -> 0x0300 - | PInvokeCallingConvention.Thiscall -> 0x0400 - | PInvokeCallingConvention.Fastcall -> 0x0500 - | PInvokeCallingConvention.WinApi -> 0x0100 - end ||| - begin match attr.CharEncoding with - | PInvokeCharEncoding.None -> 0x0000 - | PInvokeCharEncoding.Ansi -> 0x0002 - | PInvokeCharEncoding.Unicode -> 0x0004 - | PInvokeCharEncoding.Auto -> 0x0006 - end ||| - begin match attr.CharBestFit with - | PInvokeCharBestFit.UseAssembly -> 0x0000 - | PInvokeCharBestFit.Enabled -> 0x0010 - | PInvokeCharBestFit.Disabled -> 0x0020 - end ||| - begin match attr.ThrowOnUnmappableChar with - | PInvokeThrowOnUnmappableChar.UseAssembly -> 0x0000 - | PInvokeThrowOnUnmappableChar.Enabled -> 0x1000 - | PInvokeThrowOnUnmappableChar.Disabled -> 0x2000 - end ||| - (if attr.NoMangle then 0x0001 else 0x0000) ||| - (if attr.LastError then 0x0040 else 0x0000) - AddUnsharedRow cenv TableNames.ImplMap - (UnsharedRow - [| UShort (uint16 flags) - MemberForwarded (mf_MethodDef, midx) - StringE (GetStringHeapIdx cenv attr.Name) - SimpleIndex (TableNames.ModuleRef, GetModuleRefAsIdx cenv attr.Where) |]) |> ignore - | _ -> () + if canGenMethodDef cenv md then + let midx = GetMethodDefIdx cenv md + let idx2 = AddUnsharedRow cenv TableNames.Method (GenMethodDefAsRow cenv env midx md) + if midx <> idx2 then failwith "index of method def on pass 3 does not match index on pass 2" + GenReturnPass3 cenv md.Return + md.Parameters |> List.iteri (fun n param -> GenParamPass3 cenv env (n+1) param) + md.CustomAttrs |> GenCustomAttrsPass3Or4 cenv (hca_MethodDef, midx) + md.SecurityDecls.AsList |> GenSecurityDeclsPass3 cenv (hds_MethodDef, midx) + md.GenericParams |> List.iteri (fun n gp -> GenGenericParamPass3 cenv env n (tomd_MethodDef, midx) gp) + match md.Body with + | MethodBody.PInvoke attrLazy -> + let attr = attrLazy.Value + let flags = + begin match attr.CallingConv with + | PInvokeCallingConvention.None -> 0x0000 + | PInvokeCallingConvention.Cdecl -> 0x0200 + | PInvokeCallingConvention.Stdcall -> 0x0300 + | PInvokeCallingConvention.Thiscall -> 0x0400 + | PInvokeCallingConvention.Fastcall -> 0x0500 + | PInvokeCallingConvention.WinApi -> 0x0100 + end ||| + begin match attr.CharEncoding with + | PInvokeCharEncoding.None -> 0x0000 + | PInvokeCharEncoding.Ansi -> 0x0002 + | PInvokeCharEncoding.Unicode -> 0x0004 + | PInvokeCharEncoding.Auto -> 0x0006 + end ||| + begin match attr.CharBestFit with + | PInvokeCharBestFit.UseAssembly -> 0x0000 + | PInvokeCharBestFit.Enabled -> 0x0010 + | PInvokeCharBestFit.Disabled -> 0x0020 + end ||| + begin match attr.ThrowOnUnmappableChar with + | PInvokeThrowOnUnmappableChar.UseAssembly -> 0x0000 + | PInvokeThrowOnUnmappableChar.Enabled -> 0x1000 + | PInvokeThrowOnUnmappableChar.Disabled -> 0x2000 + end ||| + (if attr.NoMangle then 0x0001 else 0x0000) ||| + (if attr.LastError then 0x0040 else 0x0000) + AddUnsharedRow cenv TableNames.ImplMap + (UnsharedRow + [| UShort (uint16 flags) + MemberForwarded (mf_MethodDef, midx) + StringE (GetStringHeapIdx cenv attr.Name) + SimpleIndex (TableNames.ModuleRef, GetModuleRefAsIdx cenv attr.Where) |]) |> ignore + | _ -> () let GenMethodDefPass4 cenv env md = - let midx = GetMethodDefIdx cenv md - List.iteri (fun n gp -> GenGenericParamPass4 cenv env n (tomd_MethodDef, midx) gp) md.GenericParams + if canGenMethodDef cenv md then + let midx = GetMethodDefIdx cenv md + List.iteri (fun n gp -> GenGenericParamPass4 cenv env n (tomd_MethodDef, midx) gp) md.GenericParams let GenPropertyMethodSemanticsPass3 cenv pidx kind mref = // REVIEW: why are we catching exceptions here? @@ -2939,9 +2964,27 @@ let DataCapacity = 200 [] let ResourceCapacity = 200 -let generateIL requiredDataFixups (desiredMetadataVersion, generatePdb, ilg : ILGlobals, emitTailcalls, deterministic, showTimes) (m : ILModuleDef) cilStartAddress normalizeAssemblyRefs = +let generateIL requiredDataFixups (desiredMetadataVersion, generatePdb, ilg : ILGlobals, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt: ILAttribute option) (m : ILModuleDef) cilStartAddress normalizeAssemblyRefs = let isDll = m.IsDLL + let hasInternalsVisibleToAttrib = + m.CustomAttrs.AsArray + |> Array.exists (fun x -> + x.Method.MethodRef.Name = "InternalsVisibleToAttribute" && + x.Method.MethodRef.DeclaringTypeRef.FullName = "System.Runtime.CompilerServices" + ) + + let m = + // Emit System.Runtime.CompilerServices.ReferenceAssemblyAttribute as an assembly-level attribute when generating a reference assembly. + // Useful for the runtime to know that the assembly is a reference assembly. + match referenceAssemblyAttribOpt with + | Some referenceAssemblyAttrib when referenceAssemblyOnly -> + { m with + CustomAttrsStored = + mkILCustomAttrsReader (fun _ -> Array.append [|referenceAssemblyAttrib|] m.CustomAttrs.AsArray) } + | _ -> + m + let tables = Array.init 64 (fun i -> if (i = TableNames.AssemblyRef.Index || @@ -2988,7 +3031,9 @@ let generateIL requiredDataFixups (desiredMetadataVersion, generatePdb, ilg : IL blobs= MetadataTable<_>.New("blobs", HashIdentity.Structural) strings= MetadataTable<_>.New("strings", EqualityComparer.Default) userStrings= MetadataTable<_>.New("user strings", EqualityComparer.Default) - normalizeAssemblyRefs = normalizeAssemblyRefs + normalizeAssemblyRefs = normalizeAssemblyRefs + hasInternalsVisibleToAttrib = hasInternalsVisibleToAttrib + referenceAssemblyOnly = referenceAssemblyOnly pdbImports = Dictionary<_, _>(HashIdentity.Reference) } // Now the main compilation step @@ -3090,7 +3135,7 @@ let TableCapacity = 20000 [] let MetadataCapacity = 500000 -let writeILMetadataAndCode (generatePdb, desiredMetadataVersion, ilg, emitTailcalls, deterministic, showTimes) modul cilStartAddress normalizeAssemblyRefs = +let writeILMetadataAndCode (generatePdb, desiredMetadataVersion, ilg, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt) modul cilStartAddress normalizeAssemblyRefs = // When we know the real RVAs of the data section we fixup the references for the FieldRVA table. // These references are stored as offsets into the metadata we return from this function @@ -3099,7 +3144,7 @@ let writeILMetadataAndCode (generatePdb, desiredMetadataVersion, ilg, emitTailca let next = cilStartAddress let strings, userStrings, blobs, guids, tables, entryPointToken, code, requiredStringFixups, data, resources, pdbData, mappings = - generateIL requiredDataFixups (desiredMetadataVersion, generatePdb, ilg, emitTailcalls, deterministic, showTimes) modul cilStartAddress normalizeAssemblyRefs + generateIL requiredDataFixups (desiredMetadataVersion, generatePdb, ilg, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt) modul cilStartAddress normalizeAssemblyRefs reportTime showTimes "Generated Tables and Code" let tableSize (tab: TableName) = tables.[tab.Index].Count @@ -3558,7 +3603,7 @@ let rec writeBinaryAndReportMappings (outfile, let pdbData, pdbOpt, debugDirectoryChunk, debugDataChunk, debugChecksumPdbChunk, debugEmbeddedPdbChunk, debugDeterministicPdbChunk, textV2P, mappings = try let res = writeBinaryAndReportMappingsAux(stream, false, ilg, pdbfile, signer, portablePDB, embeddedPDB, embedAllSource, embedSourceList, sourceLink, - checksumAlgorithm, emitTailcalls, deterministic, showTimes, pathMap) modul normalizeAssemblyRefs + checksumAlgorithm, emitTailcalls, deterministic, showTimes, false, None, pathMap) modul normalizeAssemblyRefs try FileSystemUtilities.setExecutablePermission outfile @@ -3577,16 +3622,16 @@ let rec writeBinaryAndReportMappings (outfile, and writeBinaryWithNoPdb (stream: Stream, ilg: ILGlobals, signer: ILStrongNameSigner option, portablePDB, embeddedPDB, - embedAllSource, embedSourceList, sourceLink, checksumAlgorithm, emitTailcalls, deterministic, showTimes, pathMap) + embedAllSource, embedSourceList, sourceLink, checksumAlgorithm, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt, pathMap) modul normalizeAssemblyRefs = writeBinaryAndReportMappingsAux(stream, true, ilg, None, signer, portablePDB, embeddedPDB, embedAllSource, embedSourceList, sourceLink, - checksumAlgorithm, emitTailcalls, deterministic, showTimes, pathMap) modul normalizeAssemblyRefs + checksumAlgorithm, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt, pathMap) modul normalizeAssemblyRefs |> ignore and writeBinaryAndReportMappingsAux (stream: Stream, leaveStreamOpen: bool, ilg: ILGlobals, pdbfile: string option, signer: ILStrongNameSigner option, portablePDB, embeddedPDB, - embedAllSource, embedSourceList, sourceLink, checksumAlgorithm, emitTailcalls, deterministic, showTimes, pathMap) + embedAllSource, embedSourceList, sourceLink, checksumAlgorithm, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt, pathMap) modul normalizeAssemblyRefs = // Store the public key from the signer into the manifest. This means it will be written // to the binary and also acts as an indicator to leave space for delay sign @@ -3698,7 +3743,7 @@ and writeBinaryAndReportMappingsAux (stream: Stream, leaveStreamOpen: bool, | None -> failwith "Expected mscorlib to have a version number" let entryPointToken, code, codePadding, metadata, data, resources, requiredDataFixups, pdbData, mappings, guidStart = - writeILMetadataAndCode ((pdbfile <> None), desiredMetadataVersion, ilg, emitTailcalls, deterministic, showTimes) modul next normalizeAssemblyRefs + writeILMetadataAndCode ((pdbfile <> None), desiredMetadataVersion, ilg, emitTailcalls, deterministic, showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt) modul next normalizeAssemblyRefs reportTime showTimes "Generated IL and metadata" let _codeChunk, next = chunk code.Length next @@ -4368,8 +4413,8 @@ let WriteILBinary (filename, options: options, inputModule, normalizeAssemblyRef options.embedSourceList, options.sourceLink, options.checksumAlgorithm, options.emitTailcalls, options.deterministic, options.showTimes, options.dumpDebugInfo, options.pathMap) inputModule normalizeAssemblyRefs |> ignore -let WriteILBinaryStreamWithNoPDB (stream, options: options, inputModule, normalizeAssemblyRefs) = +let WriteILBinaryStreamWithNoPDB (stream, (options: options), referenceAssemblyOnly, referenceAssemblyAttribOpt, inputModule, normalizeAssemblyRefs) = writeBinaryWithNoPdb (stream, options.ilg, options.signer, options.portablePDB, options.embeddedPDB, options.embedAllSource, - options.embedSourceList, options.sourceLink, options.checksumAlgorithm, options.emitTailcalls, options.deterministic, options.showTimes, options.pathMap) inputModule normalizeAssemblyRefs + options.embedSourceList, options.sourceLink, options.checksumAlgorithm, options.emitTailcalls, options.deterministic, options.showTimes, referenceAssemblyOnly, referenceAssemblyAttribOpt, options.pathMap) inputModule normalizeAssemblyRefs |> ignore diff --git a/src/fsharp/absil/ilwrite.fsi b/src/fsharp/absil/ilwrite.fsi index 152c9abc45f..380f94f3b27 100644 --- a/src/fsharp/absil/ilwrite.fsi +++ b/src/fsharp/absil/ilwrite.fsi @@ -29,4 +29,4 @@ type options = val WriteILBinary: filename: string * options: options * inputModule: ILModuleDef * (ILAssemblyRef -> ILAssemblyRef) -> unit /// Write a binary to the given stream. Extra configuration parameters can also be specified. -val WriteILBinaryStreamWithNoPDB: stream: Stream * options: options * inputModule: ILModuleDef * (ILAssemblyRef -> ILAssemblyRef) -> unit \ No newline at end of file +val WriteILBinaryStreamWithNoPDB: stream: Stream * options: options * referenceAssemblyOnly: bool * referenceAssemblyAttribOpt: ILAttribute option * inputModule: ILModuleDef * (ILAssemblyRef -> ILAssemblyRef) -> unit \ No newline at end of file diff --git a/src/fsharp/fsc.fs b/src/fsharp/fsc.fs index 52d6ae9fdfe..2f5b066cd5f 100644 --- a/src/fsharp/fsc.fs +++ b/src/fsharp/fsc.fs @@ -779,13 +779,6 @@ let main3(Args (ctok, tcConfig, tcImports, frameworkTcImports: TcImports, tcGlob errorRecoveryNoRange e exiter.Exit 1 - // Perform optimization - use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Optimize - - let optEnv0 = GetInitialOptimizationEnv (tcImports, tcGlobals) - - let importMap = tcImports.GetImportMap() - let metadataVersion = match tcConfig.metadataVersion with | Some v -> v @@ -793,18 +786,35 @@ let main3(Args (ctok, tcConfig, tcImports, frameworkTcImports: TcImports, tcGlob match frameworkTcImports.DllTable.TryFind tcConfig.primaryAssembly.Name with | Some ib -> ib.RawMetadata.TryGetILModuleDef().Value.MetadataVersion | _ -> "" + + let optimizedImpls, optDataResources = + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.MetadataOnly + | MetadataAssemblyGeneration.TestSigOfImpl -> + let optimizedImpls = + typedImplFiles + |> List.map (fun x -> { ImplFile = x; OptimizeDuringCodeGen = (fun _ expr -> expr) }) + |> TypedAssemblyAfterOptimization + optimizedImpls, [] + | _ -> + // Perform optimization + use unwindBuildPhase = PushThreadBuildPhaseUntilUnwind BuildPhase.Optimize + + let optEnv0 = GetInitialOptimizationEnv (tcImports, tcGlobals) - let optimizedImpls, optimizationData, _ = - ApplyAllOptimizations - (tcConfig, tcGlobals, (LightweightTcValForUsingInBuildMethodCall tcGlobals), outfile, - importMap, false, optEnv0, generatedCcu, typedImplFiles) + let importMap = tcImports.GetImportMap() - AbortOnError(errorLogger, exiter) + let optimizedImpls, optimizationData, _ = + ApplyAllOptimizations + (tcConfig, tcGlobals, (LightweightTcValForUsingInBuildMethodCall tcGlobals), outfile, + importMap, false, optEnv0, generatedCcu, typedImplFiles) - // Encode the optimization data - ReportTime tcConfig "Encoding OptData" + AbortOnError(errorLogger, exiter) + + // Encode the optimization data + ReportTime tcConfig ("Encoding OptData") - let optDataResources = EncodeOptimizationData(tcGlobals, tcConfig, outfile, exportRemapping, (generatedCcu, optimizationData), false) + optimizedImpls, EncodeOptimizationData(tcGlobals, tcConfig, outfile, exportRemapping, (generatedCcu, optimizationData), false) // Pass on only the minimum information required for the next phase Args (ctok, tcConfig, tcImports, tcGlobals, errorLogger, @@ -905,28 +915,81 @@ let main6 dynamicAssemblyCreator (Args (ctok, tcConfig, tcImports: TcImports, t match dynamicAssemblyCreator with | None -> try - try - ILBinaryWriter.WriteILBinary - (outfile, - { ilg = tcGlobals.ilg - pdbfile=pdbfile - emitTailcalls = tcConfig.emitTailcalls - deterministic = tcConfig.deterministic - showTimes = tcConfig.showTimes - portablePDB = tcConfig.portablePDB - embeddedPDB = tcConfig.embeddedPDB - embedAllSource = tcConfig.embedAllSource - embedSourceList = tcConfig.embedSourceList - sourceLink = tcConfig.sourceLink - checksumAlgorithm = tcConfig.checksumAlgorithm - signer = GetStrongNameSigner signingInfo - dumpDebugInfo = tcConfig.dumpDebugInfo - pathMap = tcConfig.pathMap }, - ilxMainModule, - normalizeAssemblyRefs - ) - with Failure msg -> - error(Error(FSComp.SR.fscProblemWritingBinary(outfile, msg), rangeCmdArgs)) + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.None -> () + | _ -> + let outfile = + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.ReferenceOut outputPath -> + outputPath + | _ -> + outfile + let referenceAssemblyAttribOpt = + tcGlobals.iltyp_ReferenceAssemblyAttributeOpt + |> Option.map (fun ilTy -> + mkILCustomAttribute (ilTy.TypeRef, [], [], []) + ) + try + use stream = + try + // Ensure the output directory exists otherwise it will fail + let dir = FileSystem.GetDirectoryNameShim outfile + if not (FileSystem.DirectoryExistsShim dir) then FileSystem.DirectoryCreateShim dir |> ignore + FileSystem.OpenFileForWriteShim(outfile, FileMode.Create, FileAccess.Write, FileShare.Read) + with _ -> + failwith ("Could not open file for writing (binary mode): " + outfile) + + ILBinaryWriter.WriteILBinaryStreamWithNoPDB + (stream, + { ilg = tcGlobals.ilg + pdbfile=pdbfile + emitTailcalls = tcConfig.emitTailcalls + deterministic = tcConfig.deterministic + showTimes = tcConfig.showTimes + portablePDB = tcConfig.portablePDB + embeddedPDB = tcConfig.embeddedPDB + embedAllSource = tcConfig.embedAllSource + embedSourceList = tcConfig.embedSourceList + sourceLink = tcConfig.sourceLink + checksumAlgorithm = tcConfig.checksumAlgorithm + signer = GetStrongNameSigner signingInfo + dumpDebugInfo = tcConfig.dumpDebugInfo + pathMap = tcConfig.pathMap }, + true, + referenceAssemblyAttribOpt, + ilxMainModule, + normalizeAssemblyRefs + ) + with Failure msg -> + error(Error(FSComp.SR.fscProblemWritingBinary(outfile, msg), rangeCmdArgs)) + + match tcConfig.emitMetadataAssembly with + | MetadataAssemblyGeneration.MetadataOnly + | MetadataAssemblyGeneration.TestSigOfImpl + | MetadataAssemblyGeneration.ReferenceOnly -> () + | _ -> + try + ILBinaryWriter.WriteILBinary + (outfile, + { ilg = tcGlobals.ilg + pdbfile=pdbfile + emitTailcalls = tcConfig.emitTailcalls + deterministic = tcConfig.deterministic + showTimes = tcConfig.showTimes + portablePDB = tcConfig.portablePDB + embeddedPDB = tcConfig.embeddedPDB + embedAllSource = tcConfig.embedAllSource + embedSourceList = tcConfig.embedSourceList + sourceLink = tcConfig.sourceLink + checksumAlgorithm = tcConfig.checksumAlgorithm + signer = GetStrongNameSigner signingInfo + dumpDebugInfo = tcConfig.dumpDebugInfo + pathMap = tcConfig.pathMap }, + ilxMainModule, + normalizeAssemblyRefs + ) + with Failure msg -> + error(Error(FSComp.SR.fscProblemWritingBinary(outfile, msg), rangeCmdArgs)) with e -> errorRecoveryNoRange e exiter.Exit 1 diff --git a/src/fsharp/service/FSharpCheckerResults.fs b/src/fsharp/service/FSharpCheckerResults.fs index a0d5572617e..73e0e3bd176 100644 --- a/src/fsharp/service/FSharpCheckerResults.fs +++ b/src/fsharp/service/FSharpCheckerResults.fs @@ -15,6 +15,7 @@ open Internal.Utilities.Library open Internal.Utilities.Library.Extras open FSharp.Core.Printf open FSharp.Compiler +open FSharp.Compiler.Syntax open FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.AccessibilityLogic open FSharp.Compiler.CheckExpressions @@ -46,6 +47,10 @@ open FSharp.Compiler.Text.Position open FSharp.Compiler.Text.Range open FSharp.Compiler.TypedTree open FSharp.Compiler.TypedTreeOps +open FSharp.Compiler.AbstractIL +open System.Reflection.PortableExecutable +open FSharp.Compiler.CreateILModule +open FSharp.Compiler.IlxGen open FSharp.Compiler.BuildGraph open Internal.Utilities @@ -2187,7 +2192,7 @@ type FSharpCheckProjectResults keepAssemblyContents: bool, diagnostics: FSharpDiagnostic[], details:(TcGlobals * TcImports * CcuThunk * ModuleOrNamespaceType * Choice * - TopAttribs option * ILAssemblyRef * + TopAttribs option * (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * TypedImplFile list option * string[] * FSharpProjectOptions) option) = let getDetails() = @@ -2205,12 +2210,12 @@ type FSharpCheckProjectResults member _.HasCriticalErrors = details.IsNone member _.AssemblySignature = - let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, topAttribs, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() FSharpAssemblySignature(tcGlobals, thisCcu, ccuSig, tcImports, topAttribs, ccuSig) member _.TypedImplementationFiles = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let tcGlobals, tcImports, thisCcu, _ccuSig, _builderOrSymbolUses, _topAttribs, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, _ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2219,7 +2224,7 @@ type FSharpCheckProjectResults member info.AssemblyContents = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2228,7 +2233,7 @@ type FSharpCheckProjectResults member _.GetOptimizedAssemblyContents() = if not keepAssemblyContents then invalidOp "The 'keepAssemblyContents' flag must be set to true on the FSharpChecker in order to access the checked contents of assemblies" - let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, ccuSig, _builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() let mimpls = match tcAssemblyExpr with | None -> [] @@ -2245,9 +2250,142 @@ type FSharpCheckProjectResults FSharpAssemblyContents(tcGlobals, thisCcu, Some ccuSig, tcImports, mimpls) + member _.TryEmitMetadataOnlyAssembly(stream: Stream) = + match tcConfigOption with + | Some tcConfig -> + let ctok = CompilationThreadToken() + + let (tcGlobals, tcImports, thisCcu, ccuSig, _tcSymbolUses, topAttribs, tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions) = getDetails() + + let topAttribs = + match topAttribs with + | Some topAttribs -> topAttribs + | _ -> EmptyTopAttrs + + let signingInfo = ValidateKeySigningAttributes (tcConfig, tcGlobals, topAttribs) + + // Try to find an AssemblyVersion attribute + let assemVerFromAttrib = + match AttributeHelpers.TryFindStringAttribute tcGlobals "System.Reflection.AssemblyVersionAttribute" topAttribs.assemblyAttrs with + | Some v -> + let v = + try + parseILVersion v + |> Some + with + | _ -> + None + match v with + | Some v -> + match tcConfig.version with + | VersionNone -> Some v + | _ -> None + | _ -> + None + | _ -> None + + let outfile = + match tcConfig.outputFile with + | Some outfile -> outfile + | _ -> "" + + let assemblyName = + match tcAssemblyData() with + | Some data -> data.ShortAssemblyName + | _ -> "" + + let optimizedImpls = + [ + CreateDummyTypedImplFile tcGlobals (QualifiedNameOfFile(Ident("", range0))) ccuSig + ] + |> List.map (fun x -> { ImplFile = x; OptimizeDuringCodeGen = (fun _ expr -> expr) }) + |> TypedAssemblyAfterOptimization + + let optDataResources = [] + + let exportRemapping = MakeExportRemapping thisCcu thisCcu.Contents + let sigDataAttributes, sigDataResources = + try + EncodeSignatureData(tcConfig, tcGlobals, exportRemapping, thisCcu, "", (* this makes encoding in-memory *) true) + with _ -> + [], [] + + let metadataVersion = + match tcConfig.metadataVersion with + | Some v -> v + | _ -> "" + + // TAST -> IL + // Create the Abstract IL generator + let ilxGenerator = CreateIlxAssemblyGenerator (tcConfig, tcImports, tcGlobals, (LightweightTcValForUsingInBuildMethodCall tcGlobals), thisCcu) + + let codegenBackend = IlWriteBackend + + // Generate the Abstract IL Code + let codegenResults = GenerateIlxCode (codegenBackend, false, false, tcConfig, topAttribs, optimizedImpls, thisCcu.AssemblyName, ilxGenerator) + + // Build the Abstract IL view of the final main module, prior to static linking + + let topAssemblyAttrs = codegenResults.topAssemblyAttrs + let topAttrs = {topAttribs with assemblyAttrs=topAssemblyAttrs} + let permissionSets = codegenResults.permissionSets + let secDecls = mkILSecurityDecls permissionSets + + let ilxMainModule = + MainModuleBuilder.CreateMainModule + (ctok, tcConfig, tcGlobals, tcImports, + None, assemblyName, outfile, topAttrs, + sigDataAttributes, sigDataResources, optDataResources, + codegenResults, assemVerFromAttrib, metadataVersion, secDecls) + + // Binary Writer + + let normalizeAssemblyRefs (aref: ILAssemblyRef) = + match tcImports.TryFindDllInfo (ctok, Range.rangeStartup, aref.Name, lookupOnly=false) with + | Some dllInfo -> + match dllInfo.ILScopeRef with + | ILScopeRef.Assembly ref -> ref + | _ -> aref + | None -> aref + + let referenceAssemblyAttribOpt = + tcGlobals.iltyp_ReferenceAssemblyAttributeOpt + |> Option.map (fun ilTy -> + mkILCustomAttribute (ilTy.TypeRef, [], [], []) + ) + + try + ILBinaryWriter.WriteILBinaryStreamWithNoPDB + (stream, + { ilg = tcGlobals.ilg + pdbfile=None + emitTailcalls = tcConfig.emitTailcalls + deterministic = tcConfig.deterministic + showTimes = tcConfig.showTimes + portablePDB = tcConfig.portablePDB + embeddedPDB = tcConfig.embeddedPDB + embedAllSource = tcConfig.embedAllSource + embedSourceList = tcConfig.embedSourceList + sourceLink = tcConfig.sourceLink + checksumAlgorithm = tcConfig.checksumAlgorithm + signer = GetStrongNameSigner signingInfo + dumpDebugInfo = tcConfig.dumpDebugInfo + pathMap = tcConfig.pathMap }, + true, + referenceAssemblyAttribOpt, + ilxMainModule, + normalizeAssemblyRefs + ) + + None + with _ -> + None + | _ -> + None + // Not, this does not have to be a SyncOp, it can be called from any thread member _.GetUsesOfSymbol(symbol:FSharpSymbol, ?cancellationToken: CancellationToken) = - let _, _tcImports, _thisCcu, _ccuSig, builderOrSymbolUses, _topAttribs, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let _, _tcImports, _thisCcu, _ccuSig, builderOrSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() let results = match builderOrSymbolUses with @@ -2278,7 +2416,7 @@ type FSharpCheckProjectResults // Not, this does not have to be a SyncOp, it can be called from any thread member _.GetAllUsesOfAllSymbols(?cancellationToken: CancellationToken) = - let tcGlobals, tcImports, thisCcu, ccuSig, builderOrSymbolUses, _topAttribs, _ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, ccuSig, builderOrSymbolUses, _topAttribs, _ilAssemRef, _tcAssemblyData, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() let cenv = SymbolEnv(tcGlobals, thisCcu, Some ccuSig, tcImports) let tcSymbolUses = @@ -2309,18 +2447,18 @@ type FSharpCheckProjectResults yield FSharpSymbolUse(symbolUse.DisplayEnv, symbol, symbolUse.ItemWithInst.TyparInst, symbolUse.ItemOccurence, symbolUse.Range) |] member _.ProjectContext = - let tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _ilAssemRef, ad, _tcAssemblyExpr, _dependencyFiles, projectOptions = getDetails() + let tcGlobals, tcImports, thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, ad, _tcAssemblyExpr, _dependencyFiles, projectOptions = getDetails() let assemblies = tcImports.GetImportedAssemblies() |> List.map (fun x -> FSharpAssembly(tcGlobals, tcImports, x.FSharpViewOfMetadata)) FSharpProjectContext(thisCcu, assemblies, ad, projectOptions) member _.DependencyFiles = - let _tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _ilAssemRef, _ad, _tcAssemblyExpr, dependencyFiles, _projectOptions = getDetails() + let _tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, _ilAssemRef, _ad, _tcAssemblyExpr, dependencyFiles, _projectOptions = getDetails() dependencyFiles member _.AssemblyFullName = - let _tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() + let _tcGlobals, _tcImports, _thisCcu, _ccuSig, _tcSymbolUses, _topAttribs, _tcAssemblyData, ilAssemRef, _ad, _tcAssemblyExpr, _dependencyFiles, _projectOptions = getDetails() ilAssemRef.QualifiedName override _.ToString() = "FSharpCheckProjectResults(" + projectFileName + ")" @@ -2390,7 +2528,7 @@ type FsiInteractiveChecker(legacyReferenceResolver, FSharpCheckProjectResults (filename, Some tcConfig, keepAssemblyContents, errors, Some(tcGlobals, tcImports, tcFileInfo.ThisCcu, tcFileInfo.CcuSigForFile, - (Choice2Of2 tcFileInfo.ScopeSymbolUses), None, mkSimpleAssemblyRef "stdin", + (Choice2Of2 tcFileInfo.ScopeSymbolUses), None, (fun () -> None), mkSimpleAssemblyRef "stdin", tcState.TcEnvFromImpls.AccessRights, None, dependencyFiles, projectOptions)) diff --git a/src/fsharp/service/FSharpCheckerResults.fsi b/src/fsharp/service/FSharpCheckerResults.fsi index bfdca4ae149..0c844e632dd 100644 --- a/src/fsharp/service/FSharpCheckerResults.fsi +++ b/src/fsharp/service/FSharpCheckerResults.fsi @@ -447,6 +447,7 @@ type public FSharpCheckProjectResults = ModuleOrNamespaceType * Choice * TopAttribs option * + (unit -> IRawFSharpAssemblyData option) * ILAssemblyRef * AccessorDomain * TypedImplFile list option * diff --git a/src/fsharp/service/service.fs b/src/fsharp/service/service.fs index 329991dbcb4..5be06278449 100644 --- a/src/fsharp/service/service.fs +++ b/src/fsharp/service/service.fs @@ -809,7 +809,7 @@ type BackgroundCompiler( | None -> return FSharpCheckProjectResults (options.ProjectFileName, None, keepAssemblyContents, creationDiags, None) | Some builder -> - let! tcProj, ilAssemRef, _, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() + let! tcProj, ilAssemRef, tcAssemblyDataOpt, tcAssemblyExprOpt = builder.GetFullCheckResultsAndImplementationsForProject() let errorOptions = tcProj.TcConfig.errorSeverityOptions let fileName = DummyFileNameForRangesWithoutASpecificLocation @@ -824,6 +824,12 @@ type BackgroundCompiler( let diagnostics = [| yield! creationDiags; yield! DiagnosticHelpers.CreateDiagnostics (errorOptions, true, fileName, tcErrors, suggestNamesForErrors) |] + + let getAssemblyData() = + match tcAssemblyDataOpt with + | ProjectAssemblyDataResult.Available data -> Some data + | _ -> None + let results = FSharpCheckProjectResults (options.ProjectFileName, @@ -831,7 +837,7 @@ type BackgroundCompiler( keepAssemblyContents, diagnostics, Some(tcProj.TcGlobals, tcProj.TcImports, tcState.Ccu, tcState.CcuSig, - (Choice1Of2 builder), topAttribs, ilAssemRef, + (Choice1Of2 builder), topAttribs, getAssemblyData, ilAssemRef, tcEnvAtEnd.AccessRights, tcAssemblyExprOpt, Array.ofList tcDependencyFiles, options)) diff --git a/src/fsharp/symbols/Symbols.fs b/src/fsharp/symbols/Symbols.fs index 107d5a37dd7..9f479b3ed10 100644 --- a/src/fsharp/symbols/Symbols.fs +++ b/src/fsharp/symbols/Symbols.fs @@ -33,18 +33,18 @@ type FSharpAccessibility(a:Accessibility, ?isProtected) = match x with | CompPath(ILScopeRef.Local, []) -> true | _ -> false - + let (|Public|Internal|Private|) (TAccess p) = match p with | [] -> Public - | _ when List.forall isInternalCompPath p -> Internal + | _ when List.forall isInternalCompPath p -> Internal | _ -> Private - member _.IsPublic = not isProtected && match a with TAccess [] -> true | _ -> false + member _.IsPublic = not isProtected && (match a with TAccess [] -> true | _ -> false) - member _.IsPrivate = not isProtected && match a with Private -> true | _ -> false + member _.IsPrivate = not isProtected && (match a with Private -> true | _ -> false) - member _.IsInternal = not isProtected && match a with Internal -> true | _ -> false + member _.IsInternal = not isProtected && (match a with Internal -> true | _ -> false) member _.IsProtected = isProtected diff --git a/src/fsharp/xlf/FSComp.txt.cs.xlf b/src/fsharp/xlf/FSComp.txt.cs.xlf index 3eb83581fb4..650c728d9a6 100644 --- a/src/fsharp/xlf/FSComp.txt.cs.xlf +++ b/src/fsharp/xlf/FSComp.txt.cs.xlf @@ -347,11 +347,31 @@ Vytiskněte odvozená rozhraní všech kompilovaných souborů do přidružených souborů podpisu. + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Zobrazte si povolené hodnoty verze jazyka a pak zadejte požadovanou verzi, například latest nebo preview. + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Podporované jazykové verze: diff --git a/src/fsharp/xlf/FSComp.txt.de.xlf b/src/fsharp/xlf/FSComp.txt.de.xlf index 3baeb1d5559..b84ad9d10a7 100644 --- a/src/fsharp/xlf/FSComp.txt.de.xlf +++ b/src/fsharp/xlf/FSComp.txt.de.xlf @@ -347,11 +347,31 @@ Drucken der abgeleiteten Schnittstellen aller Dateien an zugehörige Signaturdateien + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Zeigen Sie die zulässigen Werte für die Sprachversion an. Geben Sie die Sprachversion als "latest" oder "preview" an. + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Unterstützte Sprachversionen: diff --git a/src/fsharp/xlf/FSComp.txt.es.xlf b/src/fsharp/xlf/FSComp.txt.es.xlf index cfb14c1fc30..3909830b8d4 100644 --- a/src/fsharp/xlf/FSComp.txt.es.xlf +++ b/src/fsharp/xlf/FSComp.txt.es.xlf @@ -347,11 +347,31 @@ Imprimir las interfaces deducidas de todos los archivos de compilación en los archivos de signatura asociados + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Mostrar los valores permitidos para la versión de idioma, especificar la versión de idioma como "latest" "preview" + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Versiones de lenguaje admitidas: diff --git a/src/fsharp/xlf/FSComp.txt.fr.xlf b/src/fsharp/xlf/FSComp.txt.fr.xlf index 277267d2ce4..a6ebb875eba 100644 --- a/src/fsharp/xlf/FSComp.txt.fr.xlf +++ b/src/fsharp/xlf/FSComp.txt.fr.xlf @@ -347,11 +347,31 @@ Imprimer les interfaces inférées de tous les fichiers de compilation sur les fichiers de signature associés + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Afficher les valeurs autorisées pour la version du langage, spécifier la version du langage comme 'dernière' ou 'préversion' + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Versions linguistiques prises en charge : diff --git a/src/fsharp/xlf/FSComp.txt.it.xlf b/src/fsharp/xlf/FSComp.txt.it.xlf index 9274bb675b5..9743f1ad6fb 100644 --- a/src/fsharp/xlf/FSComp.txt.it.xlf +++ b/src/fsharp/xlf/FSComp.txt.it.xlf @@ -347,11 +347,31 @@ Stampare le interfacce derivate di tutti i file di compilazione nei file di firma associati + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Visualizza i valori consentiti per la versione del linguaggio. Specificare la versione del linguaggio, ad esempio 'latest' o 'preview' + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Versioni del linguaggio supportate: diff --git a/src/fsharp/xlf/FSComp.txt.ja.xlf b/src/fsharp/xlf/FSComp.txt.ja.xlf index 13643a967ad..895cb22c174 100644 --- a/src/fsharp/xlf/FSComp.txt.ja.xlf +++ b/src/fsharp/xlf/FSComp.txt.ja.xlf @@ -347,11 +347,31 @@ すべてのコンパイル ファイルの推定されたインターフェイスを関連する署名ファイルに印刷します + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' 言語バージョンで許可された値を表示し、'最新' や 'プレビュー' などの言語バージョンを指定する + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: サポートされる言語バージョン: diff --git a/src/fsharp/xlf/FSComp.txt.ko.xlf b/src/fsharp/xlf/FSComp.txt.ko.xlf index 58c636dfddc..cbf3719d78d 100644 --- a/src/fsharp/xlf/FSComp.txt.ko.xlf +++ b/src/fsharp/xlf/FSComp.txt.ko.xlf @@ -347,11 +347,31 @@ 모든 컴파일 파일의 유추된 인터페이스를 관련 서명 파일로 인쇄합니다. + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' 언어 버전의 허용된 값을 표시하고 '최신' 또는 '미리 보기'와 같은 언어 버전을 지정합니다. + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: 지원되는 언어 버전: diff --git a/src/fsharp/xlf/FSComp.txt.pl.xlf b/src/fsharp/xlf/FSComp.txt.pl.xlf index c31e0b8a2a9..fd114461ef7 100644 --- a/src/fsharp/xlf/FSComp.txt.pl.xlf +++ b/src/fsharp/xlf/FSComp.txt.pl.xlf @@ -347,11 +347,31 @@ Drukowanie wywnioskowanych interfejsów wszystkich plików kompilacji do skojarzonych plików sygnatur + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Wyświetl dozwolone wartości dla wersji językowej; określ wersję językową, np. „latest” lub „preview” + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Obsługiwane wersje językowe: diff --git a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf index 29ca9a45876..32369f75b43 100644 --- a/src/fsharp/xlf/FSComp.txt.pt-BR.xlf +++ b/src/fsharp/xlf/FSComp.txt.pt-BR.xlf @@ -347,11 +347,31 @@ Imprimir as interfaces inferidas de todos os arquivos de compilação para os arquivos de assinatura associados + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Exibe os valores permitidos para a versão do idioma, especifica a versão do idioma, como 'mais recente ' ou 'prévia' + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Versões de linguagens com suporte: diff --git a/src/fsharp/xlf/FSComp.txt.ru.xlf b/src/fsharp/xlf/FSComp.txt.ru.xlf index e8a6833c7f2..120bad9da04 100644 --- a/src/fsharp/xlf/FSComp.txt.ru.xlf +++ b/src/fsharp/xlf/FSComp.txt.ru.xlf @@ -347,11 +347,31 @@ Печать определяемых интерфейсов всех файлов компиляции в связанные файлы подписей + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Отображение допустимых значений для версии языка. Укажите версию языка, например, "latest" или "preview". + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Поддерживаемые языковые версии: diff --git a/src/fsharp/xlf/FSComp.txt.tr.xlf b/src/fsharp/xlf/FSComp.txt.tr.xlf index a6537d19072..13518bdede8 100644 --- a/src/fsharp/xlf/FSComp.txt.tr.xlf +++ b/src/fsharp/xlf/FSComp.txt.tr.xlf @@ -347,11 +347,31 @@ Tüm derleme dosyalarının çıkarsanan arabirimlerini ilişkili imza dosyalarına yazdır + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' Dil sürümü için izin verilen değerleri görüntüleyin, dil sürümünü 'en son' veya 'önizleme' örneklerindeki gibi belirtin + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: Desteklenen dil sürümleri: diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf index 2fa933cd50f..8fffe57c5e5 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hans.xlf @@ -347,11 +347,31 @@ 将所有编译文件的推断接口打印到关联的签名文件 + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' 显示语言版本的允许值,指定语言版本,如“最新”或“预览” + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: 支持的语言版本: diff --git a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf index 620b3cbf53a..af977a7b5b7 100644 --- a/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf +++ b/src/fsharp/xlf/FSComp.txt.zh-Hant.xlf @@ -347,11 +347,31 @@ 將所有編譯檔案的推斷介面列印至相關聯的簽章檔案 + + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + Invalid use of emitting a reference assembly. Check the compiler options to not specify static linking, or using '--refonly' and '--refout' together. + + + + Invalid reference assembly path' + Invalid reference assembly path' + + Display the allowed values for language version, specify language version such as 'latest' or 'preview' 顯示語言版本允許的值,指定 'latest' 或 'preview' 等語言版本 + + Produce a reference assembly, instead of a full assembly, as the primary output + Produce a reference assembly, instead of a full assembly, as the primary output + + + + Produce a reference assembly with the specified file path. + Produce a reference assembly with the specified file path. + + Supported language versions: 支援的語言版本: diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj index f08d6de964e..72d4fbd3eaa 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.Tests.fsproj @@ -2,8 +2,7 @@ Exe - net472;net5.0 - net5.0 + net472;net5.0 $(NoWarn);44;75; true false @@ -72,7 +71,7 @@ ParserTests.fs - + Program.fs @@ -80,7 +79,7 @@ - + - + \ No newline at end of file diff --git a/tests/FSharp.Test.Utilities/Compiler.fs b/tests/FSharp.Test.Utilities/Compiler.fs index 511b0db5a1e..d5593b7406f 100644 --- a/tests/FSharp.Test.Utilities/Compiler.fs +++ b/tests/FSharp.Test.Utilities/Compiler.fs @@ -16,6 +16,9 @@ open System.Collections.Immutable open System.IO open System.Text open System.Text.RegularExpressions +open System.Reflection +open System.Reflection.Metadata +open System.Reflection.PortableExecutable module rec Compiler = @@ -44,7 +47,8 @@ module rec Compiler = SourceKind: SourceKind Name: string option IgnoreWarnings: bool - References: CompilationUnit list } + References: CompilationUnit list + CompileDirectory: string option } override this.ToString() = match this.Name with | Some n -> n | _ -> (sprintf "%A" this) type CSharpCompilationSource = @@ -110,14 +114,15 @@ module rec Compiler = match source with | null -> failwith "Source cannot be null" | _ -> - { Source = Text source - Baseline = None - Options = defaultOptions - OutputType = Library - SourceKind = kind - Name = None - IgnoreWarnings = false - References = [] } + { Source = Text source + Baseline = None + Options = defaultOptions + OutputType = Library + SourceKind = kind + Name = None + IgnoreWarnings = false + References = [] + CompileDirectory = None } let private csFromString (source: string) : CSharpCompilationSource = match source with @@ -168,6 +173,20 @@ module rec Compiler = let FSharp (source: string) : CompilationUnit = fsFromString source SourceKind.Fs |> FS + let FSharpWithInputAndOutputPath (inputFilePath: string) (outputFilePath: string) : CompilationUnit = + let compileDirectory = Path.GetDirectoryName(outputFilePath) + let name = Path.GetFileName(outputFilePath) + { Source = Path(inputFilePath) + Baseline = None + Options = defaultOptions + OutputType = Library + SourceKind = SourceKind.Fs + Name = Some name + IgnoreWarnings = false + References = [] + CompileDirectory = Some compileDirectory } + |> FS + let CSharp (source: string) : CompilationUnit = csFromString source |> CS @@ -305,7 +324,12 @@ module rec Compiler = let references = processReferences fsSource.References - let compilation = Compilation.Create(source, sourceKind, output, options, references) + let compilation = + match fsSource.CompileDirectory with + | Some compileDirectory -> + Compilation.Create(source, sourceKind, output, options, references, compileDirectory) + | _ -> + Compilation.Create(source, sourceKind, output, options, references) compileFSharpCompilation compilation fsSource.IgnoreWarnings @@ -365,6 +389,27 @@ module rec Compiler = | CS cs -> compileCSharp cs | _ -> failwith "TODO" + let private getAssemblyInBytes (result: TestResult) = + match result with + | Success output -> + match output.OutputPath with + | Some filePath -> File.ReadAllBytes(filePath) + | _ -> failwith "Output path not found." + | _ -> + failwith "Compilation has errors." + + let compileGuid (cUnit: CompilationUnit) : Guid = + let bytes = + compile cUnit + |> shouldSucceed + |> getAssemblyInBytes + + use reader1 = new PEReader(bytes.ToImmutableArray()) + let reader1 = reader1.GetMetadataReader() + + reader1.GetModuleDefinition().Mvid |> reader1.GetGuid + + let private parseFSharp (fsSource: FSharpCompilationSource) : TestResult = let source = getSource fsSource.Source let parseResults = CompilerAssert.Parse source diff --git a/tests/FSharp.Test.Utilities/CompilerAssert.fs b/tests/FSharp.Test.Utilities/CompilerAssert.fs index 53fc3060bca..d8a24126832 100644 --- a/tests/FSharp.Test.Utilities/CompilerAssert.fs +++ b/tests/FSharp.Test.Utilities/CompilerAssert.fs @@ -61,12 +61,12 @@ type CompilationReference = static member Create(cmpl: TestCompilation) = TestCompilationReference cmpl -and Compilation = private Compilation of source: string * SourceKind * CompileOutput * options: string[] * CompilationReference list * name: string option with +and Compilation = private Compilation of source: string * SourceKind * CompileOutput * options: string[] * CompilationReference list * name: string option * compileDirectory: string option with - static member Create(source, sourceKind, output, ?options, ?cmplRefs, ?name) = + static member Create(source, sourceKind, output, ?options, ?cmplRefs, ?name, ?compileDirectory) = let options = defaultArg options [||] let cmplRefs = defaultArg cmplRefs [] - Compilation(source, sourceKind, output, options, cmplRefs, name) + Compilation(source, sourceKind, output, options, cmplRefs, name, compileDirectory) [] type CompilerAssert private () = @@ -199,7 +199,7 @@ type CompilerAssert private () = static let rec compileCompilationAux outputPath (disposals: ResizeArray) ignoreWarnings (cmpl: Compilation) : (FSharpDiagnostic[] * string) * string list = let compilationRefs, deps = match cmpl with - | Compilation(_, _, _, _, cmpls, _) -> + | Compilation(_, _, _, _, cmpls, _, _) -> let compiledRefs = cmpls |> List.map (fun cmpl -> @@ -240,29 +240,29 @@ type CompilerAssert private () = let isScript = match cmpl with - | Compilation(_, kind, _, _, _, _) -> + | Compilation(_, kind, _, _, _, _, _) -> match kind with | Fs -> false | Fsx -> true let isExe = match cmpl with - | Compilation(_, _, output, _, _, _) -> + | Compilation(_, _, output, _, _, _, _) -> match output with | Library -> false | Exe -> true let source = match cmpl with - | Compilation(source, _, _, _, _, _) -> source + | Compilation(source, _, _, _, _, _, _) -> source let options = match cmpl with - | Compilation(_, _, _, options, _, _) -> options + | Compilation(_, _, _, options, _, _, _) -> options let nameOpt = match cmpl with - | Compilation(_, _, _, _, _, nameOpt) -> nameOpt + | Compilation(_, _, _, _, _, nameOpt, _) -> nameOpt let disposal, res = compileDisposable outputPath isScript isExe (Array.append options compilationRefs) nameOpt source disposals.Add disposal @@ -276,7 +276,14 @@ type CompilerAssert private () = res, (deps @ deps2) static let rec compileCompilation ignoreWarnings (cmpl: Compilation) f = - let compileDirectory = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.GetRandomFileName()) + let compileDirectory = + match cmpl with + | Compilation(compileDirectory=compileDirectory) -> + match compileDirectory with + | None -> + CompilerAssert.GenerateDllOutputPath() + | Some compileDirectory -> + compileDirectory let disposals = ResizeArray() try Directory.CreateDirectory(compileDirectory) |> ignore @@ -290,7 +297,14 @@ type CompilerAssert private () = // The reason behind is so we can compose verification of test runs easier. // TODO: We must not rely on the filesystem when compiling static let rec returnCompilation (cmpl: Compilation) ignoreWarnings = - let compileDirectory = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.GetRandomFileName()) + let compileDirectory = + match cmpl with + | Compilation(compileDirectory=compileDirectory) -> + match compileDirectory with + | None -> + CompilerAssert.GenerateDllOutputPath() + | Some compileDirectory -> + compileDirectory Directory.CreateDirectory(compileDirectory) |> ignore compileCompilationAux compileDirectory (ResizeArray()) ignoreWarnings cmpl @@ -355,6 +369,9 @@ type CompilerAssert private () = static member DefaultProjectOptions = defaultProjectOptions + static member GenerateFsInputPath() = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.ChangeExtension(Path.GetRandomFileName(), ".fs")) + static member GenerateDllOutputPath() = Path.Combine(Path.GetTempPath(), "CompilerAssert", Path.ChangeExtension(Path.GetRandomFileName(), ".dll")) + static member CompileWithErrors(cmpl: Compilation, expectedErrors, ?ignoreWarnings) = let ignoreWarnings = defaultArg ignoreWarnings false compileCompilation ignoreWarnings cmpl (fun ((errors, _), _) -> diff --git a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj index e1d3a54ed4f..07250b365ad 100644 --- a/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj +++ b/tests/FSharp.Test.Utilities/FSharp.Test.Utilities.fsproj @@ -1,7 +1,7 @@  - net472;net5.0 + net472;net5.0 net5.0 win-x86;win-x64;linux-x64;osx-x64 $(AssetTargetFallback);portable-net45+win8+wp8+wpa81 diff --git a/tests/FSharp.Test.Utilities/Xunit/Attributes/DirectoryAttribute.fs b/tests/FSharp.Test.Utilities/Xunit/Attributes/DirectoryAttribute.fs index facf0674bd7..c44f1bd9f73 100644 --- a/tests/FSharp.Test.Utilities/Xunit/Attributes/DirectoryAttribute.fs +++ b/tests/FSharp.Test.Utilities/Xunit/Attributes/DirectoryAttribute.fs @@ -48,7 +48,8 @@ type DirectoryAttribute(dir: string) = SourceKind = SourceKind.Fsx Name = Some fs IgnoreWarnings = false - References = [] } |> FS + References = [] + CompileDirectory = None } |> FS member _.Includes with get() = includes and set v = includes <- v diff --git a/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs new file mode 100644 index 00000000000..0ded6dd6104 --- /dev/null +++ b/tests/fsharp/Compiler/CodeGen/EmittedIL/DeterministicTests.fs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests.CodeGen.EmittedIL + +open System.IO +open FSharp.Test +open FSharp.Test.Compiler +open NUnit.Framework + +[] +module DeterministicTests = + + [] + let ``Simple assembly should be deterministic``() = + let inputFilePath = CompilerAssert.GenerateFsInputPath() + let outputFilePath = CompilerAssert.GenerateDllOutputPath() + let src = + """ +module Assembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + File.WriteAllText(inputFilePath, src) + + let mvid1 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--deterministic"] + |> compileGuid + let mvid2 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--deterministic"] + |> compileGuid + + // Two identical compilations should produce the same MVID + Assert.AreEqual(mvid1, mvid2) + + [] + let ``Simple assembly with different platform should not be deterministic``() = + let inputFilePath = CompilerAssert.GenerateFsInputPath() + let outputFilePath = CompilerAssert.GenerateDllOutputPath() + let src = + """ +module Assembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + File.WriteAllText(inputFilePath, src) + + let mvid1 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--deterministic"] + |> compileGuid + let mvid2 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--deterministic";"--platform:Itanium"] + |> compileGuid + + // No two platforms should produce the same MVID + Assert.AreNotEqual(mvid1, mvid2) + + [] + let ``Simple reference assembly should be deterministic``() = + let inputFilePath = CompilerAssert.GenerateFsInputPath() + let outputFilePath = CompilerAssert.GenerateDllOutputPath() + let src = + """ +module ReferenceAssembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + File.WriteAllText(inputFilePath, src) + + let mvid1 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--refonly";"--deterministic"] + |> compileGuid + let mvid2 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--refonly";"--deterministic"] + |> compileGuid + + // Two identical compilations should produce the same MVID + Assert.AreEqual(mvid1, mvid2) + + [] + let ``Simple reference assembly with different platform should not be deterministic``() = + let inputFilePath = CompilerAssert.GenerateFsInputPath() + let outputFilePath = CompilerAssert.GenerateDllOutputPath() + let src = + """ +module ReferenceAssembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + File.WriteAllText(inputFilePath, src) + + let mvid1 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--refonly";"--deterministic"] + |> compileGuid + let mvid2 = + FSharpWithInputAndOutputPath inputFilePath outputFilePath + |> withOptions ["--refonly";"--deterministic";"--platform:Itanium"] + |> compileGuid + + // No two platforms should produce the same MVID + Assert.AreNotEqual(mvid1, mvid2) diff --git a/tests/fsharp/Compiler/CodeGen/EmittedIL/ReferenceAssemblyTests.fs b/tests/fsharp/Compiler/CodeGen/EmittedIL/ReferenceAssemblyTests.fs new file mode 100644 index 00000000000..3608764b50c --- /dev/null +++ b/tests/fsharp/Compiler/CodeGen/EmittedIL/ReferenceAssemblyTests.fs @@ -0,0 +1,647 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests.CodeGen.EmittedIL + +open FSharp.Test.Compiler +open NUnit.Framework + +[] +module ReferenceAssemblyTests = + + let referenceAssemblyAttributeExpectedIL = + """.custom instance void [runtime]System.Runtime.CompilerServices.ReferenceAssemblyAttribute::.ctor() = ( 01 00 00 00 )""" + + [] + let ``Simple reference assembly should have expected IL``() = + let src = + """ +module ReferenceAssembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--refonly"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void test() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly should have expected IL with dummy typed impl file``() = + let src = + """ +module ReferenceAssembly + +open System + +let test() = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--test:RefOnlyTestSigOfImpl"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void test() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly should have expected IL without a private function``() = + let src = + """ +module ReferenceAssembly + +open System + +let private privTest() = + Console.WriteLine("Private Hello World!") + +let test() = + privTest() + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--refonly"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void test() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly should have expected IL with anonymous record``() = + let src = + """ +module ReferenceAssembly + +open System + +let test(_x: {| a: int32 |}) = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--refonly"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly should have expected IL with anonymous record with dummy typed impl file``() = + let src = + """ +module ReferenceAssembly + +open System + +let test(_x: {| a: int32 |}) = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--test:RefOnlyTestSigOfImpl"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly with nested module should have expected IL``() = + let src = + """ +module ReferenceAssembly + +open System + +module Nested = + + let test() = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--refonly"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class abstract auto ansi sealed nested public Nested + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void test() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly with nested module should have expected IL with dummy typed impl``() = + let src = + """ +module ReferenceAssembly + +open System + +module Nested = + + let test() = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--test:RefOnlyTestSigOfImpl"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class abstract auto ansi sealed nested public Nested + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .method public static void test() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly with nested module with type should have expected IL``() = + let src = + """ +module ReferenceAssembly + +open System + +module Nested = + + type Test = { x: int } + + let test(_x: Test) = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--refonly"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class abstract auto ansi sealed nested public Nested + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class auto ansi serializable sealed nested public Test + extends [runtime]System.Object + implements class [runtime]System.IEquatable`1, + [runtime]System.Collections.IStructuralEquatable, + class [runtime]System.IComparable`1, + [runtime]System.IComparable, + [runtime]System.Collections.IStructuralComparable + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 02 00 00 00 00 00 ) + .field assembly int32 x@ + .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) + .method public hidebysig specialname + instance int32 get_x() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public specialname rtspecialname + instance void .ctor(int32 x) cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public strict virtual instance string + ToString() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(class ReferenceAssembly/Nested/Test obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(object obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(object obj, + class [runtime]System.Collections.IComparer comp) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 GetHashCode(class [runtime]System.Collections.IEqualityComparer comp) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 GetHashCode() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(object obj, + class [runtime]System.Collections.IEqualityComparer comp) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(class ReferenceAssembly/Nested/Test obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(object obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .property instance int32 x() + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32) = ( 01 00 04 00 00 00 00 00 00 00 00 00 ) + .get instance int32 ReferenceAssembly/Nested/Test::get_x() + } + } + + .method public static void test(class ReferenceAssembly/Nested/Test _x) cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore + + [] + let ``Simple reference assembly with nested module with type should have expected IL with dummy typed impl``() = + let src = + """ +module ReferenceAssembly + +open System + +module Nested = + + type Test = { x: int } + + let test(_x: Test) = + Console.WriteLine("Hello World!") + """ + + FSharp src + |> withOptions ["--test:RefOnlyTestSigOfImpl"] + |> compile + |> shouldSucceed + |> verifyIL [ + referenceAssemblyAttributeExpectedIL + """.class public abstract auto ansi sealed ReferenceAssembly + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class abstract auto ansi sealed nested public Nested + extends [runtime]System.Object + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 07 00 00 00 00 00 ) + .class auto ansi serializable sealed nested public Test + extends [runtime]System.Object + implements class [runtime]System.IEquatable`1, + [runtime]System.Collections.IStructuralEquatable, + class [runtime]System.IComparable`1, + [runtime]System.IComparable, + [runtime]System.Collections.IStructuralComparable + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags) = ( 01 00 02 00 00 00 00 00 ) + .field assembly int32 x@ + .custom instance void [runtime]System.Diagnostics.DebuggerBrowsableAttribute::.ctor(valuetype [runtime]System.Diagnostics.DebuggerBrowsableState) = ( 01 00 00 00 00 00 00 00 ) + .method public hidebysig specialname + instance int32 get_x() cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public specialname rtspecialname + instance void .ctor(int32 x) cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public strict virtual instance string + ToString() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(object obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(class ReferenceAssembly/Nested/Test other) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 CompareTo(object other, + class [runtime]System.Collections.IComparer comparer) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 GetHashCode() cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance int32 GetHashCode(class [runtime]System.Collections.IEqualityComparer comparer) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(object other, + class [runtime]System.Collections.IEqualityComparer comparer) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(object obj) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .method public hidebysig virtual final + instance bool Equals(class ReferenceAssembly/Nested/Test other) cil managed + { + .custom instance void [runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + .property instance int32 x() + { + .custom instance void [FSharp.Core]Microsoft.FSharp.Core.CompilationMappingAttribute::.ctor(valuetype [FSharp.Core]Microsoft.FSharp.Core.SourceConstructFlags, + int32) = ( 01 00 04 00 00 00 00 00 00 00 00 00 ) + .get instance int32 ReferenceAssembly/Nested/Test::get_x() + } + } + + .method public static void test(class ReferenceAssembly/Nested/Test _x) cil managed + { + + .maxstack 8 + IL_0000: ldnull + IL_0001: throw + } + + } + + } + + .class private abstract auto ansi sealed ''.$ReferenceAssembly + extends [runtime]System.Object + { + }""" + ] + |> ignore diff --git a/tests/fsharp/FSharpSuite.Tests.fsproj b/tests/fsharp/FSharpSuite.Tests.fsproj index 79fdc521ea7..c7ac3c7c4fe 100644 --- a/tests/fsharp/FSharpSuite.Tests.fsproj +++ b/tests/fsharp/FSharpSuite.Tests.fsproj @@ -27,6 +27,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl b/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl index 853541eca4d..fd16c286f09 100644 --- a/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl +++ b/tests/fsharpqa/Source/CompilerOptions/fsc/help/help40.437.1033.bsl @@ -39,6 +39,9 @@ Copyright (c) Microsoft Corporation. All Rights Reserved. signature files --nocopyfsharpcore Don't copy FSharp.Core.dll along the produced binaries +--refonly[+|-] Produce a reference assembly, instead of a full assembly, as the primary + output +--refout: Produce a reference assembly with the specified file path. - INPUT FILES -