diff --git a/packages/jsii-calc/lib/compliance.ts b/packages/jsii-calc/lib/compliance.ts index 09990ed8af..db640cb841 100644 --- a/packages/jsii-calc/lib/compliance.ts +++ b/packages/jsii-calc/lib/compliance.ts @@ -887,3 +887,11 @@ export class AbstractClassReturner { } } } + +export interface MutableObjectLiteral { + value: string; +} + +export class ClassWithMutableObjectLiteralProperty { + public mutableObject: MutableObjectLiteral = { value: 'default' }; +} diff --git a/packages/jsii-calc/test/assembly.jsii b/packages/jsii-calc/test/assembly.jsii index e312a04915..4297289ffa 100644 --- a/packages/jsii-calc/test/assembly.jsii +++ b/packages/jsii-calc/test/assembly.jsii @@ -1018,6 +1018,23 @@ } ] }, + "jsii-calc.ClassWithMutableObjectLiteralProperty": { + "assembly": "jsii-calc", + "fqn": "jsii-calc.ClassWithMutableObjectLiteralProperty", + "initializer": { + "initializer": true + }, + "kind": "class", + "name": "ClassWithMutableObjectLiteralProperty", + "properties": [ + { + "name": "mutableObject", + "type": { + "fqn": "jsii-calc.MutableObjectLiteral" + } + } + ] + }, "jsii-calc.DefaultedConstructorArgument": { "assembly": "jsii-calc", "fqn": "jsii-calc.DefaultedConstructorArgument", @@ -1883,6 +1900,22 @@ } ] }, + "jsii-calc.MutableObjectLiteral": { + "assembly": "jsii-calc", + "datatype": true, + "fqn": "jsii-calc.MutableObjectLiteral", + "kind": "interface", + "name": "MutableObjectLiteral", + "properties": [ + { + "abstract": true, + "name": "value", + "type": { + "primitive": "string" + } + } + ] + }, "jsii-calc.Negate": { "assembly": "jsii-calc", "base": { @@ -3230,5 +3263,5 @@ } }, "version": "0.7.6", - "fingerprint": "ecDtx3DHVZi7UjyCDhGncg4jbSRaD536bUyh6YzAxlY=" + "fingerprint": "DFShLmIJQmPjTv5Dmz4JoBKqoCtAyifD1RpCuHo+sEc=" } diff --git a/packages/jsii-kernel/lib/kernel.ts b/packages/jsii-kernel/lib/kernel.ts index 867f4d5d00..858f57322e 100644 --- a/packages/jsii-kernel/lib/kernel.ts +++ b/packages/jsii-kernel/lib/kernel.ts @@ -14,6 +14,7 @@ import { TOKEN_DATE, TOKEN_ENUM, TOKEN_REF } from './api'; */ const OBJID_PROP = '$__jsii__objid__$'; const FQN_PROP = '$__jsii__fqn__$'; +const PROXIES_PROP = '$__jsii__proxies__$'; /** * A special FQN that can be used to create empty javascript objects. @@ -923,12 +924,17 @@ export class Kernel { // so the client receives a real object. if (typeof(v) === 'object' && targetType && spec.isNamedTypeReference(targetType)) { this._debug('coalescing to', targetType); - const newObjRef = this._create({ fqn: targetType.fqn }); - const newObj = this._findObject(newObjRef); - for (const k of Object.keys(v)) { - newObj[k] = v[k]; - } - return newObjRef; + const proxies = v[PROXIES_PROP] = v[PROXIES_PROP] || {}; + if (proxies[targetType.fqn]) { return proxies[targetType.fqn]; } + const proxy = new Proxy(v, { + has(target: any, propertyKey: string) { + return propertyKey in target || propertyKey === FQN_PROP; + }, + get(target: any, propertyKey: string) { + return propertyKey === FQN_PROP ? targetType.fqn : target[propertyKey]; + } + }); + return this._createObjref(proxy, targetType.fqn); } // date (https://stackoverflow.com/a/643827/737957) diff --git a/packages/jsii-kernel/test/test.kernel.ts b/packages/jsii-kernel/test/test.kernel.ts index 92a963e86f..54aa5b58d8 100644 --- a/packages/jsii-kernel/test/test.kernel.ts +++ b/packages/jsii-kernel/test/test.kernel.ts @@ -873,6 +873,21 @@ defineTest('node.js standard library', async (test, sandbox) => { { result: "6a2da20943931e9834fc12cfe5bb47bbd9ae43489a30726962b576f4e3993e50" }); }); +// @see awslabs/jsii#248 +defineTest('object literals are returned by reference', async (test, sandbox) => { + const objref = sandbox.create({ fqn: 'jsii-calc.ClassWithMutableObjectLiteralProperty' }); + const property = sandbox.get({ objref, property: 'mutableObject' }).value; + + const newValue = 'Bazinga!1!'; + sandbox.set({ objref: property, property: 'value', value: newValue }); + + test.equal(newValue, + sandbox.get({ + objref: sandbox.get({ objref, property: 'mutableObject' }).value, + property: 'value' + }).value); +}); + const testNames: { [name: string]: boolean } = { }; async function createCalculatorSandbox(name: string) { diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii index e312a04915..4297289ffa 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/.jsii @@ -1018,6 +1018,23 @@ } ] }, + "jsii-calc.ClassWithMutableObjectLiteralProperty": { + "assembly": "jsii-calc", + "fqn": "jsii-calc.ClassWithMutableObjectLiteralProperty", + "initializer": { + "initializer": true + }, + "kind": "class", + "name": "ClassWithMutableObjectLiteralProperty", + "properties": [ + { + "name": "mutableObject", + "type": { + "fqn": "jsii-calc.MutableObjectLiteral" + } + } + ] + }, "jsii-calc.DefaultedConstructorArgument": { "assembly": "jsii-calc", "fqn": "jsii-calc.DefaultedConstructorArgument", @@ -1883,6 +1900,22 @@ } ] }, + "jsii-calc.MutableObjectLiteral": { + "assembly": "jsii-calc", + "datatype": true, + "fqn": "jsii-calc.MutableObjectLiteral", + "kind": "interface", + "name": "MutableObjectLiteral", + "properties": [ + { + "abstract": true, + "name": "value", + "type": { + "primitive": "string" + } + } + ] + }, "jsii-calc.Negate": { "assembly": "jsii-calc", "base": { @@ -3230,5 +3263,5 @@ } }, "version": "0.7.6", - "fingerprint": "ecDtx3DHVZi7UjyCDhGncg4jbSRaD536bUyh6YzAxlY=" + "fingerprint": "DFShLmIJQmPjTv5Dmz4JoBKqoCtAyifD1RpCuHo+sEc=" } diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ClassWithMutableObjectLiteralProperty.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ClassWithMutableObjectLiteralProperty.cs new file mode 100644 index 0000000000..ba4858aa12 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/ClassWithMutableObjectLiteralProperty.cs @@ -0,0 +1,27 @@ +using Amazon.JSII.Runtime.Deputy; + +namespace Amazon.JSII.Tests.CalculatorNamespace +{ + [JsiiClass(typeof(ClassWithMutableObjectLiteralProperty), "jsii-calc.ClassWithMutableObjectLiteralProperty", "[]")] + public class ClassWithMutableObjectLiteralProperty : DeputyBase + { + public ClassWithMutableObjectLiteralProperty(): base(new DeputyProps(new object[]{})) + { + } + + protected ClassWithMutableObjectLiteralProperty(ByRefValue reference): base(reference) + { + } + + protected ClassWithMutableObjectLiteralProperty(DeputyProps props): base(props) + { + } + + [JsiiProperty("mutableObject", "{\"fqn\":\"jsii-calc.MutableObjectLiteral\"}")] + public virtual IMutableObjectLiteral MutableObject + { + get => GetInstanceProperty(); + set => SetInstanceProperty(value); + } + } +} \ No newline at end of file diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IMutableObjectLiteral.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IMutableObjectLiteral.cs new file mode 100644 index 0000000000..50776bf6d2 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/IMutableObjectLiteral.cs @@ -0,0 +1,15 @@ +using Amazon.JSII.Runtime.Deputy; + +namespace Amazon.JSII.Tests.CalculatorNamespace +{ + [JsiiInterface(typeof(IMutableObjectLiteral), "jsii-calc.MutableObjectLiteral")] + public interface IMutableObjectLiteral + { + [JsiiProperty("value", "{\"primitive\":\"string\"}")] + string Value + { + get; + set; + } + } +} \ No newline at end of file diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteral.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteral.cs new file mode 100644 index 0000000000..0313dfa532 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteral.cs @@ -0,0 +1,14 @@ +using Amazon.JSII.Runtime.Deputy; + +namespace Amazon.JSII.Tests.CalculatorNamespace +{ + public class MutableObjectLiteral : DeputyBase, IMutableObjectLiteral + { + [JsiiProperty("value", "{\"primitive\":\"string\"}", true)] + public string Value + { + get; + set; + } + } +} \ No newline at end of file diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteralProxy.cs b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteralProxy.cs new file mode 100644 index 0000000000..7564f1904c --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/dotnet/Amazon.JSII.Tests.CalculatorPackageId/Amazon/JSII/Tests/CalculatorNamespace/MutableObjectLiteralProxy.cs @@ -0,0 +1,19 @@ +using Amazon.JSII.Runtime.Deputy; + +namespace Amazon.JSII.Tests.CalculatorNamespace +{ + [JsiiTypeProxy(typeof(IMutableObjectLiteral), "jsii-calc.MutableObjectLiteral")] + internal sealed class MutableObjectLiteralProxy : DeputyBase, IMutableObjectLiteral + { + private MutableObjectLiteralProxy(ByRefValue reference): base(reference) + { + } + + [JsiiProperty("value", "{\"primitive\":\"string\"}")] + public string Value + { + get => GetInstanceProperty(); + set => SetInstanceProperty(value); + } + } +} \ No newline at end of file diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java index a0bd08be2b..e173852e15 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java +++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/$Module.java @@ -29,6 +29,7 @@ protected Class resolveClass(final String fqn) throws ClassNotFoundException case "jsii-calc.BinaryOperation": return software.amazon.jsii.tests.calculator.BinaryOperation.class; case "jsii-calc.Calculator": return software.amazon.jsii.tests.calculator.Calculator.class; case "jsii-calc.CalculatorProps": return software.amazon.jsii.tests.calculator.CalculatorProps.class; + case "jsii-calc.ClassWithMutableObjectLiteralProperty": return software.amazon.jsii.tests.calculator.ClassWithMutableObjectLiteralProperty.class; case "jsii-calc.DefaultedConstructorArgument": return software.amazon.jsii.tests.calculator.DefaultedConstructorArgument.class; case "jsii-calc.DerivedClassHasNoProperties.Base": return software.amazon.jsii.tests.calculator.DerivedClassHasNoProperties.Base.class; case "jsii-calc.DerivedClassHasNoProperties.Derived": return software.amazon.jsii.tests.calculator.DerivedClassHasNoProperties.Derived.class; @@ -51,6 +52,7 @@ protected Class resolveClass(final String fqn) throws ClassNotFoundException case "jsii-calc.JSObjectLiteralToNativeClass": return software.amazon.jsii.tests.calculator.JSObjectLiteralToNativeClass.class; case "jsii-calc.JavaReservedWords": return software.amazon.jsii.tests.calculator.JavaReservedWords.class; case "jsii-calc.Multiply": return software.amazon.jsii.tests.calculator.Multiply.class; + case "jsii-calc.MutableObjectLiteral": return software.amazon.jsii.tests.calculator.MutableObjectLiteral.class; case "jsii-calc.Negate": return software.amazon.jsii.tests.calculator.Negate.class; case "jsii-calc.NodeStandardLibrary": return software.amazon.jsii.tests.calculator.NodeStandardLibrary.class; case "jsii-calc.NumberGenerator": return software.amazon.jsii.tests.calculator.NumberGenerator.class; diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/ClassWithMutableObjectLiteralProperty.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/ClassWithMutableObjectLiteralProperty.java new file mode 100644 index 0000000000..6e2fbe4411 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/ClassWithMutableObjectLiteralProperty.java @@ -0,0 +1,21 @@ +package software.amazon.jsii.tests.calculator; + +@javax.annotation.Generated(value = "jsii-pacmak") +@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.$Module.class, fqn = "jsii-calc.ClassWithMutableObjectLiteralProperty") +public class ClassWithMutableObjectLiteralProperty extends software.amazon.jsii.JsiiObject { + protected ClassWithMutableObjectLiteralProperty(final software.amazon.jsii.JsiiObject.InitializationMode mode) { + super(mode); + } + public ClassWithMutableObjectLiteralProperty() { + super(software.amazon.jsii.JsiiObject.InitializationMode.Jsii); + software.amazon.jsii.JsiiEngine.getInstance().createNewObject(this); + } + + public software.amazon.jsii.tests.calculator.MutableObjectLiteral getMutableObject() { + return this.jsiiGet("mutableObject", software.amazon.jsii.tests.calculator.MutableObjectLiteral.class); + } + + public void setMutableObject(final software.amazon.jsii.tests.calculator.MutableObjectLiteral value) { + this.jsiiSet("mutableObject", java.util.Objects.requireNonNull(value, "mutableObject is required")); + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/MutableObjectLiteral.java b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/MutableObjectLiteral.java new file mode 100644 index 0000000000..bea2aafd41 --- /dev/null +++ b/packages/jsii-pacmak/test/expected.jsii-calc/java/src/main/java/software/amazon/jsii/tests/calculator/MutableObjectLiteral.java @@ -0,0 +1,72 @@ +package software.amazon.jsii.tests.calculator; + +@javax.annotation.Generated(value = "jsii-pacmak") +public interface MutableObjectLiteral extends software.amazon.jsii.JsiiSerializable { + java.lang.String getValue(); + void setValue(final java.lang.String value); + + /** + * @return a {@link Builder} of {@link MutableObjectLiteral} + */ + static Builder builder() { + return new Builder(); + } + + /** + * A builder for {@link MutableObjectLiteral} + */ + final class Builder { + private java.lang.String _value; + + /** + * Sets the value of Value + * @param value the value to be set + * @return {@code this} + */ + public Builder withValue(final java.lang.String value) { + this._value = java.util.Objects.requireNonNull(value, "value is required"); + return this; + } + + /** + * Builds the configured instance. + * @return a new instance of {@link MutableObjectLiteral} + * @throws NullPointerException if any required attribute was not provided + */ + public MutableObjectLiteral build() { + return new MutableObjectLiteral() { + private java.lang.String $value = java.util.Objects.requireNonNull(_value, "value is required"); + + @Override + public java.lang.String getValue() { + return this.$value; + } + + @Override + public void setValue(final java.lang.String value) { + this.$value = java.util.Objects.requireNonNull(value, "value is required"); + } + + }; + } + } + + /** + * A proxy class which represents a concrete javascript instance of this type. + */ + final static class Jsii$Proxy extends software.amazon.jsii.JsiiObject implements software.amazon.jsii.tests.calculator.MutableObjectLiteral { + protected Jsii$Proxy(final software.amazon.jsii.JsiiObject.InitializationMode mode) { + super(mode); + } + + @Override + public java.lang.String getValue() { + return this.jsiiGet("value", java.lang.String.class); + } + + @Override + public void setValue(final java.lang.String value) { + this.jsiiSet("value", java.util.Objects.requireNonNull(value, "value is required")); + } + } +} diff --git a/packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst b/packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst index 5e7176ac48..f7ad1c5810 100644 --- a/packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst +++ b/packages/jsii-pacmak/test/expected.jsii-calc/sphinx/jsii-calc.rst @@ -973,6 +973,39 @@ CalculatorProps (interface) :type: number or ``undefined`` *(abstract)* +ClassWithMutableObjectLiteralProperty +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. py:class:: ClassWithMutableObjectLiteralProperty() + + **Language-specific names:** + + .. tabs:: + + .. code-tab:: c# + + using Amazon.JSII.Tests.CalculatorNamespace; + + .. code-tab:: java + + import software.amazon.jsii.tests.calculator.ClassWithMutableObjectLiteralProperty; + + .. code-tab:: javascript + + const { ClassWithMutableObjectLiteralProperty } = require('jsii-calc'); + + .. code-tab:: typescript + + import { ClassWithMutableObjectLiteralProperty } from 'jsii-calc'; + + + + + .. py:attribute:: mutableObject + + :type: :py:class:`~jsii-calc.MutableObjectLiteral`\ + + DefaultedConstructorArgument ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -2274,6 +2307,40 @@ Multiply :type: :py:class:`@scope/jsii-calc-lib.Value`\ *(readonly)* +MutableObjectLiteral (interface) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. py:class:: MutableObjectLiteral + + **Language-specific names:** + + .. tabs:: + + .. code-tab:: c# + + using Amazon.JSII.Tests.CalculatorNamespace; + + .. code-tab:: java + + import software.amazon.jsii.tests.calculator.MutableObjectLiteral; + + .. code-tab:: javascript + + // MutableObjectLiteral is an interface + + .. code-tab:: typescript + + import { MutableObjectLiteral } from 'jsii-calc'; + + + + + + .. py:attribute:: value + + :type: string *(abstract)* + + Negate ^^^^^^