Skip to content

Commit c81b4c1

Browse files
authored
fix(kernel): Set this in static contexts (#460)
Javascript allows static members to refer to other static members through `this`, but the `jsii-kernel` passed `null` for the `this` context in `sinvoke`, making it impossible to invoke static methods that made use of this feature through the `jsii` runtimes. Fixes aws/aws-cdk#2304
1 parent a5e8a93 commit c81b4c1

File tree

15 files changed

+277
-6
lines changed

15 files changed

+277
-6
lines changed

packages/jsii-calc/lib/compliance.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1635,4 +1635,31 @@ export interface OptionalStruct {
16351635
* @stable
16361636
*/
16371637
export class ClassWithDocs {
1638-
}
1638+
}
1639+
1640+
/**
1641+
* This is used to validate the ability to use `this` from within a static context.
1642+
*
1643+
* https://github.com/awslabs/aws-cdk/issues/2304
1644+
*/
1645+
export class StaticContext {
1646+
private static _staticVariable = true;
1647+
1648+
public static canAccessStaticContext(): boolean {
1649+
return this.staticContextAvailable();
1650+
}
1651+
1652+
private static staticContextAvailable() {
1653+
return true;
1654+
}
1655+
1656+
public static get staticVariable() {
1657+
return this._staticVariable;
1658+
}
1659+
1660+
public static set staticVariable(value: boolean) {
1661+
this._staticVariable = value;
1662+
}
1663+
1664+
private constructor() { }
1665+
}

packages/jsii-calc/test/assembly.jsii

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5597,6 +5597,48 @@
55975597
],
55985598
"name": "SingleInstanceTwoTypes"
55995599
},
5600+
"jsii-calc.StaticContext": {
5601+
"assembly": "jsii-calc",
5602+
"docs": {
5603+
"remarks": "https://github.com/awslabs/aws-cdk/issues/2304",
5604+
"summary": "This is used to validate the ability to use `this` from within a static context."
5605+
},
5606+
"fqn": "jsii-calc.StaticContext",
5607+
"kind": "class",
5608+
"locationInModule": {
5609+
"filename": "lib/compliance.ts",
5610+
"line": 1645
5611+
},
5612+
"methods": [
5613+
{
5614+
"locationInModule": {
5615+
"filename": "lib/compliance.ts",
5616+
"line": 1648
5617+
},
5618+
"name": "canAccessStaticContext",
5619+
"returns": {
5620+
"type": {
5621+
"primitive": "boolean"
5622+
}
5623+
},
5624+
"static": true
5625+
}
5626+
],
5627+
"name": "StaticContext",
5628+
"properties": [
5629+
{
5630+
"locationInModule": {
5631+
"filename": "lib/compliance.ts",
5632+
"line": 1656
5633+
},
5634+
"name": "staticVariable",
5635+
"static": true,
5636+
"type": {
5637+
"primitive": "boolean"
5638+
}
5639+
}
5640+
]
5641+
},
56005642
"jsii-calc.Statics": {
56015643
"assembly": "jsii-calc",
56025644
"fqn": "jsii-calc.Statics",
@@ -6655,5 +6697,5 @@
66556697
}
66566698
},
66576699
"version": "0.9.0",
6658-
"fingerprint": "rheLw7bhMAmuMfbnzQ4tXZyQTSYIydCp59wiBfA8Gpo="
6700+
"fingerprint": "7Kv5qQOHJ4CtCTKoLfaavS4jHjk/HJvT6aWOIZAHTvw="
66596701
}

packages/jsii-kernel/lib/kernel.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -269,10 +269,10 @@ export class Kernel {
269269
}
270270

271271
const prototype = this._findSymbol(fqn);
272-
const fn = prototype[method];
272+
const fn = prototype[method] as (...params: any[]) => any;
273273

274274
const ret = this._ensureSync(`method '${fqn}.${method}'`, () => {
275-
return this._wrapSandboxCode(() => fn.apply(null, this._toSandboxValues(args, ti.parameters)));
275+
return this._wrapSandboxCode(() => fn.apply(prototype, this._toSandboxValues(args, ti.parameters)));
276276
});
277277

278278
this._debug('method returned:', ret);

packages/jsii-kernel/test/test.kernel.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -996,6 +996,31 @@ defineTest('ObjRefs are labeled with the "most correct" type', async (test, sand
996996
}
997997
});
998998

999+
/**
1000+
* This is used to validate the ability to use `this` from within a static context.
1001+
*
1002+
* https://github.com/awslabs/aws-cdk/issues/2304
1003+
*/
1004+
defineTest('sinvoke allows access to the static context', async (test, sandbox) => {
1005+
test.doesNotThrow(() => {
1006+
const response = sandbox.sinvoke({ fqn: 'jsii-calc.StaticContext', method: 'canAccessStaticContext' });
1007+
test.ok(response.result, 'The result should be true');
1008+
});
1009+
});
1010+
defineTest('sget allows access to the static context', async (test, sandbox) => {
1011+
test.doesNotThrow(() => {
1012+
const response = sandbox.sget({ fqn: 'jsii-calc.StaticContext', property: 'staticVariable' });
1013+
test.ok(response.value, 'The result should be true');
1014+
});
1015+
});
1016+
defineTest('sset allows access to the static context', async (test, sandbox) => {
1017+
test.doesNotThrow(() => {
1018+
sandbox.sset({ fqn: 'jsii-calc.StaticContext', property: 'staticVariable', value: false });
1019+
const response = sandbox.sget({ fqn: 'jsii-calc.StaticContext', property: 'staticVariable' });
1020+
test.ok(!response.value, 'The result should be true');
1021+
});
1022+
});
1023+
9991024
/*
10001025
10011026
Test currently disabled because we don't have the infrastructure to make it pass.

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

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5597,6 +5597,48 @@
55975597
],
55985598
"name": "SingleInstanceTwoTypes"
55995599
},
5600+
"jsii-calc.StaticContext": {
5601+
"assembly": "jsii-calc",
5602+
"docs": {
5603+
"remarks": "https://github.com/awslabs/aws-cdk/issues/2304",
5604+
"summary": "This is used to validate the ability to use `this` from within a static context."
5605+
},
5606+
"fqn": "jsii-calc.StaticContext",
5607+
"kind": "class",
5608+
"locationInModule": {
5609+
"filename": "lib/compliance.ts",
5610+
"line": 1645
5611+
},
5612+
"methods": [
5613+
{
5614+
"locationInModule": {
5615+
"filename": "lib/compliance.ts",
5616+
"line": 1648
5617+
},
5618+
"name": "canAccessStaticContext",
5619+
"returns": {
5620+
"type": {
5621+
"primitive": "boolean"
5622+
}
5623+
},
5624+
"static": true
5625+
}
5626+
],
5627+
"name": "StaticContext",
5628+
"properties": [
5629+
{
5630+
"locationInModule": {
5631+
"filename": "lib/compliance.ts",
5632+
"line": 1656
5633+
},
5634+
"name": "staticVariable",
5635+
"static": true,
5636+
"type": {
5637+
"primitive": "boolean"
5638+
}
5639+
}
5640+
]
5641+
},
56005642
"jsii-calc.Statics": {
56015643
"assembly": "jsii-calc",
56025644
"fqn": "jsii-calc.Statics",
@@ -6655,5 +6697,5 @@
66556697
}
66566698
},
66576699
"version": "0.9.0",
6658-
"fingerprint": "rheLw7bhMAmuMfbnzQ4tXZyQTSYIydCp59wiBfA8Gpo="
6700+
"fingerprint": "7Kv5qQOHJ4CtCTKoLfaavS4jHjk/HJvT6aWOIZAHTvw="
66596701
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Amazon.JSII.Runtime.Deputy;
2+
3+
namespace Amazon.JSII.Tests.CalculatorNamespace
4+
{
5+
/// <summary>This is used to validate the ability to use `this` from within a static context.</summary>
6+
/// <remarks>https://github.com/awslabs/aws-cdk/issues/2304</remarks>
7+
[JsiiClass(nativeType: typeof(StaticContext), fullyQualifiedName: "jsii-calc.StaticContext")]
8+
public class StaticContext : DeputyBase
9+
{
10+
protected StaticContext(ByRefValue reference): base(reference)
11+
{
12+
}
13+
14+
protected StaticContext(DeputyProps props): base(props)
15+
{
16+
}
17+
18+
[JsiiProperty(name: "staticVariable", typeJson: "{\"primitive\":\"boolean\"}")]
19+
public static bool StaticVariable
20+
{
21+
get => GetStaticProperty<bool>(typeof(StaticContext));
22+
set => SetStaticProperty(typeof(StaticContext), value);
23+
}
24+
25+
[JsiiMethod(name: "canAccessStaticContext", returnsJson: "{\"type\":{\"primitive\":\"boolean\"}}")]
26+
public static bool CanAccessStaticContext()
27+
{
28+
return InvokeStaticMethod<bool>(typeof(StaticContext), new object[]{});
29+
}
30+
}
31+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ protected Class<?> resolveClass(final String fqn) throws ClassNotFoundException
112112
case "jsii-calc.ReturnsPrivateImplementationOfInterface": return software.amazon.jsii.tests.calculator.ReturnsPrivateImplementationOfInterface.class;
113113
case "jsii-calc.RuntimeTypeChecking": return software.amazon.jsii.tests.calculator.RuntimeTypeChecking.class;
114114
case "jsii-calc.SingleInstanceTwoTypes": return software.amazon.jsii.tests.calculator.SingleInstanceTwoTypes.class;
115+
case "jsii-calc.StaticContext": return software.amazon.jsii.tests.calculator.StaticContext.class;
115116
case "jsii-calc.Statics": return software.amazon.jsii.tests.calculator.Statics.class;
116117
case "jsii-calc.StringEnum": return software.amazon.jsii.tests.calculator.StringEnum.class;
117118
case "jsii-calc.StripInternal": return software.amazon.jsii.tests.calculator.StripInternal.class;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package software.amazon.jsii.tests.calculator;
2+
3+
/**
4+
* This is used to validate the ability to use `this` from within a static context.
5+
*
6+
* https://github.com/awslabs/aws-cdk/issues/2304
7+
*/
8+
@javax.annotation.Generated(value = "jsii-pacmak")
9+
@software.amazon.jsii.Jsii(module = software.amazon.jsii.tests.calculator.$Module.class, fqn = "jsii-calc.StaticContext")
10+
public class StaticContext extends software.amazon.jsii.JsiiObject {
11+
protected StaticContext(final software.amazon.jsii.JsiiObject.InitializationMode mode) {
12+
super(mode);
13+
}
14+
15+
public static java.lang.Boolean canAccessStaticContext() {
16+
return software.amazon.jsii.JsiiObject.jsiiStaticCall(software.amazon.jsii.tests.calculator.StaticContext.class, "canAccessStaticContext", java.lang.Boolean.class);
17+
}
18+
19+
public static java.lang.Boolean getStaticVariable() {
20+
return software.amazon.jsii.JsiiObject.jsiiStaticGet(software.amazon.jsii.tests.calculator.StaticContext.class, "staticVariable", java.lang.Boolean.class);
21+
}
22+
23+
public static void setStaticVariable(final java.lang.Boolean value) {
24+
software.amazon.jsii.JsiiObject.jsiiStaticSet(software.amazon.jsii.tests.calculator.StaticContext.class, "staticVariable", java.util.Objects.requireNonNull(value, "staticVariable is required"));
25+
}
26+
}

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2128,6 +2128,22 @@ def interface2(self) -> "IPublicInterface":
21282128
return jsii.invoke(self, "interface2", [])
21292129

21302130

2131+
class StaticContext(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.StaticContext"):
2132+
@jsii.member(jsii_name="canAccessStaticContext")
2133+
@classmethod
2134+
def can_access_static_context(cls) -> bool:
2135+
return jsii.sinvoke(cls, "canAccessStaticContext", [])
2136+
2137+
@classproperty
2138+
@jsii.member(jsii_name="staticVariable")
2139+
def static_variable(cls) -> bool:
2140+
return jsii.sget(cls, "staticVariable")
2141+
2142+
@static_variable.setter
2143+
def static_variable(cls, value: bool):
2144+
return jsii.sset(cls, "staticVariable", value)
2145+
2146+
21312147
class Statics(metaclass=jsii.JSIIMeta, jsii_type="jsii-calc.Statics"):
21322148
def __init__(self, value: str) -> None:
21332149
jsii.create(Statics, self, [value])
@@ -2613,6 +2629,6 @@ def parts(self, value: typing.List[scope.jsii_calc_lib.Value]):
26132629
return jsii.set(self, "parts", value)
26142630

26152631

2616-
__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AsyncVirtualMethods", "AugmentableClass", "BinaryOperation", "Calculator", "CalculatorProps", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithDocs", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConstructorPassesThisOut", "Constructors", "ConsumersOfThisCrazyTypeSystem", "DefaultedConstructorArgument", "DerivedClassHasNoProperties", "DerivedStruct", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnotherPublicInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IMutableObjectLiteral", "INonInternalInterface", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnsNumber", "ImplementInternalInterface", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "JsiiAgent", "LoadBalancedFargateServiceProps", "Multiply", "Negate", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "Old", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverrideReturnsObject", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RuntimeTypeChecking", "SingleInstanceTwoTypes", "Statics", "StringEnum", "StripInternal", "Sum", "SyncVirtualMethods", "Thrower", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicMethod", "VirtualMethodPlayground", "__jsii_assembly__", "composition"]
2632+
__all__ = ["AbstractClass", "AbstractClassBase", "AbstractClassReturner", "Add", "AllTypes", "AllTypesEnum", "AllowedMethodNames", "AsyncVirtualMethods", "AugmentableClass", "BinaryOperation", "Calculator", "CalculatorProps", "ClassThatImplementsTheInternalInterface", "ClassThatImplementsThePrivateInterface", "ClassWithDocs", "ClassWithMutableObjectLiteralProperty", "ClassWithPrivateConstructorAndAutomaticProperties", "ConstructorPassesThisOut", "Constructors", "ConsumersOfThisCrazyTypeSystem", "DefaultedConstructorArgument", "DerivedClassHasNoProperties", "DerivedStruct", "DoNotOverridePrivates", "DoNotRecognizeAnyAsOptional", "DocumentedClass", "DontComplainAboutVariadicAfterOptional", "DoubleTrouble", "EraseUndefinedHashValues", "EraseUndefinedHashValuesOptions", "ExportedBaseClass", "ExtendsInternalInterface", "GiveMeStructs", "Greetee", "GreetingAugmenter", "IAnotherPublicInterface", "IExtendsPrivateInterface", "IFriendlier", "IFriendlyRandomGenerator", "IInterfaceImplementedByAbstractClass", "IInterfaceThatShouldNotBeADataType", "IInterfaceWithInternal", "IInterfaceWithMethods", "IInterfaceWithOptionalMethodArguments", "IInterfaceWithProperties", "IInterfaceWithPropertiesExtension", "IJSII417Derived", "IJSII417PublicBaseOfBase", "IMutableObjectLiteral", "INonInternalInterface", "IPrivatelyImplemented", "IPublicInterface", "IPublicInterface2", "IRandomNumberGenerator", "IReturnsNumber", "ImplementInternalInterface", "ImplementsInterfaceWithInternal", "ImplementsInterfaceWithInternalSubclass", "ImplementsPrivateInterface", "ImplictBaseOfBase", "InbetweenClass", "InterfaceInNamespaceIncludesClasses", "InterfaceInNamespaceOnlyInterface", "JSII417Derived", "JSII417PublicBaseOfBase", "JSObjectLiteralForInterface", "JSObjectLiteralToNative", "JSObjectLiteralToNativeClass", "JavaReservedWords", "JsiiAgent", "LoadBalancedFargateServiceProps", "Multiply", "Negate", "NodeStandardLibrary", "NullShouldBeTreatedAsUndefined", "NullShouldBeTreatedAsUndefinedData", "NumberGenerator", "ObjectRefsInCollections", "Old", "OptionalConstructorArgument", "OptionalStruct", "OptionalStructConsumer", "OverrideReturnsObject", "PartiallyInitializedThisConsumer", "Polymorphism", "Power", "PublicClass", "PythonReservedWords", "ReferenceEnumFromScopedPackage", "ReturnsPrivateImplementationOfInterface", "RuntimeTypeChecking", "SingleInstanceTwoTypes", "StaticContext", "Statics", "StringEnum", "StripInternal", "Sum", "SyncVirtualMethods", "Thrower", "UnaryOperation", "UnionProperties", "UseBundledDependency", "UseCalcBase", "UsesInterfaceWithProperties", "VariadicMethod", "VirtualMethodPlayground", "__jsii_assembly__", "composition"]
26172633

26182634
publication.publish()

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

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5344,6 +5344,52 @@ SingleInstanceTwoTypes
53445344
:rtype: :py:class:`~jsii-calc.IPublicInterface`\
53455345

53465346

5347+
StaticContext
5348+
^^^^^^^^^^^^^
5349+
5350+
.. py:class:: StaticContext
5351+
5352+
**Language-specific names:**
5353+
5354+
.. tabs::
5355+
5356+
.. code-tab:: c#
5357+
5358+
using Amazon.JSII.Tests.CalculatorNamespace;
5359+
5360+
.. code-tab:: java
5361+
5362+
import software.amazon.jsii.tests.calculator.StaticContext;
5363+
5364+
.. code-tab:: javascript
5365+
5366+
const { StaticContext } = require('jsii-calc');
5367+
5368+
.. code-tab:: typescript
5369+
5370+
import { StaticContext } from 'jsii-calc';
5371+
5372+
5373+
5374+
This is used to validate the ability to use `this` from within a static context.
5375+
5376+
5377+
5378+
https://github.com/awslabs/aws-cdk/issues/2304
5379+
5380+
5381+
5382+
5383+
.. py:staticmethod:: canAccessStaticContext() -> boolean
5384+
5385+
:rtype: boolean
5386+
5387+
5388+
.. py:attribute:: staticVariable
5389+
5390+
:type: boolean *(static)*
5391+
5392+
53475393
Statics
53485394
^^^^^^^
53495395

0 commit comments

Comments
 (0)