Skip to content

Commit 89d4f74

Browse files
committed
wip
1 parent 16d8126 commit 89d4f74

31 files changed

+670
-652
lines changed

eng/liveBuilds.targets

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,6 @@
192192
$(LibrariesNativeArtifactsPath)package.json;
193193
$(LibrariesNativeArtifactsPath)dotnet.wasm;
194194
$(LibrariesNativeArtifactsPath)dotnet.js.symbols;
195-
$(LibrariesNativeArtifactsPath)dotnet.timezones.blat;
196195
$(LibrariesNativeArtifactsPath)*.dat;"
197196
IsNative="true" />
198197
<!-- for threaded wasm -->

eng/native/configurecompiler.cmake

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -584,6 +584,9 @@ if(CLR_CMAKE_TARGET_UNIX)
584584
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_ANDROID>)
585585
elseif(CLR_CMAKE_TARGET_LINUX)
586586
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_LINUX>)
587+
if(CLR_CMAKE_TARGET_BROWSER)
588+
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_BROWSER>)
589+
endif()
587590
if(CLR_CMAKE_TARGET_LINUX_MUSL)
588591
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_LINUX_MUSL>)
589592
endif()
@@ -597,8 +600,6 @@ if(CLR_CMAKE_TARGET_UNIX)
597600
endif()
598601
elseif(CLR_CMAKE_TARGET_WASI)
599602
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_WASI>)
600-
elseif(CLR_CMAKE_TARGET_BROWSER)
601-
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_BROWSER>)
602603
else(CLR_CMAKE_TARGET_UNIX)
603604
add_compile_definitions($<$<NOT:$<BOOL:$<TARGET_PROPERTY:IGNORE_DEFAULT_TARGET_OS>>>:TARGET_WINDOWS>)
604605
endif(CLR_CMAKE_TARGET_UNIX)

src/installer/pkg/sfx/Microsoft.NETCore.App/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,13 +221,13 @@
221221
<PlatformManifestFileEntry Include="libmono-profiler-browser.a" IsNative="true" />
222222
<PlatformManifestFileEntry Include="libmono-wasm-eh-js.a" IsNative="true" />
223223
<PlatformManifestFileEntry Include="libmono-wasm-eh-wasm.a" IsNative="true" />
224+
<PlatformManifestFileEntry Include="wasm-bundled-timezones.a" IsNative="true" />
224225
<PlatformManifestFileEntry Include="dotnet.js" IsNative="true" />
225226
<PlatformManifestFileEntry Include="dotnet.worker.js" IsNative="true" />
226227
<PlatformManifestFileEntry Include="dotnet.js.symbols" IsNative="true" />
227228
<PlatformManifestFileEntry Include="dotnet.d.ts" IsNative="true" />
228229
<PlatformManifestFileEntry Include="dotnet-legacy.d.ts" IsNative="true" />
229230
<PlatformManifestFileEntry Include="dotnet.wasm" IsNative="true" />
230-
<PlatformManifestFileEntry Include="dotnet.timezones.blat" IsNative="true" />
231231
<PlatformManifestFileEntry Include="icudt.dat" IsNative="true" />
232232
<PlatformManifestFileEntry Include="icudt_no_CJK.dat" IsNative="true" />
233233
<PlatformManifestFileEntry Include="icudt_CJK.dat" IsNative="true" />
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System;
5+
using System.Runtime.InteropServices;
6+
7+
internal static partial class Interop
8+
{
9+
internal static partial class Sys
10+
{
11+
[LibraryImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetTimeZoneData", StringMarshalling = StringMarshalling.Utf8, SetLastError = true)]
12+
internal static partial IntPtr GetTimeZoneData(string fileName, out int length);
13+
}
14+
}

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2143,6 +2143,9 @@
21432143
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetDefaultTimeZone.AnyMobile.cs" Condition="'$(TargetsAndroid)' == 'true' or '$(TargetsLinuxBionic)' == 'true' or '$(IsiOSLike)' == 'true'">
21442144
<Link>Common\Interop\Unix\System.Native\Interop.GetDefaultTimeZone.AnyMobile.cs</Link>
21452145
</Compile>
2146+
<Compile Include="$(CommonPath)Interop\Unix\System.Native\Interop.GetTimeZoneData.Wasm.cs" Condition="'$(TargetsWasi)' == 'true' or '$(TargetsBrowser)' == 'true'">
2147+
<Link>Common\src\Interop\Unix\System.Native\Interop.GetTimeZoneData.Wasm.cs</Link>
2148+
</Compile>
21462149
<Compile Include="$(CommonPath)\Interop\Unix\System.Native\Interop.GetEnv.cs">
21472150
<Link>Common\Interop\Unix\System.Native\Interop.GetEnv.cs</Link>
21482151
</Compile>

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.NonAndroid.cs

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public sealed partial class TimeZoneInfo
1818
private const string TimeZoneDirectoryEnvironmentVariable = "TZDIR";
1919
private const string TimeZoneEnvironmentVariable = "TZ";
2020

21+
#if TARGET_WASI || TARGET_BROWSER
22+
// if TZDIR is set, then the embedded TZ data will be ignored and normal unix behavior will be used
23+
private static readonly bool UseEmbeddedTzDatabase = Environment.GetEnvironmentVariable(TimeZoneDirectoryEnvironmentVariable) == null;
24+
#endif
25+
2126
private static TimeZoneInfo GetLocalTimeZoneCore()
2227
{
2328
// Without Registry support, create the TimeZoneInfo from a TZ file
@@ -29,9 +34,30 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
2934
value = null;
3035
e = null;
3136

37+
byte[]? rawData=null;
38+
#if TARGET_WASI || TARGET_BROWSER
39+
if (UseEmbeddedTzDatabase)
40+
{
41+
if(!TryLoadEmbeddedTzFile(id, ref rawData))
42+
{
43+
e = new FileNotFoundException(id, "Embedded TZ data not found");
44+
return TimeZoneInfoResult.TimeZoneNotFoundException;
45+
}
46+
47+
value = GetTimeZoneFromTzData(rawData, id);
48+
49+
if (value == null)
50+
{
51+
e = new InvalidTimeZoneException(SR.Format(SR.InvalidTimeZone_InvalidFileData, id, id));
52+
return TimeZoneInfoResult.InvalidTimeZoneException;
53+
}
54+
55+
return TimeZoneInfoResult.Success;
56+
}
57+
#endif
58+
3259
string timeZoneDirectory = GetTimeZoneDirectory();
3360
string timeZoneFilePath = Path.Combine(timeZoneDirectory, id);
34-
byte[] rawData;
3561
try
3662
{
3763
rawData = File.ReadAllBytes(timeZoneFilePath);
@@ -74,52 +100,68 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachineCore(string id,
74100
/// <remarks>
75101
/// Lines that start with # are comments and are skipped.
76102
/// </remarks>
77-
private static List<string> GetTimeZoneIds()
103+
private static IEnumerable<string> GetTimeZoneIds()
104+
{
105+
#if TARGET_WASI || TARGET_BROWSER
106+
byte[]? rawData = null;
107+
if (UseEmbeddedTzDatabase)
108+
{
109+
if(!TryLoadEmbeddedTzFile(TimeZoneFileName, ref rawData))
110+
{
111+
return Array.Empty<string>();
112+
}
113+
using var reader = new StreamReader(new MemoryStream(rawData), Encoding.UTF8);
114+
return ParseTimeZoneIds(reader);
115+
}
116+
#endif
117+
try
118+
{
119+
using var reader = new StreamReader(Path.Combine(GetTimeZoneDirectory(), TimeZoneFileName), Encoding.UTF8);
120+
return ParseTimeZoneIds(reader);
121+
}
122+
catch (IOException) { }
123+
catch (UnauthorizedAccessException) { }
124+
return Array.Empty<string>();
125+
}
126+
127+
private static List<string> ParseTimeZoneIds(StreamReader reader)
78128
{
79129
List<string> timeZoneIds = new List<string>();
80130

81-
try
131+
string? zoneTabFileLine;
132+
while ((zoneTabFileLine = reader.ReadLine()) != null)
82133
{
83-
using (StreamReader sr = new StreamReader(Path.Combine(GetTimeZoneDirectory(), TimeZoneFileName), Encoding.UTF8))
134+
if (!string.IsNullOrEmpty(zoneTabFileLine) && zoneTabFileLine[0] != '#')
84135
{
85-
string? zoneTabFileLine;
86-
while ((zoneTabFileLine = sr.ReadLine()) != null)
136+
// the format of the line is "country-code \t coordinates \t TimeZone Id \t comments"
137+
138+
int firstTabIndex = zoneTabFileLine.IndexOf('\t');
139+
if (firstTabIndex >= 0)
87140
{
88-
if (!string.IsNullOrEmpty(zoneTabFileLine) && zoneTabFileLine[0] != '#')
141+
int secondTabIndex = zoneTabFileLine.IndexOf('\t', firstTabIndex + 1);
142+
if (secondTabIndex >= 0)
89143
{
90-
// the format of the line is "country-code \t coordinates \t TimeZone Id \t comments"
91-
92-
int firstTabIndex = zoneTabFileLine.IndexOf('\t');
93-
if (firstTabIndex >= 0)
144+
string timeZoneId;
145+
int startIndex = secondTabIndex + 1;
146+
int thirdTabIndex = zoneTabFileLine.IndexOf('\t', startIndex);
147+
if (thirdTabIndex >= 0)
94148
{
95-
int secondTabIndex = zoneTabFileLine.IndexOf('\t', firstTabIndex + 1);
96-
if (secondTabIndex >= 0)
97-
{
98-
string timeZoneId;
99-
int startIndex = secondTabIndex + 1;
100-
int thirdTabIndex = zoneTabFileLine.IndexOf('\t', startIndex);
101-
if (thirdTabIndex >= 0)
102-
{
103-
int length = thirdTabIndex - startIndex;
104-
timeZoneId = zoneTabFileLine.Substring(startIndex, length);
105-
}
106-
else
107-
{
108-
timeZoneId = zoneTabFileLine.Substring(startIndex);
109-
}
149+
int length = thirdTabIndex - startIndex;
150+
timeZoneId = zoneTabFileLine.Substring(startIndex, length);
151+
}
152+
else
153+
{
154+
timeZoneId = zoneTabFileLine.Substring(startIndex);
155+
}
110156

111-
if (!string.IsNullOrEmpty(timeZoneId))
112-
{
113-
timeZoneIds.Add(timeZoneId);
114-
}
115-
}
157+
if (!string.IsNullOrEmpty(timeZoneId))
158+
{
159+
timeZoneIds.Add(timeZoneId);
116160
}
117161
}
118162
}
119163
}
120164
}
121-
catch (IOException) { }
122-
catch (UnauthorizedAccessException) { }
123165

124166
return timeZoneIds;
125167
}
@@ -379,6 +421,22 @@ private static bool TryLoadTzFile(string tzFilePath, [NotNullWhen(true)] ref byt
379421
return false;
380422
}
381423

424+
#if TARGET_WASI || TARGET_BROWSER
425+
private static bool TryLoadEmbeddedTzFile(string name, [NotNullWhen(true)] ref byte[]? rawData)
426+
{
427+
IntPtr bytes = Interop.Sys.GetTimeZoneData(name, out int length);
428+
if(bytes == IntPtr.Zero)
429+
{
430+
rawData = null;
431+
return false;
432+
}
433+
434+
rawData = new byte[length];
435+
Marshal.Copy(bytes, rawData, 0, length);
436+
return true;
437+
}
438+
#endif
439+
382440
/// <summary>
383441
/// Gets the tzfile raw data for the current 'local' time zone using the following rules.
384442
///
@@ -387,6 +445,10 @@ private static bool TryLoadTzFile(string tzFilePath, [NotNullWhen(true)] ref byt
387445
/// 2. Get the default TZ from the device
388446
/// 3. Use UTC if all else fails.
389447
///
448+
/// On WASI / Browser
449+
/// 0. if TZDIR is not set, use TZ variable as id to embedded database.
450+
/// 1. fall back to unix behavior if TZDIR is set.
451+
///
390452
/// On all other platforms
391453
/// 1. Read the TZ environment variable. If it is set, use it.
392454
/// 2. Look for the data in /etc/localtime.
@@ -406,6 +468,11 @@ private static bool TryGetLocalTzFile([NotNullWhen(true)] out byte[]? rawData, [
406468
{
407469
#if TARGET_IOS || TARGET_TVOS
408470
tzVariable = Interop.Sys.GetDefaultTimeZone();
471+
#elif TARGET_WASI || TARGET_BROWSER
472+
if (UseEmbeddedTzDatabase)
473+
{
474+
return false; // use UTC
475+
}
409476
#else
410477
return
411478
TryLoadTzFile("/etc/localtime", ref rawData, ref id) ||
@@ -419,6 +486,17 @@ private static bool TryGetLocalTzFile([NotNullWhen(true)] out byte[]? rawData, [
419486
{
420487
return false;
421488
}
489+
#if TARGET_WASI || TARGET_BROWSER
490+
if (UseEmbeddedTzDatabase)
491+
{
492+
if(!TryLoadEmbeddedTzFile(tzVariable, ref rawData))
493+
{
494+
return false;
495+
}
496+
id = tzVariable;
497+
return true;
498+
}
499+
#endif
422500

423501
// Otherwise, use the path from the env var. If it's not absolute, make it relative
424502
// to the system timezone directory

src/mono/mono/utils/mono-dl-wasm.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include <config.h>
22
#include <mono/utils/mono-compiler.h>
3+
#include <mono/eglib/glib.h>
34

45
#if defined (HOST_WASM)
56

@@ -93,3 +94,45 @@ mono_dl_close_handle (MonoDl *module, MonoError *error)
9394
MONO_EMPTY_SOURCE_FILE (mono_dl_wasm);
9495

9596
#endif
97+
98+
#if defined (HOST_WASM)
99+
100+
static GHashTable *name_to_blob = NULL;
101+
102+
typedef struct {
103+
const unsigned char *data;
104+
unsigned int size;
105+
} FileBlob;
106+
107+
int
108+
mono_wasm_add_bundled_file (const char *name, const unsigned char *data, unsigned int size)
109+
{
110+
// printf("mono_wasm_add_bundled_file: %s %p %d\n", name, data, size);
111+
if(name_to_blob == NULL)
112+
{
113+
name_to_blob = g_hash_table_new (g_str_hash, g_str_equal);
114+
}
115+
FileBlob *blob = g_new0 (FileBlob, 1);
116+
blob->data = data;
117+
blob->size = size;
118+
g_hash_table_insert (name_to_blob, (gpointer) name, blob);
119+
return 0;
120+
}
121+
122+
const unsigned char*
123+
mono_wasm_get_bundled_file (const char *name, int* out_length)
124+
{
125+
FileBlob *blob = (FileBlob *)g_hash_table_lookup (name_to_blob, name);
126+
if (blob != NULL)
127+
{
128+
// printf("mono_wasm_get_bundled_file: %s %p %d \n", name, blob->data, blob->size);
129+
*out_length = blob->size;
130+
return blob->data;
131+
}
132+
133+
// printf("mono_wasm_get_bundled_file: %s not found \n", name);
134+
*out_length = 0;
135+
return NULL;
136+
}
137+
138+
#endif /* HOST_WASM */

src/mono/mono/utils/mono-dl.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,10 @@ int mono_dl_convert_flags (int mono_flags, int native_flags);
5858
char* mono_dl_current_error_string (void);
5959
const char* mono_dl_get_system_dir (void);
6060

61+
#if defined (HOST_WASM)
62+
int mono_wasm_add_bundled_file (const char *name, const unsigned char *data, unsigned int size);
63+
const unsigned char* mono_wasm_get_bundled_file (const char *name, int* out_length);
64+
#endif /* HOST_WASM */
65+
6166
#endif /* __MONO_UTILS_DL_H__ */
6267

0 commit comments

Comments
 (0)