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:06:57 UTC

svn commit: r1092559 - in /tapestry/tapestry5/trunk/plastic/src: main/java/org/apache/tapestry5/internal/plastic/ main/java/org/apache/tapestry5/plastic/ test/groovy/org/apache/tapestry5/plastic/ test/java/testsubjects/

Author: hlship
Date: Fri Apr 15 01:06:56 2011
New Revision: 1092559

URL: http://svn.apache.org/viewvc?rev=1092559&view=rev
Log:
TAP5-853: Add new methods to InstructionBuilder to support simple while loops and array iteration

Added:
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/WhileCallback.java
      - copied, changed from r1092558, tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java
    tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/WhileSubject.java
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/Condition.java
    tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/InstructionBuilder.java
    tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodImplementationTests.groovy

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=1092559&r1=1092558&r2=1092559&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:06:56 2011
@@ -23,7 +23,6 @@ import org.apache.tapestry5.internal.pla
 import org.apache.tapestry5.internal.plastic.asm.Opcodes;
 import org.apache.tapestry5.internal.plastic.asm.Type;
 import org.apache.tapestry5.plastic.Condition;
-import org.apache.tapestry5.plastic.WhenCallback;
 import org.apache.tapestry5.plastic.InstructionBuilder;
 import org.apache.tapestry5.plastic.InstructionBuilderCallback;
 import org.apache.tapestry5.plastic.MethodDescription;
@@ -32,6 +31,8 @@ import org.apache.tapestry5.plastic.Plas
 import org.apache.tapestry5.plastic.PlasticUtils;
 import org.apache.tapestry5.plastic.SwitchCallback;
 import org.apache.tapestry5.plastic.TryCatchCallback;
+import org.apache.tapestry5.plastic.WhenCallback;
+import org.apache.tapestry5.plastic.WhileCallback;
 
 @SuppressWarnings("rawtypes")
 public class InstructionBuilderImpl extends Lockable implements Opcodes, InstructionBuilder
@@ -50,6 +51,10 @@ public class InstructionBuilderImpl exte
         m.put(Condition.NON_NULL, IFNULL);
         m.put(Condition.ZERO, IFNE);
         m.put(Condition.NON_ZERO, IFEQ);
+        m.put(Condition.EQUAL, IF_ICMPNE);
+        m.put(Condition.NOT_EQUAL, IF_ICMPEQ);
+        m.put(Condition.LESS_THAN, IF_ICMPGE);
+        m.put(Condition.GREATER, IF_ICMPLE);
     }
 
     private static final Map<Object, Integer> constantOpcodes = new HashMap<Object, Integer>();
@@ -355,7 +360,7 @@ public class InstructionBuilderImpl exte
     {
         check();
 
-        v.visitLdcInsn(index);
+        loadConstant(index);
 
         PrimitiveType type = PrimitiveType.getByName(elementType);
 
@@ -371,6 +376,15 @@ public class InstructionBuilderImpl exte
         return this;
     }
 
+    public InstructionBuilder loadArrayElement()
+    {
+        check();
+
+        v.visitInsn(AALOAD);
+
+        return this;
+    }
+
     public InstructionBuilder checkcast(String className)
     {
         check();
@@ -577,7 +591,7 @@ public class InstructionBuilderImpl exte
 
         LocalVariable prior = state.locals.put(name, var);
 
-        new InstructionBuilderImpl(state).doCallback(callback);
+        runSubBuilder(callback);
 
         v.visitLabel(end);
 
@@ -642,7 +656,7 @@ public class InstructionBuilderImpl exte
         });
     }
 
-    public InstructionBuilder when(Condition condition, WhenCallback callback)
+    public InstructionBuilder when(Condition condition, final WhenCallback callback)
     {
         check();
 
@@ -654,27 +668,144 @@ public class InstructionBuilderImpl exte
 
         v.visitJumpInsn(conditionToOpcode.get(condition), ifFalseLabel);
 
-        InstructionBuilderImpl trueBuilder = new InstructionBuilderImpl(state);
-
-        callback.ifTrue(trueBuilder);
-
-        trueBuilder.lock();
+        runSubBuilder(new InstructionBuilderCallback()
+        {
+            public void doBuild(InstructionBuilder builder)
+            {
+                callback.ifTrue(builder);
+            }
+        });
 
         v.visitJumpInsn(GOTO, endIfLabel);
 
         v.visitLabel(ifFalseLabel);
 
-        InstructionBuilderImpl falseBuilder = new InstructionBuilderImpl(state);
+        runSubBuilder(new InstructionBuilderCallback()
+        {
+            public void doBuild(InstructionBuilder builder)
+            {
+                callback.ifFalse(builder);
+            }
+        });
 
-        callback.ifFalse(falseBuilder);
+        v.visitLabel(endIfLabel);
 
-        falseBuilder.lock();
+        return this;
+    }
 
-        v.visitLabel(endIfLabel);
+    public InstructionBuilder doWhile(Condition condition, final WhileCallback callback)
+    {
+        check();
+
+        assert condition != null;
+        assert callback != null;
+
+        Label doCheck = state.newLabel();
+
+        Label exitLoop = new Label();
+
+        runSubBuilder(new InstructionBuilderCallback()
+        {
+            public void doBuild(InstructionBuilder builder)
+            {
+                callback.buildTest(builder);
+            }
+        });
+
+        v.visitJumpInsn(conditionToOpcode.get(condition), exitLoop);
+
+        runSubBuilder(new InstructionBuilderCallback()
+        {
+
+            public void doBuild(InstructionBuilder builder)
+            {
+                callback.buildBody(builder);
+            }
+        });
+
+        v.visitJumpInsn(GOTO, doCheck);
+
+        v.visitLabel(exitLoop);
+
+        return this;
+    }
+
+    public InstructionBuilder increment(String name)
+    {
+        check();
+
+        LocalVariable var = state.locals.get(name);
+
+        v.visitIincInsn(var.index, 1);
+
+        return this;
+    }
+
+    public InstructionBuilder arrayLength()
+    {
+        check();
+
+        v.visitInsn(ARRAYLENGTH);
+
+        return this;
+    }
+
+    public InstructionBuilder iterateArray(final InstructionBuilderCallback callback)
+    {
+        final String name = newUniqueLocalVarName("i");
+
+        startVariable(name, "int", new InstructionBuilderCallback()
+        {
+            public void doBuild(InstructionBuilder builder)
+            {
+                builder.loadConstant(0).storeVariable(name);
+
+                builder.doWhile(Condition.LESS_THAN, new WhileCallback()
+                {
+                    public void buildTest(InstructionBuilder builder)
+                    {
+                        builder.dupe().arrayLength();
+                        builder.loadVariable(name).swap();
+                    }
+
+                    public void buildBody(InstructionBuilder builder)
+                    {
+                        builder.dupe().loadVariable(name).loadArrayElement();
+
+                        callback.doBuild(builder);
+                        
+                        builder.increment(name);
+                    }
+                });
+            }
+        });
 
         return this;
     }
 
+    private String newUniqueLocalVarName(String base)
+    {
+        if (!state.locals.containsKey(base))
+            return base;
+
+        int index = 0;
+
+        while (true)
+        {
+            String name = base + "_" + index;
+
+            if (!state.locals.containsKey(name))
+                return name;
+
+            index++;
+        }
+    }
+
+    private void runSubBuilder(InstructionBuilderCallback callback)
+    {
+        new InstructionBuilderImpl(state).doCallback(callback);
+    }
+
     void doCallback(InstructionBuilderCallback callback)
     {
         check();

Modified: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java?rev=1092559&r1=1092558&r2=1092559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java Fri Apr 15 01:06:56 2011
@@ -15,9 +15,44 @@
 package org.apache.tapestry5.plastic;
 
 /**
- * Condition used with {@link InstructionBuilder#when(Condition, WhenCallback)}.
+ * Condition used with {@link InstructionBuilder#when(Condition, WhenCallback)}. Most conditions
+ * pop the top element off the stack; some pop two elements.
  */
 public enum Condition
 {
-    NULL, NON_NULL, ZERO, NON_ZERO;
+    /** Is the top element of the stack null? */
+    NULL,
+
+    /** Is the top element of the stack non-null? */
+    NON_NULL,
+
+    /** Is the top element of the stack the integer zero? */
+    ZERO,
+
+    /** Is the top element of the stack not the integer zero? */
+    NON_ZERO,
+
+    /**
+     * Compare two integer elements on the stack; branch if the deeper
+     * element is less than the top element.
+     */
+    LESS_THAN,
+
+    /**
+     * Compare two integer elements on the stack; branch if the deeper
+     * element equal to the top element.
+     */
+    EQUAL,
+
+    /**
+     * Compare two integer elements on the stack; branch if the deeper
+     * element is not equal to the top element.
+     */
+    NOT_EQUAL,
+
+    /**
+     * Compare two integer elements on the stack; branch if the deeper
+     * element is greater than the top element.
+     */
+    GREATER;
 }

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=1092559&r1=1092558&r2=1092559&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:06:56 2011
@@ -191,7 +191,7 @@ public interface InstructionBuilder
      * Loads a value from an array object, which must be the top element of the stack.
      * 
      * @param index
-     *            into the array
+     *            constant index into the array
      * @param elementType
      *            the type name of the elements of the array
      *            <strong>Note: currently only reference types (objects and arrays) are supported, not
@@ -201,6 +201,13 @@ public interface InstructionBuilder
     InstructionBuilder loadArrayElement(int index, String elementType);
 
     /**
+     * Loads a value from an array object. The stack should have the array at depth 1, and an array index
+     * on top. Only object arrays (not arrays of primitives) are supported.
+     */
+    @Opcodes("AALOAD")
+    InstructionBuilder loadArrayElement();
+
+    /**
      * Adds a check that the object on top of the stack is assignable to the indicated class.
      * 
      * @param className
@@ -417,9 +424,46 @@ public interface InstructionBuilder
      *            to evaluate
      * @param ifTrue
      *            generates code for when condition is true
-     * @return
+     * @return this builder
      */
     @Opcodes("IFEQ, etc., GOTO")
     InstructionBuilder when(Condition condition, InstructionBuilderCallback ifTrue);
 
+    /**
+     * Implements a simple loop based on a condition. First the {@linkplain WhileCallback#buildTest(InstructionBuilder)}
+     * code is executed, then the condition is evaluated (which will consume at least the top value on the stack).
+     * When the condition is false, the loop is exited. When the condition is true, the code defined by
+     * {@link WhileCallback#buildBody(InstructionBuilder)} is executed, and then a GOTO back to the test code.
+     * 
+     * @param condition
+     * @param callback
+     * @return this builder
+     */
+    @Opcodes("IFEQ, etc., GOTO")
+    InstructionBuilder doWhile(Condition condition, WhileCallback callback);
+
+    /**
+     * Expects an array to be the top value on the stack. Iterates the array.
+     * The callback generates code that will have each successive value from the array
+     * as the top value on the stack. Creates a variable to store the loop index.
+     * 
+     * @param callback
+     *            to invoke. The element will be the top value on the stack. The callback is responsible
+     *            for removing it from the stack.
+     * @return this builder
+     */
+    @Opcodes("IINC, ARRAYLENGTH, IFEQ, etc., GOTO")
+    InstructionBuilder iterateArray(InstructionBuilderCallback callback);
+
+    /**
+     * Increments a local integer variable.
+     * 
+     * @return this builder
+     */
+    @Opcodes("IINC")
+    InstructionBuilder increment(String name);
+
+    /** Expects the top object on the stack to be an array. Replaces it with the length of that array. */
+    @Opcodes("ARRAYLENGTH")
+    InstructionBuilder arrayLength();
 }

Copied: tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/WhileCallback.java (from r1092558, tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java)
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/WhileCallback.java?p2=tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/WhileCallback.java&p1=tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java&r1=1092558&r2=1092559&rev=1092559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/Condition.java (original)
+++ tapestry/tapestry5/trunk/plastic/src/main/java/org/apache/tapestry5/plastic/WhileCallback.java Fri Apr 15 01:06:56 2011
@@ -15,9 +15,19 @@
 package org.apache.tapestry5.plastic;
 
 /**
- * Condition used with {@link InstructionBuilder#when(Condition, WhenCallback)}.
+ * Callback used with {@link InstructionBuilder#doWhile(Condition, WhileCallback)}.
  */
-public enum Condition
+public interface WhileCallback
 {
-    NULL, NON_NULL, ZERO, NON_ZERO;
+    /**
+     * Creates the code to be evaluated by the {@link Condition}; often this involves
+     * loading a value from a variable or argument, or {@link InstructionBuilder#dupe()}'ing the
+     * top value on the stack.
+     */
+    void buildTest(InstructionBuilder builder);
+
+    /**
+     * Provides the main code executed inside the loop.
+     */
+    void buildBody(InstructionBuilder builder);
 }

Modified: tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodImplementationTests.groovy
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodImplementationTests.groovy?rev=1092559&r1=1092558&r2=1092559&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodImplementationTests.groovy (original)
+++ tapestry/tapestry5/trunk/plastic/src/test/groovy/org/apache/tapestry5/plastic/MethodImplementationTests.groovy Fri Apr 15 01:06:56 2011
@@ -43,4 +43,40 @@ class MethodImplementationTests extends 
 
         o.value == 2
     }
+
+    def "while, increment, array operations"() {
+        setup:
+
+        def mgr = new PlasticManager();
+
+        PlasticClass pc = mgr.getPlasticClass("testsubjects.WhileSubject")
+
+        PlasticMethod m = findMethod(pc, "firstNonNull")
+
+        m.changeImplementation({ InstructionBuilder b1 ->
+            b1.loadArgument 0
+            b1.iterateArray ({ InstructionBuilder b2 ->
+                b2.dupe().when (Condition.NON_NULL,
+                        [
+                            ifTrue: { it.returnResult() } ,
+                            ifFalse: { it.pop() }
+                        ] as WhenCallback)
+            } as InstructionBuilderCallback)
+
+            b1.returnDefaultValue()
+        } as InstructionBuilderCallback)
+
+        def o = pc.createInstantiator().newInstance()
+
+        expect:
+
+        o.firstNonNull ( [
+            null,
+            "fred",
+            "barney",
+            null,
+            "wilma"
+        ]
+        as String[]) == "fred"
+    }
 }

Added: tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/WhileSubject.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/WhileSubject.java?rev=1092559&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/WhileSubject.java (added)
+++ tapestry/tapestry5/trunk/plastic/src/test/java/testsubjects/WhileSubject.java Fri Apr 15 01:06:56 2011
@@ -0,0 +1,9 @@
+package testsubjects;
+
+public class WhileSubject
+{
+    public String firstNonNull(String[] values)
+    {
+        return null;
+    }
+}