You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2011/04/15 03:07:14 UTC

svn commit: r1092562 - in /tapestry/tapestry5/trunk: plastic/src/main/java/org/apache/tapestry5/internal/plastic/ plastic/src/main/java/org/apache/tapestry5/plastic/ tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ tapestry-ioc/sr...

Author: hlship
Date: Fri Apr 15 01:07:13 2011
New Revision: 1092562

URL: http://svn.apache.org/viewvc?rev=1092562&view=rev
Log:
TAP5-853: Re-implement ChainBuilder using PlasticProxyFactory

Modified:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImpl.java
    tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImplTest.java

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java?rev=1092562&r1=1092561&r2=1092562&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/internal/plastic/InstructionBuilderImpl.java Fri Apr 15 01:07:13 2011
@@ -60,6 +60,17 @@ public class InstructionBuilderImpl exte
         m.put(Condition.GREATER, IF_ICMPLE);
     }
 
+    private static final Map<String, Integer> typeToSpecialComparisonOpcode = new HashMap<String, Integer>();
+
+    static
+    {
+        Map<String, Integer> m = typeToSpecialComparisonOpcode;
+
+        m.put("long", LCMP);
+        m.put("float", FCMPL);
+        m.put("double", DCMPL);
+    }
+
     private static final Map<Object, Integer> constantOpcodes = new HashMap<Object, Integer>();
 
     static
@@ -764,6 +775,38 @@ public class InstructionBuilderImpl exte
         return this;
     }
 
+    public InstructionBuilder dupeWide()
+    {
+        check();
+
+        v.visitInsn(DUP2);
+
+        return this;
+    }
+
+    public InstructionBuilder popWide()
+    {
+        check();
+
+        v.visitInsn(POP2);
+
+        return this;
+    }
+
+    public InstructionBuilder compareSpecial(String typeName)
+    {
+        check();
+
+        Integer opcode = typeToSpecialComparisonOpcode.get(typeName);
+
+        if (opcode == null)
+            throw new IllegalArgumentException(String.format("Not a special primitive type: '%s'.", typeName));
+
+        v.visitInsn(opcode);
+
+        return this;
+    }
+
     private void runSubBuilder(InstructionBuilderCallback callback)
     {
         new InstructionBuilderImpl(state).doCallback(callback);

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java?rev=1092562&r1=1092561&r2=1092562&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java Fri Apr 15 01:07:13 2011
@@ -280,6 +280,14 @@ public interface InstructionBuilder
     @Opcodes("DUP, DUP_X1, DUP_X2")
     InstructionBuilder dupe(int depth);
 
+    /** Duplicates a wide value (a primitive long or double). */
+    @Opcodes("DUP2")
+    InstructionBuilder dupeWide();
+
+    /** Pops a wide value (a primitive long or double). */
+    @Opcodes("POP2")
+    InstructionBuilder popWide();
+
     /**
      * Duplicates the top object on the stack. Commonly used with {@link #when(Condition, WhenCallback)}.
      * 
@@ -465,4 +473,14 @@ public interface InstructionBuilder
     /** Expects the top object on the stack to be an array. Replaces it with the length of that array. */
     @Opcodes("ARRAYLENGTH")
     InstructionBuilder arrayLength();
+
+    /**
+     * Special comparison logic for primitive float, double and long. Expect two matching wide values
+     * on the stack. Reduces the two wide values to a single int value: -1, 0, or 1 depending on whether the deeper
+     * value is less than, equal to, or greater than the top value on the stack.
+     * 
+     * @param typeName
+     */
+    @Opcodes("LCMP, FCMPL, DCMPL")
+    InstructionBuilder compareSpecial(String typeName);
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImpl.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImpl.java?rev=1092562&r1=1092561&r2=1092562&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImpl.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/main/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImpl.java Fri Apr 15 01:07:13 2011
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007, 2008 The Apache Software Foundation
+// Copyright 2006, 2007, 2008, 2011 The Apache Software Foundation
 //
 // 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
+// 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,
@@ -14,189 +14,123 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
-import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newConcurrentMap;
-import org.apache.tapestry5.ioc.services.*;
-import org.apache.tapestry5.ioc.util.BodyBuilder;
-
-import static java.lang.String.format;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.Modifier;
+import java.lang.reflect.Array;
+import java.lang.reflect.Method;
 import java.util.List;
-import java.util.Map;
+
+import org.apache.tapestry5.ioc.services.Builtin;
+import org.apache.tapestry5.ioc.services.ChainBuilder;
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
+import org.apache.tapestry5.plastic.ClassInstantiator;
+import org.apache.tapestry5.plastic.Condition;
+import org.apache.tapestry5.plastic.InstructionBuilder;
+import org.apache.tapestry5.plastic.InstructionBuilderCallback;
+import org.apache.tapestry5.plastic.PlasticClass;
+import org.apache.tapestry5.plastic.PlasticClassTransformer;
+import org.apache.tapestry5.plastic.PlasticField;
+import org.apache.tapestry5.plastic.WhenCallback;
 
 public class ChainBuilderImpl implements ChainBuilder
 {
-    private final ClassFactory classFactory;
-
-    /**
-     * Map, keyed on service interface, of implementation Class.
-     */
+    private final PlasticProxyFactory proxyFactory;
 
-    private final Map<Class, Class> cache = newConcurrentMap();
-
-    public ChainBuilderImpl(@Builtin ClassFactory classFactory)
+    public ChainBuilderImpl(@Builtin
+    PlasticProxyFactory proxyFactory)
     {
-        this.classFactory = classFactory;
+        this.proxyFactory = proxyFactory;
     }
 
     @SuppressWarnings("unchecked")
-    public <T> T build(Class<T> commandInterface, List<T> commands)
-    {
-        Class<T> chainClass = findImplementationClass(commandInterface);
-
-        return createInstance(chainClass, commands);
-    }
-
-    private Class findImplementationClass(Class commandInterface)
-    {
-        Class result = cache.get(commandInterface);
-
-        if (result == null)
-        {
-            result = constructImplementationClass(commandInterface);
-            cache.put(commandInterface, result);
-        }
-
-        return result;
-    }
-
-    private Class constructImplementationClass(Class commandInterface)
+    public <T> T build(final Class<T> commandInterface, List<T> commands)
     {
-        // In rare, rare cases, a race condition to create an implementation class
-        // for the same interface may occur. We just let that happen, and there'll
-        // be two different classes corresponding to the same interface.
-
-        String name = ClassFabUtils.generateClassName(commandInterface);
-
-        ClassFab cf = classFactory.newClass(name, Object.class);
-
-        addInfrastructure(cf, commandInterface);
-
-        addMethods(cf, commandInterface);
-
-        return cf.createClass();
-    }
-
-    private void addInfrastructure(ClassFab cf, Class commandInterface)
-    {
-        // Array types are very, very tricky to deal with.
-        // Also, generics don't help (<T> new T[]) is still java.lang.Object[].
-
-        String arrayClassName = commandInterface.getCanonicalName() + "[]";
-        String jvmName = ClassFabUtils.toJVMBinaryName(arrayClassName);
-
-        Class array;
-
-        try
-        {
-            ClassLoader loader = commandInterface.getClass().getClassLoader();
-            if (loader == null) loader = Thread.currentThread().getContextClassLoader();
+        // Jump through some hoops to convert the list into an array of the proper type
 
-            array = Class.forName(jvmName, true, loader);
-        }
-        catch (Exception ex)
-        {
-            throw new RuntimeException(ex);
-        }
-
-        cf.addInterface(commandInterface);
-        cf.addField("_commands", Modifier.PRIVATE | Modifier.FINAL, array);
+        Object[] array = (Object[]) Array.newInstance(commandInterface, commands.size());
 
-        BodyBuilder builder = new BodyBuilder();
-        builder.addln("_commands = (%s[]) $1.toArray(new %<s[0]);", commandInterface.getName());
-
-        cf.addConstructor(new Class[] { List.class }, null, builder.toString());
-    }
+        final Object[] commandsArray = commands.toArray(array);
 
-    @SuppressWarnings("unchecked")
-    private <T> T createInstance(Class<T> instanceClass, List<T> commands)
-    {
-        try
+        ClassInstantiator<T> instantiator = proxyFactory.createProxy(commandInterface, new PlasticClassTransformer()
         {
-            Constructor<T> ctor = (Constructor<T>) instanceClass.getConstructors()[0];
+            public void transform(PlasticClass plasticClass)
+            {
+                PlasticField commandsField = plasticClass.introduceField(commandsArray.getClass(), "commands").inject(
+                        commandsArray);
 
-            return instanceClass.cast(ctor.newInstance(commands));
-        }
-        catch (Exception ex)
-        {
-            // This should not be reachable!
-            throw new RuntimeException(ex);
-        }
-
-    }
-
-    private void addMethods(ClassFab cf, Class commandInterface)
-    {
-        MethodIterator mi = new MethodIterator(commandInterface);
-
-        while (mi.hasNext())
-        {
-            MethodSignature sig = mi.next();
+                for (Method method : commandInterface.getMethods())
+                {
+                    implementMethod(plasticClass, method, commandsField);
+                }
 
-            addMethod(cf, commandInterface, sig);
-        }
+                plasticClass.addToString(String.format("<Command chain of %s>", commandInterface.getName()));
+            }
+        });
 
-        if (!mi.getToString()) cf.addToString(format("<Command chain of %s>", commandInterface.getName()));
+        return instantiator.newInstance();
     }
 
-    private void addMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
+    private void implementMethod(PlasticClass plasticClass, final Method method, final PlasticField commandsField)
     {
-        Class returnType = sig.getReturnType();
-
-        if (returnType.equals(void.class))
+        plasticClass.introduceMethod(method).changeImplementation(new InstructionBuilderCallback()
         {
-            addVoidMethod(cf, commandInterface, sig);
-            return;
-        }
-
-        String defaultValue = defaultForReturnType(returnType);
-
-        BodyBuilder builder = new BodyBuilder();
-        builder.begin();
-
-        builder.addln("%s result = %s;", ClassFabUtils.toJavaClassName(returnType), defaultValue);
-        builder.addln("for (int i = 0; i < _commands.length; i++)");
-
-        builder.begin();
-        builder.addln("result = _commands[i].%s($$);", sig.getName());
-
-        builder.addln("if (result != %s) break;", defaultValue);
+            public void doBuild(InstructionBuilder builder)
+            {
+                builder.loadThis().getField(commandsField).iterateArray(new InstructionBuilderCallback()
+                {
+                    public void doBuild(InstructionBuilder builder)
+                    {
+                        // The command is on the stack; add the elements and invoke the method.
 
-        builder.end();
+                        builder.loadArguments().invoke(method);
 
-        builder.addln("return result;");
-        builder.end();
+                        Class returnType = method.getReturnType();
 
-        cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
-    }
-
-    private String defaultForReturnType(Class returnType)
-    {
-        // For all object and array types.
+                        if (returnType == void.class)
+                            return;
 
-        if (!returnType.isPrimitive()) return "null";
+                        final boolean wide = returnType == long.class || returnType == double.class;
 
-        if (returnType.equals(boolean.class)) return "false";
+                        if (wide)
+                            builder.dupeWide();
+                        else
+                            builder.dupe();
 
-        // Assume, then, that it is a numeric type (this method
-        // isn't called for type void). Javassist seems to be
-        // able to handle 0 for all numeric types.
+                        if (returnType == float.class)
+                        {
+                            builder.loadConstant(0f).compareSpecial("float");
+                        }
 
-        return "0";
-    }
+                        if (returnType == long.class)
+                        {
+                            builder.loadConstant(0l).compareSpecial("long");
+                        }
 
-    private void addVoidMethod(ClassFab cf, Class commandInterface, MethodSignature sig)
-    {
-        BodyBuilder builder = new BodyBuilder();
+                        if (returnType == double.class)
+                        {
+                            builder.loadConstant(0d).compareSpecial("double");
+                        }
 
-        builder.begin();
+                        Condition condition = returnType.isPrimitive() ? Condition.NON_ZERO : Condition.NON_NULL;
 
-        builder.addln("for (int i = 0; i < _commands.length; i++)");
-        builder.addln("  _commands[i].%s($$);", sig.getName());
+                        builder.when(condition, new WhenCallback()
+                        {
+                            public void ifTrue(InstructionBuilder builder)
+                            {
+                                builder.returnResult();
+                            }
 
-        builder.end();
+                            public void ifFalse(InstructionBuilder builder)
+                            {
+                                if (wide)
+                                    builder.popWide();
+                                else
+                                    builder.pop();
+                            }
+                        });
+                    }
+                });
 
-        cf.addMethod(Modifier.PUBLIC, sig, builder.toString());
+                builder.returnDefaultValue();
+            }
+        });
     }
-
 }

Modified: tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImplTest.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImplTest.java?rev=1092562&r1=1092561&r2=1092562&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImplTest.java (original)
+++ tapestry/tapestry5/trunk/tapestry-ioc/src/test/java/org/apache/tapestry5/ioc/internal/services/ChainBuilderImplTest.java Fri Apr 15 01:07:13 2011
@@ -1,10 +1,10 @@
-// Copyright 2006, 2007 The Apache Software Foundation
+// Copyright 2006, 2007, 2011 The Apache Software Foundation
 //
 // 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
+// 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,
@@ -14,18 +14,16 @@
 
 package org.apache.tapestry5.ioc.internal.services;
 
+import java.util.Arrays;
+import java.util.List;
+
 import org.apache.tapestry5.ioc.internal.IOCInternalTestCase;
 import org.apache.tapestry5.ioc.services.ChainBuilder;
 import org.testng.Assert;
 import org.testng.annotations.Test;
 
-import java.util.Arrays;
-import java.util.List;
-
 public class ChainBuilderImplTest extends IOCInternalTestCase
 {
-    private final ChainBuilder builder = new ChainBuilderImpl(new ClassFactoryImpl());
-
     @Test
     public void simple_void_method()
     {
@@ -151,41 +149,11 @@ public class ChainBuilderImplTest extend
         return newMock(ChainCommand.class);
     }
 
-    @Test
-    public void fabricated_classes_are_reused()
-    {
-        Runnable r1 = mockRunnable();
-        Runnable r2 = mockRunnable();
-
-        Runnable chain1 = build(Runnable.class, r1);
-        Runnable chain2 = build(Runnable.class, r2);
-
-        Assert.assertSame(chain1.getClass(), chain2.getClass());
-
-        // Now make sure that the two instances are independent.
-
-        r1.run();
-
-        replay();
-
-        chain1.run();
-
-        verify();
-
-        r2.run();
-
-        replay();
-
-        chain2.run();
-
-        verify();
-    }
-
     private <T> T build(Class<T> commandInterface, T... commands)
     {
         List<T> list = Arrays.asList(commands);
 
-        return builder.build(commandInterface, list);
+        return getService(ChainBuilder.class).build(commandInterface, list);
     }
 
 }