Skip to content

Commit 7455e80

Browse files
committed
.
1 parent 14bfc8b commit 7455e80

File tree

3 files changed

+141
-110
lines changed

3 files changed

+141
-110
lines changed

src/Asynkron.JsEngine/Ast/AssignmentReference.cs

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -157,27 +157,6 @@ TypedAstEvaluator.PropertyHandle GetHandle()
157157
});
158158
}
159159

160-
if (target is JsArray jsArray && JsOps.TryResolveArrayIndex(propertyValue, out var arrayIndex, context))
161-
{
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-
176-
return new AssignmentReference(
177-
() => jsArray.GetElement(arrayIndex),
178-
newValue => jsArray.SetElement(arrayIndex, newValue));
179-
}
180-
181160
if (target is TypedArrayBase typedArray &&
182161
JsOps.TryResolveArrayIndex(propertyValue, out var typedIndex, context))
183162
{
@@ -205,28 +184,6 @@ TypedAstEvaluator.PropertyHandle GetHandle()
205184
newValue => handle.SetValue(newValue));
206185
}
207186

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-
230187
internal static object? ReadIdentifierValue(Func<object?> getter, EvaluationContext context)
231188
{
232189
try

src/Asynkron.JsEngine/Runtime/JsOps.cs

Lines changed: 102 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1725,102 +1725,137 @@ private static bool TryAssignArrayLikeValue(object? target, object? propertyKey,
17251725
return true;
17261726
}
17271727

1728+
if (propertyName is null)
1729+
{
1730+
return true;
1731+
}
1732+
17281733
if (string.Equals(propertyName, "length", StringComparison.Ordinal))
17291734
{
17301735
jsArray.SetLength(value, context);
17311736
return true;
17321737
}
17331738

1734-
if (TryResolveArrayIndex(propertyKey, out var index, context))
1739+
var isArrayIndex = TryResolveArrayIndex(propertyKey, out var index, context);
1740+
var ownDescriptor = jsArray.GetOwnPropertyDescriptor(propertyName);
1741+
1742+
if (isArrayIndex)
17351743
{
1736-
if (propertyName is not null && TryHandleArrayIndexedAssignment(jsArray, propertyName, value, context))
1744+
if (ownDescriptor is not null)
17371745
{
1738-
return true;
1739-
}
1746+
if (ownDescriptor.IsAccessorDescriptor)
1747+
{
1748+
if (ownDescriptor.Set is null)
1749+
{
1750+
if (context?.CurrentScope.IsStrict == true)
1751+
{
1752+
throw StandardLibrary.ThrowTypeError(
1753+
$"Cannot set property '{propertyName}' that has only a getter.",
1754+
context,
1755+
context.RealmState);
1756+
}
1757+
1758+
return true;
1759+
}
17401760

1741-
jsArray.SetElement(index, value);
1742-
return true;
1743-
}
1744-
}
1761+
TypedAstEvaluator.InvokeCallable(ownDescriptor.Set, [value], jsArray, context);
1762+
return true;
1763+
}
17451764

1746-
if (target is TypedArrayBase typedArray && TryResolveArrayIndex(propertyKey, out var typedIndex, context))
1747-
{
1748-
typedArray.SetValue(typedIndex, value);
1749-
return true;
1750-
}
1765+
if (!ownDescriptor.Writable)
1766+
{
1767+
if (context?.CurrentScope.IsStrict == true)
1768+
{
1769+
throw StandardLibrary.ThrowTypeError(
1770+
$"Cannot assign to read only property '{propertyName}'.",
1771+
context,
1772+
context.RealmState);
1773+
}
17511774

1752-
return false;
1753-
}
1775+
return true;
1776+
}
17541777

1755-
private static bool TryHandleArrayIndexedAssignment(
1756-
JsArray array,
1757-
string propertyName,
1758-
object? value,
1759-
EvaluationContext? context)
1760-
{
1761-
var descriptor = FindPropertyDescriptorInChain(array, propertyName);
1762-
if (descriptor is null)
1763-
{
1764-
return false;
1765-
}
1778+
jsArray.SetElement(index, value);
1779+
return true;
1780+
}
17661781

1767-
if (descriptor.IsAccessorDescriptor)
1768-
{
1769-
if (descriptor.Set is null)
1770-
{
1771-
if (context?.CurrentScope.IsStrict == true)
1782+
IJsPropertyAccessor? current = jsArray.PrototypeAccessor ?? jsArray.Prototype;
1783+
while (current is not null)
17721784
{
1773-
throw StandardLibrary.ThrowTypeError(
1774-
$"Cannot set property '{propertyName}' that has only a getter.",
1775-
context,
1776-
context.RealmState);
1785+
var inheritedDescriptor = current.GetOwnPropertyDescriptor(propertyName);
1786+
if (inheritedDescriptor is not null)
1787+
{
1788+
if (inheritedDescriptor.IsAccessorDescriptor)
1789+
{
1790+
if (inheritedDescriptor.Set is null)
1791+
{
1792+
if (context?.CurrentScope.IsStrict == true)
1793+
{
1794+
throw StandardLibrary.ThrowTypeError(
1795+
$"Cannot set property '{propertyName}' that has only a getter.",
1796+
context,
1797+
context.RealmState);
1798+
}
1799+
1800+
return true;
1801+
}
1802+
1803+
TypedAstEvaluator.InvokeCallable(inheritedDescriptor.Set, [value], jsArray, context);
1804+
return true;
1805+
}
1806+
1807+
if (!inheritedDescriptor.Writable)
1808+
{
1809+
if (context?.CurrentScope.IsStrict == true)
1810+
{
1811+
throw StandardLibrary.ThrowTypeError(
1812+
$"Cannot assign to read only property '{propertyName}'.",
1813+
context,
1814+
context.RealmState);
1815+
}
1816+
1817+
return true;
1818+
}
1819+
1820+
jsArray.DefineProperty(propertyName, new PropertyDescriptor
1821+
{
1822+
Value = value,
1823+
Writable = true,
1824+
Enumerable = inheritedDescriptor.Enumerable,
1825+
Configurable = inheritedDescriptor.Configurable,
1826+
HasValue = true,
1827+
HasWritable = true,
1828+
HasEnumerable = inheritedDescriptor.HasEnumerable,
1829+
HasConfigurable = inheritedDescriptor.HasConfigurable
1830+
});
1831+
return true;
1832+
}
1833+
1834+
current = current switch
1835+
{
1836+
IJsObjectLike objectLike => objectLike.Prototype,
1837+
IPrototypeAccessorProvider provider => provider.PrototypeAccessor,
1838+
_ => null
1839+
};
17771840
}
17781841

1842+
jsArray.SetElement(index, value);
17791843
return true;
17801844
}
17811845

1782-
descriptor.Set.Invoke([value], array);
1846+
jsArray.SetProperty(propertyName, value, jsArray);
17831847
return true;
17841848
}
17851849

1786-
if (!descriptor.Writable)
1850+
if (target is TypedArrayBase typedArray && TryResolveArrayIndex(propertyKey, out var typedIndex, context))
17871851
{
1788-
if (context?.CurrentScope.IsStrict == true)
1789-
{
1790-
throw StandardLibrary.ThrowTypeError(
1791-
$"Cannot assign to read only property '{propertyName}'.",
1792-
context,
1793-
context.RealmState);
1794-
}
1795-
1852+
typedArray.SetValue(typedIndex, value);
17961853
return true;
17971854
}
17981855

17991856
return false;
18001857
}
18011858

1802-
private static PropertyDescriptor? FindPropertyDescriptorInChain(IJsPropertyAccessor start, string propertyName)
1803-
{
1804-
IJsPropertyAccessor? current = start;
1805-
while (current is not null)
1806-
{
1807-
var descriptor = current.GetOwnPropertyDescriptor(propertyName);
1808-
if (descriptor is not null)
1809-
{
1810-
return descriptor;
1811-
}
1812-
1813-
current = current switch
1814-
{
1815-
IJsObjectLike objectLike => objectLike.Prototype,
1816-
IPrototypeAccessorProvider provider => provider.PrototypeAccessor,
1817-
_ => null
1818-
};
1819-
}
1820-
1821-
return null;
1822-
}
1823-
18241859
public static bool DeletePropertyValue(object? target, object? propertyKey, EvaluationContext? context = null)
18251860
{
18261861
if (target is null || ReferenceEquals(target, Symbol.Undefined))
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using Asynkron.JsEngine.JsTypes;
2+
using NUnit.Framework;
3+
4+
namespace Asynkron.JsEngine.Tests.Test262;
5+
6+
[TestFixture]
7+
public class RegressionTests
8+
{
9+
[Test]
10+
public async Task ForInMemberLhsInvokesArrayPrototypeSetter()
11+
{
12+
var engine = new JsEngine();
13+
var result = await engine.Evaluate(
14+
"""
15+
var obj = Object.create(null);
16+
var let, value;
17+
obj.key = 1;
18+
19+
for (let in obj) ;
20+
21+
Object.defineProperty(Array.prototype, "1", {
22+
set: function(param) {
23+
value = param;
24+
}
25+
});
26+
27+
for ([let][1] in obj) ;
28+
[
29+
typeof Object.getOwnPropertyDescriptor(Array.prototype, "1").set,
30+
value
31+
];
32+
""");
33+
34+
var resultArray = result as JsArray ?? throw new AssertionException("Expected array result");
35+
TestContext.WriteLine($"SetterType={resultArray.Items[0]}, Value={resultArray.Items[1]}");
36+
Assert.That(resultArray.Items[0], Is.EqualTo("function"));
37+
Assert.That(resultArray.Items[1], Is.EqualTo("key"));
38+
}
39+
}

0 commit comments

Comments
 (0)