Skip to content

Commit 239e318

Browse files
committed
SI-7475 findMember and findMembers: estranged no more
Swathes of important logic are duplicated between `findMember` and `findMembers` after they separated on grounds of irreconcilable differences about how fast they should run: d905558 Variation #10 to optimze findMember fcb0c01 Attempt #9 to opimize findMember. 71d2ceb Attempt #8 to opimize findMember. 77e5692 Attempty #7 to optimize findMember 275115e Fixing problem that caused fingerprints to fail in e94252e Attemmpt #6 to optimize findMember 73e61b8 Attempt #5 to optimize findMember. 04f0b65 Attempt #4 to optimize findMember 0e3c70f Attempt #3 to optimize findMember 41f4497 Attempt #2 to optimize findMember 1a73aa0 Attempt #1 to optimize findMember This didn't actually bear fruit, and the intervening years have seen the implementations drift. Now is the time to reunite them under the banner of `FindMemberBase`. Each has a separate subclass to customise the behaviour. This is primarily used by `findMember` to cache member types and to assemble the resulting list of symbols in an low-allocation manner. While there I have introduced some polymorphic calls, the call sites are only bi-morphic, and our typical pattern of compilation involves far more `findMember` calls, so I expect that JIT will keep the virtual call cost to an absolute minimum. Test results have been updated now that `findMembers` correctly excludes constructors and doesn't inherit privates. Coming up next: we can actually fix SI-7475!
1 parent 1236550 commit 239e318

File tree

5 files changed

+29
-171
lines changed

5 files changed

+29
-171
lines changed

src/reflect/scala/reflect/internal/Types.scala

+2-168
Original file line numberDiff line numberDiff line change
@@ -990,65 +990,7 @@ trait Types
990990
*
991991
*/
992992
def findMembers(excludedFlags: Long, requiredFlags: Long): Scope = {
993-
// TODO refactor to use `FindMemberBase`
994-
def findMembersInternal: Scope = {
995-
var members: Scope = null
996-
if (Statistics.canEnable) Statistics.incCounter(findMembersCount)
997-
val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMembersNanos) else null
998-
999-
//Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG
1000-
var required = requiredFlags
1001-
var excluded = excludedFlags | DEFERRED
1002-
var retryForDeferred = true
1003-
var self: Type = null
1004-
while (retryForDeferred) {
1005-
retryForDeferred = false
1006-
val bcs0 = baseClasses
1007-
var bcs = bcs0
1008-
while (!bcs.isEmpty) {
1009-
val decls = bcs.head.info.decls
1010-
var entry = decls.elems
1011-
while (entry ne null) {
1012-
val sym = entry.sym
1013-
val flags = sym.flags
1014-
if ((flags & required) == required) {
1015-
val excl = flags & excluded
1016-
if (excl == 0L &&
1017-
(// omit PRIVATE LOCALS unless selector class is contained in class owning the def.
1018-
(bcs eq bcs0) ||
1019-
(flags & PrivateLocal) != PrivateLocal ||
1020-
(bcs0.head.hasTransOwner(bcs.head)))) {
1021-
if (members eq null) members = newFindMemberScope
1022-
var others: ScopeEntry = members.lookupEntry(sym.name)
1023-
var symtpe: Type = null
1024-
while ((others ne null) && {
1025-
val other = others.sym
1026-
(other ne sym) &&
1027-
((other.owner eq sym.owner) ||
1028-
(flags & PRIVATE) != 0 || {
1029-
if (self eq null) self = narrowForFindMember(this)
1030-
if (symtpe eq null) symtpe = self.memberType(sym)
1031-
!(self.memberType(other) matches symtpe)
1032-
})}) {
1033-
others = members lookupNextEntry others
1034-
}
1035-
if (others eq null) members enter sym
1036-
} else if (excl == DEFERRED) {
1037-
retryForDeferred = (excludedFlags & DEFERRED) == 0
1038-
}
1039-
}
1040-
entry = entry.next
1041-
} // while (entry ne null)
1042-
// excluded = excluded | LOCAL
1043-
bcs = bcs.tail
1044-
} // while (!bcs.isEmpty)
1045-
required |= DEFERRED
1046-
excluded &= ~(DEFERRED.toLong)
1047-
} // while (retryForDeferred)
1048-
if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start)
1049-
if (members eq null) EmptyScope else members
1050-
}
1051-
993+
def findMembersInternal = new FindMembers(this, excludedFlags, requiredFlags).apply()
1052994
if (this.isGround) findMembersInternal
1053995
else suspendingTypeVars(typeVarsInType(this))(findMembersInternal)
1054996
}
@@ -1063,115 +1005,7 @@ trait Types
10631005
* @param stableOnly If set, return only members that are types or stable values
10641006
*/
10651007
def findMember(name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean): Symbol = {
1066-
def findMemberInternal = {
1067-
// TODO delete `findMemberInternalOld`
1068-
val resultOld = findMemberInternalOld
1069-
val resultNew = findMemberInternalNew
1070-
if (resultOld.alternatives != resultNew.alternatives) {
1071-
findMemberInternalOld
1072-
findMemberInternalNew
1073-
}
1074-
assert(resultOld.alternatives == resultNew.alternatives, s"\nOLD:${resultOld.alternatives}\nNEW${resultNew.alternatives}")
1075-
resultNew
1076-
}
1077-
def findMemberInternalNew = new FindMember(this, name, excludedFlags, requiredFlags, stableOnly).apply()
1078-
def findMemberInternalOld: Symbol = {
1079-
var member: Symbol = NoSymbol
1080-
var members: List[Symbol] = null
1081-
var lastM: ::[Symbol] = null
1082-
if (Statistics.canEnable) Statistics.incCounter(findMemberCount)
1083-
val start = if (Statistics.canEnable) Statistics.pushTimer(typeOpsStack, findMemberNanos) else null
1084-
1085-
//Console.println("find member " + name.decode + " in " + this + ":" + this.baseClasses)//DEBUG
1086-
var membertpe: Type = null
1087-
var required = requiredFlags
1088-
var excluded = excludedFlags | DEFERRED
1089-
var continue = true
1090-
var self: Type = null
1091-
1092-
while (continue) {
1093-
continue = false
1094-
val bcs0 = baseClasses
1095-
var bcs = bcs0
1096-
// omit PRIVATE LOCALS unless selector class is contained in class owning the def.
1097-
def admitPrivateLocal(owner: Symbol): Boolean = {
1098-
val selectorClass = this match {
1099-
case tt: ThisType => tt.sym // SI-7507 the first base class is not necessarily the selector class.
1100-
case _ => bcs0.head
1101-
}
1102-
selectorClass.hasTransOwner(owner)
1103-
}
1104-
while (!bcs.isEmpty) {
1105-
val decls = bcs.head.info.decls
1106-
var entry = decls.lookupEntry(name)
1107-
while (entry ne null) {
1108-
val sym = entry.sym
1109-
val flags = sym.flags
1110-
if ((flags & required) == required) {
1111-
val excl = flags & excluded
1112-
if (excl == 0L &&
1113-
(
1114-
(bcs eq bcs0) ||
1115-
(flags & PrivateLocal) != PrivateLocal ||
1116-
admitPrivateLocal(bcs.head))) {
1117-
if (name.isTypeName || (stableOnly && sym.isStable && !sym.hasVolatileType)) {
1118-
if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start)
1119-
return sym
1120-
} else if (member eq NoSymbol) {
1121-
member = sym
1122-
} else if (members eq null) {
1123-
if ((member ne sym) &&
1124-
((member.owner eq sym.owner) ||
1125-
(flags & PRIVATE) != 0 || {
1126-
if (self eq null) self = narrowForFindMember(this)
1127-
if (membertpe eq null) membertpe = self.memberType(member)
1128-
!(membertpe matches self.memberType(sym))
1129-
})) {
1130-
lastM = new ::(sym, null)
1131-
members = member :: lastM
1132-
}
1133-
} else {
1134-
var others: List[Symbol] = members
1135-
var symtpe: Type = null
1136-
while ((others ne null) && {
1137-
val other = others.head
1138-
(other ne sym) &&
1139-
((other.owner eq sym.owner) ||
1140-
(flags & PRIVATE) != 0 || {
1141-
if (self eq null) self = narrowForFindMember(this)
1142-
if (symtpe eq null) symtpe = self.memberType(sym)
1143-
!(self.memberType(other) matches symtpe)
1144-
})}) {
1145-
others = others.tail
1146-
}
1147-
if (others eq null) {
1148-
val lastM1 = new ::(sym, null)
1149-
lastM.tl = lastM1
1150-
lastM = lastM1
1151-
}
1152-
}
1153-
} else if (excl == DEFERRED) {
1154-
continue = true
1155-
}
1156-
}
1157-
entry = decls lookupNextEntry entry
1158-
} // while (entry ne null)
1159-
// excluded = excluded | LOCAL
1160-
bcs = if (name == nme.CONSTRUCTOR) Nil else bcs.tail
1161-
} // while (!bcs.isEmpty)
1162-
required |= DEFERRED
1163-
excluded &= ~(DEFERRED.toLong)
1164-
} // while (continue)
1165-
if (Statistics.canEnable) Statistics.popTimer(typeOpsStack, start)
1166-
if (members eq null) {
1167-
if (member == NoSymbol) if (Statistics.canEnable) Statistics.incCounter(noMemberCount)
1168-
member
1169-
} else {
1170-
if (Statistics.canEnable) Statistics.incCounter(multMemberCount)
1171-
lastM.tl = Nil
1172-
baseClasses.head.newOverloaded(this, members)
1173-
}
1174-
}
1008+
def findMemberInternal = new FindMember(this, name, excludedFlags, requiredFlags, stableOnly).apply()
11751009

11761010
if (this.isGround) findMemberInternal
11771011
else suspendingTypeVars(typeVarsInType(this))(findMemberInternal)

src/reflect/scala/reflect/internal/tpe/FindMembers.scala

+25
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,31 @@ trait FindMembers {
150150
protected def memberTypeLow(sym: Symbol): Type = self.memberType(sym)
151151
}
152152

153+
private[reflect] final class FindMembers(tpe: Type, excludedFlags: Long, requiredFlags: Long)
154+
extends FindMemberBase[Scope](tpe, nme.ANYname, excludedFlags, requiredFlags) {
155+
private[this] var _membersScope: Scope = null
156+
private def membersScope: Scope = {
157+
if (_membersScope eq null) _membersScope = newFindMemberScope
158+
_membersScope
159+
}
160+
161+
protected def shortCircuit(sym: Symbol): Boolean = false
162+
protected def result: Scope = membersScope
163+
164+
protected def addMemberIfNew(sym: Symbol): Unit = {
165+
val members = membersScope
166+
var others = members.lookupEntry(sym.name)
167+
var isNew = true
168+
while (others ne null) {
169+
val member = others.sym
170+
if (!isNewMember(member, sym))
171+
isNew = false
172+
others = members lookupNextEntry others // next existing member with the same name.
173+
}
174+
if (isNew) members.enter(sym)
175+
}
176+
}
177+
153178
private[reflect] final class FindMember(tpe: Type, name: Name, excludedFlags: Long, requiredFlags: Long, stableOnly: Boolean)
154179
extends FindMemberBase[Symbol](tpe, name, excludedFlags, requiredFlags) {
155180
// Gathering the results into a hand rolled ListBuffer

test/files/presentation/ide-bug-1000531.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ reload: CrashOnLoad.scala
33
askTypeCompletion at CrashOnLoad.scala(6,12)
44
================================================================================
55
[response] askTypeCompletion at (6,12)
6-
retrieved 120 members
6+
retrieved 119 members
77
[inaccessible] protected[package lang] def clone(): Object
88
[inaccessible] protected[package lang] def finalize(): Unit
99
[inaccessible] protected[this] def reversed: List[B]

test/files/presentation/ping-pong.check

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ reload: PingPong.scala
33
askTypeCompletion at PingPong.scala(10,23)
44
================================================================================
55
[response] askTypeCompletion at (10,23)
6-
retrieved 35 members
6+
retrieved 34 members
77
[inaccessible] private[this] val ping: Ping
88
[inaccessible] protected[package lang] def clone(): Object
99
[inaccessible] protected[package lang] def finalize(): Unit

test/files/run/reflection-magicsymbols-invoke.check

-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ Array
8282
it's important to print the list of Array's members
8383
if some of them change (possibly, adding and/or removing magic symbols), we must update this test
8484
constructor Array: (_length: Int)Array[T]
85-
constructor Cloneable: ()java.lang.Cloneable
8685
method !=: (x$1: Any)Boolean
8786
method !=: (x$1: AnyRef)Boolean
8887
method ##: ()Int

0 commit comments

Comments
 (0)