• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

raphw / byte-buddy / #801

27 Oct 2025 09:37AM UTC coverage: 84.715% (-0.4%) from 85.118%
#801

push

raphw
Fix imports.

29586 of 34924 relevant lines covered (84.72%)

0.85 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

98.95
/byte-buddy-dep/src/main/java/net/bytebuddy/implementation/ToStringMethod.java
1
/*
2
 * Copyright 2014 - Present Rafael Winterhalter
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package net.bytebuddy.implementation;
17

18
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20
import net.bytebuddy.description.field.FieldDescription;
21
import net.bytebuddy.description.method.MethodDescription;
22
import net.bytebuddy.description.type.TypeDescription;
23
import net.bytebuddy.dynamic.scaffold.InstrumentedType;
24
import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
25
import net.bytebuddy.implementation.bytecode.Duplication;
26
import net.bytebuddy.implementation.bytecode.StackManipulation;
27
import net.bytebuddy.implementation.bytecode.TypeCreation;
28
import net.bytebuddy.implementation.bytecode.constant.TextConstant;
29
import net.bytebuddy.implementation.bytecode.member.FieldAccess;
30
import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
31
import net.bytebuddy.implementation.bytecode.member.MethodReturn;
32
import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
33
import net.bytebuddy.matcher.ElementMatcher;
34
import net.bytebuddy.matcher.ElementMatchers;
35
import net.bytebuddy.utility.nullability.MaybeNull;
36
import org.objectweb.asm.MethodVisitor;
37
import org.objectweb.asm.Opcodes;
38

39
import java.util.ArrayList;
40
import java.util.List;
41

42
import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
43
import static net.bytebuddy.matcher.ElementMatchers.isStatic;
44
import static net.bytebuddy.matcher.ElementMatchers.isToString;
45
import static net.bytebuddy.matcher.ElementMatchers.none;
46
import static net.bytebuddy.matcher.ElementMatchers.not;
47

48
/**
49
 * An implementation of {@link Object#toString()} that concatenates the {@link String} representation of all fields that are declared by a class.
50
 */
51
@HashCodeAndEqualsPlugin.Enhance
52
public class ToStringMethod implements Implementation {
53

54
    /**
55
     * The {@link StringBuilder#StringBuilder(String)} constructor.
56
     */
57
    private static final MethodDescription.InDefinedShape STRING_BUILDER_CONSTRUCTOR = TypeDescription.ForLoadedType.of(StringBuilder.class)
1✔
58
            .getDeclaredMethods()
1✔
59
            .filter(isConstructor().and(ElementMatchers.takesArguments(String.class)))
1✔
60
            .getOnly();
1✔
61

62
    /**
63
     * The {@link StringBuilder#toString()} method.
64
     */
65
    private static final MethodDescription.InDefinedShape TO_STRING = TypeDescription.ForLoadedType.of(StringBuilder.class)
1✔
66
            .getDeclaredMethods()
1✔
67
            .filter(isToString())
1✔
68
            .getOnly();
1✔
69

70
    /**
71
     * A resolver for the prefix of a {@link String} representation.
72
     */
73
    private final PrefixResolver prefixResolver;
74

75
    /**
76
     * A token that is added between the prefix and the first field value.
77
     */
78
    private final String start;
79

80
    /**
81
     * A token that is added after the last field value.
82
     */
83
    private final String end;
84

85
    /**
86
     * A token that is added between two field values.
87
     */
88
    private final String separator;
89

90
    /**
91
     * A token that is added between a field's name and its value.
92
     */
93
    private final String definer;
94

95
    /**
96
     * A filter that determines what fields to ignore.
97
     */
98
    private final ElementMatcher.Junction<? super FieldDescription.InDefinedShape> ignored;
99

100
    /**
101
     * Creates a new {@code toString} implementation.
102
     *
103
     * @param prefixResolver A resolver for the prefix of a {@link String} representation.
104
     */
105
    protected ToStringMethod(PrefixResolver prefixResolver) {
106
        this(prefixResolver, "{", "}", ", ", "=", none());
1✔
107
    }
1✔
108

109
    /**
110
     * Creates a new {@code toString} implementation.
111
     *
112
     * @param prefixResolver A resolver for the prefix of a {@link String} representation.
113
     * @param start          A token that is added between the prefix and the first field value.
114
     * @param end            A token that is added after the last field value.
115
     * @param separator      A token that is added between two field values.
116
     * @param definer        A token that is added between a field's name and its value.
117
     * @param ignored        A filter that determines what fields to ignore.
118
     */
119
    private ToStringMethod(PrefixResolver prefixResolver,
120
                           String start,
121
                           String end,
122
                           String separator,
123
                           String definer,
124
                           ElementMatcher.Junction<? super FieldDescription.InDefinedShape> ignored) {
1✔
125
        this.prefixResolver = prefixResolver;
1✔
126
        this.start = start;
1✔
127
        this.end = end;
1✔
128
        this.separator = separator;
1✔
129
        this.definer = definer;
1✔
130
        this.ignored = ignored;
1✔
131
    }
1✔
132

133
    /**
134
     * Creates a {@link Object#toString()} implementation that is prefixed by the fully qualified class name of the instrumented type.
135
     *
136
     * @return A {@link Object#toString()} implementation that is prefixed by the fully qualified class name of the instrumented type.
137
     */
138
    public static ToStringMethod prefixedByFullyQualifiedClassName() {
139
        return prefixedBy(PrefixResolver.Default.FULLY_QUALIFIED_CLASS_NAME);
1✔
140
    }
141

142
    /**
143
     * Creates a {@link Object#toString()} implementation that is prefixed by the canonical class name of the instrumented type.
144
     *
145
     * @return A {@link Object#toString()} implementation that is prefixed by the canonical class name of the instrumented type.
146
     */
147
    public static ToStringMethod prefixedByCanonicalClassName() {
148
        return prefixedBy(PrefixResolver.Default.CANONICAL_CLASS_NAME);
1✔
149
    }
150

151
    /**
152
     * Creates a {@link Object#toString()} implementation that is prefixed by the simple class name of the instrumented type.
153
     *
154
     * @return A {@link Object#toString()} implementation that is prefixed by the simple class name of the instrumented type.
155
     */
156
    public static ToStringMethod prefixedBySimpleClassName() {
157
        return prefixedBy(PrefixResolver.Default.SIMPLE_CLASS_NAME);
1✔
158
    }
159

160
    /**
161
     * Creates a {@link Object#toString()} implementation that is prefixed by the supplied string.
162
     *
163
     * @param prefix The prefix to use.
164
     * @return A {@link Object#toString()} implementation that is prefixed by the supplied string.
165
     */
166
    public static ToStringMethod prefixedBy(String prefix) {
167
        if (prefix == null) {
1✔
168
            throw new IllegalArgumentException("Prefix cannot be null");
1✔
169
        }
170
        return prefixedBy(new PrefixResolver.ForFixedValue(prefix));
1✔
171
    }
172

173
    /**
174
     * Creates a {@link Object#toString()} implementation that is prefixed by the string that is supplied by the given prefix resolver.
175
     *
176
     * @param prefixResolver The prefix resolver to use.
177
     * @return A {@link Object#toString()} implementation that is prefixed by the string that is supplied by the given prefix resolver.
178
     */
179
    public static ToStringMethod prefixedBy(PrefixResolver prefixResolver) {
180
        return new ToStringMethod(prefixResolver);
1✔
181
    }
182

183
    /**
184
     * Returns a new version of this toString method implementation that ignores the specified fields additionally to any
185
     * previously specified fields.
186
     *
187
     * @param ignored A matcher to specify any fields that should be ignored.
188
     * @return A new version of this toString method implementation that also ignores any fields matched by the provided matcher.
189
     */
190
    public ToStringMethod withIgnoredFields(ElementMatcher<? super FieldDescription.InDefinedShape> ignored) {
191
        return new ToStringMethod(prefixResolver, start, end, separator, definer, this.ignored.<FieldDescription.InDefinedShape>or(ignored));
1✔
192
    }
193

194
    /**
195
     * Changes the tokens used for the {@link Object#toString()} implementation.
196
     *
197
     * @param start     A token that is added between the prefix and the first field value.
198
     * @param end       A token that is added after the last field value.
199
     * @param separator A token that is added between two field values.
200
     * @param definer   A token that is added between two field values.
201
     * @return A new instance of this implementation that uses the supplied tokens.
202
     */
203
    public Implementation withTokens(String start, String end, String separator, String definer) {
204
        if (start == null || end == null || separator == null || definer == null) {
1✔
205
            throw new IllegalArgumentException("Token values cannot be null");
1✔
206
        }
207
        return new ToStringMethod(prefixResolver, start, end, separator, definer, ignored);
1✔
208
    }
209

210
    /**
211
     * {@inheritDoc}
212
     */
213
    public InstrumentedType prepare(InstrumentedType instrumentedType) {
214
        return instrumentedType;
1✔
215
    }
216

217
    /**
218
     * {@inheritDoc}
219
     */
220
    public Appender appender(Target implementationTarget) {
221
        if (implementationTarget.getInstrumentedType().isInterface()) {
1✔
222
            throw new IllegalStateException("Cannot implement meaningful toString method for " + implementationTarget.getInstrumentedType());
1✔
223
        }
224
        String prefix = prefixResolver.resolve(implementationTarget.getInstrumentedType());
1✔
225
        if (prefix == null) {
1✔
226
            throw new IllegalStateException("Prefix for toString method cannot be null");
×
227
        }
228
        return new Appender(prefix,
1✔
229
                start,
230
                end,
231
                separator,
232
                definer,
233
                implementationTarget.getInstrumentedType().getDeclaredFields().filter(not(isStatic().or(ignored))));
1✔
234
    }
235

236
    /**
237
     * An appender to implement {@link ToStringMethod}.
238
     */
239
    @HashCodeAndEqualsPlugin.Enhance
240
    protected static class Appender implements ByteCodeAppender {
241

242
        /**
243
         * The prefix to use.
244
         */
245
        private final String prefix;
246

247
        /**
248
         * A token that is added between the prefix and the first field value.
249
         */
250
        private final String start;
251

252
        /**
253
         * A token that is added after the last field value.
254
         */
255
        private final String end;
256

257
        /**
258
         * A token that is added between two field values.
259
         */
260
        private final String separator;
261

262
        /**
263
         * A token that is added between a field's name and its value.
264
         */
265
        private final String definer;
266

267
        /**
268
         * The list of fields to include in the {@link Object#toString()} implementation.
269
         */
270
        private final List<? extends FieldDescription.InDefinedShape> fieldDescriptions;
271

272
        /**
273
         * Creates a new appender.
274
         *
275
         * @param prefix            The prefix to use.
276
         * @param start             A token that is added between the prefix and the first field value.
277
         * @param end               A token that is added after the last field value.
278
         * @param separator         A token that is added between two field values.
279
         * @param definer           A token that is added between a field's name and its value.
280
         * @param fieldDescriptions The list of fields to include in the {@link Object#toString()} implementation.
281
         */
282
        protected Appender(String prefix,
283
                           String start,
284
                           String end,
285
                           String separator,
286
                           String definer,
287
                           List<? extends FieldDescription.InDefinedShape> fieldDescriptions) {
1✔
288
            this.prefix = prefix;
1✔
289
            this.start = start;
1✔
290
            this.end = end;
1✔
291
            this.separator = separator;
1✔
292
            this.definer = definer;
1✔
293
            this.fieldDescriptions = fieldDescriptions;
1✔
294
        }
1✔
295

296
        /**
297
         * {@inheritDoc}
298
         */
299
        public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
300
            if (instrumentedMethod.isStatic()) {
1✔
301
                throw new IllegalStateException("toString method must not be static: " + instrumentedMethod);
1✔
302
            } else if (!instrumentedMethod.getReturnType().asErasure().isAssignableFrom(String.class)) {
1✔
303
                throw new IllegalStateException("toString method does not return String-compatible type: " + instrumentedMethod);
1✔
304
            }
305
            List<StackManipulation> stackManipulations = new ArrayList<StackManipulation>(Math.max(0, fieldDescriptions.size() * 7 - 2) + 10);
1✔
306
            stackManipulations.add(TypeCreation.of(TypeDescription.ForLoadedType.of(StringBuilder.class)));
1✔
307
            stackManipulations.add(Duplication.SINGLE);
1✔
308
            stackManipulations.add(new TextConstant(prefix));
1✔
309
            stackManipulations.add(MethodInvocation.invoke(STRING_BUILDER_CONSTRUCTOR));
1✔
310
            stackManipulations.add(new TextConstant(start));
1✔
311
            stackManipulations.add(ValueConsumer.STRING);
1✔
312
            boolean first = true;
1✔
313
            for (FieldDescription.InDefinedShape fieldDescription : fieldDescriptions) {
1✔
314
                if (first) {
1✔
315
                    first = false;
1✔
316
                } else {
317
                    stackManipulations.add(new TextConstant(separator));
1✔
318
                    stackManipulations.add(ValueConsumer.STRING);
1✔
319
                }
320
                stackManipulations.add(new TextConstant(fieldDescription.getName() + definer));
1✔
321
                stackManipulations.add(ValueConsumer.STRING);
1✔
322
                stackManipulations.add(MethodVariableAccess.loadThis());
1✔
323
                stackManipulations.add(FieldAccess.forField(fieldDescription).read());
1✔
324
                stackManipulations.add(ValueConsumer.of(fieldDescription.getType().asErasure()));
1✔
325
            }
1✔
326
            stackManipulations.add(new TextConstant(end));
1✔
327
            stackManipulations.add(ValueConsumer.STRING);
1✔
328
            stackManipulations.add(MethodInvocation.invoke(TO_STRING));
1✔
329
            stackManipulations.add(MethodReturn.REFERENCE);
1✔
330
            return new Size(new StackManipulation.Compound(stackManipulations).apply(methodVisitor, implementationContext).getMaximalSize(), instrumentedMethod.getStackSize());
1✔
331
        }
332
    }
333

334
    /**
335
     * A prefix resolver is responsible for providing a value that is prepended to a {@link Object#toString()} implementation.
336
     */
337
    public interface PrefixResolver {
338

339
        /**
340
         * Resolves the prefixed value.
341
         *
342
         * @param instrumentedType The instrumented type.
343
         * @return The value to be prefixed.
344
         */
345
        @MaybeNull
346
        String resolve(TypeDescription instrumentedType);
347

348
        /**
349
         * Default implementations for a prefix resolver.
350
         */
351
        enum Default implements PrefixResolver {
1✔
352

353
            /**
354
             * A prefix resolver for the instrumented type's fully qualified class name.
355
             */
356
            FULLY_QUALIFIED_CLASS_NAME {
1✔
357
                /** {@inheritDoc} */
358
                public String resolve(TypeDescription instrumentedType) {
359
                    return instrumentedType.getName();
1✔
360
                }
361
            },
362

363
            /**
364
             * A prefix resolver for the instrumented type's fully qualified class name.
365
             */
366
            CANONICAL_CLASS_NAME {
1✔
367
                /** {@inheritDoc} */
368
                @MaybeNull
369
                public String resolve(TypeDescription instrumentedType) {
370
                    return instrumentedType.getCanonicalName();
1✔
371
                }
372
            },
373

374
            /**
375
             * A prefix resolver for the instrumented type's simple class name.
376
             */
377
            SIMPLE_CLASS_NAME {
1✔
378
                /** {@inheritDoc} */
379
                public String resolve(TypeDescription instrumentedType) {
380
                    return instrumentedType.getSimpleName();
1✔
381
                }
382
            }
383
        }
384

385
        /**
386
         * A prefix resolver that returns a fixed value.
387
         */
388
        @HashCodeAndEqualsPlugin.Enhance
389
        class ForFixedValue implements PrefixResolver {
390

391
            /**
392
             * The prefix to prepend.
393
             */
394
            private final String prefix;
395

396
            /**
397
             * Creates a new prefix resolver that returns a fixed value.
398
             *
399
             * @param prefix The prefix to prepend.
400
             */
401
            protected ForFixedValue(String prefix) {
1✔
402
                this.prefix = prefix;
1✔
403
            }
1✔
404

405
            /**
406
             * {@inheritDoc}
407
             */
408
            public String resolve(TypeDescription instrumentedType) {
409
                return prefix;
1✔
410
            }
411
        }
412
    }
413

414
    /**
415
     * A value consumer that is responsible for adding a field value to the string creating {@link StringBuilder}.
416
     */
417
    protected enum ValueConsumer implements StackManipulation {
1✔
418

419
        /**
420
         * A value consumer for a {@code boolean} value.
421
         */
422
        BOOLEAN {
1✔
423
            /** {@inheritDoc} */
424
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
425
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Z)Ljava/lang/StringBuilder;", false);
1✔
426
                return Size.ZERO;
1✔
427
            }
428
        },
429

430
        /**
431
         * A value consumer for a {@code char} value.
432
         */
433
        CHARACTER {
1✔
434
            /** {@inheritDoc} */
435
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
436
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(C)Ljava/lang/StringBuilder;", false);
1✔
437
                return Size.ZERO;
1✔
438
            }
439
        },
440

441
        /**
442
         * A value consumer for an {@code int} value.
443
         */
444
        INTEGER {
1✔
445
            /** {@inheritDoc} */
446
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
447
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(I)Ljava/lang/StringBuilder;", false);
1✔
448
                return Size.ZERO;
1✔
449
            }
450
        },
451

452
        /**
453
         * A value consumer for a {@code long} value.
454
         */
455
        LONG {
1✔
456
            /** {@inheritDoc} */
457
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
458
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false);
1✔
459
                return new Size(-1, 0);
1✔
460
            }
461
        },
462

463
        /**
464
         * A value consumer for a {@code float} value.
465
         */
466
        FLOAT {
1✔
467
            /** {@inheritDoc} */
468
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
469
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(F)Ljava/lang/StringBuilder;", false);
1✔
470
                return Size.ZERO;
1✔
471
            }
472
        },
473

474
        /**
475
         * A value consumer for a {@code double} value.
476
         */
477
        DOUBLE {
1✔
478
            /** {@inheritDoc} */
479
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
480
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(D)Ljava/lang/StringBuilder;", false);
1✔
481
                return new Size(-1, 0);
1✔
482
            }
483
        },
484

485
        /**
486
         * A value consumer for a {@link String} value.
487
         */
488
        STRING {
1✔
489
            /** {@inheritDoc} */
490
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
491
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
492
                return Size.ZERO;
1✔
493
            }
494
        },
495

496

497
        /**
498
         * A value consumer for a {@link CharSequence} value.
499
         */
500
        CHARACTER_SEQUENCE {
1✔
501
            /** {@inheritDoc} */
502
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
503
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", false);
1✔
504
                return Size.ZERO;
1✔
505
            }
506
        },
507

508
        /**
509
         * A value consumer for a reference type.
510
         */
511
        OBJECT {
1✔
512
            /** {@inheritDoc} */
513
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
514
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false);
1✔
515
                return Size.ZERO;
1✔
516
            }
517
        },
518

519
        /**
520
         * A value consumer for a {@code boolean} array type.
521
         */
522
        BOOLEAN_ARRAY {
1✔
523
            /** {@inheritDoc} */
524
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
525
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([Z)Ljava/lang/String;", false);
1✔
526
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
527
                return Size.ZERO;
1✔
528
            }
529
        },
530

531
        /**
532
         * A value consumer for a {@code byte} array type.
533
         */
534
        BYTE_ARRAY {
1✔
535
            /** {@inheritDoc} */
536
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
537
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([B)Ljava/lang/String;", false);
1✔
538
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
539
                return Size.ZERO;
1✔
540
            }
541
        },
542

543
        /**
544
         * A value consumer for a {@code short} array type.
545
         */
546
        SHORT_ARRAY {
1✔
547
            /** {@inheritDoc} */
548
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
549
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([S)Ljava/lang/String;", false);
1✔
550
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
551
                return Size.ZERO;
1✔
552
            }
553
        },
554

555
        /**
556
         * A value consumer for a {@code char} array type.
557
         */
558
        CHARACTER_ARRAY {
1✔
559
            /** {@inheritDoc} */
560
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
561
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([C)Ljava/lang/String;", false);
1✔
562
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
563
                return Size.ZERO;
1✔
564
            }
565
        },
566

567
        /**
568
         * A value consumer for an {@code int} array type.
569
         */
570
        INTEGER_ARRAY {
1✔
571
            /** {@inheritDoc} */
572
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
573
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([I)Ljava/lang/String;", false);
1✔
574
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
575
                return Size.ZERO;
1✔
576
            }
577
        },
578

579
        /**
580
         * A value consumer for a {@code long} array type.
581
         */
582
        LONG_ARRAY {
1✔
583
            /** {@inheritDoc} */
584
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
585
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([J)Ljava/lang/String;", false);
1✔
586
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
587
                return Size.ZERO;
1✔
588
            }
589
        },
590

591
        /**
592
         * A value consumer for a {@code float} array type.
593
         */
594
        FLOAT_ARRAY {
1✔
595
            /** {@inheritDoc} */
596
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
597
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([F)Ljava/lang/String;", false);
1✔
598
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
599
                return Size.ZERO;
1✔
600
            }
601
        },
602

603
        /**
604
         * A value consumer for a {@code double} array type.
605
         */
606
        DOUBLE_ARRAY {
1✔
607
            /** {@inheritDoc} */
608
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
609
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([D)Ljava/lang/String;", false);
1✔
610
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
611
                return Size.ZERO;
1✔
612
            }
613
        },
614

615
        /**
616
         * A value consumer for a reference array type.
617
         */
618
        REFERENCE_ARRAY {
1✔
619
            /** {@inheritDoc} */
620
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
621
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "toString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
1✔
622
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
623
                return Size.ZERO;
1✔
624
            }
625
        },
626

627
        /**
628
         * A value consumer for a nested array type.
629
         */
630
        NESTED_ARRAY {
1✔
631
            /** {@inheritDoc} */
632
            public Size apply(MethodVisitor methodVisitor, Context implementationContext) {
633
                methodVisitor.visitMethodInsn(Opcodes.INVOKESTATIC, "java/util/Arrays", "deepToString", "([Ljava/lang/Object;)Ljava/lang/String;", false);
1✔
634
                methodVisitor.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false);
1✔
635
                return Size.ZERO;
1✔
636
            }
637
        };
638

639
        /**
640
         * Resolves an appropriate value resolver for a given type.
641
         *
642
         * @param typeDescription The type for which to resolve a value resolver.
643
         * @return An appropriate stack manipulation.
644
         */
645
        @SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "Assuming component type for array type.")
646
        protected static StackManipulation of(TypeDescription typeDescription) {
647
            if (typeDescription.represents(boolean.class)) {
1✔
648
                return BOOLEAN;
1✔
649
            } else if (typeDescription.represents(char.class)) {
1✔
650
                return CHARACTER;
1✔
651
            } else if (typeDescription.represents(byte.class)
1✔
652
                    || typeDescription.represents(short.class)
1✔
653
                    || typeDescription.represents(int.class)) {
1✔
654
                return INTEGER;
1✔
655
            } else if (typeDescription.represents(long.class)) {
1✔
656
                return LONG;
1✔
657
            } else if (typeDescription.represents(float.class)) {
1✔
658
                return FLOAT;
1✔
659
            } else if (typeDescription.represents(double.class)) {
1✔
660
                return DOUBLE;
1✔
661
            } else if (typeDescription.represents(String.class)) {
1✔
662
                return STRING;
1✔
663
            } else if (typeDescription.isAssignableTo(CharSequence.class)) {
1✔
664
                return CHARACTER_SEQUENCE;
1✔
665
            } else if (typeDescription.represents(boolean[].class)) {
1✔
666
                return BOOLEAN_ARRAY;
1✔
667
            } else if (typeDescription.represents(byte[].class)) {
1✔
668
                return BYTE_ARRAY;
1✔
669
            } else if (typeDescription.represents(short[].class)) {
1✔
670
                return SHORT_ARRAY;
1✔
671
            } else if (typeDescription.represents(char[].class)) {
1✔
672
                return CHARACTER_ARRAY;
1✔
673
            } else if (typeDescription.represents(int[].class)) {
1✔
674
                return INTEGER_ARRAY;
1✔
675
            } else if (typeDescription.represents(long[].class)) {
1✔
676
                return LONG_ARRAY;
1✔
677
            } else if (typeDescription.represents(float[].class)) {
1✔
678
                return FLOAT_ARRAY;
1✔
679
            } else if (typeDescription.represents(double[].class)) {
1✔
680
                return DOUBLE_ARRAY;
1✔
681
            } else if (typeDescription.isArray()) {
1✔
682
                return typeDescription.getComponentType().isArray()
1✔
683
                        ? NESTED_ARRAY
684
                        : REFERENCE_ARRAY;
685
            } else {
686
                return OBJECT;
1✔
687
            }
688
        }
689

690
        /**
691
         * {@inheritDoc}
692
         */
693
        public boolean isValid() {
694
            return true;
×
695
        }
696
    }
697
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc