1 /*
   2  * Copyright (c) 2014, 2018, 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 gc.g1.unloading.bytecode;
  24 
  25 import java.util.Collections;
  26 import java.util.LinkedList;
  27 import java.util.List;
  28 import java.util.Random;
  29 import java.util.concurrent.atomic.AtomicLong;
  30 
  31 /**
  32  * I hope I'll reuse this source code generator. That's why I extracted it to separate class.
  33  *
  34  */
  35 public class SourceGenerator {
  36 
  37     private static final int METHODS_NUMBER_LIMIT = 100;
  38 
  39     private static final int LOCALS_NUMBER_LIMIT = 50;
  40 
  41     private static final int METHOD_ARGS_NUMBER_LIMIT = 15;
  42 
  43     private static final int FIELDS_NUMBER_LIMIT = 200;
  44 
  45     private Random rnd;
  46 
  47     private static AtomicLong atomicLong = new AtomicLong();
  48 
  49     public SourceGenerator(long seed) {
  50         rnd = new Random(seed);
  51     }
  52 
  53     public CharSequence generateSource(String className) {
  54         return generateSource(className, null);
  55     }
  56 
  57     public CharSequence generateSource(String className, CharSequence insert) {
  58         StringBuilder sb = new StringBuilder("public class " + className + " { ");
  59 
  60         List<CharSequence> hunks = new LinkedList<>();
  61         int fieldsNumber = rnd.nextInt(FIELDS_NUMBER_LIMIT);
  62         for (int i = 0; i < fieldsNumber; i++) {
  63             hunks.add(createField(rnd));
  64         }
  65         int methodsNumber = rnd.nextInt(METHODS_NUMBER_LIMIT);
  66         for (int i = 0; i < methodsNumber; i++) {
  67             hunks.add(createMethod(rnd));
  68         }
  69 
  70         Collections.shuffle(hunks, rnd);
  71         for (CharSequence cs : hunks) {
  72             sb.append(cs);
  73         }
  74         if (insert != null) {
  75             sb.append(insert);
  76         }
  77         sb.append(" } ");
  78         return sb;
  79     }
  80 
  81     private CharSequence createField(Random rnd) {
  82         StringBuilder sb = new StringBuilder();
  83         if (rnd.nextBoolean())
  84             sb.append(" static ");
  85         boolean isFinal;
  86         if (isFinal = rnd.nextBoolean())
  87             sb.append(" final ");
  88         if (rnd.nextBoolean() && !isFinal)
  89             sb.append(" volatile ");
  90         sb.append(AccessModifier.getRandomAccessModifier(rnd).toString());
  91         Type type = Type.getRandomType(rnd);
  92         sb.append(type.toString());
  93         sb.append(" field_" + atomicLong.getAndIncrement());
  94         if (rnd.nextBoolean() || isFinal)
  95             sb.append(" = " + type.init(rnd));
  96         sb.append(";\n");
  97         return sb.toString();
  98     }
  99 
 100     private CharSequence createMethod(Random rnd) {
 101         StringBuilder sb = new StringBuilder();
 102         if (rnd.nextBoolean())
 103             sb.append(" static ");
 104         if (rnd.nextBoolean())
 105             sb.append(" final ");
 106         if (rnd.nextBoolean())
 107             sb.append(" synchronized ");
 108         sb.append(AccessModifier.getRandomAccessModifier(rnd).toString());
 109         Type returnType = Type.getRandomType(rnd);
 110         sb.append(returnType.toString());
 111         sb.append(" method_" + atomicLong.getAndIncrement());
 112         sb.append("(");
 113         sb.append(generateMethodArgs(rnd));
 114         sb.append(") {\n");
 115         sb.append(generateMethodContent(rnd));
 116         sb.append(" return " + returnType.init(rnd));
 117         sb.append("; };\n");
 118         return sb.toString();
 119     }
 120 
 121     private CharSequence generateMethodContent(Random rnd) {
 122         StringBuilder sb = new StringBuilder();
 123         int number = rnd.nextInt(LOCALS_NUMBER_LIMIT);
 124         for (int i = 0; i < number; i++) {
 125             Type type = Type.getRandomType(rnd);
 126             sb.append(type + " ");
 127             String localName = " local_" + i;
 128             sb.append(localName);
 129             boolean initialized;
 130             if (initialized = rnd.nextBoolean()) {
 131                 sb.append(" = " + type.init(rnd));
 132             }
 133             sb.append(";\n");
 134             if (initialized)
 135                 sb.append("System.out.println(\" \" + " + localName + ");");
 136         }
 137         return sb.toString();
 138     }
 139 
 140     private CharSequence generateMethodArgs(Random rnd) {
 141         StringBuilder sb = new StringBuilder();
 142         int number = rnd.nextInt(METHOD_ARGS_NUMBER_LIMIT);
 143         for (int i = 0; i < number; i++) {
 144             sb.append(Type.getRandomType(rnd));
 145             sb.append(" arg_" + i);
 146             if (i < number - 1) {
 147                 sb.append(" , ");
 148             }
 149         }
 150         return sb.toString();
 151     }
 152 
 153 }
 154 
 155 enum AccessModifier {
 156     PRIVATE, PROTECTED, PACKAGE, PUBLIC;
 157 
 158     public String toString() {
 159         switch (this) {
 160             case PRIVATE:
 161                 return " private ";
 162             case PROTECTED:
 163                 return " protected ";
 164             case PACKAGE:
 165                 return " ";
 166             default:
 167                 return " public ";
 168         }
 169     };
 170 
 171     public static AccessModifier getRandomAccessModifier(Random rnd) {
 172         AccessModifier[] a = AccessModifier.class.getEnumConstants();
 173         return a[rnd.nextInt(a.length)];
 174     }
 175 }
 176 
 177 enum Type {
 178     LONG, INT, BOOLEAN, OBJECT, STRING, DOUBLE, DATE;
 179 
 180     public String toString() {
 181         switch (this) {
 182             case LONG:
 183                 return " long ";
 184             case INT:
 185                 return " int ";
 186             case BOOLEAN:
 187                 return " boolean ";
 188             case OBJECT:
 189                 return " Object ";
 190             case STRING:
 191                 return " String ";
 192             case DOUBLE:
 193                 return " double ";
 194             case DATE:
 195                 return " java.util.Date ";
 196             default:
 197                 return null;
 198         }
 199     }
 200 
 201     ;
 202 
 203     public String init(Random rnd) {
 204         switch (this) {
 205             case LONG:
 206                 return " " + rnd.nextLong() + "L ";
 207             case INT:
 208                 return rnd.nextBoolean() ? " " + rnd.nextInt() : " new Object().hashCode() ";
 209             case BOOLEAN:
 210                 return " " + rnd.nextBoolean();
 211             case OBJECT:
 212                 return " new Object() ";
 213             case STRING:
 214                 return " \"str_bytesToReplace" + rnd.nextInt(4) + "\"";
 215             case DOUBLE:
 216                 return " " + rnd.nextDouble();
 217             case DATE:
 218                 return " new java.util.Date() ";
 219             default:
 220                 return null;
 221         }
 222     }
 223 
 224     public static Type getRandomType(Random rnd) {
 225         Type[] a = Type.class.getEnumConstants();
 226         return a[rnd.nextInt(a.length)];
 227     }
 228 }