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