Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ujjwalchadha/perf/eventsourcecodegen #852

Merged
merged 12 commits into from
Jun 9, 2021
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ nuget/Microsoft.Windows.CsWinRT.Prerelease.targets
*.binlog
vs_buildtools.exe
.buildtools
src/BenchmarkDotNet.Artifacts/*
12 changes: 9 additions & 3 deletions src/Benchmarks/Benchmarks.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,26 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<Platforms>x64;x86</Platforms>
<TargetFrameworks>netcoreapp2.0;net5.0;netcoreapp3.1</TargetFrameworks>
<TargetFrameworks>netcoreapp2.0;netcoreapp3.1;net5.0;</TargetFrameworks>
<UseWinmd>false</UseWinmd>
<BenchmarkWinmdSupport Condition="'$(BenchmarkWinmdSupport)' == ''">false</BenchmarkWinmdSupport>
<!-- Controls whether to build using WinMDs or the projection. -->
<UseWinmd Condition="'$(TargetFramework)' == 'netcoreapp3.1' And $(BenchmarkWinmdSupport) == true">true</UseWinmd>
<ApplicationManifest Condition="$(UseWinmd) == true">Benchmarks.manifest</ApplicationManifest>
<BenchmarkTargetFramework>$(TargetFramework)</BenchmarkTargetFramework>
<BenchmarkTargetFramework Condition="'$(BenchmarkTargetFramework)' == 'netcoreapp2.0'">netstandard2.0</BenchmarkTargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="Microsoft.Windows.SDK.Contracts" Version="10.0.18362.2005" Condition="$(UseWinmd) == true"/>
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.5.3" Condition="$(UseWinmd) == false"/>
</ItemGroup>

<ItemGroup>
<Reference Include="$(MSBuildThisFileDirectory)..\Projections\Windows\bin\x64\Release\$(BenchmarkTargetFramework)\Windows.dll"></Reference>
<Reference Include="$(MSBuildThisFileDirectory)..\Projections\Benchmark\bin\x64\Release\$(BenchmarkTargetFramework)\Benchmark.dll"></Reference>

<ProjectReference Include="..\Projections\Windows\Windows.csproj" Condition="$(UseWinmd) == false" />
<ProjectReference Include="..\Projections\Benchmark\Benchmark.csproj" Condition="$(UseWinmd) == false" />
<ProjectReference Include="..\TestWinRT\BenchmarkComponent\BenchmarkComponent.vcxproj" Condition="$(UseWinmd) == true" />
Expand All @@ -32,6 +37,7 @@
<Reference Include="BenchmarkComponent.winmd" Condition="$(UseWinmd) == true">
<HintPath>$(MSBuildThisFileDirectory)..\_build\$(Platform)\$(Configuration)\BenchmarkComponent\bin\BenchmarkComponent\BenchmarkComponent.winmd</HintPath>
<IsWinMDFile>true</IsWinMDFile>
<Visible>True</Visible>
</Reference>

<!-- When building for NetCoreApp 3.1, we test the WinMD scenario via reg free winrt, so it needs to be in the bin folder for the generated project to reference it. -->
Expand All @@ -42,7 +48,7 @@
</None>

</ItemGroup>

<Target Name="FilterProjection" BeforeTargets="CoreCompile" Condition="$(UseWinmd) == false">
<ItemGroup>
<!--Remove references to projection source winmds to prevent compile conflict warnings-->
Expand Down
17 changes: 17 additions & 0 deletions src/Benchmarks/ReflectionPerf.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using BenchmarkComponent;
using BenchmarkDotNet.Attributes;
using System;

namespace Benchmarks
{
Expand Down Expand Up @@ -61,5 +62,21 @@ public object ExecuteMarshalingForCustomObject()
{
return instance.NewWrappedClassObject;
}

[Benchmark]
public void IntEventSource()
{
ClassWithMarshalingRoutines instance = new ClassWithMarshalingRoutines();
manodasanW marked this conversation as resolved.
Show resolved Hide resolved

int x = 0;
int y = 1;
int z;

instance.IntProperty = x;
instance.CallForInt(() => y);

instance.IntPropertyChanged += (object sender, int value) => z = value;
instance.RaiseIntChanged();
}
}
}
2 changes: 1 addition & 1 deletion src/benchmark.cmd
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
msbuild Benchmarks\Benchmarks.csproj -t:restore -t:build /p:platform=x64 /p:configuration=release
msbuild Benchmarks\Benchmarks.csproj -t:restore -t:build /p:platform=x64 /p:configuration=release /p:solutiondir=%~dp0
dotnet %~dp0Benchmarks\bin\x64\Release\netcoreapp2.0\Benchmarks.dll -filter * --runtimes netcoreapp2.0 netcoreapp3.1 netcoreapp5.0
2 changes: 1 addition & 1 deletion src/benchmark_winmd.cmd
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
msbuild Benchmarks\Benchmarks.csproj -t:restore -t:clean;rebuild /p:BenchmarkWinmdSupport=true /p:platform=x64 /p:configuration=release /p:TargetFramework=netcoreapp3.1
msbuild Benchmarks\Benchmarks.csproj -t:restore -t:clean;rebuild /p:BenchmarkWinmdSupport=true /p:platform=x64 /p:configuration=release /p:TargetFramework=netcoreapp3.1 /p:solutiondir=%~dp0
%~dp0Benchmarks\bin\x64\Release\netcoreapp3.1\Benchmarks.exe -filter *
rem Clean project to prevent mismatch scenarios with the typical benchmark.cmd scenario.
msbuild Benchmarks\Benchmarks.csproj -t:restore -t:clean /p:BenchmarkWinmdSupport=true /p:platform=x64 /p:configuration=release /p:TargetFramework=netcoreapp3.1 >nul
214 changes: 201 additions & 13 deletions src/cswinrt/code_writers.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <set>
#include <filesystem>
#include <iostream>
#include <regex>
#include <concurrent_unordered_map.h>

namespace cswinrt
{
Expand Down Expand Up @@ -453,6 +455,60 @@ namespace cswinrt
bind<write_parameter_name>(param));
}

void write_projection_arg(writer& w, method_signature::param_t const& param)
{
w.write("%",
bind<write_parameter_name>(param));
}

void write_event_source_type_name(writer& w, type_semantics const& eventTypeSemantics)
{
auto eventTypeCode = w.write_temp("%", bind<write_type_name>(eventTypeSemantics, typedef_name_type::Projected, false));
std::string eventTypeName = "_EventSource_" + eventTypeCode;
std::regex re(R"-((\ |:|<|>|,|\.))-");
w.write("%", std::regex_replace(eventTypeName, re, "_"));
}

method_signature get_event_invoke_method(TypeDef const& eventType)
{
for (auto&& method : eventType.MethodList())
ujjwalchadha marked this conversation as resolved.
Show resolved Hide resolved
{
if (method.Name() == "Invoke")
{
return method_signature(method);
}
}
throw_invalid("Event type must have an Invoke method");
}

void write_event_invoke_params(writer& w, method_signature const& methodSig)
{
w.write("%", bind_list<write_projection_parameter>(", ", methodSig.params()));
}

void write_event_invoke_return(writer& w, method_signature const& methodSig)
{
if (methodSig.return_signature())
{
w.write("return ");
}
}

void write_event_invoke_return_default(writer& w, method_signature const& methodSig)
{
if (!methodSig.return_signature())
{
return;
}
auto&& semantics = get_type_semantics(methodSig.return_signature().Type());
w.write("default(%)", bind<write_projection_type>(semantics));
}

void write_event_invoke_args(writer& w, method_signature const& methodSig)
{
w.write("%", bind_list<write_projection_arg>(", ", methodSig.params()));
}

void write_abi_type(writer& w, type_semantics const& semantics)
{
call(semantics,
Expand Down Expand Up @@ -2467,14 +2523,37 @@ db_path.stem().string());
);
}

void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics);

void write_event_source_ctor(writer& w, Event const& evt)
{
if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType)
{
if (eventType.TypeNamespace() == "System" && eventType.TypeName() == "EventHandler`1")
{
auto [add, remove] = get_event_methods(evt);
w.write(R"(
new EventSource__EventHandler%(_obj,
%,
%))",
bind<write_type_params>(eventType),
get_invoke_info(w, add).first,
get_invoke_info(w, remove).first);
return true;
}
return false;
}))
{
return;
}

auto [add, remove] = get_event_methods(evt);
w.write(R"(
new EventSource<%>(_obj,
new %%(_obj,
%,
%))",
bind<write_type_name>(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false),
bind<write_event_source_type_name>(get_type_semantics(evt.EventType())),
bind<write_event_source_generic_args>(get_type_semantics(evt.EventType())),
get_invoke_info(w, add).first,
get_invoke_info(w, remove).first);
}
Expand Down Expand Up @@ -6255,13 +6334,13 @@ bind_list<write_parameter_name_with_modifier>(", ", signature.params())
{
if (factory.activatable)
{
w.write_each<write_factory_activatable_method>(factory.type.MethodList(), projected_type_name);
w.write_each<write_factory_activatable_method>(factory.type.MethodList(), projected_type_name);
}
else if (factory.statics)
{
w.write_each<write_static_method>(factory.type.MethodList(), projected_type_name, true, ""sv);
w.write_each<write_static_property>(factory.type.PropertyList(), projected_type_name, true, ""sv);
w.write_each<write_static_event>(factory.type.EventList(), projected_type_name, true, ""sv);
w.write_each<write_static_method>(factory.type.MethodList(), projected_type_name, true, ""sv);
w.write_each<write_static_property>(factory.type.PropertyList(), projected_type_name, true, ""sv);
w.write_each<write_static_event>(factory.type.EventList(), projected_type_name, true, ""sv);
}
}
}
Expand All @@ -6272,7 +6351,7 @@ bind_list<write_parameter_name_with_modifier>(", ", signature.params())
{
auto factory_type_name = write_type_name_temp(w, type, "%ServerActivationFactory", typedef_name_type::CCW);
auto base_class = (is_static(type) || !has_default_constructor(type)) ?
"ComponentActivationFactory" : write_type_name_temp(w, type, "ActivatableComponentActivationFactory<%>", typedef_name_type::Projected);
"ComponentActivationFactory" : write_type_name_temp(w, type, "ActivatableComponentActivationFactory<%>", typedef_name_type::Projected);
auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected);

w.write(R"(
Expand Down Expand Up @@ -6328,8 +6407,8 @@ return IntPtr.Zero;
}
)",
bind_each([](writer& w, TypeDef const& type)
{
w.write(R"(
{
w.write(R"(

if (runtimeClassId == "%.%")
{
Expand All @@ -6340,8 +6419,117 @@ type.TypeNamespace(),
type.TypeName(),
bind<write_type_name>(type, typedef_name_type::CCW, true)
);
},
types
));
},
types
));
}
}

void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics)
{
if (!std::holds_alternative<generic_type_instance>(eventTypeSemantics))
{
return;
}
for_typedef(w, eventTypeSemantics, [&](TypeDef const& eventType)
{
std::vector<std::string> genericArgs;
int i = 0;
for (auto&& x: std::get<generic_type_instance>(eventTypeSemantics).generic_args)
{
auto semantics = w.get_generic_arg_scope(i).first;
if (std::holds_alternative<generic_type_param>(semantics))
{
genericArgs.push_back(w.write_temp("%", bind<write_generic_type_name>(i)));
}
i++;
}
if (genericArgs.size() == 0)
{
return;
}
w.write("<%>", bind_list([](writer& w, auto&& value)
{
w.write(value);
}, ", "sv, genericArgs));
});
}

void write_event_source_subclass(writer& w, cswinrt::type_semantics eventTypeSemantics)
{
for_typedef(w, eventTypeSemantics, [&](TypeDef const& eventType)
{
if (eventType.TypeNamespace() == "System" && eventType.TypeName() == "EventHandler`1")
{
return;
}
auto eventTypeCode = w.write_temp("%", bind<write_type_name>(eventType, typedef_name_type::Projected, false));
auto invokeMethodSig = get_event_invoke_method(eventType);
w.write(R"(
internal unsafe class %% : EventSource<%>
{
private % handler;

internal %(IObjectReference obj,
delegate* unmanaged[Stdcall]<System.IntPtr, System.IntPtr, out WinRT.EventRegistrationToken, int> addHandler,
delegate* unmanaged[Stdcall]<System.IntPtr, WinRT.EventRegistrationToken, int> removeHandler) : base(obj, addHandler, removeHandler)
{
}

override protected System.Delegate EventInvoke
{
get
{
if (handler == null)
manodasanW marked this conversation as resolved.
Show resolved Hide resolved
{
handler = (%) =>
{
if (_event == null)
{
return %;
}
%_event.Invoke(%);
};
}
return handler;
}
}
}
)",
bind<write_event_source_type_name>(eventTypeSemantics),
bind<write_event_source_generic_args>(eventTypeSemantics),
eventTypeCode,
eventTypeCode,
bind<write_event_source_type_name>(eventTypeSemantics),
bind<write_event_invoke_params>(invokeMethodSig),
bind<write_event_invoke_return_default>(invokeMethodSig),
bind<write_event_invoke_return>(invokeMethodSig),
bind<write_event_invoke_args>(invokeMethodSig));
});
}

void write_temp_class_event_source_subclass(writer& w, TypeDef const& classType, concurrency::concurrent_unordered_map<std::string, std::string>& typeNameToDefinitionMap)
{
for (auto&& ii : classType.InterfaceImpl())
manodasanW marked this conversation as resolved.
Show resolved Hide resolved
{
for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& interfaceType)
{
for (auto&& eventObj : interfaceType.EventList())
{
auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType());
auto&& eventTypeCode = w.write_temp("%", bind<write_type_name>(eventTypeSemantics, typedef_name_type::Projected, false));
typeNameToDefinitionMap[eventTypeCode] = w.write_temp("%", bind<write_event_source_subclass>(eventTypeSemantics));
}
});
}
}

void write_temp_interface_event_source_subclass(writer& w, TypeDef const& interfaceType, concurrency::concurrent_unordered_map<std::string, std::string>& typeNameToDefinitionMap)
{
for (auto&& eventObj : interfaceType.EventList())
{
auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType());
auto&& eventTypeCode = w.write_temp("%", bind<write_type_name>(eventTypeSemantics, typedef_name_type::Projected, false));
typeNameToDefinitionMap[eventTypeCode] = w.write_temp("%", bind<write_event_source_subclass>(eventTypeSemantics));
}
}
}
Loading