From 93148a8e5e398930606df0e9e1927a8ea8e6bd57 Mon Sep 17 00:00:00 2001 From: Julius Davies Date: Thu, 23 Aug 2012 14:28:14 +0000 Subject: [PATCH] [LANG-816] introduce CONST() methods to prevent Javac from inlining constants git-svn-id: https://svn.apache.org/repos/asf/commons/proper/lang/trunk@1376505 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/commons/lang3/ObjectUtils.java | 166 ++++++++++++++++++ .../apache/commons/lang3/ObjectUtilsTest.java | 18 ++ 2 files changed, 184 insertions(+) diff --git a/src/main/java/org/apache/commons/lang3/ObjectUtils.java b/src/main/java/org/apache/commons/lang3/ObjectUtils.java index c8b32854e..bcb01ae3f 100644 --- a/src/main/java/org/apache/commons/lang3/ObjectUtils.java +++ b/src/main/java/org/apache/commons/lang3/ObjectUtils.java @@ -605,4 +605,170 @@ private Object readResolve() { } } + + // Constants (LANG-816): + /* + These methods ensure constants are not inlined by javac. + For example, typically a developer might declare a constant like so: + + public final static int MAGIC_NUMBER = 5; + + Should a different jar file refer to this, and the MAGIC_NUMBER + is changed a later date (e.g., MAGIC_NUMBER = 6), the different jar + file will need to recompile itself. This is because javac + typically inlines the primitive or String constant directly into + the bytecode, and removes the reference to the MAGIC_NUMBER field. + + To help the other jar (so that it does not need to recompile + when constants are changed) the original developer can declare + their constant using one of the CONST() utility methods, instead: + + public final static int MAGIC_NUMBER = CONST(5); + */ + + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static boolean MAGIC_FLAG = CONST(true); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the boolean value to return + * @return the boolean v, unchanged + */ + public static boolean CONST(final boolean v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_BYTE = CONST(127); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the byte value to return + * @return the byte v, unchanged + */ + public static byte CONST(final byte v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_CHAR = CONST('a'); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the char value to return + * @return the char v, unchanged + */ + public static char CONST(final char v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_SHORT = CONST(123); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the short value to return + * @return the short v, unchanged + */ + public static short CONST(final short v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_INT = CONST(123); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the int value to return + * @return the int v, unchanged + */ + public static int CONST(final int v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_LONG = CONST(123L); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the long value to return + * @return the long v, unchanged + */ + public static long CONST(final long v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_FLOAT = CONST(1.0f); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the float value to return + * @return the float v, unchanged + */ + public static float CONST(final float v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_DOUBLE = CONST(1.0); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the double value to return + * @return the double v, unchanged + */ + public static double CONST(final double v) { return v; } + + /** + * This method returns the provided value unchanged. + * This can prevent javac from inlining a constant + * field, e.g., + * + * public final static byte MAGIC_STRING = CONST("abc"); + * + * This way any jars that refer to this field do not + * have to recompile themselves if the field's value + * changes at some future date. + * + * @param v the genericized Object value to return (typically a String). + * @return the genericized Object v, unchanged (typically a String). + */ + public static T CONST(final T v) { return v; } + } diff --git a/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java b/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java index e3981e32c..847897708 100644 --- a/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java +++ b/src/test/java/org/apache/commons/lang3/ObjectUtilsTest.java @@ -450,6 +450,24 @@ public void testPossibleCloneOfUncloneable() throws Throwable { } } + @Test + public void testConstMethods() { + + // To truly test the CONST() method, we'd want to look in the + // bytecode to see if the literals were folded into the + // class, or if the bytecode kept the method call. + + assertEquals("CONST(boolean)", true, ObjectUtils.CONST(true)); + assertEquals("CONST(byte)", (byte) 3, ObjectUtils.CONST((byte) 3)); + assertEquals("CONST(char)", (char) 3, ObjectUtils.CONST((char) 3)); + assertEquals("CONST(short)", (short) 3, ObjectUtils.CONST((short) 3)); + assertEquals("CONST(int)", 3, ObjectUtils.CONST(3)); + assertEquals("CONST(long)", 3l, ObjectUtils.CONST(3l)); + assertEquals("CONST(float)", 3f, ObjectUtils.CONST(3f), 0); + assertEquals("CONST(double)", 3.0, ObjectUtils.CONST(3.0), 0); + assertEquals("CONST(Object)", "abc", ObjectUtils.CONST("abc")); + } + /** * String that is cloneable. */