2424 */
2525package org .graalvm .visualvm .heapviewer .truffle .details ;
2626
27+ import java .io .UnsupportedEncodingException ;
28+ import java .util .Collections ;
29+ import java .util .HashMap ;
30+ import java .util .List ;
31+ import java .util .Map ;
32+ import java .util .WeakHashMap ;
33+ import org .graalvm .visualvm .lib .jfluid .heap .Heap ;
2734import org .graalvm .visualvm .lib .jfluid .heap .Instance ;
35+ import org .graalvm .visualvm .lib .jfluid .heap .JavaClass ;
36+ import org .graalvm .visualvm .lib .jfluid .heap .PrimitiveArrayInstance ;
2837import org .graalvm .visualvm .lib .profiler .heapwalk .details .api .DetailsSupport ;
2938import org .graalvm .visualvm .lib .profiler .heapwalk .details .spi .DetailsProvider ;
3039import org .graalvm .visualvm .lib .profiler .heapwalk .details .spi .DetailsUtils ;
@@ -48,12 +57,19 @@ public class TruffleDetailsProvider extends DetailsProvider.Basic {
4857 private static final String INSTRUMENT_INFO_MASK = "com.oracle.truffle.api.InstrumentInfo" ; // NOI18N
4958 private static final String NATIVE_ROOT_MASK = "com.oracle.truffle.nfi.LibFFIFunctionMessageResolutionForeign$ExecuteLibFFIFunctionSubNode$EXECUTERootNode" ; // NOI18N
5059 private static final String NODE_MASK = "com.oracle.truffle.api.nodes.Node+" ; // NOI18N
60+ private static final String TSTRING_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString+" ; // NOI18N
61+ private static final String TS_LONG_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString$LazyLong" ; // NOI18N
62+ private static final String TS_CONCAT_MASK = "com.oracle.truffle.api.strings.AbstractTruffleString$LazyConcat" ; // NOI18N
63+
64+ private static final String TS_ENCODING_CLASS = "com.oracle.truffle.api.strings.TruffleString$Encoding" ; // NOI18N
65+ private static final Object CACHE_LOCK = new Object ();
66+ private static WeakHashMap <Heap ,Map <Byte ,Encoding >> CACHE ;
5167
5268 public TruffleDetailsProvider () {
5369 super (DEFAULT_CALL_TARGET_MASK , OPTIMIZED_CALL_TARGET_MASK , OPTIMIZED_CALL_TARGET1_MASK ,
5470 ENT_OPTIMIZED_CALL_TARGET_MASK , LANG_INFO_MASK , LANG_CACHE_MASK ,
5571 LANG_CACHE1_MASK , POLYGLOT_MASK , INSTRUMENT_INFO_MASK , NATIVE_ROOT_MASK ,
56- NODE_MASK );
72+ NODE_MASK , TSTRING_MASK , TS_LONG_MASK , TS_CONCAT_MASK );
5773 }
5874
5975 public String getDetailsString (String className , Instance instance ) {
@@ -115,6 +131,61 @@ public String getDetailsString(String className, Instance instance) {
115131 if (NODE_MASK .equals (className )) {
116132 return DetailsUtils .getInstanceFieldString (instance , "sourceSection" );
117133 }
134+ if (TSTRING_MASK .equals (className )) {
135+ Instance next = instance ;
136+ do {
137+ String str = getString (next );
138+ if (str != null ) {
139+ return str ;
140+ }
141+ next = (Instance ) next .getValueOfField ("next" ); // NOI18N
142+ } while (next != null && !instance .equals (next ));
143+
144+ Object data = instance .getValueOfField ("data" );
145+ if (data instanceof PrimitiveArrayInstance ) {
146+ Encoding encoding = getEncoding (instance );
147+ Byte stride = (Byte )instance .getValueOfField ("stride" ); // NOI18N
148+ if (stride != null && encoding != null ) {
149+ byte [] bytes = convertBytes ((PrimitiveArrayInstance )data , encoding .naturalStride , stride );
150+ try {
151+ if ("BYTES" .equals (encoding .name )) {
152+ return new String (bytes , "ISO-8859-1" );
153+ }
154+ return new String (bytes , encoding .name );
155+ } catch (UnsupportedEncodingException ex ) {
156+ try {
157+ return new String (bytes , encoding .name .replace ('_' , '-' ));
158+ } catch (UnsupportedEncodingException ex1 ) {
159+ return new String (bytes );
160+ }
161+ }
162+ }
163+ } else {
164+ return DetailsUtils .getInstanceString ((Instance ) data );
165+ }
166+ }
167+ if (TS_LONG_MASK .equals (className )) {
168+ return String .valueOf (DetailsUtils .getLongFieldValue (instance , "value" , 0 )); // NOI18N
169+ }
170+ if (TS_CONCAT_MASK .equals (className )) {
171+ Object vall = instance .getValueOfField ("left" ); // NOI18N
172+ Object valr = instance .getValueOfField ("right" ); // NOI18N
173+
174+ String left = DetailsUtils .getInstanceString ((Instance )vall );
175+
176+ if (left == null ) {
177+ return DetailsUtils .getInstanceString ((Instance )valr );
178+ }
179+ if (valr == null || left .length () > DetailsUtils .MAX_ARRAY_LENGTH ) {
180+ return left ;
181+ }
182+ String value = left + DetailsUtils .getInstanceString ((Instance )valr );
183+
184+ if (value .length () > DetailsUtils .MAX_ARRAY_LENGTH ) {
185+ return value .substring (0 , DetailsUtils .MAX_ARRAY_LENGTH ) + "..." ; // NOI18N
186+ }
187+ return value ;
188+ }
118189 return null ;
119190 }
120191
@@ -128,4 +199,80 @@ public View getDetailsView(String className, Instance instance) {
128199 }
129200 return null ;
130201 }
202+
203+ private String getString (Instance truffleString ) {
204+ Object data = truffleString .getValueOfField ("data" ); // NOI18N
205+ if (data instanceof Instance ) {
206+ Instance idata = (Instance ) data ;
207+ if (idata .getJavaClass ().getName ().equals (String .class .getName ())) {
208+ return DetailsUtils .getInstanceString (idata );
209+ }
210+ }
211+ return null ;
212+ }
213+
214+ private Encoding getEncoding (Instance truffleString ) {
215+ Byte encodingId = (Byte ) truffleString .getValueOfField ("encoding" ); // NOI18N
216+
217+ Map <Byte , Encoding > heapCache = getEncodingCache (truffleString );
218+ Encoding cachedEncoding = heapCache .get (encodingId );
219+ if (cachedEncoding == null && encodingId != null ) {
220+ Heap heap = truffleString .getJavaClass ().getHeap ();
221+ JavaClass encodingClass = heap .getJavaClassByName (TS_ENCODING_CLASS );
222+ for (Instance encoding : encodingClass .getInstances ()) {
223+ Byte id = (Byte ) encoding .getValueOfField ("id" ); // NOI18N
224+
225+ if (id .equals (encodingId )) {
226+ cachedEncoding = new Encoding (encoding );
227+ heapCache .put (encodingId , cachedEncoding );
228+ }
229+ }
230+ }
231+ return cachedEncoding ;
232+ }
233+
234+ private Map <Byte , Encoding > getEncodingCache (Instance truffleString ) {
235+ synchronized (CACHE_LOCK ) {
236+ if (CACHE == null ) {
237+ CACHE = new WeakHashMap ();
238+ }
239+ Heap heap = truffleString .getJavaClass ().getHeap ();
240+ Map <Byte , Encoding > heapCache = CACHE .get (heap );
241+ if (heapCache == null ) {
242+ heapCache = Collections .synchronizedMap (new HashMap <>());
243+ CACHE .put (heap , heapCache );
244+ }
245+ return heapCache ;
246+ }
247+ }
248+
249+ private byte [] convertBytes (PrimitiveArrayInstance data , byte naturalStride , byte stride ) {
250+ int inCharSize = 1 << stride ;
251+ int outCharSize = 1 << naturalStride ;
252+ int padding = outCharSize - inCharSize ;
253+ byte [] bytes = new byte [(data .getLength () / inCharSize ) * outCharSize ];
254+ List <String > values = data .getValues ();
255+ int op = 0 ;
256+
257+ for (int ip = 0 ; ip < values .size ();) {
258+ for (int j = 0 ; j < inCharSize ; j ++) {
259+ bytes [op ++] = Byte .valueOf (values .get (ip ++));
260+ }
261+ op += padding ;
262+ }
263+ return bytes ;
264+ }
265+
266+ private static class Encoding {
267+
268+ byte encId ;
269+ String name ;
270+ byte naturalStride ;
271+
272+ Encoding (Instance encoding ) {
273+ encId = (Byte ) encoding .getValueOfField ("id" ); // NOI18N
274+ name = DetailsUtils .getInstanceFieldString (encoding , "name" ); // NOI18N
275+ naturalStride = (Byte ) encoding .getValueOfField ("naturalStride" ); // NOI18N
276+ }
277+ }
131278}
0 commit comments