Skip to content

Commit f0dfb61

Browse files
Fix index operations (#15)
* Fix index operations Currently index detection is done by looking for specific interface implementations. This change enables the use of the `DefaultMemberAttribute` to determine the indexer for a type. * Change enumeration test to be order independant
1 parent ed2115c commit f0dfb61

File tree

3 files changed

+49
-77
lines changed

3 files changed

+49
-77
lines changed

src/PSLambda/CompileVisitor.cs

Lines changed: 27 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -555,74 +555,41 @@ public object VisitIndexExpression(IndexExpressionAst indexExpressionAst)
555555
indexExpressionAst.Index.Compile(this));
556556
}
557557

558-
if (TryFindGenericInterface(source.Type, typeof(IList<>), out Type genericList))
559-
{
560-
return MakeIndex(
561-
source,
562-
genericList.GetProperty(Strings.DefaultIndexerPropertyName),
563-
new[] { indexExpressionAst.Index.Compile(this) });
564-
}
565-
566-
if (TryFindGenericInterface(source.Type, typeof(IDictionary<,>), out Type genericDictionary))
567-
{
568-
return MakeIndex(
569-
source,
570-
genericDictionary.GetProperty(Strings.DefaultIndexerPropertyName),
571-
new[] { indexExpressionAst.Index.Compile(this) });
572-
}
558+
var defaultMember =
559+
source.Type.GetCustomAttribute<DefaultMemberAttribute>(inherit: true);
573560

574-
if (TryFindGenericInterface(source.Type, typeof(IEnumerable<>), out Type genericEnumerable) ||
575-
(source.Type.IsGenericType &&
576-
source.Type.GetGenericTypeDefinition() == typeof(IEnumerable<>)))
561+
if (defaultMember == null)
577562
{
578-
if (genericEnumerable == null)
579-
{
580-
genericEnumerable = source.Type;
581-
}
582-
583-
Expression index;
584-
try
585-
{
586-
index = Convert(indexExpressionAst.Index.Compile(this), typeof(int));
587-
}
588-
catch (InvalidOperationException e)
589-
{
590-
Errors.ReportParseError(indexExpressionAst.Index.Extent, e);
591-
return Empty();
592-
}
593-
594-
return Call(
595-
typeof(Enumerable),
596-
Strings.ElementAtOrDefaultMethodName,
597-
genericEnumerable.GetGenericArguments(),
598-
source,
599-
index);
563+
Errors.ReportParseError(
564+
indexExpressionAst.Extent,
565+
nameof(ErrorStrings.UnknownIndexer),
566+
string.Format(
567+
CultureInfo.CurrentCulture,
568+
ErrorStrings.UnknownIndexer,
569+
source.Type.FullName));
570+
return Empty();
600571
}
601572

602-
if (typeof(System.Collections.IList).IsAssignableFrom(source.Type))
603-
{
604-
return MakeIndex(
605-
source,
606-
ReflectionCache.IList_Item,
607-
new[] { indexExpressionAst.Index.Compile(this) });
608-
}
573+
var index = indexExpressionAst.Index.Compile(this);
574+
var indexerProperty = source.Type
575+
.GetProperty(defaultMember.MemberName, new[] { index.Type });
609576

610-
if (typeof(System.Collections.IDictionary).IsAssignableFrom(source.Type))
577+
if (indexerProperty == null)
611578
{
612-
return MakeIndex(
613-
source,
614-
ReflectionCache.IDictionary_Item,
615-
new[] { indexExpressionAst.Index.Compile(this) });
579+
Errors.ReportParseError(
580+
indexExpressionAst.Extent,
581+
nameof(ErrorStrings.UnknownIndexer),
582+
string.Format(
583+
CultureInfo.CurrentCulture,
584+
ErrorStrings.UnknownIndexer,
585+
source.Type.FullName));
586+
return Empty();
616587
}
617588

618-
Errors.ReportParseError(
619-
indexExpressionAst.Extent,
620-
nameof(ErrorStrings.UnknownIndexer),
621-
string.Format(
622-
CultureInfo.CurrentCulture,
623-
ErrorStrings.UnknownIndexer,
624-
source.Type.FullName));
625-
return Empty();
589+
return Property(
590+
source,
591+
indexerProperty,
592+
index);
626593
}
627594

628595
public object VisitInvokeMemberExpression(InvokeMemberExpressionAst invokeMemberExpressionAst)

test/Loops.Tests.ps1

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,17 @@ Describe 'basic loop functionality' {
2525
three = 'four'
2626
}
2727

28-
$sb = [System.Text.StringBuilder]::new()
28+
$results = [System.Collections.Generic.List[string]]::new()
2929
foreach($item in $hashtable) {
30-
$sb.Append($item.Value.ToString())
30+
$results.Add($item.Value.ToString())
3131
}
3232

33-
return $sb.ToString()
33+
return $results
3434
}
3535

36-
$delegate.Invoke() | Should -Be twofour
36+
$results = $delegate.Invoke()
37+
$results | Should -Contain two
38+
$results | Should -Contain four
3739
}
3840

3941
It 'prioritizes IEnumerable<> over IDictionary' {

test/MiscLanguageFeatures.Tests.ps1

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -206,28 +206,31 @@ Describe 'Misc Language Features' {
206206
$delegate.Invoke() | Should -Be 'System'
207207
}
208208

209-
It 'indexed IEnumerable<> are typed properly' {
209+
It 'can index IList' {
210210
$delegate = New-PSDelegate {
211-
$strings = generic(
212-
[System.Linq.Enumerable]::Select(
213-
('test', 'test2', 'test3'),
214-
[func[string, string]]{ ($string) => { $string }}),
215-
[string], [string])
216-
217-
return $strings[1].EndsWith('2')
211+
$list = [System.Collections.ArrayList]::new()
212+
$list.AddRange([object[]]('one', 'two', 'three'))
213+
return [string]$list[1] -eq 'two'
218214
}
219215

220216
$delegate.Invoke() | Should -Be $true
221217
}
222218

223-
It 'can index IList' {
219+
It 'can index custom indexers' {
224220
$delegate = New-PSDelegate {
225-
$list = [System.Collections.ArrayList]::new()
226-
$list.AddRange([object[]]('one', 'two', 'three'))
227-
return [string]$list[1] -eq 'two'
221+
$pso = [psobject]::AsPSObject([System.Text.StringBuilder]::new())
222+
$pso.Methods['Append'].Invoke(@('testing'))
223+
return $pso
228224
}
229225

230-
$delegate.Invoke() | Should -Be $true
226+
$result = $delegate.Invoke()
227+
$result | Should -BeOfType System.Text.StringBuilder
228+
$result.ToString() | Should -Be testing
229+
}
230+
231+
It 'throws the correct message when indexer cannot be found' {
232+
$expectedMsg = 'The indexer could not be determined for the type "System.Int32".'
233+
{ New-PSDelegate { (10)[0] }} | Should -Throw $expectedMsg
231234
}
232235
}
233236
}

0 commit comments

Comments
 (0)