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);
}
}