Skip to content

Commit

Permalink
Add method reference expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Earthcomputer committed Apr 6, 2024
1 parent 8bb9eb1 commit 8770366
Show file tree
Hide file tree
Showing 14 changed files with 399 additions and 17 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ dependencies {
implementation(files(Jvm.current().toolsJar))

// TODO: temporary waiting for MixinExtras expression library
testLibs(implementation("com.github.LlamaLad7.MixinExtras:mixinextras-common:d999081")!!)
testLibs(implementation("com.github.LlamaLad7.MixinExtras:mixinextras-common:e0f566c")!!)
implementation("org.spongepowered:mixin:0.8.4")
implementation("org.ow2.asm:asm-util:9.3")

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@

package com.demonwav.mcdev.mixintestdata.meExpression;

import java.util.stream.Stream;

public class MEExpressionTestData {
private static final SynchedData<Integer> STINGER_COUNT = null;
private SynchedDataManager synchedData;
Expand All @@ -38,6 +40,8 @@ public void complexFunction() {

String[] strings1 = new String[] { local1, local2 };
String[] strings2 = new String[one];

Stream.empty().map(this::nonStaticMapper).map(MEExpressionTestData::staticMapper).map(ConstructedByMethodReference::new);
}

private static void acceptInaccessibleType(InaccessibleType type) {
Expand All @@ -50,6 +54,14 @@ public int getStingerCount() {
return (Integer) this.synchedData.get(STINGER_COUNT);
}

private Object nonStaticMapper(Object arg) {
return arg;
}

private static Object staticMapper(Object arg) {
return arg;
}

private static class InaccessibleType {

}
Expand All @@ -62,4 +74,8 @@ public <V> V get(SynchedData<V> data) {

public static class SynchedData<V> {
}

public static class ConstructedByMethodReference {
public ConstructedByMethodReference(Object bar) {}
}
}
2 changes: 2 additions & 0 deletions src/main/grammars/MEExpressionLexer.flex
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ BITWISE_AND = &
BITWISE_XOR = "^"
BITWISE_OR = "|"
ASSIGN = =
METHOD_REF = ::

STRING_TERMINATOR = '
STRING_ESCAPE = \\'|\\\\
Expand Down Expand Up @@ -133,6 +134,7 @@ STRING_ESCAPE = \\'|\\\\
{BITWISE_XOR} { yybegin(YYINITIAL); return MEExpressionTypes.TOKEN_BITWISE_XOR; }
{BITWISE_OR} { yybegin(YYINITIAL); return MEExpressionTypes.TOKEN_BITWISE_OR; }
{ASSIGN} { yybegin(YYINITIAL); return MEExpressionTypes.TOKEN_ASSIGN; }
{METHOD_REF} { yybegin(YYINITIAL); return MEExpressionTypes.TOKEN_METHOD_REF; }
{STRING_TERMINATOR} { yybegin(STRING); return MEExpressionTypes.TOKEN_STRING_TERMINATOR; }
}

Expand Down
28 changes: 28 additions & 0 deletions src/main/grammars/MEExpressionParser.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,9 @@ expression ::= capturingExpression |
castExpression |
parenthesizedExpression |
methodCallExpression |
boundMethodReferenceExpression |
freeMethodReferenceExpression |
constructorReferenceExpression |
arrayAccessExpression |
memberAccessExpression |
newExpression |
Expand Down Expand Up @@ -170,6 +173,31 @@ staticMethodCallExpression ::= name TOKEN_LEFT_PAREN arguments rightParen {
]
}

boundMethodReferenceExpression ::= expression !(TOKEN_METHOD_REF TOKEN_NEW) TOKEN_METHOD_REF name {
pin = 3
mixin = "com.demonwav.mcdev.platform.mixin.expression.psi.mixins.impl.MEBoundReferenceExpressionImplMixin"
methods = [
receiverExpr = "expression"
memberName = "name"
]
}

freeMethodReferenceExpression ::= TOKEN_METHOD_REF name {
pin = 1
mixin = "com.demonwav.mcdev.platform.mixin.expression.psi.mixins.impl.MEFreeMethodReferenceExpressionImplMixin"
methods = [
memberName = "name"
]
}

constructorReferenceExpression ::= type TOKEN_METHOD_REF TOKEN_NEW {
pin = 3
mixin = "com.demonwav.mcdev.platform.mixin.expression.psi.mixins.impl.MEConstructorReferenceExpressionImplMixin"
methods = [
className = "type"
]
}

arrayAccessExpression ::= expression TOKEN_LEFT_BRACKET expression? rightBracket {
pin = 2
implements = "com.demonwav.mcdev.platform.mixin.expression.psi.mixins.MEArrayAccessExpressionMixin"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ package com.demonwav.mcdev.platform.mixin.expression
import com.demonwav.mcdev.asset.MCDevBundle
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEArrayAccessExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEBinaryExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEBoundMethodReferenceExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEDeclaration
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEDeclarationItem
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionTypes
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEFreeMethodReferenceExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MELitExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEMemberAccessExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEMethodCallExpression
Expand Down Expand Up @@ -76,7 +78,9 @@ class MEExpressionAnnotator : Annotator {
)
is MESuperCallExpression,
is MEMethodCallExpression,
is MEStaticMethodCallExpression -> highlightVariable(
is MEStaticMethodCallExpression,
is MEBoundMethodReferenceExpression,
is MEFreeMethodReferenceExpression -> highlightVariable(
holder,
element,
MEExpressionSyntaxHighlighter.IDENTIFIER_CALL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ class MEExpressionColorSettingsPage : ColorSettingsPage {
MCDevBundle.pointer("mixinextras.expression.lang.highlighting.dot.display_name"),
MEExpressionSyntaxHighlighter.DOT
),
AttributesDescriptor(
MCDevBundle.pointer("mixinextras.expression.lang.highlighting.method_reference.display_name"),
MEExpressionSyntaxHighlighter.METHOD_REFERENCE
),
AttributesDescriptor(
MCDevBundle.pointer("mixinextras.expression.lang.highlighting.comma.display_name"),
MEExpressionSyntaxHighlighter.COMMA
Expand Down Expand Up @@ -137,6 +141,7 @@ class MEExpressionColorSettingsPage : ColorSettingsPage {
<class_name>ClassName</class_name>.class,
<variable>foo</variable>.<member_name>bar</member_name>,
new <primitive_type>int</primitive_type>[] { 1, 2, 3 },
<variable>method</variable>::<call>reference</call>,
'a bad character: ' # other_identifier
)[0]
""".trimIndent()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ class MEExpressionCompletionContributor : CompletionContributor() {
Keyword("instanceof", TailType.INSERT_SPACE)
)
)
extend(
CompletionType.BASIC,
MEExpressionCompletionUtil.METHOD_REFERENCE_PLACE,
KeywordCompletionProvider(
Keyword("new")
)
)
extend(
CompletionType.BASIC,
MEExpressionCompletionUtil.STRING_LITERAL_PLACE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,14 @@ import com.demonwav.mcdev.MinecraftProjectSettings
import com.demonwav.mcdev.platform.mixin.expression.MEExpressionMatchUtil.insnOrNull
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEArrayAccessExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEAssignStatement
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEBoundMethodReferenceExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MECapturingExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MECastExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEClassConstantExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionStatement
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEExpressionTypes
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEFreeMethodReferenceExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MELitExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEMemberAccessExpression
import com.demonwav.mcdev.platform.mixin.expression.gen.psi.MEMethodCallExpression
Expand Down Expand Up @@ -116,6 +118,7 @@ import com.llamalad7.mixinextras.expression.impl.flow.FlowValue
import com.llamalad7.mixinextras.expression.impl.pool.IdentifierPool
import com.llamalad7.mixinextras.utils.Decorations
import org.apache.commons.lang3.mutable.MutableInt
import org.objectweb.asm.Handle
import org.objectweb.asm.Opcodes
import org.objectweb.asm.Type
import org.objectweb.asm.signature.SignatureReader
Expand All @@ -125,6 +128,7 @@ import org.objectweb.asm.tree.FieldInsnNode
import org.objectweb.asm.tree.IincInsnNode
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.IntInsnNode
import org.objectweb.asm.tree.InvokeDynamicInsnNode
import org.objectweb.asm.tree.LdcInsnNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
Expand All @@ -142,22 +146,25 @@ object MEExpressionCompletionUtil {
private val TYPE_PATTERN = PlatformPatterns.psiElement()
.inside(MEStatement::class.java)
.validType()
private val AFTER_END_EXPRESSION_PATTERN = PlatformPatterns.psiElement().afterLeaf(
PlatformPatterns.psiElement().withElementType(
TokenSet.create(
MEExpressionTypes.TOKEN_IDENTIFIER,
MEExpressionTypes.TOKEN_WILDCARD,
MEExpressionTypes.TOKEN_RIGHT_PAREN,
MEExpressionTypes.TOKEN_RIGHT_BRACKET,
MEExpressionTypes.TOKEN_RIGHT_BRACE,
MEExpressionTypes.TOKEN_BOOL_LIT,
MEExpressionTypes.TOKEN_CLASS,
MEExpressionTypes.TOKEN_INT_LIT,
MEExpressionTypes.TOKEN_DEC_LIT,
MEExpressionTypes.TOKEN_NULL_LIT,
MEExpressionTypes.TOKEN_STRING_TERMINATOR,
private val AFTER_END_EXPRESSION_PATTERN = StandardPatterns.or(
PlatformPatterns.psiElement().afterLeaf(
PlatformPatterns.psiElement().withElementType(
TokenSet.create(
MEExpressionTypes.TOKEN_IDENTIFIER,
MEExpressionTypes.TOKEN_WILDCARD,
MEExpressionTypes.TOKEN_RIGHT_PAREN,
MEExpressionTypes.TOKEN_RIGHT_BRACKET,
MEExpressionTypes.TOKEN_RIGHT_BRACE,
MEExpressionTypes.TOKEN_BOOL_LIT,
MEExpressionTypes.TOKEN_CLASS,
MEExpressionTypes.TOKEN_INT_LIT,
MEExpressionTypes.TOKEN_DEC_LIT,
MEExpressionTypes.TOKEN_NULL_LIT,
MEExpressionTypes.TOKEN_STRING_TERMINATOR,
)
)
)
),
PlatformPatterns.psiElement().afterLeaf(PlatformPatterns.psiElement().withText("new").afterLeaf("::")),
)

val STATEMENT_KEYWORD_PLACE = PlatformPatterns.psiElement().afterLeaf(
Expand All @@ -167,6 +174,7 @@ object MEExpressionCompletionUtil {
NORMAL_ELEMENT,
StandardPatterns.not(AFTER_END_EXPRESSION_PATTERN),
StandardPatterns.not(PlatformPatterns.psiElement().afterLeaf(".")),
StandardPatterns.not(PlatformPatterns.psiElement().afterLeaf("::")),
)
val CLASS_PLACE = StandardPatterns.and(
NORMAL_ELEMENT,
Expand All @@ -180,6 +188,10 @@ object MEExpressionCompletionUtil {
NORMAL_ELEMENT,
AFTER_END_EXPRESSION_PATTERN,
)
val METHOD_REFERENCE_PLACE = StandardPatterns.and(
NORMAL_ELEMENT,
PlatformPatterns.psiElement().afterLeaf("::"),
)
val STRING_LITERAL_PLACE = PlatformPatterns.psiElement().withElementType(
TokenSet.create(MEExpressionTypes.TOKEN_STRING, MEExpressionTypes.TOKEN_STRING_TERMINATOR)
)
Expand All @@ -204,6 +216,23 @@ object MEExpressionCompletionUtil {
}
}

private val COLON_COLON_NEW_TAIL = object : TailType() {
override fun processTail(editor: Editor, tailOffset: Int): Int {
editor.document.insertString(tailOffset, "::new")
return moveCaret(editor, tailOffset, 5)
}

override fun isApplicable(context: InsertionContext): Boolean {
val chars = context.document.charsSequence
val colonColonOffset = CharArrayUtil.shiftForward(chars, context.tailOffset, " \n\t")
if (!CharArrayUtil.regionMatches(chars, colonColonOffset, "::")) {
return true
}
val newOffset = CharArrayUtil.shiftForward(chars, colonColonOffset + 2, " \n\t")
return !CharArrayUtil.regionMatches(chars, newOffset, "new")
}
}

fun getStringCompletions(project: Project, contextElement: PsiElement): List<LookupElement> {
val expressionAnnotation = contextElement.findMultiInjectionHost()?.parentOfType<PsiAnnotation>()
?: return emptyList()
Expand Down Expand Up @@ -548,6 +577,22 @@ object MEExpressionCompletionUtil {
super.visitStaticMethodCallExpression(o)
}

override fun visitBoundMethodReferenceExpression(o: MEBoundMethodReferenceExpression) {
val name = o.memberName
if (name != null && !name.isWildcard && !pool.memberExists(name.text)) {
unresolvedNames += name
}
super.visitBoundMethodReferenceExpression(o)
}

override fun visitFreeMethodReferenceExpression(o: MEFreeMethodReferenceExpression) {
val name = o.memberName
if (name != null && !name.isWildcard && !pool.memberExists(name.text)) {
unresolvedNames += name
}
super.visitFreeMethodReferenceExpression(o)
}

override fun visitMemberAccessExpression(o: MEMemberAccessExpression) {
val name = o.memberName
if (!name.isWildcard && !pool.memberExists(name.text)) {
Expand Down Expand Up @@ -813,6 +858,32 @@ object MEExpressionCompletionUtil {
}
}
}
is InvokeDynamicInsnNode -> {
if (insn.bsm.owner == "java/lang/invoke/LambdaMetafactory") {
val handle = insn.bsmArgs.getOrNull(1) as? Handle ?: return emptyList()
val definitionValue = "method = \"L${handle.owner};${handle.name}${handle.desc}\""
if (handle.tag !in Opcodes.H_INVOKEVIRTUAL..Opcodes.H_INVOKEINTERFACE) {
return emptyList()
}
if (handle.tag == Opcodes.H_NEWINVOKESPECIAL) {
return listOf(
createTypeLookup(Type.getObjectType(handle.owner))
.withTailText("::new")
.withTail(COLON_COLON_NEW_TAIL)
.createEliminable("constructorRef ${handle.owner}")
)
} else {
return listOf(
LookupElementBuilder.create(handle.name.toValidIdentifier())
.withIcon(PlatformIcons.METHOD_ICON)
.withPresentableText(handle.owner.substringAfterLast('/') + "." + insn.name)
.withTypeText(Type.getReturnType(insn.desc).presentableName())
.withDefinitionAndFold(insn.name.toValidIdentifier(), "method", definitionValue)
.createEliminable("methodRef ${handle.owner}.${handle.name}${handle.desc}")
)
}
}
}
}

return emptyList()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class MEExpressionSyntaxHighlighter : SyntaxHighlighterBase() {
"MEEXPRESSION_DOT",
DefaultLanguageHighlighterColors.DOT
)
val METHOD_REFERENCE = createTextAttributesKey(
"MEEXPRESSION_METHOD_REFERENCE",
DefaultLanguageHighlighterColors.DOT
)
val COMMA = createTextAttributesKey(
"MEEXPRESSION_COMMA",
DefaultLanguageHighlighterColors.COMMA
Expand Down Expand Up @@ -129,6 +133,7 @@ class MEExpressionSyntaxHighlighter : SyntaxHighlighterBase() {
val BRACKETS_KEYS = arrayOf(BRACKETS)
val BRACES_KEYS = arrayOf(BRACES)
val DOT_KEYS = arrayOf(DOT)
val METHOD_REFERENCE_KEYS = arrayOf(METHOD_REFERENCE)
val COMMA_KEYS = arrayOf(COMMA)
val CAPTURE_KEYS = arrayOf(CAPTURE)
val WILDCARD_KEYS = arrayOf(WILDCARD)
Expand Down Expand Up @@ -168,6 +173,9 @@ class MEExpressionSyntaxHighlighter : SyntaxHighlighterBase() {
if (tokenType == MEExpressionTypes.TOKEN_DOT) {
return DOT_KEYS
}
if (tokenType == MEExpressionTypes.TOKEN_METHOD_REF) {
return METHOD_REFERENCE_KEYS
}
if (tokenType == MEExpressionTypes.TOKEN_COMMA) {
return COMMA_KEYS
}
Expand Down
Loading

0 comments on commit 8770366

Please sign in to comment.