2020
2121package com.demonwav.mcdev.platform.mixin.handlers.injectionPoint
2222
23+ import com.demonwav.mcdev.platform.mixin.handlers.MixinAnnotationHandler
2324import com.demonwav.mcdev.platform.mixin.reference.MixinSelector
25+ import com.demonwav.mcdev.platform.mixin.util.MethodTargetMember
2426import com.demonwav.mcdev.util.constantValue
2527import com.demonwav.mcdev.util.createLiteralExpression
2628import com.demonwav.mcdev.util.descriptor
29+ import com.demonwav.mcdev.util.enumValueOfOrNull
2730import com.demonwav.mcdev.util.ifNotBlank
31+ import com.demonwav.mcdev.util.mapToArray
32+ import com.demonwav.mcdev.util.toTypedArray
2833import com.intellij.codeInsight.lookup.LookupElementBuilder
2934import com.intellij.openapi.editor.Editor
3035import com.intellij.openapi.project.Project
36+ import com.intellij.openapi.util.text.StringUtil
3137import com.intellij.psi.CommonClassNames
3238import com.intellij.psi.JavaPsiFacade
3339import com.intellij.psi.JavaTokenType
@@ -43,9 +49,11 @@ import com.intellij.psi.PsiLiteral
4349import com.intellij.psi.PsiLiteralExpression
4450import com.intellij.psi.PsiSwitchLabelStatementBase
4551import com.intellij.psi.util.PsiUtil
52+ import com.intellij.util.ArrayUtilRt
4653import java.util.Locale
4754import org.objectweb.asm.Opcodes
4855import org.objectweb.asm.Type
56+ import org.objectweb.asm.tree.AbstractInsnNode
4957import org.objectweb.asm.tree.ClassNode
5058import org.objectweb.asm.tree.FrameNode
5159import org.objectweb.asm.tree.InsnNode
@@ -57,10 +65,71 @@ import org.objectweb.asm.tree.MethodNode
5765import org.objectweb.asm.tree.TypeInsnNode
5866
5967class ConstantInjectionPoint : InjectionPoint <PsiElement >() {
68+ companion object {
69+ private val ARGS_KEYS = arrayOf(
70+ " nullValue=true" ,
71+ " intValue" ,
72+ " floatValue" ,
73+ " longValue" ,
74+ " doubleValue" ,
75+ " stringValue" ,
76+ " classValue" ,
77+ " expandZeroConditions"
78+ )
79+ }
80+
6081 override fun onCompleted (editor : Editor , reference : PsiLiteral ) {
6182 completeExtraStringAtAttribute(editor, reference, " args" )
6283 }
6384
85+ override fun getArgsKeys (at : PsiAnnotation ) = ARGS_KEYS
86+
87+ override fun getArgsValues (at : PsiAnnotation , key : String ): Array <Any > {
88+ fun collectTargets (constantToCompletion : (Any ) -> Any? ): Array <Any > {
89+ val injectorAnnotation = AtResolver .findInjectorAnnotation(at) ? : return ArrayUtilRt .EMPTY_OBJECT_ARRAY
90+ val handler = injectorAnnotation.qualifiedName
91+ ?.let { MixinAnnotationHandler .forMixinAnnotation(it, at.project) }
92+ ? : return ArrayUtilRt .EMPTY_OBJECT_ARRAY
93+
94+ val expandConditions = parseExpandConditions(AtResolver .getArgs(at))
95+
96+ return handler.resolveTarget(injectorAnnotation)
97+ .asSequence()
98+ .filterIsInstance<MethodTargetMember >()
99+ .flatMap { target ->
100+ target.classAndMethod.method.instructions?.let { insns ->
101+ Iterable { insns.iterator() }.asSequence()
102+ .mapNotNull { it.computeConstantValue(expandConditions) }
103+ .mapNotNull(constantToCompletion)
104+ } ? : emptySequence()
105+ }
106+ .toTypedArray()
107+ }
108+
109+ return when (key) {
110+ " expandZeroConditions" -> ExpandCondition .values().mapToArray { it.name.lowercase(Locale .ROOT ) }
111+ " intValue" -> collectTargets { cst -> cst.takeIf { it is Int } }
112+ " floatValue" -> collectTargets { cst -> cst.takeIf { it is Float } }
113+ " longValue" -> collectTargets { cst -> cst.takeIf { it is Long } }
114+ " doubleValue" -> collectTargets { cst -> cst.takeIf { it is Double } }
115+ " stringValue" -> collectTargets { cst ->
116+ (cst as ? String )?.let { str ->
117+ val escapedStr = StringUtil .escapeStringCharacters(str)
118+ when {
119+ str.isEmpty() -> null
120+ escapedStr.trim() != escapedStr -> LookupElementBuilder .create(escapedStr)
121+ .withPresentableText(" \" ${escapedStr} \" " )
122+ else -> escapedStr
123+ }
124+ }
125+ }
126+ " classValue" -> collectTargets { cst -> (cst as ? Type )?.internalName }
127+ else -> ArrayUtilRt .EMPTY_OBJECT_ARRAY
128+ }
129+ }
130+
131+ override fun isArgValueList (at : PsiAnnotation , key : String ) = key == " expandZeroConditions"
132+
64133 fun getConstantInfo (at : PsiAnnotation ): ConstantInfo ? {
65134 val args = AtResolver .getArgs(at)
66135 val nullValue = args[" nullValue" ]?.let (java.lang.Boolean ::parseBoolean) ? : false
@@ -88,19 +157,15 @@ class ConstantInjectionPoint : InjectionPoint<PsiElement>() {
88157 intValue ? : floatValue ? : longValue ? : doubleValue ? : stringValue ? : classValue!!
89158 }
90159
91- val expandConditions = args[" expandZeroConditions" ]
160+ return ConstantInfo (constant, parseExpandConditions(args))
161+ }
162+
163+ private fun parseExpandConditions (args : Map <String , String >): Set <ExpandCondition > {
164+ return args[" expandZeroConditions" ]
92165 ?.replace(" " , " " )
93166 ?.split(' ,' )
94- ?.mapNotNull {
95- try {
96- ExpandCondition .valueOf(it.uppercase(Locale .ENGLISH ))
97- } catch (e: IllegalArgumentException ) {
98- null
99- }
100- }
167+ ?.mapNotNull { enumValueOfOrNull<ExpandCondition >(it.uppercase(Locale .ROOT )) }
101168 ?.toSet() ? : emptySet()
102-
103- return ConstantInfo (constant, expandConditions)
104169 }
105170
106171 private fun Boolean.toInt (): Int {
@@ -248,59 +313,10 @@ class ConstantInjectionPoint : InjectionPoint<PsiElement>() {
248313 ) : CollectVisitor<PsiElement>(mode) {
249314 override fun accept (methodNode : MethodNode ) {
250315 methodNode.instructions?.iterator()?.forEachRemaining { insn ->
251- val constant = when (insn) {
252- is InsnNode -> when (insn.opcode) {
253- in Opcodes .ICONST_M1 .. Opcodes .ICONST_5 -> insn.opcode - Opcodes .ICONST_0
254- Opcodes .LCONST_0 -> 0L
255- Opcodes .LCONST_1 -> 1L
256- Opcodes .FCONST_0 -> 0.0f
257- Opcodes .FCONST_1 -> 1.0f
258- Opcodes .FCONST_2 -> 2.0f
259- Opcodes .DCONST_0 -> 0.0
260- Opcodes .DCONST_1 -> 1.0
261- Opcodes .ACONST_NULL -> null
262- else -> return @forEachRemaining
263- }
264-
265- is IntInsnNode -> when (insn.opcode) {
266- Opcodes .BIPUSH , Opcodes .SIPUSH -> insn.operand
267- else -> return @forEachRemaining
268- }
269-
270- is LdcInsnNode -> insn.cst
271- is JumpInsnNode -> {
272- if (constantInfo == null || ! constantInfo.expandConditions.any { insn.opcode in it.opcodes }) {
273- return @forEachRemaining
274- }
275- var lastInsn = insn.previous
276- while (lastInsn != null && (lastInsn is LabelNode || lastInsn is FrameNode )) {
277- lastInsn = lastInsn.previous
278- }
279- if (lastInsn != null ) {
280- val lastOpcode = lastInsn.opcode
281- if (lastOpcode == Opcodes .LCMP ||
282- lastOpcode == Opcodes .FCMPL ||
283- lastOpcode == Opcodes .FCMPG ||
284- lastOpcode == Opcodes .DCMPL ||
285- lastOpcode == Opcodes .DCMPG
286- ) {
287- return @forEachRemaining
288- }
289- }
290- 0
291- }
292-
293- is TypeInsnNode -> {
294- if (insn.opcode < Opcodes .CHECKCAST ) {
295- // Don't treat NEW and ANEWARRAY as constants
296- // Matches Mixin's handling
297- return @forEachRemaining
298- }
299- Type .getObjectType(insn.desc)
300- }
301-
302- else -> return @forEachRemaining
303- }
316+ val constant = (
317+ insn.computeConstantValue(constantInfo?.expandConditions ? : emptySet())
318+ ? : return @forEachRemaining
319+ ).let { if (it is NullSentinel ) null else it }
304320
305321 if (constantInfo != null && constant != constantInfo.constant) {
306322 return @forEachRemaining
@@ -344,3 +360,61 @@ class ConstantInjectionPoint : InjectionPoint<PsiElement>() {
344360 }
345361 }
346362}
363+
364+ private object NullSentinel
365+
366+ private fun AbstractInsnNode.computeConstantValue (expandConditions : Set <ConstantInjectionPoint .ExpandCondition >): Any? {
367+ return when (this ) {
368+ is InsnNode -> when (opcode) {
369+ in Opcodes .ICONST_M1 .. Opcodes .ICONST_5 -> opcode - Opcodes .ICONST_0
370+ Opcodes .LCONST_0 -> 0L
371+ Opcodes .LCONST_1 -> 1L
372+ Opcodes .FCONST_0 -> 0.0f
373+ Opcodes .FCONST_1 -> 1.0f
374+ Opcodes .FCONST_2 -> 2.0f
375+ Opcodes .DCONST_0 -> 0.0
376+ Opcodes .DCONST_1 -> 1.0
377+ Opcodes .ACONST_NULL -> NullSentinel
378+ else -> null
379+ }
380+
381+ is IntInsnNode -> when (opcode) {
382+ Opcodes .BIPUSH , Opcodes .SIPUSH -> operand
383+ else -> null
384+ }
385+
386+ is LdcInsnNode -> cst
387+ is JumpInsnNode -> {
388+ if (expandConditions.none { opcode in it.opcodes }) {
389+ return null
390+ }
391+ var lastInsn = previous
392+ while (lastInsn != null && (lastInsn is LabelNode || lastInsn is FrameNode )) {
393+ lastInsn = lastInsn.previous
394+ }
395+ if (lastInsn != null ) {
396+ val lastOpcode = lastInsn.opcode
397+ if (lastOpcode == Opcodes .LCMP ||
398+ lastOpcode == Opcodes .FCMPL ||
399+ lastOpcode == Opcodes .FCMPG ||
400+ lastOpcode == Opcodes .DCMPL ||
401+ lastOpcode == Opcodes .DCMPG
402+ ) {
403+ return null
404+ }
405+ }
406+ 0
407+ }
408+
409+ is TypeInsnNode -> {
410+ if (opcode < Opcodes .CHECKCAST ) {
411+ // Don't treat NEW and ANEWARRAY as constants
412+ // Matches Mixin's handling
413+ return null
414+ }
415+ Type .getObjectType(desc)
416+ }
417+
418+ else -> null
419+ }
420+ }
0 commit comments