Skip to content

Commit 8aedfc9

Browse files
authored
fix(dotnet): stop mutating Dictionary when iterating on it (#691)
When bringding callback results into dotnet types, we used to modify the Dictionary item within the loop, which is illegal in C#. Any mutation on the collection will cause the iterator to abort. Insead, create a new Dictionary and add the resolved values to that. Fixes #690
1 parent 3defca3 commit 8aedfc9

File tree

11 files changed

+135
-24
lines changed

11 files changed

+135
-24
lines changed

packages/jsii-calc/lib/compliance.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,6 +1737,10 @@ export class DataRenderer {
17371737
return this.renderMap(data);
17381738
}
17391739

1740+
public renderArbitrary(data: { [key: string]: any }): string {
1741+
return this.renderMap(data);
1742+
}
1743+
17401744
public renderMap(map: { [key: string]: any }): string {
17411745
return JSON.stringify(map, null, 2);
17421746
}

packages/jsii-calc/test/assembly.jsii

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,34 @@
21322132
"filename": "lib/compliance.ts",
21332133
"line": 1740
21342134
},
2135+
"name": "renderArbitrary",
2136+
"parameters": [
2137+
{
2138+
"name": "data",
2139+
"type": {
2140+
"collection": {
2141+
"elementtype": {
2142+
"primitive": "any"
2143+
},
2144+
"kind": "map"
2145+
}
2146+
}
2147+
}
2148+
],
2149+
"returns": {
2150+
"type": {
2151+
"primitive": "string"
2152+
}
2153+
}
2154+
},
2155+
{
2156+
"docs": {
2157+
"stability": "experimental"
2158+
},
2159+
"locationInModule": {
2160+
"filename": "lib/compliance.ts",
2161+
"line": 1744
2162+
},
21352163
"name": "renderMap",
21362164
"parameters": [
21372165
{
@@ -7160,7 +7188,7 @@
71607188
"kind": "interface",
71617189
"locationInModule": {
71627190
"filename": "lib/compliance.ts",
7163-
"line": 1762
7191+
"line": 1766
71647192
},
71657193
"name": "SecondLevelStruct",
71667194
"properties": [
@@ -7173,7 +7201,7 @@
71737201
"immutable": true,
71747202
"locationInModule": {
71757203
"filename": "lib/compliance.ts",
7176-
"line": 1766
7204+
"line": 1770
71777205
},
71787206
"name": "deeperRequiredProp",
71797207
"type": {
@@ -7189,7 +7217,7 @@
71897217
"immutable": true,
71907218
"locationInModule": {
71917219
"filename": "lib/compliance.ts",
7192-
"line": 1771
7220+
"line": 1775
71937221
},
71947222
"name": "deeperOptionalProp",
71957223
"optional": true,
@@ -7816,7 +7844,7 @@
78167844
"kind": "class",
78177845
"locationInModule": {
78187846
"filename": "lib/compliance.ts",
7819-
"line": 1779
7847+
"line": 1783
78207848
},
78217849
"methods": [
78227850
{
@@ -7825,7 +7853,7 @@
78257853
},
78267854
"locationInModule": {
78277855
"filename": "lib/compliance.ts",
7828-
"line": 1788
7856+
"line": 1792
78297857
},
78307858
"name": "howManyVarArgsDidIPass",
78317859
"parameters": [
@@ -7857,7 +7885,7 @@
78577885
},
78587886
"locationInModule": {
78597887
"filename": "lib/compliance.ts",
7860-
"line": 1780
7888+
"line": 1784
78617889
},
78627890
"name": "roundTrip",
78637891
"parameters": [
@@ -8242,7 +8270,7 @@
82428270
"kind": "interface",
82438271
"locationInModule": {
82448272
"filename": "lib/compliance.ts",
8245-
"line": 1745
8273+
"line": 1749
82468274
},
82478275
"name": "TopLevelStruct",
82488276
"properties": [
@@ -8255,7 +8283,7 @@
82558283
"immutable": true,
82568284
"locationInModule": {
82578285
"filename": "lib/compliance.ts",
8258-
"line": 1749
8286+
"line": 1753
82598287
},
82608288
"name": "required",
82618289
"type": {
@@ -8271,7 +8299,7 @@
82718299
"immutable": true,
82728300
"locationInModule": {
82738301
"filename": "lib/compliance.ts",
8274-
"line": 1759
8302+
"line": 1763
82758303
},
82768304
"name": "secondLevel",
82778305
"type": {
@@ -8296,7 +8324,7 @@
82968324
"immutable": true,
82978325
"locationInModule": {
82988326
"filename": "lib/compliance.ts",
8299-
"line": 1754
8327+
"line": 1758
83008328
},
83018329
"name": "optional",
83028330
"optional": true,
@@ -9053,5 +9081,5 @@
90539081
}
90549082
},
90559083
"version": "0.14.3",
9056-
"fingerprint": "HflAzjllHZLqkcqeSKW9kn6gvwv+uDmBpezvDUDp9RY="
9084+
"fingerprint": "Ld7vqOD5LvQ5a/I1LdRkj08XkdS+7syV580DELBDa+Y="
90579085
}

packages/jsii-dotnet-runtime-test/test/Amazon.JSII.Runtime.IntegrationTests/ComplianceTests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,12 @@ public void CallbacksCorrectlyDeserializeArguments()
920920
{
921921
var obj = new DataRendererSubclass();
922922
Assert.Equal("{\n \"anumber\": 42,\n \"astring\": \"bazinga!\"\n}", obj.Render(null));
923+
924+
Assert.Equal("{\n \"Key\": {},\n \"Baz\": \"Zinga\"\n}", obj.RenderArbitrary(new Dictionary<string, object>()
925+
{
926+
{ "Key", obj },
927+
{ "Baz", "Zinga" }
928+
}));
923929
}
924930

925931
class DataRendererSubclass : DataRenderer

packages/jsii-dotnet-runtime/src/Amazon.JSII.Runtime/CallbackExtensions.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,15 +144,20 @@ private static object FromKernel(object obj, IReferenceMap referenceMap)
144144
* result in an ArgumentError for not being able to convert JObject to IDictionary.
145145
*/
146146
var dict = ((JObject)obj).ToObject<Dictionary<string, object>>();
147+
var mapped = new Dictionary<string, object>(dict.Count);
147148
foreach (var key in dict.Keys)
148149
{
149150
var value = dict[key];
150151
if (value != null && value.GetType() == typeof(JObject))
151152
{
152-
dict[key] = FromKernel(value, referenceMap);
153+
mapped[key] = FromKernel(value, referenceMap);
154+
}
155+
else
156+
{
157+
mapped[key] = value;
153158
}
154159
}
155-
return dict;
160+
return mapped;
156161
}
157162
return obj;
158163
}

packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,34 @@
21322132
"filename": "lib/compliance.ts",
21332133
"line": 1740
21342134
},
2135+
"name": "renderArbitrary",
2136+
"parameters": [
2137+
{
2138+
"name": "data",
2139+
"type": {
2140+
"collection": {
2141+
"elementtype": {
2142+
"primitive": "any"
2143+
},
2144+
"kind": "map"
2145+
}
2146+
}
2147+
}
2148+
],
2149+
"returns": {
2150+
"type": {
2151+
"primitive": "string"
2152+
}
2153+
}
2154+
},
2155+
{
2156+
"docs": {
2157+
"stability": "experimental"
2158+
},
2159+
"locationInModule": {
2160+
"filename": "lib/compliance.ts",
2161+
"line": 1744
2162+
},
21352163
"name": "renderMap",
21362164
"parameters": [
21372165
{
@@ -7160,7 +7188,7 @@
71607188
"kind": "interface",
71617189
"locationInModule": {
71627190
"filename": "lib/compliance.ts",
7163-
"line": 1762
7191+
"line": 1766
71647192
},
71657193
"name": "SecondLevelStruct",
71667194
"properties": [
@@ -7173,7 +7201,7 @@
71737201
"immutable": true,
71747202
"locationInModule": {
71757203
"filename": "lib/compliance.ts",
7176-
"line": 1766
7204+
"line": 1770
71777205
},
71787206
"name": "deeperRequiredProp",
71797207
"type": {
@@ -7189,7 +7217,7 @@
71897217
"immutable": true,
71907218
"locationInModule": {
71917219
"filename": "lib/compliance.ts",
7192-
"line": 1771
7220+
"line": 1775
71937221
},
71947222
"name": "deeperOptionalProp",
71957223
"optional": true,
@@ -7816,7 +7844,7 @@
78167844
"kind": "class",
78177845
"locationInModule": {
78187846
"filename": "lib/compliance.ts",
7819-
"line": 1779
7847+
"line": 1783
78207848
},
78217849
"methods": [
78227850
{
@@ -7825,7 +7853,7 @@
78257853
},
78267854
"locationInModule": {
78277855
"filename": "lib/compliance.ts",
7828-
"line": 1788
7856+
"line": 1792
78297857
},
78307858
"name": "howManyVarArgsDidIPass",
78317859
"parameters": [
@@ -7857,7 +7885,7 @@
78577885
},
78587886
"locationInModule": {
78597887
"filename": "lib/compliance.ts",
7860-
"line": 1780
7888+
"line": 1784
78617889
},
78627890
"name": "roundTrip",
78637891
"parameters": [
@@ -8242,7 +8270,7 @@
82428270
"kind": "interface",
82438271
"locationInModule": {
82448272
"filename": "lib/compliance.ts",
8245-
"line": 1745
8273+
"line": 1749
82468274
},
82478275
"name": "TopLevelStruct",
82488276
"properties": [
@@ -8255,7 +8283,7 @@
82558283
"immutable": true,
82568284
"locationInModule": {
82578285
"filename": "lib/compliance.ts",
8258-
"line": 1749
8286+
"line": 1753
82598287
},
82608288
"name": "required",
82618289
"type": {
@@ -8271,7 +8299,7 @@
82718299
"immutable": true,
82728300
"locationInModule": {
82738301
"filename": "lib/compliance.ts",
8274-
"line": 1759
8302+
"line": 1763
82758303
},
82768304
"name": "secondLevel",
82778305
"type": {
@@ -8296,7 +8324,7 @@
82968324
"immutable": true,
82978325
"locationInModule": {
82988326
"filename": "lib/compliance.ts",
8299-
"line": 1754
8327+
"line": 1758
83008328
},
83018329
"name": "optional",
83028330
"optional": true,
@@ -9053,5 +9081,5 @@
90539081
}
90549082
},
90559083
"version": "0.14.3",
9056-
"fingerprint": "HflAzjllHZLqkcqeSKW9kn6gvwv+uDmBpezvDUDp9RY="
9084+
"fingerprint": "Ld7vqOD5LvQ5a/I1LdRkj08XkdS+7syV580DELBDa+Y="
90579085
}

packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/DataRenderer.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ public virtual string Render(Amazon.JSII.Tests.CalculatorNamespace.LibNamespace.
3333
return InvokeInstanceMethod<string>(new object[]{data});
3434
}
3535

36+
/// <remarks>
37+
/// stability: Experimental
38+
/// </remarks>
39+
[JsiiMethod(name: "renderArbitrary", returnsJson: "{\"type\":{\"primitive\":\"string\"}}", parametersJson: "[{\"name\":\"data\",\"type\":{\"collection\":{\"elementtype\":{\"primitive\":\"any\"},\"kind\":\"map\"}}}]")]
40+
public virtual string RenderArbitrary(System.Collections.Generic.IDictionary<string, object> data)
41+
{
42+
return InvokeInstanceMethod<string>(new object[]{data});
43+
}
44+
3645
/// <remarks>
3746
/// stability: Experimental
3847
/// </remarks>

packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/DataRenderer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,14 @@ public java.lang.String render() {
3737
return this.jsiiCall("render", java.lang.String.class);
3838
}
3939

40+
/**
41+
* EXPERIMENTAL
42+
*/
43+
@software.amazon.jsii.Stability(software.amazon.jsii.Stability.Level.Experimental)
44+
public java.lang.String renderArbitrary(final java.util.Map<java.lang.String, java.lang.Object> data) {
45+
return this.jsiiCall("renderArbitrary", java.lang.String.class, new Object[] { java.util.Objects.requireNonNull(data, "data is required") });
46+
}
47+
4048
/**
4149
* EXPERIMENTAL
4250
*/

packages/jsii-pacmak/test/expected.jsii-calc/python/src/jsii_calc/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,16 @@ def render(self, *, anumber: jsii.Number, astring: str, first_optional: typing.O
850850

851851
return jsii.invoke(self, "render", [data])
852852

853+
@jsii.member(jsii_name="renderArbitrary")
854+
def render_arbitrary(self, data: typing.Mapping[str,typing.Any]) -> str:
855+
"""
856+
:param data: -
857+
858+
stability
859+
:stability: experimental
860+
"""
861+
return jsii.invoke(self, "renderArbitrary", [data])
862+
853863
@jsii.member(jsii_name="renderMap")
854864
def render_map(self, map: typing.Mapping[str,typing.Any]) -> str:
855865
"""

packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1479,6 +1479,13 @@ DataRenderer
14791479
:rtype: string
14801480

14811481

1482+
.. py:method:: renderArbitrary(data) -> string
1483+
1484+
:param data:
1485+
:type data: string => any
1486+
:rtype: string
1487+
1488+
14821489
.. py:method:: renderMap(map) -> string
14831490
14841491
:param map:

packages/jsii-reflect/test/jsii-tree.test.all.expected.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,11 @@ assemblies
320320
│ │ │ │ └─┬ data
321321
│ │ │ │ └── type: Optional<@scope/jsii-calc-lib.MyFirstStruct>
322322
│ │ │ └── returns: string
323+
│ │ ├─┬ renderArbitrary(data) method (experimental)
324+
│ │ │ ├─┬ parameters
325+
│ │ │ │ └─┬ data
326+
│ │ │ │ └── type: Map<string => any>
327+
│ │ │ └── returns: string
323328
│ │ └─┬ renderMap(map) method (experimental)
324329
│ │ ├─┬ parameters
325330
│ │ │ └─┬ map

0 commit comments

Comments
 (0)