Skip to content

Commit 9ce155a

Browse files
author
dlsmith
committed
DynamicJava: Restored the following features: assignments to new variables treated as declarations; checking return types; checking thrown types; expressions of type void do not output a value. The solution for assignments and voids is a hack; in the future this should be fixed by modifying the parser to never produce top-leve Expressions. Also added a SymbolUtil.typeOfGeneralClass() method, and fixed a bug in which expression statements appearing in the body of a method caused an internal error.
git-svn-id: file:///tmp/test-svn/trunk@4283 fe72c1cf-3628-48e9-8b72-1c46755d3cff
1 parent b125d4a commit 9ce155a

16 files changed

Lines changed: 289 additions & 93 deletions

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/ClassContext.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Iterator;
44
import edu.rice.cs.plt.iter.SequenceIterator;
5+
import edu.rice.cs.plt.iter.IterUtil;
56
import edu.rice.cs.plt.lambda.LambdaUtil;
67
import edu.rice.cs.dynamicjava.symbol.*;
78
import edu.rice.cs.dynamicjava.symbol.type.Type;
@@ -128,6 +129,18 @@ private boolean hasMethod(String name, TypeSystem ts) {
128129
else { return super.getThis(className); }
129130
}
130131

132+
/**
133+
* The expected type of a {@code return} statement in the given context, or {@code null}
134+
* if {@code return} statements should not appear here.
135+
*/
136+
@Override public Type getReturnType() { return null; }
137+
138+
/**
139+
* The types that are allowed to be thrown in the current context. If there is no
140+
* such declaration, the list will be empty.
141+
*/
142+
@Override public Iterable<Type> getDeclaredThrownTypes() { return IterUtil.empty(); }
143+
131144
/**
132145
* Return the type referenced by {@code super} in the current context, or {@code null}
133146
* if there is no such type (for example, in a static context).

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/DelegatingContext.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,22 @@ public Type getSuperType(TypeSystem ts) {
210210
return _next.getSuperType(ts);
211211
}
212212

213+
/**
214+
* The expected type of a {@code return} statement in the given context, or {@code null}
215+
* if {@code return} statements should not appear here.
216+
*/
217+
public Type getReturnType() {
218+
return _next.getReturnType();
219+
}
220+
221+
/**
222+
* The types that are allowed to be thrown in the current context. If there is no
223+
* such declaration, the list will be empty.
224+
*/
225+
public Iterable<Type> getDeclaredThrownTypes() {
226+
return _next.getDeclaredThrownTypes();
227+
}
228+
213229
public ClassLoader getClassLoader() {
214230
return _next.getClassLoader();
215231
}

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/ExpressionChecker.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ else if (ts.containsClass(classType, memberName.image())) {
503503
}
504504

505505
// TODO: Check accessibility of method
506-
// TODO: Check for uncaught exceptions
506+
checkThrownExceptions(inv.thrown(), node);
507507
node.setArguments(IterUtil.asList(inv.args()));
508508
setMethod(node, inv.method());
509509
Type result = ts.capture(inv.returnType());
@@ -565,7 +565,7 @@ else if (ts.containsClass(classType, memberName.image())) {
565565
// Note: Changes made below may also need to be made in the TypeSystem's boxing & unboxing implementations
566566
TypeSystem.ObjectMethodInvocation inv = ts.lookupMethod(receiver, node.getMethodName(), targs, args);
567567
// TODO: Check accessibility of method
568-
// TODO: Check for uncaught exceptions
568+
checkThrownExceptions(inv.thrown(), node);
569569
node.setExpression(inv.object());
570570
node.setArguments(IterUtil.asList(inv.args()));
571571
setMethod(node, inv.method());
@@ -607,7 +607,7 @@ else if (ts.containsClass(classType, memberName.image())) {
607607
try {
608608
TypeSystem.MethodInvocation inv = ts.lookupMethod(obj, node.getMethodName(), targs, args);
609609
// TODO: Check accessibility of method
610-
// TODO: Check for uncaught exceptions
610+
checkThrownExceptions(inv.thrown(), node);
611611
node.setArguments(IterUtil.asList(inv.args()));
612612
setMethod(node, inv.method());
613613
setDJClass(node, context.getThis());
@@ -645,7 +645,7 @@ else if (ts.containsClass(classType, memberName.image())) {
645645
// Note: Changes made below may also need to be made in the TypeSystem's boxing & unboxing implementations
646646
TypeSystem.MethodInvocation inv = ts.lookupStaticMethod(t, node.getMethodName(), targs, args);
647647
// TODO: Check accessibility of method
648-
// TODO: Check for uncaught exceptions
648+
checkThrownExceptions(inv.thrown(), node);
649649
node.setArguments(IterUtil.asList(inv.args()));
650650
setMethod(node, inv.method());
651651
Type result = ts.capture(inv.returnType());
@@ -751,7 +751,7 @@ private void addRuntimeCheck(Node node, Type expectedType, Type declaredActualTy
751751
try {
752752
TypeSystem.ConstructorInvocation inv = ts.lookupConstructor(t, targs, args);
753753
// TODO: Check accessibility of constructor
754-
// TODO: Check for uncaught exceptions
754+
checkThrownExceptions(inv.thrown(), node);
755755
node.setArguments(IterUtil.asList(inv.args()));
756756
setConstructor(node, inv.constructor());
757757
return setType(node, t);
@@ -790,7 +790,7 @@ private void addRuntimeCheck(Node node, Type expectedType, Type declaredActualTy
790790
try {
791791
TypeSystem.ConstructorInvocation inv = ts.lookupConstructor(t, targs, args);
792792
// TODO: Check accessibility of constructor
793-
// TODO: Check for uncaught exceptions
793+
checkThrownExceptions(inv.thrown(), node);
794794
node.setArguments(IterUtil.asList(inv.args()));
795795
}
796796
catch (InvalidTypeArgumentException e) {
@@ -842,7 +842,7 @@ private void addRuntimeCheck(Node node, Type expectedType, Type declaredActualTy
842842
try {
843843
TypeSystem.ConstructorInvocation inv = ts.lookupConstructor(t, targs, args);
844844
// TODO: Check accessibility of constructor
845-
// TODO: Check for uncaught exceptions
845+
checkThrownExceptions(inv.thrown(), node);
846846
node.setArguments(IterUtil.asList(inv.args()));
847847
setConstructor(node, inv.constructor());
848848
return setType(node, t);
@@ -896,7 +896,7 @@ private void addRuntimeCheck(Node node, Type expectedType, Type declaredActualTy
896896
try {
897897
TypeSystem.ConstructorInvocation inv = ts.lookupConstructor(t, targs, args);
898898
// TODO: Check accessibility of constructor
899-
// TODO: Check for uncaught exceptions
899+
checkThrownExceptions(inv.thrown(), node);
900900
node.setArguments(IterUtil.asList(inv.args()));
901901
}
902902
catch (InvalidTypeArgumentException e) {
@@ -923,6 +923,25 @@ private void addRuntimeCheck(Node node, Type expectedType, Type declaredActualTy
923923
setConstructor(node, IterUtil.first(c.declaredConstructors()));
924924
return setType(node, ts.makeClassType(c));
925925
}
926+
927+
private void checkThrownExceptions(Iterable<? extends Type> thrownTypes, Node node) {
928+
Iterable<Type> allowed = IterUtil.compose(TypeSystem.RUNTIME_EXCEPTION,
929+
context.getDeclaredThrownTypes());
930+
for (Type thrown : thrownTypes) {
931+
if (ts.isAssignable(TypeSystem.EXCEPTION, thrown)) {
932+
boolean valid = false;
933+
for (Type t : allowed) {
934+
if (ts.isAssignable(t, thrown)) { valid = true; break; }
935+
}
936+
if (!valid) {
937+
setErrorStrings(node, ts.userRepresentation(thrown));
938+
throw new ExecutionError("uncaught.exception", node);
939+
}
940+
}
941+
}
942+
}
943+
944+
926945

927946
/**
928947
* Visits an ArrayAccess. JLS 15.13.

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/FunctionContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,14 @@ private boolean isLocalFunction(String name) {
8484
else { return super.getSuperType(ts); }
8585
}
8686

87+
@Override public Type getReturnType() {
88+
if (_f instanceof LocalFunction) { return ((LocalFunction) _f).returnType(); }
89+
else if (_f instanceof DJMethod) { return ((DJMethod) _f).returnType(); }
90+
else { return null; }
91+
}
92+
93+
@Override public Iterable<Type> getDeclaredThrownTypes() { return _f.thrownTypes(); }
94+
8795
private boolean isStatic() {
8896
return _f instanceof DJMethod && ((DJMethod)_f).isStatic();
8997
}

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/Interpreter.java

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,14 @@
1212
import koala.dynamicjava.parser.wrapper.ParseError;
1313
import edu.rice.cs.dynamicjava.Options;
1414

15+
// temporary imports to support hacked assignment handling and void returns
16+
import koala.dynamicjava.tree.SimpleAssignExpression;
17+
import koala.dynamicjava.tree.AmbiguousName;
18+
import koala.dynamicjava.tree.VariableDeclaration;
19+
import koala.dynamicjava.SourceInfo;
20+
import koala.dynamicjava.interpreter.NodeProperties;
21+
import edu.rice.cs.dynamicjava.symbol.TypeSystem;
22+
1523
import static edu.rice.cs.plt.debug.DebugUtil.debug;
1624

1725
/**
@@ -72,6 +80,25 @@ private TypeContext typeCheck(Iterable<Node> tree) throws InterpreterException {
7280
try {
7381
TypeContext newContext = _typeContext;
7482
for (Node n : tree) {
83+
84+
// A hack to allow declarations without types: (TODO: fix this)
85+
if (n instanceof SimpleAssignExpression) {
86+
SimpleAssignExpression assign = (SimpleAssignExpression) n;
87+
if (assign.getLeftExpression() instanceof AmbiguousName) {
88+
AmbiguousName ambigName = (AmbiguousName) assign.getLeftExpression();
89+
if (ambigName.getIdentifiers().size() == 1) {
90+
String name = ambigName.getRepresentation();
91+
if (!newContext.variableExists(name, _opt.typeSystem())) {
92+
SourceInfo si = n.getSourceInfo();
93+
n = new VariableDeclaration(false, null, name, assign.getRightExpression(),
94+
si.getFilename(), si.getStartLine(), si.getStartColumn(),
95+
si.getEndLine(), si.getEndColumn());
96+
assign.setProperty("assignmentAsDeclaration", n);
97+
}
98+
}
99+
}
100+
}
101+
75102
if (n instanceof Expression) { n.acceptVisitor(new ExpressionChecker(newContext, _opt)); }
76103
else { newContext = n.acceptVisitor(new StatementChecker(newContext, _opt)); }
77104
}
@@ -85,7 +112,17 @@ private Pair<RuntimeBindings, Option<Object>> evaluate(Iterable<Node> tree) thro
85112
RuntimeBindings newBindings = _bindings;
86113
Option<Object> val = Option.none();
87114
for (Node n : tree) {
88-
if (n instanceof Expression) { val = Option.some(new ExpressionEvaluator(newBindings, _opt).value(n)); }
115+
// TODO: eliminate hacks that support inferred assignment and void returns
116+
if (n.hasProperty("assignmentAsDeclaration")) {
117+
n = (Node) n.getProperty("assignmentAsDeclaration");
118+
}
119+
if (n instanceof Expression) {
120+
Object evalResult = new ExpressionEvaluator(newBindings, _opt).value(n);
121+
if (evalResult == null && NodeProperties.getType(n).equals(TypeSystem.VOID)) {
122+
val = Option.none();
123+
}
124+
else { val = Option.some(evalResult); }
125+
}
89126
else {
90127
StatementEvaluator.Result r = n.acceptVisitor(new StatementEvaluator(newBindings, _opt));
91128
newBindings = r.bindings();

dynamicjava/src/edu/rice/cs/dynamicjava/interpreter/StatementChecker.java

Lines changed: 90 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575

7676
import java.util.Set;
7777
import java.util.HashSet;
78+
import java.util.List;
79+
import java.util.LinkedList;
7880
import edu.rice.cs.plt.iter.IterUtil;
7981
import edu.rice.cs.plt.tuple.Pair;
8082
import edu.rice.cs.plt.lambda.Lambda;
@@ -122,7 +124,11 @@ public StatementChecker(TypeContext ctx, Options options) {
122124

123125
private TypeContext checkList(Iterable<? extends Node> l) {
124126
TypeContext c = context;
125-
for (Node n : l) { c = n.acceptVisitor(new StatementChecker(c, opt)); }
127+
for (Node n : l) {
128+
// TODO: fix the parser so there aren't any Expressions here
129+
if (n instanceof Expression) { n.acceptVisitor(new ExpressionChecker(c, opt)); }
130+
else { c = n.acceptVisitor(new StatementChecker(c, opt)); }
131+
}
126132
return c;
127133
}
128134

@@ -262,24 +268,32 @@ private Pair<String, String> splitName(String name) {
262268

263269
/** Checks the declaration's initializer and creates a new context */
264270
@Override public TypeContext visit(VariableDeclaration node) {
265-
Type t = checkTypeName(node.getType());
266-
LocalVariable v = new LocalVariable(node.getName(), t, node.isFinal());
267-
setVariable(node, v);
268-
TypeContext newContext = new LocalContext(context, v);
269-
270-
if (node.getInitializer() != null) {
271-
Type initT = checkType(node.getInitializer(), t);
272-
try {
273-
Expression newInit = ts.assign(t, node.getInitializer());
274-
node.setInitializer(newInit);
275-
}
276-
catch (UnsupportedConversionException e) {
277-
setErrorStrings(node, ts.userRepresentation(initT), ts.userRepresentation(t));
278-
throw new ExecutionError("assignment.types", node);
271+
if (node.getType() == null) {
272+
// We infer the variable's type. We can assume the initializer is non-null.
273+
Type initT = checkType(node.getInitializer());
274+
LocalVariable v = new LocalVariable(node.getName(), initT, node.isFinal());
275+
setVariable(node, v);
276+
return new LocalContext(context, v);
277+
}
278+
else {
279+
Type t = checkTypeName(node.getType());
280+
LocalVariable v = new LocalVariable(node.getName(), t, node.isFinal());
281+
setVariable(node, v);
282+
TypeContext newContext = new LocalContext(context, v);
283+
284+
if (node.getInitializer() != null) {
285+
Type initT = checkType(node.getInitializer(), t);
286+
try {
287+
Expression newInit = ts.assign(t, node.getInitializer());
288+
node.setInitializer(newInit);
289+
}
290+
catch (UnsupportedConversionException e) {
291+
setErrorStrings(node, ts.userRepresentation(initT), ts.userRepresentation(t));
292+
throw new ExecutionError("assignment.types", node);
293+
}
279294
}
295+
return newContext;
280296
}
281-
282-
return newContext;
283297
}
284298

285299
@Override public TypeContext visit(ClassDeclaration node) {
@@ -575,29 +589,32 @@ else if (ts.isIterable(collType)) {
575589
* Visits a TryStatement. JLS 14.20.
576590
*/
577591
@Override public TypeContext visit(TryStatement node) {
578-
node.getTryBlock().acceptVisitor(this);
579-
for (CatchStatement c : node.getCatchStatements()) { c.acceptVisitor(this); }
580-
if (node.getFinallyBlock() != null) { node.getFinallyBlock().acceptVisitor(this); }
581-
return context;
582-
}
583-
584-
/**
585-
* Visits a CatchStatement. JLS 14.20.
586-
*/
587-
@Override public TypeContext visit(CatchStatement node) {
588-
FormalParameter p = node.getException();
589-
Type paramT = checkTypeName(p.getType());
590-
LocalVariable var = setVariable(p, new LocalVariable(p.getName(), paramT, p.isFinal()));
591-
if (!ts.isAssignable(TypeSystem.THROWABLE, paramT)) {
592-
setErrorStrings(node, ts.userRepresentation(paramT));
593-
throw new ExecutionError("catch.type", node);
592+
List<Type> caughtTypes = new LinkedList<Type>();
593+
for (CatchStatement c : node.getCatchStatements()) {
594+
FormalParameter p = c.getException();
595+
Type caughtT = checkTypeName(p.getType());
596+
if (!ts.isAssignable(TypeSystem.THROWABLE, caughtT)) {
597+
setErrorStrings(c, ts.userRepresentation(caughtT));
598+
throw new ExecutionError("catch.type", c);
599+
}
600+
if (!ts.isReifiable(caughtT)) {
601+
throw new ExecutionError("reifiable.type", c);
602+
}
603+
setVariable(p, new LocalVariable(p.getName(), caughtT, p.isFinal()));
604+
setErasedType(c, ts.erasedClass(caughtT));
605+
caughtTypes.add(caughtT);
594606
}
595-
if (!ts.isReifiable(paramT)) { throw new ExecutionError("reifiable.type", node); }
596607

597-
TypeContext newContext = new LocalContext(context, var);
598-
node.getBlock().acceptVisitor(new StatementChecker(newContext, opt));
599-
600-
setErasedType(node, ts.erasedClass(paramT));
608+
TypeContext tryContext = new TryBlockContext(context, caughtTypes);
609+
node.getTryBlock().acceptVisitor(new StatementChecker(tryContext, opt));
610+
611+
for (CatchStatement c : node.getCatchStatements()) {
612+
TypeContext catchContext = new LocalContext(context, getVariable(c.getException()));
613+
c.getBlock().acceptVisitor(new StatementChecker(catchContext, opt));
614+
}
615+
616+
if (node.getFinallyBlock() != null) { node.getFinallyBlock().acceptVisitor(this); }
617+
601618
return context;
602619
}
603620

@@ -610,15 +627,48 @@ else if (ts.isIterable(collType)) {
610627
setErrorStrings(node, ts.userRepresentation(thrown));
611628
throw new ExecutionError("throw.type", node);
612629
}
630+
else if (ts.isAssignable(TypeSystem.EXCEPTION, thrown)) {
631+
boolean valid = false;
632+
Iterable<Type> allowed = IterUtil.compose(TypeSystem.RUNTIME_EXCEPTION,
633+
context.getDeclaredThrownTypes());
634+
for (Type t : allowed) {
635+
if (ts.isAssignable(t, thrown)) { valid = true; break; }
636+
}
637+
if (!valid) {
638+
setErrorStrings(node, ts.userRepresentation(thrown));
639+
throw new ExecutionError("uncaught.exception", node);
640+
}
641+
}
613642
return context;
614643
}
615644

616645
/**
617646
* Visits a ReturnStatement
618647
*/
619648
@Override public TypeContext visit(ReturnStatement node) {
620-
// TODO: Check that the return type is correct (including the void case)
621-
if (node.getExpression() != null) { checkType(node.getExpression()); }
649+
Type expected = context.getReturnType();
650+
if (expected == null) { throw new ExecutionError("return.not.allowed", node); }
651+
652+
if (node.getExpression() == null) {
653+
if (!expected.equals(TypeSystem.VOID)) {
654+
setErrorStrings(node, ts.userRepresentation(TypeSystem.VOID),
655+
ts.userRepresentation(expected));
656+
throw new ExecutionError("return.type", node);
657+
}
658+
}
659+
else {
660+
checkType(node.getExpression(), expected);
661+
try {
662+
Expression newExp = ts.assign(expected, node.getExpression());
663+
node.setExpression(newExp);
664+
}
665+
catch (UnsupportedConversionException e) {
666+
setErrorStrings(node, ts.userRepresentation(getType(node.getExpression())),
667+
ts.userRepresentation(expected));
668+
throw new ExecutionError("return.type", node);
669+
}
670+
}
671+
622672
return context;
623673
}
624674

0 commit comments

Comments
 (0)