mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-21 06:45:32 -06:00
Merge branch 'master' of https://github.com/pauldotknopf/Qml.Net
This commit is contained in:
commit
a9cd59ff24
9 changed files with 149 additions and 27 deletions
|
|
@ -191,3 +191,4 @@ ApplicationWindow {
|
|||
- [ ] .NET Events to signals
|
||||
- [ ] Custom V8 type that looks like an array, but wraps a .NET ```IList<T>``` instance, for modification of list in Qml, and performance.
|
||||
- [ ] General perf improvements (particularly with reflection).
|
||||
- [ ] Qml debugger for VS and VS Code.
|
||||
|
|
|
|||
|
|
@ -31,26 +31,32 @@ void NetObject::method_gccollect(const BuiltinFunction *, Scope &scope, CallData
|
|||
void NetObject::method_await(const BuiltinFunction *, Scope &scope, CallData *callData) {
|
||||
scope.result = QV4::Encode::undefined();
|
||||
|
||||
if (callData->argc != 2) {
|
||||
if (callData->argc != 2 && callData->argc != 3) {
|
||||
qWarning() << "Invalid number of parameters passed to Net.await(task, callback)";
|
||||
return;
|
||||
}
|
||||
|
||||
ScopedValue task(scope, callData->args[0]);
|
||||
ScopedValue callback(scope, callData->args[1]);
|
||||
ScopedValue successCallback(scope, callData->args[1]);
|
||||
ScopedValue failureCallback(scope);
|
||||
|
||||
if(callData->argc == 3) {
|
||||
failureCallback = ScopedValue(scope, callData->args[2]);
|
||||
}
|
||||
|
||||
if(task->isNullOrUndefined()) {
|
||||
qWarning() << "No task for Net.await(task, callback)";
|
||||
return;
|
||||
}
|
||||
|
||||
if(callback->isNullOrUndefined()) {
|
||||
if(successCallback->isNullOrUndefined()) {
|
||||
qWarning() << "No callback for Net.await(task, callback)";
|
||||
return;
|
||||
}
|
||||
|
||||
QJSValue taskJsValue(scope.engine, task->asReturnedValue());
|
||||
QJSValue callbackJsValue(scope.engine, callback->asReturnedValue());
|
||||
QJSValue callbackJsValue(scope.engine, successCallback->asReturnedValue());
|
||||
QJSValue failureJsValue(scope.engine, failureCallback->asReturnedValue());
|
||||
|
||||
QObject* qObject = taskJsValue.toQObject();
|
||||
NetValueInterface* netValue = qobject_cast<NetValueInterface*>(qObject);
|
||||
|
|
@ -61,7 +67,11 @@ void NetObject::method_await(const BuiltinFunction *, Scope &scope, CallData *ca
|
|||
}
|
||||
|
||||
// Send the method to .NET, await the task, and call the callback.
|
||||
awaitTask(netValue->getNetReference(), QSharedPointer<NetJSValue>(new NetJSValue(callbackJsValue)));
|
||||
awaitTask(netValue->getNetReference(),
|
||||
QSharedPointer<NetJSValue>(new NetJSValue(callbackJsValue)),
|
||||
failureJsValue.isNull()
|
||||
? QSharedPointer<NetJSValue>(nullptr)
|
||||
: QSharedPointer<NetJSValue>(new NetJSValue(failureJsValue)));
|
||||
}
|
||||
|
||||
void NetObject::method_cancelTokenSource(const BuiltinFunction *, Scope &scope, CallData *callData) {
|
||||
|
|
@ -98,32 +108,49 @@ ReturnedValue NetObject::method_gccollect(const FunctionObject *b, const Value *
|
|||
ReturnedValue NetObject::method_await(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) {
|
||||
QV4::Scope scope(b);
|
||||
|
||||
if(argc != 2) {
|
||||
if(argc != 2 && argc != 3) {
|
||||
qWarning() << "Invalid number of parameters passed to Net.await(task, callback)";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
QV4::ScopedValue task(scope, argv[0]);
|
||||
QV4::ScopedValue callback(scope, argv[1]);
|
||||
QV4::ScopedValue successCallback(scope, argv[1]);
|
||||
QV4::ScopedValue failureCallback(scope, Encode::null());
|
||||
|
||||
if(argc == 3) {
|
||||
// We are passing in a failure callback
|
||||
failureCallback = argv[2];
|
||||
}
|
||||
|
||||
if(task->isNullOrUndefined()) {
|
||||
qWarning() << "No task for Net.await(task, callback)";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
if(callback->isNullOrUndefined()) {
|
||||
if(successCallback->isNullOrUndefined()) {
|
||||
qWarning() << "No callback for Net.await(task, callback)";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
QJSValue taskJsValue(scope.engine, task->asReturnedValue());
|
||||
QJSValue callbackJsValue(scope.engine, callback->asReturnedValue());
|
||||
QJSValue successCallbackJsValue(scope.engine, successCallback->asReturnedValue());
|
||||
QJSValue failureCallbackJsValue(scope.engine, failureCallback->asReturnedValue());
|
||||
|
||||
if(!taskJsValue.isQObject()) {
|
||||
qWarning() << "Invalid task object passed to Net.await(task, callback)";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
if(!successCallbackJsValue.isCallable()) {
|
||||
qWarning() << "Invalid function passed for success callback";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
if(!failureCallbackJsValue.isNull() && !failureCallbackJsValue.isCallable()) {
|
||||
qWarning() << "Invalid function passed for failure callback";
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
||||
QObject* qObject = taskJsValue.toQObject();
|
||||
NetValueInterface* netValue = qobject_cast<NetValueInterface*>(qObject);
|
||||
|
||||
|
|
@ -133,7 +160,11 @@ ReturnedValue NetObject::method_await(const FunctionObject *b, const Value *this
|
|||
}
|
||||
|
||||
// Send the method to .NET, await the task, and call the callback.
|
||||
awaitTask(netValue->getNetReference(), QSharedPointer<NetJSValue>(new NetJSValue(callbackJsValue)));
|
||||
awaitTask(netValue->getNetReference(),
|
||||
QSharedPointer<NetJSValue>(new NetJSValue(successCallbackJsValue)),
|
||||
failureCallbackJsValue.isNull()
|
||||
? QSharedPointer<NetJSValue>(nullptr)
|
||||
: QSharedPointer<NetJSValue>(new NetJSValue(failureCallbackJsValue)));
|
||||
|
||||
return Encode::undefined();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ typedef void (*writePropertyCb)(NetPropertyInfoContainer* property, NetReference
|
|||
typedef void (*invokeMethodCb)(NetMethodInfoContainer* method, NetReferenceContainer* target, NetVariantListContainer* parameters, NetVariantContainer* result);
|
||||
typedef void (*gcCollectCb)(int maxGeneration);
|
||||
typedef bool (*raiseNetSignalsCb)(NetReferenceContainer* target, LPWCSTR signalName, NetVariantListContainer* parameters);
|
||||
typedef void (*awaitTaskCb)(NetReferenceContainer* target, NetJSValueContainer* callback);
|
||||
typedef void (*awaitTaskCb)(NetReferenceContainer* target, NetJSValueContainer* successCallback, NetJSValueContainer* failureCallback);
|
||||
|
||||
struct Q_DECL_EXPORT NetTypeInfoManagerCallbacks {
|
||||
isTypeValidCb isTypeValid;
|
||||
|
|
@ -126,10 +126,14 @@ bool raiseNetSignals(QSharedPointer<NetReference> target, QString signalName, QS
|
|||
return sharedCallbacks.raiseNetSignals(targetContainer, (LPWCSTR)signalName.utf16(), parametersContainer);
|
||||
}
|
||||
|
||||
void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> callback) {
|
||||
void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> successCallback, QSharedPointer<NetJSValue> failureCallback) {
|
||||
NetReferenceContainer* targetContainer = new NetReferenceContainer{target};
|
||||
NetJSValueContainer* callbackContainer = new NetJSValueContainer{callback};
|
||||
sharedCallbacks.awaitTask(targetContainer, callbackContainer);
|
||||
NetJSValueContainer* sucessCallbackContainer = new NetJSValueContainer{successCallback};
|
||||
NetJSValueContainer* failureCallbackContainer = nullptr;
|
||||
if(failureCallback != nullptr) {
|
||||
failureCallbackContainer = new NetJSValueContainer{failureCallback};
|
||||
}
|
||||
sharedCallbacks.awaitTask(targetContainer, sucessCallbackContainer, failureCallbackContainer);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -33,6 +33,6 @@ void gcCollect(int generation);
|
|||
|
||||
bool raiseNetSignals(QSharedPointer<NetReference> target, QString signalName, QSharedPointer<NetVariantList> parameters);
|
||||
|
||||
void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> callback);
|
||||
void awaitTask(QSharedPointer<NetReference> target, QSharedPointer<NetJSValue> successCallback, QSharedPointer<NetJSValue> failureCallback);
|
||||
|
||||
#endif // NET_TYPE_INFO_MANAGER_H
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System.Threading;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Moq;
|
||||
using Qml.Net.Internal.Qml;
|
||||
|
|
@ -99,6 +100,45 @@ namespace Qml.Net.Tests.Qml
|
|||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Can_listen_to_exception_throw()
|
||||
{
|
||||
var oldContext = SynchronizationContext.Current;
|
||||
SynchronizationContext.SetSynchronizationContext(new NoAsyncSynchronizationContext());
|
||||
|
||||
try
|
||||
{
|
||||
Mock.Setup(x => x.TestAsync()).Returns(Task.FromException<Exception>(new Exception("throw this")));
|
||||
Mock.Setup(x => x.TestMethodWithArg("throw this"));
|
||||
Mock.Setup(x => x.TestMethodWithArg("success"));
|
||||
|
||||
NetTestHelper.RunQml(qmlApplicationEngine,
|
||||
@"
|
||||
import QtQuick 2.0
|
||||
import tests 1.0
|
||||
AwaitTestsQml {
|
||||
id: test
|
||||
Component.onCompleted: function() {
|
||||
var task = test.testAsync('throw this')
|
||||
Net.await(task, function(result) {
|
||||
test.testMethodWithArg('success')
|
||||
},
|
||||
function(ex) {
|
||||
test.testMethodWithArg(ex.message)
|
||||
})
|
||||
}
|
||||
}
|
||||
");
|
||||
|
||||
Mock.Verify(x => x.TestMethodWithArg("throw this"), Times.Once);
|
||||
Mock.Verify(x => x.TestMethodWithArg("success"), Times.Never);
|
||||
}
|
||||
finally
|
||||
{
|
||||
SynchronizationContext.SetSynchronizationContext(oldContext);
|
||||
}
|
||||
}
|
||||
|
||||
private class NoAsyncSynchronizationContext : SynchronizationContext
|
||||
{
|
||||
public override void Post(SendOrPostCallback d, object state)
|
||||
|
|
|
|||
|
|
@ -343,26 +343,45 @@ namespace Qml.Net.Internal
|
|||
}
|
||||
}
|
||||
|
||||
public async Task AwaitTask(IntPtr t, IntPtr c)
|
||||
public async Task AwaitTask(IntPtr t, IntPtr sc, IntPtr fc)
|
||||
{
|
||||
using (var target = new NetReference(t))
|
||||
using(var callback = new NetJsValue(c))
|
||||
using(var successCallback = new NetJsValue(sc))
|
||||
using(var failureCallback = fc != IntPtr.Zero ? new NetJsValue(fc) : null)
|
||||
{
|
||||
var taskObject = target.Instance;
|
||||
if (taskObject is Task task)
|
||||
{
|
||||
await task;
|
||||
|
||||
try
|
||||
{
|
||||
await task;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// The task throw an exception.
|
||||
// Invoke our failure callback.
|
||||
if (failureCallback == null)
|
||||
{
|
||||
// The caller didn't want to listen to failures.
|
||||
// Just throw it and let "TaskScheduler.UnobservedTaskException"
|
||||
// handle it.
|
||||
throw;
|
||||
}
|
||||
|
||||
failureCallback.Call(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var result = (object)((dynamic)task).Result;
|
||||
callback.Call(result);
|
||||
successCallback.Call(result);
|
||||
}
|
||||
catch (RuntimeBinderException)
|
||||
{
|
||||
// No "Result" property, a task with no callbacks.
|
||||
// TODO: Find a better way to handle this than catching an exception.
|
||||
callback.Call();
|
||||
successCallback.Call();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -8,7 +8,14 @@ namespace Qml.Net.Internal
|
|||
{
|
||||
public static bool IsPrimitive(Type type)
|
||||
{
|
||||
if (type.Namespace == "System") return true;
|
||||
if (type.Namespace == "System")
|
||||
{
|
||||
if (type.Name == "Exception")
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ namespace Qml.Net.Internal.Types
|
|||
|
||||
bool RaiseNetSignals(IntPtr target, string signalName, IntPtr parameters);
|
||||
|
||||
Task AwaitTask(IntPtr target, IntPtr callback);
|
||||
Task AwaitTask(IntPtr target, IntPtr succesCallback, IntPtr failureCallback);
|
||||
}
|
||||
|
||||
internal class CallbacksImpl
|
||||
|
|
@ -126,7 +126,7 @@ namespace Qml.Net.Internal.Types
|
|||
delegate bool RaiseNetSignalsDelegate(IntPtr target, [MarshalAs(UnmanagedType.LPWStr)]string signalName, IntPtr parameters);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
delegate void AwaitTaskDelegate(IntPtr target, IntPtr callback);
|
||||
delegate void AwaitTaskDelegate(IntPtr target, IntPtr successCallback, IntPtr failureCallback);
|
||||
|
||||
public CallbacksImpl(ICallbacks callbacks)
|
||||
{
|
||||
|
|
@ -226,9 +226,9 @@ namespace Qml.Net.Internal.Types
|
|||
return _callbacks.RaiseNetSignals(target, signalName, parameters);
|
||||
}
|
||||
|
||||
private void AwaitTask(IntPtr target, IntPtr callback)
|
||||
private void AwaitTask(IntPtr target, IntPtr succesCallback, IntPtr failureCallback)
|
||||
{
|
||||
_callbacks.AwaitTask(target, callback);
|
||||
_callbacks.AwaitTask(target, succesCallback, failureCallback);
|
||||
}
|
||||
|
||||
public Callbacks Callbacks()
|
||||
|
|
|
|||
|
|
@ -20,5 +20,25 @@ namespace Qml.Net
|
|||
}
|
||||
return Activator.CreateInstance(type);
|
||||
}
|
||||
|
||||
public static ITypeCreator FromDelegate(Func<Type, object> func)
|
||||
{
|
||||
return new DelegateTypeCreator(func);
|
||||
}
|
||||
|
||||
class DelegateTypeCreator : ITypeCreator
|
||||
{
|
||||
private readonly Func<Type, object> _func;
|
||||
|
||||
public DelegateTypeCreator(Func<Type, object> func)
|
||||
{
|
||||
_func = func;
|
||||
}
|
||||
|
||||
public object Create(Type type)
|
||||
{
|
||||
return _func(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue