@Override protected void onApply(CtBehavior behavior, Bytecode bytecode) throws BadBytecode { bytecode = BytecodeTools.prepareMethodForBytecode(behavior, bytecode); // loop through the opcodes and change any GT/GE opcodes to ICMPGT/ICMPGE CodeIterator iterator = behavior.getMethodInfo().getCodeAttribute().iterator(); while (iterator.hasNext()) { int index = iterator.next(); int opcode = iterator.byteAt(index); switch (opcode) { case Opcode.IFGT: // overwrite the opcode iterator.writeByte(Opcode.IF_ICMPGT, index); // insert the method call iterator.insertAt(index, bytecode.get()); behavior.getMethodInfo().getCodeAttribute().computeMaxStack(); break; case Opcode.IFGE: // overwrite the opcode iterator.writeByte(Opcode.IF_ICMPGE, index); // insert the method call iterator.insertAt(index, bytecode.get()); behavior.getMethodInfo().getCodeAttribute().computeMaxStack(); break; } } }
private String from(Expr expr) { CtBehavior source = expr.where(); return " in " + source.getName() + "(" + expr.getFileName() + ":" + expr.getLineNumber() + ")"; }
/** * Filters the caller methods. * * @param method the method to filter * @return boolean true if the method should be filtered away */ private boolean methodFilterCaller(final CtBehavior method) { if (Modifier.isNative(method.getModifiers()) || Modifier.isInterface(method.getModifiers()) || method.getName().equals(TransformationUtil.GET_META_DATA_METHOD) || method.getName().equals(TransformationUtil.SET_META_DATA_METHOD) || method.getName().equals(TransformationUtil.CLASS_LOOKUP_METHOD) || method.getName().equals(TransformationUtil.GET_UUID_METHOD)) { return true; } else { return false; } }
/** * Return javaassist source snippet which lists all the parameters and their values. If available * the source names are extracted from the debug information and used, otherwise just a number is * shown. * * @param method * @return * @throws NotFoundException */ public static String getSignature(CtBehavior method) throws NotFoundException { CtClass[] parameterTypes = method.getParameterTypes(); CodeAttribute codeAttribute = method.getMethodInfo().getCodeAttribute(); LocalVariableAttribute locals = null; if (codeAttribute != null) { AttributeInfo attribute; attribute = codeAttribute.getAttribute("LocalVariableTable"); locals = (LocalVariableAttribute) attribute; } String methodName = method.getName(); StringBuilder sb = new StringBuilder(methodName).append("(\" "); for (int i = 0; i < parameterTypes.length; i++) { if (i > 0) { // add a comma and a space between printed values sb.append(" + \", \" "); } CtClass parameterType = parameterTypes[i]; boolean isArray = parameterType.isArray(); CtClass arrayType = parameterType.getComponentType(); if (isArray) { while (arrayType.isArray()) { arrayType = arrayType.getComponentType(); } } sb.append(" + \""); try { sb.append(parameterNameFor(method, locals, i)); } catch (Exception e) { sb.append(i + 1); } sb.append("\" + \"="); if (parameterType.isPrimitive()) { // let the compiler handle primitive -> string sb.append("\"+ $").append(i + 1); } else { String s = "org.slf4j.instrumentation.ToStringHelper.render"; sb.append("\"+ ").append(s).append("($").append(i + 1).append(')'); } } sb.append("+\")"); String signature = sb.toString(); return signature; }
/** * Determine the name of parameter with index i in the given method. Use the locals attributes * about local variables from the classfile. Note: This is still work in progress. * * @param method * @param locals * @param i * @return the name of the parameter if available or a number if not. */ static String parameterNameFor(CtBehavior method, LocalVariableAttribute locals, int i) { if (locals == null) { return Integer.toString(i + 1); } int modifiers = method.getModifiers(); int j = i; if (Modifier.isSynchronized(modifiers)) { // skip object to synchronize upon. j++; // System.err.println("Synchronized"); } if (Modifier.isStatic(modifiers) == false) { // skip "this" j++; // System.err.println("Instance"); } String variableName = locals.variableName(j); // if (variableName.equals("this")) { // System.err.println("'this' returned as a parameter name for " // + method.getName() + " index " + j // + // ", names are probably shifted. Please submit source for class in slf4j bugreport"); // } return variableName; }
public boolean isTarget(CtClass self, CtBehavior behavior) { try { CtClass[] params = behavior.getParameterTypes(); return params.length == 2 && params[0] == CtClass.intType && params[1] == CtClass.intType; } catch (NotFoundException e) { return false; } }
@Override public void enhanceThisClass(ApplicationClass applicationClass) throws Exception { final CtClass ctClass = makeClass(applicationClass); if (ctClass.isInterface()) { return; } if (ctClass.getName().endsWith(".package")) { return; } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor && !ctClass.isInterface()) { CtConstructor defaultConstructor = CtNewConstructor.make("public " + ctClass.getSimpleName() + "() {}", ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } if (isScala(applicationClass)) { // Temporary hack for Scala. Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); return; } for (CtField ctField : ctClass.getDeclaredFields()) { try { if (isProperty(ctField)) { // Property name String propertyName = ctField.getName().substring(0, 1).toUpperCase() + ctField.getName().substring(1); String getter = "get" + propertyName; String setter = "set" + propertyName; try { CtMethod ctMethod = ctClass.getDeclaredMethod(getter); if (ctMethod.getParameterTypes().length > 0 || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a getter !"); } } catch (NotFoundException noGetter) { // Créé le getter String code = "public " + ctField.getType().getName() + " " + getter + "() { return this." + ctField.getName() + "; }"; CtMethod getMethod = CtMethod.make(code, ctClass); getMethod.setModifiers(getMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(getMethod); } if (!isFinal(ctField)) { try { CtMethod ctMethod = ctClass.getDeclaredMethod(setter); if (ctMethod.getParameterTypes().length != 1 || !ctMethod.getParameterTypes()[0].equals(ctField.getType()) || Modifier.isStatic(ctMethod.getModifiers())) { throw new NotFoundException("it's not a setter !"); } } catch (NotFoundException noSetter) { // Créé le setter CtMethod setMethod = CtMethod.make( "public void " + setter + "(" + ctField.getType().getName() + " value) { this." + ctField.getName() + " = value; }", ctClass); setMethod.setModifiers(setMethod.getModifiers() | AccessFlag.SYNTHETIC); ctClass.addMethod(setMethod); createAnnotation(getAnnotations(setMethod), PlayPropertyAccessor.class); } } } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } } // Add a default constructor if needed try { boolean hasDefaultConstructor = false; for (CtConstructor constructor : ctClass.getDeclaredConstructors()) { if (constructor.getParameterTypes().length == 0) { hasDefaultConstructor = true; break; } } if (!hasDefaultConstructor) { CtConstructor defaultConstructor = CtNewConstructor.defaultConstructor(ctClass); ctClass.addConstructor(defaultConstructor); } } catch (Exception e) { Logger.error(e, "Error in PropertiesEnhancer"); throw new UnexpectedException("Error in PropertiesEnhancer", e); } // Intercept all fields access for (final CtBehavior ctMethod : ctClass.getDeclaredBehaviors()) { ctMethod.instrument( new ExprEditor() { @Override public void edit(FieldAccess fieldAccess) throws CannotCompileException { try { // Acces à une property ? if (isProperty(fieldAccess.getField())) { // TODO : vérifier que c'est bien un champ d'une classe de l'application // (fieldAccess.getClassName()) // Si c'est un getter ou un setter String propertyName = null; if (fieldAccess .getField() .getDeclaringClass() .equals(ctMethod.getDeclaringClass()) || ctMethod .getDeclaringClass() .subclassOf(fieldAccess.getField().getDeclaringClass())) { if ((ctMethod.getName().startsWith("get") || (!isFinal(fieldAccess.getField()) && ctMethod.getName().startsWith("set"))) && ctMethod.getName().length() > 3) { propertyName = ctMethod.getName().substring(3); propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1); } } // On n'intercepte pas le getter de sa propre property if (propertyName == null || !propertyName.equals(fieldAccess.getFieldName())) { String invocationPoint = ctClass.getName() + "." + ctMethod.getName() + ", line " + fieldAccess.getLineNumber(); if (fieldAccess.isReader()) { // Réécris l'accés en lecture à la property fieldAccess.replace( "$_ = ($r)play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeReadProperty($0, \"" + fieldAccess.getFieldName() + "\", \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } else if (!isFinal(fieldAccess.getField()) && fieldAccess.isWriter()) { // Réécris l'accés en ecriture à la property fieldAccess.replace( "play.classloading.enhancers.PropertiesEnhancer.FieldAccessor.invokeWriteProperty($0, \"" + fieldAccess.getFieldName() + "\", " + fieldAccess.getField().getType().getName() + ".class, $1, \"" + fieldAccess.getClassName() + "\", \"" + invocationPoint + "\");"); } } } } catch (Exception e) { throw new UnexpectedException("Error in PropertiesEnhancer", e); } } }); } // Done. applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.defrost(); }
private void enhance_(ApplicationClass applicationClass, boolean buildAuthorityRegistryOnly) throws Exception { Plugin.trace("about to enhance applicationClass: %s", applicationClass); CtClass ctClass = makeClass(applicationClass); Set<CtBehavior> s = new HashSet<CtBehavior>(); s.addAll(Arrays.asList(ctClass.getDeclaredMethods())); s.addAll(Arrays.asList(ctClass.getMethods())); s.addAll(Arrays.asList(ctClass.getConstructors())); s.addAll(Arrays.asList(ctClass.getDeclaredConstructors())); for (final CtBehavior ctBehavior : s) { if (!Modifier.isPublic(ctBehavior.getModifiers()) || javassist.Modifier.isAbstract(ctBehavior.getModifiers())) { continue; } boolean needsEnhance = false; RequireRight rr = null; RequirePrivilege rp = null; RequireAccounting ra = null; boolean allowSystem = false; Object[] aa = ctBehavior.getAnnotations(); for (Object o : aa) { if (o instanceof RequirePrivilege) { needsEnhance = true; rp = (RequirePrivilege) o; continue; } if (o instanceof RequireRight) { needsEnhance = true; rr = (RequireRight) o; continue; } if (o instanceof AllowSystemAccount) { allowSystem = true; continue; } if (o instanceof RequireAccounting) { needsEnhance = true; ra = (RequireAccounting) o; } } if (!needsEnhance) continue; String key = ctBehavior.getLongName(); String errMsg = String.format("Error enhancing class %s.%s: ", ctClass, ctBehavior); // process rr & rp if (null != rr || null != rp) { // check before/after enhancement Authority.registAuthoriable_(key, rr, rp); if (!buildAuthorityRegistryOnly) { // verify if before attribute of rr and rp is consistent if (null != rr && null != rp && (rr.before() != rp.before())) { String reason = "The before setting of RequireRight and RequirePrivilege doesn't match"; throw new RuntimeException(errMsg + reason); } boolean before = true; if (null != rr) before = rr.before(); if (null != rp) before = rp.before(); // try best to guess the target object String curObj = ""; if (null != rr) { // target object only impact dynamic access checking, hence rr shall not be null boolean isConstructor = ctBehavior instanceof CtConstructor; boolean isStatic = false; if (!isConstructor) isStatic = Modifier.isStatic(ctBehavior.getModifiers()); int paraCnt = ctBehavior.getParameterTypes().length; int id = rr.target(); // calibrate target id if (0 == id) { if (isConstructor) { id = -1; } else if (isStatic) { if (paraCnt > 0) id = 1; else id = -1; } } else if (id > paraCnt) { id = paraCnt; } // speculate cur target statement String sid = null; if (id == -1) sid = "_"; if (id > -1) sid = String.valueOf(id); if (null != sid) { curObj = "play.modules.aaa.PlayDynamicRightChecker.setObjectIfNoCurrent($" + sid + ");"; } if (-1 == id) before = false; } // check permission enhancement if (before) { ctBehavior.insertBefore( curObj + " play.modules.aaa.enhancer.Enhancer.Authority.checkPermission(\"" + key + "\", " + Boolean.toString(allowSystem) + ");"); } else { ctBehavior.insertAfter( curObj + " play.modules.aaa.enhancer.Enhancer.Authority.checkPermission(\"" + key + "\", " + Boolean.toString(allowSystem) + ");"); } } } if (buildAuthorityRegistryOnly) continue; // process ra if (null != ra) { CtClass[] paraTypes = ctBehavior.getParameterTypes(); String sParam = null; if (0 < paraTypes.length) { sParam = "new Object[0]"; } else { sParam = "{$$}"; } String msg = ra.value(); if (null == msg || "".equals(msg)) msg = key; if (ra.before()) { ctBehavior.insertBefore( "play.modules.aaa.utils.Accounting.info(\"" + msg + "\", " + Boolean.toString(allowSystem) + ", " + sParam + ");"); } else { ctBehavior.insertAfter( "play.modules.aaa.utils.Accounting.info(\"" + msg + "\", " + Boolean.toString(allowSystem) + ", " + sParam + ");"); } CtClass etype = ClassPool.getDefault().get("java.lang.Exception"); ctBehavior.addCatch( "{play.modules.aaa.utils.Accounting.error($e, \"" + msg + "\", " + Boolean.toString(allowSystem) + ", " + sParam + "); throw $e;}", etype); } } if (buildAuthorityRegistryOnly) return; applicationClass.enhancedByteCode = ctClass.toBytecode(); ctClass.detach(); }