Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

fixed BYTEMAN-210

implemented support for array length accesses i.e. expressions of the
form arrayExpression.length

modified the type checker so AssignableExpressions know whether they
are type checkign as an LVALUE or RVALUE -- this i sneeded to ensure
attempts to update arrayExpression.length are rejected with a type
error

modifed the grammar so that array indexed terms can appear as the target
of a field dereference without needing bracketing

extended the test case in TestArray to check that array length
accesses are ok. the test case does not include an example where
arrayExpression.length is used as an LVALUE because this just throws
an exception but this was checked manually.
  • Loading branch information...
commit f9fb44cca744b602085791ff62340c1e24876a4c 1 parent f6ada62
Andrew Dinn adinn authored
2  agent/grammar/cup/ECAGrammar.cup
View
@@ -604,6 +604,8 @@ expr_field_expr
{: RESULT = node(ParseNode.FIELD, fleft, fright, f, se); :}
| meth_expr:me DOT simple_name:f
{: RESULT = node(ParseNode.FIELD, fleft, fright, f, me); :}
+ | array_expr:ae DOT simple_name:f
+ {: RESULT = node(ParseNode.FIELD, fleft, fright, f, ae); :}
| expr_field_expr:efe DOT simple_name:f
{: RESULT = node(ParseNode.FIELD, fleft, fright, f, efe); :}
;
25 agent/src/main/java/org/jboss/byteman/rule/expression/ArrayExpression.java
View
@@ -75,6 +75,26 @@ public void bind() throws TypeException
}
public Type typeCheck(Type expected) throws TypeException {
+ typeCheckAny();
+ if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+ throw new TypeException("ArrayExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
+ }
+
+ return type;
+ }
+
+ public Type typeCheckAssign(Type expected) throws TypeException
+ {
+ typeCheckAny();
+ if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
+ throw new TypeException("ArrayExpression.typeCheckAssign : invalid value type " + expected.getName() + " for array assignment " + getPos());
+ }
+
+ return type;
+ }
+
+ private void typeCheckAny() throws TypeException
+ {
Type arrayType = arrayRef.typeCheck(Type.UNDEFINED);
Type nextType = arrayType;
for (Expression expr : idxList) {
@@ -85,11 +105,6 @@ public Type typeCheck(Type expected) throws TypeException {
expr.typeCheck(Type.N);
}
type = nextType;
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("ArrayExpression.typeCheck : invalid expected result type " + expected.getName() + getPos());
- }
-
- return type;
}
public Object interpret(HelperAdapter helper) throws ExecuteException {
9 agent/src/main/java/org/jboss/byteman/rule/expression/AssignExpression.java
View
@@ -54,11 +54,14 @@ public void bind() throws TypeException
}
public Type typeCheck(Type expected) throws TypeException {
- // we accept any type we are given and check the var type then we use its type to check the expression
+ // we accept any type we are given and check the rhs expression
+ // then we type check the lhs ensuring that it can be assigned
+ // with a value of this type. the resulting type has to be that
+ // of the lhs.
// if either operand cannot type check then it will throw an error
- Type type1 = lhs.typeCheck(expected);
- Type type2 = getOperand(1).typeCheck(type1);
+ Type type2 = getOperand(1).typeCheck(expected);
+ Type type1 = lhs.typeCheckAssign(type2);
type = type1;
return type;
}
7 agent/src/main/java/org/jboss/byteman/rule/expression/AssignableExpression.java
View
@@ -50,6 +50,13 @@ protected AssignableExpression(Rule rule, Type type, ParseNode token) {
}
/**
+ * typecheck the expression as an lvalue of an assignment operation
+ * @throws TypeException
+ */
+ public abstract Type typeCheckAssign(Type expected)
+ throws TypeException;
+
+ /**
* execute an assignment to the referenced location by interpretation of the expression,
* using the object passed in this call
* @param helperAdapter an execution context associated with the rule which contains a map of
26 agent/src/main/java/org/jboss/byteman/rule/expression/DollarExpression.java
View
@@ -160,6 +160,27 @@ public void bind(boolean isUpdateable) throws TypeException
}
public Type typeCheck(Type expected) throws TypeException {
+
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+ throw new TypeException("DollarExpression.typeCheck : invalid expected type " + expected.getName() + " for bound parameter " + name + getPos());
+ }
+ return type;
+ }
+
+ public Type typeCheckAssign(Type expected) throws TypeException {
+
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
+ throw new TypeException("DollarExpression.typeCheck : invalid value type " + expected.getName() + " for assignment to bound parameter " + name + getPos());
+ }
+ return type;
+ }
+
+ private void typeCheckAny()
+ {
// if the associated binding is an alias then dereference it
if (binding.isAlias()) {
@@ -167,11 +188,6 @@ public Type typeCheck(Type expected) throws TypeException {
}
type = binding.getType();
-
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("DollarExpression.typeCheck : invalid expected type " + expected.getName() + " for bound parameter " + name + getPos());
- }
- return type;
}
public Object interpret(HelperAdapter helper) throws ExecuteException
96 agent/src/main/java/org/jboss/byteman/rule/expression/FieldExpression.java
View
@@ -37,6 +37,7 @@
import org.objectweb.asm.Opcodes;
import java.io.StringWriter;
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -54,6 +55,7 @@ public FieldExpression(Rule rule, Type type, ParseNode fieldTree, String fieldNa
this.ownerType = null;
this.indirectStatic = null;
this.fieldIndex = -1;
+ this.isArrayLength = false;
}
/**
@@ -100,8 +102,44 @@ public void bindAssign() throws TypeException
{
bind();
}
-
public Type typeCheck(Type expected) throws TypeException {
+ checkIndirectStatic();
+ if (indirectStatic != null) {
+ // this is really a static field reference pointed to by owner so get it to type check
+ type = Type.dereference(indirectStatic.typeCheck(expected));
+ } else {
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+ throw new TypeException("FieldExpresssion.typeCheck : invalid expected type " + expected.getName() + getPos());
+ }
+ }
+ return type;
+ }
+
+ public Type typeCheckAssign(Type expected) throws TypeException {
+ checkIndirectStatic();
+ if (indirectStatic != null) {
+ // this is really a static field reference pointed to by owner so get it to type check
+ type = Type.dereference(indirectStatic.typeCheckAssign(expected));
+ return type;
+ } else {
+ typeCheckAny();
+
+ // we cannot accept an array length access in this position
+ if (isArrayLength) {
+ throw new TypeException("FieldExpresssion.typeCheck : invalid attempt to update array length " + owner + getPos());
+ }
+
+ if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
+ throw new TypeException("FieldExpresssion.typeCheck : invalid value type " + expected.getName() + "for assignment" + getPos());
+ }
+ }
+ return type;
+ }
+
+ private void checkIndirectStatic() throws TypeException
+ {
if (owner == null && pathList != null) {
// factor off a typename from the path
TypeGroup typeGroup = getTypeGroup();
@@ -137,24 +175,29 @@ public Type typeCheck(Type expected) throws TypeException {
// get rid of the path list now
this.pathList = null;
}
+ }
- if (indirectStatic != null) {
- // this is really a static field reference pointed to by owner so get it to type check
- type = Type.dereference(indirectStatic.typeCheck(expected));
- return type;
- } else {
+ private void typeCheckAny() throws TypeException {
- // ok, type check the owner and then use it to derive the field type
+ // ok, type check the owner and then use it to derive the field type
- ownerType = Type.dereference(owner.typeCheck(Type.UNDEFINED));
+ ownerType = Type.dereference(owner.typeCheck(Type.UNDEFINED));
- if (ownerType.isUndefined()) {
- throw new TypeException("FieldExpresssion.typeCheck : unbound owner type for field " + fieldName + getPos());
- }
+ if (ownerType.isUndefined()) {
+ throw new TypeException("FieldExpresssion.typeCheck : unbound owner type for field " + fieldName + getPos());
+ }
- Class ownerClazz = ownerType.getTargetClass();
- Class valueClass = null;
+ Class ownerClazz = ownerType.getTargetClass();
+ Class valueClass = null;
+ if (ownerType.isArray()) {
+ if (fieldName.equals("length")) {
+ isArrayLength = true;
+ type = Type.I;
+ } else {
+ throw new TypeException("FieldExpresssion.typeCheck : array type " + ownerType.getName() + " does not accept field reference " + fieldName + getPos());
+ }
+ } else {
try {
field = lookupField(ownerClazz);
} catch (NoSuchFieldException e) {
@@ -167,12 +210,6 @@ public Type typeCheck(Type expected) throws TypeException {
valueClass = field.getType();
type = getTypeGroup().ensureType(valueClass);
-
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("FieldExpresssion.typeCheck : invalid expected type " + expected.getName() + getPos());
- }
-
- return type;
}
}
@@ -180,13 +217,23 @@ public Object interpret(HelperAdapter helper) throws ExecuteException
{
if (indirectStatic != null) {
return indirectStatic.interpret(helper);
+ } else if (isArrayLength) {
+ Object value = owner.interpret(helper);
+ if (value == null) {
+ throw new ExecuteException("FieldExpression.interpret : attempted array length indirection through null value " + owner + getPos());
+ }
+ try {
+ return Array.getLength(value);
+ } catch (Exception e) {
+ throw new ExecuteException("FieldExpression.interpret : exception accessing array length " + owner + getPos(), e);
+ }
} else {
try {
// TODO the reference should really be an expression?
Object value = owner.interpret(helper);
if (value == null) {
- throw new ExecuteException("FieldExpression.interpret : attempted field indirection through null value " + token.getText() + getPos());
+ throw new ExecuteException("FieldExpression.interpret : attempted field indirection through null value " + owner + getPos());
}
return field.get(value);
@@ -211,6 +258,11 @@ public void compile(MethodVisitor mv, CompileContext compileContext) throws Comp
if (indirectStatic != null) {
// this is just wrapping a static field expression so compile it
indirectStatic.compile(mv, compileContext);
+ } else if (isArrayLength) {
+ owner.compile(mv, compileContext);
+ mv.visitInsn(Opcodes.ARRAYLENGTH);
+ // we removed the owner and replaced with expected words
+ compileContext.addStackCount(expected - 1);
} else {
if (isPublicField) {
// we can use GETFIELD to access a public field
@@ -303,6 +355,8 @@ public void writeTo(StringWriter stringWriter) {
private Type ownerType;
private Field field;
private AssignableExpression indirectStatic;
+ private boolean isArrayLength;
+
/**
* true if this is a public field otherwise false
*/
@@ -323,7 +377,7 @@ public Object interpretAssign(HelperAdapter helperAdapter, Object value) throws
Object ownerInstance = owner.interpret(helperAdapter);
if (ownerInstance == null) {
- throw new ExecuteException("FieldExpression.interpret : attempted field indirection through null value " + token.getText() + getPos());
+ throw new ExecuteException("FieldExpression.interpret : attempted field indirection through null value " + owner + getPos());
}
field.set(ownerInstance, value);
24 agent/src/main/java/org/jboss/byteman/rule/expression/StaticExpression.java
View
@@ -75,6 +75,25 @@ public void bindAssign() throws TypeException
}
public Type typeCheck(Type expected) throws TypeException {
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+ throw new TypeException("StaticExpression.typeCheck : invalid expected return type " + expected.getName() + getPos());
+ }
+ return type;
+ }
+
+ public Type typeCheckAssign(Type expected) throws TypeException {
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
+ throw new TypeException("StaticExpression.typeCheck : invalid value type " + expected.getName() + " for static field assignment " + getPos());
+ }
+ return type;
+ }
+
+ public void typeCheckAny() throws TypeException {
+
// look for a class whose name matches some initial segment of pathList
TypeGroup typeGroup = getTypeGroup();
ownerType = Type.dereference(typeGroup.create(ownerTypeName));
@@ -97,11 +116,6 @@ public Type typeCheck(Type expected) throws TypeException {
clazz = field.getType();
type = typeGroup.ensureType(clazz);
-
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("StaticExpression.typeCheck : invalid expected return type " + expected.getName() + getPos());
- }
- return type;
}
public Object interpret(HelperAdapter helper) throws ExecuteException {
24 agent/src/main/java/org/jboss/byteman/rule/expression/Variable.java
View
@@ -104,6 +104,26 @@ private boolean bind(boolean isUpdateable)throws TypeException
}
public Type typeCheck(Type expected) throws TypeException {
+
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
+ throw new TypeException("Variable.typeCheck() : invalid result type : " + expected.getName() + getPos());
+ }
+ return type;
+ }
+
+ public Type typeCheckAssign(Type expected) throws TypeException {
+
+ typeCheckAny();
+
+ if (Type.dereference(expected).isDefined() && !type.isAssignableFrom(expected)) {
+ throw new TypeException("Variable.typeCheck() : invalid value type : " + expected.getName() + " for assignment " + getPos());
+ }
+ return type;
+ }
+
+ public void typeCheckAny() throws TypeException {
// type must be defined by now or we are in trouble
Binding binding = getBindings().lookup(name);
@@ -113,10 +133,6 @@ public Type typeCheck(Type expected) throws TypeException {
if (type.isUndefined()) {
throw new TypeException("Variable.typeCheck : unable to derive type for variable " + name + getPos());
}
- if (Type.dereference(expected).isDefined() && !expected.isAssignableFrom(type)) {
- throw new TypeException("Variable.typeCheck() : invalid result type : " + expected.getName() + getPos());
- }
- return type;
}
public Object interpret(HelperAdapter helper) throws ExecuteException {
1,360 agent/src/main/java/org/jboss/byteman/rule/grammar/ECAGrammarParser.java
View
691 additions, 669 deletions not shown
4 agent/src/main/java/org/jboss/byteman/rule/grammar/ECATokenLexer.java
View
@@ -1,4 +1,4 @@
-/* The following code was generated by JFlex 1.4.3 on 01/05/12 10:46 */
+/* The following code was generated by JFlex 1.4.3 on 24/07/12 14:53 */
/*
* JBoss, Home of Professional Open Source
@@ -33,7 +33,7 @@
/**
* This class is a scanner generated by
* <a href="http://www.jflex.de/">JFlex</a> 1.4.3
- * on 01/05/12 10:46 from the specification file
+ * on 24/07/12 14:53 from the specification file
* <tt>/ssd/home/adinn/jboss/byteman/git/byteman/agent/grammar/flex/ECAToken.flex</tt>
*/
public class ECATokenLexer implements java_cup.runtime.Scanner {
2  agent/src/main/java/org/jboss/byteman/rule/grammar/sym.java
View
@@ -1,7 +1,7 @@
//----------------------------------------------------
// The following code was generated by CUP v0.11a beta 20060608
-// Tue May 01 10:46:04 BST 2012
+// Tue Jul 24 14:53:20 BST 2012
//----------------------------------------------------
package org.jboss.byteman.rule.grammar;
40 agent/src/test/java/org/jboss/byteman/tests/javaops/TestArray.java
View
@@ -37,6 +37,7 @@ public TestArray()
static int[] iarray;
static Object[][] oarray;
+ static int runNumber = 0;
public void test()
{
@@ -55,6 +56,21 @@ public void test()
}
checkOutput(true);
+
+ runNumber++;
+
+ oarray[0] = new Object[2];
+ oarray[0][0] = oarray[0][1] = "hello";
+
+ try {
+ log("calling TestArray.triggerMethod2");
+ triggerMethod2(iarray, oarray);
+ log("called TestArray.triggerMethod2 : oarray[0][1] == " + oarray[0][1]);
+ } catch (Exception e) {
+ log(e);
+ }
+
+ checkOutput(true);
}
public Object[] triggerMethod1(int[] iarray, Object[][] oarray)
@@ -63,13 +79,27 @@ public void test()
return null;
}
+ public void triggerMethod2(int[] iarray, Object[][] oarray)
+ {
+ log("inside TestArray.triggerMethod2");
+ }
+
@Override
public String getExpected() {
- logExpected("calling TestArray.triggerMethod1");
- logExpected("inside TestArray.triggerMethod1");
- logExpected("triggerMethod1 : iarray[0] == " + iarray[0]);
- logExpected("triggerMethod1 : oarray[0][0] == " + oarray[0][0]);
- logExpected("called TestArray.triggerMethod1 : result == " + oarray[0]);
+ if (runNumber == 0) {
+ logExpected("calling TestArray.triggerMethod1");
+ logExpected("inside TestArray.triggerMethod1");
+ logExpected("triggerMethod1 : iarray[0] == " + iarray[0]);
+ logExpected("triggerMethod1 : oarray[0][0] == " + oarray[0][0]);
+ logExpected("called TestArray.triggerMethod1 : result == " + oarray[0]);
+ } else {
+ logExpected("calling TestArray.triggerMethod2");
+ logExpected("inside TestArray.triggerMethod2");
+ logExpected("triggerMethod2 : iarray.length == " + 1);
+ logExpected("triggerMethod2 : oarray[0].length == " + 2);
+ logExpected("triggerMethod2 : oarray[0][1] == hello");
+ logExpected("called TestArray.triggerMethod2 : oarray[0][1] == goodbye");
+ }
return super.getExpected();
}
17 agent/src/test/resources/scripts/javaops/TestArray.btm
View
@@ -35,3 +35,20 @@ DO test.log("triggerMethod1 : iarray[0] == " + iarray[0]);
test.log("triggerMethod1 : oarray[0][0] == " + oarray[0][0]);
return oarray[0]
ENDRULE
+
+RULE test array length and array updates
+CLASS TestArray
+METHOD triggerMethod2(int[], Object[][])
+HELPER org.jboss.byteman.tests.helpers.Default
+AFTER CALL log
+BIND test : Test = $0;
+ iarray : int[] = $1;
+ oarray : Object[][] = $2;
+ l : int = oarray[0].length
+IF TRUE
+DO test.log("triggerMethod2 : iarray.length == " + iarray.length);
+ test.log("triggerMethod2 : oarray[0].length == " + l);
+ test.log("triggerMethod2 : oarray[0][" + (l - 1) + "] == " + oarray[0][l - 1]);
+ oarray[0][l - 1] = "goodbye";
+ENDRULE
+
Please sign in to comment.
Something went wrong with that request. Please try again.