Skip to content

Commit 0b03f8e

Browse files
authored
Add support for using and await using (#2139)
1 parent 533a270 commit 0b03f8e

32 files changed

+256
-97
lines changed

Jint.Tests.Test262/Test262Harness.settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
"async-iteration",
1010
"Atomics",
1111
"decorators",
12-
"explicit-resource-management",
1312
"import-defer",
1413
"iterator-helpers",
1514
"iterator-sequencing",
@@ -32,6 +31,9 @@
3231
],
3332
"ExcludedFiles": [
3433

34+
// needs async-iteration
35+
"built-ins/AsyncIteratorPrototype/Symbol.asyncDispose/*.js",
36+
3537
// requires rewrite of destructing towards spec
3638
"language/destructuring/binding/keyed-destructuring-property-reference-target-evaluation-order-with-bindings.js",
3739
"language/expressions/assignment/destructuring/keyed-destructuring-property-reference-target-evaluation-order-with-bindings.js",
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
namespace Jint.Tests.Runtime;
2+
3+
public class InteropDisposeTests
4+
{
5+
private readonly Engine _engine;
6+
private readonly SyncDisposable _syncDisposable = new();
7+
8+
#if NETCOREAPP
9+
private readonly AsyncDisposable _asyncDisposable = new();
10+
#endif
11+
12+
public InteropDisposeTests()
13+
{
14+
_engine = new Engine();
15+
_engine.SetValue("getSync", () => _syncDisposable);
16+
17+
#if NETCOREAPP
18+
_engine.SetValue("getAsync", () => _asyncDisposable);
19+
#endif
20+
}
21+
22+
[Theory]
23+
[InlineData("{ using temp = getSync(); }")]
24+
[InlineData("(function x() { using temp = getSync(); })()")]
25+
[InlineData("class X { constructor() { using temp = getSync(); } } new X();")]
26+
[InlineData("class X { static { using temp = getSync(); } } new X();")]
27+
[InlineData("for (let i = 0; i < 1; i++) { using temp = getSync(); }")]
28+
public void ShouldSyncDispose(string program)
29+
{
30+
_engine.Execute(program);
31+
_syncDisposable.Disposed.Should().BeTrue();
32+
}
33+
34+
private class SyncDisposable : IDisposable
35+
{
36+
public bool Disposed { get; private set; }
37+
38+
public void Dispose()
39+
{
40+
Disposed = true;
41+
}
42+
}
43+
44+
#if NETCOREAPP
45+
[Theory]
46+
[InlineData("(async function x() { await using temp = getAsync(); })();")]
47+
public void ShouldAsyncDispose(string program)
48+
{
49+
_engine.Evaluate(program).UnwrapIfPromise();
50+
_asyncDisposable.Disposed.Should().BeTrue();
51+
}
52+
53+
private class AsyncDisposable : IAsyncDisposable
54+
{
55+
public bool Disposed { get; private set; }
56+
57+
public ValueTask DisposeAsync()
58+
{
59+
Disposed = true;
60+
return default;
61+
}
62+
}
63+
#endif
64+
}

Jint/AstExtensions.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ internal static void BindingInitialization(
299299
if (expression is Identifier identifier)
300300
{
301301
var catchEnvRecord = (DeclarativeEnvironment) env;
302-
catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value);
302+
catchEnvRecord.CreateMutableBindingAndInitialize(identifier.Name, canBeDeleted: false, value, DisposeHint.Normal);
303303
}
304304
else if (expression is DestructuringPattern pattern)
305305
{
@@ -498,6 +498,16 @@ internal static void AllPrivateIdentifiersValid(this Script script, Realm realm,
498498
validator.Visit(script);
499499
}
500500

501+
internal static DisposeHint GetDisposeHint(this VariableDeclarationKind statement)
502+
{
503+
return statement switch
504+
{
505+
VariableDeclarationKind.AwaitUsing => DisposeHint.Async,
506+
VariableDeclarationKind.Using => DisposeHint.Sync,
507+
_ => DisposeHint.Normal,
508+
};
509+
}
510+
501511
private sealed class MinimalSyntaxElement : Node
502512
{
503513
public MinimalSyntaxElement(in SourceLocation location) : base(NodeType.Unknown)
@@ -545,4 +555,4 @@ private static void Throw(Realm r, PrivateIdentifier id)
545555
ExceptionHelper.ThrowSyntaxError(r, $"Private field '#{id.Name}' must be declared in an enclosing class");
546556
}
547557
}
548-
}
558+
}

Jint/Engine.Defaults.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ public partial class Engine
77
internal static readonly ParserOptions BaseParserOptions = ParserOptions.Default with
88
{
99
EcmaVersion = EcmaVersion.ES2023,
10-
ExperimentalESFeatures = ExperimentalESFeatures.ImportAttributes | ExperimentalESFeatures.RegExpDuplicateNamedCapturingGroups,
10+
ExperimentalESFeatures = ExperimentalESFeatures.ImportAttributes
11+
| ExperimentalESFeatures.RegExpDuplicateNamedCapturingGroups
12+
| ExperimentalESFeatures.ExplicitResourceManagement,
1113
Tolerant = false,
1214
};
1315
}

Jint/Engine.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1080,11 +1080,11 @@ private void GlobalDeclarationInstantiation(
10801080

10811081
if (strict)
10821082
{
1083-
env.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao);
1083+
env.CreateImmutableBindingAndInitialize(KnownKeys.Arguments, strict: false, ao, DisposeHint.Normal);
10841084
}
10851085
else
10861086
{
1087-
env.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao);
1087+
env.CreateMutableBindingAndInitialize(KnownKeys.Arguments, canBeDeleted: false, ao, DisposeHint.Normal);
10881088
}
10891089
}
10901090

@@ -1108,7 +1108,7 @@ private void GlobalDeclarationInstantiation(
11081108
for (var i = 0; i < varsToInitialize.Count; i++)
11091109
{
11101110
var pair = varsToInitialize[i];
1111-
env.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined);
1111+
env.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, JsValue.Undefined, DisposeHint.Normal);
11121112
}
11131113

11141114
varEnv = env;
@@ -1126,7 +1126,7 @@ private void GlobalDeclarationInstantiation(
11261126
{
11271127
var pair = varsToInitialize[i];
11281128
var initialValue = pair.InitialValue ?? env.GetBindingValue(pair.Name, strict: false);
1129-
varEnv.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue);
1129+
varEnv.CreateMutableBindingAndInitialize(pair.Name, canBeDeleted: false, initialValue, DisposeHint.Normal);
11301130
}
11311131
}
11321132

@@ -1367,7 +1367,7 @@ internal void EvalDeclarationInstantiation(
13671367
if (!bindingExists)
13681368
{
13691369
varEnvRec.CreateMutableBinding(fn, canBeDeleted: true);
1370-
varEnvRec.InitializeBinding(fn, fo);
1370+
varEnvRec.InitializeBinding(fn, fo, DisposeHint.Normal);
13711371
}
13721372
else
13731373
{
@@ -1388,7 +1388,7 @@ internal void EvalDeclarationInstantiation(
13881388
if (!bindingExists)
13891389
{
13901390
varEnvRec.CreateMutableBinding(vn, canBeDeleted: true);
1391-
varEnvRec.InitializeBinding(vn, JsValue.Undefined);
1391+
varEnvRec.InitializeBinding(vn, JsValue.Undefined, DisposeHint.Normal);
13921392
}
13931393
}
13941394
}

Jint/Jint.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</PropertyGroup>
2828

2929
<PropertyGroup Condition=" '$(TargetFramework)' != 'net462' and '$(TargetFramework)' != 'netstandard2.0' ">
30-
<DefineConstants>$(DefineConstants);SUPPORTS_SPAN_PARSE;SUPPORTS_WEAK_TABLE_ADD_OR_UPDATE;SUPPORTS_WEAK_TABLE_CLEAR</DefineConstants>
30+
<DefineConstants>$(DefineConstants);SUPPORTS_SPAN_PARSE;SUPPORTS_WEAK_TABLE_ADD_OR_UPDATE;SUPPORTS_WEAK_TABLE_CLEAR;SUPPORTS_ASYNC_DISPOSE</DefineConstants>
3131
</PropertyGroup>
3232

3333
<PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net6.0'))">

Jint/Native/Disposable/DisposeCapability.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
namespace Jint.Native.Disposable;
44

5-
internal enum DisposeHint
6-
{
7-
Normal,
8-
Sync,
9-
Async,
10-
}
11-
125
internal sealed class DisposeCapability
136
{
147
private readonly Engine _engine;

Jint/Native/Function/ClassDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ public JsValue BuildConstructor(EvaluationContext context, Environment env)
203203

204204
if (_className is not null)
205205
{
206-
classEnv.InitializeBinding(_className, F);
206+
classEnv.InitializeBinding(_className, F, DisposeHint.Normal);
207207
}
208208

209209
F._privateMethods = instancePrivateMethods;

Jint/Native/Function/ScriptFunction.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ internal ScriptFunction(
5959
protected internal override JsValue Call(JsValue thisObject, JsCallArguments arguments)
6060
{
6161
var strict = _functionDefinition!.Strict || _thisMode == FunctionThisMode.Strict;
62-
using (new StrictModeScope(strict, true))
62+
using (new StrictModeScope(strict, force: true))
6363
{
6464
try
6565
{
@@ -76,6 +76,7 @@ protected internal override JsValue Call(JsValue thisObject, JsCallArguments arg
7676
var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
7777

7878
var result = _functionDefinition.EvaluateBody(context, this, arguments);
79+
result = calleeContext.LexicalEnvironment.DisposeResources(result);
7980

8081
if (result.Type == CompletionType.Throw)
8182
{
@@ -159,6 +160,7 @@ ObjectInstance IConstructor.Construct(JsCallArguments arguments, JsValue newTarg
159160
var context = _engine._activeEvaluationContext ?? new EvaluationContext(_engine);
160161

161162
var result = _functionDefinition!.EvaluateBody(context, this, arguments);
163+
result = constructorEnv.DisposeResources(result);
162164

163165
// The DebugHandler needs the current execution context before the return for stepping through the return point
164166
// We exclude the empty constructor generated for classes without an explicit constructor.

Jint/Native/Object/ObjectInstance.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using Jint.Native.Array;
66
using Jint.Native.BigInt;
77
using Jint.Native.Boolean;
8-
using Jint.Native.Disposable;
98
using Jint.Native.Json;
109
using Jint.Native.Number;
1110
using Jint.Native.Promise;

0 commit comments

Comments
 (0)