27
27
import static com .oracle .graal .pointsto .heap .ImageLayerLoader .get ;
28
28
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .FACTORY_TAG ;
29
29
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .GENERATED_SERIALIZATION_TAG ;
30
+ import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .HOLDER_CLASS_TAG ;
31
+ import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .LAMBDA_TYPE_TAG ;
30
32
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .RAW_DECLARING_CLASS_TAG ;
31
33
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .RAW_TARGET_CONSTRUCTOR_CLASS_TAG ;
32
34
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .TARGET_CONSTRUCTOR_TAG ;
35
37
import static com .oracle .graal .pointsto .heap .ImageLayerSnapshotUtil .WRAPPED_TYPE_TAG ;
36
38
37
39
import java .lang .reflect .Constructor ;
40
+ import java .lang .reflect .Executable ;
41
+ import java .util .Arrays ;
42
+ import java .util .Objects ;
43
+ import java .util .Optional ;
44
+ import java .util .stream .Stream ;
38
45
39
46
import org .graalvm .collections .EconomicMap ;
47
+ import org .graalvm .nativeimage .AnnotationAccess ;
40
48
41
49
import com .oracle .graal .pointsto .heap .ImageLayerLoader ;
42
50
import com .oracle .graal .pointsto .heap .ImageLayerLoaderHelper ;
51
+ import com .oracle .graal .pointsto .infrastructure .OriginalMethodProvider ;
52
+ import com .oracle .graal .pointsto .meta .AnalysisMethod ;
53
+ import com .oracle .graal .pointsto .meta .AnalysisType ;
54
+ import com .oracle .graal .pointsto .meta .AnalysisUniverse ;
55
+ import com .oracle .svm .core .annotate .TargetClass ;
56
+ import com .oracle .svm .core .bootstrap .BootstrapMethodConfiguration ;
43
57
import com .oracle .svm .core .reflect .serialize .SerializationSupport ;
58
+ import com .oracle .svm .core .util .VMError ;
44
59
import com .oracle .svm .hosted .code .FactoryMethodSupport ;
45
60
import com .oracle .svm .hosted .reflect .serialize .SerializationFeature ;
61
+ import com .oracle .svm .hosted .substitute .SubstitutionMethod ;
46
62
import com .oracle .svm .util .ReflectionUtil ;
47
63
64
+ import jdk .graal .compiler .serviceprovider .JavaVersionUtil ;
48
65
import jdk .internal .reflect .ReflectionFactory ;
66
+ import jdk .vm .ci .meta .ConstantPool ;
67
+ import jdk .vm .ci .meta .JavaConstant ;
49
68
50
69
public class SVMImageLayerLoaderHelper extends ImageLayerLoaderHelper {
70
+ private static final Class <?> DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS = ReflectionUtil .lookupClass (false , "java.lang.invoke.DirectMethodHandle$StaticAccessor" );
71
+ private static final Class <?> DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS = ReflectionUtil .lookupClass (false , "java.lang.invoke.DirectMethodHandle$Constructor" );
72
+ private static final String STATIC_BASE_FIELD_NAME = "staticBase" ;
73
+ private static final String INSTANCE_CLASS_FIELD_NAME = "instanceClass" ;
74
+ private static final int INVOKE_DYNAMIC_OPCODE = 186 ;
75
+
51
76
public SVMImageLayerLoaderHelper (ImageLayerLoader imageLayerLoader ) {
52
77
super (imageLayerLoader );
53
78
}
@@ -70,11 +95,77 @@ protected boolean loadType(EconomicMap<String, Object> typeData, int tid) {
70
95
Class <?> constructorAccessor = serializationSupport .getSerializationConstructorAccessor (rawDeclaringClass , rawTargetConstructorClass ).getClass ();
71
96
imageLayerLoader .getMetaAccess ().lookupJavaType (constructorAccessor );
72
97
return true ;
98
+ } else if (wrappedType .equals (LAMBDA_TYPE_TAG )) {
99
+ String holderClassName = get (typeData , HOLDER_CLASS_TAG );
100
+ Class <?> holderClass = imageLayerLoader .lookupClass (false , holderClassName );
101
+ loadLambdaTypes (holderClass );
102
+ return true ;
73
103
}
74
104
75
105
return super .loadType (typeData , tid );
76
106
}
77
107
108
+ /**
109
+ * The constant pool index of bootstrap method is not stable in different JVM instances, so the
110
+ * only solution is to load all lambda types of the given holder class.
111
+ */
112
+ private void loadLambdaTypes (Class <?> holderClass ) {
113
+ AnalysisUniverse universe = imageLayerLoader .getUniverse ();
114
+ AnalysisType type = universe .getBigbang ().getMetaAccess ().lookupJavaType (holderClass );
115
+ boolean isSubstitution = AnnotationAccess .isAnnotationPresent (holderClass , TargetClass .class );
116
+ ConstantPool constantPool = getConstantPool (type , isSubstitution );
117
+ int index = JavaVersionUtil .JAVA_SPEC > 21 ? 0 : -1 ;
118
+ ConstantPool .BootstrapMethodInvocation bootstrap ;
119
+ while ((bootstrap = getBootstrap (constantPool , index )) != null ) {
120
+ if (BootstrapMethodConfiguration .singleton ().isMetafactory (OriginalMethodProvider .getJavaMethod (bootstrap .getMethod ()))) {
121
+ constantPool .loadReferencedType (index , INVOKE_DYNAMIC_OPCODE );
122
+ JavaConstant test = constantPool .lookupAppendix (index , INVOKE_DYNAMIC_OPCODE );
123
+ Object appendix = universe .getSnippetReflection ().asObject (Object .class , test );
124
+
125
+ Class <?> potentialLambdaClass ;
126
+ if (DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS .isInstance (appendix )) {
127
+ potentialLambdaClass = ReflectionUtil .readField (DIRECT_METHOD_HANDLE_STATIC_ACCESSOR_CLASS , STATIC_BASE_FIELD_NAME , appendix );
128
+ } else if (DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS .isInstance (appendix )) {
129
+ potentialLambdaClass = ReflectionUtil .readField (DIRECT_METHOD_HANDLE_CONSTRUCTOR_CLASS , INSTANCE_CLASS_FIELD_NAME , appendix );
130
+ } else {
131
+ throw VMError .shouldNotReachHere ("Unexpected appendix %s" , appendix );
132
+ }
133
+ universe .getBigbang ().getMetaAccess ().lookupJavaType (potentialLambdaClass );
134
+ }
135
+ if (JavaVersionUtil .JAVA_SPEC > 21 ) {
136
+ index ++;
137
+ } else {
138
+ index --;
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * A default and substitution class have two different constant pools. The constant pool can
145
+ * only be fetched through the methods of the class, so we iterate over the methods and the
146
+ * constructors and take the first constant pool that matches the current class.
147
+ */
148
+ private static ConstantPool getConstantPool (AnalysisType type , boolean isSubstitution ) {
149
+ Stream <AnalysisMethod > candidates = Stream .concat (Arrays .stream (type .getDeclaredMethods (false )), Arrays .stream (type .getDeclaredConstructors (false )));
150
+ Optional <ConstantPool > cp = candidates .map (method -> {
151
+ Executable javaMethod = method .getJavaMethod ();
152
+ if (((javaMethod != null && AnnotationAccess .isAnnotationPresent (javaMethod .getDeclaringClass (), TargetClass .class )) || (method .wrapped instanceof SubstitutionMethod )) == isSubstitution ) {
153
+ return method .getConstantPool ();
154
+ }
155
+ return null ;
156
+ }).filter (Objects ::nonNull ).findAny ();
157
+ assert cp .isPresent () : String .format ("No constant pool was found in the %s class." , isSubstitution ? "substitution" : "default" );
158
+ return cp .get ();
159
+ }
160
+
161
+ private static ConstantPool .BootstrapMethodInvocation getBootstrap (ConstantPool constantPool , int index ) {
162
+ try {
163
+ return constantPool .lookupBootstrapMethodInvocation (index , INVOKE_DYNAMIC_OPCODE );
164
+ } catch (IndexOutOfBoundsException e ) {
165
+ return null ;
166
+ }
167
+ }
168
+
78
169
@ Override
79
170
protected boolean loadMethod (EconomicMap <String , Object > methodData , int mid ) {
80
171
String wrappedMethod = get (methodData , WRAPPED_METHOD_TAG );
0 commit comments