ClearScript 7.0 RC3: Fixed more task-promise conversion issues (GitHub Issue #198).
This commit is contained in:
Родитель
3fbb65b9e7
Коммит
b1268188aa
|
@ -7,5 +7,5 @@
|
|||
|
||||
#define CLEARSCRIPT_VERSION_STRING "7.0.0"
|
||||
#define CLEARSCRIPT_VERSION_COMMA_SEPARATED 7,0,0
|
||||
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.0.0-rc2"
|
||||
#define CLEARSCRIPT_VERSION_STRING_INFORMATIONAL "7.0.0-rc3"
|
||||
#define CLEARSCRIPT_FILE_FLAGS VS_FF_PRERELEASE
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
using System.Threading.Tasks;
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.ClearScript.JavaScript
|
||||
{
|
||||
|
@ -6,7 +9,9 @@ namespace Microsoft.ClearScript.JavaScript
|
|||
{
|
||||
uint BaseLanguageVersion { get; }
|
||||
|
||||
void CompletePromiseWithResult<T>(Task<T> task, object resolve, object reject);
|
||||
void CompletePromise(Task task, object resolve, object reject);
|
||||
object CreatePromiseForTask<T>(Task<T> task);
|
||||
object CreatePromiseForTask(Task task);
|
||||
|
||||
Task<object> CreateTaskForPromise(ScriptObject promise);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@ namespace Microsoft.ClearScript.JavaScript
|
|||
/// </summary>
|
||||
public static class JavaScriptExtensions
|
||||
{
|
||||
private delegate void Executor(object resolve, object reject);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a <see cref="Task{TResult}"/> instance to a
|
||||
/// <see href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise">promise</see>
|
||||
|
@ -47,11 +45,7 @@ namespace Microsoft.ClearScript.JavaScript
|
|||
throw new NotSupportedException("The script engine does not support promises");
|
||||
}
|
||||
|
||||
return engine.Script.EngineInternal.createPromise(new Executor((resolve, reject) =>
|
||||
{
|
||||
Action<Task<TResult>> continuation = thisTask => javaScriptEngine.CompletePromiseWithResult(thisTask, resolve, reject);
|
||||
task.ContinueWith(continuation, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}));
|
||||
return javaScriptEngine.CreatePromiseForTask(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -85,11 +79,7 @@ namespace Microsoft.ClearScript.JavaScript
|
|||
throw new NotSupportedException("The script engine does not support promises");
|
||||
}
|
||||
|
||||
return engine.Script.EngineInternal.createPromise(new Executor((resolve, reject) =>
|
||||
{
|
||||
Action<Task> continuation = thisTask => javaScriptEngine.CompletePromise(thisTask, resolve, reject);
|
||||
task.ContinueWith(continuation, TaskContinuationOptions.ExecuteSynchronously);
|
||||
}));
|
||||
return javaScriptEngine.CreatePromiseForTask(task);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -104,33 +94,18 @@ namespace Microsoft.ClearScript.JavaScript
|
|||
MiscHelpers.VerifyNonNullArgument(promise, "promise");
|
||||
|
||||
var scriptObject = promise as ScriptObject;
|
||||
if ((scriptObject == null) || !scriptObject.Engine.Script.EngineInternal.isPromise(promise))
|
||||
if (scriptObject == null)
|
||||
{
|
||||
throw new ArgumentException("The object is not a promise", nameof(promise));
|
||||
}
|
||||
|
||||
var source = new TaskCompletionSource<object>();
|
||||
|
||||
Action<object> onResolved = result =>
|
||||
var javaScriptEngine = scriptObject.Engine as IJavaScriptEngine;
|
||||
if ((javaScriptEngine == null) || (javaScriptEngine.BaseLanguageVersion < 6))
|
||||
{
|
||||
source.SetResult(result);
|
||||
};
|
||||
throw new NotSupportedException("The script engine does not support promises");
|
||||
}
|
||||
|
||||
Action<object> onRejected = error =>
|
||||
{
|
||||
try
|
||||
{
|
||||
scriptObject.Engine.Script.EngineInternal.throwValue(error);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
source.SetException(exception);
|
||||
}
|
||||
};
|
||||
|
||||
scriptObject.InvokeMethod("then", onResolved, onRejected);
|
||||
|
||||
return source.Task;
|
||||
return javaScriptEngine.CreateTaskForPromise(scriptObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,13 +17,13 @@ using System.Runtime.InteropServices;
|
|||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("7.0.0")]
|
||||
[assembly: AssemblyFileVersion("7.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc2")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc3")]
|
||||
|
||||
namespace Microsoft.ClearScript.Properties
|
||||
{
|
||||
internal static class ClearScriptVersion
|
||||
{
|
||||
public const string Triad = "7.0.0";
|
||||
public const string Informational = "7.0.0-rc2";
|
||||
public const string Informational = "7.0.0-rc3";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ namespace Microsoft.ClearScript.V8
|
|||
#region data
|
||||
|
||||
private static readonly DocumentInfo initScriptInfo = new DocumentInfo(MiscHelpers.FormatInvariant("{0} [internal]", nameof(V8ScriptEngine)));
|
||||
[ThreadStatic] private static bool bypassTaskPromiseConversion;
|
||||
|
||||
private readonly V8ScriptEngineFlags engineFlags;
|
||||
private readonly V8ContextProxy proxy;
|
||||
|
@ -288,23 +287,25 @@ namespace Microsoft.ClearScript.V8
|
|||
return value instanceof savedPromise;
|
||||
},
|
||||
|
||||
completePromiseWithResult: function (task, resolve, reject) {
|
||||
completePromiseWithResult: function (getResult, resolve, reject) {
|
||||
try {
|
||||
resolve(task.Result);
|
||||
resolve(getResult());
|
||||
}
|
||||
catch (exception) {
|
||||
reject(exception);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
completePromise: function (task, resolve, reject) {
|
||||
completePromise: function (wait, resolve, reject) {
|
||||
try {
|
||||
task.Wait();
|
||||
wait();
|
||||
resolve();
|
||||
}
|
||||
catch (exception) {
|
||||
reject(exception);
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
throwValue: function (value) {
|
||||
|
@ -1101,6 +1102,26 @@ namespace Microsoft.ClearScript.V8
|
|||
}
|
||||
}
|
||||
|
||||
private object CreatePromise(Action<object, object> executor)
|
||||
{
|
||||
VerifyNotDisposed();
|
||||
var v8Script = (V8ScriptItem)script;
|
||||
var v8Internal = (V8ScriptItem)v8Script.GetProperty("EngineInternal");
|
||||
return V8ScriptItem.Wrap(this, v8Internal.InvokeMethod(false, "createPromise", executor));
|
||||
}
|
||||
|
||||
private void CompletePromise<T>(Task<T> task, object resolve, object reject)
|
||||
{
|
||||
Func<T> getResult = () => task.Result;
|
||||
Script.EngineInternal.completePromiseWithResult(getResult, resolve, reject);
|
||||
}
|
||||
|
||||
private void CompletePromise(Task task, object resolve, object reject)
|
||||
{
|
||||
Action wait = task.Wait;
|
||||
Script.EngineInternal.completePromise(wait, resolve, reject);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ScriptEngine overrides (public members)
|
||||
|
@ -1289,7 +1310,7 @@ namespace Microsoft.ClearScript.V8
|
|||
return obj;
|
||||
}
|
||||
|
||||
if (engineFlags.HasFlag(V8ScriptEngineFlags.EnableTaskPromiseConversion) && !bypassTaskPromiseConversion)
|
||||
if (engineFlags.HasFlag(V8ScriptEngineFlags.EnableTaskPromiseConversion))
|
||||
{
|
||||
// .NET Core async functions return Task subclass instances that trigger result wrapping
|
||||
|
||||
|
@ -1303,17 +1324,11 @@ namespace Microsoft.ClearScript.V8
|
|||
{
|
||||
if (testObject.GetType().IsAssignableToGenericType(typeof(Task<>), out var typeArgs))
|
||||
{
|
||||
using (Scope.Create(() => MiscHelpers.Exchange(ref bypassTaskPromiseConversion, true), oldValue => bypassTaskPromiseConversion = oldValue))
|
||||
{
|
||||
obj = typeof(TaskConverter<>).MakeSpecificType(typeArgs).InvokeMember("ToPromise", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new[] {testObject, this});
|
||||
}
|
||||
obj = typeof(TaskConverter<>).MakeSpecificType(typeArgs).InvokeMember("ToPromise", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static, null, null, new[] {testObject, this});
|
||||
}
|
||||
else if (testObject is Task task)
|
||||
{
|
||||
using (Scope.Create(() => MiscHelpers.Exchange(ref bypassTaskPromiseConversion, true), oldValue => bypassTaskPromiseConversion = oldValue))
|
||||
{
|
||||
obj = task.ToPromise(this);
|
||||
}
|
||||
obj = task.ToPromise(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1378,12 +1393,9 @@ namespace Microsoft.ClearScript.V8
|
|||
}
|
||||
|
||||
var scriptItem = V8ScriptItem.Wrap(this, obj);
|
||||
if (engineFlags.HasFlag(V8ScriptEngineFlags.EnableTaskPromiseConversion) && !bypassTaskPromiseConversion && (obj is IV8Object v8Object) && v8Object.IsPromise())
|
||||
if (engineFlags.HasFlag(V8ScriptEngineFlags.EnableTaskPromiseConversion) && (obj is IV8Object v8Object) && v8Object.IsPromise())
|
||||
{
|
||||
using (Scope.Create(() => MiscHelpers.Exchange(ref bypassTaskPromiseConversion, true), oldValue => bypassTaskPromiseConversion = oldValue))
|
||||
{
|
||||
return scriptItem.ToTask();
|
||||
}
|
||||
return scriptItem.ToTask();
|
||||
}
|
||||
|
||||
return scriptItem;
|
||||
|
@ -1505,20 +1517,51 @@ namespace Microsoft.ClearScript.V8
|
|||
|
||||
uint IJavaScriptEngine.BaseLanguageVersion => 8;
|
||||
|
||||
void IJavaScriptEngine.CompletePromiseWithResult<T>(Task<T> task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask<T>(Task<T> task)
|
||||
{
|
||||
using (Scope.Create(() => MiscHelpers.Exchange(ref bypassTaskPromiseConversion, true), oldValue => bypassTaskPromiseConversion = oldValue))
|
||||
return CreatePromise((resolve, reject) =>
|
||||
{
|
||||
Script.EngineInternal.completePromiseWithResult(task, resolve, reject);
|
||||
}
|
||||
task.ContinueWith(_ => CompletePromise(task, resolve, reject), TaskContinuationOptions.ExecuteSynchronously);
|
||||
});
|
||||
}
|
||||
|
||||
void IJavaScriptEngine.CompletePromise(Task task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask(Task task)
|
||||
{
|
||||
using (Scope.Create(() => MiscHelpers.Exchange(ref bypassTaskPromiseConversion, true), oldValue => bypassTaskPromiseConversion = oldValue))
|
||||
return CreatePromise((resolve, reject) =>
|
||||
{
|
||||
Script.EngineInternal.completePromise(task, resolve, reject);
|
||||
task.ContinueWith(_ => CompletePromise(task, resolve, reject), TaskContinuationOptions.ExecuteSynchronously);
|
||||
});
|
||||
}
|
||||
|
||||
Task<object> IJavaScriptEngine.CreateTaskForPromise(ScriptObject promise)
|
||||
{
|
||||
if (!(promise is V8ScriptItem v8Promise) || !v8Promise.IsPromise())
|
||||
{
|
||||
throw new ArgumentException("The object is not a V8 promise", nameof(promise));
|
||||
}
|
||||
|
||||
var source = new TaskCompletionSource<object>();
|
||||
|
||||
Action<object> onResolved = result =>
|
||||
{
|
||||
source.SetResult(result);
|
||||
};
|
||||
|
||||
Action<object> onRejected = error =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Script.EngineInternal.throwValue(error);
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
source.SetException(exception);
|
||||
}
|
||||
};
|
||||
|
||||
v8Promise.InvokeMethod(false, "then", onResolved, onRejected);
|
||||
|
||||
return source.Task;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -76,6 +76,24 @@ namespace Microsoft.ClearScript.V8
|
|||
return obj;
|
||||
}
|
||||
|
||||
public bool IsPromise()
|
||||
{
|
||||
return target.IsPromise();
|
||||
}
|
||||
|
||||
public object InvokeMethod(bool marshalResult, string name, params object[] args)
|
||||
{
|
||||
VerifyNotDisposed();
|
||||
|
||||
var result = engine.ScriptInvoke(() => target.InvokeMethod(name, engine.MarshalToScript(args)));
|
||||
if (marshalResult)
|
||||
{
|
||||
return engine.MarshalToHost(result, false);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private void VerifyNotDisposed()
|
||||
{
|
||||
if (disposedFlag.IsSet)
|
||||
|
@ -255,8 +273,7 @@ namespace Microsoft.ClearScript.V8
|
|||
|
||||
public override object InvokeMethod(string name, params object[] args)
|
||||
{
|
||||
VerifyNotDisposed();
|
||||
return engine.MarshalToHost(engine.ScriptInvoke(() => target.InvokeMethod(name, engine.MarshalToScript(args))), false);
|
||||
return InvokeMethod(true, name, args);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -90,12 +90,17 @@ namespace Microsoft.ClearScript.Windows
|
|||
|
||||
uint IJavaScriptEngine.BaseLanguageVersion => 3;
|
||||
|
||||
void IJavaScriptEngine.CompletePromiseWithResult<T>(Task<T> task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask<T>(Task<T> task)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void IJavaScriptEngine.CompletePromise(Task task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask(Task task)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<object> IJavaScriptEngine.CreateTaskForPromise(ScriptObject promise)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -299,12 +299,17 @@ namespace Microsoft.ClearScript.Windows
|
|||
|
||||
uint IJavaScriptEngine.BaseLanguageVersion => 3;
|
||||
|
||||
void IJavaScriptEngine.CompletePromiseWithResult<T>(Task<T> task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask<T>(Task<T> task)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
void IJavaScriptEngine.CompletePromise(Task task, object resolve, object reject)
|
||||
object IJavaScriptEngine.CreatePromiseForTask(Task task)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
Task<object> IJavaScriptEngine.CreateTaskForPromise(ScriptObject promise)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -13,4 +13,4 @@ using System.Runtime.InteropServices;
|
|||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("7.0.0")]
|
||||
[assembly: AssemblyFileVersion("7.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc2")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc3")]
|
||||
|
|
|
@ -13,4 +13,4 @@ using System.Runtime.InteropServices;
|
|||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("7.0.0")]
|
||||
[assembly: AssemblyFileVersion("7.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc2")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc3")]
|
||||
|
|
|
@ -1397,6 +1397,10 @@ namespace Microsoft.ClearScript.Test
|
|||
|
||||
engine.Execute("value.then(value => result = value);");
|
||||
Assert.AreEqual("foo", engine.Script.result);
|
||||
|
||||
engine.Execute("TaskPromiseConversionTest.GetStringAsync().then(async function () { result = TaskPromiseConversionTest.GetStringQuicklyAsync() instanceof Promise; });");
|
||||
Task.Delay(200).Wait();
|
||||
Assert.AreEqual(true, engine.Script.result);
|
||||
}
|
||||
|
||||
// ReSharper restore InconsistentNaming
|
||||
|
@ -1708,9 +1712,19 @@ namespace Microsoft.ClearScript.Test
|
|||
{
|
||||
public static async Task<string> GetStringAsync()
|
||||
{
|
||||
await Task.Delay(100);
|
||||
await Task.Delay(10);
|
||||
return "foo";
|
||||
}
|
||||
|
||||
#pragma warning disable 1998
|
||||
|
||||
public static async Task<string> GetStringQuicklyAsync()
|
||||
{
|
||||
return "foo";
|
||||
}
|
||||
|
||||
#pragma warning restore 1998
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
|
|
@ -13,4 +13,4 @@ using System.Runtime.InteropServices;
|
|||
[assembly: ComVisible(false)]
|
||||
[assembly: AssemblyVersion("7.0.0")]
|
||||
[assembly: AssemblyFileVersion("7.0.0")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc2")]
|
||||
[assembly: AssemblyInformationalVersion("7.0.0-rc3")]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<#
|
||||
var version = new Version(7, 0, 0);
|
||||
var versionSuffix = "-rc2";
|
||||
var versionSuffix = "-rc3";
|
||||
new Random(versionSuffix.Length); // suppress "versionSuffix not used" warning
|
||||
#>
|
||||
|
|
Загрузка…
Ссылка в новой задаче