diff --git a/eng/Versions.props b/eng/Versions.props
index fb8f027e0f2205..d70bf1b85b3f59 100644
--- a/eng/Versions.props
+++ b/eng/Versions.props
@@ -165,7 +165,7 @@
2.0.4
4.12.0
2.14.3
- 6.0.100-rc.2.21463.12
+ 6.0.100-rc.2.21473.29
6.0.0-preview-20210916.1
diff --git a/eng/testing/scenarios/BuildWasmAppsJobsList.txt b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
index c4c5a1875c3bfc..8afeea6cdeeb13 100644
--- a/eng/testing/scenarios/BuildWasmAppsJobsList.txt
+++ b/eng/testing/scenarios/BuildWasmAppsJobsList.txt
@@ -11,6 +11,7 @@ Wasm.Build.Tests.LocalEMSDKTests
Wasm.Build.Tests.MainWithArgsTests
Wasm.Build.Tests.NativeBuildTests
Wasm.Build.Tests.NativeLibraryTests
+Wasm.Build.Tests.PInvokeTableGeneratorTests
Wasm.Build.Tests.RebuildTests
Wasm.Build.Tests.SatelliteAssembliesTests
Wasm.Build.Tests.WasmBuildAppTest
diff --git a/src/libraries/workloads-testing.targets b/src/libraries/workloads-testing.targets
index d3dc57267a608a..6a8fa5055bbbb3 100644
--- a/src/libraries/workloads-testing.targets
+++ b/src/libraries/workloads-testing.targets
@@ -16,7 +16,6 @@
-
@@ -41,7 +40,6 @@
-
diff --git a/src/mono/wasm/BlazorOverwrite.targets b/src/mono/wasm/BlazorOverwrite.targets
deleted file mode 100644
index a276d385723725..00000000000000
--- a/src/mono/wasm/BlazorOverwrite.targets
+++ /dev/null
@@ -1,741 +0,0 @@
-
-
-
-
- true
-
-
- true
-
-
-
-
- $(MSBuildThisFileDirectory)..\
- <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">net6.0
- <_BlazorWebAssemblySdkTasksTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net472
- <_BlazorWebAssemblySdkTasksAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\$(_BlazorWebAssemblySdkTasksTFM)\Microsoft.NET.Sdk.BlazorWebAssembly.Tasks.dll
- <_BlazorWebAssemblySdkToolAssembly>$(BlazorWebAssemblySdkDirectoryRoot)tools\net6.0\Microsoft.NET.Sdk.BlazorWebAssembly.Tool.dll
-
-
-
-
-
-
-
-
-
-
-
-
-
- true
- true
-
-
- false
- false
- true
- false
- false
- false
- <_AggressiveAttributeTrimming Condition="'$(_AggressiveAttributeTrimming)' == ''">true
- false
- true
-
-
- false
- false
- false
- false
- true
-
-
- false
-
- <_TargetingNET60OrLater Condition="'$(TargetFrameworkIdentifier)' == '.NETCoreApp' AND $([MSBuild]::VersionGreaterThanOrEquals('$(TargetFrameworkVersion)', '6.0'))">true
-
-
-
- false
-
- true
- true
- false
- ComputeFilesToPublish;_GatherWasmFilesToPublish;$(WasmNestedPublishAppDependsOn)
- <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' != 'true'">ResolveRuntimePackAssets
- <_ScrambleDotnetJsFileNameAfterThisTarget Condition="'$(UsingBrowserRuntimeWorkload)' == 'true'">WasmBuildApp
-
-
- Publish
-
-
-
-
-
-
-
-
-
-
-
- $(ResolveStaticWebAssetsInputsDependsOn);
- _AddBlazorWasmStaticWebAssets;
-
-
-
- _GenerateBuildBlazorBootJson;
- $(StaticWebAssetsPrepareForRunDependsOn)
-
-
-
- $(ResolvePublishStaticWebAssetsDependsOn);
- ProcessPublishFilesForBlazor;
- ComputeBlazorExtensions;
- _AddPublishBlazorBootJsonToStaticWebAssets;
-
-
-
- $(GenerateStaticWebAssetsPublishManifestDependsOn);
- GeneratePublishBlazorBootJson;
-
-
-
-
-
-
-
-
-
- <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
- <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
- <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
-
-
-
- <_DotNetJsItem Remove="@(_DotNetJsItem)" />
- <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
- _framework/$(_BlazorDotnetJsFileName)
- native
- true
-
-
-
-
- <_DotnetJsStaticWebAssetCandidate Remove="@(_DotnetJsStaticWebAssetCandidate)" />
- <_DotnetJsCopyCandidates Remove="@(_DotnetJsCopyCandidates)" />
-
-
-
-
-
-
-
-
-
-
-
- <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
- <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
-
-
-
-
-
-
- <_DotNetJsVersion>$(BundledNETCoreAppPackageVersion)
- <_DotNetJsVersion Condition="'$(RuntimeFrameworkVersion)' != ''">$(RuntimeFrameworkVersion)
- <_BlazorDotnetJsFileName>dotnet.$(_DotNetJsVersion).js
-
-
-
- <_DotNetJsItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(ReferenceCopyLocalPaths.DestinationSubPath)' == 'dotnet.js' AND '%(ReferenceCopyLocalPaths.AssetType)' == 'native'">
- _framework/$(_BlazorDotnetJsFileName)
-
-
-
-
- <_DotNetJsItem Remove="@(_DotNetJsItem)" />
- <_DotNetJsItem Include="@(WasmNativeAsset)" Condition="'%(WasmNativeAsset.FileName)%(WasmNativeAsset.Extension)' == 'dotnet.js'">
- _framework/$(_BlazorDotnetJsFileName)
- native
- true
-
-
-
-
-
-
-
-
-
-
-
-
- <_DotnetJsStaticWebAsset Include="@(_DotnetJsStaticWebAssetCandidate->'%(ContentRoot)_framework\dotnet.js')" />
- <_BlazorStaticWebAsset Include="@(_DotnetJsStaticWebAsset)" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorEnableTimeZoneSupport>$(BlazorEnableTimeZoneSupport)
- <_BlazorEnableTimeZoneSupport Condition="'$(_BlazorEnableTimeZoneSupport)' == ''">true
- <_BlazorInvariantGlobalization>$(InvariantGlobalization)
- <_BlazorInvariantGlobalization Condition="'$(_BlazorInvariantGlobalization)' == ''">true
- <_BlazorCopyOutputSymbolsToOutputDirectory>$(CopyOutputSymbolsToOutputDirectory)
- <_BlazorCopyOutputSymbolsToOutputDirectory Condition="'$(_BlazorCopyOutputSymbolsToOutputDirectory)'==''">true
- <_BlazorWebAssemblyLoadAllGlobalizationData>$(BlazorWebAssemblyLoadAllGlobalizationData)
- <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(_BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
-
-
- $(OutputPath)$(PublishDirName)\
-
-
-
-
-
- <_BlazorJSFile Include="$(BlazorWebAssemblyJSPath)" />
- <_BlazorJSFile Include="$(BlazorWebAssemblyJSMapPath)" Condition="Exists('$(BlazorWebAssemblyJSMapPath)')" />
- <_BlazorJsFile>
- _framework/%(Filename)%(Extension)
-
-
-
-
-
- <_BlazorConfigFileCandidates Include="@(StaticWebAsset)" Condition="'%(SourceType)' == 'Discovered'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildGZipCompressDirectory>$(IntermediateOutputPath)build-gz\
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildGZipCompressedFile>
- %(RelatedAsset)
-
-
- <_BlazorGzipStaticWebAsset Include="@(_BlazorBuildGZipCompressedFile->'%(FullPath)')" />
-
-
-
- <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
-
-
-
- <_BuildBlazorBootJson
- Include="$(_BlazorBuildBootJsonPath)"
- RelativePath="_framework/blazor.boot.json" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorBuildBootJsonPath>$(IntermediateOutputPath)blazor.boot.json
- <_BlazorWebAssemblyLoadAllGlobalizationData Condition="'$(BlazorWebAssemblyLoadAllGlobalizationData)' == ''">false
-
-
-
- <_BlazorJsModuleCandidatesForBuild
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Publish'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorTypeGranularTrimmerDescriptorFile>$(IntermediateOutputPath)typegranularity.trimmerdescriptor.xml
-
-
-
- <_BlazorTypeGranularAssembly
- Include="@(ManagedAssemblyToLink)"
- Condition="'%(Extension)' == '.dll' AND $([System.String]::Copy('%(Filename)').StartsWith('Microsoft.AspNetCore.'))">
- false
- all
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorAotEnabled>$(UsingBrowserRuntimeWorkload)
- <_BlazorAotEnabled Condition="'$(_BlazorAotEnabled)' == ''">false
- <_BlazorLinkerEnabled>$(PublishTrimmed)
- <_BlazorLinkerEnabled Condition="'$(_BlazorLinkerEnabled)' == ''">true
-
-
-
-
-
- <_BlazorPublishPrefilteredAssets
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture' or '%(AssetRole)' == 'Alternative'" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorExtensionsCandidate Include="@(BlazorPublishExtension->'%(FullPath)')">
- $(PackageId)
- Computed
- $(PublishDir)wwwroot
- $(StaticWebAssetBasePath)
- %(BlazorPublishExtension.RelativePath)
- Publish
- All
- Primary
- BlazorWebAssemblyResource
- extension:%(BlazorPublishExtension.ExtensionName)
- Never
- PreserveNewest
- %(BlazorPublishExtension.Identity)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_PublishBlazorBootJson
- Include="$(IntermediateOutputPath)blazor.publish.boot.json"
- RelativePath="_framework/blazor.boot.json" />
-
-
-
-
-
-
-
-
-
-
- <_BlazorPublishAsset
- Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and '%(StaticWebAsset.AssetTraitValue)' != 'manifest' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture') and '%(StaticWebAsset.AssetTraitValue)' != 'boot'" />
-
- <_BlazorPublishConfigFile
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' and '%(StaticWebAsset.AssetTraitValue)' == 'settings'"/>
-
- <_BlazorJsModuleCandidatesForPublish
- Include="@(StaticWebAsset)"
- Condition="'%(StaticWebAsset.AssetTraitName)' == 'JSModule' and '%(StaticWebAsset.AssetTraitValue)' == 'JSLibraryModule' and '%(AssetKind)' != 'Build'" />
-
-
- <_BlazorPublishAsset Remove="@(_BlazorExtensionsCandidatesForPublish)" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_CompressedFileOutputPath>$(IntermediateOutputPath)compress\
- <_BlazorWebAssemblyBrotliIncremental>true
-
-
-
- <_DotNetHostDirectory>$(NetCoreRoot)
- <_DotNetHostFileName>dotnet
- <_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe
-
-
-
-
-
-
-
- <_GzipFileToCompressForPublish Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'BlazorWebAssemblyResource' or '%(StaticWebAsset.AssetTraitName)' == 'Culture')" >
- %(Identity)
- Alternative
- Content-Encoding
- gzip
-
-
- <_BrotliFileToCompressForPublish Include="@(_GzipFileToCompressForPublish)" Condition="'%(AssetKind)' != 'Build'">
- br
-
-
-
- <_AlreadyGzipCompressedAssets
- Include="@(StaticWebAsset)"
- Condition="'%(AssetKind)' != 'Build' and ('%(StaticWebAsset.AssetTraitName)' == 'Content-Encoding' and '%(StaticWebAsset.AssetTraitValue)' == 'gzip')" />
- <_GzipFileToCompressForPublish Remove="@(_AlreadyGzipCompressedAssets->'%(RelatedAsset)')" />
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- <_BlazorPublishGZipCompressedFile>
- %(RelatedAsset)
-
- <_BlazorPublishBrotliCompressedFile>
- %(RelatedAsset)
-
-
-
-
-
-
-
-
diff --git a/src/mono/wasm/build/WasmApp.Native.targets b/src/mono/wasm/build/WasmApp.Native.targets
index 1c282834433a6d..e564d43520b592 100644
--- a/src/mono/wasm/build/WasmApp.Native.targets
+++ b/src/mono/wasm/build/WasmApp.Native.targets
@@ -143,6 +143,7 @@
<_MonoAotCrossCompilerPath>@(MonoAotCrossCompiler->WithMetadataValue('RuntimeIdentifier','browser-wasm'))
<_EmccDefaultFlagsRsp>$([MSBuild]::NormalizePath($(_WasmRuntimePackSrcDir), 'emcc-default.rsp'))
+ false
true
true
$(WasmBuildNative)
@@ -156,8 +157,7 @@
<_EmccAssertionLevelDefault>0
<_EmccOptimizationFlagDefault Condition="'$(_WasmDevel)' == 'true'">-O0 -s ASSERTIONS=$(_EmccAssertionLevelDefault)
- <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(OS)' != 'Windows_NT' and '$(Configuration)' == 'Debug'">-Os
- <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' != 'Debug'">-Oz
+ <_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == '' and '$(Configuration)' == 'Debug' and '$(WasmBuildingForNestedPublish)' != 'true'">-O1
<_EmccOptimizationFlagDefault Condition="'$(_EmccOptimizationFlagDefault)' == ''">-Oz
$(_EmccOptimizationFlagDefault)
@@ -206,6 +206,9 @@
<_EmccLDFlags Include="@(_EmccCommonFlags)" />
<_EmccLDFlags Include="-s TOTAL_MEMORY=$(EmccTotalMemory)" />
+
+ <_EmccLDFlags Include="-s ERROR_ON_UNDEFINED_SYMBOLS=0" Condition="'$(WasmBuildingForNestedPublish)' != 'true'" />
+
<_DriverCDependencies Include="$(_WasmPInvokeHPath);$(_WasmICallTablePath)" />
<_DriverCDependencies Include="$(_DriverGenCPath)" Condition="'$(_DriverGenCNeeded)' == 'true'" />
@@ -296,7 +299,8 @@
Inputs="@(_BitcodeFile);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)"
Outputs="@(_BitcodeFile->'%(ObjectFile)')"
Condition="'$(_WasmShouldAOT)' == 'true' and @(_BitcodeFile->Count()) > 0"
- DependsOnTargets="_WasmWriteRspForCompilingBitcode">
+ DependsOnTargets="_WasmWriteRspForCompilingBitcode"
+ Returns="@(FileWrites)">
<_BitCodeFile Dependencies="%(_BitCodeFile.Dependencies);$(_EmccDefaultFlagsRsp);$(_EmccCompileBitcodeRsp)" />
@@ -362,7 +366,8 @@
+ DependsOnTargets="_WasmSelectRuntimeComponentsForLinking;_WasmCompileAssemblyBitCodeFilesForAOT;_WasmWriteRspFilesForLinking"
+ Returns="@(FileWrites)" >
diff --git a/src/tasks/WasmAppBuilder/EmccCompile.cs b/src/tasks/WasmAppBuilder/EmccCompile.cs
index a5d6af6c3b473a..b0bc0ff80050b3 100644
--- a/src/tasks/WasmAppBuilder/EmccCompile.cs
+++ b/src/tasks/WasmAppBuilder/EmccCompile.cs
@@ -95,6 +95,7 @@ private bool ExecuteActual()
if (!ShouldCompile(srcFile, objFile, depFiles, out string reason))
{
Log.LogMessage(MessageImportance.Low, $"Skipping {srcFile} because {reason}.");
+ outputItems.Add(CreateOutputItemFor(srcFile, objFile));
}
else
{
@@ -107,7 +108,8 @@ private bool ExecuteActual()
if (_numCompiled == _totalFiles)
{
// nothing to do!
- return true;
+ OutputFiles = outputItems.ToArray();
+ return !Log.HasLoggedErrors;
}
if (_numCompiled > 0)
@@ -200,9 +202,7 @@ bool ProcessSourceFile(string srcFile, string objFile)
else
Log.LogMessage(MessageImportance.Low, $"Copied {tmpObjFile} to {objFile}");
- ITaskItem newItem = new TaskItem(objFile);
- newItem.SetMetadata("SourceFile", srcFile);
- outputItems.Add(newItem);
+ outputItems.Add(CreateOutputItemFor(srcFile, objFile));
int count = Interlocked.Increment(ref _numCompiled);
Log.LogMessage(MessageImportance.High, $"[{count}/{_totalFiles}] {Path.GetFileName(srcFile)} -> {Path.GetFileName(objFile)} [took {elapsedSecs:F}s]");
@@ -219,6 +219,13 @@ bool ProcessSourceFile(string srcFile, string objFile)
File.Delete(tmpObjFile);
}
}
+
+ ITaskItem CreateOutputItemFor(string srcFile, string objFile)
+ {
+ ITaskItem newItem = new TaskItem(objFile);
+ newItem.SetMetadata("SourceFile", srcFile);
+ return newItem;
+ }
}
private bool ShouldCompile(string srcFile, string objFile, string[] depFiles, out string reason)
diff --git a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
index 825de67d6e2704..7264944fa5b571 100644
--- a/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
+++ b/src/tasks/WasmAppBuilder/PInvokeTableGenerator.cs
@@ -3,13 +3,10 @@
using System;
using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Text;
-using System.Text.Json;
using System.Reflection;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
@@ -121,25 +118,42 @@ private void EmitPInvokeTable(StreamWriter w, Dictionary modules
w.WriteLine("// GENERATED FILE, DO NOT MODIFY");
w.WriteLine();
- var decls = new HashSet();
- foreach (var pinvoke in pinvokes.OrderBy(l => l.EntryPoint))
+ var pinvokesGroupedByEntryPoint = pinvokes.Where(l => modules.ContainsKey(l.Module))
+ .OrderBy(l => l.EntryPoint)
+ .GroupBy(l => l.EntryPoint);
+
+ var comparer = new PInvokeComparer();
+ foreach (IGrouping group in pinvokesGroupedByEntryPoint)
{
- if (modules.ContainsKey(pinvoke.Module)) {
- try
+ var candidates = group.Distinct(comparer).ToArray();
+ PInvoke first = candidates[0];
+ if (ShouldTreatAsVariadic(candidates))
+ {
+ Log.LogWarning($"Found a native function ({first.EntryPoint}) with varargs, which is not supported. Calling it will fail at runtime. Module: {first.Module}." +
+ $" Managed DllImports: {Environment.NewLine}{CandidatesToString(candidates)}");
+
+ string? decl = GenPInvokeDecl(first, treatAsVariadic: true);
+ if (decl != null)
{
- var decl = GenPInvokeDecl(pinvoke);
- if (decls.Contains(decl))
- continue;
+ w.WriteLine($"// Variadic signature created for");
+ foreach (PInvoke pinvoke in candidates)
+ w.WriteLine($"// {pinvoke.Method}");
w.WriteLine(decl);
- decls.Add(decl);
- }
- catch (NotSupportedException)
- {
- // See the FIXME in GenPInvokeDecl
- Log.LogWarning($"Cannot handle function pointer arguments/return value in pinvoke method '{pinvoke.Method}' in type '{pinvoke.Method.DeclaringType}'.");
- pinvoke.Skip = true;
}
+
+ continue;
+ }
+
+ var decls = new HashSet();
+ foreach (var candidate in candidates)
+ {
+ var decl = GenPInvokeDecl(candidate, treatAsVariadic: false);
+ if (decl == null || decls.Contains(decl))
+ continue;
+
+ w.WriteLine(decl);
+ decls.Add(decl);
}
}
@@ -186,6 +200,25 @@ static string ModuleNameToId(string name)
return fixedName;
}
+
+ static bool ShouldTreatAsVariadic(PInvoke[] candidates)
+ {
+ if (candidates.Length < 2)
+ return false;
+
+ PInvoke first = candidates[0];
+ if (TryIsMethodGetParametersUnsupported(first.Method, out _))
+ return false;
+
+ int firstNumArgs = first.Method.GetParameters().Length;
+ return candidates
+ .Skip(1)
+ .Any(c => !TryIsMethodGetParametersUnsupported(c.Method, out _) &&
+ c.Method.GetParameters().Length != firstNumArgs);
+ }
+
+ static string CandidatesToString(IEnumerable group)
+ => string.Join(Environment.NewLine, group);
}
private string MapType (Type t)
@@ -205,7 +238,29 @@ private string MapType (Type t)
return "int";
}
- private string GenPInvokeDecl(PInvoke pinvoke)
+ // FIXME: System.Reflection.MetadataLoadContext can't decode function pointer types
+ // https://github.com/dotnet/runtime/issues/43791
+ private static bool TryIsMethodGetParametersUnsupported(MethodInfo method, [NotNullWhen(true)] out string? reason)
+ {
+ try
+ {
+ method.GetParameters();
+ }
+ catch (NotSupportedException nse)
+ {
+ reason = nse.Message;
+ return true;
+ }
+ catch
+ {
+ // not concerned with other exceptions
+ }
+
+ reason = null;
+ return false;
+ }
+
+ private string? GenPInvokeDecl(PInvoke pinvoke, bool treatAsVariadic)
{
var sb = new StringBuilder();
var method = pinvoke.Method;
@@ -215,15 +270,33 @@ private string GenPInvokeDecl(PInvoke pinvoke)
sb.Append($"int {pinvoke.EntryPoint} (int, int, int, int, int);");
return sb.ToString();
}
+
+ if (TryIsMethodGetParametersUnsupported(pinvoke.Method, out string? reason))
+ {
+ Log.LogWarning($"Skipping the following DllImport because '{reason}'. {Environment.NewLine} {pinvoke.Method}");
+ pinvoke.Skip = true;
+ return null;
+ }
+
sb.Append(MapType(method.ReturnType));
sb.Append($" {pinvoke.EntryPoint} (");
- int pindex = 0;
- var pars = method.GetParameters();
- foreach (var p in pars) {
- if (pindex > 0)
- sb.Append(',');
- sb.Append(MapType(pars[pindex].ParameterType));
- pindex++;
+ if (!treatAsVariadic)
+ {
+ int pindex = 0;
+ var pars = method.GetParameters();
+ foreach (var p in pars) {
+ if (pindex > 0)
+ sb.Append(',');
+ sb.Append(MapType(pars[pindex].ParameterType));
+ pindex++;
+ }
+ }
+ else
+ {
+ // FIXME: handle sigs with different first args
+ ParameterInfo firstParam = method.GetParameters()[0];
+ sb.Append(MapType(firstParam.ParameterType));
+ sb.Append(", ...");
}
sb.Append(");");
return sb.ToString();
@@ -366,7 +439,7 @@ private static bool IsBlittable (Type type)
private static void Error (string msg) => throw new LogAsErrorException(msg);
}
-internal class PInvoke
+internal class PInvoke : IEquatable
{
public PInvoke(string entryPoint, string module, MethodInfo method)
{
@@ -379,6 +452,30 @@ public PInvoke(string entryPoint, string module, MethodInfo method)
public string Module;
public MethodInfo Method;
public bool Skip;
+
+ public bool Equals(PInvoke? other)
+ => other != null &&
+ string.Equals(EntryPoint, other.EntryPoint, StringComparison.Ordinal) &&
+ string.Equals(Module, other.Module, StringComparison.Ordinal) &&
+ string.Equals(Method.ToString(), other.Method.ToString(), StringComparison.Ordinal);
+
+ public override string ToString() => $"{{ EntryPoint: {EntryPoint}, Module: {Module}, Method: {Method}, Skip: {Skip} }}";
+}
+
+internal class PInvokeComparer : IEqualityComparer
+{
+ public bool Equals(PInvoke? x, PInvoke? y)
+ {
+ if (x == null && y == null)
+ return true;
+ if (x == null || y == null)
+ return false;
+
+ return x.Equals(y);
+ }
+
+ public int GetHashCode(PInvoke pinvoke)
+ => $"{pinvoke.EntryPoint}{pinvoke.Module}{pinvoke.Method}".GetHashCode();
}
internal class PInvokeCallback
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
index 687429a3fca482..66b5c5b2782766 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmBuildPublishTests.cs
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System;
using System.IO;
using System.Linq;
using Xunit;
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
index 6c7e3ed788be56..727e6589c4c6d2 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/BlazorWasmTests.cs
@@ -79,6 +79,7 @@ private CommandResult PublishForRequiresWorkloadTest(string config, string extra
[Theory]
[InlineData("Debug")]
[InlineData("Release")]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/59538")]
public void Net50Projects_NativeReference(string config)
=> BuildNet50Project(config, aot: false, expectError: true, @"");
@@ -92,6 +93,7 @@ public void Net50Projects_NativeReference(string config)
[Theory]
[MemberData(nameof(Net50TestData))]
+ [ActiveIssue("https://github.com/dotnet/runtime/issues/59538")]
public void Net50Projects_AOT(string config, bool aot, bool expectError)
=> BuildNet50Project(config, aot: aot, expectError: expectError);
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
index 3ccf400ee92113..eb306286ba5e0d 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeLibraryTests.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.IO;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
index 9047fd062a82e4..cd0d3ac4396349 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NativeRebuildTestsBase.cs
@@ -77,6 +77,7 @@ protected string Rebuild(bool nativeRelink, bool invariant, BuildArgs buildArgs,
buildArgs = newBuildArgs;
_testOutput.WriteLine($"{Environment.NewLine}Rebuilding with no changes ..{Environment.NewLine}");
+ Console.WriteLine($"{Environment.NewLine}Rebuilding with no changes ..{Environment.NewLine}");
(_, string output) = BuildProject(buildArgs,
id: id,
dotnetWasmFromRuntimePack: false,
@@ -137,6 +138,18 @@ internal void CompareStat(IDictionary oldStat, IDictionary GetFilesTable(bool unchanged, params string[] baseDirs)
+ {
+ var dict = new Dictionary();
+ foreach (var baseDir in baseDirs)
+ {
+ foreach (var file in Directory.EnumerateFiles(baseDir, "*", new EnumerationOptions { RecurseSubdirectories = true }))
+ dict[Path.GetFileName(file)] = (file, unchanged);
+ }
+
+ return dict;
+ }
+
internal IDictionary GetFilesTable(BuildArgs buildArgs, BuildPaths paths, bool unchanged)
{
List files = new()
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
index 96f6d41fa10025..3131dec390ce05 100644
--- a/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/NativeRebuildTests/NoopNativeRebuildTest.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System.IO;
using System.Linq;
using Wasm.Build.Tests;
using Xunit;
@@ -33,5 +34,60 @@ public void NoOpRebuildForNativeBuilds(BuildArgs buildArgs, bool nativeRelink, b
CompareStat(originalStat, newStat, pathsDict.Values);
RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
}
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorNoopRebuild(string config)
+ {
+ string id = $"blz_rebuild_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ string objDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm");
+
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ File.Move(Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build.binlog"),
+ Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build-first.binlog"));
+
+ var pathsDict = GetFilesTable(true, objDir);
+ pathsDict.Remove("runtime-icall-table.h");
+ var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ CompareStat(originalStat, newStat, pathsDict.Values);
+ }
+
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [InlineData("Debug")]
+ [InlineData("Release")]
+ public void BlazorOnlyLinkRebuild(string config)
+ {
+ string id = $"blz_relink_{config}";
+ string projectFile = CreateBlazorWasmTemplateProject(id);
+ AddItemsPropertiesToProject(projectFile, extraProperties: "true");
+
+ string objDir = Path.Combine(_projectDir!, "obj", config, "net6.0", "wasm");
+
+ BlazorBuild(id, config, NativeFilesType.Relinked);
+ File.Move(Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build.binlog"),
+ Path.Combine(s_buildEnv.LogRootPath, id, $"{id}-build-first.binlog"));
+
+ var pathsDict = GetFilesTable(true, objDir);
+ pathsDict.Remove("runtime-icall-table.h");
+ pathsDict.UpdateTo(unchanged: false, "dotnet.wasm", "dotnet.js", "emcc-link.rsp");
+
+ var originalStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ // build again
+ BlazorBuild(id, config, NativeFilesType.Relinked, "-p:EmccLinkOptimizationFlag=-O1");
+ var newStat = StatFiles(pathsDict.Select(kvp => kvp.Value.fullPath));
+
+ CompareStat(originalStat, newStat, pathsDict.Values);
+ }
}
}
diff --git a/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
new file mode 100644
index 00000000000000..27783ae85c95ad
--- /dev/null
+++ b/src/tests/BuildWasmApps/Wasm.Build.Tests/PInvokeTableGeneratorTests.cs
@@ -0,0 +1,139 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.IO;
+using Xunit;
+using Xunit.Abstractions;
+
+#nullable enable
+
+namespace Wasm.Build.Tests
+{
+ public class PInvokeTableGeneratorTests : BuildTestBase
+ {
+ public PInvokeTableGeneratorTests(ITestOutputHelper output, SharedBuildPerTestClassFixture buildContext)
+ : base(output, buildContext)
+ {
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void NativeLibraryWithVariadicFunctions(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main(string[] args)
+ {
+ Console.WriteLine($""Main running"");
+ if (args.Length > 0)
+ {
+ // We don't want to run this, because we can't call variadic functions
+ Console.WriteLine($""sum_three: {sum_three(7, 14, 21)}"");
+ Console.WriteLine($""sum_two: {sum_two(3, 6)}"");
+ Console.WriteLine($""sum_one: {sum_one(5)}"");
+ }
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_one(int a);
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_two(int a, int b);
+ [DllImport(""variadic"", EntryPoint=""sum"")] public static extern int sum_three(int a, int b, int c);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"variadic_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*native function.*sum.*varargs", output);
+ Assert.Matches("warning.*sum_(one|two|three)", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void DllImportWithFunctionPointersCompilesWithWarning(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main()
+ {
+ Console.WriteLine($""Main running"");
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public unsafe static extern int using_sum_one(delegate* unmanaged callback);
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public static extern int sum_one(int a, int b);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"fnptr_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*Skipping.*because.*function pointer", output);
+ Assert.Matches("warning.*using_sum_one", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ [ConditionalTheory(typeof(BuildTestBase), nameof(IsUsingWorkloads))]
+ [BuildAndRun(host: RunHost.V8)]
+ public void DllImportWithFunctionPointers_ForVariadicFunction_CompilesWithWarning(BuildArgs buildArgs, RunHost host, string id)
+ {
+ string code = @"
+ using System;
+ using System.Runtime.InteropServices;
+ public class Test
+ {
+ public static int Main()
+ {
+ Console.WriteLine($""Main running"");
+ return 42;
+ }
+
+ [DllImport(""variadic"", EntryPoint=""sum"")]
+ public unsafe static extern int using_sum_one(delegate* unmanaged callback);
+ }";
+
+ (buildArgs, string output) = BuildForVariadicFunctionTests(code,
+ buildArgs with { ProjectName = $"fnptr_variadic_{buildArgs.Config}_{id}" },
+ id);
+ Assert.Matches("warning.*Skipping.*because.*function pointer", output);
+ Assert.Matches("warning.*using_sum_one", output);
+
+ output = RunAndTestWasmApp(buildArgs, buildDir: _projectDir, expectedExitCode: 42, host: host, id: id);
+ Assert.Contains("Main running", output);
+ }
+
+ private (BuildArgs, string) BuildForVariadicFunctionTests(string programText, BuildArgs buildArgs, string id)
+ {
+ string filename = "variadic.o";
+ buildArgs = ExpandBuildArgs(buildArgs,
+ extraItems: $"",
+ extraProperties: "true<_WasmDevel>true");
+
+ (_, string output) = BuildProject(buildArgs,
+ initProject: () =>
+ {
+ File.WriteAllText(Path.Combine(_projectDir!, "Program.cs"), programText);
+ File.Copy(Path.Combine(BuildEnvironment.TestAssetsPath, "native-libs", filename),
+ Path.Combine(_projectDir!, filename));
+ },
+ publish: buildArgs.AOT,
+ id: id,
+ dotnetWasmFromRuntimePack: false);
+
+ return (buildArgs, output);
+ }
+ }
+}
diff --git a/src/tests/BuildWasmApps/testassets/native-libs/variadic.c b/src/tests/BuildWasmApps/testassets/native-libs/variadic.c
new file mode 100644
index 00000000000000..cd4009439d19da
--- /dev/null
+++ b/src/tests/BuildWasmApps/testassets/native-libs/variadic.c
@@ -0,0 +1,14 @@
+#include
+
+int sum(int n, ...)
+{
+ int result = 0;
+ va_list ptr;
+ va_start(ptr, n);
+
+ for (int i = 0; i < n; i++)
+ result += va_arg(ptr, int);
+
+ va_end(ptr);
+ return result;
+}
diff --git a/src/tests/BuildWasmApps/testassets/native-libs/variadic.o b/src/tests/BuildWasmApps/testassets/native-libs/variadic.o
new file mode 100644
index 00000000000000..b4558ce3519793
Binary files /dev/null and b/src/tests/BuildWasmApps/testassets/native-libs/variadic.o differ