Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
April 2021
- 70 participants
- 1934 messages
[PATCH 1/6] jscript: Get dispid and call invoke in a single step when setting a member property.
by Jacek Caban
Signed-off-by: Jacek Caban <jacek(a)codeweavers.com>
---
dlls/jscript/compile.c | 89 ++++++++++++++++++--------------------
dlls/jscript/dispex.c | 32 ++++++++++++++
dlls/jscript/engine.c | 54 +++++++++++++++++++++++
dlls/jscript/engine.h | 2 +
dlls/jscript/jscript.h | 1 +
dlls/jscript/tests/lang.js | 6 +++
dlls/jscript/tests/run.c | 19 ++++++++
7 files changed, 156 insertions(+), 47 deletions(-)
April 1, 2021
Re: [PATCH 2/5] wined3d: Introduce wined3d_device_context_draw_indexed().
by Henri Verbeet
On Wed, 31 Mar 2021 at 18:38, Zebediah Figura <z.figura12(a)gmail.com> wrote:
> +void CDECL wined3d_device_context_draw_indexed(struct wined3d_device_context *context, unsigned int base_vertex_index,
> + unsigned int start_index, unsigned int index_count, unsigned int start_instance, unsigned int instance_count)
The base vertex index is signed. Passing it as an unsigned integer in
the API will probably work out fine in practice, but it would probably
be best avoided.
April 1, 2021
Re: [PATCH] include: Add ID2D1DeviceContext1 definition.
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 1, 2021
Re: [PATCH 1/5] wined3d: Check for a valid index buffer in d3d[89]_device_DrawIndexedPrimitive().
by Henri Verbeet
Signed-off-by: Henri Verbeet <hverbeet(a)codeweavers.com>
April 1, 2021
[tools] testbot/web: Fix Apache intermittently not finding CreatePatches().
by Francois Gouget
If CreatePatches() is not refered to with its full name Apache (mod_perl
really) intermittently fails to find it and issues an error of the form:
Undefined subroutine &WineTestBot::Jobs::CreatePatches called at .../testbot/lib/ObjectModel/Item.pm line 275.
Strangely enough it never has trouble finding any of the other Create*()
functions.
Signed-off-by: Francois Gouget <fgouget(a)codeweavers.com>
---
If anyone can explain this mystery to me I would be grateful.
---
testbot/lib/WineTestBot/Jobs.pm | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/testbot/lib/WineTestBot/Jobs.pm b/testbot/lib/WineTestBot/Jobs.pm
index 5dbc1a7f4..a5ae13525 100644
--- a/testbot/lib/WineTestBot/Jobs.pm
+++ b/testbot/lib/WineTestBot/Jobs.pm
@@ -454,7 +454,10 @@ my @PropertyDescriptors = (
CreateBasicPropertyDescriptor("Remarks", "Remarks", !1, !1, "A", 128),
CreateBasicPropertyDescriptor("Submitted", "Submitted", !1, !1, "DT", 19),
CreateBasicPropertyDescriptor("Ended", "Ended", !1, !1, "DT", 19),
- CreateItemrefPropertyDescriptor("Patch", "Submitted from patch", !1, !1, \&CreatePatches, ["PatchId"]),
+ # Somehow mod_perl sometimes fails to find CreatePatches() if not given the
+ # fully qualified name, but never has any trouble with the other Create*()
+ # functions.
+ CreateItemrefPropertyDescriptor("Patch", "Submitted from patch", !1, !1, \&WineTestBot::Patches::CreatePatches, ["PatchId"]),
CreateDetailrefPropertyDescriptor("Steps", "Steps", !1, !1, \&CreateSteps),
);
SetDetailrefKeyPrefix("Job", @PropertyDescriptors);
--
2.20.1
April 1, 2021
[tools] testbot/PatchUtils: Rework GetPatchImpacts() to fix patchset handling.
by Francois Gouget
Rename the Tests set to Modules since it contains information about each
module impacted by the patch (which also happens to contain a list of
the still existing and patched test units).
Better separate the build impacts (Build*) from the test impacts
(Patched*): the former impact the build of all following parts, while
only the last part impacts the tests that need to be run.
This stops the TestBot from running test units which were not impacted
by the _last part_ of the patchset.
Document the returned $Impacts structure.
---
For the following wtbsuite tests:
WTBS S8.2 - Check the make_* scripts are still run (advpack:advpack).
WTBS S5.6 - Rerun a single test despite previous patches (serialui:confdlg).
WTBS S5.5 - A non-test patch to another dll (serialui).
WTBS S5.4 - Rerun a single test despite previous patches (msi:source).
WTBS S4.3 - Trigger an autoconf run.
WTBS S4.2 - Another non-test Wine patch (api-ms-win-core-timezone-l1-1-0).
---
testbot/bin/WineRunBuild.pl | 6 +-
testbot/bin/build/WineTest.pl | 30 +--
testbot/lib/WineTestBot/PatchUtils.pm | 370 ++++++++++++++++----------
testbot/lib/WineTestBot/Patches.pm | 15 +-
testbot/web/Submit.pl | 36 ++-
5 files changed, 266 insertions(+), 191 deletions(-)
diff --git a/testbot/bin/WineRunBuild.pl b/testbot/bin/WineRunBuild.pl
index adb1959f0..8a92f88d1 100755
--- a/testbot/bin/WineRunBuild.pl
+++ b/testbot/bin/WineRunBuild.pl
@@ -461,16 +461,16 @@ foreach my $TestStep (@{$Job->Steps->GetItems()})
my $Impacts = GetPatchImpacts($FileName);
my $StepDir = $Step->CreateDir();
-foreach my $TestInfo (values %{$Impacts->{Tests}})
+foreach my $Module (values %{$Impacts->{Modules}})
{
foreach my $Bits ("", "64")
{
- my $Local = "$TestInfo->{ExeBase}$Bits.exe";
+ my $Local = "$Module->{ExeBase}$Bits.exe";
next if (!$TestExes{$Local});
Debug(Elapsed($Start), " Retrieving '$Local'\n");
my $BuildDir = "wine-$TestExes{$Local}";
- if ($TA->GetFile("$BuildDir/$TestInfo->{Path}/$TestInfo->{ExeBase}.exe",
+ if ($TA->GetFile("$BuildDir/$Module->{Path}/$Module->{ExeBase}.exe",
"$StepDir/$Local"))
{
chmod 0664, "$StepDir/$Local";
diff --git a/testbot/bin/build/WineTest.pl b/testbot/bin/build/WineTest.pl
index 3e13637d0..ffd665faa 100755
--- a/testbot/bin/build/WineTest.pl
+++ b/testbot/bin/build/WineTest.pl
@@ -118,21 +118,21 @@ sub TestPatch($$)
}
else
{
- foreach my $Module (sort keys %{$Impacts->{Tests}})
+ foreach my $ModuleName (sort keys %{$Impacts->{Modules}})
{
- my $TestInfo = $Impacts->{Tests}->{$Module};
- if ($TestInfo->{All} or
- ($Mission->{test} eq "module" and $TestInfo->{PatchedModule}))
+ my $Module = $Impacts->{Modules}->{$ModuleName};
+ if ($Module->{All} or
+ ($Mission->{test} eq "module" and $Module->{Patched}))
{
# When given a module name WineTest runs all its tests.
# But make sure the module actually has tests first!
- push @TestList, $Module if (%{$TestInfo->{Files}});
+ push @TestList, $ModuleName if (@{$Module->{TestUnits}});
}
else
{
- foreach my $PatchedUnit (sort keys %{$TestInfo->{PatchedUnits}})
+ foreach my $TestUnit (@{$Module->{RunTestUnits}})
{
- push @TestList, "$Module:$PatchedUnit";
+ push @TestList, "$ModuleName:$TestUnit";
}
}
}
@@ -157,15 +157,15 @@ sub TestPatch($$)
sub GetExecutable($$$)
{
- my ($Mission, $Impacts, $Module) = @_;
+ my ($Mission, $Impacts, $ModuleName) = @_;
- my $TestInfo = $Impacts->{Tests}->{$Module};
- my $TestExecutable = "$TestInfo->{ExeBase}.exe";
+ my $Module = $Impacts->{Modules}->{$ModuleName};
+ my $TestExecutable = "$Module->{ExeBase}.exe";
my $Dst = "$DataDir/staging/$TestExecutable";
unlink $Dst;
my $WineDir = GetWineDir($Mission);
- if (!symlink "$WineDir/$TestInfo->{Path}/$TestExecutable", $Dst)
+ if (!symlink "$WineDir/$Module->{Path}/$TestExecutable", $Dst)
{
LogMsg "Could not find $TestExecutable: $!\n";
return undef;
@@ -207,7 +207,7 @@ sub TestExe($$$)
#
my $Action = "";
-my ($Usage, $OptNoSubmit, $MissionStatement, $FileName, $Module, $BaseTag);
+my ($Usage, $OptNoSubmit, $MissionStatement, $FileName, $ModuleName, $BaseTag);
while (@ARGV)
{
my $Arg = shift @ARGV;
@@ -268,9 +268,9 @@ while (@ARGV)
last; # The remainder are the executable's arguments
}
}
- elsif ($Action eq "testpatch" and !defined $Module)
+ elsif ($Action eq "testpatch" and !defined $ModuleName)
{
- $Module = $Arg;
+ $ModuleName = $Arg;
last; # The remainder are the executable's arguments
}
else
@@ -431,7 +431,7 @@ foreach my $Mission (@{$TaskMissions->{Missions}})
}
elsif (@ARGV)
{
- my $FileName = GetExecutable($Mission, $Impacts, $Module);
+ my $FileName = GetExecutable($Mission, $Impacts, $ModuleName);
exit(1) if (!$FileName or !TestExe($Mission, $FileName, \@ARGV));
}
else
diff --git a/testbot/lib/WineTestBot/PatchUtils.pm b/testbot/lib/WineTestBot/PatchUtils.pm
index 87e4508ba..0d60fdb29 100644
--- a/testbot/lib/WineTestBot/PatchUtils.pm
+++ b/testbot/lib/WineTestBot/PatchUtils.pm
@@ -187,25 +187,24 @@ sub LastPartSeparator()
return "===== TestBot: Last patchset part =====\n";
}
-sub _CreateTestInfo($$$)
+sub _CreateModuleInfo($$$)
{
my ($Impacts, $Root, $Dir) = @_;
- my $Module = _Dir2ModuleName($Root, $Dir);
- $Impacts->{BuildModules}->{$Module} = 1;
+ my $ModuleName = _Dir2ModuleName($Root, $Dir);
$Impacts->{IsWinePatch} = 1;
- my $Tests = $Impacts->{Tests};
- if (!$Tests->{$Module})
+ my $Module = $Impacts->{Modules}->{$ModuleName};
+ if (!$Module)
{
- $Tests->{$Module} = {
- "Module" => $Module,
+ $Impacts->{Modules}->{$ModuleName} = $Module = {
+ "Name" => $ModuleName,
"Path" => "$Root/$Dir/tests",
- "ExeBase" => "${Module}_test",
+ "ExeBase" => "${ModuleName}_test",
};
- foreach my $File (keys %{$_TestList->{$Module}})
+ foreach my $File (keys %{$_TestList->{$ModuleName}})
{
- $Tests->{$Module}->{Files}->{$File} = 0; # not modified
+ $Module->{TestFiles}->{$File} = 0; # not modified
}
}
@@ -231,9 +230,9 @@ sub _HandleFile($$$)
{
my ($Root, $Dir, $File) = ($1, $2, $3);
- my $Module = _CreateTestInfo($Impacts, $Root, $Dir);
- $Impacts->{PatchedTests} = 1;
- $Impacts->{Tests}->{$Module}->{Files}->{$File} = $Change;
+ my $Module = _CreateModuleInfo($Impacts, $Root, $Dir);
+ $Module->{TestFiles}->{$File} = $Change;
+ $Module->{PatchedTests} = 1;
if ($File eq "Makefile.in" and $Change ne "modify")
{
@@ -247,10 +246,9 @@ sub _HandleFile($$$)
foreach my $Dir ($PatchedDir, keys %{$_WineParentDirs->{$PatchedDir} || {}})
{
- my $Module = _CreateTestInfo($Impacts, $Root, $Dir);
- $Impacts->{Tests}->{$Module}->{PatchedModule} = 1;
+ my $Module = _CreateModuleInfo($Impacts, $Root, $Dir);
+ $Module->{Patched} = 1;
}
- $Impacts->{PatchedModules} = 1;
if ($File eq "Makefile.in" and $Change ne "modify")
{
@@ -258,35 +256,31 @@ sub _HandleFile($$$)
$Impacts->{MakeMakefiles} = 1;
}
}
- else
+ elsif ($Impacts->{WineFiles}->{$FilePath})
{
- my $WineFiles = $Impacts->{WineFiles} || $_WineFiles;
- if ($WineFiles->{$FilePath})
+ if ($FilePath !~ /^(?:$AmbiguousPathsRe)/)
{
- if ($FilePath !~ /^(?:$AmbiguousPathsRe)/)
- {
- $Impacts->{IsWinePatch} = 1;
- }
- # Else this file exists in Wine but has a very common name so it may just
- # as well belong to another repository.
+ $Impacts->{IsWinePatch} = 1;
+ }
+ # Else this file exists in Wine but has a very common name so it may just
+ # as well belong to another repository.
- if ($FilePath !~ /^(?:$IgnoredPathsRe)/)
+ if ($FilePath !~ /^(?:$IgnoredPathsRe)/)
+ {
+ $Impacts->{BuildRoot} = $Impacts->{PatchedRoot} = 1;
+ if ($FilePath =~ m~/Makefile\.in$~ and $Change ne "modify")
{
- $Impacts->{PatchedRoot} = 1;
- if ($FilePath =~ m~/Makefile\.in$~ and $Change ne "modify")
- {
- # This adds / removes a directory
- $Impacts->{MakeMakefiles} = 1;
- }
+ # This adds / removes a directory
+ $Impacts->{MakeMakefiles} = 1;
}
- # Else patches to this file don't impact the Wine build.
- }
- elsif ($FilePath =~ m~/Makefile\.in$~ and $Change eq "new")
- {
- # This may or may not be a Wine patch but the new Makefile.in will be
- # added to the build by make_makefiles.
- $Impacts->{PatchedRoot} = $Impacts->{MakeMakefiles} = 1;
}
+ # Else patches to this file don't impact the Wine build.
+ }
+ elsif ($FilePath =~ m~/Makefile\.in$~ and $Change eq "new")
+ {
+ # This may or may not be a Wine patch but the new Makefile.in will be
+ # added to the build by make_makefiles.
+ $Impacts->{BuildRoot} = $Impacts->{PatchedRoot} = $Impacts->{MakeMakefiles} = 1;
}
}
@@ -299,6 +293,121 @@ Analyzes a patch and returns a hashtable describing the impact it has on the
Wine build: whether it requires updating the makefiles, re-running autoconf or
configure, whether it impacts the tests, etc.
+Notes: A module is a dlls/ or programs/ directory. All references to patchsets
+mean the patchset as being tested. So if a patch series has 5 parts and the
+TestBot is testing part 3, the LAST PART is part 3, and ANY PART means part 1,
+2 or 3.
+
+=over
+=item IsWinePatch
+
+This flag is set if the LAST PART of the patchset has been identified as a
+Wine patch.
+
+=item BuildModules
+
+This is a count of the number of modules where either the source or the tests
+have been modified by ANY PART of the patchset.
+
+=item BuildRoot
+
+This flag is set if ANY PART of the patchset modified the Wine source
+outside of a specific module in a way that impacts the build.
+
+=item Autoconf
+=item MakeErrors
+=item MakeFir
+=item MakeMakefiles
+=item MakeOpenGL
+=item MakeRequests
+=item MakeUnicode
+=item MakeVulkan
+
+These flags are set if ANY PART of the patchset requires running the
+corresponding script before building Wine.
+
+=item TestBuild
+
+This flag is set if the LAST PART of the patchset modified the Wine source
+such that the build needs to be tested.
+
+=item RunTestUnits
+=item PatchedModules
+=item PatchedModuleOrTests
+
+These are counts based on the LAST PART of the patchset, so excluding any
+impact from PREVIOUS PARTS. They count, respectively, the test units that need
+to be rerun (so excluding deleted ones), the modules whose source has been
+modified (including file deletions), and the modules where either the source
+or the tests have been modified (again including deletions).
+
+=item PatchedRoot
+
+This flag is set if the LAST PART of the patchset modified the Wine source
+outside of a specific module.
+
+=item Modules
+
+A set of structures containing information about each module impacted by
+ANY PART of the patchset, indexed by module name. Each module has the
+following information:
+
+=over
+
+=item Patched
+
+This flag is true if the LAST PART of the patchset has modified the module
+itself, so excluding its test units.
+
+=item PatchedTests
+
+This flag is true if the LAST PART of the patchset has modified the tests of
+the module.
+
+=item TestUnits
+
+This is an alphabetical list of the module's test units, after applying the
+patchset (so excluding any test unit deleted by the patchset).
+
+=item All
+
+This flag is true if all the module's test units are impacted by the LAST PART
+of the patchset. This is typically true if a resource file or helper dll is
+modified.
+
+=item RunTestUnits
+
+This is an alphabetical list of the module's test units that are impacted by
+the LAST PART of the patchset such that they should be rerun. In particular if
+All is true this is identical to TestUnits.
+
+=item TestFiles
+
+This hashtable is indexed by the filenames of the files in the module's tests
+directory. Note that this is not limited to the test unit C files but also
+includes the resource files, makefile and helper dlls (but not testlist.c).
+For each file the value describes the modification performed by the
+LAST PART of the patchset: 0 (false) if there is no modification, "modify" for
+modified files, "new" for added files and "rm" for deletions.
+
+=back
+
+=item WineFiles
+
+The keys of this hashtable enumerate all the Wine source files, including all
+files added and removed by PREVIOUS PARTS of the patchset.
+This is mostly for the internal use of the impact analyzer and should be
+treated as read-only by outside code (and copy-on-write by the analyzer).
+
+=item NewFiles
+=item DeletedFiles
+
+The keys of these hashtables enumerate, respectively, the files added and
+removed by the LAST PART of the patchset.
+This is mostly for the internal use of the impact analyzer.
+
+=back
+
=back
=cut
@@ -309,39 +418,31 @@ sub GetPatchImpacts($)
my $fh;
return undef if (!open($fh, "<", $PatchFileName));
- my $Impacts = {
- # Number of test units impacted either directly, or indirectly by a module
- # patch.
- ModuleUnitCount => 0,
- # Number of patched test units.
- TestUnitCount => 0,
- # The modules that need a rebuild, even if only for the tests.
- BuildModules => {},
- # Information about 'tests' directories.
- Tests => {},
- };
_LoadWineFiles();
- my $PastImpacts;
+ my $Impacts = { Modules => {}, WineFiles => $_WineFiles };
my ($Path, $Change);
while (my $Line = <$fh>)
{
+ # All the files on '---' lines will be passed to _HandleFile() when the
+ # corresponding '+++' line comes.
if ($Line =~ m=^--- \w+/(aclocal\.m4|configure\.ac)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{Autoconf} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{Autoconf} = 1;
+ $Impacts->{PatchedRoot} = 1;
}
elsif ($Line =~ m=^--- \w+/(tools/make_makefiles)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{MakeMakefiles} = 1;
- $Impacts->{IsWinePatch} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{MakeMakefiles} = 1;
+ $Impacts->{PatchedRoot} = $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- \w+/(tools/make_requests|server/protocol\.def)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{MakeRequests} = 1;
- $Impacts->{IsWinePatch} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{MakeRequests} = 1;
+ $Impacts->{PatchedRoot} = $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- \w+/(dlls/dsound/make_fir)$=)
{
@@ -358,20 +459,20 @@ sub GetPatchImpacts($)
elsif ($Line =~ m=^--- \w+/(dlls/opengl32/make_opengl|dlls/opengl32/winegl\.xml|include/wine/wgl_driver\.h)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{MakeOpenGL} = 1;
- $Impacts->{IsWinePatch} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{MakeOpenGL} = 1;
+ $Impacts->{PatchedRoot} = $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- \w+/(dlls/winevulkan/make_vulkan)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{MakeVulkan} = 1;
- $Impacts->{IsWinePatch} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{MakeVulkan} = 1;
+ $Impacts->{PatchedRoot} = $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- \w+/(tools/make_unicode)$=)
{
$Path = $1;
- $Impacts->{PatchedRoot} = $Impacts->{MakeUnicode} = 1;
- $Impacts->{IsWinePatch} = 1;
+ $Impacts->{BuildRoot} = $Impacts->{MakeUnicode} = 1;
+ $Impacts->{PatchedRoot} = $Impacts->{IsWinePatch} = 1;
}
elsif ($Line =~ m=^--- /dev/null$=)
{
@@ -398,16 +499,18 @@ sub GetPatchImpacts($)
elsif ($Line eq LastPartSeparator())
{
# All the diffs so far belong to previous parts of this patchset.
- # But:
- # - Only the last part must be taken into account to determine if a
- # rebuild and testing is needed.
- # - Yet if a rebuild is needed the previous parts' patches will impact
- # the scope of the rebuild so that information must be preserved.
- # So save current impacts in $PastImpacts and reset the current state.
- $PastImpacts = {};
-
- # Build a copy of the Wine files list reflecting the current situation.
- $Impacts->{WineFiles} = { %$_WineFiles } if (!$Impacts->{WineFiles});
+ # But only the last part must be taken into account to determine if
+ # testing is needed. So reset all the Patched* fields.
+
+ delete $Impacts->{PatchedRoot};
+ foreach my $Module (values %{$Impacts->{Modules}})
+ {
+ delete $Module->{Patched};
+ delete $Module->{PatchedTests};
+ }
+
+ # Make a copy of the Wine files list reflecting the current situation.
+ $Impacts->{WineFiles} = { %{$Impacts->{WineFiles}} };
if ($Impacts->{IsWinePatch} or $Impacts->{MakeMakefiles})
{
map { $Impacts->{WineFiles}->{$_} = 1 } keys %{$Impacts->{NewFiles}};
@@ -417,30 +520,23 @@ sub GetPatchImpacts($)
delete $Impacts->{IsWinePatch};
}
- # The modules impacted by previous parts will still need to be built,
- # but only if the last part justifies a build. So make a backup.
- $PastImpacts->{BuildModules} = $Impacts->{BuildModules};
- $Impacts->{BuildModules} = {};
-
- # Also backup the build-related fields.
- foreach my $Field ("PatchedRoot", "PatchedModules", "PatchedTests")
+ # Reset the status of all modified test unit files to not modified
+ # (deleted files won't come back).
+ foreach my $Module (values %{$Impacts->{Modules}})
{
- $PastImpacts->{$Field} = $Impacts->{$Field};
- $Impacts->{$Field} = undef;
- }
-
- # Reset the status of all test unit files to not modified.
- foreach my $TestInfo (values %{$Impacts->{Tests}})
- {
- foreach my $File (keys %{$TestInfo->{Files}})
+ my $TestFiles = $Module->{TestFiles};
+ foreach my $File (keys %$TestFiles)
{
- if ($TestInfo->{Files}->{$File} ne "rm")
+ if ($TestFiles->{$File} eq "rm")
+ {
+ delete $TestFiles->{$File};
+ }
+ else
{
- $TestInfo->{Files}->{$File} = 0;
+ $TestFiles->{$File} = 0;
}
}
}
- $Impacts->{ModuleUnitCount} = $Impacts->{TestUnitCount} = 0;
}
else
{
@@ -450,76 +546,58 @@ sub GetPatchImpacts($)
}
close($fh);
- foreach my $TestInfo (values %{$Impacts->{Tests}})
+ # Calculate the impact counts and lists of test units.
+ foreach my $Module (values %{$Impacts->{Modules}})
{
- # For each module, identify modifications to non-C files and helper dlls
- foreach my $File (keys %{$TestInfo->{Files}})
+ $Module->{TestUnits} = [];
+ $Module->{RunTestUnits} = [];
+
+ # For each module build the lists of test units
+ my $TestFiles = $Module->{TestFiles};
+ foreach my $File (sort keys %$TestFiles)
{
- # Skip unmodified files
- next if (!$TestInfo->{Files}->{$File});
# Assume makefile modifications may break the build but not the tests
next if ($File eq "Makefile.in");
my $Base = $File;
if ($Base !~ s/(?:\.c|\.spec)$//)
{
- # Any change to a non-C non-Spec file can potentially impact all tests
- $TestInfo->{All} = 1;
- last;
+ # Any change to a non-C non-Spec file (including deletions) can
+ # potentially impact all tests
+ $Module->{All} = 1 if ($TestFiles->{$File});
+ next;
}
- if (exists $TestInfo->{Files}->{"$Base.spec"} and
- ($TestInfo->{Files}->{"$Base.c"} or
- $TestInfo->{Files}->{"$Base.spec"}))
- {
- # Any change to a helper dll can potentially impact all tests
- $TestInfo->{All} = 1;
- last;
- }
- }
- $TestInfo->{PatchedUnits} = {};
- foreach my $File (keys %{$TestInfo->{Files}})
- {
- my $Base = $File;
- # Non-C files are not test units
- next if ($Base !~ s/(?:\.c|\.spec)$//);
- # Helper dlls are not test units
- next if (exists $TestInfo->{Files}->{"$Base.spec"});
- # Don't try running a deleted test unit obviously
- next if ($TestInfo->{Files}->{$File} eq "rm");
- $TestInfo->{AllUnits}->{$Base} = 1;
-
- if ($TestInfo->{All} or $TestInfo->{Files}->{$File})
+ if (exists $TestFiles->{"$Base.spec"})
{
- $TestInfo->{PatchedUnits}->{$Base} = 1;
- $Impacts->{ModuleUnitCount}++;
+ # This is a helper dll, not a test unit.
+ # Any change, including deletions, could impact all tests.
+ $Module->{All} ||= ($TestFiles->{"$Base.c"} or
+ $TestFiles->{"$Base.spec"});
+ next;
}
- elsif ($TestInfo->{PatchedModule})
+
+ if ($TestFiles->{$File} ne "rm")
{
- # The module has been patched so this test unit is impacted indirectly.
- $Impacts->{ModuleUnitCount}++;
+ push @{$Module->{TestUnits}}, $Base;
+ push @{$Module->{RunTestUnits}}, $Base if ($TestFiles->{$File});
}
}
+ $Module->{All} ||= (@{$Module->{TestUnits}} == @{$Module->{RunTestUnits}});
+ $Module->{RunTestUnits} = $Module->{TestUnits} if ($Module->{All});
- $TestInfo->{UnitCount} = scalar(keys %{$TestInfo->{PatchedUnits}});
- $Impacts->{TestUnitCount} += $TestInfo->{UnitCount};
- }
-
- if ($Impacts->{PatchedRoot} or $Impacts->{PatchedModules} or
- $Impacts->{PatchedTests})
- {
- # Any patched area will need to be rebuilt...
- $Impacts->{RebuildRoot} = $Impacts->{PatchedRoot};
- $Impacts->{RebuildModules} = $Impacts->{PatchedModules};
+ my $RunTestUnits = scalar(@{$Module->{RunTestUnits}});
+ $Impacts->{RunTestUnits} += $RunTestUnits;
+ $Impacts->{ModuleTestUnits} += $Module->{Patched} ? scalar(@{$Module->{TestUnits}}) : $RunTestUnits;
- # ... even if the patch was in previous parts
- if ($PastImpacts)
+ if ($Module->{Patched} or $Module->{PatchedTests})
{
- $Impacts->{RebuildRoot} ||= $PastImpacts->{PatchedRoot};
- $Impacts->{RebuildModules} ||= $PastImpacts->{PatchedModules};
- map { $Impacts->{BuildModules}->{$_} = 1 } keys %{$PastImpacts->{BuildModules}};
+ $Impacts->{PatchedModuleOrTests}++;
+ $Impacts->{PatchedModules}++ if ($Module->{Patched});
}
+ $Impacts->{BuildModules}++;
}
+ $Impacts->{TestBuild} = $Impacts->{PatchedRoot} || $Impacts->{PatchedModuleOrTests};
return $Impacts;
}
@@ -534,11 +612,11 @@ sub GetBuildTimeout($$)
my ($Impacts, $TaskMissions) = @_;
my ($ExeCount, $WineCount);
- map {$_ =~ /^exe/ ? $ExeCount++ : $WineCount++ } keys %{$TaskMissions->{Builds}};
+ map { $_ =~ /^exe/ ? $ExeCount++ : $WineCount++ } keys %{$TaskMissions->{Builds}};
# Set $ModuleCount to 0 if a full rebuild is needed
- my $ModuleCount = (!$Impacts or $Impacts->{RebuildRoot}) ? 0 :
- scalar(keys %{$Impacts->{BuildModules}});
+ my $ModuleCount = (!$Impacts or $Impacts->{BuildRoot}) ? 0 :
+ $Impacts->{BuildModules};
my $Reconfig = (!$Impacts or $Impacts->{Autoconf} or $Impacts->{MakeMakefiles});
my ($ExeTimeout, $WineTimeout) = (0, 0);
@@ -575,9 +653,9 @@ sub GetTestTimeout($$)
elsif ($Mission->{test} ne "build")
{
# Note: If only test units have been patched then
- # ModuleUnitCount == TestUnitCount.
- my $UnitCount = $Mission->{test} eq "test" ? $Impacts->{TestUnitCount} :
- $Impacts->{ModuleUnitCount};
+ # ModuleTestUnits == RunTestUnits.
+ my $UnitCount = $Mission->{test} eq "test" ? $Impacts->{RunTestUnits} :
+ $Impacts->{ModuleTestUnits};
my $TestsTimeout = min(2, $UnitCount) * $SingleTimeout +
max(0, $UnitCount - 2) * $SingleAvgTime;
$Timeout += min($SuiteTimeout, $TestsTimeout);
diff --git a/testbot/lib/WineTestBot/Patches.pm b/testbot/lib/WineTestBot/Patches.pm
index b6a8af4c4..cc0fbe94a 100644
--- a/testbot/lib/WineTestBot/Patches.pm
+++ b/testbot/lib/WineTestBot/Patches.pm
@@ -213,8 +213,7 @@ sub Submit($$;$)
$SubjectInfo ||= $self->ParseSubject();
my $Impacts = GetPatchImpacts($PatchFileName);
- if (!$Impacts->{PatchedRoot} and !$Impacts->{PatchedModules} and
- !$Impacts->{PatchedTests})
+ if (!$Impacts->{TestBuild})
{
if ($Impacts->{IsWinePatch})
{
@@ -255,14 +254,14 @@ sub Submit($$;$)
my $BuildVMs = CreateVMs();
$BuildVMs->AddFilter("Type", ["build"]);
$BuildVMs->AddFilter("Role", ["base"]);
- if ($Impacts->{TestUnitCount} and !$BuildVMs->IsEmpty())
+ if ($Impacts->{RunTestUnits} and !$BuildVMs->IsEmpty())
{
# Create steps for the Windows tests
my ($BuildStep, $BuildMissions);
- foreach my $Module (sort keys %{$Impacts->{Tests}})
+ foreach my $ModuleName (sort keys %{$Impacts->{Modules}})
{
- my $TestInfo = $Impacts->{Tests}->{$Module};
- foreach my $PatchedUnit (sort keys %{$TestInfo->{PatchedUnits}})
+ my $Module = $Impacts->{Modules}->{$ModuleName};
+ foreach my $TestUnit (@{$Module->{RunTestUnits}})
{
foreach my $Bits ("32", "64")
{
@@ -305,7 +304,7 @@ sub Submit($$;$)
# Create one Step per (module, unit, bitness) combination
my $NewStep = $NewJob->Steps->Add();
$NewStep->PreviousNo($BuildStep->No);
- my $FileName = $TestInfo->{ExeBase};
+ my $FileName = $Module->{ExeBase};
$FileName .= "64" if ($Bits eq "64");
$NewStep->FileName("$FileName.exe");
$NewStep->FileType("exe$Bits");
@@ -317,7 +316,7 @@ sub Submit($$;$)
$Task->VM($VM);
$Task->Timeout($SingleTimeout);
$Task->Missions($TaskMissions->{Statement});
- $Task->CmdLineArg($PatchedUnit);
+ $Task->CmdLineArg($TestUnit);
}
}
}
diff --git a/testbot/web/Submit.pl b/testbot/web/Submit.pl
index 68978f089..76069bbc3 100644
--- a/testbot/web/Submit.pl
+++ b/testbot/web/Submit.pl
@@ -168,15 +168,13 @@ sub _AnalyzePatch($)
$self->{Impacts} ||= GetPatchImpacts($self->_GetStagingFilePath());
- if (!$self->{Impacts}->{PatchedRoot} and
- !$self->{Impacts}->{PatchedModules} and
- !$self->{Impacts}->{PatchedTests})
+ if (!$self->{Impacts}->{TestBuild})
{
$self->{ErrField} = "FileName";
$self->{ErrMessage} = "'$self->{FileName}' is not a valid patch";
return undef;
}
- if ($self->{Impacts}->{ModuleUnitCount} == 0)
+ if ($self->{Impacts}->{PatchedModuleOrTests} == 0)
{
$self->{ErrField} = "FileName";
$self->{ErrMessage} = "The patch does not directly impact the Wine dlls and programs";
@@ -307,11 +305,10 @@ sub _GetTestExecutables($)
{
if ($self->{FileType} eq "patch")
{
- foreach my $Module (sort keys %{$self->{Impacts}->{Tests}})
+ foreach my $Module (values %{$self->{Impacts}->{Modules}})
{
- my $TestInfo = $self->{Impacts}->{Tests}->{$Module};
- next if (!%{$TestInfo->{Files}});
- $self->{TestExecutables}->{"$TestInfo->{ExeBase}.exe"} = $Module;
+ next if (!@{$Module->{TestUnits}});
+ $self->{TestExecutables}->{"$Module->{ExeBase}.exe"} = $Module->{Name};
}
}
else
@@ -332,10 +329,10 @@ sub _ValidateTestExecutable($;$)
$self->{TestExecutable} = (sort keys %{$self->{TestExecutables}})[0];
if (!defined $self->{CmdLineArg} and $self->{Impacts})
{
- my $Module = $self->{TestExecutables}->{$self->{TestExecutable}};
- my $TestInfo = $self->{Impacts}->{Tests}->{$Module};
- $self->{CmdLineArg} = (sort keys %{$TestInfo->{PatchedUnits}})[0] ||
- (sort keys %{$TestInfo->{AllUnits}})[0];
+ my $ModuleName = $self->{TestExecutables}->{$self->{TestExecutable}};
+ my $Module = $self->{Impacts}->{Modules}->{$ModuleName};
+ $self->{CmdLineArg} = ($Module->{RunTestUnits}->[0] ||
+ $Module->{TestUnits}->[0]);
}
}
@@ -640,21 +637,22 @@ sub GetHeaderText($)
$self->_GetTestExecutables();
foreach my $TestExecutable (sort keys %{$self->{TestExecutables}})
{
- my $Module = $self->{TestExecutables}->{$TestExecutable};
- my $TestInfo = $self->{Impacts}->{Tests}->{$Module};
+ my $ModuleName = $self->{TestExecutables}->{$TestExecutable};
+ my $Module = $self->{Impacts}->{Modules}->{$ModuleName};
my @TestUnits;
- foreach my $TestUnit (sort keys %{$TestInfo->{AllUnits}})
+ foreach my $TestUnit (@{$Module->{TestUnits}})
{
- if ($TestInfo->{PatchedUnits}->{$TestUnit})
+ if ($Module->{TestFiles}->{"$TestUnit.c"})
{
+ # This test unit was patched
push @TestUnits, "<i>$TestUnit</i>";
}
- elsif ($TestInfo->{All} or $TestInfo->{PatchedModule})
+ elsif ($Module->{All} or $Module->{Patched})
{
push @TestUnits, $TestUnit;
}
}
- push @TestExecutables, "$Module: ". join(" ", @TestUnits) if (@TestUnits);
+ push @TestExecutables, "$ModuleName: ". join(" ", @TestUnits) if (@TestUnits);
}
push @Headers, join("<br>\n", "Here is a list of the test executables impacted by the patch (patched test units, if any, are in italics).", @TestExecutables);
}
@@ -788,7 +786,7 @@ sub GenerateFields($)
if (!$self->{UserVMSelection} and !$VMRow->{Extra} and
$VMRow->{VM}->Status !~ /^(?:offline|maintenance)$/ and
($VMRow->{VM}->Type eq "wine" or !$self->{Impacts} or
- $self->{Impacts}->{TestUnitCount}))
+ $self->{Impacts}->{RunTestUnits}))
{
$VMRow->{Checked} = 1;
}
--
2.20.1
April 1, 2021
[PATCH 2/2] msvcp140_1: Implement the DLL.
by Arkadiusz Hiler
This backs C++17's std::pmr implementation.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=49954
Signed-off-by: Arkadiusz Hiler <ahiler(a)codeweavers.com>
---
configure | 1 +
configure.ac | 1 +
dlls/msvcp140_1/Makefile.in | 9 +-
dlls/msvcp140_1/msvcp140_1.c | 266 +++++++++++++++++++++++++++
dlls/msvcp140_1/msvcp140_1.spec | 14 +-
dlls/msvcp140_1/tests/Makefile.in | 3 +
dlls/msvcp140_1/tests/msvcp140_1.c | 277 +++++++++++++++++++++++++++++
7 files changed, 563 insertions(+), 8 deletions(-)
create mode 100644 dlls/msvcp140_1/msvcp140_1.c
create mode 100644 dlls/msvcp140_1/tests/Makefile.in
create mode 100644 dlls/msvcp140_1/tests/msvcp140_1.c
diff --git a/configure b/configure
index a19bb0b9581..aedbac67fe4 100755
--- a/configure
+++ b/configure
@@ -20825,6 +20825,7 @@ wine_fn_config_makefile dlls/msvcp120_app enable_msvcp120_app
wine_fn_config_makefile dlls/msvcp140 enable_msvcp140
wine_fn_config_makefile dlls/msvcp140/tests enable_tests
wine_fn_config_makefile dlls/msvcp140_1 enable_msvcp140_1
+wine_fn_config_makefile dlls/msvcp140_1/tests enable_tests
wine_fn_config_makefile dlls/msvcp60 enable_msvcp60
wine_fn_config_makefile dlls/msvcp60/tests enable_tests
wine_fn_config_makefile dlls/msvcp70 enable_msvcp70
diff --git a/configure.ac b/configure.ac
index eb08e8b0f17..edd7be66ec3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3500,6 +3500,7 @@ WINE_CONFIG_MAKEFILE(dlls/msvcp120_app)
WINE_CONFIG_MAKEFILE(dlls/msvcp140)
WINE_CONFIG_MAKEFILE(dlls/msvcp140/tests)
WINE_CONFIG_MAKEFILE(dlls/msvcp140_1)
+WINE_CONFIG_MAKEFILE(dlls/msvcp140_1/tests)
WINE_CONFIG_MAKEFILE(dlls/msvcp60)
WINE_CONFIG_MAKEFILE(dlls/msvcp60/tests)
WINE_CONFIG_MAKEFILE(dlls/msvcp70)
diff --git a/dlls/msvcp140_1/Makefile.in b/dlls/msvcp140_1/Makefile.in
index 6582299d7d6..846bcdc855b 100644
--- a/dlls/msvcp140_1/Makefile.in
+++ b/dlls/msvcp140_1/Makefile.in
@@ -1 +1,8 @@
-MODULE = msvcp140_1.dll
+MODULE = msvcp140_1.dll
+PARENTSRC = ../msvcp90
+
+EXTRADLLFLAGS = -mno-cygwin -fno-builtin
+
+C_SRCS = \
+ type_info.c \
+ msvcp140_1.c
diff --git a/dlls/msvcp140_1/msvcp140_1.c b/dlls/msvcp140_1/msvcp140_1.c
new file mode 100644
index 00000000000..344fee3dc53
--- /dev/null
+++ b/dlls/msvcp140_1/msvcp140_1.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright 2021 Arkadiusz Hiler for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <malloc.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+#include "winnls.h"
+#include "cxx.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msvcp);
+
+#ifdef __i386__
+#define MAX_UNALIGNED_ALIGNMENT 8
+#else
+#define MAX_UNALIGNED_ALIGNMENT 16
+#endif
+
+static HMODULE msvcp140;
+
+void init_type_info(void *base);
+int CDECL _callnewh(size_t size);
+
+void (__cdecl *throw_bad_alloc)(void);
+
+/* non-static, needed by type_info */
+void* __cdecl MSVCRT_operator_new(size_t size)
+{
+ void *retval;
+ int freed;
+
+ do
+ {
+ retval = malloc(size);
+ if (retval)
+ {
+ TRACE("(%Iu) returning %p\n", size, retval);
+ return retval;
+ }
+ freed = _callnewh(size);
+ } while (freed);
+
+ TRACE("(%Iu) out of memory\n", size);
+ throw_bad_alloc();
+ return NULL;
+}
+
+void __cdecl MSVCRT_operator_delete(void *mem)
+{
+ free(mem);
+}
+
+static void* __cdecl MSVCRT_operator_new_aligned(size_t size, size_t alignment)
+{
+ void *retval;
+ int freed;
+
+ do
+ {
+ retval = _aligned_malloc(size, alignment);
+ if (retval)
+ {
+ TRACE("(%Iu) returning %p\n", size, retval);
+ return retval;
+ }
+ freed = _callnewh(size);
+ } while (freed);
+
+ TRACE("(%Iu) out of memory\n", size);
+ throw_bad_alloc();
+ return NULL;
+}
+
+static void __cdecl MSVCRT_operator_delete_aligned(void *mem, size_t alignment)
+{
+ _aligned_free(mem);
+}
+
+typedef struct {
+ const vtable_ptr *vtable;
+} memory_resource;
+
+extern const vtable_ptr MSVCP_aligned_resource_vtable;
+extern const vtable_ptr MSVCP_unaligned_resource_vtable;
+extern const vtable_ptr MSVCP_null_resource_vtable;
+
+__ASM_BLOCK_BEGIN(vtables)
+ __ASM_VTABLE(base_memory_resource,
+ VTABLE_ADD_FUNC(nop_dtor));
+ __ASM_VTABLE(aligned_resource,
+ VTABLE_ADD_FUNC(nop_dtor)
+ VTABLE_ADD_FUNC(aligned_do_allocate)
+ VTABLE_ADD_FUNC(aligned_do_deallocate)
+ VTABLE_ADD_FUNC(do_is_equal));
+ __ASM_VTABLE(unaligned_resource,
+ VTABLE_ADD_FUNC(nop_dtor)
+ VTABLE_ADD_FUNC(unaligned_do_allocate)
+ VTABLE_ADD_FUNC(unaligned_do_deallocate)
+ VTABLE_ADD_FUNC(do_is_equal));
+ __ASM_VTABLE(null_resource,
+ VTABLE_ADD_FUNC(nop_dtor)
+ VTABLE_ADD_FUNC(null_do_allocate)
+ VTABLE_ADD_FUNC(null_do_deallocate)
+ VTABLE_ADD_FUNC(do_is_equal));
+__ASM_BLOCK_END
+
+DEFINE_RTTI_DATA0(base_memory_resource, 0, ".?AVmemory_resource(a)pmr@std@@")
+DEFINE_RTTI_DATA1(aligned_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_Aligned_new_delete_resource(a)pmr@std@@")
+DEFINE_RTTI_DATA1(unaligned_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_Unligned_new_delete_resource(a)pmr@std@@")
+DEFINE_RTTI_DATA1(null_resource, 0, &base_memory_resource_rtti_base_descriptor, ".?AV_null_resource(a)pmr@std@@")
+
+DEFINE_THISCALL_WRAPPER(nop_dtor, 4)
+void __thiscall nop_dtor(void *this)
+{
+ /* nop */
+}
+
+DEFINE_THISCALL_WRAPPER(do_is_equal, 8)
+bool __thiscall do_is_equal(memory_resource *this, memory_resource *other)
+{
+ return this == other;
+}
+
+DEFINE_THISCALL_WRAPPER(aligned_do_allocate, 12)
+void* __thiscall aligned_do_allocate(memory_resource *this, size_t bytes, size_t alignment)
+{
+ return MSVCRT_operator_new_aligned(bytes, alignment);
+}
+
+DEFINE_THISCALL_WRAPPER(aligned_do_deallocate, 16)
+void __thiscall aligned_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment)
+{
+ MSVCRT_operator_delete_aligned(p, alignment);
+}
+
+DEFINE_THISCALL_WRAPPER(unaligned_do_allocate, 12)
+void* __thiscall unaligned_do_allocate(memory_resource *this, size_t bytes, size_t alignment)
+{
+ if (alignment > MAX_UNALIGNED_ALIGNMENT)
+ throw_bad_alloc();
+
+ return MSVCRT_operator_new(bytes);
+}
+
+DEFINE_THISCALL_WRAPPER(unaligned_do_deallocate, 16)
+void __thiscall unaligned_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment)
+{
+ MSVCRT_operator_delete(p);
+}
+
+DEFINE_THISCALL_WRAPPER(null_do_allocate, 12)
+void* __thiscall null_do_allocate(memory_resource *this, size_t bytes, size_t alignment)
+{
+ throw_bad_alloc();
+ return NULL;
+}
+
+DEFINE_THISCALL_WRAPPER(null_do_deallocate, 16)
+void __thiscall null_do_deallocate(memory_resource *this, void *p, size_t bytes, size_t alignment)
+{
+ /* nop */
+}
+
+static memory_resource impl_aligned_resource = { &MSVCP_aligned_resource_vtable };
+static memory_resource impl_unaligned_resource = { &MSVCP_unaligned_resource_vtable };
+static memory_resource impl_null_resource = { &MSVCP_null_resource_vtable };
+
+memory_resource *default_unaligned_resource = &impl_unaligned_resource;
+memory_resource *default_aligned_resource = &impl_aligned_resource;
+
+/* EXPORTS */
+
+memory_resource* __cdecl _Aligned_new_delete_resource(void)
+{
+ return &impl_aligned_resource;
+}
+
+memory_resource* __cdecl _Unaligned_new_delete_resource(void)
+{
+ return &impl_unaligned_resource;
+}
+
+memory_resource* __cdecl _Aligned_get_default_resource(void)
+{
+ return default_aligned_resource;
+}
+
+memory_resource* __cdecl _Aligned_set_default_resource(memory_resource *res)
+{
+ if (res == NULL)
+ res = &impl_aligned_resource;
+
+ return InterlockedExchangePointer((void**)&default_aligned_resource, res);
+}
+
+memory_resource* __cdecl _Unaligned_get_default_resource(void)
+{
+ return default_unaligned_resource;
+}
+
+memory_resource* __cdecl _Unaligned_set_default_resource(memory_resource *res)
+{
+ if (res == NULL)
+ res = &impl_unaligned_resource;
+
+ return InterlockedExchangePointer((void**)&default_unaligned_resource, res);
+}
+
+memory_resource* __cdecl null_memory_resource(void)
+{
+ return &impl_null_resource;
+}
+
+/* DLL INIT */
+
+static void init_cxx_funcs(void)
+{
+ msvcp140 = LoadLibraryA("msvcp140.dll");
+ if (!msvcp140) FIXME("Failed to load msvcp140.dll\n");
+ throw_bad_alloc = (void*)GetProcAddress(msvcp140, "?_Xbad_alloc(a)std@@YAXXZ");
+ if (!throw_bad_alloc) FIXME("Failed to get address of ?_Xbad_alloc(a)std@@YAXXZ\n");
+}
+
+static void init_memory_resource(void *base)
+{
+#ifdef __x86_64__
+ init_null_resource_rtti(base);
+ init_unaligned_resource_rtti(base);
+ init_aligned_resource_rtti(base);
+ init_base_memory_resource_rtti(base);
+#endif
+}
+
+BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
+{
+ switch (reason)
+ {
+ case DLL_PROCESS_ATTACH:
+ init_cxx_funcs();
+ init_type_info(inst);
+ init_memory_resource(inst);
+ break;
+ case DLL_PROCESS_DETACH:
+ FreeLibrary(msvcp140);
+ break;
+ }
+ return TRUE;
+}
diff --git a/dlls/msvcp140_1/msvcp140_1.spec b/dlls/msvcp140_1/msvcp140_1.spec
index bfe82ab00da..e4446f4f4c2 100644
--- a/dlls/msvcp140_1/msvcp140_1.spec
+++ b/dlls/msvcp140_1/msvcp140_1.spec
@@ -1,7 +1,7 @@
-@ stub _Aligned_get_default_resource
-@ stub _Aligned_new_delete_resource
-@ stub _Aligned_set_default_resource
-@ stub _Unaligned_get_default_resource
-@ stub _Unaligned_new_delete_resource
-@ stub _Unaligned_set_default_resource
-@ stub null_memory_resource
+@ cdecl _Aligned_get_default_resource()
+@ cdecl _Aligned_new_delete_resource()
+@ cdecl _Aligned_set_default_resource(ptr)
+@ cdecl _Unaligned_get_default_resource()
+@ cdecl _Unaligned_new_delete_resource()
+@ cdecl _Unaligned_set_default_resource(ptr)
+@ cdecl null_memory_resource()
diff --git a/dlls/msvcp140_1/tests/Makefile.in b/dlls/msvcp140_1/tests/Makefile.in
new file mode 100644
index 00000000000..523a2016d3a
--- /dev/null
+++ b/dlls/msvcp140_1/tests/Makefile.in
@@ -0,0 +1,3 @@
+TESTDLL = msvcp140_1.dll
+
+C_SRCS = msvcp140_1.c
diff --git a/dlls/msvcp140_1/tests/msvcp140_1.c b/dlls/msvcp140_1/tests/msvcp140_1.c
new file mode 100644
index 00000000000..a32dd364fe8
--- /dev/null
+++ b/dlls/msvcp140_1/tests/msvcp140_1.c
@@ -0,0 +1,277 @@
+/*
+ * Copyright 2021 Arkadiusz Hiler for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <errno.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+
+#include "wine/test.h"
+#include "wine/exception.h"
+#include "winbase.h"
+
+typedef unsigned char MSVCP_bool;
+
+#undef __thiscall
+#ifdef __i386__
+#define __thiscall __stdcall
+#else
+#define __thiscall __cdecl
+#endif
+
+#ifdef __i386__
+#define MAX_UNALIGNED_ALIGNMENT 8
+#else
+#define MAX_UNALIGNED_ALIGNMENT 16
+#endif
+
+/* Emulate __thiscall */
+#ifdef __i386__
+
+#include "pshpack1.h"
+struct thiscall_thunk
+{
+ BYTE pop_eax; /* popl %eax (ret addr) */
+ BYTE pop_edx; /* popl %edx (func) */
+ BYTE pop_ecx; /* popl %ecx (this) */
+ BYTE push_eax; /* pushl %eax */
+ WORD jmp_edx; /* jmp *%edx */
+};
+#include "poppack.h"
+
+static void* (WINAPI *call_thiscall_func1)(void *func, void *this);
+static void* (WINAPI *call_thiscall_func2)(void *func, void *this, const void *a);
+static void* (WINAPI *call_thiscall_func3)(void *func, void *this, const void *a, const void *b);
+static void* (WINAPI *call_thiscall_func4)(void *func, void *this, const void *a, const void *b, const void *c);
+
+static void init_thiscall_thunk(void)
+{
+ struct thiscall_thunk *thunk = VirtualAlloc(NULL, sizeof(*thunk), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
+ thunk->pop_eax = 0x58; /* popl %eax */
+ thunk->pop_edx = 0x5a; /* popl %edx */
+ thunk->pop_ecx = 0x59; /* popl %ecx */
+ thunk->push_eax = 0x50; /* pushl %eax */
+ thunk->jmp_edx = 0xe2ff; /* jmp *%edx */
+ call_thiscall_func1 = (void*)thunk;
+ call_thiscall_func2 = (void*)thunk;
+ call_thiscall_func3 = (void*)thunk;
+ call_thiscall_func4 = (void*)thunk;
+}
+
+#define call_func1(func,_this) call_thiscall_func1(func,_this)
+#define call_func2(func,_this,a) call_thiscall_func2(func,_this,(const void*)(a))
+#define call_func3(func,_this,a,b) call_thiscall_func3(func,_this,(const void*)(a),(const void*)(b))
+#define call_func4(func,_this,a,b,c) call_thiscall_func4(func,_this,(const void*)(a),(const void*)(b), (const void*)(c))
+
+#else
+
+#define init_thiscall_thunk()
+#define call_func1(func,_this) func(_this)
+#define call_func2(func,_this,a) func(_this,a)
+#define call_func3(func,_this,a,b) func(_this,a,b)
+#define call_func4(func,_this,a,b,c) func(_this,a,b,c)
+
+#endif /* __i386__ */
+
+struct memory_resource_vtbl;
+
+typedef struct {
+ struct memory_resource_vtbl *vtbl;
+} memory_resource;
+
+struct memory_resource_vtbl
+{
+ void (__thiscall *dtor)(void *this);
+ void* (__thiscall *do_allocate)(memory_resource *this, size_t bytes, size_t alignment);
+ void (__thiscall *do_deallocate)(memory_resource *this, void *p, size_t bytes, size_t alignment);
+ MSVCP_bool (__thiscall *do_is_equal)(memory_resource *this, memory_resource *other);
+};
+
+static HMODULE msvcp;
+static memory_resource* (__cdecl *p__Aligned_new_delete_resource)(void);
+static memory_resource* (__cdecl *p__Unaligned_new_delete_resource)(void);
+static memory_resource* (__cdecl *p__Aligned_get_default_resource)(void);
+static memory_resource* (__cdecl *p__Unaligned_get_default_resource)(void);
+static memory_resource* (__cdecl *p__Aligned_set_default_resource)(memory_resource* resource);
+static memory_resource* (__cdecl *p__Unaligned_set_default_resource)(memory_resource* resource);
+static memory_resource* (__cdecl *p_null_memory_resource)(void);
+
+static HMODULE ucrtbase;
+static void* (__cdecl *p_malloc)(size_t size);
+static void (__cdecl *p_free)(void *ptr);
+static void* (__cdecl *p__aligned_malloc)(size_t size, size_t alignment);
+static void (__cdecl *p__aligned_free)(void *ptr);
+
+#define SETNOFAIL(lib,x,y) x = (void*)GetProcAddress(lib,y)
+#define SET(lib,x,y) do { SETNOFAIL(lib,x,y); ok(x != NULL, "Export '%s' not found\n", y); } while(0)
+
+static BOOL init(void)
+{
+ msvcp = LoadLibraryA("msvcp140_1.dll");
+ if(!msvcp)
+ {
+ win_skip("msvcp140_1.dll not installed\n");
+ return FALSE;
+ }
+
+ ucrtbase = LoadLibraryA("ucrtbase.dll");
+ if(!ucrtbase)
+ {
+ win_skip("ucrtbase.dll not installed\n");
+ FreeLibrary(msvcp);
+ return FALSE;
+ }
+
+ SET(msvcp, p__Aligned_new_delete_resource, "_Aligned_new_delete_resource");
+ SET(msvcp, p__Unaligned_new_delete_resource, "_Unaligned_new_delete_resource");
+ SET(msvcp, p_null_memory_resource, "null_memory_resource");
+ SET(msvcp, p__Aligned_get_default_resource, "_Aligned_get_default_resource");
+ SET(msvcp, p__Unaligned_get_default_resource, "_Unaligned_get_default_resource");
+ SET(msvcp, p__Aligned_set_default_resource, "_Aligned_set_default_resource");
+ SET(msvcp, p__Unaligned_set_default_resource, "_Unaligned_set_default_resource");
+
+ SET(ucrtbase, p__aligned_malloc, "_aligned_malloc");
+ SET(ucrtbase, p__aligned_free, "_aligned_free");
+ SET(ucrtbase, p_malloc, "malloc");
+ SET(ucrtbase, p_free, "free");
+
+ init_thiscall_thunk();
+
+ return TRUE;
+}
+
+static void test__Aligned_new_delete_resource(void)
+{
+ void *ptr;
+ memory_resource *resource = p__Aligned_new_delete_resource();
+ ok(resource != NULL, "Failed to get aligned new delete memory resource.\n");
+
+ /* calling dtor should be harmless nop */
+ call_func1(resource->vtbl->dtor, resource);
+
+ ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 32);
+ ok(ptr != NULL, "Failed to allocate memory using memory resource.\n");
+ call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 32);
+
+ /* up to the alignment of 16 it is using non-aligned new/delete */
+ ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 16);
+ ok(ptr != NULL, "Failed to allocate memory using memory resource.\n");
+ p_free(ptr); /* aligned delete, crashes with non-aligned */
+
+ ptr = p_malloc(140);
+ ok(ptr != NULL, "Failed to allocate memory using malloc.\n");
+ call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 8);
+
+ /* past the alignment of 16 it is using aligned new/delete */
+ ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 32);
+ ok(ptr != NULL, "Failed to allocate memory using memory resource.\n");
+ p__aligned_free(ptr); /* aligned delete, crashes with non-aligned */
+
+ ptr = p__aligned_malloc(140, 32);
+ ok(ptr != NULL, "Failed to allocate memory using _aligned_malloc.\n");
+ call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 32);
+
+ ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n");
+}
+
+static void test__Unaligned_new_delete_resource(void)
+{
+ void *ptr;
+ memory_resource *resource = p__Unaligned_new_delete_resource();
+ ok(resource != NULL, "Failed to get unaligned new delete memory resource.\n");
+
+ /* calling dtor should be a harmless nop */
+ call_func1(resource->vtbl->dtor, resource);
+
+ ptr = call_func3(resource->vtbl->do_allocate, resource, 140, MAX_UNALIGNED_ALIGNMENT);
+ ok(ptr != NULL, "Failed to allocate memory using memory resource.\n");
+ call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, MAX_UNALIGNED_ALIGNMENT);
+
+ /* up to the alignment of 16 it is using non-aligned new/delete */
+ ptr = call_func3(resource->vtbl->do_allocate, resource, 140, 8);
+ ok(ptr != NULL, "Failed to allocate memory using memory resource.\n");
+ p_free(ptr); /* aligned delete */
+
+ ptr = p_malloc(140);
+ ok(ptr != NULL, "Failed to allocate memory using malloc.\n");
+ call_func4(resource->vtbl->do_deallocate, resource, ptr, 140, 2);
+
+ /* alignment past MAX_UNALIGNED_ALIGNMENT results in bad alloc exception */
+ /* ptr = call_func3(resource->vtbl->do_allocate, resource, 140, MAX_UNALIGNED_ALIGNMENT*2); */
+
+ ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n");
+}
+
+static void test_null_memory_resource(void)
+{
+ memory_resource *resource = p_null_memory_resource();
+ ok(resource != NULL, "Failed to get null memory resource.\n");
+
+ /* should result in bad alloc exception */
+ /* call_func3(resource->vtbl->do_allocate, resource, 140, 8); */
+
+ /* harmless nop */
+ call_func4(resource->vtbl->do_deallocate, resource, (void*)(INT_PTR)-1, 140, 2);
+
+ ok((MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource), "Expected resource to be equal to itself.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, NULL), "Expected resource to not be equal to NULL.\n");
+ ok(!(MSVCP_bool)(INT_PTR)call_func2(resource->vtbl->do_is_equal, resource, resource+1), "Expected resource to not be equal to a random pointer.\n");
+}
+
+static void test_get_set_defult_resource(memory_resource *(__cdecl *new_delete_resource)(void),
+ memory_resource *(__cdecl *get_default_resource)(void),
+ memory_resource *(__cdecl *set_default_resource)(memory_resource *resource))
+{
+ memory_resource *new_resource = new_delete_resource();
+ memory_resource *default_resource = get_default_resource();
+ ok(default_resource == new_resource, "Expected the default memory resource to be equal new/delete one.\n");
+
+ default_resource = set_default_resource((void*)0xdeadbeef);
+ ok(default_resource == new_resource, "Expected that setting default resource would return the old one.\n");
+
+ default_resource = get_default_resource();
+ ok(default_resource == (void*)0xdeadbeef, "Expected that setting reasource would take effect.\n");
+
+ default_resource = set_default_resource(NULL);
+ ok(default_resource == (void*)0xdeadbeef, "Expected that setting default resource would return the old one.\n");
+
+ default_resource = get_default_resource();
+ ok(default_resource == new_resource, "Expected that setting default resource to NULL would reset the value.\n");
+}
+
+START_TEST(msvcp140_1)
+{
+ if (!init()) return;
+
+ test__Aligned_new_delete_resource();
+ test__Unaligned_new_delete_resource();
+
+ test_null_memory_resource();
+
+ test_get_set_defult_resource(p__Aligned_new_delete_resource, p__Aligned_get_default_resource, p__Aligned_set_default_resource);
+ test_get_set_defult_resource(p__Unaligned_new_delete_resource, p__Unaligned_get_default_resource, p__Unaligned_set_default_resource);
+
+ FreeLibrary(msvcp);
+ FreeLibrary(ucrtbase);
+}
--
2.31.1
April 1, 2021
[PATCH 1/2] msvcp90: Move type_info to a separate file.
by Arkadiusz Hiler
type_info is needed for RTTI, and in some cases pulling in whole
exception.c is not an option due to its dependency on multiple MSVCRT_
functions.
Signed-off-by: Arkadiusz Hiler <ahiler(a)codeweavers.com>
---
dlls/msvcirt/Makefile.in | 1 +
dlls/msvcirt/msvcirt.c | 1 +
dlls/msvcirt/msvcirt.h | 1 +
dlls/msvcp100/Makefile.in | 1 +
dlls/msvcp110/Makefile.in | 1 +
dlls/msvcp120/Makefile.in | 1 +
dlls/msvcp140/Makefile.in | 1 +
dlls/msvcp60/Makefile.in | 1 +
dlls/msvcp60/main.c | 1 +
dlls/msvcp60/msvcp.h | 1 +
dlls/msvcp70/Makefile.in | 1 +
dlls/msvcp71/Makefile.in | 1 +
dlls/msvcp80/Makefile.in | 1 +
dlls/msvcp90/Makefile.in | 1 +
dlls/msvcp90/exception.c | 31 ------------------
dlls/msvcp90/msvcp90.h | 1 +
dlls/msvcp90/msvcp_main.c | 1 +
dlls/msvcp90/type_info.c | 67 +++++++++++++++++++++++++++++++++++++++
18 files changed, 83 insertions(+), 31 deletions(-)
create mode 100644 dlls/msvcp90/type_info.c
diff --git a/dlls/msvcirt/Makefile.in b/dlls/msvcirt/Makefile.in
index 1dee2b24c02..b792edabf09 100644
--- a/dlls/msvcirt/Makefile.in
+++ b/dlls/msvcirt/Makefile.in
@@ -6,5 +6,6 @@ IMPORTS = msvcrt
EXTRADLLFLAGS = -mno-cygwin -Wb,--prefer-native
C_SRCS = \
+ type_info.c \
exception.c \
msvcirt.c
diff --git a/dlls/msvcirt/msvcirt.c b/dlls/msvcirt/msvcirt.c
index db4b736d272..87e1908c4a8 100644
--- a/dlls/msvcirt/msvcirt.c
+++ b/dlls/msvcirt/msvcirt.c
@@ -5213,6 +5213,7 @@ BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved )
{
case DLL_PROCESS_ATTACH:
init_cxx_funcs();
+ init_type_info(inst);
init_exception(inst);
init_io(inst);
DisableThreadLibraryCalls( inst );
diff --git a/dlls/msvcirt/msvcirt.h b/dlls/msvcirt/msvcirt.h
index 5b59b7862a3..e775eabb2fe 100644
--- a/dlls/msvcirt/msvcirt.h
+++ b/dlls/msvcirt/msvcirt.h
@@ -71,4 +71,5 @@ typedef enum {
extern void* (__cdecl *MSVCRT_operator_new)(SIZE_T);
extern void (__cdecl *MSVCRT_operator_delete)(void*);
+void init_type_info(void*);
void init_exception(void*);
diff --git a/dlls/msvcp100/Makefile.in b/dlls/msvcp100/Makefile.in
index 258aff4b770..0ee8fee26c8 100644
--- a/dlls/msvcp100/Makefile.in
+++ b/dlls/msvcp100/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp110/Makefile.in b/dlls/msvcp110/Makefile.in
index 6b1998a68ea..01a22340b6d 100644
--- a/dlls/msvcp110/Makefile.in
+++ b/dlls/msvcp110/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp120/Makefile.in b/dlls/msvcp120/Makefile.in
index 2a77859bf99..22923b61937 100644
--- a/dlls/msvcp120/Makefile.in
+++ b/dlls/msvcp120/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp140/Makefile.in b/dlls/msvcp140/Makefile.in
index ea05d6b3456..00bc6e219ce 100644
--- a/dlls/msvcp140/Makefile.in
+++ b/dlls/msvcp140/Makefile.in
@@ -5,6 +5,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp60/Makefile.in b/dlls/msvcp60/Makefile.in
index b43395b44d1..92188139b02 100644
--- a/dlls/msvcp60/Makefile.in
+++ b/dlls/msvcp60/Makefile.in
@@ -6,6 +6,7 @@ IMPORTS = msvcrt
EXTRADLLFLAGS = -mno-cygwin -Wb,--prefer-native
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp60/main.c b/dlls/msvcp60/main.c
index ab9a6a3179d..a553e1438c2 100644
--- a/dlls/msvcp60/main.c
+++ b/dlls/msvcp60/main.c
@@ -89,6 +89,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
case DLL_PROCESS_ATTACH:
init_cxx_funcs();
_Init_locks__Init_locks_ctor(NULL);
+ init_type_info(hinstDLL);
init_exception(hinstDLL);
init_locale(hinstDLL);
init_io(hinstDLL);
diff --git a/dlls/msvcp60/msvcp.h b/dlls/msvcp60/msvcp.h
index 2507d9123d9..f078b33542a 100644
--- a/dlls/msvcp60/msvcp.h
+++ b/dlls/msvcp60/msvcp.h
@@ -465,6 +465,7 @@ ostreambuf_iterator_wchar* __thiscall num_put_wchar_put_uint64(const num_put*, o
ostreambuf_iterator_wchar* __thiscall num_put_wchar_put_bool(const num_put*, ostreambuf_iterator_wchar*,
ostreambuf_iterator_wchar, ios_base*, wchar_t, bool);
+void init_type_info(void*);
void init_exception(void*);
void init_locale(void*);
void init_io(void*);
diff --git a/dlls/msvcp70/Makefile.in b/dlls/msvcp70/Makefile.in
index a023d1b8e2a..57987596f50 100644
--- a/dlls/msvcp70/Makefile.in
+++ b/dlls/msvcp70/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp71/Makefile.in b/dlls/msvcp71/Makefile.in
index 6713ab469d3..743b6d4751c 100644
--- a/dlls/msvcp71/Makefile.in
+++ b/dlls/msvcp71/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp80/Makefile.in b/dlls/msvcp80/Makefile.in
index 22a18fcc66a..7c1947f2721 100644
--- a/dlls/msvcp80/Makefile.in
+++ b/dlls/msvcp80/Makefile.in
@@ -6,6 +6,7 @@ PARENTSRC = ../msvcp90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp90/Makefile.in b/dlls/msvcp90/Makefile.in
index b7e7b73423f..fe4bec04a9c 100644
--- a/dlls/msvcp90/Makefile.in
+++ b/dlls/msvcp90/Makefile.in
@@ -5,6 +5,7 @@ EXTRADEFS = -D_MSVCP_VER=90
EXTRADLLFLAGS = -mno-cygwin
C_SRCS = \
+ type_info.c \
exception.c \
ios.c \
locale.c \
diff --git a/dlls/msvcp90/exception.c b/dlls/msvcp90/exception.c
index 8ea4e132a0f..e6c9227cc27 100644
--- a/dlls/msvcp90/exception.c
+++ b/dlls/msvcp90/exception.c
@@ -65,34 +65,6 @@ extern const vtable_ptr MSVCP_bad_cast_vtable;
/* ??_7range_error(a)std@@6B@ */
extern const vtable_ptr MSVCP_range_error_vtable;
-static void MSVCP_type_info_dtor(type_info * _this)
-{
- free(_this->name);
-}
-
-/* Unexported */
-DEFINE_THISCALL_WRAPPER(MSVCP_type_info_vector_dtor,8)
-void * __thiscall MSVCP_type_info_vector_dtor(type_info * _this, unsigned int flags)
-{
- TRACE("(%p %x)\n", _this, flags);
- if (flags & 2)
- {
- /* we have an array, with the number of elements stored before the first object */
- INT_PTR i, *ptr = (INT_PTR *)_this - 1;
-
- for (i = *ptr - 1; i >= 0; i--) MSVCP_type_info_dtor(_this + i);
- MSVCRT_operator_delete(ptr);
- }
- else
- {
- MSVCP_type_info_dtor(_this);
- if (flags & 1) MSVCRT_operator_delete(_this);
- }
- return _this;
-}
-
-DEFINE_RTTI_DATA0( type_info, 0, ".?AVtype_info@@" )
-
/* ??0exception@@QAE(a)ABQBD@Z */
/* ??0exception@@QEAA(a)AEBQEBD@Z */
DEFINE_THISCALL_WRAPPER(MSVCP_exception_ctor,8)
@@ -1022,8 +994,6 @@ void __cdecl __ExceptionPtrDestroy(exception_ptr *ep)
#endif
__ASM_BLOCK_BEGIN(exception_vtables)
- __ASM_VTABLE(type_info,
- VTABLE_ADD_FUNC(MSVCP_type_info_vector_dtor));
EXCEPTION_VTABLE(exception,
VTABLE_ADD_FUNC(MSVCP_exception_vector_dtor)
VTABLE_ADD_FUNC(MSVCP_exception_what));
@@ -1130,7 +1100,6 @@ void throw_exception(exception_type et, const char *str)
void init_exception(void *base)
{
#ifdef __x86_64__
- init_type_info_rtti(base);
init_exception_rtti(base);
init_bad_alloc_rtti(base);
init_logic_error_rtti(base);
diff --git a/dlls/msvcp90/msvcp90.h b/dlls/msvcp90/msvcp90.h
index b2e0ae8b15c..c4908f24245 100644
--- a/dlls/msvcp90/msvcp90.h
+++ b/dlls/msvcp90/msvcp90.h
@@ -618,6 +618,7 @@ ostreambuf_iterator_wchar* __thiscall num_put_wchar_put_uint64(const num_put*, o
ostreambuf_iterator_wchar* __thiscall num_put_wchar_put_bool(const num_put*, ostreambuf_iterator_wchar*,
ostreambuf_iterator_wchar, ios_base*, wchar_t, bool);
+void init_type_info(void*);
void init_exception(void*);
void init_locale(void*);
void init_io(void*);
diff --git a/dlls/msvcp90/msvcp_main.c b/dlls/msvcp90/msvcp_main.c
index 6bfd8b14367..b023a3573de 100644
--- a/dlls/msvcp90/msvcp_main.c
+++ b/dlls/msvcp90/msvcp_main.c
@@ -184,6 +184,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
case DLL_PROCESS_ATTACH:
init_cxx_funcs();
_Init_locks__Init_locks_ctor(NULL);
+ init_type_info(hinstDLL);
init_exception(hinstDLL);
init_locale(hinstDLL);
init_io(hinstDLL);
diff --git a/dlls/msvcp90/type_info.c b/dlls/msvcp90/type_info.c
new file mode 100644
index 00000000000..38134ac5e86
--- /dev/null
+++ b/dlls/msvcp90/type_info.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2021 Arkadiusz Hiler for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "msvcp90.h"
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msvcp);
+
+static void MSVCP_type_info_dtor(type_info * _this)
+{
+ free(_this->name);
+}
+
+/* Unexported */
+DEFINE_THISCALL_WRAPPER(MSVCP_type_info_vector_dtor,8)
+void * __thiscall MSVCP_type_info_vector_dtor(type_info * _this, unsigned int flags)
+{
+ TRACE("(%p %x)\n", _this, flags);
+ if (flags & 2)
+ {
+ /* we have an array, with the number of elements stored before the first object */
+ INT_PTR i, *ptr = (INT_PTR *)_this - 1;
+
+ for (i = *ptr - 1; i >= 0; i--) MSVCP_type_info_dtor(_this + i);
+ MSVCRT_operator_delete(ptr);
+ }
+ else
+ {
+ MSVCP_type_info_dtor(_this);
+ if (flags & 1) MSVCRT_operator_delete(_this);
+ }
+ return _this;
+}
+
+DEFINE_RTTI_DATA0( type_info, 0, ".?AVtype_info@@" )
+
+__ASM_BLOCK_BEGIN(type_info_vtables)
+ __ASM_VTABLE(type_info,
+ VTABLE_ADD_FUNC(MSVCP_type_info_vector_dtor));
+__ASM_BLOCK_END
+
+void init_type_info(void *base)
+{
+#ifdef __x86_64__
+ init_type_info_rtti(base);
+#endif
+}
--
2.31.1
April 1, 2021
Re: [PATCH 2/2] gdi32: Generate and use a lookup cache when looking up RGB values for a color table.
by Gabriel Ivăncescu
Hi Huw,
Thanks for the review!
On 01/04/2021 10:52, Huw Davies wrote:
> On Wed, Mar 31, 2021 at 03:35:58PM +0300, Gabriel Ivăncescu wrote:
>> This vastly improves the performance. The cache generation is relatively
>> constant in terms of algorithm complexity, around O(n) with some extra
>> overhead for the entire cache (depending on the color table's colors, because
>> they get "blocked" soon enough), which is fixed at 32768 entries. It scales
>> well with large amount of colors in the color table. The lookup after that
>> should be fast.
>>
>> In contrast, the current method is O(N * M) where N is the amount of pixels
>> and M is the number of colors in the table, which is very slow for larger M
>> (especially 256 colors).
>>
>> More detailed information in the bug report.
>>
>> Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=50898
>> Signed-off-by: Gabriel Ivăncescu <gabrielopcode(a)gmail.com>
>> ---
>> dlls/gdi32/dibdrv/primitives.c | 272 +++++++++++++++++++++++++++++++--
>> 1 file changed, 256 insertions(+), 16 deletions(-)
>>
>> diff --git a/dlls/gdi32/dibdrv/primitives.c b/dlls/gdi32/dibdrv/primitives.c
>> index 569baae..cc86ed9 100644
>> --- a/dlls/gdi32/dibdrv/primitives.c
>> +++ b/dlls/gdi32/dibdrv/primitives.c
>> @@ -3497,22 +3497,255 @@ static void convert_to_16(dib_info *dst, const dib_info *src, const RECT *src_re
>> }
>> }
>>
>> -static inline BOOL color_tables_match(const dib_info *d1, const dib_info *d2)
>> +/*
>> + * To lookup RGB values into nearest color in the color table, Windows uses 5-bits of the RGB
>> + * at the "center" of the RGB cube, presumably to do a similar lookup cache. The lowest 3 bits
>> + * of the color are thus set to halfway (0x04) and then it's used in the distance calculation
>> + * to the exact color in the color table. We exploit this as well to create a lookup cache.
>> + *
>> + * Generating the table is done by going "outwards" from the center of each color in the table.
>> + * First, we find out the center RGB cube spot into the map for each color in the color table.
>> + * In case of conflict, we calculate the *true* distance, since color table is not quantized.
>> + * Each color in the color table should then have an associated center in the lookup cache map.
>> + *
>> + * Next, we go "outwards" from each center by increasing an offset for the main axis. This is
>> + * always expressed as an offset from the center, and starts from 1 and goes up. Each offset
>> + * has multiple generations. A generation defines the distance from the center, and is always
>> + * increasing. Thus when we're at a generation, we go through *all* colors in the color table
>> + * first, fill the entire generation for all of them, then move to the next generation. Since
>> + * the next generation will always have a larger distance than the current one, we know that
>> + * once we filled all the previous generations, we're done with them and don't need to check.
>> + *
>> + * Note that an offset spans multiple generations, due to how distances work in 3 dimensions.
>> + * For example, an offset of 1 is simple and has 3 generations. The examples below are all
>> + * offsets from the center cube, e.g. (0,-1,1) means +1 for first axis, -1 for the second.
>> + *
>> + * First generation (offset = 1):
>> + * ( 0, 0, 1) ( 0, 0,-1) ( 0, 1, 0) ( 0,-1, 0) ( 1, 0, 0) (-1, 0, 0)
>> + *
>> + * In the first generation, the distance is always 1. For each color in the color table, we
>> + * go through all of the above displacements from the center, and fill the lookup cache map.
>> + *
>> + * Second generation (offset = 1 still, but distance is larger now; some are dups, that's OK):
>> + * ( 0, 1, 1) ( 0,-1, 1) ( 1, 0, 1) (-1, 0, 1) ( 0, 1,-1) ( 0,-1,-1) ( 1, 0,-1) (-1, 0,-1)
>> + * ( 0, 1, 1) ( 0, 1,-1) ( 1, 1, 0) (-1, 1, 0) ( 0,-1, 1) ( 0,-1,-1) ( 1,-1, 0) (-1,-1, 0)
>> + * ( 1, 0, 1) ( 1, 0,-1) ( 1, 1, 0) ( 1,-1, 0) (-1, 0, 1) (-1, 0,-1) (-1, 1, 0) (-1,-1, 0)
>> + *
>> + * On the 2nd line, we swapped the main axis from (x,x,1) to (x,1,x), and on 3rd to (1,x,x).
>> + * Note that the distance is always the same (sqrt(2)), and we just permute the axis values.
>> + *
>> + * Third generation (offset = 1 still, but distance is even larger: sqrt(3)):
>> + * ( 1, 1, 1) ( 1,-1, 1) (-1, 1, 1) (-1,-1, 1) ( 1, 1,-1) ( 1,-1,-1) (-1, 1,-1) (-1,-1,-1)
>> + * ...
>> + *
>> + * Only after all of the coordinate absolute values are equal to the offset do we increase it.
>> + * When the offset is increased, the main axis' absolute value is increased, and the distance.
>> + * For other offsets, such as offset = 3, generations start with zeros for the other axis, for
>> + * example (0,0,3). The next generation will increase one axis by 1, e.g. (0,1,3) and (1,0,3)
>> + * which are part of the same generation (same distance). The next generation will increase the
>> + * third axis, e.g. (1,1,3). Then the second axis is raised again, and the process repeats for
>> + * the third axis until it's equal to the second axis, e.g. (0,2,3)->(1,2,3)->(2,2,3).
>> + *
>> + * As each generation is increased, the lowest value in the axis is increased, and the other
>> + * starts from zero again. Then they are swapped and permuted. This is due to how the distance
>> + * is calculated in 3 dimensions. For each generation we fill *each* color in the color table's
>> + * displacement in all directions, since they have the same distance.
>> + *
>> + * Lastly, we keep track of each of the six directions (in 3 dimensions) and if one direction
>> + * was not filled at all in the current offset, we mark it as "blocked" and we no longer check
>> + * for it the next offset. When all six directions are blocked, the entry is no longer checked.
>> + *
>> + * The lookup cache map is completely filled when *all* color table entries are blocked.
>> +*/
>
> Perhaps I'm misunderstanding your algorithm, but doesn't this end up with
> (2,2,2) coming before (3,0,0)?
>
Actually yeah, you're right, I messed up somewhere when testing with the
quantized cubes. I'll need to revamp this at some point, but for now
I'll leave it aside. I'm thinking of going with something else easier
(see below), if it's acceptable.
>> +static void rgb_lookup_colortable_init(const dib_info *dib, struct rgb_lookup_colortable_ctx *ctx)
>> {
>> - /* Windows reduces precision to 5 bits, probably in order to build some sort of lookup cache */
>> - return rgb_to_pixel_colortable( dst, (r & ~7) + 4, (g & ~7) + 4, (b & ~7) + 4 );
>> + BYTE indices[256], available_directions[256], tmp_directions[256], map_set_bits[ARRAY_SIZE(ctx->map) / 8];
>> + unsigned color_table_size = dib->color_table ? dib->color_table_size : 1 << dib->bit_count;
>> + const RGBQUAD *color_table = get_dib_color_table(dib);
>> + unsigned idx, offset, num_entries;
>> + int i, j;
>> +
>> + /* Testing shows that for low amount of colors, the overhead is larger than the
>> + O(N*M) algorithm, presumably due to branch prediction and cache locality, but
>> + it gets quickly out of hand (up to 5x slower) for larger amount of colors... */
>> + if (color_table_size <= 40)
>> + {
>
> As a side note, there are going to be a vanishingly small number of
> colour tables with sizes [17,255], so if you need to special case,
> special case for sizes <= 16.
>
>> + unsigned r, g, b;
>> +
>> + for (b = 4; b < 256; b += 1 << 3)
>> + for (g = 4; g < 256; g += 1 << 3)
>> + for (r = 4; r < 256; r += 1 << 3)
>> + ctx->map[r >> 3 | (g & ~7) << 2 | (b & ~7) << 7] = rgb_to_pixel_colortable(dib, r, g, b);
>> + return;
>> + }
>> +
>> + memset(map_set_bits, 0, sizeof(map_set_bits));
>> + memset(available_directions, 0xff, color_table_size);
>> + memset(tmp_directions, 0, color_table_size);
>> +
>> + /* indirect list of valid color table indices, as we remove those fully surrounded */
>> + for (idx = 0; idx < color_table_size; idx++)
>> + indices[idx] = idx;
>> +
>> + /* first, fill the centers (offset = 0) of each quantized table color in the map */
>> + for (idx = 0; idx < color_table_size; idx++)
>> + {
>> + int dr1, dg1, db1, dr2, dg2, db2, pos_r, pos_g, pos_b;
>> + unsigned pos = (color_table[idx].rgbRed >> 3) |
>> + (color_table[idx].rgbGreen & ~7) << 2 |
>> + (color_table[idx].rgbBlue & ~7) << 7;
>> +
>> + if (map_set_bits[pos / 8] & (1 << pos % 8))
>> + {
>> + pos_r = (color_table[idx].rgbRed & ~7) | 4;
>> + pos_g = (color_table[idx].rgbGreen & ~7) | 4;
>> + pos_b = (color_table[idx].rgbBlue & ~7) | 4;
>> + dr1 = pos_r - color_table[idx].rgbRed;
>> + dg1 = pos_g - color_table[idx].rgbGreen;
>> + db1 = pos_b - color_table[idx].rgbBlue;
>> + dr2 = pos_r - color_table[ctx->map[pos]].rgbRed;
>> + dg2 = pos_g - color_table[ctx->map[pos]].rgbGreen;
>> + db2 = pos_b - color_table[ctx->map[pos]].rgbBlue;
>> +
>> + if (dr1*dr1 + dg1*dg1 + db1*db1 >= dr2*dr2 + dg2*dg2 + db2*db2)
>> + continue;
>> + }
>> + else
>> + map_set_bits[pos / 8] |= 1 << pos % 8;
>> +
>> + ctx->map[pos] = idx;
>> + }
>> +
>> + /* now do the rest */
>> + for (offset = 1, num_entries = color_table_size; num_entries != 0; offset++)
>> + {
>> + for (i = 0; i <= offset; i++)
>> + {
>> + for (j = 0; j <= i; j++)
>> + {
>> + /* we're at one generation now, go through each color in the color table */
>> + for (idx = 0; idx < num_entries; idx++)
>> + {
>> + unsigned direction_mask = 1, color_table_index = indices[idx];
>> + unsigned shift2 = 5, shift3 = 10;
>> + int center, main_axis = offset;
>> +
>> + center = (color_table[color_table_index].rgbRed >> 3) |
>> + (color_table[color_table_index].rgbGreen & ~7) << 2 |
>> + (color_table[color_table_index].rgbBlue & ~7) << 7;
>> + do
>> + {
>> + do
>> + {
>> + if (available_directions[idx] & direction_mask)
>> + {
>> + BOOL direction_blocked = TRUE;
>> + do
>> + {
>> + unsigned shift_tmp;
>> + do
>> + {
>> + do
>> + {
>> + unsigned pos = center + main_axis + (i << shift2) + (j << shift3);
>> +
>> + if (rgb_lookup_colortable_set_pos(ctx, pos, color_table, color_table_index, map_set_bits))
>> + direction_blocked = FALSE;
>> + j = -j;
>> + } while (j < 0);
>> + i = -i;
>> + } while (i < 0);
>> +
>> + /* repeat once and swap the two non-main axis */
>> + shift_tmp = shift2; shift2 = shift3; shift3 = shift_tmp;
>> + } while (shift2 > shift3 && i != j);
>> +
>> + if (!direction_blocked)
>> + tmp_directions[idx] |= direction_mask;
>> + }
>> + direction_mask <<= 1;
>> + main_axis = -main_axis;
>> + } while (main_axis < 0);
>> +
>> + /* change main to next axis */
>> + shift2 = 0;
>> + shift3 = (main_axis < 32) ? 10 : 5;
>> + main_axis <<= 5;
>> + } while (main_axis < 32768);
>> + }
>> + }
>> + }
>
> I suspect things might become clearer if one were to unwind some of these loops.
>
> Did you consider using a lazy initialization of the lookup table instead?
>
> Huw.
>
Lazy initialization meaning build it as we scan the image? That would
work too. I also thought of caching the color tables globally.
Now I don't know if that's acceptable, but here's my idea since it's
what is good in "practice":
Hold a small fixed size global cache (4-5 entries) of color tables, with
synchronization in case of multi-threading (just for correctness, I
doubt any app will actually multi-thread this). If we go the lazy
initialization approach, we'll also keep track which of the entries were
not filled yet in the cache.
It would be a global table for all color tables, but since I expect all
the performance problems to be using the same color table over and over
again in a loop, it would work "in practice".
When we have to discard/replace one, we choose the oldest one that
wasn't accessed (we keep some timestamp on each cache). Does that sound
feasible?
Maybe if I end up using this global cache, I can discard the lazy init
since it complicates things, do you think that's better?
Thanks,
Gabriel
April 1, 2021
Re: [PATCH] Rewrite wineserver main.c in Haskell.
by Jefferson Carpenter
On 4/1/2021 12:30 AM, Marvin wrote:
> Hi,
>
> While running your changed tests, I think I found new failures.
> Being a bot and all I'm not very good at pattern recognition, so I might be
> wrong, but could you please double-check?
>
> Full results can be found at:
> https://testbot.winehq.org/JobDetails.pl?Key=88045
>
> Your paranoid android.
>
>
> === debiant2 (build log) ===
>
> configure: error: Install ghc.
> Task: The win32 Wine build failed
>
> === debiant2 (build log) ===
>
> configure: error: Install ghc.
> Task: The wow64 Wine build failed
>
Francois, We're ultimately gonna need stack installed on the test boxes.
This is of course the first patch in the Rewrite Wine in Haskell series.
April 1, 2021