1
1
/*
2
- * Copyright 2017-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
2
+ * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3
3
*/
4
4
5
5
@file:Suppress(" EXPERIMENTAL_FEATURE_WARNING" )
@@ -24,6 +24,10 @@ private const val JUCA_PKG = "java/util/concurrent/atomic"
24
24
private const val JLI_PKG = " java/lang/invoke"
25
25
private const val ATOMIC = " atomic"
26
26
27
+ private const val TRACE = " Trace"
28
+ private const val TRACE_BASE = " TraceBase"
29
+ private const val TRACE_FORMAT = " TraceFormat"
30
+
27
31
private val INT_ARRAY_TYPE = getType(" [I" )
28
32
private val LONG_ARRAY_TYPE = getType(" [J" )
29
33
private val BOOLEAN_ARRAY_TYPE = getType(" [Z" )
@@ -75,6 +79,17 @@ private const val GET_VALUE = "getValue"
75
79
private const val SET_VALUE = " setValue"
76
80
77
81
private const val AFU_CLS = " $AFU_PKG /AtomicFU"
82
+ private const val TRACE_KT = " $AFU_PKG /TraceKt"
83
+ private const val TRACE_BASE_CLS = " $AFU_PKG /$TRACE_BASE "
84
+
85
+ private val TRACE_BASE_TYPE = getObjectType(TRACE_BASE_CLS )
86
+
87
+ private val TRACE_APPEND = MethodId (TRACE_BASE_CLS , " append" , getMethodDescriptor(VOID_TYPE , STRING_TYPE ), INVOKEVIRTUAL )
88
+ private val TRACE_DEFAULT_ARGS = " I${OBJECT_TYPE .descriptor} "
89
+ private const val DEFAULT = " \$ default"
90
+
91
+ private val TRACE_FACTORY = MethodId (TRACE_KT , TRACE , " (IL$AFU_PKG /$TRACE_FORMAT ;)L$AFU_PKG /$TRACE_BASE ;" , INVOKESTATIC )
92
+ private val TRACE_PARTIAL_ARGS_FACTORY = MethodId (TRACE_KT , " $TRACE$DEFAULT " , " (IL$AFU_PKG /$TRACE_FORMAT ;$TRACE_DEFAULT_ARGS )L$AFU_PKG /$TRACE_BASE ;" , INVOKESTATIC )
78
93
79
94
private val FACTORIES : Set <MethodId > = setOf (
80
95
MethodId (AFU_CLS , ATOMIC , " (Ljava/lang/Object;)L$AFU_PKG /AtomicRef;" , INVOKESTATIC ),
@@ -86,7 +101,17 @@ private val FACTORIES: Set<MethodId> = setOf(
86
101
MethodId (" $AFU_PKG /AtomicLongArray" , " <init>" , " (I)V" , INVOKESPECIAL ),
87
102
MethodId (" $AFU_PKG /AtomicBooleanArray" , " <init>" , " (I)V" , INVOKESPECIAL ),
88
103
MethodId (" $AFU_PKG /AtomicArray" , " <init>" , " (I)V" , INVOKESPECIAL ),
89
- MethodId (" $AFU_PKG /AtomicFU_commonKt" , " atomicArrayOfNulls" , " (I)L$AFU_PKG /AtomicArray;" , INVOKESTATIC )
104
+ MethodId (" $AFU_PKG /AtomicFU_commonKt" , " atomicArrayOfNulls" , " (I)L$AFU_PKG /AtomicArray;" , INVOKESTATIC ),
105
+
106
+ MethodId (AFU_CLS , ATOMIC , " (Ljava/lang/Object;L$TRACE_BASE_CLS ;)L$AFU_PKG /AtomicRef;" , INVOKESTATIC ),
107
+ MethodId (AFU_CLS , ATOMIC , " (IL$TRACE_BASE_CLS ;)L$AFU_PKG /AtomicInt;" , INVOKESTATIC ),
108
+ MethodId (AFU_CLS , ATOMIC , " (JL$TRACE_BASE_CLS ;)L$AFU_PKG /AtomicLong;" , INVOKESTATIC ),
109
+ MethodId (AFU_CLS , ATOMIC , " (ZL$TRACE_BASE_CLS ;)L$AFU_PKG /AtomicBoolean;" , INVOKESTATIC ),
110
+
111
+ MethodId (AFU_CLS , ATOMIC + DEFAULT , " (Ljava/lang/Object;L$TRACE_BASE_CLS ;$TRACE_DEFAULT_ARGS )L$AFU_PKG /AtomicRef;" , INVOKESTATIC ),
112
+ MethodId (AFU_CLS , ATOMIC + DEFAULT , " (IL$TRACE_BASE_CLS ;$TRACE_DEFAULT_ARGS )L$AFU_PKG /AtomicInt;" , INVOKESTATIC ),
113
+ MethodId (AFU_CLS , ATOMIC + DEFAULT , " (JL$TRACE_BASE_CLS ;$TRACE_DEFAULT_ARGS )L$AFU_PKG /AtomicLong;" , INVOKESTATIC ),
114
+ MethodId (AFU_CLS , ATOMIC + DEFAULT , " (ZL$TRACE_BASE_CLS ;$TRACE_DEFAULT_ARGS )L$AFU_PKG /AtomicBoolean;" , INVOKESTATIC )
90
115
)
91
116
92
117
private operator fun Int.contains (bit : Int ) = this and bit != 0
@@ -155,6 +180,8 @@ class AtomicFUTransformer(
155
180
156
181
private val fields = mutableMapOf<FieldId , FieldInfo >()
157
182
private val accessors = mutableMapOf<MethodId , FieldInfo >()
183
+ private val traceFields = mutableSetOf<FieldId >()
184
+ private val traceAccessors = mutableSetOf<MethodId >()
158
185
private val removeMethods = mutableSetOf<MethodId >()
159
186
160
187
override fun transform () {
@@ -329,16 +356,20 @@ class AtomicFUTransformer(
329
356
val fieldType = getType(fi.desc)
330
357
val accessorMethod = MethodId (className, name, desc, accessToInvokeOpcode(access))
331
358
info(" $field accessor $name found" )
332
- val fieldInfo = registerField(field, fieldType, isStatic)
333
- fieldInfo.accessors + = accessorMethod
334
- accessors[accessorMethod] = fieldInfo
359
+ if (fieldType == TRACE_BASE_TYPE ) {
360
+ traceAccessors.add(accessorMethod)
361
+ } else {
362
+ val fieldInfo = registerField(field, fieldType, isStatic)
363
+ fieldInfo.accessors + = accessorMethod
364
+ accessors[accessorMethod] = fieldInfo
365
+ }
335
366
}
336
367
}
337
368
}
338
369
339
370
// returns a type on which this is a potential accessor
340
371
private fun getPotentialAccessorType (access : Int , className : String , methodType : Type ): Type ? {
341
- if (methodType.returnType !in AFU_TYPES ) return null
372
+ if (methodType.returnType !in AFU_TYPES && methodType.returnType != TRACE_BASE_TYPE ) return null
342
373
return if (access and ACC_STATIC != 0 ) {
343
374
if (access and ACC_FINAL != 0 && methodType.argumentTypes.isEmpty()) {
344
375
// accessor for top-level atomic
@@ -423,6 +454,12 @@ class AtomicFUTransformer(
423
454
transformed = true
424
455
return fv
425
456
}
457
+ // skip trace field
458
+ if (fieldType == TRACE_BASE_TYPE ) {
459
+ traceFields + = FieldId (className, name, desc)
460
+ transformed = true
461
+ return null
462
+ }
426
463
return super .visitField(access, name, desc, signature, value)
427
464
}
428
465
@@ -493,11 +530,11 @@ class AtomicFUTransformer(
493
530
exceptions : Array <out String >?
494
531
): MethodVisitor ? {
495
532
val methodId = MethodId (className, name, desc, accessToInvokeOpcode(access))
496
- if (methodId in accessors || methodId in removeMethods) {
533
+ if (methodId in accessors || methodId in traceAccessors || methodId in removeMethods) {
497
534
// drop and skip the methods that were found in Phase 1
498
535
// todo: should remove those methods from kotlin metadata, too
499
536
transformed = true
500
- return null
537
+ return null // drop accessor
501
538
}
502
539
val sourceInfo = SourceInfo (methodId, source)
503
540
val superMV = if (name == " <clinit>" && desc == " ()V" ) {
@@ -532,8 +569,8 @@ class AtomicFUTransformer(
532
569
// remove unused methods from metadata
533
570
metadata?.let {
534
571
val mt = MetadataTransformer (
535
- removeFields = fields.keys,
536
- removeMethods = accessors.keys + removeMethods
572
+ removeFields = fields.keys + traceFields ,
573
+ removeMethods = accessors.keys + traceAccessors + removeMethods
537
574
)
538
575
if (mt.transformMetadata(it)) transformed = true
539
576
if (cv != null ) it.accept(cv.visitAnnotation(KOTLIN_METADATA_DESC , true ))
@@ -946,29 +983,29 @@ class AtomicFUTransformer(
946
983
947
984
private fun putPrimitiveTypeWrapper (
948
985
factoryInsn : MethodInsnNode ,
986
+ initStart : AbstractInsnNode ,
949
987
f : FieldInfo ,
950
988
next : FieldInsnNode
951
989
): AbstractInsnNode ? {
952
990
// generate wrapper class for static fields of primitive type
953
991
val factoryArg = getMethodType(factoryInsn.desc).argumentTypes[0 ]
954
992
generateRefVolatileClass(f, factoryArg)
955
- val firstInitInsn = FlowAnalyzer (next).getInitStart()
956
993
// remove calling atomic factory for static field and following putstatic
957
994
val afterPutStatic = next.next
958
995
instructions.remove(factoryInsn)
959
996
instructions.remove(next)
960
- initRefVolatile(f, factoryArg, firstInitInsn , afterPutStatic)
997
+ initRefVolatile(f, factoryArg, initStart , afterPutStatic)
961
998
return afterPutStatic
962
999
}
963
1000
964
1001
private fun putJucaAtomicArray (
965
1002
arrayfactoryInsn : MethodInsnNode ,
1003
+ initStart : AbstractInsnNode ,
966
1004
f : FieldInfo ,
967
1005
next : FieldInsnNode
968
1006
): AbstractInsnNode ? {
969
1007
// replace with invoking j.u.c.a.Atomic*Array constructor
970
1008
val jucaAtomicArrayDesc = f.typeInfo.fuType.descriptor
971
- val initStart = FlowAnalyzer (next).getInitStart().next
972
1009
if (initStart.opcode == NEW ) {
973
1010
// change descriptor of NEW instruction
974
1011
(initStart as TypeInsnNode ).desc = descToName(jucaAtomicArrayDesc)
@@ -992,10 +1029,10 @@ class AtomicFUTransformer(
992
1029
993
1030
private fun putPureVhArray (
994
1031
arrayFactoryInsn : MethodInsnNode ,
1032
+ initStart : AbstractInsnNode ,
995
1033
f : FieldInfo ,
996
1034
next : FieldInsnNode
997
1035
): AbstractInsnNode ? {
998
- val initStart = FlowAnalyzer (next).getInitStart().next
999
1036
if (initStart.opcode == NEW ) {
1000
1037
// remove dup
1001
1038
instructions.remove(initStart.next)
@@ -1015,6 +1052,55 @@ class AtomicFUTransformer(
1015
1052
return next.next
1016
1053
}
1017
1054
1055
+ // erases pushing atomic factory trace arguments
1056
+ // returns the first value argument push
1057
+ private fun eraseTraceInit (atomicFactory : MethodInsnNode , isArrayFactory : Boolean ): AbstractInsnNode {
1058
+ val initStart = FlowAnalyzer (atomicFactory).getInitStart(1 )
1059
+ if (isArrayFactory) return initStart
1060
+ var lastArg = atomicFactory.previous
1061
+ val valueArgInitLast = FlowAnalyzer (atomicFactory).getValueArgInitLast()
1062
+ while (lastArg != valueArgInitLast) {
1063
+ val prev = lastArg.previous
1064
+ instructions.remove(lastArg)
1065
+ lastArg = prev
1066
+ }
1067
+ return initStart
1068
+ }
1069
+
1070
+ private fun eraseTraceInfo (append : AbstractInsnNode ): AbstractInsnNode {
1071
+ // remove append trace instructions: from append invocation up to getfield Trace or accessor to Trace field
1072
+ val afterAppend = append.next
1073
+ var start = append
1074
+ val isGetFieldTrace = { insn: AbstractInsnNode ->
1075
+ insn.opcode == GETFIELD && (start as FieldInsnNode ).desc == getObjectType(TRACE_BASE_CLS ).descriptor }
1076
+ val isTraceAccessor = { insn: AbstractInsnNode ->
1077
+ if (insn is MethodInsnNode ) {
1078
+ val methodId = MethodId (insn.owner, insn.name, insn.desc, insn.opcode)
1079
+ methodId in traceAccessors
1080
+ } else false
1081
+ }
1082
+ while (! (isGetFieldTrace(start) || isTraceAccessor(start))) {
1083
+ start = start.previous
1084
+ }
1085
+ // now start contains Trace getfield insn or Trace accessor
1086
+ if (isTraceAccessor(start)) {
1087
+ instructions.remove(start.previous.previous)
1088
+ instructions.remove(start.previous)
1089
+ } else {
1090
+ instructions.remove(start.previous)
1091
+ }
1092
+ if (start.next is VarInsnNode ) {
1093
+ val v = (start.next as VarInsnNode ).`var `
1094
+ localVariables.removeIf { it.index == v }
1095
+ }
1096
+ while (start != afterAppend) {
1097
+ val next = start.next
1098
+ instructions.remove(start)
1099
+ start = next
1100
+ }
1101
+ return afterAppend
1102
+ }
1103
+
1018
1104
private fun transform (i : AbstractInsnNode ): AbstractInsnNode ? {
1019
1105
when (i) {
1020
1106
is MethodInsnNode -> {
@@ -1026,15 +1112,18 @@ class AtomicFUTransformer(
1026
1112
val fieldId = (next as ? FieldInsnNode )?.checkPutFieldOrPutStatic()
1027
1113
? : abort(" factory $methodId invocation must be followed by putfield" )
1028
1114
val f = fields[fieldId]!!
1115
+ val isArray = AFU_CLASSES [i.owner]?.let { it.originalType.sort == ARRAY } ? : false
1116
+ // erase pushing arguments for trace initialisation
1117
+ val newInitStart = eraseTraceInit(i, isArray)
1029
1118
// in FU mode wrap values of top-level primitive atomics into corresponding *RefVolatile class
1030
1119
if (! vh && f.isStatic && ! f.isArray) {
1031
- return putPrimitiveTypeWrapper(i, f, next)
1120
+ return putPrimitiveTypeWrapper(i, newInitStart, f, next)
1032
1121
}
1033
1122
if (f.isArray) {
1034
1123
return if (vh) {
1035
- putPureVhArray(i, f, next)
1124
+ putPureVhArray(i, newInitStart, f, next)
1036
1125
} else {
1037
- putJucaAtomicArray(i, f, next)
1126
+ putJucaAtomicArray(i, newInitStart, f, next)
1038
1127
}
1039
1128
}
1040
1129
instructions.remove(i)
@@ -1063,10 +1152,30 @@ class AtomicFUTransformer(
1063
1152
transformed = true
1064
1153
return fixupLoadedAtomicVar(f, j)
1065
1154
}
1155
+ methodId == TRACE_FACTORY || methodId == TRACE_PARTIAL_ARGS_FACTORY -> {
1156
+ // remove trace factory and following putfield
1157
+ val argsSize = getMethodType(methodId.desc).argumentTypes.size
1158
+ val putfield = i.next
1159
+ val next = putfield.next
1160
+ val depth = if (i.opcode == INVOKESPECIAL ) 2 else argsSize
1161
+ val initStart = FlowAnalyzer (i.previous).getInitStart(depth).previous
1162
+ var lastArg = i
1163
+ while (lastArg != initStart) {
1164
+ val prev = lastArg.previous
1165
+ instructions.remove(lastArg)
1166
+ lastArg = prev
1167
+ }
1168
+ instructions.remove(initStart) // aload of the parent class
1169
+ instructions.remove(putfield)
1170
+ return next
1171
+ }
1172
+ methodId == TRACE_APPEND -> {
1173
+ return eraseTraceInfo(i)
1174
+ }
1066
1175
methodId in removeMethods -> {
1067
1176
abort(
1068
1177
" invocation of method $methodId on atomic types. " +
1069
- " Make the later method 'inline' to use it" , i
1178
+ " Make the latter method 'inline' to use it" , i
1070
1179
)
1071
1180
}
1072
1181
i.opcode == INVOKEVIRTUAL && i.owner in AFU_CLASSES -> {
0 commit comments