Skip to content

Commit 0313647

Browse files
committed
.
1 parent 9a56bf1 commit 0313647

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

src/Asynkron.JsEngine/Ast/AssignmentReference.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ TypedAstEvaluator.PropertyHandle GetHandle()
159159

160160
if (target is JsArray jsArray && JsOps.TryResolveArrayIndex(propertyValue, out var arrayIndex, context))
161161
{
162+
var propertyName = JsOps.GetRequiredPropertyName(propertyValue, context);
163+
if (ShouldUsePropertyHandle(jsArray, propertyName))
164+
{
165+
var propertyHandle = TypedAstEvaluator.PropertyHandle.Resolve(
166+
jsArray,
167+
propertyName,
168+
context,
169+
context.CurrentScope.IsStrict,
170+
allowPrivate: !member.IsComputed);
171+
return new AssignmentReference(
172+
() => propertyHandle.GetValue(),
173+
newValue => propertyHandle.SetValue(newValue));
174+
}
175+
162176
return new AssignmentReference(
163177
() => jsArray.GetElement(arrayIndex),
164178
newValue => jsArray.SetElement(arrayIndex, newValue));
@@ -191,6 +205,28 @@ TypedAstEvaluator.PropertyHandle GetHandle()
191205
newValue => handle.SetValue(newValue));
192206
}
193207

208+
private static bool ShouldUsePropertyHandle(IJsPropertyAccessor target, string propertyName)
209+
{
210+
IJsPropertyAccessor? current = target;
211+
while (current is not null)
212+
{
213+
var descriptor = current.GetOwnPropertyDescriptor(propertyName);
214+
if (descriptor is not null)
215+
{
216+
return descriptor.IsAccessorDescriptor || descriptor is { Writable: false };
217+
}
218+
219+
current = current switch
220+
{
221+
IJsObjectLike objectLike => objectLike.Prototype,
222+
IPrototypeAccessorProvider provider => provider.PrototypeAccessor,
223+
_ => null
224+
};
225+
}
226+
227+
return false;
228+
}
229+
194230
internal static object? ReadIdentifierValue(Func<object?> getter, EvaluationContext context)
195231
{
196232
try

src/Asynkron.JsEngine/Ast/TypedAstEvaluator.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,29 @@ private static bool TryAwaitPromise(object? candidate, EvaluationContext context
248248
yield break;
249249
}
250250

251+
case TypedArrayBase typedArray:
252+
{
253+
// TypedArray for-in only exposes own enumerable properties (indices and custom slots).
254+
var seenKeys = new HashSet<string>(StringComparer.Ordinal);
255+
foreach (var key in typedArray.GetOwnPropertyNames().ToList())
256+
{
257+
if (!seenKeys.Add(key))
258+
{
259+
continue;
260+
}
261+
262+
var desc = typedArray.GetOwnPropertyDescriptor(key);
263+
if (desc is null || desc is { Enumerable: false })
264+
{
265+
continue;
266+
}
267+
268+
yield return key;
269+
}
270+
271+
yield break;
272+
}
273+
251274
case string s:
252275
{
253276
for (var i = 0; i < s.Length; i++)

src/Asynkron.JsEngine/Parser/TypedAstParser.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1604,12 +1604,52 @@ private StatementNode ParseForStatement(Token forToken)
16041604
}
16051605
else
16061606
{
1607+
if (Check(TokenType.Await) && CheckAhead(TokenType.Using))
1608+
{
1609+
if (!IsAwaitAllowed)
1610+
{
1611+
throw new ParseException("'await using' is only allowed inside async contexts.", Peek(),
1612+
_source);
1613+
}
1614+
1615+
Advance(); // await
1616+
Advance(); // using
1617+
var previousAllowIn = _allowInExpressions;
1618+
_allowInExpressions = false;
1619+
try
1620+
{
1621+
initializerDeclaration =
1622+
(VariableDeclaration)ParseVariableDeclaration(VariableKind.AwaitUsing, false, true);
1623+
initializer = initializerDeclaration;
1624+
}
1625+
finally
1626+
{
1627+
_allowInExpressions = previousAllowIn;
1628+
}
1629+
}
1630+
else if (Match(TokenType.Using))
1631+
{
1632+
var previousAllowIn = _allowInExpressions;
1633+
_allowInExpressions = false;
1634+
try
1635+
{
1636+
initializerDeclaration =
1637+
(VariableDeclaration)ParseVariableDeclaration(VariableKind.Using, false, true);
1638+
initializer = initializerDeclaration;
1639+
}
1640+
finally
1641+
{
1642+
_allowInExpressions = previousAllowIn;
1643+
}
1644+
}
1645+
else
16071646
// In non-strict mode, 'for (let' is only a lexical declaration if followed by '[', '{'
16081647
// or a binding identifier. Otherwise 'let' is just an identifier expression.
16091648
// See: for ( [lookahead ∉ { let [ }] Expression
16101649
if (Check(TokenType.Let) &&
16111650
(InStrictContext || CheckAhead(TokenType.LeftBracket) || CheckAhead(TokenType.LeftBrace) ||
1612-
(CheckAheadBindingIdentifier() && !CheckAhead(TokenType.Let))))
1651+
(CheckAheadBindingIdentifier() && !CheckAhead(TokenType.Let)) &&
1652+
!CheckAhead(TokenType.In)))
16131653
{
16141654
Advance(); // consume 'let'
16151655
var previousAllowIn = _allowInExpressions;

0 commit comments

Comments
 (0)