Skip to content

Commit

Permalink
fixed BYTEMAN-210
Browse files Browse the repository at this point in the history
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
adinn committed Jul 25, 2012
1 parent f6ada62 commit f9fb44c
Show file tree
Hide file tree
Showing 13 changed files with 916 additions and 720 deletions.
2 changes: 2 additions & 0 deletions agent/grammar/cup/ECAGrammar.cup
Expand Up @@ -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); :}
;
Expand Down
Expand Up @@ -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) {
Expand All @@ -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 {
Expand Down
Expand Up @@ -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;
}
Expand Down
Expand Up @@ -49,6 +49,13 @@ protected AssignableExpression(Rule rule, Type type, ParseNode token) {
super(rule, type, 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
Expand Down
Expand Up @@ -160,18 +160,34 @@ 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()) {
binding = binding.getAlias();
}

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
Expand Down
Expand Up @@ -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;

Expand All @@ -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;
}

/**
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand All @@ -167,26 +210,30 @@ 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;
}
}

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);
Expand All @@ -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
Expand Down Expand Up @@ -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
*/
Expand All @@ -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);
Expand Down
Expand Up @@ -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));
Expand All @@ -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 {
Expand Down
Expand Up @@ -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);
Expand All @@ -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 {
Expand Down

0 comments on commit f9fb44c

Please sign in to comment.