Permalink
Browse files

Added ability to mention class literals in rule code -- fixes BYTEMAN…

…-251
  • Loading branch information...
1 parent e5efcb1 commit bbc551d556d9cd7e68305fa45c0e3f70d4b964ea @adinn committed Dec 10, 2013
@@ -198,10 +198,11 @@ scan with {: Symbol token = getScanner().next_token();
/* rule definition keywords */
/* first the redundant ones */
/*
-terminal RULE, CLASS, METHOD, LINE, ENDRULE;
+terminal RULE, METHOD, LINE, ENDRULE;
*/
/* now the required ones */
+terminal CLASS;
terminal BIND, IF, DO;
/* keywords ocurring in bindings, conditions and actions */
@@ -601,6 +602,8 @@ array_idx
field_expr
::= path:p DOT simple_name:f
{: RESULT = node(ParseNode.FIELD, fleft, fright, f, p); :}
+ | path:p DOT CLASS
+ {: RESULT = node(ParseNode.CLASS, pleft, pright, p); :}
| expr_field_expr:efe {: RESULT = efe; :}
;
@@ -108,7 +108,9 @@ Float = {Sign}? {PosFloat}
"RULE" { return symbol(sym.RULE); }
"CLASS" { return symbol(sym.CLASS); }
-
+
+"class" { return symbol(sym.CLASS); }
+
"METHOD" { return symbol(sym.METHOD); }
"LINE" { return symbol(sym.LINE); }
@@ -131,6 +133,8 @@ Float = {Sign}? {PosFloat}
"NEW"|"new" { return symbol(sym.NEW); }
+"class" { return symbol(sym.CLASS); }
+
/* various bracket pairs */
"(" { return symbol(sym.LPAREN); }
View
@@ -235,6 +235,21 @@
<argLine>-javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestNew.btm</argLine>
</configuration>
</execution>
+ <execution>
+ <id>javaops.TestClassLiteral</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <forkMode>once</forkMode>
+ <includes>
+ <include>org/jboss/byteman/tests/javaops/TestClassLiteral.class</include>
+ </includes>
+ <argLine>-javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestClassLiteral.btm</argLine>
+ </configuration>
+ </execution>
<!-- location clauses -->
<execution>
<id>location.TestAll</id>
@@ -1027,6 +1042,21 @@
<argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestNew.btm</argLine>
</configuration>
</execution>
+ <execution>
+ <id>javaops.TestClassLiteral.compiled</id>
+ <phase>integration-test</phase>
+ <goals>
+ <goal>integration-test</goal>
+ <goal>verify</goal>
+ </goals>
+ <configuration>
+ <forkMode>once</forkMode>
+ <includes>
+ <include>org/jboss/byteman/tests/javaops/TestClassLiteral.class</include>
+ </includes>
+ <argLine>-Dorg.jboss.byteman.compile.to.bytecode -javaagent:${project.build.directory}/byteman-${project.version}.jar=script:${project.build.testOutputDirectory}/scripts/javaops/TestClassLiteral.btm</argLine>
+ </configuration>
+ </execution>
<!-- location clauses compiled -->
<execution>
<id>location.TestAll.compiled</id>
@@ -0,0 +1,139 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2013, Red Hat and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ *
+ * @authors Andrew Dinn
+ */
+
+package org.jboss.byteman.rule.expression;
+
+import org.jboss.byteman.rule.Rule;
+import org.jboss.byteman.rule.compiler.CompileContext;
+import org.jboss.byteman.rule.exception.CompileException;
+import org.jboss.byteman.rule.exception.ExecuteException;
+import org.jboss.byteman.rule.exception.TypeException;
+import org.jboss.byteman.rule.grammar.ParseNode;
+import org.jboss.byteman.rule.helper.HelperAdapter;
+import org.jboss.byteman.rule.type.Type;
+import org.jboss.byteman.rule.type.TypeGroup;
+import org.objectweb.asm.MethodVisitor;
+
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+
+/**
+ * Clas modelling a class literal of the form foo.bar.baz.Mumble.class
+ */
+public class ClassLiteralExpression extends Expression
+{
+ public ClassLiteralExpression(Rule rule, Type type, ParseNode pathTree, String[] pathList) {
+ // we cannot process the pathlist until typecheck time
+ super(rule, type, pathTree);
+ this.pathList = pathList;
+ this.ownerType = null;
+ this.classIndex = -1;
+ }
+
+ /**
+ * verify that variables mentioned in this expression are actually available in the supplied
+ * bindings list and infer/validate the type of this expression or its subexpressions
+ * where possible
+ *
+ * @return true if all variables in this expression are bound and no type mismatches have
+ * been detected during inference/validation.
+ */
+ public void bind() throws TypeException
+ {
+ // nothing to bind
+ }
+
+ public Type typeCheck(Type expected) throws TypeException {
+ // we have a putative reference to a class of the form
+ // foo.bar.baz.Mumble.class
+ TypeGroup typeGroup = getTypeGroup();
+ ownerType = typeGroup.create(getPath(pathList.length));
+ if (ownerType == null || ownerType.getTargetClass() == null) {
+ throw new TypeException("FieldExpression.typeCheck : invalid class literal " + getPath(pathList.length) + ".class" + getPos());
+ }
+ type = typeGroup.ensureType(Class.class);
+
+ return type;
+ }
+
+ public Object interpret(HelperAdapter helper) throws ExecuteException
+ {
+ return ownerType.getTargetClass();
+ }
+
+ public void compile(MethodVisitor mv, CompileContext compileContext) throws CompileException
+ {
+ // make sure we are at the right source line
+ compileContext.notifySourceLine(line);
+
+ int currentStack = compileContext.getStackCount();
+ int expected = 1;
+
+ // TODO -- this is only ok if the class is accessible
+ // we will need to use a helper method for non-accessible classes
+ mv.visitLdcInsn(org.objectweb.asm.Type.getType(ownerType.getTargetClass()));
+ // we added one object
+ compileContext.addStackCount(1);
+ // check the stack height is ok
+ if (compileContext.getStackCount() != currentStack + expected) {
+ throw new CompileException("ClassLiteralExpression.compile : invalid stack height " + compileContext.getStackCount() + " expecting " + (currentStack + expected));
+ }
+ }
+
+ public String getPath(int len)
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(pathList[0]);
+
+ for (int i = 1; i < len; i++) {
+ buffer.append(".");
+ buffer.append(pathList[i]);
+ }
+ return buffer.toString();
+ }
+
+ public void writeTo(StringWriter stringWriter) {
+ // we normally have a owner expression but before binding we have a path
+ String sepr = "";
+ for (String field : pathList) {
+ stringWriter.write(sepr);
+ stringWriter.write(field);
+ sepr =".";
+ }
+ }
+
+ private String[] pathList;
+ private Type ownerType;
+ private Field field;
+
+ /**
+ * true if this is a public class otherwise false
+ */
+ private boolean isPublicClass;
+ /**
+ * index used by compiled code when referring to a non-public class.
+ */
+ private int classIndex;
+
+}
@@ -29,6 +29,7 @@
import org.jboss.byteman.rule.type.Type;
import org.jboss.byteman.rule.exception.TypeException;
import org.jboss.byteman.rule.Rule;
+import org.omg.CORBA.FieldNameHelper;
import java.util.List;
import java.util.ArrayList;
@@ -57,6 +58,7 @@ public static Expression createExpression(Rule rule, Bindings bindings, ParseNod
// (ARRAY typename)
// (ARRAY expr expr_list)
// (FIELD expr simple_name)
+ // (CLASS path simple_name)
// (METH simple_name expr expr_list)
// (NEW name expr_list)
// (BOOLEAN_LITERAL)
@@ -114,6 +116,12 @@ public static Expression createExpression(Rule rule, Bindings bindings, ParseNod
expr = createFieldExpression(rule, bindings, child0, child1, type);
}
break;
+ case CLASS:
+ {
+ ParseNode child0 = (ParseNode) exprTree.getChild(0);
+ expr = createClassLiteralExpression(rule, bindings, child0, type);
+ }
+ break;
case METH:
{
ParseNode child0 = (ParseNode) exprTree.getChild(0);
@@ -287,11 +295,28 @@ public static AssignableExpression createFieldExpression(Rule rule, Bindings bin
target = createExpression(rule, bindings, targetTree, Type.UNDEFINED);
pathList = null;
}
+
fieldExpr = new FieldExpression(rule, type, fieldTree, fieldTree.getText(), target, pathList);
return fieldExpr;
}
+ public static Expression createClassLiteralExpression(Rule rule, Bindings bindings, ParseNode pathTree, Type type)
+ throws TypeException
+ {
+ Expression classLiteralExpr;
+ String[] pathList;
+
+ // this is a class literal -- use the path as the tree for the expression
+ // the path tree must have tag PATH
+
+ pathList = createPathList(pathTree);
+
+ classLiteralExpr = new ClassLiteralExpression(rule, type, pathTree, pathList);
+
+ return classLiteralExpr;
+ }
+
public static Expression createCallExpression(Rule rule, Bindings bindings, ParseNode selectorTree, ParseNode recipientTree, ParseNode argTree, Type type)
throws TypeException
{
@@ -356,6 +356,7 @@ public void writeTo(StringWriter stringWriter) {
private Field field;
private AssignableExpression indirectStatic;
private boolean isArrayLength;
+ private boolean isClassAccess;
/**
* true if this is a public field otherwise false
@@ -118,7 +118,7 @@ public Type typeCheck(Type expected) throws TypeException {
// factor off a typename from the path
Type rootType = typeGroup.match(pathList);
if (rootType == null) {
- throw new TypeException("FieldExpression.typeCheck : invalid path " + getPath(pathList.length) + " to static method " + name + getPos());
+ throw new TypeException("MethodExpression.typeCheck : invalid path " + getPath(pathList.length) + " to static method " + name + getPos());
}
// find out how many of the path elements are included in the type name
Oops, something went wrong.

0 comments on commit bbc551d

Please sign in to comment.