@@ -2,17 +2,18 @@ import { DotNet } from '@microsoft/dotnet-js-interop';
2
2
import { attachDebuggerHotkey , hasDebuggingEnabled } from './MonoDebugger' ;
3
3
import { showErrorNotification } from '../../BootErrors' ;
4
4
import { WebAssemblyResourceLoader , LoadingResource } from '../WebAssemblyResourceLoader' ;
5
- import { Platform , System_Array , Pointer , System_Object , System_String } from '../Platform' ;
5
+ import { Platform , System_Array , Pointer , System_Object , System_String , HeapLock } from '../Platform' ;
6
6
import { loadTimezoneData } from './TimezoneDataFile' ;
7
7
import { WebAssemblyBootResourceType } from '../WebAssemblyStartOptions' ;
8
8
import { initializeProfiling } from '../Profiling' ;
9
9
10
- let mono_string_get_utf8 : ( managedString : System_String ) => Pointer ;
11
10
let mono_wasm_add_assembly : ( name : string , heapAddress : number , length : number ) => void ;
12
11
const appBinDirName = 'appBinDir' ;
13
12
const uint64HighOrderShift = Math . pow ( 2 , 32 ) ;
14
13
const maxSafeNumberHighPart = Math . pow ( 2 , 21 ) - 1 ; // The high-order int32 from Number.MAX_SAFE_INTEGER
15
14
15
+ let currentHeapLock : MonoHeapLock | null = null ;
16
+
16
17
// Memory access helpers
17
18
// The implementations are exactly equivalent to what the global getValue(addr, type) function does,
18
19
// except without having to parse the 'type' parameter, and with less risk of mistakes at the call site
@@ -124,12 +125,29 @@ export const monoPlatform: Platform = {
124
125
return unboxedValue ;
125
126
}
126
127
127
- return BINDING . conv_string ( fieldValue as any as System_String ) ;
128
+ let decodedString : string | null | undefined ;
129
+ if ( currentHeapLock ) {
130
+ decodedString = currentHeapLock . stringCache . get ( fieldValue ) ;
131
+ if ( decodedString === undefined ) {
132
+ decodedString = BINDING . conv_string ( fieldValue as any as System_String ) ;
133
+ currentHeapLock . stringCache . set ( fieldValue , decodedString ) ;
134
+ }
135
+ } else {
136
+ decodedString = BINDING . conv_string ( fieldValue as any as System_String ) ;
137
+ }
138
+
139
+ return decodedString ;
128
140
} ,
129
141
130
142
readStructField : function readStructField < T extends Pointer > ( baseAddress : Pointer , fieldOffset ?: number ) : T {
131
143
return ( ( baseAddress as any as number ) + ( fieldOffset || 0 ) ) as any as T ;
132
144
} ,
145
+
146
+ beginHeapLock : function ( ) {
147
+ assertHeapIsNotLocked ( ) ;
148
+ currentHeapLock = new MonoHeapLock ( ) ;
149
+ return currentHeapLock ;
150
+ }
133
151
} ;
134
152
135
153
function addScriptTagsToDocument ( resourceLoader : WebAssemblyResourceLoader ) {
@@ -246,7 +264,6 @@ function createEmscriptenModuleInstance(resourceLoader: WebAssemblyResourceLoade
246
264
module . preRun . push ( ( ) => {
247
265
// By now, emscripten should be initialised enough that we can capture these methods for later use
248
266
mono_wasm_add_assembly = cwrap ( 'mono_wasm_add_assembly' , null , [ 'string' , 'number' , 'number' ] ) ;
249
- mono_string_get_utf8 = cwrap ( 'mono_wasm_string_get_utf8' , 'number' , [ 'number' ] ) ;
250
267
MONO . loaded_files = [ ] ;
251
268
252
269
if ( timeZoneResource ) {
@@ -387,6 +404,7 @@ function attachInteropInvoker(): void {
387
404
388
405
DotNet . attachDispatcher ( {
389
406
beginInvokeDotNetFromJS : ( callId : number , assemblyName : string | null , methodIdentifier : string , dotNetObjectId : any | null , argsJson : string ) : void => {
407
+ assertHeapIsNotLocked ( ) ;
390
408
if ( ! dotNetObjectId && ! assemblyName ) {
391
409
throw new Error ( 'Either assemblyName or dotNetObjectId must have a non null value.' ) ;
392
410
}
@@ -409,6 +427,7 @@ function attachInteropInvoker(): void {
409
427
) ;
410
428
} ,
411
429
invokeDotNetFromJS : ( assemblyName , methodIdentifier , dotNetObjectId , argsJson ) => {
430
+ assertHeapIsNotLocked ( ) ;
412
431
return dotNetDispatcherInvokeMethodHandle (
413
432
assemblyName ? assemblyName : null ,
414
433
methodIdentifier ,
@@ -460,3 +479,22 @@ function changeExtension(filename: string, newExtensionWithLeadingDot: string) {
460
479
461
480
return filename . substr ( 0 , lastDotIndex ) + newExtensionWithLeadingDot ;
462
481
}
482
+
483
+ function assertHeapIsNotLocked ( ) {
484
+ if ( currentHeapLock ) {
485
+ throw new Error ( 'Assertion failed - heap is currently locked' ) ;
486
+ }
487
+ }
488
+
489
+ class MonoHeapLock implements HeapLock {
490
+ // Within a given heap lock, it's safe to cache decoded strings since the memory can't change
491
+ stringCache = new Map < number , string | null > ( ) ;
492
+
493
+ release ( ) {
494
+ if ( currentHeapLock !== this ) {
495
+ throw new Error ( 'Trying to release a lock which isn\'t current' ) ;
496
+ }
497
+
498
+ currentHeapLock = null ;
499
+ }
500
+ }
0 commit comments