Skip to content

Commit 0958cf3

Browse files
authored
Hide protected and overridable members from public projections (#1319)
1 parent 953d65c commit 0958cf3

21 files changed

+286
-22
lines changed

README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,16 @@ If you really want to build it yourself, the simplest way to do so is to run the
1515

1616
* Open a dev command prompt pointing at the root of the repo.
1717
* Open the `cppwinrt.sln` solution.
18-
* Build the x64 Release configuration of the `prebuild` and `cppwinrt` projects only. Do not attempt to build anything else just yet.
18+
* Build the x64 Release configuration of the `cppwinrt` project only. Do not attempt to build anything else just yet.
1919
* Run `build_projection.cmd` in the dev command prompt.
2020
* Switch to the x64 Debug configuration in Visual Studio and build all projects as needed.
21+
22+
## Comparing Outputs
23+
24+
Comparing the output of the prior release and your current changes will help show the impact of any updates. Starting from
25+
a dev command prompt at the root of the repo _after_ following the above build instructions:
26+
27+
* Run `build_projection.cmd` in the dev command prompt
28+
* Run `build_prior_projection.cmd` in the dev command prompt as well
29+
* Run `prepare_versionless_diffs.cmd` which removes version stamps on both current and prior projection
30+
* Use a directory-level differencing tool to compare `_build\$(arch)\$(flavor)\winrt` and `_reference\$(arch)\$(flavor)\winrt`

build_prior_projection.cmd

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@echo off
2+
3+
setlocal ENABLEDELAYEDEXPANSION
4+
5+
set target_platform=%1
6+
set target_configuration=%2
7+
if "%target_platform%"=="" set target_platform=x64
8+
9+
if /I "%target_platform%" equ "all" (
10+
if "%target_configuration%"=="" (
11+
set target_configuration=all
12+
)
13+
call %0 x86 !target_configuration!
14+
call %0 x64 !target_configuration!
15+
call %0 arm !target_configuration!
16+
call %0 arm64 !target_configuration!
17+
goto :eof
18+
)
19+
20+
if /I "%target_configuration%" equ "all" (
21+
call %0 %target_platform% Debug
22+
call %0 %target_platform% Release
23+
goto :eof
24+
)
25+
26+
if "%target_configuration%"=="" (
27+
set target_configuration=Debug
28+
)
29+
30+
set reference_output=%~p0\_reference\%target_platform%\%target_configuration%
31+
if exist "%reference_output%" (
32+
echo Removing existing reference projections
33+
rmdir /s /q "%reference_output%"
34+
)
35+
36+
if not exist ".\.nuget" mkdir ".\.nuget"
37+
if not exist ".\.nuget\nuget.exe" powershell -Command "$ProgressPreference = 'SilentlyContinue' ; Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe"
38+
39+
mkdir %reference_output%\package
40+
.\.nuget\nuget.exe install Microsoft.Windows.CppWinRT -o %reference_output%\package
41+
set reference_cppwinrt=
42+
for /F "delims=" %%a in ('dir /s /b %reference_output%\package\cppwinrt.exe') DO set reference_cppwinrt=%%a
43+
if "%reference_cppwinrt%"=="" (
44+
echo Could not find the reference cppwinrt.exe under %reference_output%\package
45+
goto :EOF
46+
)
47+
48+
echo Generating reference projection from %reference_cppwinrt% to %reference_output%\cppwinrt
49+
%reference_cppwinrt% -in local -out %reference_output% -verbose
50+
echo.

build_projection.cmd

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,13 @@ if "%target_configuration%"=="" (
2727
set target_configuration=Debug
2828
)
2929

30+
set cppwinrt_exe=%~p0\_build\x64\Release\cppwinrt.exe
31+
32+
if not exist "%cppwinrt_exe%" (
33+
echo Remember to build the "prebuild" and then "cppwinrt" projects for Release x64 first
34+
goto :eof
35+
)
36+
3037
echo Building projection into %target_platform% %target_configuration%
31-
%~p0\_build\x64\Release\cppwinrt.exe -in local -out %~p0\_build\%target_platform%\%target_configuration% -verbose
38+
%cppwinrt_exe% -in local -out %~p0\_build\%target_platform%\%target_configuration% -verbose
3239
echo.

build_test_all.cmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ if "%target_configuration%"=="" set target_configuration=Release
1010
if "%target_version%"=="" set target_version=1.2.3.4
1111

1212
if not exist ".\.nuget" mkdir ".\.nuget"
13-
if not exist ".\.nuget\nuget.exe" powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe"
13+
if not exist ".\.nuget\nuget.exe" powershell -Command "$ProgressPreference = 'SilentlyContinue' ; Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe"
1414

1515
call .nuget\nuget.exe restore cppwinrt.sln"
1616
call .nuget\nuget.exe restore natvis\cppwinrtvisualizer.sln

cppwinrt/code_writers.h

Lines changed: 55 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2035,13 +2035,39 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
20352035
{
20362036
for (auto&& [name, info] : interfaces)
20372037
{
2038-
if (!info.overridable)
2038+
if (!info.overridable && !info.is_protected)
20392039
{
20402040
w.write(", %", name);
20412041
}
20422042
}
20432043
}
20442044

2045+
static void write_class_override_protected_requires(writer& w, get_interfaces_t const& interfaces)
2046+
{
2047+
bool first = true;
2048+
2049+
for (auto&& [name, info] : interfaces)
2050+
{
2051+
if (info.is_protected)
2052+
{
2053+
if (first)
2054+
{
2055+
first = false;
2056+
w.write(",\n protected impl::require<D, %", name);
2057+
}
2058+
else
2059+
{
2060+
w.write(", %", name);
2061+
}
2062+
}
2063+
}
2064+
2065+
if (!first)
2066+
{
2067+
w.write('>');
2068+
}
2069+
}
2070+
20452071
static void write_class_override_defaults(writer& w, get_interfaces_t const& interfaces)
20462072
{
20472073
bool first = true;
@@ -2073,6 +2099,18 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
20732099
}
20742100
}
20752101

2102+
static void write_class_override_friends(writer& w, get_interfaces_t const& interfaces)
2103+
{
2104+
for (auto&& [name, info] : interfaces)
2105+
{
2106+
if (info.is_protected)
2107+
{
2108+
w.write("\n friend impl::consume_t<D, %>;", name);
2109+
w.write("\n friend impl::require_one<D, %>;", name);
2110+
}
2111+
}
2112+
}
2113+
20762114
static void write_call_factory(writer& w, TypeDef const& type, TypeDef const& factory)
20772115
{
20782116
std::string factory_name;
@@ -2243,10 +2281,10 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
22432281
auto format = R"( template <typename D, typename... Interfaces>
22442282
struct %T :
22452283
implements<D%, composing, Interfaces...>,
2246-
impl::require<D%>,
2284+
impl::require<D%>%,
22472285
impl::base<D, %%>%
22482286
{
2249-
using composable = %;
2287+
using composable = %;%
22502288
protected:
22512289
%% };
22522290
)";
@@ -2258,10 +2296,12 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
22582296
type_name,
22592297
bind<write_class_override_implements>(interfaces),
22602298
bind<write_class_override_requires>(interfaces),
2299+
bind<write_class_override_protected_requires>(interfaces),
22612300
type_name,
22622301
bind<write_class_override_bases>(type),
22632302
bind<write_class_override_defaults>(interfaces),
22642303
type_name,
2304+
bind<write_class_override_friends>(interfaces),
22652305
bind<write_class_override_constructors>(type, factories),
22662306
bind<write_class_override_usings>(interfaces));
22672307
}
@@ -2326,18 +2366,21 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
23262366

23272367
for (auto&& [interface_name, info] : get_interfaces(w, type))
23282368
{
2329-
if (info.defaulted && !info.base)
2369+
if (!info.is_protected && !info.overridable)
23302370
{
2331-
for (auto&& method : info.type.MethodList())
2371+
if (info.defaulted && !info.base)
23322372
{
2333-
method_usage[get_name(method)].insert(default_interface_name);
2373+
for (auto&& method : info.type.MethodList())
2374+
{
2375+
method_usage[get_name(method)].insert(default_interface_name);
2376+
}
23342377
}
2335-
}
2336-
else
2337-
{
2338-
for (auto&& method : info.type.MethodList())
2378+
else
23392379
{
2340-
method_usage[get_name(method)].insert(interface_name);
2380+
for (auto&& method : info.type.MethodList())
2381+
{
2382+
method_usage[get_name(method)].insert(interface_name);
2383+
}
23412384
}
23422385
}
23432386
}
@@ -2804,7 +2847,7 @@ struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable<T, D, %>
28042847

28052848
for (auto&& [interface_name, info] : get_interfaces(w, type))
28062849
{
2807-
if (!info.defaulted || info.base)
2850+
if ((!info.defaulted || info.base) && (!info.is_protected && !info.overridable))
28082851
{
28092852
if (first)
28102853
{

cppwinrt/component_writers.h

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -743,13 +743,13 @@ catch (...) { return winrt::to_hresult(); }
743743
auto format = R"(namespace winrt::@::implementation
744744
{
745745
template <typename D%, typename... I>
746-
struct WINRT_IMPL_EMPTY_BASES %_base : implements<D, @::%%%, %I...>%%%
746+
struct WINRT_IMPL_EMPTY_BASES %_base : implements<D, @::%%%, %I...>%%%%
747747
{
748748
using base_type = %_base;
749749
using class_type = @::%;
750750
using implements_type = typename %_base::implements_type;
751751
using implements_type::implements_type;
752-
%
752+
%%
753753
hstring GetRuntimeClassName() const
754754
{
755755
return L"%.%";
@@ -764,6 +764,8 @@ catch (...) { return winrt::to_hresult(); }
764764
std::string base_type_argument;
765765
std::string no_module_lock;
766766
std::string external_requires;
767+
std::string external_protected_requires;
768+
std::string friends;
767769

768770
if (base_type)
769771
{
@@ -774,7 +776,9 @@ catch (...) { return winrt::to_hresult(); }
774776
composable_base_name = w.write_temp("using composable_base = %;", base_type);
775777
auto base_interfaces = get_interfaces(w, base_type);
776778
uint32_t base_interfaces_count{};
779+
uint32_t protected_base_interfaces_count{};
777780
external_requires = ",\n impl::require<D";
781+
external_protected_requires = ",\n protected impl::require<D";
778782

779783
for (auto&&[name, info] : base_interfaces)
780784
{
@@ -783,9 +787,25 @@ catch (...) { return winrt::to_hresult(); }
783787
continue;
784788
}
785789

786-
++base_interfaces_count;
787-
external_requires += ", ";
788-
external_requires += name;
790+
if (info.is_protected || info.overridable)
791+
{
792+
++protected_base_interfaces_count;
793+
external_protected_requires += ", ";
794+
external_protected_requires += name;
795+
796+
friends += "\n friend impl::consume_t<D, ";
797+
friends += name;
798+
friends += ">;";
799+
friends += "\n friend impl::require_one<D, ";
800+
friends += name;
801+
friends += ">;";
802+
}
803+
else
804+
{
805+
++base_interfaces_count;
806+
external_requires += ", ";
807+
external_requires += name;
808+
}
789809
}
790810

791811
if (base_interfaces_count)
@@ -796,6 +816,15 @@ catch (...) { return winrt::to_hresult(); }
796816
{
797817
external_requires.clear();
798818
}
819+
820+
if (protected_base_interfaces_count)
821+
{
822+
external_protected_requires += '>';
823+
}
824+
else
825+
{
826+
external_protected_requires.clear();
827+
}
799828
}
800829
else
801830
{
@@ -816,13 +845,15 @@ catch (...) { return winrt::to_hresult(); }
816845
base_type_argument,
817846
no_module_lock,
818847
external_requires,
848+
external_protected_requires,
819849
bind<write_component_class_base>(type),
820850
bind<write_component_override_defaults>(type),
821851
type_name,
822852
type_namespace,
823853
type_name,
824854
type_name,
825855
composable_base_name,
856+
friends,
826857
type_namespace,
827858
type_name,
828859
bind<write_component_class_override_constructors>(type),

cppwinrt/helpers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,7 @@ namespace cppwinrt
527527
{
528528
TypeDef type;
529529
bool is_default{};
530+
bool is_protected{};
530531
bool defaulted{};
531532
bool overridable{};
532533
bool base{};
@@ -577,6 +578,7 @@ namespace cppwinrt
577578
auto type = impl.Interface();
578579
auto name = w.write_temp("%", type);
579580
info.is_default = has_attribute(impl, "Windows.Foundation.Metadata", "DefaultAttribute");
581+
info.is_protected = has_attribute(impl, "Windows.Foundation.Metadata", "ProtectedAttribute");
580582
info.defaulted = !base && (defaulted || info.is_default);
581583

582584
{

prepare_versionless_diffs.cmd

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
@echo off
2+
3+
setlocal ENABLEDELAYEDEXPANSION
4+
5+
set target_platform=%1
6+
set target_configuration=%2
7+
if "%target_platform%"=="" set target_platform=x64
8+
9+
if /I "%target_platform%" equ "all" (
10+
if "%target_configuration%"=="" (
11+
set target_configuration=all
12+
)
13+
call %0 x86 !target_configuration!
14+
call %0 x64 !target_configuration!
15+
call %0 arm !target_configuration!
16+
call %0 arm64 !target_configuration!
17+
goto :eof
18+
)
19+
20+
if /I "%target_configuration%" equ "all" (
21+
call %0 %target_platform% Debug
22+
call %0 %target_platform% Release
23+
goto :eof
24+
)
25+
26+
if "%target_configuration%"=="" (
27+
set target_configuration=Debug
28+
)
29+
30+
set reference_output=%~p0\_reference\%target_platform%\%target_configuration%
31+
set build_output=%~p0\_build\%target_platform%\%target_configuration%
32+
33+
echo Removing version stamps from %reference_output%\winrt
34+
pushd %reference_output%\winrt
35+
powershell -Command "gci -r -include *.h,*.ixx | %%{ (get-content $_) -replace 'was generated by.*|CPPWINRT_VERSION.*','' | set-content $_ }"
36+
popd
37+
38+
echo Removing version stamps from %build_output%\winrt
39+
pushd %build_output%\winrt
40+
powershell -Command "gci -r -include *.h,*.ixx | %%{ (get-content $_) -replace 'was generated by.*|CPPWINRT_VERSION.*','' | set-content $_ }"
41+
popd

test/old_tests/Composable/Base.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ namespace winrt::Composable::implementation
4141
return 42;
4242
}
4343

44+
int32_t Base::ProtectedMethod()
45+
{
46+
return 0xDEADBEEF;
47+
}
48+
4449
hstring Base::Name() const
4550
{
4651
return m_name;

test/old_tests/Composable/Base.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namespace winrt::Composable::implementation
1818
hstring OverridableMethod() ;
1919
virtual hstring OverridableVirtualMethod();
2020
int32_t OverridableNoexceptMethod() noexcept;
21+
int32_t ProtectedMethod();
2122

2223
hstring Name() const;
2324

0 commit comments

Comments
 (0)