Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ private static void EmitGlobalProperty(
var effectiveType = Kind(declaredType) == "promise"
? declaredType.GetProperty("result")
: declaredType;
effectiveType = AdapterPropertyWireType(generation, effectiveType,
property.Property.TryGetProperty("optional", out var optional) && optional.GetBoolean());
var mapping = MapType(
generation,
effectiveType,
Expand Down Expand Up @@ -1478,7 +1480,7 @@ private static string EmitBinaryWriteCallbackArgument(
payloadType,
optional: false,
"binary callback argument");
var concreteExpression = IsValueType(requiredMapping.CSharpType)
var concreteExpression = IsNonNullableValueType(generation, requiredMapping.CSharpType)
? valueExpression + ".Value"
: valueExpression;
var encoded = EmitBinaryWriteValue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ private static void EmitProperty(
var effectiveType = Kind(declaredType) == "promise"
? declaredType.GetProperty("result")
: declaredType;
effectiveType = AdapterPropertyWireType(generation, effectiveType, property.GetProperty("optional").GetBoolean());
var mapping = MapType(
generation,
effectiveType,
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -441,16 +441,22 @@ private static void EmitObjectModelBinaryCodec(
StringBuilder source,
GenerationContext generation,
string declarationName,
IReadOnlyList<ObjectModelProperty> properties)
IReadOnlyList<ObjectModelProperty> properties,
IReadOnlyList<string>? constructorProperties = null,
bool external = false,
bool isValueType = false)
{
var requiredCount = properties.Count(static property => !property.Optional);
source.AppendLine()
.AppendLine(" internal static uint __WebSceneWriteBinary(")
.AppendLine(" ref global::WebScene.JavaScript.Interop.JavaScriptBinaryWriter writer,")
.Append(" ").Append(declarationName).AppendLine(" value)")
.AppendLine(" {")
.AppendLine(" global::System.ArgumentNullException.ThrowIfNull(value);")
.Append(" var propertyCount = ").Append(requiredCount)
.AppendLine(" {");
if (!isValueType)
{
source.AppendLine(" global::System.ArgumentNullException.ThrowIfNull(value);");
}
source.Append(" var propertyCount = ").Append(requiredCount)
.AppendLine(";");
foreach (var property in properties.Where(static property => property.Optional))
{
Expand All @@ -473,12 +479,20 @@ private static void EmitObjectModelBinaryCodec(
.AppendLine(" {");
}
var indent = property.Optional ? " " : " ";
var expression = "value." + propertyName + (property.Optional ? ".Value!" : string.Empty);
if (property.ExternalNumericType is not null)
{
expression = "__WebSceneWriteInt64(" + expression + ")";
}
if (external && !property.Optional)
{
var local = generation.NextLocal("externalProperty");
source.Append(indent).Append(property.Mapping.CSharpType).Append(' ')
.Append(local).Append(" = ").Append(expression).AppendLine(";");
expression = local;
}
var value = EmitBinaryWriteValue(
source,
generation,
valueType,
"value." + propertyName + (property.Optional ? ".Value!" : string.Empty),
indent);
source, generation, valueType, expression, indent);
source.Append(indent).Append("writer.SetObjectProperty(result, propertyIndex++, ")
.Append(Literal(property.JavaScriptName)).Append("u8, ")
.Append(value).AppendLine(");");
Expand All @@ -499,6 +513,11 @@ private static void EmitObjectModelBinaryCodec(
var values = new List<(ObjectModelProperty Property, string Local)>();
foreach (var property in properties)
{
if (property.ExternalOptional is not null)
{
values.Add((property, EmitExternalOptionalRead(source, generation, property)));
continue;
}
if (!property.Optional)
{
var local = EmitBinaryReadValue(
Expand All @@ -508,6 +527,10 @@ private static void EmitObjectModelBinaryCodec(
"value.GetRequiredProperty(" + Literal(property.JavaScriptName) + "u8)",
"invoker",
" ");
if (property.ExternalNumericType is not null)
{
local = "__WebSceneReadInt64(" + local + ")";
}
values.Add((property, local));
continue;
}
Expand Down Expand Up @@ -540,6 +563,14 @@ private static void EmitObjectModelBinaryCodec(
.AppendLine(" }");
values.Add((property, localOptional));
}
if (constructorProperties is not null)
{
source.Append(" return new ").Append(declarationName).Append('(')
.Append(string.Join(", ", constructorProperties.Select(name =>
values.Single(value => value.Property.CSharpName == name).Local)))
.AppendLine(");").AppendLine(" }");
return;
}
source.AppendLine(" return new()")
.AppendLine(" {");
foreach (var (property, local) in values)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ private static TypeMapping MapType(
: "double",
"typeParameter" => EscapeIdentifier(type.GetProperty("name").GetString()!),
"reference" => ResolveReference(generation, type),
"retainedHandle" => "global::WebScene.JavaScript.Interop.JavaScriptObjectReference",
"union" => ResolveUnion(generation, type, member),
_ => null
};
Expand All @@ -113,6 +114,7 @@ private static TypeMapping MapType(
referenceName!,
out var sourceType)
&& Kind(sourceType) == "typeAlias"
&& !generation.ExternalCodecs.ContainsKey(sourceName)
&& sourceType.TryGetProperty("aliasTarget", out var aliasTarget))
{
referenceAliasMapping = MapType(
Expand Down Expand Up @@ -895,19 +897,19 @@ private static TypeMapping MapOptionalModelProperty(TypeMapping value)
}
if (generation.TypeMappings.TryGetValue(name, out var mapping))
{
return mapping;
return ResolveExternalMapping(generation, qualifiedName ?? name, mapping);
}
if (qualifiedName is not null
&& generation.TypeMappings.TryGetValue(qualifiedName, out mapping))
{
return mapping;
return ResolveExternalMapping(generation, qualifiedName ?? name, mapping);
}
if (type.TryGetProperty("display", out var display)
&& generation.TypeMappings.TryGetValue(
display.GetString()!,
out mapping))
{
return mapping;
return ResolveExternalMapping(generation, qualifiedName ?? name, mapping);
}
var sourceName = qualifiedName ?? name;
if (TryGetType(generation.Types, sourceName, name, out var sourceType))
Expand Down Expand Up @@ -1162,14 +1164,59 @@ private static string ArgumentArray(IEnumerable<Parameter> parameters)
: ")")))
+ " }";

private static string ResolveExternalMapping(
GenerationContext generation,
string sourceName,
string mapping)
{
// Handle mappings already have a native wire representation. Other
// external contracts do not own the generated model codec methods.
if (mapping.TrimEnd('?') is not "global::WebScene.JavaScript.Interop.JavaScriptObjectReference"
and not "global::WebScene.JavaScript.Interop.JavaScriptFunctionReference"
&& !TryPrepareExternalCodec(generation, sourceName, mapping, out var reason)
&& generation.ReportedExternalMappings.Add(sourceName))
{
generation.Context.ReportDiagnostic(Diagnostic.Create(
ExternalMappingWithoutBinaryCodec,
Location.None,
sourceName,
mapping,
reason));
}
return mapping;
}

// Adapter classes implement callbacks in .NET; they cannot be constructed
// from a JavaScript object. Property access returns an owned native handle.
private static JsonElement AdapterPropertyWireType(
GenerationContext generation, JsonElement type, bool optional)
{
var payload = type;
var nullable = optional;
if (Kind(type) == "union")
{
if (!TryGetBinaryUnionPayloadType(type, out payload)) return type;
nullable |= FlattenUnionTypes(type).Any(candidate => Kind(candidate) is "null" or "undefined");
}
if (Kind(payload) != "reference"
|| !MapType(generation, payload, optional: false, "adapter property").IsObjectReferenceProvider)
{
return type;
}
using var projected = JsonDocument.Parse(nullable
? "{\"kind\":\"union\",\"types\":[{\"kind\":\"retainedHandle\"},{\"kind\":\"null\"}]}"
: "{\"kind\":\"retainedHandle\"}");
return projected.RootElement.Clone();
}

private static bool CanEmitBinaryType(
GenerationContext generation,
JsonElement type)
{
var kind = Kind(type);
if (kind is "string" or "number" or "boolean" or "null"
or "undefined" or "object" or "any" or "unknown"
or "callback")
or "callback" or "retainedHandle")
{
return true;
}
Expand Down Expand Up @@ -1223,6 +1270,10 @@ JsonValueKind.String or JsonValueKind.Number
var sourceName = type.TryGetProperty("qualifiedName", out var qualified)
? qualified.GetString()!
: shortName;
if (generation.ExternalCodecs.ContainsKey(sourceName))
{
return true;
}
if (!generation.ModelNames.ContainsKey(sourceName)
&& !generation.ModelNames.ContainsKey(shortName))
{
Expand Down Expand Up @@ -1251,6 +1302,10 @@ private static string EmitBinaryWriteValue(
var result = generation.NextLocal("binaryValue");
switch (kind)
{
case "retainedHandle":
source.Append(indent).Append("var ").Append(result).Append(" = writer.WriteHandle(")
.Append(valueExpression).AppendLine(");");
return result;
case "string":
source.Append(indent).Append("var ").Append(result)
.Append(" = writer.WriteString(")
Expand Down Expand Up @@ -1358,6 +1413,10 @@ private static string EmitBinaryWriteValue(
valueExpression,
indent);
}
var nullableValue = generation.NextLocal("nullableValue");
source.Append(indent).Append("var ").Append(nullableValue).Append(" = ")
.Append(valueExpression).AppendLine(";");
valueExpression = nullableValue;
source.Append(indent).Append("uint ").Append(result)
.AppendLine(";")
.Append(indent).Append("if (").Append(valueExpression)
Expand All @@ -1374,7 +1433,7 @@ private static string EmitBinaryWriteValue(
optional: false,
"binary union");
var concreteExpression = IsNonNullableValueType(
mapping.CSharpType)
generation, mapping.CSharpType)
? valueExpression + ".Value"
: valueExpression;
var child = EmitBinaryWriteValue(
Expand Down Expand Up @@ -1425,9 +1484,7 @@ private static string EmitBinaryWriteValue(
{
source.Append(indent).Append("var ").Append(result)
.Append(" = ").Append(
QualifyGeneratedBinaryType(
generation,
mapping.NonNullableCSharpType))
BinaryReferenceCodecType(generation, type, mapping.NonNullableCSharpType))
.Append(".__WebSceneWriteBinary(ref writer, ")
.Append(valueExpression).AppendLine(");");
}
Expand All @@ -1451,6 +1508,10 @@ private static string EmitBinaryReadValue(
var result = generation.NextLocal("binaryResult");
switch (kind)
{
case "retainedHandle":
source.Append(indent).Append("var ").Append(result).Append(" = ")
.Append(valueExpression).AppendLine(".GetHandle();");
return result;
case "string":
source.Append(indent).Append("var ").Append(result)
.Append(" = ").Append(valueExpression)
Expand Down Expand Up @@ -1630,9 +1691,7 @@ private static string EmitBinaryReadValue(
{
source.Append(indent).Append("var ").Append(result)
.Append(" = ").Append(
QualifyGeneratedBinaryType(
generation,
mapping.NonNullableCSharpType))
BinaryReferenceCodecType(generation, type, mapping.NonNullableCSharpType))
.Append(".__WebSceneReadBinary(")
.Append(valueExpression).Append(", ")
.Append(invokerExpression).AppendLine(");");
Expand Down Expand Up @@ -1697,12 +1756,25 @@ private static bool TryGetBinaryUnionPayloadType(
return true;
}

private static bool IsNonNullableValueType(string type)
=> type is "bool" or "double" or "int" or "long"
private static bool IsNonNullableValueType(GenerationContext generation, string type)
=> generation.ExternalCodecs.Values.Any(codec => codec.IsValueType
&& (codec.ClrType == type || codec.ClrType == "global::" + type))
|| type is "bool" or "double" or "int" or "long"
or "global::System.Numerics.BigInteger"
or "global::WebScene.JavaScript.Interop.JavaScriptObjectReference"
|| type.StartsWith("(", StringComparison.Ordinal)
&& !type.EndsWith("?", StringComparison.Ordinal);

private static string BinaryReferenceCodecType(
GenerationContext generation, JsonElement type, string clrType)
{
var name = type.TryGetProperty("qualifiedName", out var qualified)
? qualified.GetString()! : type.GetProperty("name").GetString()!;
return generation.ExternalCodecs.TryGetValue(name, out var codec)
? "global::" + generation.Namespace + "." + codec.Name
: QualifyGeneratedBinaryType(generation, clrType);
}

private static string QualifyGeneratedBinaryType(
GenerationContext generation,
string type)
Expand Down
Loading
Loading