All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robovm.compiler.plugin.invokedynamic.stringconcat.StringConcatRewriter Maven / Gradle / Ivy

The newest version!
package org.robovm.compiler.plugin.invokedynamic.stringconcat;

import soot.*;
import soot.jimple.Jimple;
import soot.jimple.StringConstant;

import java.util.*;

public class StringConcatRewriter {
    private static final String JAVA_OBJECT = "java.lang.Object";
    private static final String JAVA_STRING = "java.lang.String";
    private static final String JAVA_STRINGBUILDER = "java.lang.StringBuilder";
    private static final String JAVA_STRINGBUILDER_INIT = "void ()";
    private static final String JAVA_STRINGBUILDER_TOSTRING = "java.lang.String toString()";
    private static final String APPEND = "append";

    private SootMethod StringBuilder_init;
    private SootMethod StringBuilder_toString;
    private SootMethod defaultStringBuilder_append;
    private Map StringBuilder_append;

    public StringConcatRewriter() {
        init();
    }

    private void init() {
        SootClass java_lang_StringBuilder = SootResolver.v().resolveClass(JAVA_STRINGBUILDER, SootClass.SIGNATURES);
        StringBuilder_init = java_lang_StringBuilder.getMethod(JAVA_STRINGBUILDER_INIT);
        StringBuilder_toString = java_lang_StringBuilder.getMethod(JAVA_STRINGBUILDER_TOSTRING);

        StringBuilder_append = new HashMap<>();
        defaultStringBuilder_append = java_lang_StringBuilder
                .getMethod(APPEND, Collections.singletonList(RefType.v(JAVA_OBJECT)),
                        RefType.v(JAVA_STRINGBUILDER));

        StringBuilder_append.put(BooleanType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(BooleanType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(CharType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(CharType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(ByteType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(IntType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(ShortType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(IntType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(IntType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(IntType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(LongType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(LongType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(FloatType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(FloatType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(DoubleType.v(),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(DoubleType.v()),
                        RefType.v(JAVA_STRINGBUILDER)));
        StringBuilder_append.put(RefType.v(JAVA_STRING),
                java_lang_StringBuilder.getMethod(APPEND, Collections.singletonList(RefType.v(JAVA_STRING)),
                        RefType.v(JAVA_STRINGBUILDER)));
    }

    public LinkedList rewriteMakeConcat(Body body, Value outValue, List concatArgs) {
        return rewrite(body, outValue, concatArgs);
    }

    public LinkedList rewriteMakeConcatWithConstants(Body body, Value outValue,
            List concatArgs, List bootstrapArgs) {
        Value recipeValue = bootstrapArgs.get(0);
        if (!(recipeValue instanceof StringConstant)) {
            throw new IllegalArgumentException("makeConcatWithConstants argument 'recipe' must be a String!");
        }

        String recipe = ((StringConstant) recipeValue).value;
        List args = new LinkedList<>();
        StringBuilder sb = new StringBuilder();
        int argIndex = 0;
        int constIndex = 1; // First constant is recipe
        int length = recipe.length();

        for (int i = 0; i < length; i++) {
            char c = recipe.charAt(i);
            if (c == '\u0001') { // Reference to argument
                if (sb.length() > 0) {
                    args.add(StringConstant.v(sb.toString()));
                    sb.setLength(0);
                }
                args.add(concatArgs.get(argIndex++));
            } else if (c == '\u0002') { // Reference to constant
                StringConstant constant = (StringConstant) bootstrapArgs.get(constIndex++);
                sb.append(constant.value);
            } else {
                sb.append(c);
            }
        }

        if (sb.length() > 0) {
            args.add(StringConstant.v(sb.toString()));
        }

        return rewrite(body, outValue, args);
    }

    private LinkedList rewrite(Body body, Value outValue, List args) {
        LinkedList newUnits = new LinkedList<>();

        // Create new local StringBuilder
        Local stringBuilderLocal = createNewLocal(body, RefType.v(JAVA_STRINGBUILDER));
        newUnits.add(
                Jimple.v().newAssignStmt(stringBuilderLocal, Jimple.v().newNewExpr(RefType.v(JAVA_STRINGBUILDER))));

        // Initialize local StringBuilder
        newUnits.add(Jimple.v().newInvokeStmt(
                Jimple.v().newSpecialInvokeExpr(stringBuilderLocal, StringBuilder_init.makeRef())));

        for (Value value : args) {
            if (value instanceof StringConstant) {
                // Create new constant string local
                Local constString = createNewLocal(body, RefType.v(JAVA_STRING));
                newUnits.add(Jimple.v().newAssignStmt(constString, value));
                value = constString;
            }

            // Append argument to StringBuilder
            newUnits.add(Jimple.v().newInvokeStmt(
                    Jimple.v().newVirtualInvokeExpr(stringBuilderLocal,
                            StringBuilder_append.getOrDefault(value.getType(),
                                    defaultStringBuilder_append).makeRef(),
                            value)));
        }

        // Assign StringBuilder.toString()
        newUnits.add(Jimple.v().newAssignStmt(outValue,
                Jimple.v().newVirtualInvokeExpr(stringBuilderLocal, StringBuilder_toString.makeRef())));

        return newUnits;
    }

    private Local createNewLocal(Body body, Type t) {
        Local local = Jimple.v().newLocal("$v" + body.getLocals().size(), t);
        body.getLocals().add(local);

        return local;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy