Java tutorial
/* Copyright 2013 Industrie IT Pty Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package com.industrieit.ohr; import com.industrieit.ohr.array.LongInlineOHRArray; import com.industrieit.ohr.array.OHRLongArray; import com.industrieit.ohr.array.BooleanInlineOHRArray; import com.industrieit.ohr.array.OHRBooleanArray; import com.industrieit.ohr.array.ByteInlineOHRArray; import com.industrieit.ohr.array.OHRByteArray; import com.industrieit.ohr.array.DoubleInlineOHRArray; import com.industrieit.ohr.array.OHRDoubleArray; import com.industrieit.ohr.array.FloatInlineOHRArray; import com.industrieit.ohr.array.OHRFloatArray; import com.industrieit.ohr.array.IntInlineOHRArray; import com.industrieit.ohr.array.IntInlineOHRArrayUnchecked; import com.industrieit.ohr.array.OHRIntArray; import com.industrieit.ohr.array.ShortInlineOHRArray; import com.industrieit.ohr.array.OHRShortArray; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.Externalizable; import javassist.CtClass; import javassist.CtMethod; import java.io.File; import java.io.IOException; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; import java.util.List; import java.util.StringTokenizer; import javassist.CannotCompileException; import static javassist.ClassPool.getDefault; import javassist.CtConstructor; import javassist.CtField; import javassist.CtNewMethod; import javassist.NotFoundException; import org.apache.commons.io.IOUtils; import sun.misc.Unsafe; public class OHRJavassister { private static Class[] cls = new Class[1000]; private static int clcounter = 33; //private static int clincrement=133; private static IdentityHashMap<Class, Class> processed2 = new IdentityHashMap<Class, Class>(); private static Class[] propertyOrdering = { OHRBase.class, OHRLongArray.class, OHRDoubleArray.class, Object.class, long.class, double.class, int.class, OHRIntArray.class, float.class, OHRFloatArray.class, short.class, OHRShortArray.class, String.class, CharSequence.class, byte.class, OHRByteArray.class, boolean.class, OHRBooleanArray.class }; public static Class getClassForId(int id) { return cls[id]; } private static Object incMutex = new Object(); public static int incrementClsCounter() { synchronized (incMutex) { clcounter++; return clcounter; } } static int registerExternalOHR(Class cl) { int cnt = incrementClsCounter(); cls[cnt] = cl; return cnt; } public static Class ohr(Class cll) { try { //System.out.println("++++++++++ "+cll.getName()); /*if (cll.getName().startsWith("ohr.")) { throw new RuntimeException(cll.getName()); }*/ if (processed2.containsKey(cll)) { return processed2.get(cll); } HashSet<Long> handleOffsets = new HashSet<Long>(); String cnam = cll.getName(); if (!cnam.startsWith("ohr.")) { cnam = "ohr." + cll.getName(); //cnam=cnam.substring(4); } Class cl = Class.forName(cnam.substring(4)); int clnumber = incrementClsCounter(); List<Integer> owned = new ArrayList<Integer>(); //remove the old implementation if its around from another process String fname = "target/classes/" + cnam.replace(".", "/") + ".class"; System.out.println("deleted" + fname + " " + (new File(fname).delete())); if (!Modifier.isAbstract(cl.getModifiers())) { throw new RuntimeException("not an abstract class " + cl.getName()); } System.out.println("processing ohr " + cnam); CtClass bc = getDefault().getCtClass(cl.getName()); CtClass cc = getDefault().makeClass(cnam, bc); StringBuilder initBuilder = new StringBuilder(); initBuilder.append("public void internalInit() {\n"); StringBuilder constructBuilder = new StringBuilder(); constructBuilder.append("{"); String intname = OHRBase.class.getName(); System.out.println("intername is " + intname); CtClass ci = getDefault().getCtClass(intname); CtClass extern = getDefault().getCtClass(Externalizable.class.getName()); //cc.addInterface(ci); cc.setInterfaces(new CtClass[] { ci, extern }); cc.setSuperclass(bc); //add base implmenetation methods and properties setBaseMixinsPre(cc, false); //first long for id and other stuff long offset = 8; BeanInfo bi = Introspector.getBeanInfo(cl); PropertyDescriptor[] pds = bi.getPropertyDescriptors(); for (int co = 0; co < propertyOrdering.length; co++) { Class cprop = propertyOrdering[co]; for (int i = 0; i < pds.length; i++) { // Get property name String propName = pds[i].getName(); if (propName.equals("class")) { continue; } String typ = pds[i].getPropertyType().getName(); Class type = pds[i].getPropertyType(); //if (propName.startsWith("fath")) //PL.pl("[[[[["+type+" "+propName+" "+cprop); if (cprop == Object.class) { //handle refs only if (type.isPrimitive()) { continue; } if (type == String.class) { continue; } if (type == CharSequence.class) { continue; } if (type == OHRLongArray.class) { continue; } if (type == OHRIntArray.class) { continue; } if (type == OHRShortArray.class) { continue; } if (type == OHRByteArray.class) { continue; } if (type == OHRBooleanArray.class) { continue; } if (type == OHRDoubleArray.class) { continue; } if (type == OHRFloatArray.class) { continue; } } else if (cprop != type) { //PL.pl("skipping "+type+" "+cprop); continue; } //PL.pl("[[[[[ " + type + " - " + propName + " - " + cprop); //System.out.println("--prop--" + propName); String rname = pds[i].getReadMethod().getName(); String wname = null; if (pds[i].getWriteMethod() != null) { wname = pds[i].getWriteMethod().getName(); } boolean reifread = isMethodReifAnnotated(pds[i].getReadMethod()); boolean reifwrite = isMethodReifAnnotated(pds[i].getWriteMethod()); String wcons = getConsistencyAsString(pds[i].getWriteMethod()); String rcons = getConsistencyAsString(pds[i].getReadMethod()); System.out.println("TYPE " + pds[i].getPropertyType().getName() + " " + pds[i].getPropertyType().getInterfaces()); if (pds[i].getPropertyType() == String.class && isInlineString(pds[i])) { //NOTE - only for inline strings - normal strings are handled as extrefs like any other object System.out.println("ITS An inline string!!!!"); int length = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class).length(); boolean trim = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class) .trimOverflow(); boolean ascii = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class).asciiOnly(); String wmeth = "public void " + wname + "(" + typ + " o) { ohwritestr" + wcons + "(" + offset + "l,o," + length + "," + trim + "," + ascii + "); }"; //add setter CtMethod wmethod = CtNewMethod.make(wmeth, cc); cc.addMethod(wmethod); System.out.println(wmeth); String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohreadstr" + rcons + "(" + offset + "l," + ascii + "); }"; //add setter CtMethod rmethod = CtNewMethod.make(rmeth, cc); //rmethod.getMethodInfo().addAttribute(attr); cc.addMethod(rmethod); System.out.println(rmeth); int bytesperchar = ascii ? 1 : 2; //pad to 16 bits int ll = 4 + length * bytesperchar; if (ll % 2 != 0) { ll++; } offset += ll; //lebgth marker as well as unicode 16 encoded characters } else if (pds[i].getPropertyType() == CharSequence.class && isInlineString(pds[i])) { //NOTE - only for inline strings - normal strings are handled as extrefs like any other object System.out.println("ITS An inline charsequence!!!!"); int length = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class).length(); boolean trim = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class) .trimOverflow(); boolean ascii = pds[i].getWriteMethod().getAnnotation(InlineStringReify.class).asciiOnly(); String wmeth = "public void " + wname + "(" + typ + " o) { ohwritestr" + wcons + "(" + offset + "l,o," + length + "," + trim + "," + ascii + "); }"; //add setter CtMethod wmethod = CtNewMethod.make(wmeth, cc); cc.addMethod(wmethod); System.out.println(wmeth); String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohreadcs" + rcons + "(" + offset + "l," + ascii + "); }"; //add setter CtMethod rmethod = CtNewMethod.make(rmeth, cc); //rmethod.getMethodInfo().addAttribute(attr); cc.addMethod(rmethod); System.out.println(rmeth); int bytesperchar = ascii ? 1 : 2; //pad to 8 byte boundary! int ll = (int) Math.ceil((4.0 + length * bytesperchar) / 8) * 8; offset += ll; //lebgth marker as well as unicode 16 encoded characters } else if ((pds[i].getPropertyType() == OHRLongArray.class || pds[i].getPropertyType() == OHRIntArray.class || pds[i].getPropertyType() == OHRShortArray.class || pds[i].getPropertyType() == OHRByteArray.class || pds[i].getPropertyType() == OHRFloatArray.class || pds[i].getPropertyType() == OHRDoubleArray.class || pds[i].getPropertyType() == OHRBooleanArray.class) && pds[i].getReadMethod().isAnnotationPresent(InlineArrayReify.class)) { int bitsperitem = 0; String cldef = null; Class at = pds[i].getPropertyType(); boolean unchecked = pds[i].getReadMethod().isAnnotationPresent(UncheckedBoundsXXX.class); if (at == OHRLongArray.class) { bitsperitem = 8 * 8; cldef = LongInlineOHRArray.class.getName(); } else if (at == OHRIntArray.class) { bitsperitem = 4 * 8; //cldef=IntInlineOHRArrayCop.class.getName(); if (unchecked) { cldef = IntInlineOHRArrayUnchecked.class.getName(); } else { cldef = IntInlineOHRArray.class.getName(); } } if (at == OHRDoubleArray.class) { bitsperitem = 8 * 8; cldef = DoubleInlineOHRArray.class.getName(); } if (at == OHRFloatArray.class) { bitsperitem = 4 * 8; cldef = FloatInlineOHRArray.class.getName(); } if (at == OHRShortArray.class) { bitsperitem = 2 * 8; cldef = ShortInlineOHRArray.class.getName(); } if (at == OHRByteArray.class) { bitsperitem = 1 * 8; cldef = ByteInlineOHRArray.class.getName(); } if (at == OHRBooleanArray.class) { bitsperitem = 1; cldef = BooleanInlineOHRArray.class.getName(); } //NOTE - only for inline strings - normal strings are handled as extrefs like any other object System.out.println("ITS An inline array!!!!"); int length = pds[i].getReadMethod().getAnnotation(InlineArrayReify.class).length(); long bytealloc = OHRInlineArrayHandler.getGenericArrayAllocationSize(bitsperitem, length); //PL.pl("byte allocation for logn array length "+length+" "+bytealloc); CtClass ctc = getDefault().getCtClass(cldef); String varname = "var" + i; CtField cf = new CtField(ctc, varname, cc); cf.setModifiers(Modifier.PRIVATE); cc.addField(cf); //add data to constructor initBuilder.append( "com.industrieit.ohr.OHRInlineArrayHandler.initialiseInlineGenericArray(this.basePtr+" + offset + "l," + length + "l," + bitsperitem + ");\n"); constructBuilder.append(varname + "=new " + cldef + "(this," + offset + "l);\n"); //+ "//this.basePtr"+offset+"l);"); //String wmeth = "public void " + wname + "(" + typ + " o) { throw new java.lang.RuntimeException(\"not supported\"); }"; //add setter //CtMethod wmethod = CtNewMethod.make(wmeth, cc); //cc.addMethod(wmethod); //System.out.println(wmeth); String rmeth = "public " + typ + " " + rname + "() { return " + varname + "; }"; //add setter CtMethod rmethod = CtNewMethod.make(rmeth, cc); //rmethod.getMethodInfo().addAttribute(attr); cc.addMethod(rmethod); System.out.println("||||||||" + rmeth + "|||||||||"); offset += bytealloc; } else if (pds[i].getPropertyType().isPrimitive()) { //PL.pl("ITS A PRIMITIVE!"); int vv = 0; if (cprop == long.class) { vv = 8; } if (cprop == double.class) { vv = 8; } if (cprop == int.class) { vv = 4; } if (cprop == float.class) { vv = 4; } if (cprop == short.class) { vv = 2; } if (cprop == byte.class) { vv = 1; } System.out.println( "for " + pds[i].getName() + " typ is " + pds[i].getPropertyType().getName()); String wmeth = "public void " + wname + "(" + typ + " o) { ohwrite" + wcons + "(" + offset + "l,o); }"; //add setter //ConstPool constpool = cc.getClassFile().getConstPool(); if (reifwrite) { CtMethod wmethod = CtNewMethod.make(wmeth, cc); cc.addMethod(wmethod); System.out.println("&&&&&&&" + wmeth); } String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohread" + typ + rcons + "(" + offset + "l); }"; //add setter //rmethod.getMethodInfo().addAttribute(attr); if (reifread) { CtMethod rmethod = CtNewMethod.make(rmeth, cc); cc.addMethod(rmethod); System.out.println("&&&&&&&&&&&&&&&&&&&&&&&&&&&" + rmeth + vv); } offset += vv; } else { System.out.println("ITS AN ASSUMED REIFY!!!"); if (pds[i].getWriteMethod().isAnnotationPresent(Owned.class)) { owned.add(i); } //CtClass tc = getDefault().getCtClass(pds[i].getPropertyType().getName()); CtClass tc = getDefault().getCtClass(OHRBase.class.getName()); //String fnam="ohrt"+i; //CtField f = new CtField(tc, fnam, cc); //f.setModifiers(Modifier.PROTECTED); //cc.addField(f); //store by reify //handleOffsets.add(offset); String wmeth = "public void " + wname + "(" + typ + " o) { ohwritere" + wcons + "(" + offset + "l,o); }"; //add setter CtMethod wmethod = CtNewMethod.make(wmeth, cc); if (reifwrite) { cc.addMethod(wmethod); } System.out.println(wmeth); //String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohreadre(" + offset + "l," + typ + ".class); }"; String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohreadre" + rcons + "(" + offset + "l); };"; //add setter CtMethod rmethod = CtNewMethod.make(rmeth, cc); //rmethod.getMethodInfo().addAttribute(attr); if (reifread) { cc.addMethod(rmethod); } System.out.println(rmeth); handleOffsets.add(offset); offset += 8; } /* if (!isReif(type)) { PL.pl(""+pds[i].getName()+" is a non reified handle!!!!"); //store by handle handleOffsets.add(offset); String wmeth = "public void " + wname + "(" + typ + " o) { ohwritehand(" + offset + "l,o); }"; //add setter CtMethod wmethod = CtNewMethod.make(wmeth, cc); if (reifwrite) { cc.addMethod(wmethod); } System.out.println(wmeth); String rmeth = "public " + typ + " " + rname + "() { return (" + typ + ") ohreadhand(" + offset + "l); }"; //add setter CtMethod rmethod = CtNewMethod.make(rmeth, cc); //rmethod.getMethodInfo().addAttribute(attr); if (reifread) { cc.addMethod(rmethod); } System.out.println(rmeth); }*/ } //PL.pl("offset is "+offset); } //offset+=8; //ok create the get handleoffsets method //print out total byts allocated //PL.pl("%%%%%%%%%% TOTAL BYTES = " + offset); StringBuilder sb = new StringBuilder(); sb.append("public long[] handleOffsets() { "); sb.append("long a[] = new long[").append(handleOffsets.size()).append("];"); int c = 0; for (long l : handleOffsets) { sb.append("a[").append(c).append("]=").append(l).append("l;"); c++; } sb.append("return a; }"); System.out.println(sb.toString()); CtMethod om = CtNewMethod.make(sb.toString(), cc); cc.addMethod(om); String sizem = "public long gsize() { return " + (offset) + "l; }"; //PL.pl(sizem); CtMethod sm = CtNewMethod.make(sizem, cc); cc.addMethod(sm); //add clsid CtMethod cmid = CtNewMethod.make("public int ohclassId() { return " + clnumber + "; }", cc); cc.addMethod(cmid); setBaseMixinsPost(cc, false, owned, pds, constructBuilder, initBuilder); cc.writeFile("target/classes"); /*for (Method me : cc.toClass().getDeclaredMethods()) { //test print, ok //System.out.println(me.getName()); }*/ Class ppp = Class.forName(cnam); Field f = ppp.getDeclaredField("u"); f.setAccessible(true); f.set(ppp.newInstance(), USafe.getUnsafe()); //synchronized (mutex) //{ processed2.put(cl, ppp); processed2.put(ppp, ppp); cls[clnumber] = ppp; return ppp; //} } catch (Exception e) { throw new RuntimeException(e); } } private static Object mutex = new Object(); private static HashMap<Class, Boolean> reifcache = new HashMap<Class, Boolean>(); /*private static boolean isOHROverride(PropertyDescriptor pd) { boolean b=false; PL.pl("write method "+pd.getWriteMethod()+" rm "+pd.getReadMethod()); if (pd.getWriteMethod()!=null) { PL.pl("is ohr override write method not false "+pd.getName()); b= pd.getWriteMethod().isAnnotationPresent(OHR.class); } PL.pl("is ohr override "+pd.getName()+" "+b); return b; }*/ private static boolean isAlreadyReifXXXXX(Class cl) { Boolean b = reifcache.get(cl); //PL.pl("jjjjjjjjjjjjj testing reif "+cl+" "+b); if (b != null) { return b.booleanValue(); } reifcache.put(cl, Boolean.TRUE); return true; /*for (Class c : cl.getInterfaces()) { //System.out.println("class "+c.getName()); if (c == OHReify.class) { //System.out.println("inside"); reifcache.put(cl, Boolean.TRUE); return true; } } reifcache.put(cl, Boolean.FALSE); return false;*/ } /*public long[] r() { long a[] = new long[3]; a[1] = 2l; return a; }*/ static Unsafe u; static { u = USafe.getUnsafe(); } private static boolean isMethodReifAnnotated(Method m) { if (m == null) { return false; } return m.isAnnotationPresent(Reify.class); } private static boolean isInlineString(PropertyDescriptor pd) { return pd.getWriteMethod().isAnnotationPresent(InlineStringReify.class); } private static String getConsistencyAsString(Method meth) { return getConsistency(meth).name(); } private static Consistency getConsistency(Method meth) { try { if (meth == null) { return Consistency.NORMAL; } Reify r = (Reify) meth.getAnnotation(Reify.class); return r.consistency(); } catch (Exception e) { //never happens throw new RuntimeException(e); } } private static void setBaseMixinsPre(CtClass cc, boolean includeChecks) throws CannotCompileException, IOException, NotFoundException { //add fields //add basePtr //CtField cf = new CtField(CtClass.longType, "crapField", cc); //cf.setModifiers(Modifier.PRIVATE | Modifier.VOLATILE); //cc.addField(cf); //add basePtr CtField f = new CtField(CtClass.longType, "basePtr", cc); f.setModifiers(Modifier.PRIVATE | Modifier.VOLATILE); cc.addField(f); //add instmarker CtField fm = new CtField(CtClass.intType, "instmarker", cc); fm.setModifiers(Modifier.PRIVATE | Modifier.VOLATILE); cc.addField(fm); CtClass cu = getDefault().get("sun.misc.Unsafe"); CtField f2 = new CtField(cu, "u", cc); f2.setModifiers(Modifier.STATIC | Modifier.PRIVATE); cc.addField(f2); //add check method String checkmeth = "public void doOHRCheck() { if (u.getInt(basePtr+4)!=instmarker) throw new com.industrieit.ohr.StaleHandleException(\"bad instmarker\"); }"; //PL.pl(checkmeth); CtMethod cm = CtNewMethod.make(checkmeth, cc); cc.addMethod(cm); String str = IOUtils.toString(OHRJavassister.class.getResourceAsStream("/ohr/base/basemethodspre.txt")); String checkplace = ""; if (includeChecks) { checkplace = "doOHRCheck();"; } //place in prechecks if desired str = str.replace("<<READCHECK>>", checkplace); str = str.replace("<<WRITECHECK>>", checkplace); StringTokenizer st = new StringTokenizer(str, "~~~"); while (st.hasMoreElements()) { String meth = st.nextToken(); //PL.pl("adding method: " + meth); CtMethod wmethod = CtNewMethod.make(meth, cc); cc.addMethod(wmethod); } } private static void setBaseMixinsPost(CtClass cc, boolean includeChecks, List<Integer> owned, PropertyDescriptor[] pds, StringBuilder constructBuilder, StringBuilder initBuilder) throws CannotCompileException, IOException, NotFoundException { //empty constrcutor constructBuilder.append("}"); System.out.println(constructBuilder.toString()); CtConstructor ctc = new CtConstructor(new CtClass[0], cc); ctc.setBody(constructBuilder.toString()); cc.addConstructor(ctc); initBuilder.append("\n}"); System.out.println(initBuilder.toString()); CtMethod initmeth = CtNewMethod.make(initBuilder.toString(), cc); cc.addMethod(initmeth); StringBuilder sb = new StringBuilder(); sb.append("public void freeOwnedChildren() {"); for (Integer i : owned) { String fnam = pds[i].getName(); sb.append("com.industreiit.ohr.Reifier.freeOHR(" + pds[i].getReadMethod().getName() + "());"); } sb.append("}"); CtMethod owm = CtNewMethod.make(sb.toString(), cc); cc.addMethod(owm); String str = IOUtils.toString(OHRJavassister.class.getResourceAsStream("/ohr/base/basemethodspost.txt")); String checkplace = ""; if (includeChecks) { checkplace = "doOHRCheck();"; } str = str.replace("<<READCHECK>>", checkplace); str = str.replace("<<WRITECHECK>>", checkplace); StringTokenizer st = new StringTokenizer(str, "~~~"); while (st.hasMoreElements()) { String meth = st.nextToken(); //PL.pl("adding method: " + meth); CtMethod wmethod = CtNewMethod.make(meth, cc); cc.addMethod(wmethod); } } }