1 /*
   2  * Permission is hereby granted, free of charge, to any person obtaining a copy of
   3  * this software and associated documentation files (the "Software"), to deal in
   4  * the Software without restriction, including without limitation the rights to
   5  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
   6  * of the Software, and to permit persons to whom the Software is furnished to do
   7  * so, subject to the following conditions:
   8  *
   9  * The above copyright notice and this permission notice shall be included in all
  10  * copies or substantial portions of the Software.
  11  *
  12  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  18  * SOFTWARE.
  19  */
  20 package jdk.nashorn.internal.runtime.regexp.joni;
  21 
  22 import java.io.FileOutputStream;
  23 import java.io.IOException;
  24 
  25 import jdk.nashorn.internal.runtime.regexp.joni.constants.AsmConstants;
  26 import jdk.internal.org.objectweb.asm.ClassWriter;
  27 import jdk.internal.org.objectweb.asm.MethodVisitor;
  28 import jdk.internal.org.objectweb.asm.Opcodes;
  29 
  30 abstract class AsmCompilerSupport extends Compiler implements Opcodes, AsmConstants {
  31     protected ClassWriter factory;      // matcher allocator, also bit set, code rage and string template container
  32     protected MethodVisitor factoryInit;// factory constructor
  33     protected String factoryName;
  34 
  35     protected ClassWriter machine;      // matcher
  36     protected MethodVisitor machineInit;// matcher constructor
  37     protected MethodVisitor match;      // actual matcher implementation (the matchAt method)
  38     protected String machineName;
  39 
  40     // we will? try to manage visitMaxs ourselves for efficiency
  41     protected int maxStack = 1;
  42     protected int maxVars = LAST_INDEX;
  43 
  44     // for field generation
  45     protected int bitsets, ranges, templates;
  46 
  47     // simple class name postfix scheme for now
  48     static int REG_NUM = 0;
  49 
  50     // dummy class loader for now
  51     private static final class DummyClassLoader extends ClassLoader {
  52         public Class<?> defineClass(String name, byte[] bytes) {
  53             return super.defineClass(name, bytes, 0, bytes.length);
  54         }
  55     };
  56 
  57     private static final DummyClassLoader loader = new DummyClassLoader();
  58 
  59     AsmCompilerSupport(Analyser analyser) {
  60         super(analyser);
  61     }
  62 
  63     protected final void prepareFactory() {
  64         factory = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  65         factoryName = "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory" + REG_NUM;
  66 
  67         factory.visit(V1_4, ACC_PUBLIC + ACC_FINAL, factoryName, null, "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory", null);
  68 
  69         MethodVisitor create = factory.visitMethod(ACC_SYNTHETIC, "create", "(Lorg/joni/Regex;[BII)Lorg/joni/Matcher;", null, null);
  70         create.visitTypeInsn(NEW, machineName);
  71         create.visitInsn(DUP);          // instance
  72         create.visitVarInsn(ALOAD, 1);  // Regex
  73         create.visitVarInsn(ALOAD, 2);  // bytes[]
  74         create.visitVarInsn(ILOAD, 3);  // p
  75         create.visitVarInsn(ILOAD, 4);  // end
  76         create.visitMethodInsn(INVOKESPECIAL, machineName, "<init>", "(Lorg/joni/Regex;[BII)V");
  77         create.visitInsn(ARETURN);
  78         create.visitMaxs(0, 0);
  79         //create.visitMaxs(6, 5);
  80         create.visitEnd();
  81     }
  82 
  83     protected final void prepareFactoryInit() {
  84         factoryInit = factory.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
  85         factoryInit.visitVarInsn(ALOAD, 0);
  86         factoryInit.visitMethodInsn(INVOKESPECIAL, "jdk/nashorn/internal/runtime/regexp/joni/MatcherFactory", "<init>", "()V");
  87     }
  88 
  89     protected final void setupFactoryInit() {
  90         factoryInit.visitInsn(RETURN);
  91         factoryInit.visitMaxs(0, 0);
  92         //init.visitMaxs(1, 1);
  93         factoryInit.visitEnd();
  94     }
  95 
  96     protected final void prepareMachine() {
  97         machine = new ClassWriter(ClassWriter.COMPUTE_MAXS);
  98         machineName = "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine" + REG_NUM;
  99     }
 100 
 101     protected final void prepareMachineInit() {
 102         machine.visit(V1_4, ACC_PUBLIC + ACC_FINAL, machineName, null, "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine", null);
 103         machineInit = machine.visitMethod(ACC_PROTECTED, "<init>", "(Lorg/joni/Regex;[BII)V", null, null);
 104         machineInit.visitVarInsn(ALOAD, THIS);  // this
 105         machineInit.visitVarInsn(ALOAD, 1);     // Regex
 106         machineInit.visitVarInsn(ALOAD, 2);     // bytes[]
 107         machineInit.visitVarInsn(ILOAD, 3);     // p
 108         machineInit.visitVarInsn(ILOAD, 4);     // end
 109         machineInit.visitMethodInsn(INVOKESPECIAL, "jdk/nashorn/internal/runtime/regexp/joni/NativeMachine", "<init>", "(Lorg/joni/Regex;[BII)V");
 110     }
 111 
 112     protected final void setupMachineInit() {
 113         if (bitsets + ranges + templates > 0) { // ok, some of these are in use, we'd like to cache the factory
 114             machine.visitField(ACC_PRIVATE + ACC_FINAL, "factory", "L" + factoryName + ";", null, null);
 115             machineInit.visitVarInsn(ALOAD, THIS);  // this
 116             machineInit.visitVarInsn(ALOAD, 1);     // this, Regex
 117             machineInit.visitFieldInsn(GETFIELD, "jdk/nashorn/internal/runtime/regexp/joni/Regex", "factory", "Lorg/joni/MatcherFactory;"); // this, factory
 118             machineInit.visitTypeInsn(CHECKCAST, factoryName);
 119             machineInit.visitFieldInsn(PUTFIELD, machineName, "factory", "L" + factoryName + ";"); // []
 120         }
 121 
 122         machineInit.visitInsn(RETURN);
 123         machineInit.visitMaxs(0, 0);
 124         //init.visitMaxs(5, 5);
 125         machineInit.visitEnd();
 126     }
 127 
 128     protected final void prepareMachineMatch() {
 129         match = machine.visitMethod(ACC_SYNTHETIC, "matchAt", "(III)I", null, null);
 130         move(S, SSTART);        // s = sstart
 131         load("bytes", "[B");    //
 132         astore(BYTES);          // byte[]bytes = this.bytes
 133     }
 134 
 135     protected final void setupMachineMatch() {
 136         match.visitInsn(ICONST_M1);
 137         match.visitInsn(IRETURN);
 138 
 139         match.visitMaxs(maxStack, maxVars);
 140         match.visitEnd();
 141     }
 142 
 143     protected final void setupClasses() {
 144         byte[]factoryCode = factory.toByteArray();
 145         byte[]machineCode = machine.toByteArray();
 146 
 147         if (Config.DEBUG_ASM) {
 148             try {
 149                 FileOutputStream fos;
 150                 fos = new FileOutputStream(factoryName.substring(factoryName.lastIndexOf('/') + 1) + ".class");
 151                 fos.write(factoryCode);
 152                 fos.close();
 153                 fos = new FileOutputStream(machineName.substring(machineName.lastIndexOf('/') + 1) + ".class");
 154                 fos.write(machineCode);
 155                 fos.close();
 156             } catch (IOException ioe) {
 157                 ioe.printStackTrace(Config.err);
 158             }
 159         }
 160 
 161         loader.defineClass(machineName.replace('/', '.'), machineCode);
 162         Class<?> cls = loader.defineClass(factoryName.replace('/', '.'), factoryCode);
 163         try {
 164             regex.factory = (MatcherFactory)cls.newInstance();
 165         } catch(Exception e) {
 166             e.printStackTrace(Config.err);
 167         }
 168     }
 169 
 170     protected final void aload(int var) {
 171         match.visitVarInsn(ALOAD, var);
 172     }
 173 
 174     protected final void astore(int var) {
 175         match.visitVarInsn(ASTORE, var);
 176     }
 177 
 178     protected final void loadThis() {
 179         match.visitVarInsn(ALOAD, THIS);
 180     }
 181 
 182     protected final void load(int var) {
 183         match.visitVarInsn(ILOAD, var);
 184     }
 185 
 186     protected final void store(int var) {
 187         match.visitVarInsn(ISTORE, var);
 188     }
 189 
 190     protected final void move(int to, int from) {
 191         load(from);
 192         store(to);
 193     }
 194 
 195     protected final void load(String field, String singature) {
 196         loadThis();
 197         match.visitFieldInsn(GETFIELD, machineName, field, singature);
 198     }
 199 
 200     protected final void load(String field) {
 201         load(field, "I");
 202     }
 203 
 204     protected final void store(String field, String singature) {
 205         loadThis();
 206         match.visitFieldInsn(PUTFIELD, machineName, field, singature);
 207     }
 208 
 209     protected final void store(String field) {
 210         store(field, "I");
 211     }
 212 
 213     protected final String installTemplate(char[] arr, int p, int length) {
 214         String templateName = TEMPLATE + ++templates;
 215         installArray(templateName, arr, p, length);
 216         return templateName;
 217     }
 218 
 219     protected final String installCodeRange(int[]arr) {
 220         String coreRangeName = CODERANGE + ++ranges;
 221         installArray(coreRangeName, arr);
 222         return coreRangeName;
 223     }
 224 
 225     protected final String installBitSet(int[]arr) {
 226         String bitsetName = BITSET + ++bitsets;
 227         installArray(bitsetName, arr);
 228         return bitsetName;
 229     }
 230 
 231     private void installArray(String name, int[]arr) {
 232         factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[I", null, null);
 233         factoryInit.visitVarInsn(ALOAD, THIS);          // this;
 234         loadInt(factoryInit, arr.length);               // this, length
 235         factoryInit.visitIntInsn(NEWARRAY, T_INT);      // this, arr
 236         for (int i=0;i < arr.length; i++) buildArray(i, arr[i], IASTORE);
 237         factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[I");
 238     }
 239 
 240     private void installArray(String name, char[]arr, int p, int length) {
 241         factory.visitField(ACC_PRIVATE + ACC_FINAL, name, "[B", null, null);
 242         factoryInit.visitVarInsn(ALOAD, THIS);          // this;
 243         loadInt(factoryInit, arr.length);               // this, length
 244         factoryInit.visitIntInsn(NEWARRAY, T_BYTE);     // this, arr
 245         for (int i=p, j=0; i < p + length; i++, j++) buildArray(j, arr[i] & 0xff, BASTORE);
 246         factoryInit.visitFieldInsn(PUTFIELD, factoryName, name, "[B");
 247     }
 248 
 249     private void buildArray(int index, int value, int type) {
 250         factoryInit.visitInsn(DUP);     // ... arr, arr
 251         loadInt(factoryInit, index);    // ... arr, arr, index
 252         loadInt(factoryInit, value);    // ... arr, arr, index, value
 253         factoryInit.visitInsn(type);    // ... arr
 254     }
 255 
 256     private void loadInt(MethodVisitor mv, int value) {
 257         if (value >= -1 && value <= 5) {
 258             mv.visitInsn(value + ICONST_0); // ICONST_0 == 3
 259         } else if (value >= 6 && value <= 127 || value >= -128 && value <= -2) {
 260             mv.visitIntInsn(BIPUSH, value);
 261         } else if (value >= 128 && value <= 32767 || value >= -32768 && value <= -129) {
 262             mv.visitIntInsn(SIPUSH, value);
 263         } else {
 264             mv.visitLdcInsn(new Integer(value));
 265         }
 266     }
 267 }