Skip to content

Commit

Permalink
Supported ArrayBuffer serialization.
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Apr 17, 2024
1 parent 5a57757 commit 8f03833
Show file tree
Hide file tree
Showing 14 changed files with 526 additions and 333 deletions.
23 changes: 17 additions & 6 deletions DupeNukem.Core/Internal/ByteArrayConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,29 @@ public override bool CanConvert(Type objectType) =>
objectType.Equals(type);

public override object? ReadJson(
JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) =>
reader.ReadAsString() is { } body ?
Convert.FromBase64String(body) :
null;
JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
ConverterContext.AssertValidState();

var typedValue = serializer.Deserialize<TypedValue>(reader);
if (typedValue.Type == TypedValueTypes.ByteArray)
{
var base64 = typedValue.Body.ToObject<string>(serializer)!;
return Convert.FromBase64String(base64);
}
return null;
}

public override void WriteJson(
JsonWriter writer, object? value, JsonSerializer serializer)
{
ConverterContext.AssertValidState();

if (value is byte[] arr)
{
var body = Convert.ToBase64String(arr);
writer.WriteValue(body);
var base64 = Convert.ToBase64String(arr);
var typedValue = new TypedValue(TypedValueTypes.ByteArray, base64);
serializer.Serialize(writer, typedValue);
}
else
{
Expand Down
31 changes: 22 additions & 9 deletions DupeNukem.Core/Internal/CancellationTokenConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using System.Threading.Tasks;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace DupeNukem.Internal;

Expand Down Expand Up @@ -45,24 +46,26 @@ public Task CancelAsync()
public override object? ReadJson(
JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
var body = serializer.Deserialize<CancellationTokenBody>(reader);
if (!string.IsNullOrEmpty(body.Scope))
ConverterContext.AssertValidState();

var value = serializer.Deserialize<TypedValue>(reader);
if (value.Type == TypedValueTypes.AbortSignal &&
value.Body.ToObject<CancellationTokenBody>(serializer) is { } body &&
!string.IsNullOrEmpty(body.Scope))
{
// Once the CancellationToken argument is found, the CancellationTokenProxy is generated and
// make this instance visible from the JavaScript side.
// (Actually, it will be extracted and registered later from the DeserializingRegisteredObjectRegistry.)
// By being called from the JavaScript side, as in "{Id}.cancel".
// CancellationTokenSource.Cancel() is called which is held internally.
var ctp = new CancellationTokenProxy();
DeserializingRegisteredObjectRegistry.TryCapture(body.Scope, ctp);
ConverterContext.Current.RegisterObject(body.Scope, ctp);

// Already aborted:
if (body.Aborted)
{
// Cancel now.
ctp.Cancel();
}

return ctp.Token;
}
else
Expand All @@ -74,9 +77,19 @@ public Task CancelAsync()
public override void WriteJson(
JsonWriter writer, object? value, JsonSerializer serializer)
{
var ct = value is CancellationToken c ? c : default;
ConverterContext.AssertValidState();

// TODO:
//writer.WriteValue(tag.Name);
if (value is CancellationToken c)
{
var scope = $"abortSignal_{0}"; // TODO:
var cancellationTokenBody = new CancellationTokenBody(scope, c.IsCancellationRequested);
var body = JToken.FromObject(cancellationTokenBody);
var typedValue = new TypedValue(TypedValueTypes.AbortSignal, body);
serializer.Serialize(writer, typedValue);
}
else
{
writer.WriteNull();
}
}
}
56 changes: 56 additions & 0 deletions DupeNukem.Core/Internal/ClosureConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
////////////////////////////////////////////////////////////////////////////
//
// DupeNukem - WebView attachable full-duplex asynchronous interoperable
// independent messaging library between .NET and JavaScript.
//
// Copyright (c) Kouji Matsui (@kozy_kekyo, @[email protected])
//
// Licensed under Apache-v2: https://opensource.org/licenses/Apache-2.0
//
////////////////////////////////////////////////////////////////////////////

using System;
using System.Threading;
using Newtonsoft.Json;

namespace DupeNukem.Internal;

internal sealed class ClosureConverter : JsonConverter
{
public override bool CanConvert(Type objectType) =>
typeof(Delegate).IsAssignableFrom(objectType);

public override object? ReadJson(
JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
ConverterContext.AssertValidState();

var typedValue = serializer.Deserialize<TypedValue>(reader);
if (typedValue.Type == TypedValueTypes.Closure)
{
var name = typedValue.Body.ToObject<string>(serializer)!;
if (name.StartsWith("__peerClosures__.closure_$"))
{
return ConverterContext.Current.RegisterPeerClosure(name, objectType);
}
}
return null;
}

public override void WriteJson(
JsonWriter writer, object? value, JsonSerializer serializer)
{
ConverterContext.AssertValidState();

if (value is Delegate dlg)
{
var name = ConverterContext.Current.RegisterHostClosure(dlg);
var typedValue = new TypedValue(TypedValueTypes.Closure, name);
serializer.Serialize(writer, typedValue);
}
else
{
writer.WriteNull();
}
}
}
117 changes: 117 additions & 0 deletions DupeNukem.Core/Internal/ConverterContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
////////////////////////////////////////////////////////////////////////////
//
// DupeNukem - WebView attachable full-duplex asynchronous interoperable
// independent messaging library between .NET and JavaScript.
//
// Copyright (c) Kouji Matsui (@kozy_kekyo, @[email protected])
//
// Licensed under Apache-v2: https://opensource.org/licenses/Apache-2.0
//
////////////////////////////////////////////////////////////////////////////

using System;
using System.Diagnostics;
using System.Threading;

namespace DupeNukem.Internal;

internal static class ConverterContext
{
// JsonSerializer cannot pass application-specific context information
// to the Converter during serialization runs.
// The ConverterContext class is used to identify the corresponding Messenger instance
// during serialization.

private sealed class MessengerContext
{
private IMessenger? messenger;
private int count;

public IMessenger Current
{
get
{
Debug.Assert(this.messenger != null);
Debug.Assert(this.count >= 1);
return this.messenger!;
}
}

public void Enter(IMessenger messenger)
{
if (this.count <= 0)
{
Debug.Assert(this.messenger == null);
this.messenger = messenger;
this.count = 1;
}
else
{
Debug.Assert(this.messenger == messenger);
this.count++;
}
}

public void Exit(IMessenger messenger)
{
Debug.Assert(this.messenger == messenger);
Debug.Assert(this.count >= 1);
this.count--;
if (this.count <= 0)
{
this.messenger = null!;
this.count = 0;
}
}
}

private static readonly ThreadLocal<MessengerContext> messengers =
new(() => new MessengerContext());

public static Messenger Current
{
get
{
AssertValidState();
return (Messenger)messengers.Value!.Current;
}
}

[Conditional("DEBUG")]
public static void AssertValidState() =>
Debug.Assert(
messengers.Value!.Current is Messenger,
"Invalid state: Not called correctly.");

public static void Enter(IMessenger messenger) =>
messengers.Value!.Enter(messenger);

public static void Exit(IMessenger messenger) =>
messengers.Value!.Exit(messenger);

public static void Run(IMessenger messenger, Action action)
{
messengers.Value!.Enter(messenger);
try
{
action();
}
finally
{
messengers.Value!.Exit(messenger);
}
}

public static T Run<T>(IMessenger messenger, Func<T> action)
{
messengers.Value!.Enter(messenger);
try
{
return action();
}
finally
{
messengers.Value!.Exit(messenger);
}
}
}
67 changes: 0 additions & 67 deletions DupeNukem.Core/Internal/DeserializingRegisteredObjectRegistry.cs

This file was deleted.

24 changes: 23 additions & 1 deletion DupeNukem.Core/Internal/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ public enum MessageTypes
Succeeded,
Failed,
Invoke,
Closure,
}

[EditorBrowsable(EditorBrowsableState.Advanced)]
Expand Down Expand Up @@ -86,6 +85,29 @@ public ExceptionBody(
}
}

internal enum TypedValueTypes
{
Closure,
AbortSignal,
ByteArray,
}

internal readonly struct TypedValue
{
[JsonProperty("__type__")]
public readonly TypedValueTypes Type;

[JsonProperty("__body__")]
public readonly JToken Body;

[JsonConstructor]
public TypedValue(TypedValueTypes __type__, JToken __body__)
{
this.Type = __type__;
this.Body = __body__;
}
}

internal readonly struct CancellationTokenBody
{
[JsonProperty("__scope__")]
Expand Down
Loading

0 comments on commit 8f03833

Please sign in to comment.