Skip to content

Commit 270c6e5

Browse files
author
dlsmith
committed
DynamicJava: SourceChecker support code. Introduced DepthFirstVisitor. Added an "error" property to facilitate multiple errors in a single block and to cache errors for later analysis. Added an "archiveProperties" method to move all properties to a different name, allowing repeated checking with different TypeSystems.
git-svn-id: file:///tmp/test-svn/trunk@5108 fe72c1cf-3628-48e9-8b72-1c46755d3cff
1 parent dc96b8e commit 270c6e5

File tree

12 files changed

+919
-68
lines changed

12 files changed

+919
-68
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package edu.rice.cs.dynamicjava.interpreter;
22

33
import java.io.PrintWriter;
4+
5+
import edu.rice.cs.plt.lambda.Lambda;
46
import koala.dynamicjava.interpreter.error.ExecutionError;
57
import koala.dynamicjava.tree.SourceInfo;
68

@@ -19,4 +21,9 @@ public SourceInfo getSourceInfo() {
1921
return ((ExecutionError) getCause()).getNode().getSourceInfo();
2022
}
2123

24+
public static final Lambda<ExecutionError, CheckerException> FACTORY =
25+
new Lambda<ExecutionError, CheckerException>() {
26+
public CheckerException value(ExecutionError e) { return new CheckerException(e); }
27+
};
28+
2229
}

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

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,20 @@ public void initializeClassSignatures(AnonymousInnerAllocation ast) {
7575
}
7676

7777
private void initializeNestedClassSignatures(Iterable<? extends Node> members, TypeContext sigContext) {
78-
TypeContext bodyContext = new ClassContext(sigContext, _c);
78+
TypeContext bodyContext = new ClassContext(sigContext, _c);
79+
ExecutionError error = null;
7980
for (Node member : members) {
80-
if (member instanceof TypeDeclaration) {
81-
ClassChecker nestedChecker = new ClassChecker(getDJClass(member), _loader, bodyContext, _opt);
82-
nestedChecker.initializeClassSignatures((TypeDeclaration) member);
81+
try {
82+
if (member instanceof TypeDeclaration) {
83+
ClassChecker nestedChecker = new ClassChecker(getDJClass(member), _loader, bodyContext, _opt);
84+
nestedChecker.initializeClassSignatures((TypeDeclaration) member);
85+
}
86+
}
87+
catch (ExecutionError e) {
88+
if (error == null) { error = e; }
8389
}
8490
}
91+
if (error != null) { throw error; }
8592
}
8693

8794
/**
@@ -125,24 +132,27 @@ public void checkSignatures(AnonymousInnerAllocation ast) {
125132
checkClassMemberSignatures(ast.getMembers(), new ClassSignatureContext(_context, _c, _loader));
126133
}
127134

128-
private void checkClassMemberSignatures(Iterable<? extends Node> members, TypeContext sigContext) {
129-
TypeContext bodyContext = new ClassContext(sigContext, _c);
130-
Visitor<Void> v = new ClassMemberSignatureVisitor(bodyContext);
135+
private void visitMembers(Iterable<? extends Node> members, Visitor<?> v) {
136+
ExecutionError error = null;
131137
for (Node n : members) {
132138
debug.logStart();
133139
try { n.acceptVisitor(v); }
140+
catch (ExecutionError e) {
141+
if (error == null) { error = e; }
142+
}
134143
finally { debug.logEnd(); }
135144
}
145+
if (error != null) { throw error; }
146+
}
147+
148+
private void checkClassMemberSignatures(Iterable<? extends Node> members, TypeContext sigContext) {
149+
TypeContext bodyContext = new ClassContext(sigContext, _c);
150+
visitMembers(members, new ClassMemberSignatureVisitor(bodyContext));
136151
}
137152

138153
private void checkInterfaceMemberSignatures(Iterable<? extends Node> members, TypeContext sigContext) {
139-
TypeContext bodyContext = new ClassContext(sigContext, _c);
140-
Visitor<Void> v = new InterfaceMemberSignatureVisitor(bodyContext);
141-
for (Node n : members) {
142-
debug.logStart();
143-
try { n.acceptVisitor(v); }
144-
finally { debug.logEnd(); }
145-
}
154+
TypeContext bodyContext = new ClassContext(sigContext, _c);
155+
visitMembers(members, new InterfaceMemberSignatureVisitor(bodyContext));
146156
}
147157

148158
/**
@@ -171,13 +181,8 @@ public void checkBodies(AnonymousInnerAllocation ast) {
171181

172182
private void checkBodies(Iterable<? extends Node> members) {
173183
TypeContext sigContext = new ClassSignatureContext(_context, _c, _loader);
174-
TypeContext bodyContext = new ClassContext(sigContext, _c);
175-
MemberBodyVisitor bod = new MemberBodyVisitor(bodyContext);
176-
for (Node n : members) {
177-
debug.logStart();
178-
try { n.acceptVisitor(bod); }
179-
finally { debug.logEnd(); }
180-
}
184+
TypeContext bodyContext = new ClassContext(sigContext, _c);
185+
visitMembers(members, new MemberBodyVisitor(bodyContext));
181186
}
182187

183188
private static TypeParameter[] typeParameters(TypeDeclaration ast) {
@@ -363,9 +368,7 @@ private class MemberBodyVisitor extends AbstractVisitor<Void> {
363368
ExpressionChecker callChecker = new ExpressionChecker(bodyContext, _opt);
364369
ConstructorCall call = node.getConstructorCall();
365370
if (call != null) { callChecker.checkConstructorCall(call); }
366-
for (Node n : node.getStatements()) {
367-
bodyContext = n.acceptVisitor(new StatementChecker(bodyContext, _opt));
368-
}
371+
new StatementChecker(bodyContext, _opt).checkList(node.getStatements());
369372
// if the call is implicit, check it *after* checking the body (better error messages this way)
370373
if (call == null) { callChecker.checkConstructorCall(new ConstructorCall(null, null, true)); }
371374
return null;

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ public DJField checkEnumSwitchCase(Expression exp, Type enumT) {
229229
}
230230
String name = ids.get(0).image();
231231
Expression translation = new SimpleFieldAccess(name);
232+
setTranslation(exp, translation);
232233
try {
233234
// Should actually verify that that the name is a declared enum constant, not just
234235
// a static field. But that requires a lot of unimplemented support where we're
@@ -241,7 +242,6 @@ public DJField checkEnumSwitchCase(Expression exp, Type enumT) {
241242
}
242243
addRuntimeCheck(translation, t, ref.field().type());
243244
setType(translation, t);
244-
setTranslation(exp, translation);
245245
setType(exp, t);
246246
if (hasValue(translation)) { setValue(exp, getValue(translation)); }
247247
return ref.field();
@@ -341,8 +341,8 @@ private class ExpressionVisitor extends AbstractVisitor<Type> implements Lambda<
341341
}
342342
else {
343343
Expression resolvedExp = (Expression) resolved;
344-
resolvedExp.acceptVisitor(this);
345344
setTranslation(node, resolvedExp);
345+
resolvedExp.acceptVisitor(this);
346346
// VARIABLE_TYPE, TYPE, FIELD, and VARIABLE properties are important in the enclosing context;
347347
// others are not, and need not be copied to the AmbiguousName
348348
if (hasVariableType(resolvedExp)) { setVariableType(node, getVariableType(resolvedExp)); }
@@ -441,6 +441,7 @@ else if (ts.containsClass(classType, memberName.image(), context.accessModule())
441441
// TODO: Improve error when memberName is a non-static class
442442
}
443443
else {
444+
System.out.println("hi");
444445
setErrorStrings(node, ts.typePrinter().print(classType), memberName.image());
445446
throw new ExecutionError("no.such.member", node);
446447
}
@@ -699,8 +700,8 @@ private DJClass resolveThis(Option<String> outerName, Node node) {
699700
translation = new StaticMethodCall((ReferenceTypeName) resolved, node.getMethodName(),
700701
node.getArguments(), node.getSourceInfo());
701702
}
702-
translation.acceptVisitor(this);
703703
setTranslation(node, translation);
704+
translation.acceptVisitor(this);
704705
return setType(node, getType(translation));
705706
}
706707
else { receiver = (Expression) resolved; }

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

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,14 @@ private Iterable<Node> parse(String code) throws InterpreterException {
6868
}
6969

7070
private TypeContext typeCheck(Iterable<Node> tree) throws InterpreterException {
71-
try {
72-
TypeContext newContext = _typeContext;
73-
for (Node n : tree) {
74-
newContext = n.acceptVisitor(new StatementChecker(newContext, _opt));
75-
}
76-
return newContext;
77-
}
71+
try { return new StatementChecker(_typeContext, _opt).checkList(tree); }
7872
catch (ExecutionError e) { throw new CheckerException(e); }
7973
}
8074

8175
private Pair<RuntimeBindings, Option<Object>> evaluate(Iterable<Node> tree) throws InterpreterException {
8276
try {
83-
RuntimeBindings newBindings = _bindings;
84-
Option<Object> val = Option.none();
85-
for (Node n : tree) {
86-
StatementEvaluator.Result r = n.acceptVisitor(new StatementEvaluator(newBindings, _opt));
87-
newBindings = r.bindings();
88-
val = r.value();
89-
}
90-
return Pair.make(newBindings, val);
77+
StatementEvaluator.Result r = new StatementEvaluator(_bindings, _opt).evaluateSequence(tree);
78+
return Pair.make(r.bindings(), r.value());
9179
}
9280
catch (WrappedException e) {
9381
if (e.getCause() instanceof InterpreterException) { throw (InterpreterException) e.getCause(); }

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

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,14 +124,22 @@ public StatementChecker(TypeContext ctx, Options options) {
124124

125125
public TypeContext value(Node n) { return n.acceptVisitor(this); }
126126

127-
private Type checkType(Expression exp) { return new ExpressionChecker(context, opt).check(exp); }
128-
129-
private TypeContext checkList(Iterable<? extends Node> l) {
127+
public TypeContext checkList(Iterable<? extends Node> l) {
128+
ExecutionError error = null;
130129
TypeContext c = context;
131-
for (Node n : l) { c = n.acceptVisitor(new StatementChecker(c, opt)); }
130+
for (Node n : l) {
131+
try { c = n.acceptVisitor(new StatementChecker(c, opt)); }
132+
catch (ExecutionError e) {
133+
if (hasErrorContext(n)) { c = getErrorContext(n); }
134+
if (error == null) { error = e; }
135+
}
136+
}
137+
if (error != null) { throw error; }
132138
return c;
133139
}
134140

141+
private Type checkType(Expression exp) { return new ExpressionChecker(context, opt).check(exp); }
142+
135143
private Type checkType(Expression exp, Type expected) {
136144
return new ExpressionChecker(context, opt).check(exp, expected);
137145
}
@@ -292,16 +300,19 @@ private Pair<String, String> splitName(String name) {
292300
TypeContext newContext = new LocalContext(context, v);
293301

294302
if (initialized) {
295-
Type initT = checkType(node.getInitializer(), t);
296303
try {
297-
Expression newInit = ts.assign(t, node.getInitializer());
298-
node.setInitializer(newInit);
299-
}
300-
catch (UnsupportedConversionException e) {
301-
TypePrinter printer = ts.typePrinter();
302-
setErrorStrings(node, printer.print(initT), printer.print(t));
303-
throw new ExecutionError("assignment.types", node);
304+
Type initT = checkType(node.getInitializer(), t);
305+
try {
306+
Expression newInit = ts.assign(t, node.getInitializer());
307+
node.setInitializer(newInit);
308+
}
309+
catch (UnsupportedConversionException e) {
310+
TypePrinter printer = ts.typePrinter();
311+
setErrorStrings(node, printer.print(initT), printer.print(t));
312+
throw new ExecutionError("assignment.types", node);
313+
}
304314
}
315+
catch (ExecutionError e) { setErrorContext(node, newContext); throw e; }
305316
}
306317
return newContext;
307318
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ public StatementEvaluator(RuntimeBindings bindings, Options opt) {
6060
}
6161

6262

63-
private Result evaluateSequence(Iterable<? extends Node> nodes) {
63+
public Result evaluateSequence(Iterable<? extends Node> nodes) {
6464
Result result = new Result(_bindings);
6565
for (Node n : nodes) {
6666
result = n.acceptVisitor(new StatementEvaluator(result.bindings(), _opt));

dynamicjava/src/edu/rice/cs/dynamicjava/sourcechecker/SourceChecker.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,19 @@
88
import java.io.FileReader;
99
import java.io.PrintWriter;
1010
import java.util.ArrayList;
11+
import java.util.LinkedHashSet;
1112
import java.util.List;
13+
import java.util.Set;
1214

15+
import koala.dynamicjava.interpreter.NodeProperties;
1316
import koala.dynamicjava.interpreter.error.ExecutionError;
1417
import koala.dynamicjava.parser.wrapper.JavaCCParser;
1518
import koala.dynamicjava.parser.wrapper.ParseError;
1619
import koala.dynamicjava.tree.CompilationUnit;
20+
import koala.dynamicjava.tree.Node;
1721
import koala.dynamicjava.tree.SourceInfo;
1822
import koala.dynamicjava.tree.TypeDeclaration;
23+
import koala.dynamicjava.tree.visitor.DepthFirstVisitor;
1924
import edu.rice.cs.dynamicjava.Options;
2025
import edu.rice.cs.dynamicjava.interpreter.*;
2126
import edu.rice.cs.dynamicjava.symbol.ExtendedTypeSystem;
@@ -176,13 +181,56 @@ private abstract class ClassCheckerPhase extends Phase<Pair<TypeDeclaration, Cla
176181
protected ClassCheckerPhase(String description) { super(description); }
177182
protected final void step(Pair<TypeDeclaration, ClassChecker> arg) throws InterpreterException {
178183
try { step(arg.first(), arg.second()); }
179-
catch (ExecutionError e) { throw new CheckerException(e); }
184+
catch (ExecutionError e) { throw extractErrors(arg.first()); }
180185
}
181186
protected final SourceInfo location(Pair<TypeDeclaration, ClassChecker> arg) {
182187
return arg.first().getSourceInfo();
183188
}
184189
protected abstract void step(TypeDeclaration ast, ClassChecker checker);
185190
}
191+
192+
193+
/** Get all ERROR values associated with the given AST. */
194+
private static InterpreterException extractErrors(Node ast) {
195+
// accumulate in a set to avoid duplicates from DAGs
196+
final Set<ExecutionError> result = new LinkedHashSet<ExecutionError>();
197+
new PropertiesDepthFirstVisitor() {
198+
public void run(Node node) {
199+
if (NodeProperties.hasError(node)) { result.add(NodeProperties.getError(node)); }
200+
super.run(node);
201+
}
202+
}.run(ast);
203+
return CompositeException.make(IterUtil.map(result, CheckerException.FACTORY));
204+
}
205+
206+
/**
207+
* Append the given prefix to all properties of the given AST (the root node and its children),
208+
* effectively hiding them so that the program can be re-processed.
209+
*/
210+
private static void archiveProperties(Node ast, final String prefix) {
211+
new DepthFirstVisitor() {
212+
public void run(Node node) {
213+
node.archiveProperties(prefix);
214+
super.run(node);
215+
}
216+
}.run(ast);
217+
}
218+
219+
220+
/**
221+
* A DepthFirstVisitor extension that recurs on Node-typed properties. Note that this may lead to
222+
* multiple invocations of the same node (because the properties are often used to create DAGs).
223+
*/
224+
private static class PropertiesDepthFirstVisitor extends DepthFirstVisitor {
225+
public void run(Node node) {
226+
node.acceptVisitor(this);
227+
if (NodeProperties.hasLeftExpression(node)) { recur(NodeProperties.getLeftExpression(node)); }
228+
if (NodeProperties.hasTranslation(node)) { recur(NodeProperties.getTranslation(node)); }
229+
if (NodeProperties.hasStatementTranslation(node)) { recur(NodeProperties.getTranslation(node)); }
230+
}
231+
}
232+
233+
186234

187235
public static void main(String... args) {
188236
ArgumentParser argParser = new ArgumentParser();

dynamicjava/src/koala/dynamicjava/interpreter/NodeProperties.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@
2828

2929
package koala.dynamicjava.interpreter;
3030

31+
import koala.dynamicjava.interpreter.error.ExecutionError;
3132
import koala.dynamicjava.tree.Node;
3233
import koala.dynamicjava.tree.Expression;
34+
import edu.rice.cs.dynamicjava.interpreter.TypeContext;
3335
import edu.rice.cs.dynamicjava.symbol.*;
3436
import edu.rice.cs.dynamicjava.symbol.type.Type;
3537
import edu.rice.cs.dynamicjava.symbol.type.VariableType;
@@ -377,6 +379,38 @@ public static boolean hasTypeVariable(Node n) {
377379
};
378380

379381

382+
/** An ExecutionError that occurred at the given node. */
383+
public final static String ERROR = "error";
384+
385+
public static ExecutionError getError(Node n) {
386+
return (ExecutionError) n.getProperty(ERROR);
387+
}
388+
389+
public static ExecutionError setError(Node n, ExecutionError e) {
390+
n.setProperty(ERROR, e);
391+
return e;
392+
}
393+
394+
public static boolean hasError(Node n) {
395+
return n.hasProperty(ERROR);
396+
}
397+
398+
/** The new context that would have been the result had there not been an error. */
399+
public final static String ERROR_CONTEXT = "errorContext";
400+
401+
public static TypeContext getErrorContext(Node n) {
402+
return (TypeContext) n.getProperty(ERROR_CONTEXT);
403+
}
404+
405+
public static TypeContext setErrorContext(Node n, TypeContext c) {
406+
n.setProperty(ERROR_CONTEXT, c);
407+
return c;
408+
}
409+
410+
public static boolean hasErrorContext(Node n) {
411+
return n.hasProperty(ERROR_CONTEXT);
412+
}
413+
380414
/**
381415
* A Lambda2<Object, Object, Object> -- determines the operation to be used where it is
382416
* ambiguous (for example, a PlusExpression might require addition or concatenation)

dynamicjava/src/koala/dynamicjava/interpreter/error/ExecutionError.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,7 @@ public ExecutionError(String s, Node n) {
8484
rawMessage = s;
8585
node = n;
8686
getMessage(); // ensure that s is valid while we have a useful stack trace
87-
}
88-
89-
public ExecutionError(Throwable thrown) {
90-
this.thrown = thrown;
87+
NodeProperties.setError(n, this);
9188
}
9289

9390
/**

0 commit comments

Comments
 (0)