Skip to content

Commit 7d52244

Browse files
Added a potential fix for GitHub Issue #400.
1 parent f764372 commit 7d52244

12 files changed

Lines changed: 176 additions & 95 deletions

File tree

ClearScript/IScriptEngineException.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ public interface IScriptEngineException
4545
/// </summary>
4646
dynamic ScriptException { get; }
4747

48+
/// <summary>
49+
/// Gets the script exception object that caused the current exception to be thrown, or <c>null</c> if one was not specified.
50+
/// </summary>
51+
/// <remarks>
52+
/// This property returns the same object or value as <see cref="ScriptException"/>, but
53+
/// accessing it does not trigger the construction of a dynamic call site.
54+
/// </remarks>
55+
object ScriptExceptionAsObject { get; }
56+
4857
/// <summary>
4958
/// Gets the host exception that caused the current exception to be thrown, or <c>null</c> if one was not specified.
5059
/// </summary>

ClearScript/ScriptEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,10 +1692,10 @@ internal static void ThrowScriptError(IScriptEngineException scriptError)
16921692
{
16931693
if (scriptError is ScriptInterruptedException)
16941694
{
1695-
throw new ScriptInterruptedException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptException, scriptError.InnerException);
1695+
throw new ScriptInterruptedException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptExceptionAsObject, scriptError.InnerException);
16961696
}
16971697

1698-
throw new ScriptEngineException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptException, scriptError.InnerException);
1698+
throw new ScriptEngineException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptExceptionAsObject, scriptError.InnerException);
16991699
}
17001700
}
17011701

ClearScript/ScriptEngineException.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ internal ScriptEngineException(string engineName, string message, string errorDe
123123
/// </summary>
124124
public dynamic ScriptException => scriptException;
125125

126+
/// <summary>
127+
/// Gets the script exception object that caused the current exception to be thrown, or <c>null</c> if one was not specified.
128+
/// </summary>
129+
/// <remarks>
130+
/// This property returns the same object or value as <see cref="ScriptException"/>, but
131+
/// accessing it does not trigger the construction of a dynamic call site.
132+
/// </remarks>
133+
public object ScriptExceptionAsObject => scriptException;
134+
126135
#endregion
127136

128137
#region Object overrides

ClearScript/ScriptInterruptedException.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,15 @@ internal ScriptInterruptedException(string engineName, string message, string er
123123
/// </summary>
124124
public dynamic ScriptException => scriptException;
125125

126+
/// <summary>
127+
/// Gets the script exception object that caused the current exception to be thrown, or <c>null</c> if one was not specified.
128+
/// </summary>
129+
/// <remarks>
130+
/// This property returns the same object or value as <see cref="ScriptException"/>, but
131+
/// accessing it does not trigger the construction of a dynamic call site.
132+
/// </remarks>
133+
public object ScriptExceptionAsObject => scriptException;
134+
126135
#endregion
127136

128137
#region Object overrides

ClearScript/ScriptItem.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ public static void ThrowLastScriptError()
2727
{
2828
if (scriptError is ScriptInterruptedException)
2929
{
30-
throw new ScriptInterruptedException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptException, scriptError.InnerException);
30+
throw new ScriptInterruptedException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptExceptionAsObject, scriptError.InnerException);
3131
}
3232

33-
throw new ScriptEngineException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptException, scriptError.InnerException);
33+
throw new ScriptEngineException(scriptError.EngineName, scriptError.Message, scriptError.ErrorDetails, scriptError.HResult, scriptError.IsFatal, scriptError.ExecutionStarted, scriptError.ScriptExceptionAsObject, scriptError.InnerException);
3434
}
3535
}
3636

ClearScript/V8/SplitProxy/V8EntityHolder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ internal readonly struct V8EntityHolder
1616
public V8EntityHolder(string name, Func<V8Entity.Handle> acquireHandle)
1717
{
1818
this.name = name;
19-
registered = V8Proxy.OnEntityHolderCreated();
19+
V8Proxy.OnEntityHolderCreated();
20+
registered = true;
2021
handle = acquireHandle();
2122
}
2223

ClearScript/V8/SplitProxy/V8SplitProxyManaged.cs

Lines changed: 108 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,24 @@ namespace Microsoft.ClearScript.V8.SplitProxy
1515
// ReSharper disable once PartialTypeWithSinglePart
1616
internal static partial class V8SplitProxyManaged
1717
{
18-
public static IntPtr MethodTable { get; } = CreateMethodTable();
18+
public static IntPtr MethodTable => pFunctionPtrs;
19+
20+
private static IntPtr pDelegatePtrs;
21+
private static IntPtr pFunctionPtrs;
22+
private static int methodCount;
1923

2024
[ThreadStatic] public static Exception ScheduledException;
2125

26+
public static void Initialize()
27+
{
28+
CreateMethodTable();
29+
}
30+
31+
public static void Teardown()
32+
{
33+
DestroyMethodTable();
34+
}
35+
2236
private static void ScheduleHostException(IntPtr pObject, Exception exception)
2337
{
2438
V8SplitProxyNative.InvokeNoThrow(instance => instance.HostException_Schedule(exception.GetBaseException().Message, V8ProxyHelpers.MarshalExceptionToScript(pObject, exception)));
@@ -305,110 +319,138 @@ [In] int count
305319

306320
#endregion
307321

308-
#region method table construction
322+
#region method table construction and teardown
309323

310-
private static IntPtr CreateMethodTable()
324+
private static void CreateMethodTable()
311325
{
312-
IntPtr[] methodPtrs =
326+
Debug.Assert(methodCount == 0);
327+
328+
(IntPtr, IntPtr)[] methodPairs =
313329
{
314330
//----------------------------------------------------------------------------
315331
// IMPORTANT: maintain synchronization with V8_SPLIT_PROXY_MANAGED_METHOD_LIST
316332
//----------------------------------------------------------------------------
317333

318-
GetMethodPtr<RawScheduleForwardingException>(ScheduleForwardingException),
319-
GetMethodPtr<RawScheduleInvalidOperationException>(ScheduleInvalidOperationException),
320-
GetMethodPtr<RawScheduleScriptEngineException>(ScheduleScriptEngineException),
321-
GetMethodPtr<RawScheduleScriptInterruptedException>(ScheduleScriptInterruptedException),
334+
GetMethodPair<RawScheduleForwardingException>(ScheduleForwardingException),
335+
GetMethodPair<RawScheduleInvalidOperationException>(ScheduleInvalidOperationException),
336+
GetMethodPair<RawScheduleScriptEngineException>(ScheduleScriptEngineException),
337+
GetMethodPair<RawScheduleScriptInterruptedException>(ScheduleScriptInterruptedException),
322338

323339
#if NET5_0_OR_GREATER
324-
InvokeHostActionFastMethodPtr,
340+
(IntPtr.Zero, InvokeHostActionFastMethodPtr),
325341
#else
326-
GetMethodPtr<RawInvokeAction>(InvokeHostAction),
342+
GetMethodPair<RawInvokeAction>(InvokeHostAction),
327343
#endif
328344

329-
GetMethodPtr<RawProcessArrayBufferOrViewData>(ProcessArrayBufferOrViewData),
330-
GetMethodPtr<RawProcessCpuProfile>(ProcessCpuProfile),
331-
GetMethodPtr<RawCreateV8ObjectCache>(CreateV8ObjectCache),
345+
GetMethodPair<RawProcessArrayBufferOrViewData>(ProcessArrayBufferOrViewData),
346+
GetMethodPair<RawProcessCpuProfile>(ProcessCpuProfile),
347+
GetMethodPair<RawCreateV8ObjectCache>(CreateV8ObjectCache),
332348

333349
#if NET5_0_OR_GREATER
334-
CacheV8ObjectFastMethodPtr,
335-
GetCachedV8ObjectFastMethodPtr,
350+
(IntPtr.Zero, CacheV8ObjectFastMethodPtr),
351+
(IntPtr.Zero, GetCachedV8ObjectFastMethodPtr),
336352
#else
337-
GetMethodPtr<RawCacheV8Object>(CacheV8Object),
338-
GetMethodPtr<RawGetCachedV8Object>(GetCachedV8Object),
353+
GetMethodPair<RawCacheV8Object>(CacheV8Object),
354+
GetMethodPair<RawGetCachedV8Object>(GetCachedV8Object),
339355
#endif
340356

341-
GetMethodPtr<RawGetAllCachedV8Objects>(GetAllCachedV8Objects),
342-
GetMethodPtr<RawRemoveV8ObjectCacheEntry>(RemoveV8ObjectCacheEntry),
343-
GetMethodPtr<RawCreateDebugAgent>(CreateDebugAgent),
344-
GetMethodPtr<RawSendDebugMessage>(SendDebugMessage),
345-
GetMethodPtr<RawDestroyDebugAgent>(DestroyDebugAgent),
346-
GetMethodPtr<RawGetMaxScriptCacheSize>(GetMaxScriptCacheSize),
347-
GetMethodPtr<RawGetMaxModuleCacheSize>(GetMaxModuleCacheSize),
357+
GetMethodPair<RawGetAllCachedV8Objects>(GetAllCachedV8Objects),
358+
GetMethodPair<RawRemoveV8ObjectCacheEntry>(RemoveV8ObjectCacheEntry),
359+
GetMethodPair<RawCreateDebugAgent>(CreateDebugAgent),
360+
GetMethodPair<RawSendDebugMessage>(SendDebugMessage),
361+
GetMethodPair<RawDestroyDebugAgent>(DestroyDebugAgent),
362+
GetMethodPair<RawGetMaxScriptCacheSize>(GetMaxScriptCacheSize),
363+
GetMethodPair<RawGetMaxModuleCacheSize>(GetMaxModuleCacheSize),
348364

349365
#if NET5_0_OR_GREATER
350-
AddRefHostObjectFastMethodPtr,
351-
ReleaseHostObjectFastMethodPtr,
352-
GetHostObjectInvocabilityFastMethodPtr,
366+
(IntPtr.Zero, AddRefHostObjectFastMethodPtr),
367+
(IntPtr.Zero, ReleaseHostObjectFastMethodPtr),
368+
(IntPtr.Zero, GetHostObjectInvocabilityFastMethodPtr),
353369
#else
354-
GetMethodPtr<RawAddRefHostObject>(AddRefHostObject),
355-
GetMethodPtr<RawReleaseHostObject>(ReleaseHostObject),
356-
GetMethodPtr<RawGetHostObjectInvocability>(GetHostObjectInvocability),
370+
GetMethodPair<RawAddRefHostObject>(AddRefHostObject),
371+
GetMethodPair<RawReleaseHostObject>(ReleaseHostObject),
372+
GetMethodPair<RawGetHostObjectInvocability>(GetHostObjectInvocability),
357373
#endif
358374

359375
#if NET5_0_OR_GREATER
360-
GetHostObjectNamedPropertyFastMethodPtr,
361-
GetHostObjectNamedPropertyWithCacheabilityFastMethodPtr,
362-
SetHostObjectNamedPropertyFastMethodPtr,
376+
(IntPtr.Zero, GetHostObjectNamedPropertyFastMethodPtr),
377+
(IntPtr.Zero, GetHostObjectNamedPropertyWithCacheabilityFastMethodPtr),
378+
(IntPtr.Zero, SetHostObjectNamedPropertyFastMethodPtr),
363379
#else
364-
GetMethodPtr<RawGetHostObjectNamedProperty>(GetHostObjectNamedProperty),
365-
GetMethodPtr<RawGetHostObjectNamedPropertyWithCacheability>(GetHostObjectNamedPropertyWithCacheability),
366-
GetMethodPtr<RawSetHostObjectNamedProperty>(SetHostObjectNamedProperty),
380+
GetMethodPair<RawGetHostObjectNamedProperty>(GetHostObjectNamedProperty),
381+
GetMethodPair<RawGetHostObjectNamedPropertyWithCacheability>(GetHostObjectNamedPropertyWithCacheability),
382+
GetMethodPair<RawSetHostObjectNamedProperty>(SetHostObjectNamedProperty),
367383
#endif
368384

369-
GetMethodPtr<RawDeleteHostObjectNamedProperty>(DeleteHostObjectNamedProperty),
370-
GetMethodPtr<RawGetHostObjectPropertyNames>(GetHostObjectPropertyNames),
385+
GetMethodPair<RawDeleteHostObjectNamedProperty>(DeleteHostObjectNamedProperty),
386+
GetMethodPair<RawGetHostObjectPropertyNames>(GetHostObjectPropertyNames),
371387

372388
#if NET5_0_OR_GREATER
373-
GetHostObjectIndexedPropertyFastMethodPtr,
374-
SetHostObjectIndexedPropertyFastMethodPtr,
389+
(IntPtr.Zero, GetHostObjectIndexedPropertyFastMethodPtr),
390+
(IntPtr.Zero, SetHostObjectIndexedPropertyFastMethodPtr),
375391
#else
376-
GetMethodPtr<RawGetHostObjectIndexedProperty>(GetHostObjectIndexedProperty),
377-
GetMethodPtr<RawSetHostObjectIndexedProperty>(SetHostObjectIndexedProperty),
392+
GetMethodPair<RawGetHostObjectIndexedProperty>(GetHostObjectIndexedProperty),
393+
GetMethodPair<RawSetHostObjectIndexedProperty>(SetHostObjectIndexedProperty),
378394
#endif
379395

380-
GetMethodPtr<RawDeleteHostObjectIndexedProperty>(DeleteHostObjectIndexedProperty),
381-
GetMethodPtr<RawGetHostObjectPropertyIndices>(GetHostObjectPropertyIndices),
396+
GetMethodPair<RawDeleteHostObjectIndexedProperty>(DeleteHostObjectIndexedProperty),
397+
GetMethodPair<RawGetHostObjectPropertyIndices>(GetHostObjectPropertyIndices),
382398

383399
#if NET5_0_OR_GREATER
384-
InvokeHostObjectFastMethodPtr,
385-
InvokeHostObjectMethodFastMethodPtr,
400+
(IntPtr.Zero, InvokeHostObjectFastMethodPtr),
401+
(IntPtr.Zero, InvokeHostObjectMethodFastMethodPtr),
386402
#else
387-
GetMethodPtr<RawInvokeHostObject>(InvokeHostObject),
388-
GetMethodPtr<RawInvokeHostObjectMethod>(InvokeHostObjectMethod),
403+
GetMethodPair<RawInvokeHostObject>(InvokeHostObject),
404+
GetMethodPair<RawInvokeHostObjectMethod>(InvokeHostObjectMethod),
389405
#endif
390406

391-
GetMethodPtr<RawGetHostObjectEnumerator>(GetHostObjectEnumerator),
392-
GetMethodPtr<RawGetHostObjectAsyncEnumerator>(GetHostObjectAsyncEnumerator),
393-
GetMethodPtr<RawQueueNativeCallback>(QueueNativeCallback),
394-
GetMethodPtr<RawCreateNativeCallbackTimer>(CreateNativeCallbackTimer),
395-
GetMethodPtr<RawChangeNativeCallbackTimer>(ChangeNativeCallbackTimer),
396-
GetMethodPtr<RawDestroyNativeCallbackTimer>(DestroyNativeCallbackTimer),
397-
GetMethodPtr<RawLoadModule>(LoadModule),
398-
GetMethodPtr<RawCreateModuleContext>(CreateModuleContext),
399-
GetMethodPtr<RawWriteBytesToStream>(WriteBytesToStream),
400-
GetMethodPtr<RawGetGlobalFlags>(GetGlobalFlags)
407+
GetMethodPair<RawGetHostObjectEnumerator>(GetHostObjectEnumerator),
408+
GetMethodPair<RawGetHostObjectAsyncEnumerator>(GetHostObjectAsyncEnumerator),
409+
GetMethodPair<RawQueueNativeCallback>(QueueNativeCallback),
410+
GetMethodPair<RawCreateNativeCallbackTimer>(CreateNativeCallbackTimer),
411+
GetMethodPair<RawChangeNativeCallbackTimer>(ChangeNativeCallbackTimer),
412+
GetMethodPair<RawDestroyNativeCallbackTimer>(DestroyNativeCallbackTimer),
413+
GetMethodPair<RawLoadModule>(LoadModule),
414+
GetMethodPair<RawCreateModuleContext>(CreateModuleContext),
415+
GetMethodPair<RawWriteBytesToStream>(WriteBytesToStream),
416+
GetMethodPair<RawGetGlobalFlags>(GetGlobalFlags)
401417
};
402418

403-
var pMethodTable = Marshal.AllocCoTaskMem(IntPtr.Size * methodPtrs.Length);
404-
Marshal.Copy(methodPtrs, 0, pMethodTable, methodPtrs.Length);
405-
return pMethodTable;
419+
methodCount = methodPairs.Length;
420+
pDelegatePtrs = Marshal.AllocCoTaskMem(methodCount * IntPtr.Size);
421+
pFunctionPtrs = Marshal.AllocCoTaskMem(methodCount * IntPtr.Size);
422+
423+
for (var index = 0; index < methodCount; index++)
424+
{
425+
Marshal.WriteIntPtr(pDelegatePtrs, index * IntPtr.Size, methodPairs[index].Item1);
426+
Marshal.WriteIntPtr(pFunctionPtrs, index * IntPtr.Size, methodPairs[index].Item2);
427+
}
428+
}
429+
430+
private static void DestroyMethodTable()
431+
{
432+
Debug.Assert(methodCount > 0);
433+
434+
for (var index = 0; index < methodCount; index++)
435+
{
436+
var pDelegate = Marshal.ReadIntPtr(pDelegatePtrs, index * IntPtr.Size);
437+
if (pDelegate != IntPtr.Zero)
438+
{
439+
V8ProxyHelpers.ReleaseHostObject(pDelegate);
440+
}
441+
}
442+
443+
Marshal.FreeCoTaskMem(pDelegatePtrs);
444+
Marshal.FreeCoTaskMem(pFunctionPtrs);
445+
446+
methodCount = 0;
447+
pDelegatePtrs = IntPtr.Zero;
448+
pFunctionPtrs = IntPtr.Zero;
406449
}
407450

408-
private static IntPtr GetMethodPtr<T>(T del)
451+
private static (IntPtr, IntPtr) GetMethodPair<T>(T del)
409452
{
410-
GCHandle.Alloc(del);
411-
return Marshal.GetFunctionPointerForDelegate((Delegate)(object)del);
453+
return (V8ProxyHelpers.AddRefHostObject(del), Marshal.GetFunctionPointerForDelegate((Delegate)(object)del));
412454
}
413455

414456
#endregion
@@ -422,11 +464,11 @@ private static void ScheduleForwardingException(V8Value.Ptr pException)
422464
var exception = V8ProxyHelpers.MarshalExceptionToHost(V8Value.Get(pException));
423465
if (exception is ScriptEngineException scriptEngineException)
424466
{
425-
ScheduledException = new ScriptEngineException(scriptEngineException.EngineName, scriptEngineException.Message, scriptEngineException.ErrorDetails, scriptEngineException.HResult, scriptEngineException.IsFatal, scriptEngineException.ExecutionStarted, scriptEngineException.ScriptException, scriptEngineException);
467+
ScheduledException = new ScriptEngineException(scriptEngineException.EngineName, scriptEngineException.Message, scriptEngineException.ErrorDetails, scriptEngineException.HResult, scriptEngineException.IsFatal, scriptEngineException.ExecutionStarted, scriptEngineException.ScriptExceptionAsObject, scriptEngineException);
426468
}
427469
else if (exception is ScriptInterruptedException scriptInterruptedException)
428470
{
429-
ScheduledException = new ScriptInterruptedException(scriptInterruptedException.EngineName, scriptInterruptedException.Message, scriptInterruptedException.ErrorDetails, scriptInterruptedException.HResult, scriptInterruptedException.IsFatal, scriptInterruptedException.ExecutionStarted, scriptInterruptedException.ScriptException, scriptInterruptedException);
471+
ScheduledException = new ScriptInterruptedException(scriptInterruptedException.EngineName, scriptInterruptedException.Message, scriptInterruptedException.ErrorDetails, scriptInterruptedException.HResult, scriptInterruptedException.IsFatal, scriptInterruptedException.ExecutionStarted, scriptInterruptedException.ScriptExceptionAsObject, scriptInterruptedException);
430472
}
431473
else
432474
{

0 commit comments

Comments
 (0)