1 /*
  2  * Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package jdk.vm.ci.hotspot;
 24 
 25 import java.util.Set;
 26 import java.util.stream.Collectors;
 27 
 28 import jdk.vm.ci.common.JVMCIError;
 29 
 30 /**
 31  * Access to VM configuration data.
 32  */
 33 public class HotSpotVMConfigAccess {
 34 
 35     /**
 36      * Gets the available configuration data.
 37      */
 38     public HotSpotVMConfigStore getStore() {
 39         return store;
 40     }
 41 
 42     /**
 43      * Gets the address of a C++ symbol.
 44      *
 45      * @param name name of C++ symbol
 46      * @param notPresent if non-null and the symbol is not present then this value is returned
 47      * @return the address of the symbol
 48      * @throws JVMCIError if the symbol is not present and {@code notPresent == null}
 49      */
 50     public long getAddress(String name, Long notPresent) {
 51         Long entry = store.vmAddresses.get(name);
 52         if (entry == null) {
 53             if (notPresent != null) {
 54                 return notPresent;
 55             }
 56             throw missingEntry("address", name, store.vmFlags.keySet());
 57 
 58         }
 59         return entry;
 60     }
 61 
 62     /**
 63      * Gets the address of a C++ symbol.
 64      *
 65      * @param name name of C++ symbol
 66      * @return the address of the symbol
 67      * @throws JVMCIError if the symbol is not present
 68      */
 69     public long getAddress(String name) {
 70         return getAddress(name, null);
 71     }
 72 
 73     /**
 74      * Gets the value of a C++ constant.
 75      *
 76      * @param name name of the constant (e.g., {@code "frame::arg_reg_save_area_bytes"})
 77      * @param type the boxed type to which the constant value will be converted
 78      * @param notPresent if non-null and the constant is not present then this value is returned
 79      * @return the constant value converted to {@code type}
 80      * @throws JVMCIError if the constant is not present and {@code notPresent == null}
 81      */
 82     public <T> T getConstant(String name, Class<T> type, T notPresent) {
 83         Long c = store.vmConstants.get(name);
 84         if (c == null) {
 85             if (notPresent != null) {
 86                 return notPresent;
 87             }
 88             throw missingEntry("constant", name, store.vmConstants.keySet());
 89         }
 90         return type.cast(convertValue(name, type, c, null));
 91     }
 92 
 93     /**
 94      * Gets the value of a C++ constant.
 95      *
 96      * @param name name of the constant (e.g., {@code "frame::arg_reg_save_area_bytes"})
 97      * @param type the boxed type to which the constant value will be converted
 98      * @return the constant value converted to {@code type}
 99      * @throws JVMCIError if the constant is not present
100      */
101     public <T> T getConstant(String name, Class<T> type) {
102         return getConstant(name, type, null);
103     }
104 
105     /**
106      * Gets the offset of a non-static C++ field.
107      *
108      * @param name fully qualified name of the field
109      * @param type the boxed type to which the offset value will be converted (must be
110      *            {@link Integer} or {@link Long})
111      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
112      * @param notPresent if non-null and the field is not present then this value is returned
113      * @return the offset in bytes of the requested field
114      * @throws JVMCIError if the field is static or not present and {@code notPresent} is null
115      */
116     public <T> T getFieldOffset(String name, Class<T> type, String cppType, T notPresent) {
117         return getFieldOffset0(name, type, notPresent, cppType, null);
118     }
119 
120     /**
121      * Gets the offset of a non-static C++ field.
122      *
123      * @param name fully qualified name of the field
124      * @param type the boxed type to which the offset value will be converted (must be
125      *            {@link Integer} or {@link Long})
126      * @param notPresent if non-null and the field is not present then this value is returned
127      * @param outCppType if non-null, the C++ type of the field (e.g., {@code "HeapWord*"}) is
128      *            returned in element 0 of this array
129      * @return the offset in bytes of the requested field
130      * @throws JVMCIError if the field is static or not present and {@code notPresent} is null
131      */
132     public <T> T getFieldOffset(String name, Class<T> type, T notPresent, String[] outCppType) {
133         return getFieldOffset0(name, type, notPresent, null, outCppType);
134     }
135 
136     /**
137      * Gets the offset of a non-static C++ field.
138      *
139      * @param name fully qualified name of the field
140      * @param type the boxed type to which the offset value will be converted (must be
141      *            {@link Integer} or {@link Long})
142      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
143      * @return the offset in bytes of the requested field
144      * @throws JVMCIError if the field is static or not present
145      */
146     public <T> T getFieldOffset(String name, Class<T> type, String cppType) {
147         return getFieldOffset0(name, type, null, cppType, null);
148     }
149 
150     /**
151      * Gets the offset of a non-static C++ field.
152      *
153      * @param name fully qualified name of the field
154      * @param type the boxed type to which the offset value will be converted (must be
155      *            {@link Integer} or {@link Long})
156      * @return the offset in bytes of the requested field
157      * @throws JVMCIError if the field is static or not present
158      */
159     public <T> T getFieldOffset(String name, Class<T> type) {
160         return getFieldOffset0(name, type, null, null, null);
161     }
162 
163     private <T> T getFieldOffset0(String name, Class<T> type, T notPresent, String inCppType, String[] outCppType) {
164         assert type == Integer.class || type == Long.class;
165         VMField entry = getField(name, inCppType, notPresent == null);
166         if (entry == null) {
167             return notPresent;
168         }
169         if (entry.address != 0) {
170             throw new JVMCIError("cannot get offset of static field " + name);
171         }
172         if (outCppType != null) {
173             outCppType[0] = entry.type;
174         }
175         return type.cast(convertValue(name, type, entry.offset, inCppType));
176     }
177 
178     /**
179      * Gets the address of a static C++ field.
180      *
181      * @param name fully qualified name of the field
182      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
183      * @param notPresent if non-null and the field is not present then this value is returned
184      * @return the address of the requested field
185      * @throws JVMCIError if the field is not static or not present and {@code notPresent} is null
186      */
187     public long getFieldAddress(String name, String cppType, Long notPresent) {
188         return getFieldAddress0(name, notPresent, cppType, null);
189     }
190 
191     /**
192      * Gets the address of a static C++ field.
193      *
194      * @param name fully qualified name of the field
195      * @param notPresent if non-null and the field is not present then this value is returned
196      * @param outCppType if non-null, the C++ type of the field (e.g., {@code "HeapWord*"}) is
197      *            returned in element 0 of this array
198      * @return the address of the requested field
199      * @throws JVMCIError if the field is not static or not present and {@code notPresent} is null
200      */
201     public long getFieldAddress(String name, Long notPresent, String[] outCppType) {
202         return getFieldAddress0(name, notPresent, null, outCppType);
203     }
204 
205     /**
206      * Gets the address of a static C++ field.
207      *
208      * @param name fully qualified name of the field
209      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
210      * @return the address of the requested field
211      * @throws JVMCIError if the field is not static or not present
212      */
213     public long getFieldAddress(String name, String cppType) {
214         return getFieldAddress0(name, null, cppType, null);
215     }
216 
217     private long getFieldAddress0(String name, Long notPresent, String inCppType, String[] outCppType) {
218         VMField entry = getField(name, inCppType, notPresent == null);
219         if (entry == null) {
220             return notPresent;
221         }
222         if (entry.address == 0) {
223             throw new JVMCIError(name + " is not a static field");
224         }
225         if (outCppType != null) {
226             outCppType[0] = entry.type;
227         }
228         return entry.address;
229     }
230 
231     /**
232      * Gets the value of a static C++ field.
233      *
234      * @param name fully qualified name of the field
235      * @param type the boxed type to which the constant value will be converted
236      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
237      * @param notPresent if non-null and the field is not present then this value is returned
238      * @return the value of the requested field
239      * @throws JVMCIError if the field is not static or not present and {@code notPresent} is null
240      */
241     public <T> T getFieldValue(String name, Class<T> type, String cppType, T notPresent) {
242         return getFieldValue0(name, type, notPresent, cppType, null);
243     }
244 
245     /**
246      * Gets the value of a static C++ field.
247      *
248      * @param name fully qualified name of the field
249      * @param type the boxed type to which the constant value will be converted
250      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
251      * @return the value of the requested field
252      * @throws JVMCIError if the field is not static or not present
253      */
254     public <T> T getFieldValue(String name, Class<T> type, String cppType) {
255         return getFieldValue0(name, type, null, cppType, null);
256     }
257 
258     /**
259      * Gets the value of a static C++ field.
260      *
261      * @param name fully qualified name of the field
262      * @param type the boxed type to which the constant value will be converted
263      * @param notPresent if non-null and the field is not present then this value is returned
264      * @param outCppType if non-null, the C++ type of the field (e.g., {@code "HeapWord*"}) is
265      *            returned in element 0 of this array
266      * @return the value of the requested field
267      * @throws JVMCIError if the field is not static or not present and {@code notPresent} is null
268      */
269     public <T> T getFieldValue(String name, Class<T> type, T notPresent, String[] outCppType) {
270         return getFieldValue0(name, type, notPresent, null, outCppType);
271     }
272 
273     /**
274      * Gets the value of a static C++ field.
275      *
276      * @param name fully qualified name of the field
277      * @param type the boxed type to which the constant value will be converted
278      * @return the value of the requested field
279      * @throws JVMCIError if the field is not static or not present
280      */
281     public <T> T getFieldValue(String name, Class<T> type) {
282         return getFieldValue0(name, type, null, null, null);
283     }
284 
285     private <T> T getFieldValue0(String name, Class<T> type, T notPresent, String inCppType, String[] outCppType) {
286         VMField entry = getField(name, inCppType, notPresent == null);
287         if (entry == null) {
288             return notPresent;
289         }
290         if (entry.value == null) {
291             throw new JVMCIError(name + " is not a static field ");
292         }
293         if (outCppType != null) {
294             outCppType[0] = entry.type;
295         }
296         return type.cast(convertValue(name, type, entry.value, inCppType));
297     }
298 
299     private boolean typeEquals(String t1, String t2) {
300         if (t1.equals("int64_t") && t2.equals("intx")) {
301             return true;
302         }
303         return t1.equals(t2);
304     }
305 
306     /**
307      * Gets a C++ field.
308      *
309      * @param name fully qualified name of the field
310      * @param cppType if non-null, the expected C++ type of the field (e.g., {@code "HeapWord*"})
311      * @param required specifies if the field must be present
312      * @return the field
313      * @throws JVMCIError if the field is not present and {@code required == true}
314      */
315     private VMField getField(String name, String cppType, boolean required) {
316         VMField entry = store.vmFields.get(name);
317         if (entry == null) {
318             if (!required) {
319                 return null;
320             }
321             throw missingEntry("field", name, store.vmFields.keySet());
322         }
323 
324         // Make sure the native type is still the type we expect.
325         if (cppType != null && !typeEquals(cppType, entry.type)) {
326             throw new JVMCIError("expected type " + cppType + " but VM field " + name + " is of type " + entry.type);
327         }
328         return entry;
329     }
330 
331     /**
332      * Gets a VM flag value.
333      *
334      * @param name name of the flag (e.g., {@code "CompileTheWorldStartAt"})
335      * @param type the boxed type to which the flag's value will be converted
336      * @return the flag's value converted to {@code type} or {@code notPresent} if the flag is not
337      *         present
338      * @throws JVMCIError if the flag is not present
339      */
340     public <T> T getFlag(String name, Class<T> type) {
341         return getFlag(name, type, null);
342     }
343 
344     /**
345      * Gets a VM flag value.
346      *
347      * @param name name of the flag (e.g., {@code "CompileTheWorldStartAt"})
348      * @param type the boxed type to which the flag's value will be converted
349      * @param notPresent if non-null and the flag is not present then this value is returned
350      * @return the flag's value converted to {@code type} or {@code notPresent} if the flag is not
351      *         present
352      * @throws JVMCIError if the flag is not present and {@code notPresent == null}
353      */
354     public <T> T getFlag(String name, Class<T> type, T notPresent) {
355         VMFlag entry = store.vmFlags.get(name);
356         Object value;
357         String cppType;
358         if (entry == null) {
359             // Fall back to VM call
360             value = store.compilerToVm.getFlagValue(name);
361             if (value == store.compilerToVm) {
362                 if (notPresent != null) {
363                     return notPresent;
364                 }
365                 throw missingEntry("flag", name, store.vmFlags.keySet());
366             } else {
367                 cppType = null;
368             }
369         } else {
370             value = entry.value;
371             cppType = entry.type;
372         }
373         return type.cast(convertValue(name, type, value, cppType));
374     }
375 
376     private JVMCIError missingEntry(String category, String name, Set<String> keys) {
377         throw new JVMCIError("expected VM %s not found in %s: %s%nAvailable values:%n    %s", category, store, name,
378                         keys.stream().sorted().collect(Collectors.joining(System.lineSeparator() + "    ")));
379     }
380 
381     private static <T> Object convertValue(String name, Class<T> toType, Object value, String cppType) throws JVMCIError {
382         if (toType == Boolean.class) {
383             if (value instanceof String) {
384                 return Boolean.valueOf((String) value);
385             } else if (value instanceof Boolean) {
386                 return value;
387             } else if (value instanceof Long) {
388                 return ((long) value) != 0;
389             }
390         } else if (toType == Byte.class) {
391             if (value instanceof Long) {
392                 return (byte) (long) value;
393             }
394         } else if (toType == Integer.class) {
395             if (value instanceof Integer) {
396                 return value;
397             } else if (value instanceof Long) {
398                 return (int) (long) value;
399             }
400         } else if (toType == String.class) {
401             if (value == null || value instanceof String) {
402                 return value;
403             }
404         } else if (toType == Long.class) {
405             return value;
406         }
407 
408         throw new JVMCIError("cannot convert " + name + " of type " + value.getClass().getSimpleName() + (cppType == null ? "" : " [" + cppType + "]") + " to " + toType.getSimpleName());
409     }
410 
411     private final HotSpotVMConfigStore store;
412 
413     public HotSpotVMConfigAccess(HotSpotVMConfigStore store) {
414         this.store = store;
415     }
416 }